diff --git a/bittide-instances/bin/Shake.hs b/bittide-instances/bin/Shake.hs index e1f900da4..2304b66e5 100644 --- a/bittide-instances/bin/Shake.hs +++ b/bittide-instances/bin/Shake.hs @@ -10,14 +10,16 @@ module Main where import Prelude import Control.Applicative (liftA2) +import Control.DeepSeq (force) import Control.Monad.Extra (ifM, unlessM, when) import Data.Foldable (for_) +import Data.List (isPrefixOf, isInfixOf) import Development.Shake import GHC.Stack (HasCallStack) import Language.Haskell.TH (nameBase) import System.Console.ANSI (setSGR) import System.Directory (getCurrentDirectory, setCurrentDirectory) -import System.FilePath (isDrive, (), takeDirectory) +import System.FilePath (isDrive, (), takeDirectory, replaceFileName) import System.Process (readProcess) import Paths_bittide_instances @@ -70,6 +72,44 @@ In the future we might be more precise in our source discovery process. handle this case in Shake either. -} +-- | "Negative clock pins" used to work around: +-- +-- https://github.com/clash-lang/clash-compiler/issues/2518 +-- +-- List obtained by scanning our board's XDC file: +-- +-- @ +-- grep -i -E '(clk|clock)' KCU105_Rev1.0_02292016.xdc | grep -o -i -E '".*_n"' | sort | uniq +-- @ +-- +negativeClocks :: [String] +negativeClocks = + [ "CLK_125MHZ_N" + , "FMC_HPC_CLK0_M2C_N" + , "FMC_HPC_CLK1_M2C_N" + , "FMC_HPC_GBTCLK0_M2C_C_N" + , "FMC_HPC_GBTCLK1_M2C_C_N" + , "FMC_LPC_CLK0_M2C_N" + , "FMC_LPC_CLK1_M2C_N" + , "FMC_LPC_GBTCLK0_M2C_C_N" + , "MGT_SI570_CLOCK_C_N" + , "PCIE_CLK_QO_N" + , "REC_CLOCK_C_N" + , "SGMIICLK_N" + , "SMA_MGT_REFCLK_C_N" + , "SYSCLK_300_N" + , "USER_SI570_CLOCK_N" + , "USER_SMA_CLOCK_N" + ] + +-- | Does this line (from an XDC file) create a clock from 'negativeClocks'? +isNegativeCreateClock :: String -> Bool +isNegativeCreateClock line = + "create_clock" `isPrefixOf` line && containsNegativeClock + where + containsNegativeClock = or [nm `isInfixOf` line | nm <- tclNegativeClocks] + tclNegativeClocks = ["{" <> nm <> "}" | nm <- negativeClocks] + -- | Get all files whose changes will trigger an HDL rebuild. Because we lack a -- reliable way to determine which files should trigger a rebuild, this function -- returns a (very) pessimistic list: all files in the project's directory, @@ -201,7 +241,7 @@ shakeOpts :: ShakeOptions shakeOpts = shakeOptions { shakeFiles = buildDir , shakeChange = ChangeDigest - , shakeVersion = "6" + , shakeVersion = "7" } -- | Run Vivado on given TCL script @@ -284,6 +324,7 @@ main = do -- TODO: Dehardcode these paths. They're currently hardcoded in both the -- TCL and here, which smells. manifestPath = getManifestLocation clashBuildDir targetName + clashSdcPath = replaceFileName manifestPath (nameBase targetName <> ".sdc") synthesisDir = vivadoBuildDir show targetName checkpointsDir = synthesisDir "checkpoints" netlistDir = synthesisDir "netlist" @@ -310,12 +351,6 @@ main = do bitstreamPath = synthesisDir "bitstream.bit" probesPath = synthesisDir "probes.ltx" - postSynthMethodologyPath = reportDir "post_synth_methodology.rpt" - postSynthTimingSummaryPath = reportDir "post_synth_timing_summary.rpt" - - postPlaceMethodologyPath = reportDir "post_place_methodology.rpt" - postPlaceTimingSummaryPath = reportDir "post_place_timing_summary.rpt" - postRouteMethodologyPath = reportDir "post_route_methodology.rpt" postRouteTimingSummaryPath = reportDir "post_route_timing_summary.rpt" postRouteTimingPath = reportDir "post_route_timing.rpt" @@ -347,6 +382,11 @@ main = do -- printed in bold text. Reset manually: liftIO (setSGR []) + -- XXX: Workaround https://github.com/clash-lang/clash-compiler/issues/2518 + sdc0 <- liftIO (force . lines <$> readFile clashSdcPath) + let sdc1 = filter (not . isNegativeCreateClock) sdc0 + writeFileChanged clashSdcPath (unlines sdc1) + produces [path] -- Synthesis @@ -381,11 +421,6 @@ main = do -- also depend on the dependencies' manifest files, etc. need [runSynthTclPath, manifestPath] vivadoFromTcl runSynthTclPath - liftIO $ - meetsDrcOrError - postSynthMethodologyPath - postSynthTimingSummaryPath - postSynthCheckpointPath -- Placement runPlaceTclPath %> \path -> do @@ -394,11 +429,6 @@ main = do (postPlaceCheckpointPath : placeReportPaths) |%> \_ -> do need [runPlaceTclPath, postSynthCheckpointPath] vivadoFromTcl runPlaceTclPath - liftIO $ - meetsDrcOrError - postPlaceMethodologyPath - postPlaceTimingSummaryPath - postPlaceCheckpointPath -- Routing runRouteTclPath %> \path -> do @@ -408,11 +438,27 @@ main = do need [runRouteTclPath, postPlaceCheckpointPath] vivadoFromTcl runRouteTclPath - liftIO $ - meetsDrcOrError - postRouteMethodologyPath - postRouteTimingSummaryPath - postRouteCheckpointPath + -- Design should meet design rule checks (DRC). + liftIO $ unlessM + ( liftA2 + (&&) + (meetsTiming postRouteMethodologyPath) + (meetsTiming postRouteTimingSummaryPath) + ) + (error [I.i| + Design did not meet design rule checks (DRC). Check out the timing summary at: + + #{postRouteTimingSummaryPath} + + Check out the methodology report at: + + #{postRouteMethodologyPath} + + You can investigate interactively by opening the latest checkpoint with Vivado: + + vivado #{postRouteCheckpointPath} + + |]) -- Design should meet timing post routing. Note that this is not a -- requirement after synthesis as many of the optimizations only follow diff --git a/bittide-instances/bittide-instances.cabal b/bittide-instances/bittide-instances.cabal index 03bdf47c0..30df26c99 100644 --- a/bittide-instances/bittide-instances.cabal +++ b/bittide-instances/bittide-instances.cabal @@ -134,6 +134,7 @@ executable shake , clash-ghc , clash-lib , cryptohash-sha256 + , deepseq , directory , extra , Glob