From 676f5255c9a8a96d67e8c27d34c53f336c3dccbf Mon Sep 17 00:00:00 2001 From: timonix Date: Mon, 19 Oct 2026 04:36:36 +0200 Subject: [PATCH 1/2] Enable the yosys flow for Gowin boards --- .../Skills/fpga-toolchain-yosys/SKILL.md | 6 +-- .../Tools/ConstraintFileHelper.cs | 26 ++++++++-- .../YosysCompileSettingsViewModel.cs | 7 ++- .../Yosys/YosysService.cs | 50 ++++++++++++++----- 4 files changed, 67 insertions(+), 22 deletions(-) diff --git a/src/OneWare.OssCadSuiteIntegration/Skills/fpga-toolchain-yosys/SKILL.md b/src/OneWare.OssCadSuiteIntegration/Skills/fpga-toolchain-yosys/SKILL.md index 88485e2b..fcac8f4f 100644 --- a/src/OneWare.OssCadSuiteIntegration/Skills/fpga-toolchain-yosys/SKILL.md +++ b/src/OneWare.OssCadSuiteIntegration/Skills/fpga-toolchain-yosys/SKILL.md @@ -44,8 +44,8 @@ Compile shows, grouped into Synthesis, Place & Route and Bitstream. | --- | --- | | `yosysToolchainNextPnrTool` | The `nextpnr-*` binary, e.g. `nextpnr-ice40`, `nextpnr-ecp5`, `nextpnr-himbaechel`. Must match the synth tool's family. | | `yosysToolchainNextPnrFlags` | Extra flags, typically the device and package selection. | -| `yosysToolchainConstraintFileType` | `pcf` or `ccf` — the constraint format the board uses. | -| `yosysToolchainOutputType` | `asc` or `txt` — the place & route output format the pack tool expects. | +| `yosysToolchainConstraintFileType` | `pcf`, `ccf` or `cst` — the constraint format the board uses. `ccf` and `cst` are converted from the project's PCF file. | +| `yosysToolchainOutputType` | `asc`, `txt` or `json` — the place & route output format the pack tool expects. | | `yosysToolchainNextPnrVerbose` | `true` adds `--verbose` and shows the full nextpnr output in the Output panel. Turn this on when diagnosing timing or placement problems. | ### Bitstream (pack) @@ -54,7 +54,7 @@ Compile shows, grouped into Synthesis, Place & Route and Bitstream. | --- | --- | | `yosysToolchainPackTool` | `icepack`, `ecppack`, `gowin_pack` or `gmpack`, matching the family. | | `yosysToolchainPackFlags` | Extra flags for the pack tool. | -| `packToolOutputFormat` | `bin` or `bit`. | +| `packToolOutputFormat` | `bin`, `bit` or `fs`. | The three tool selections must agree on the FPGA family: `synth_ice40` + `nextpnr-ice40` + `icepack` is consistent, mixing families is not. The hardware package of a board ships correct diff --git a/src/OneWare.OssCadSuiteIntegration/Tools/ConstraintFileHelper.cs b/src/OneWare.OssCadSuiteIntegration/Tools/ConstraintFileHelper.cs index c83d658f..843a1c78 100644 --- a/src/OneWare.OssCadSuiteIntegration/Tools/ConstraintFileHelper.cs +++ b/src/OneWare.OssCadSuiteIntegration/Tools/ConstraintFileHelper.cs @@ -2,7 +2,18 @@ namespace OneWare.OssCadSuiteIntegration.Tools; public class ConstraintFileHelper { - public static void Convert(string pcfPath, string ccfPath) + public static void ConvertPcfToCcf(string pcfPath, string ccfPath) + { + ConvertPcf(pcfPath, ccfPath, "#", (signal, pin) => $"NET \"{signal}\" Loc = \"{pin}\";"); + } + + public static void ConvertPcfToCst(string pcfPath, string cstPath) + { + ConvertPcf(pcfPath, cstPath, "//", (signal, pin) => $"IO_LOC \"{signal}\" {pin};"); + } + + private static void ConvertPcf(string pcfPath, string targetPath, string commentPrefix, + Func formatConstraint) { if (!File.Exists(pcfPath)) throw new FileNotFoundException("PCF file not found", pcfPath); @@ -23,7 +34,7 @@ public static void Convert(string pcfPath, string ccfPath) if (line.StartsWith("#")) { - result.Add(line); + result.Add(ReplaceCommentPrefix(line, commentPrefix)); continue; } @@ -45,15 +56,20 @@ public static void Convert(string pcfPath, string ccfPath) var signal = parts[1]; var pin = parts[2]; - var newLine = $"NET \"{signal}\" Loc = \"{pin}\";"; + var newLine = formatConstraint(signal, pin); if (!string.IsNullOrEmpty(comment)) - newLine += $" {comment}"; + newLine += $" {ReplaceCommentPrefix(comment, commentPrefix)}"; result.Add(newLine); } - File.WriteAllLines(ccfPath, result); + File.WriteAllLines(targetPath, result); + } + + private static string ReplaceCommentPrefix(string pcfComment, string commentPrefix) + { + return commentPrefix + pcfComment[1..]; } public static void ConvertCcfToPcf(string ccfPath, string pcfPath) diff --git a/src/OneWare.OssCadSuiteIntegration/ViewModels/YosysCompileSettingsViewModel.cs b/src/OneWare.OssCadSuiteIntegration/ViewModels/YosysCompileSettingsViewModel.cs index 1ac3f50d..786c7623 100644 --- a/src/OneWare.OssCadSuiteIntegration/ViewModels/YosysCompileSettingsViewModel.cs +++ b/src/OneWare.OssCadSuiteIntegration/ViewModels/YosysCompileSettingsViewModel.cs @@ -111,6 +111,7 @@ public YosysCompileSettingsViewModel(UniversalFpgaProjectRoot fpgaProjectRoot, I defaultProperties.GetValueOrDefault("yosysToolchainConstraintFileType") ?? "", [ "pcf", "ccf", + "cst" ]) { HoverDescription = "Set NextPnr tool" @@ -119,7 +120,8 @@ public YosysCompileSettingsViewModel(UniversalFpgaProjectRoot fpgaProjectRoot, I _nextPnrToolOutputTypeSetting = new ComboBoxSetting("NextPnr Output file type", defaultProperties.GetValueOrDefault("yosysToolchainOutputType") ?? "", [ "asc", - "txt" + "txt", + "json" ]) { HoverDescription = "Set NextPnr tool" @@ -151,7 +153,8 @@ public YosysCompileSettingsViewModel(UniversalFpgaProjectRoot fpgaProjectRoot, I _packOutputTypeSetting = new ComboBoxSetting("Pack output format", defaultProperties.GetValueOrDefault("packToolOutputFormat") ?? "", [ "bin", - "bit" + "bit", + "fs" ]) { HoverDescription = "Set Pack tool output format" diff --git a/src/OneWare.OssCadSuiteIntegration/Yosys/YosysService.cs b/src/OneWare.OssCadSuiteIntegration/Yosys/YosysService.cs index 20d508d5..67fb5732 100644 --- a/src/OneWare.OssCadSuiteIntegration/Yosys/YosysService.cs +++ b/src/OneWare.OssCadSuiteIntegration/Yosys/YosysService.cs @@ -251,23 +251,21 @@ private async Task RunNextpnrAsync(UniversalFpgaProjectRoot project, FpgaM builder.Add("--pcf").AddPath(pcfFile); break; case "ccf": - var absolutePcfPath = Path.Combine(project.RootFolderPath, pcfFile); - outputService.WriteLine($"Converting {absolutePcfPath} to CCF File"); - - if (File.Exists(absolutePcfPath)) - { - ConstraintFileHelper.Convert(absolutePcfPath, - Path.Combine(project.RootFolderPath, "./build/constrains.ccf")); - } - else - { - outputService.WriteLine($"Could not generate CCF file from {pcfFile}"); + if (!ConvertConstraintFile(project, pcfFile, "./build/constrains.ccf", "CCF", + ConstraintFileHelper.ConvertPcfToCcf)) return false; - } builder.Add("-o"); builder.AddScript("ccf={path}", ("{path}", "./build/constrains.ccf")); break; + case "cst": + if (!ConvertConstraintFile(project, pcfFile, "./build/constraints.cst", "CST", + ConstraintFileHelper.ConvertPcfToCst)) + return false; + + builder.Add("-o"); + builder.AddScript("cst={path}", ("{path}", "./build/constraints.cst", true)); + break; default: outputService.WriteLine($"Could not find Constraint file type: {cFileType}"); return false; @@ -286,6 +284,10 @@ private async Task RunNextpnrAsync(UniversalFpgaProjectRoot project, FpgaM builder.Add("-o"); builder.AddScript("out={path}", ("{path}", pnrOutput)); break; + case "json": + pnrOutput = "./build/pnr.json"; + builder.Add("--write").AddPath(pnrOutput); + break; default: outputService.WriteLine($"Could not find output type: {cOutputType}"); return false; @@ -354,6 +356,7 @@ public async Task AssembleAsync(UniversalFpgaProjectRoot project, FpgaMode { "asc" => "./build/nextpnr.asc", "txt" => "./build/impl.txt", + "json" => "./build/pnr.json", _ => throw new ArgumentException($"Unsupported input type: {cOutputType}") }; @@ -371,12 +374,15 @@ public async Task AssembleAsync(UniversalFpgaProjectRoot project, FpgaMode { "bin" => "./build/pack.bin", "bit" => "./build/pack.bit", + "fs" => "./build/pack.fs", _ => throw new ArgumentException($"Unsupported output format: {pOutputFormat}") }; // Remove a previous result so a failing run can never be followed by a stale bitstream DeleteArtifact(project, outputPath); + // gowin_pack takes the output as an option, the other pack tools as the second argument + if (packTool == "gowin_pack") builder.Add("-o"); builder.AddPath(outputPath); var flags = properties.GetValueOrDefault("yosysToolchainPackFlags")?.Split(' ', @@ -440,6 +446,26 @@ public async Task> ExtractNodesAsync(IProjectFile file) return ReadJson(Path.Combine(buildpath, "yosys_nodes.json")); } + /// + /// Converts the project's PCF file, which the constraint editor writes, into the format the nextpnr + /// architecture reads. + /// + private bool ConvertConstraintFile(UniversalFpgaProjectRoot project, string pcfFile, string targetFile, + string targetFormat, Action convert) + { + var absolutePcfPath = Path.Combine(project.RootFolderPath, pcfFile); + outputService.WriteLine($"Converting {absolutePcfPath} to {targetFormat} File"); + + if (!File.Exists(absolutePcfPath)) + { + outputService.WriteLine($"Could not generate {targetFormat} file from {pcfFile}"); + return false; + } + + convert(absolutePcfPath, Path.Combine(project.RootFolderPath, targetFile)); + return true; + } + private static bool IsErrorLine(string line) { return line.TrimStart().StartsWith("Error:", StringComparison.OrdinalIgnoreCase); From f991452fbd08c697972e30d35782715adf5996bd Mon Sep 17 00:00:00 2001 From: timonix Date: Mon, 19 Oct 2026 04:48:31 +0200 Subject: [PATCH 2/2] Keep the old public convert api working in case its used --- .../Tools/ConstraintFileHelper.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/OneWare.OssCadSuiteIntegration/Tools/ConstraintFileHelper.cs b/src/OneWare.OssCadSuiteIntegration/Tools/ConstraintFileHelper.cs index 843a1c78..2bd9f225 100644 --- a/src/OneWare.OssCadSuiteIntegration/Tools/ConstraintFileHelper.cs +++ b/src/OneWare.OssCadSuiteIntegration/Tools/ConstraintFileHelper.cs @@ -2,6 +2,12 @@ namespace OneWare.OssCadSuiteIntegration.Tools; public class ConstraintFileHelper { + [Obsolete(message: "Use ConvertPcfToCcf instead")] + public static void Convert(string pcfPath, string ccfPath) + { + ConvertPcfToCcf(pcfPath, ccfPath); + } + public static void ConvertPcfToCcf(string pcfPath, string ccfPath) { ConvertPcf(pcfPath, ccfPath, "#", (signal, pin) => $"NET \"{signal}\" Loc = \"{pin}\";");