From 730b57d9cde01dc7844379d568481852cdb32546 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Thu, 15 Jul 2021 11:06:03 +0200 Subject: [PATCH] build/gowin: Add initial timing constraints support. --- litex/build/gowin/gowin.py | 37 +++++++++++++++++++++++++++++++---- litex/build/gowin/platform.py | 4 ++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/litex/build/gowin/gowin.py b/litex/build/gowin/gowin.py index d14fa0a13..d07ffad44 100644 --- a/litex/build/gowin/gowin.py +++ b/litex/build/gowin/gowin.py @@ -6,6 +6,7 @@ # SPDX-License-Identifier: BSD-2-Clause import os +import math import subprocess from shutil import which @@ -14,7 +15,7 @@ from migen.fhdl.structure import _Fragment from litex.build.generic_platform import * from litex.build import tools -# IO Constraints (.cst) ---------------------------------------------------------------------------- +# Constraints (.cst and .tcl) ---------------------------------------------------------------------- def _build_cst(named_sc, named_pc): cst = [] @@ -43,6 +44,13 @@ def _build_cst(named_sc, named_pc): with open("top.cst", "w") as f: f.write("\n".join(cst)) +def _build_sdc(clocks, vns): + sdc = [] + for clk, period in sorted(clocks.items(), key=lambda x: x[0].duid): + sdc.append(f"create_clock -name {vns.get_name(clk)} -period {str(period)} [get_ports {{{vns.get_name(clk)}}}]") + with open("top.sdc", "w") as f: + f.write("\n".join(sdc)) + # Script ------------------------------------------------------------------------------------------- def _build_tcl(name, partnumber, files, options): @@ -51,9 +59,12 @@ def _build_tcl(name, partnumber, files, options): # Set Device. tcl.append(f"set_device -name {name} {partnumber}") - # Add IO Constraints. + # Add IOs Constraints. tcl.append("add_file top.cst") + # Add Timings Constraints. + tcl.append("add_file top.sdc") + # Add Sources. for f, typ, lib in files: tcl.append(f"add_file {f}") @@ -76,6 +87,7 @@ class GowinToolchain: def __init__(self): self.options = {} + self.clocks = dict() def build(self, platform, fragment, build_dir = "build", @@ -103,10 +115,18 @@ class GowinToolchain: if platform.verilog_include_paths: self.options["include_path"] = "{" + ";".join(platform.verilog_include_paths) + "}" - # Generate constraints file (.cst) + # Generate constraints file. + # IOs (.cst). _build_cst( named_sc = named_sc, - named_pc = named_pc) + named_pc = named_pc + ) + + # Timings (.sdc) + _build_sdc( + clocks = self.clocks, + vns = v_output.ns + ) # Generate build script (.tcl) script = _build_tcl( @@ -128,3 +148,12 @@ class GowinToolchain: os.chdir(cwd) return v_output.ns + + def add_period_constraint(self, platform, clk, period): + clk.attr.add("keep") + period = math.floor(period*1e3)/1e3 # round to lowest picosecond + if clk in self.clocks: + if period != self.clocks[clk]: + raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns" + .format(self.clocks[clk], period)) + self.clocks[clk] = period diff --git a/litex/build/gowin/platform.py b/litex/build/gowin/platform.py index 3b75808cb..bc0d69dd9 100644 --- a/litex/build/gowin/platform.py +++ b/litex/build/gowin/platform.py @@ -39,3 +39,7 @@ class GowinPlatform(GenericPlatform): def build(self, *args, **kwargs): return self.toolchain.build(self, *args, **kwargs) + + def add_period_constraint(self, clk, period): + if clk is None: return + self.toolchain.add_period_constraint(self, clk, period)