From de5e00b81ed026312717eafdbce9d08fcb1b96c2 Mon Sep 17 00:00:00 2001 From: Peter Lebbing Date: Fri, 29 Sep 2023 14:54:13 +0200 Subject: [PATCH] Document safe PLL's All the examples in the documentation are adjusted to use safe PLL's. Documents all the changes in this PR --- ...3-01T12_18_28+01_00_xilinx_clocking_wizard | 3 - changelog/2023-07-05T16_49_21+02_00_diffclock | 8 +- changelog/2023-09-11T17_11_44+02_00_safe_pll | 21 + clash-lib/src/Clash/DataFiles.hs | 7 +- .../src/Clash/Annotations/TopEntity.hs | 38 +- clash-prelude/src/Clash/Explicit/Reset.hs | 11 +- clash-prelude/src/Clash/Intel/ClockGen.hs | 492 ++++++++++++------ clash-prelude/src/Clash/Signal.hs | 12 +- clash-prelude/src/Clash/Signal/Internal.hs | 3 + clash-prelude/src/Clash/Tutorial.hs | 19 +- clash-prelude/src/Clash/Xilinx/ClockGen.hs | 352 +++++++++++-- examples/Blinker.hs | 40 +- 12 files changed, 738 insertions(+), 268 deletions(-) delete mode 100644 changelog/2023-03-01T12_18_28+01_00_xilinx_clocking_wizard create mode 100644 changelog/2023-09-11T17_11_44+02_00_safe_pll diff --git a/changelog/2023-03-01T12_18_28+01_00_xilinx_clocking_wizard b/changelog/2023-03-01T12_18_28+01_00_xilinx_clocking_wizard deleted file mode 100644 index 0a31496c1a..0000000000 --- a/changelog/2023-03-01T12_18_28+01_00_xilinx_clocking_wizard +++ /dev/null @@ -1,3 +0,0 @@ -CHANGED: Remove `Asynchronous` constraint from Xilinx `clockWizard` and `clockWizardDifferential`. Originally intended to signal that these functions react synchronously to the incoming reset and that the outgoing lock signal is an asynchronous signal. Since synchronous reset signals are a subset of asynchronous reset signals this constraint on the input is vacuous. The constraint on the lock output does not convey this information at all and is wrong. Note that it is still necessary to synchronize the lock output in your design. -FIXED: Fix Xilinx ClockGen primitives. The port names of the Xilinx ClockGen primitives are now lowercase. The type of the lock output of Xilinx `clockWizard` and `clockWizardDifferential` is now `Signal Bool` instead of `Enable`, which was not the correct type here as a circuit should be kept in reset while the clock is stabilizing. This also fixes a polarity mismatch between hardware (and HDL simulation) and Haskell simulation. The lock signal is now also correctly resampled to the output domain. -ADD: Add Tcl generation of Xilinx `clockWizard` and `clockWizardDifferential`. This moves the responsibility of MMCM component generation from the user to `clashConnector.tcl`. diff --git a/changelog/2023-07-05T16_49_21+02_00_diffclock b/changelog/2023-07-05T16_49_21+02_00_diffclock index 3e04a84860..8837ecedbe 100644 --- a/changelog/2023-07-05T16_49_21+02_00_diffclock +++ b/changelog/2023-07-05T16_49_21+02_00_diffclock @@ -1,9 +1,3 @@ ADDED: A new clock type `DiffClock` is introduced to signify a differential clock signal that is passed to the design on two ports in antiphase. This is -used by the Xilinx `clockWizardDifferential` IP generator. - -CHANGED: Xilinx `clockWizardDifferential` now gets its input clock as a -`DiffClock` type; use `clockToDiffClock` to generate this in your test bench -if needed. Previously, the function received two clock inputs, but this -generated `create_clock` statements in the top-level SDC file for both phases -which is incorrect. +used by the differential Xilinx clock wizards in `Clash.Xilinx.ClockGen`. diff --git a/changelog/2023-09-11T17_11_44+02_00_safe_pll b/changelog/2023-09-11T17_11_44+02_00_safe_pll new file mode 100644 index 0000000000..9c4d39eb0a --- /dev/null +++ b/changelog/2023-09-11T17_11_44+02_00_safe_pll @@ -0,0 +1,21 @@ +REMOVED: The module `Clash.Clocks.Deriving` has been removed. + +FIXED: `altpll` and `alteraPll` in `Clash.Intel.ClockGen` now account for the input domain's `ResetPolarity`. Before this fix, the reset was always interpreted as an active-high signal. + +CHANGED: The IP core generators in `Clash.Intel.ClockGen` now declare that their input domain needs to have asynchronous resets (`HasAsynchronousReset`), as the functions react asynchronously to their reset input and thus need to be glitch-free. The functions marked `unsafe` do not have this constraint; instead, the function documentation calls attention to the requirement. + +INTERNAL NEW: `Clash.Primitives.DSL.declareN`, a companion to `declare` which declares multiple signals in one go. + +DEPRECATED: The functions `altpll` and `alteraPll` in `Clash.Intel.ClockGen` have been deprecated because they are unsafe to use while this is not apparent from the name. The `locked` output signal of these functions is an asynchronous signal which needs to be synchronized before it can be used (something the examples did in fact demonstrate). For the common use case, new functions are available, named `altpllSync` and `alteraPllSync`. These functions are safe. For advanced use cases, the old functionality can be obtained through `unsafeAltpll` and `unsafeAlteraPll`. + +NEW: `altpllSync` and `alteraPllSync` in `Clash.Intel.ClockGen`. These replace the deprecated functions without the `Sync` suffix. Unlike the old functions, these functions are safe to use and have a reset signal for each output domain that can be used to keep the domain in reset while the clock output stabilizes. All PLL functions now also support multiple clock outputs like the old `alteraPll` did. + +CHANGED: The wizards in `Clash.Xilinx.ClockGen` have been completely overhauled. The original functions were unsafe and broken in several ways. See the documentation in `Clash.Xilinx.ClockGen` for how to use the new functions. Significant changes are: + * `clockWizard` and `clockWizardDifferential` now output a `Clock` and a `Reset` which can be directly used by logic. Previously, it outputted a clock and an asynchronous `locked` signal which first needed to be synchronized before it could be used (hence the old function being unsafe). Additionally, the original `locked` signal was strange: it mistakenly was an `Enable` instead of a `Signal dom Bool` and there was a polarity mismatch between Clash simulation and HDL. The `locked` signal was also not resampled to the output domain in Clash simulation. + * There are new functions `unsafeClockWizard` and `unsafeClockWizardDifferential` for advanced use cases which directly expose the `locked` output of the wizard. + * All clock generators now have the option to output multiple clocks from a single instance. + * `clockWizardDifferential` now gets its input clock as a `DiffClock` type; use `clockToDiffClock` to generate this in your test bench if needed. Previously, the function received two clock inputs, but this generated `create_clock` statements in the top-level SDC file for both phases which is incorrect. + * A constraint was removed: The /output/ clock domain no longer requires asynchronous resets. This was originally intended to signal that the outgoing lock signal is an asynchronous signal. The constraint does not convey this information at all and is wrong; it also prevents using synchronous resets in the circuit as recommended by Xilinx. Note that if you use the `unsafe` functions, it is still necessary to synchronize the `locked` output in your design. + * The port names of the primitives in HDL are now correctly lower case. + * Add Tcl generation. This moves the responsibility of MMCM component generation from the user to `clashConnector.tcl`, which can be found in [`clash-lib:Clash.DataFiles`](https://hackage.haskell.org/package/clash-lib-1.8.0/docs/Clash-DataFiles.html). + * The wizards now use the user-provided name as the name of the /instance/ rather than the name of the /IP core/. This change was also done for `Clash.Intel.ClockGen` in Clash v1.2.0 in March 2020, when Clash started generating Intel Qsys files. Before that, the user needed to generate a Qsys component manually. Now, in Clash v1.8.0, we also generate the Tcl for Xilinx wizards. When the user is responsible for creating the IP core, it makes sense to always set the component name to the user-provided value. But when that is also generated by Clash, that is no longer needed. Allowing users to set the instance name instead makes it possible to match on the instance in SDC files and such. diff --git a/clash-lib/src/Clash/DataFiles.hs b/clash-lib/src/Clash/DataFiles.hs index 2b6b3ad0e8..6e7fbfcb94 100644 --- a/clash-lib/src/Clash/DataFiles.hs +++ b/clash-lib/src/Clash/DataFiles.hs @@ -46,9 +46,10 @@ import Paths_clash_lib (getDataFileName) > route_design > write_bitstream ${clash::topEntity}.bit - @clash-cores:Clash.Cores.Xilinx@ modules make use of the IP instantiating - functionality; XDC metadata functionality is not currently used as the IP is - already packaged with correct constraints by Vivado. + "Clash.Xilinx.ClockGen" and @clash-cores:Clash.Cores.Xilinx@ + modules make use of the IP instantiating functionality; XDC metadata + functionality is not currently used as the IP is already packaged with correct + constraints by Vivado. More documentation about the Tcl Connector and the Clash\<->Tcl API will be made available later. diff --git a/clash-prelude/src/Clash/Annotations/TopEntity.hs b/clash-prelude/src/Clash/Annotations/TopEntity.hs index 61c769a599..7c71c3457a 100644 --- a/clash-prelude/src/Clash/Annotations/TopEntity.hs +++ b/clash-prelude/src/Clash/Annotations/TopEntity.hs @@ -1,7 +1,7 @@ {-| Copyright : (C) 2015-2016, University of Twente, 2017 , Google Inc., - 2021-2022, QBayLogic B.V. + 2021-2023, QBayLogic B.V. License : BSD2 (see the file LICENSE) Maintainer : QBayLogic B.V. @@ -62,7 +62,11 @@ module Blinker where import Clash.Prelude import Clash.Intel.ClockGen -'Clash.Explicit.Signal.createDomain' vSystem{vName=\"DomInput\", vPeriod=20000} +-- Define a synthesis domain with a clock with a period of 20000 /ps/. Signal +-- coming from the reset button is low when pressed, and high when not pressed. +'Clash.Explicit.Signal.createDomain' + vSystem{vName=\"DomInput\", vPeriod=20000, vResetPolarity=ActiveLow} +-- Define a synthesis domain with a clock with a period of 50000 /ps/. 'Clash.Explicit.Signal.createDomain' vSystem{vName=\"Dom50\", vPeriod=50000} topEntity @@ -75,35 +79,19 @@ topEntity clk20 rstBtn enaBtn modeBtn = 'Clash.Signal.exposeClockResetEnable' ('Clash.Prelude.mealy' blinkerT initialStateBlinkerT . 'Clash.Prelude.isRising' 1) clk50 - rstSync + rst50 enaBtn modeBtn where -- Start with the first LED turned on, in rotate mode, with the counter on zero initialStateBlinkerT = (1, False, 0) - -- Signal coming from the reset button is low when pressed, and high when - -- not pressed. We convert this signal to the polarity of our domain with - -- /unsafeFromActiveLow/. - rst = 'Clash.Signal.unsafeFromActiveLow' ('Clash.Signal.unsafeFromReset' rstBtn) - - -- Instantiate a PLL: this stabilizes the incoming clock signal and indicates - -- when the signal is stable. We're also using it to transform an incoming - -- clock signal running at 20 MHz to a clock signal running at 50 MHz. - (clk50, pllStable) = - 'Clash.Intel.ClockGen.altpll' - \@Dom50 - (SSymbol @"altpll50") - clk20 - rst - - -- Synchronize reset to clock signal coming from PLL. We want the reset to - -- remain active while the PLL is NOT stable, hence the conversion with - -- /unsafeFromActiveLow/ - rstSync = - 'Clash.Prelude.resetSynchronizer' - clk50 - ('Clash.Signal.unsafeFromActiveLow' pllStable) + -- Instantiate a PLL: this stabilizes the incoming clock signal and releases + -- the reset output when the signal is stable. We're also using it to + -- transform an incoming clock signal running at 20 MHz to a clock signal + -- running at 50 MHz. Since the signature of topEntity already specifies the + -- Dom50 domain, we don't need any type signatures to specify the domain here. + (clk50, rst50) = 'Clash.Intel.ClockGen.altpllSync' clk20 rstBtn blinkerT :: (BitVector 8, Bool, Index 16650001) diff --git a/clash-prelude/src/Clash/Explicit/Reset.hs b/clash-prelude/src/Clash/Explicit/Reset.hs index 15c6a878a9..9945c125e1 100644 --- a/clash-prelude/src/Clash/Explicit/Reset.hs +++ b/clash-prelude/src/Clash/Explicit/Reset.hs @@ -158,9 +158,14 @@ unsafeAndReset (unsafeFromReset -> rst0) (unsafeFromReset -> rst1) = -- === __Example 2__ -- Similar to /Example 1/ this circuit detects a rising bit (i.e., a transition -- from 0 to 1) in a given argument. It takes a clock that is not stable yet and --- a reset singal that is not synchronized to any other signals. It stabalizes +-- a reset signal that is not synchronized to any other signals. It stabilizes -- the clock and then synchronizes the reset signal. -- +-- +-- Note that the function 'Clash.Intel.ClockGen.altpllSync' provides this +-- functionality in a convenient form, obviating the need for +-- @resetSynchronizer@ for this use case. +-- -- @ -- topEntity -- :: Clock System @@ -168,8 +173,8 @@ unsafeAndReset (unsafeFromReset -> rst0) (unsafeFromReset -> rst1) = -- -> Signal System Bit -- -> Signal System (BitVector 8) -- topEntity clk rst key1 = --- let (pllOut,pllStable) = altpll (SSymbol @"altpll50") clk rst --- rstSync = 'resetSynchronizer' pllOut (unsafeToActiveHigh pllStable) +-- let (pllOut,pllStable) = unsafeAltpll clk rst +-- rstSync = 'resetSynchronizer' pllOut (unsafeFromActiveLow pllStable) -- in exposeClockResetEnable leds pllOut rstSync enableGen -- where -- key1R = isRising 1 key1 diff --git a/clash-prelude/src/Clash/Intel/ClockGen.hs b/clash-prelude/src/Clash/Intel/ClockGen.hs index cf5026fa31..0ff0f3cab3 100644 --- a/clash-prelude/src/Clash/Intel/ClockGen.hs +++ b/clash-prelude/src/Clash/Intel/ClockGen.hs @@ -5,24 +5,24 @@ Copyright : (C) 2017-2018, Google Inc License : BSD2 (see the file LICENSE) Maintainer : QBayLogic B.V. -PLL and other clock-related components for Intel (Altera) FPGAs +This module contains functions for instantiating clock generators on Intel +FPGA's. -A PLL generates a stable clock signal for your circuit at a selectable -frequency. +We suggest you use a clock generator even if your oscillator runs at the +frequency you want to run your circuit at. -If you haven't determined the frequency you want the circuit to run at, the -predefined 100 MHz domain `Clash.Signal.System` can be a good starting point. -The datasheet for your FPGA specifies lower and upper limits, but the true -maximum frequency is determined by your circuit. The 'altpll' and 'alteraPll' -components below show an example for when the oscillator connected to the FPGA -runs at 50 MHz. If the oscillator runs at 100 MHz, change @DomInput@ to: +A clock generator generates a stable clock signal for your design at a +configurable frequency. A clock generator in an FPGA is frequently referred to +as a PLL (Phase-Locked Loop). Intel also refers to them as PLL's in general but +because this is not consistently the case among FPGA vendors, we choose the +more generic term /clock generator/. -@ -'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"DomInput\", vPeriod=10000} -@ - -We suggest you always use a PLL even if your oscillator runs at the frequency -you want to run your circuit at. +For most use cases, you would create two or more synthesis domains describing +the oscillator input and the domains you wish to use in your design, and use +the [regular functions](#g:regular) below to generate the clocks and resets of +the design from the oscillator input. There are use cases not covered by this +simpler approach, and the [unsafe functions](#g:unsafe) are provided as a means +to build advanced reset managers for the output domains. -} {-# LANGUAGE CPP #-} @@ -30,11 +30,32 @@ you want to run your circuit at. {-# LANGUAGE TypeFamilies #-} module Clash.Intel.ClockGen - ( altpllSync + ( -- * Choosing domains + -- $domains + + -- ** Caution: actual output frequency + -- $caution + + -- * Using + -- $using + + -- ** Example + -- $example + + -- ** Type checking errors + -- $error + + -- * Regular functions #regular# + altpllSync , alteraPllSync + -- * Unsafe functions #unsafe# + -- $unsafe + + -- ** Example + -- $unsafe_example , unsafeAltpll , unsafeAlteraPll - -- ** Deprecated + -- * Deprecated , altpll , alteraPll ) where @@ -49,78 +70,275 @@ import Clash.Promoted.Symbol (SSymbol) import Clash.Signal.Internal (Signal, Clock, Reset, KnownDomain, HasAsynchronousReset) +{- $domains +Synthesis domains are denoted by the type-parameter +@dom :: t'Clash.Signal.Domain'@ as occurring in for instance +@t'Clash.Signal.Signal' dom a@; see "Clash.Signal" for more information. For +each domain, there is only a single clock signal which clocks that domain; +mixing clock signals is a design error. Conversely, it is possible to clock +multiple domains using the same clock signal, in complex designs. + +For the clock generator inputs, create a domain with the correct clock frequency +and reset polarity. For instance, if the clock input is a free-running clock at +a frequency of 50 MHz (a period of 20 ns or 20,000 ps), and the reset input +connected to the clock generator is /active-low/, the following will instantiate +the required input domain: + +@ +'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"DomInput\", vPeriod=20000, vResetPolarity='Clash.Signal.ActiveLow'} +@ + +If you haven't determined the frequency you want the design to run at, the +predefined 100 MHz domain t'Clash.Signal.System' can be a good starting point. +The datasheet for your FPGA specifies lower and upper limits, but the true +maximum frequency is determined by your design. + +Supposing you need a clock running at 150 MHz for your design, the following +will instantiate a suitable domain: + +@ +'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"Dom150\", vPeriod='Clash.Signal.hzToPeriod' 150e6} +@ + +As the clock generator always reacts asynchronously to its reset input, it will +require that the @DomInput@ domain has asynchronous resets. The /unsafe/ +functions below do not enforce this requirement on the domain (but they still +react asynchronously). +-} + +{- $caution +The clock generator in the FPGA is limited in which clock frequencies it can +generate, especially when one clock generator has multiple outputs. The clock +generator will pick the attainable frequency closest to the requested frequency +(or possibly fail to synthesize). You can check the frequency that the IP core +chose by loading your design into the Quartus GUI. In the /Project Navigator/, +choose the /Hierarchy/ view and find your clock generator instance. +Double-click the instance to open Platform Designer and choose /Edit/ +/Parameters.../. In the /Output Clocks/ page, the relevant column is /Actual/ +/Settings/. If the actual value differs, copy the actual value back to the +Clash design. +-} + +{- $using +The functions in this module will instantiate an Intel IP core for a clock +generator with 1 reference clock input and a reset input, and one or more output +clocks and a @locked@ output. + +The [regular functions](#g:regular) incorporate 'Clash.Signal.resetSynchronizer' +to convert the @locked@ output port into a proper 'Reset' signal for the domains +which will keep the circuit in reset while the clock is still stabilizing. + +The clock generator will react asynchronously to the incoming reset input. When +the reset input is asserted, the clock generator's @locked@ output will +deassert, in turn causing the 'Reset' output(s) of these functions to assert. + +You can use 'Clash.Magic.setName' to give the IP instance a specific name, which +can be useful if you need to refer to the instance in Synopsys Design +Constraints files. + +The output of the function for /n/ output clocks is a /2n/-tuple with clock and +reset outputs. The compiler needs to be able to fully determine the types of the +individual tuple elements from the context; the clock generator function itself +will not constrain them. If the types of the tuple elements cannot be inferred, +you can use pattern type signatures to specify the types. Supposing the +referenced domains have been created with 'Clash.Signal.createDomain', an +instance with a single output clock can be instantiated using: + +@ +(clk150 :: 'Clock' Dom150, rst150 :: 'Reset' Dom150) = 'alteraPllSync' clkIn rstIn +@ + +An instance with two clocks can be instantiated using + +@ +( clk100 :: 'Clock' Dom100 + , rst100 :: 'Reset' Dom100 + , clk150 :: 'Clock' Dom150 + , rst150 :: 'Reset' Dom150) = 'alteraPllSync' clkIn rstIn +@ + +and so on up to 18 clocks, following the general pattern @('Clock' dom1, 'Reset' +dom1, 'Clock' dom2, 'Reset' dom2, ..., 'Clock' dom/n/, 'Reset' dom/n/)@. + +These examples show 'alteraPllSync' but it is the same for 'altpllSync' except +that it supports up to 5 clocks. + +If you need access to the @locked@ output to build a more advanced reset +manager, you should use the [unsafe functions](#g:unsafe) instead. +-} + +{- $example + +When the oscillator connected to the FPGA runs at 50 MHz and the external reset +signal is /active-low/, this will generate a 150 MHz clock for use by the +circuit: + +@ +'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"DomInput\", vPeriod=20000, vResetPolarity='Clash.Signal.ActiveLow'} +'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"Dom150\", vPeriod='Clash.Signal.hzToPeriod' 150e6} + +topEntity + :: 'Clock' DomInput + -> 'Reset' DomInput + -> t'Clash.Signal.Signal' Dom150 Int + -> t'Clash.Signal.Signal' Dom150 Int +topEntity clkIn rstIn = 'Clash.Signal.exposeClockResetEnable' (register 0) clk rst 'Clash.Signal.enableGen' + where + (clk, rst) = 'alteraPllSync' clkIn rstIn +@ +-} + +{- $error +When type checking cannot infer the types of the tuple elements, or they have +the wrong type, the GHC compiler will complain about satisfying @NumOutClocks@. +The error message on GHC 9.4 and up is: + +@ + • Cannot satisfy: clash-prelude-[...]:Clash.Clocks.Internal.NumOutClocks + (clash-prelude-[...]:Clash.Clocks.Internal.ClocksSyncClocksInst + ([...]) + DomInput) <= 18 + • In the expression: alteraPllSync clkIn rstIn +@ + +On older GHC versions, the error message is: + +@ + • Couldn't match type ‘clash-prelude-[...]:Clash.Clocks.Internal.NumOutClocks + (clash-prelude-[...]:Clash.Clocks.Internal.ClocksSyncClocksInst + ([...]) + DomInput) + <=? 18’ + with ‘'True’ + arising from a use of ‘alteraPllSync’ + • In the expression: alteraPllSync clkIn rstIn +@ + +The above error message is also emitted when trying to instantiate more than 18 +output clocks, as it will fail to find an instance. As 'altpllSync' supports no +more than 5 clocks, trying to instantiate between 6 and 18 output clocks will +also cause a type checking error. On GHC 9.4 and up, the error for attempting to +instantiate 6 clocks is: + +@ + • Cannot satisfy: 6 <= 5 + • In the expression: altpllSync clkIn rstIn +@ + +On older GHC versions, the error message is less clear: + +@ + • Couldn't match type ‘'False’ with ‘'True’ + arising from a use of ‘altpllSync’ + • In the expression: altpllSync clkIn rstIn +@ +-} + +{- $unsafe +These functions are provided for the cases where the [regular +functions](#g:regular) cannot provide the desired behavior, like when +implementing certain advanced reset managers. These functions directly expose +the /asynchronous/ @locked@ output of the clock generator, which will assert +when the output clocks are stable. @locked@ is usually connected to reset +circuitry to keep the circuit in reset while the clock is still stabilizing. + +The output of the function for /n/ output clocks is an /n+1/-tuple with /n/ +clock outputs and a @locked@ signal. The compiler needs to be able to fully +determine the types of the individual tuple elements from the context; the clock +generator function itself will not constrain them. If the types of the tuple +elements cannot be inferred, you can use pattern type signatures to specify the +types. Supposing the referenced domains have been created with +'Clash.Signal.createDomain', an instance with a single output clock can be +instantiated using: + +@ +(clk150 :: 'Clock' Dom150, locked :: t'Clash.Signal.Signal' Dom150 'Bool') = 'unsafeAlteraPll' clkIn rstIn +@ + +An instance with two clocks can be instantiated using + +@ +(clk100 :: 'Clock' Dom100 + , clk150 :: 'Clock' Dom150 + , locked :: t'Clash.Signal.Signal' Dom100 'Bool') = 'unsafeAlteraPll' clkIn rstIn +@ + +and so on up to 18 clocks, following the general pattern @('Clock' dom1, 'Clock' +dom2, ..., 'Clock' dom/n/, t'Clash.Signal.Signal' pllLock Bool)@. + +These examples show 'unsafeAlteraPll' but it is the same for 'unsafeAltpll' +except that it supports up to 5 clocks. + +Though the @locked@ output is specified as a @t'Clash.Signal.Signal' pllLock +'Bool'@, it is an asynchronous signal and will need to be synchronized before it +can be used as a (reset) signal. While in the examples above the +@locked@ output has been assigned the domain of one of the output clocks, the +domain @pllLock@ is left unrestricted. If the lock signal is to be used in +multiple domains, the @pllLock@ domain should probably be set to @domIn@ (the +domain of the input clock and reset). While in HDL +'Clash.Explicit.Signal.unsafeSynchronizer' is just a wire, in Haskell simulation +it does actually resample the signal, and by setting @pllLock@ to @domIn@, there +is no resampling of the simulated lock signal. The simulated lock signal is +simply the inverse of the reset input: @locked@ is asserted whenever the reset +input is deasserted and vice versa. +-} + +{- $unsafe_example +@ +'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"DomInput\", vPeriod=20000, vResetPolarity='Clash.Signal.ActiveLow'} +'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"Dom150\", vPeriod='Clash.Signal.hzToPeriod' 150e6} + +topEntity + :: 'Clock' DomInput + -> 'Reset' DomInput + -> t'Clash.Signal.Signal' Dom150 Int + -> t'Clash.Signal.Signal' Dom150 Int +topEntity clkIn rstIn = 'Clash.Signal.exposeClockResetEnable' (register 0) clk rst 'Clash.Signal.enableGen' + where + (clk, locked) = 'unsafeAlteraPll' clkIn rstIn + rst = 'Clash.Signal.resetSynchronizer' clk ('Clash.Signal.unsafeFromActiveLow' locked) +@ + +'Clash.Signal.resetSynchronizer' will keep the reset asserted when @locked@ is +'False', hence the use of @'Clash.Signal.unsafeFromActiveLow' locked@. +-} + +-- | Instantiate an Intel clock generator corresponding to the Intel/Quartus +-- \"ALTPLL\" IP core (Arria GX, Arria II, Stratix IV, Stratix III, Stratix II, +-- Stratix, Cyclone 10 LP, Cyclone IV, Cyclone III, Cyclone II, Cyclone) with 1 +-- reference clock input and a reset input and 1 to 5 output clocks and a +-- @locked@ output. +-- +-- This function incorporates 'Clash.Signal.resetSynchronizer's to convert the +-- @locked@ output port into proper 'Reset' signals for the output domains which +-- will keep the circuit in reset while the clock is still stabilizing. +-- +-- See also the [ALTPLL (Phase-Locked Loop) IP Core User Guide](https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_altpll.pdf) altpllSync :: forall t domIn . ( HasAsynchronousReset domIn , ClocksSyncCxt t domIn , NumOutClocksSync t domIn <= 5 ) => + -- | Free running clock (e.g. a clock pin connected to a crystal oscillator) Clock domIn -> + -- | Reset for the clock generator Reset domIn -> t altpllSync clkIn rstIn = clocksResetSynchronizer (unsafeAltpll clkIn rstIn) clkIn --- | A clock source that corresponds to the Intel/Quartus \"ALTPLL\" component --- (Arria GX, Arria II, Stratix IV, Stratix III, Stratix II, Stratix, --- Cyclone 10 LP, Cyclone IV, Cyclone III, Cyclone II, Cyclone) --- with settings to provide a stable 'Clock' from a single free-running input --- --- Only works when configured with: --- --- * 1 reference clock --- * 1 output clock --- * a reset input port --- * a locked output port --- --- The PLL lock output is asserted when the clock is stable, and is usually --- connected to reset circuitry to keep the circuit in reset while the clock is --- still stabilizing. --- --- See also the [ALTPLL (Phase-Locked Loop) IP Core User Guide](https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_altpll.pdf) --- --- === Example --- --- ==== Using a PLL --- --- When the oscillator connected to the FPGA runs at 50 MHz and the external --- reset signal is /active low/, this will generate a 100 MHz clock for the --- @'Clash.Signal.System'@ domain: --- --- @ --- 'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"DomInput\", vPeriod=20000} --- --- topEntity --- :: 'Clock' DomInput --- -> 'Signal' DomInput 'Bool' --- -> [...] --- topEntity clkInp rstInp = [...] --- where --- (clk, pllStable) = --- 'altpll' \@'Clash.Signal.System' ('SSymbol' \@\"altpll50to100\") clkInp --- ('Clash.Signal.unsafeFromActiveLow' rstInp) --- rst = 'Clash.Signal.resetSynchronizer' clk ('Clash.Signal.unsafeFromActiveLow' pllStable) --- @ --- --- 'Clash.Signal.resetSynchronizer' will keep the reset asserted when --- @pllStable@ is 'False', hence the use of --- @'Clash.Signal.unsafeFromActiveLow' pllStable@. Your circuit will have --- signals of type @'Signal' 'Clash.Signal.System'@ and all the clocks and --- resets of your components will be the @clk@ and @rst@ signals generated here --- (modulo local resets, which will be based on @rst@ or never asserted at all --- if the component doesn't need a reset). --- --- ==== Specifying the output frequency --- --- If you don't have a top-level type signature specifying the output clock --- domain, you can use type applications to specify it, e.g.: --- --- @ --- 'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"Dom100MHz\", vPeriod=10000} --- --- -- outputs a clock running at 100 MHz --- (clk100, pllLocked) = 'altpll' \@Dom100MHz ('SSymbol' \@\"altpll50to100\") clk50 rst50 --- @ +-- | Instantiate an Intel clock generator corresponding to the Intel/Quartus +-- \"ALTPLL\" IP core (Arria GX, Arria II, Stratix IV, Stratix III, Stratix II, +-- Stratix, Cyclone 10 LP, Cyclone IV, Cyclone III, Cyclone II, Cyclone) with 1 +-- reference clock input and a reset input and 1 output clock and a @locked@ +-- output. +-- +-- This function is deprecated because the @locked@ output is an asynchronous +-- signal. This means the user is required to add a synchronizer and as such +-- this function is unsafe. The common use case is now covered by 'altpllSync' +-- and 'unsafeAltpll' offers the functionality of this deprecated function for +-- advanced use cases. altpll :: forall domOut domIn name . ( HasAsynchronousReset domIn @@ -132,14 +350,24 @@ altpll :: SSymbol name -> -- | Free running clock (e.g. a clock pin connected to a crystal oscillator) Clock domIn -> - -- | Reset for the PLL + -- | Reset for the clock generator Reset domIn -> - -- | (Stable PLL clock, PLL lock) + -- | (Output clock, Clock generator locked) (Clock domOut, Signal domOut Bool) altpll _ = setName @name unsafeAltpll {-# INLINE altpll #-} {-# DEPRECATED altpll "This function is unsafe. Please see documentation of the function for alternatives." #-} +-- | Instantiate an Intel clock generator corresponding to the Intel/Quartus +-- \"ALTPLL\" IP core (Arria GX, Arria II, Stratix IV, Stratix III, Stratix II, +-- Stratix, Cyclone 10 LP, Cyclone IV, Cyclone III, Cyclone II, Cyclone) with 1 +-- reference clock input and a reset input and 1 to 5 output clocks and a +-- @locked@ output. +-- +-- __NB__: Because the clock generator reacts asynchronously to the incoming +-- reset input, the signal __must__ be glitch-free. +-- +-- See also the [ALTPLL (Phase-Locked Loop) IP Core User Guide](https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_altpll.pdf) unsafeAltpll :: forall t domIn . ( KnownDomain domIn @@ -147,7 +375,9 @@ unsafeAltpll :: , ClocksCxt t , NumOutClocks t <= 5 ) => + -- | Free running clock (e.g. a clock pin connected to a crystal oscillator) Clock domIn -> + -- | Reset for the clock generator Reset domIn -> t unsafeAltpll = clocks @@ -155,90 +385,38 @@ unsafeAltpll = clocks {-# CLASH_OPAQUE unsafeAltpll #-} {-# ANN unsafeAltpll hasBlackBox #-} +-- | Instantiate an Intel clock generator corresponding to the Intel/Quartus +-- \"Altera PLL\" IP core (Arria V, Stratix V, Cyclone V) with 1 reference clock +-- input and a reset input and 1 to 18 output clocks and a @locked@ output. +-- +-- This function incorporates 'Clash.Signal.resetSynchronizer's to convert the +-- @locked@ output port into proper 'Reset' signals for the output domains which +-- will keep the circuit in reset while the clock is still stabilizing. +-- +-- See also the [Altera Phase-Locked Loop (Altera PLL) IP Core User Guide](https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/altera_pll.pdf) alteraPllSync :: forall t domIn . ( HasAsynchronousReset domIn , ClocksSyncCxt t domIn , NumOutClocksSync t domIn <= 18 ) => + -- | Free running clock (e.g. a clock pin connected to a crystal oscillator) Clock domIn -> + -- | Reset for the clock generator Reset domIn -> t alteraPllSync clkIn rstIn = clocksResetSynchronizer (unsafeAlteraPll clkIn rstIn) clkIn --- | A clock source that corresponds to the Intel/Quartus \"Altera PLL\" --- component (Arria V, Stratix V, Cyclone V) with settings to provide a stable --- 'Clock' from a single free-running input --- --- Only works when configured with: --- --- * 1 reference clock --- * 1-16 output clocks --- * a reset input port --- * a locked output port --- --- The PLL lock output is asserted when the clocks are stable, and is usually --- connected to reset circuitry to keep the circuit in reset while the clocks --- are still stabilizing. --- --- See also the [Altera Phase-Locked Loop (Altera PLL) IP Core User Guide](https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/altera_pll.pdf) --- --- === Specifying outputs --- --- The number of output clocks depends on this function's inferred result type. --- An instance with a single output clock can be instantiated using: --- --- @ --- 'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"Dom100MHz\", vPeriod=10000} --- --- (clk100 :: 'Clock' Dom100MHz, pllLocked) = --- 'alteraPll' ('SSymbol' \@\"alterapll50to100\") clk50 rst50 --- @ --- --- An instance with two clocks can be instantiated using --- --- @ --- 'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"Dom100MHz\", vPeriod=10000} --- 'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"Dom150MHz\", vPeriod='Clash.Signal.hzToPeriod' 150e6} --- --- (clk100 :: 'Clock' Dom100MHz, clk150 :: 'Clock' Dom150MHz, pllLocked) = --- 'alteraPll' ('SSymbol' \@\"alterapllmulti\") clk50 rst50 --- @ --- --- and so on up to 16 clocks. --- --- If you don't have a top-level type signature specifying the output clock --- domains, you can specify them using a pattern type signature, as shown here. --- --- === Example --- --- When the oscillator connected to the FPGA runs at 50 MHz and the external --- reset signal is /active low/, this will generate a 100 MHz clock for the --- @'Clash.Signal.System'@ domain: --- --- @ --- 'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"DomInput\", vPeriod=20000} --- --- topEntity --- :: 'Clock' DomInput --- -> 'Signal' DomInput 'Bool' --- -> [...] --- topEntity clkInp rstInp = [...] --- where --- (clk :: 'Clock' 'Clash.Signal.System', pllStable :: 'Signal' 'Clash.Signal.System' 'Bool') --- 'alteraPll' ('SSymbol' \@\"alterapll50to100\") clkInp --- ('Clash.Signal.unsafeFromActiveLow' rstInp) --- rst = 'Clash.Signal.resetSynchronizer' clk ('Clash.Signal.unsafeFromActiveLow' pllStable) --- @ --- --- 'Clash.Signal.resetSynchronizer' will keep the reset asserted when --- @pllStable@ is 'False', hence the use of --- @'Clash.Signal.unsafeFromActiveLow' pllStable@. Your circuit will have --- signals of type @'Signal' 'Clash.Signal.System'@ and all the clocks and --- resets of your components will be the @clk@ and @rst@ signals generated here --- (modulo local resets, which will be based on @rst@ or never asserted at all --- if the component doesn't need a reset). +-- | Instantiate an Intel clock generator corresponding to the Intel/Quartus +-- \"Altera PLL\" IP core (Arria V, Stratix V, Cyclone V) with 1 reference clock +-- input and a reset input and 1 to 18 output clocks and a @locked@ output. +-- +-- This function is deprecated because the @locked@ output is an asynchronous +-- signal. This means the user is required to add a synchronizer and as such +-- this function is unsafe. The common use case is now covered by +-- 'alteraPllSync' and 'unsafeAlteraPll' offers the functionality of this +-- deprecated function for advanced use cases. alteraPll :: forall t domIn name . ( HasAsynchronousReset domIn @@ -252,13 +430,21 @@ alteraPll :: SSymbol name -> -- | Free running clock (e.g. a clock pin connected to a crystal oscillator) Clock domIn -> - -- | Reset for the PLL + -- | Reset for the clock generator Reset domIn -> t alteraPll _ = setName @name unsafeAlteraPll {-# INLINE alteraPll #-} {-# DEPRECATED alteraPll "This function is unsafe. Please see documentation of the function for alternatives." #-} +-- | Instantiate an Intel clock generator corresponding to the Intel/Quartus +-- \"Altera PLL\" IP core (Arria V, Stratix V, Cyclone V) with 1 reference clock +-- input and a reset input and 1 to 18 output clocks and a @locked@ output. +-- +-- __NB__: Because the clock generator reacts asynchronously to the incoming +-- reset input, the signal __must__ be glitch-free. +-- +-- See also the [Altera Phase-Locked Loop (Altera PLL) IP Core User Guide](https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/altera_pll.pdf) unsafeAlteraPll :: forall t domIn . ( KnownDomain domIn @@ -266,7 +452,9 @@ unsafeAlteraPll :: , ClocksCxt t , NumOutClocks t <= 18 ) => + -- | Free running clock (e.g. a clock pin connected to a crystal oscillator) Clock domIn -> + -- | Reset for the clock generator Reset domIn -> t unsafeAlteraPll = clocks diff --git a/clash-prelude/src/Clash/Signal.hs b/clash-prelude/src/Clash/Signal.hs index 961141e772..d6cef3bf4c 100644 --- a/clash-prelude/src/Clash/Signal.hs +++ b/clash-prelude/src/Clash/Signal.hs @@ -449,7 +449,7 @@ Similarly, there are 'exposeClock', 'exposeReset' and 'exposeEnable' to just expose the hidden clock, the hidden reset or the hidden enable argument. You will need to explicitly apply clocks and resets when you want to use -components such as PLLs and 'resetSynchronizer': +components such as PLLs: @ topEntity @@ -458,9 +458,8 @@ topEntity -> Signal System Bit -> Signal System (BitVector 8) topEntity clk rst key1 = - let (pllOut,pllStable) = 'Clash.Intel.ClockGen.altpll' (SSymbol \@\"altpll50\") clk rst - rstSync = 'resetSynchronizer' pllOut (unsafeToActiveHigh pllStable) - in 'exposeClockResetEnable' leds pllOut rstSync enableGen + let (pllOut,pllRst) = 'Clash.Intel.ClockGen.altpllSync' clk rst + in 'exposeClockResetEnable' leds pllOut pllRst enableGen where key1R = isRising 1 key1 leds = mealy blinkerT (1, False, 0) key1R @@ -475,9 +474,8 @@ topEntity -> Signal System Bit -> Signal System (BitVector 8) topEntity clk rst key1 = - let (pllOut,pllStable) = 'Clash.Intel.ClockGen.altpll' (SSymbol \@\"altpll50\") clk rst - rstSync = 'resetSynchronizer' pllOut (unsafeToActiveHigh pllStable) - in 'withClockResetEnable' pllOut rstSync enableGen leds + let (pllOut,pllRst) = 'Clash.Intel.ClockGen.altpllSync' clk rst + in 'withClockResetEnable' pllOut pllRst enableGen leds where key1R = isRising 1 key1 leds = mealy blinkerT (1, False, 0) key1R diff --git a/clash-prelude/src/Clash/Signal/Internal.hs b/clash-prelude/src/Clash/Signal/Internal.hs index 77f0ec1e35..3101d37bdf 100644 --- a/clash-prelude/src/Clash/Signal/Internal.hs +++ b/clash-prelude/src/Clash/Signal/Internal.hs @@ -947,6 +947,9 @@ instance Show (ClockN dom) where -- The first input is the positive phase, the second the negative phase. When -- using 'Clash.Annotations.TH.makeTopEntity', the names of the inputs will end -- in @_p@ and @_n@ respectively. +-- +-- To create a differential clock in a test bench, you can use +-- 'Clash.Explicit.Testbench.clockToDiffClock'. data DiffClock (dom :: Domain) = DiffClock ("p" ::: Clock dom) ("n" ::: ClockN dom) diff --git a/clash-prelude/src/Clash/Tutorial.hs b/clash-prelude/src/Clash/Tutorial.hs index 54c67d388f..3dbbdb8f74 100644 --- a/clash-prelude/src/Clash/Tutorial.hs +++ b/clash-prelude/src/Clash/Tutorial.hs @@ -913,19 +913,18 @@ import "Clash.Signal" import "Clash.Prelude" import "Clash.Intel.ClockGen" -'createDomain' vSystem{vName=\"DomInput\", vPeriod=20000} +'createDomain' vSystem{vName=\"DomInput\", vPeriod=20000, vResetPolarity=ActiveLow} 'createDomain' vSystem{vName=\"Dom100\", vPeriod=10000} topEntity :: Clock DomInput - -> Signal DomInput Bool + -> Reset DomInput -> Signal Dom100 Bit -> Signal Dom100 (BitVector 8) topEntity clk rst = - 'exposeClockResetEnable' ('mealy' blinkerT (1,False,0) . Clash.Prelude.isRising 1) pllOut rstSync 'enableGen' + 'exposeClockResetEnable' ('mealy' blinkerT (1,False,0) . 'Clash.Prelude.isRising' 1) pllOut pllRst 'enableGen' where - (pllOut,pllStable) = 'Clash.Intel.ClockGen.altpll' \@Dom100 (SSymbol \@\"altpll100\") clk ('Clash.Signal.unsafeFromActiveLow' rst) - rstSync = 'Clash.Signal.resetSynchronizer' pllOut ('Clash.Signal.unsafeFromActiveLow' pllStable) + (pllOut,pllRst) = 'Clash.Intel.ClockGen.altpllSync' clk rst blinkerT (leds,mode,cntr) key1R = ((leds',mode',cntr'),leds) where @@ -959,8 +958,9 @@ use work.Blinker_topEntity_types.all; entity topEntity is port(-- clock clk : in Blinker_topEntity_types.clk_DomInput; - rst : in boolean; - x : in std_logic; + -- reset + rst : in Blinker_topEntity_types.rst_DomInput; + eta : in std_logic; result : out std_logic_vector(7 downto 0)); end; @@ -994,8 +994,9 @@ use work.blinker_types.all; entity blinker is port(-- clock - CLOCK_50 : in blinker_types.clk_dominput; - KEY0 : in boolean; + CLOCK_50 : in blinker_types.clk_DomInput; + -- reset + KEY0 : in blinker_types.rst_DomInput; KEY1 : in std_logic; LED : out std_logic_vector(7 downto 0)); end; diff --git a/clash-prelude/src/Clash/Xilinx/ClockGen.hs b/clash-prelude/src/Clash/Xilinx/ClockGen.hs index fb8f03bfcc..19e2380141 100644 --- a/clash-prelude/src/Clash/Xilinx/ClockGen.hs +++ b/clash-prelude/src/Clash/Xilinx/ClockGen.hs @@ -4,7 +4,25 @@ Copyright : (C) 2017, Google Inc, License : BSD2 (see the file LICENSE) Maintainer : QBayLogic B.V. -PLL and other clock-related components for Xilinx FPGAs +This module contains functions for instantiating clock generators on Xilinx +FPGA's. + +We suggest you use a clock generator even if your oscillator runs at the +frequency you want to run your circuit at. + +A clock generator generates a stable clock signal for your design at a +configurable frequency. A clock generator in an FPGA is frequently referred to +as a PLL (Phase-Locked Loop). However, Xilinx differentiates between several +types of clock generator implementations in their FPGAs and uses the term PLL to +refer to one specific type, so we choose to use the more generic term /clock/ +/generator/ here. + +For most use cases, you would create two or more synthesis domains describing +the oscillator input and the domains you wish to use in your design, and use +the [regular functions](#g:regular) below to generate the clocks and resets of +the design from the oscillator input. There are use cases not covered by this +simpler approach, and the [unsafe functions](#g:unsafe) are provided as a means +to build advanced reset managers for the output domains. -} {-# LANGUAGE CPP #-} @@ -12,8 +30,32 @@ PLL and other clock-related components for Xilinx FPGAs {-# LANGUAGE GADTs #-} module Clash.Xilinx.ClockGen - ( clockWizard + ( -- * Choosing domains + -- $domains + + -- ** Caution: actual output frequency + -- $caution + + -- * Using + -- $using + + -- ** Example + -- $example + + -- ** Type checking errors + -- $error + + -- ** Tcl + -- $tcl + + -- * Regular functions #regular# + clockWizard , clockWizardDifferential + -- * Unsafe functions #unsafe# + -- $unsafe + + -- ** Example + -- $unsafe_example , unsafeClockWizard , unsafeClockWizardDifferential ) where @@ -26,32 +68,271 @@ import Clash.Clocks import Clash.Signal.Internal (Clock, DiffClock(..), Reset, KnownDomain, HasAsynchronousReset) +{- $domains +Synthesis domains are denoted by the type-parameter +@dom :: t'Clash.Signal.Domain'@ as occurring in for instance +@t'Clash.Signal.Signal' dom a@; see "Clash.Signal" for more information. For +each domain, there is only a single clock signal which clocks that domain; +mixing clock signals is a design error. Conversely, it is possible to clock +multiple domains using the same clock signal, in complex designs. + +For the clock generator inputs, create a domain with the correct clock frequency +and reset polarity. For instance, if the clock input is a free-running clock at +a frequency of 50 MHz (a period of 20 ns or 20,000 ps), and the reset input +connected to the clock generator is /active-low/, the following will instantiate +the required input domain: + +@ +'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"DomInput\", vPeriod=20000, vResetPolarity='Clash.Signal.ActiveLow'} +@ + +If you haven't determined the frequency you want the design to run at, the +predefined 100 MHz domain t'Clash.Signal.XilinxSystem' can be a good starting +point. The datasheet for your FPGA specifies lower and upper limits, but the +true maximum frequency is determined by your design. + +Supposing you need a clock running at 150 MHz for your design, the following +will instantiate a suitable domain: + +@ +'Clash.Signal.createDomain' 'Clash.Signal.vXilinxSystem'{vName=\"Dom150\", vPeriod='Clash.Signal.hzToPeriod' 150e6} +@ + +@Dom150@ will have 'Clash.Signal.Synchronous' resets on its memory elements +because it was derived from 'Clash.Signal.vXilinxSystem', whereas @DomInput@ +will have 'Clash.Signal.Asynchronous' resets because it was derived from +'Clash.Signal.vSystem'. Xilinx recommends synchronous resets for circuits, but +the clock generator reacts asynchronously to its reset input instead, which will +need to be declared correctly in Clash. If you use the /unsafe/ functions below, +Clash does not enforce this. +-} + +{- $caution +The clock generator in the FPGA is limited in which clock frequencies it can +generate, especially when one clock generator has multiple outputs. The clock +generator will pick the attainable frequency closest to the requested frequency +(or possibly fail to synthesize). You can check the frequency that the wizard +chose by loading your design into the Vivado GUI. In the /IP sources/ window, +choose the clock wizard and select /Re-customize IP.../. On the /Output Clocks/ +tab, the relevant column is /Actual Output Freq (MHz)/. If the actual value +differs, copy the actual value back to the Clash design. +-} + +{- $using +The functions in this module will instantiate a Xilinx MMCM clock generator +corresponding to the Xilinx \"Clock Wizard\" with 1 reference clock input and a +reset input, and 1 to 7 output clocks and a @locked@ output. + +The [regular functions](#g:regular) incorporate 'Clash.Signal.resetSynchronizer' +to convert the @locked@ output port into a proper 'Reset' signal for the domains +which will keep the circuit in reset while the clock is still stabilizing. + +The clock generator will react asynchronously to the incoming reset input. When +the reset input is asserted, the clock generator's @locked@ output will +deassert, in turn causing the 'Reset' output(s) of these functions to assert. + +You can use 'Clash.Magic.setName' to give the IP instance a specific name, which +can be useful if you need to refer to the instance in Synopsys Design +Constraints files. + +The output of the function for /n/ output clocks is a /2n/-tuple with clock and +reset outputs. The compiler needs to be able to fully determine the types of the +individual tuple elements from the context; the clock generator function itself +will not constrain them. If the types of the tuple elements cannot be inferred, +you can use pattern type signatures to specify the types. Supposing the +referenced domains have been created with 'Clash.Signal.createDomain', an +instance with a single output clock can be instantiated using: + +@ +(clk150 :: 'Clock' Dom150, rst150 :: 'Reset' Dom150) = 'clockWizard' clkIn rstIn +@ + +An instance with two clocks can be instantiated using + +@ +( clk100 :: 'Clock' Dom100 + , rst100 :: 'Reset' Dom100 + , clk150 :: 'Clock' Dom150 + , rst150 :: 'Reset' Dom150) = 'clockWizard' clkIn rstIn +@ + +and so on up to 7 clocks, following the general pattern @('Clock' dom1, 'Reset' +dom1, 'Clock' dom2, 'Reset' dom2, ..., 'Clock' dom/n/, 'Reset' dom/n/)@. + +If you need access to the @locked@ output to build a more advanced reset +manager, you should use the [unsafe functions](#g:unsafe) instead. + +See also the [Clocking Wizard LogiCORE IP Product Guide](https://docs.xilinx.com/r/en-US/pg065-clk-wiz) +-} + +{- $example + +When the oscillator connected to the FPGA runs at 50 MHz and the external reset +signal is /active-low/, this will generate a 150 MHz clock for use by the +circuit: + +@ +'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"DomInput\", vPeriod=20000, vResetPolarity='Clash.Signal.ActiveLow'} +'Clash.Signal.createDomain' 'Clash.Signal.vXilinxSystem'{vName=\"Dom150\", vPeriod='Clash.Signal.hzToPeriod' 150e6} + +topEntity + :: 'Clock' DomInput + -> 'Reset' DomInput + -> t'Clash.Signal.Signal' Dom150 Int + -> t'Clash.Signal.Signal' Dom150 Int +topEntity clkIn rstIn = 'Clash.Signal.exposeClockResetEnable' (register 0) clk rst 'Clash.Signal.enableGen' + where + (clk, rst) = 'clockWizard' clkIn rstIn +@ +-} + +{- $error +When type checking cannot infer the types of the tuple elements, or they have +the wrong type, the GHC compiler will complain about satisfying @NumOutClocks@. +The error message on GHC 9.4 and up is: + +@ + • Cannot satisfy: clash-prelude-[...]:Clash.Clocks.Internal.NumOutClocks + (clash-prelude-[...]:Clash.Clocks.Internal.ClocksSyncClocksInst + ([...]) + DomInput) <= 7 + • In the expression: clockWizard clkIn rstIn +@ + +On older GHC versions, the error message is: + +@ + • Couldn't match type ‘clash-prelude-[...]:Clash.Clocks.Internal.NumOutClocks + (clash-prelude-[...]:Clash.Clocks.Internal.ClocksSyncClocksInst + ([...]) + DomInput) + <=? 7’ + with ‘'True’ + arising from a use of ‘clockWizard’ + • In the expression: clockWizard clkIn rstIn +@ + +The above error message is also emitted when trying to instantiate more than 18 +output clocks, as it will fail to find an instance. As the wizard supports no +more than 7 clocks, trying to instantiate between 8 and 18 output clocks will +also cause a type checking error. On GHC 9.4 and up, the error for attempting to +instantiate 8 clocks is: + +@ + • Cannot satisfy: 8 <= 7 + • In the expression: clockWizard clkIn rstIn +@ + +On older GHC versions, the error message is less clear: + +@ + • Couldn't match type ‘'False’ with ‘'True’ + arising from a use of ‘clockWizard’ + • In the expression: clockWizard clkIn rstIn +@ +-} + +{- $tcl +When generating HDL, these functions will emit a Tcl script for Vivado that +instantiates the needed IP core for the function. This Tcl script adheres to the +Clash\<-\>Tcl API. The Tcl Connector bundled in @clash-lib:Clash.DataFiles@ will +automatically process these scripts and build your design in Vivado. See +@clash-lib:Clash.DataFiles@ for more information. +-} + +{- $unsafe +These functions are provided for the cases where the [regular +functions](#g:regular) cannot provide the desired behavior, like when +implementing certain advanced reset managers. These functions directly expose +the /asynchronous/ @locked@ output of the clock generator, which will assert +when the output clocks are stable. @locked@ is usually connected to reset +circuitry to keep the circuit in reset while the clock is still stabilizing. + +The output of the function for /n/ output clocks is an /n+1/-tuple with /n/ +clock outputs and a @locked@ signal. The compiler needs to be able to fully +determine the types of the individual tuple elements from the context; the clock +generator function itself will not constrain them. If the types of the tuple +elements cannot be inferred, you can use pattern type signatures to specify the +types. Supposing the referenced domains have been created with +'Clash.Signal.createDomain', an instance with a single output clock can be +instantiated using: + +@ +(clk150 :: 'Clock' Dom150, locked :: t'Clash.Signal.Signal' Dom150 'Bool') = 'unsafeClockWizard' clkIn rstIn +@ + +An instance with two clocks can be instantiated using + +@ +(clk100 :: 'Clock' Dom100 + , clk150 :: 'Clock' Dom150 + , locked :: t'Clash.Signal.Signal' Dom100 'Bool') = 'unsafeClockWizard' clkIn rstIn +@ + +and so on up to 7 clocks, following the general pattern @('Clock' dom1, 'Clock' +dom2, ..., 'Clock' dom/n/, t'Clash.Signal.Signal' pllLock Bool)@. + +Though the @locked@ output is specified as a @t'Clash.Signal.Signal' pllLock +'Bool'@, it is an asynchronous signal and will need to be synchronized before it +can be used as a (reset) signal. While in the examples above the +@locked@ output has been assigned the domain of one of the output clocks, the +domain @pllLock@ is left unrestricted. If the lock signal is to be used in +multiple domains, the @pllLock@ domain should probably be set to @domIn@ (the +domain of the input clock and reset). While in HDL +'Clash.Explicit.Signal.unsafeSynchronizer' is just a wire, in Haskell simulation +it does actually resample the signal, and by setting @pllLock@ to @domIn@, there +is no resampling of the simulated lock signal. The simulated lock signal is +simply the inverse of the reset input: @locked@ is asserted whenever the reset +input is deasserted and vice versa. +-} + +{- $unsafe_example +@ +'Clash.Signal.createDomain' 'Clash.Signal.vSystem'{vName=\"DomInput\", vPeriod=20000, vResetPolarity='Clash.Signal.ActiveLow'} +'Clash.Signal.createDomain' 'Clash.Signal.vXilinxSystem'{vName=\"Dom150\", vPeriod='Clash.Signal.hzToPeriod' 150e6} + +topEntity + :: 'Clock' DomInput + -> 'Reset' DomInput + -> t'Clash.Signal.Signal' Dom150 Int + -> t'Clash.Signal.Signal' Dom150 Int +topEntity clkIn rstIn = 'Clash.Signal.exposeClockResetEnable' (register 0) clk rst 'Clash.Signal.enableGen' + where + (clk, locked) = 'unsafeClockWizard' clkIn rstIn + rst = 'Clash.Signal.resetSynchronizer' clk ('Clash.Signal.unsafeFromActiveLow' locked) +@ + +'Clash.Signal.resetSynchronizer' will keep the reset asserted when @locked@ is +'False', hence the use of @'Clash.Signal.unsafeFromActiveLow' locked@. +-} + +-- | Instantiate a Xilinx MMCM clock generator corresponding to the Xilinx +-- \"Clock Wizard\" with 1 single-ended reference clock input and a reset input, +-- and 1 to 7 output clocks and a @locked@ output. +-- +-- This function incorporates 'Clash.Signal.resetSynchronizer's to convert the +-- @locked@ output port into proper 'Reset' signals for the output domains which +-- will keep the circuit in reset while the clock is still stabilizing. clockWizard :: forall t domIn . ( HasAsynchronousReset domIn , ClocksSyncCxt t domIn , NumOutClocksSync t domIn <= 7 ) => + -- | Free running clock (e.g. a clock pin connected to a crystal oscillator) Clock domIn -> + -- | Reset for the clock generator Reset domIn -> t clockWizard clkIn rstIn = clocksResetSynchronizer (unsafeClockWizard clkIn rstIn) clkIn --- | A clock source that corresponds to the Xilinx MMCM component created --- with the \"Clock Wizard\" with settings to provide a stable 'Clock' from --- a single free-running clock input. --- --- You can use type applications to specify the output clock domain, e.g.: --- --- @ --- createDomain vXilinxSystem{vName=\"Dom100MHz\", vPeriod=10000} --- --- -- Outputs a clock running at 100 MHz --- clockWizard \@_ \@Dom100MHz (SSymbol \@\"clkWizard50to100\") clk50 rst --- @ +-- | Instantiate a Xilinx MMCM clock generator corresponding to the Xilinx +-- \"Clock Wizard\" with 1 single-ended reference clock input and a reset input, +-- and 1 to 7 output clocks and a @locked@ output. -- --- See also the [Clocking Wizard LogiCORE IP Product Guide](https://docs.xilinx.com/r/en-US/pg065-clk-wiz) +-- __NB__: Because the clock generator reacts asynchronously to the incoming +-- reset input, the signal __must__ be glitch-free. unsafeClockWizard :: forall t domIn . ( KnownDomain domIn @@ -59,9 +340,9 @@ unsafeClockWizard :: , ClocksCxt t , NumOutClocks t <= 7 ) => - -- | Free running clock (i.e. a clock pin connected to a crystal) + -- | Free running clock (e.g. a clock pin connected to a crystal oscillator) Clock domIn -> - -- | Reset for the PLL + -- | Reset for the clock generator Reset domIn -> t unsafeClockWizard = clocks @@ -69,32 +350,40 @@ unsafeClockWizard = clocks {-# CLASH_OPAQUE unsafeClockWizard #-} {-# ANN unsafeClockWizard hasBlackBox #-} +-- | Instantiate a Xilinx MMCM clock generator corresponding to the Xilinx +-- \"Clock Wizard\" with 1 differential reference clock input and a reset input, +-- and 1 to 7 output clocks and a @locked@ output. +-- +-- This function incorporates 'Clash.Signal.resetSynchronizer's to convert the +-- @locked@ output port into proper 'Reset' signals for the output domains which +-- will keep the circuit in reset while the clock is still stabilizing. +-- +-- To create a differential clock in a test bench, you can use +-- 'Clash.Explicit.Testbench.clockToDiffClock'. clockWizardDifferential :: forall t domIn . ( HasAsynchronousReset domIn , ClocksSyncCxt t domIn , NumOutClocksSync t domIn <= 7 ) => + -- | Free running clock (e.g. a clock pin pair connected to a crystal + -- oscillator) DiffClock domIn -> + -- | Reset for the clock generator Reset domIn -> t clockWizardDifferential clkIn@(DiffClock clkInP _) rstIn = clocksResetSynchronizer (unsafeClockWizardDifferential clkIn rstIn) clkInP --- | A clock source that corresponds to the Xilinx MMCM component created --- with the \"Clock Wizard\", with settings to provide a stable 'Clock' --- from a free-running differential clock input. --- --- You can use type applications to specify the output clock domain, e.g.: --- --- @ --- createDomain vXilinxSystem{vName=\"Dom100MHz\", vPeriod=10000} +-- | Instantiate a Xilinx MMCM clock generator corresponding to the Xilinx +-- \"Clock Wizard\" with 1 differential reference clock input and a reset input, +-- and 1 to 7 output clocks and a @locked@ output. -- --- -- Outputs a clock running at 100 MHz --- clockWizardDifferential \@_ \@Dom100MHz (SSymbol \@\"clkWizard50to100\") clk50 rst --- @ +-- __NB__: Because the clock generator reacts asynchronously to the incoming +-- reset input, the signal __must__ be glitch-free. -- --- See also the [Clocking Wizard LogiCORE IP Product Guide](https://docs.xilinx.com/r/en-US/pg065-clk-wiz) +-- To create a differential clock in a test bench, you can use +-- 'Clash.Explicit.Testbench.clockToDiffClock'. unsafeClockWizardDifferential :: forall t domIn . ( KnownDomain domIn @@ -102,9 +391,10 @@ unsafeClockWizardDifferential :: , ClocksCxt t , NumOutClocks t <= 7 ) => - -- | Free running clock + -- | Free running clock (e.g. a clock pin pair connected to a crystal + -- oscillator) DiffClock domIn -> - -- | Reset for the PLL + -- | Reset for the clock generator Reset domIn -> t unsafeClockWizardDifferential (DiffClock clk _) = clocks clk diff --git a/examples/Blinker.hs b/examples/Blinker.hs index a93e1bb7ef..91148e222e 100644 --- a/examples/Blinker.hs +++ b/examples/Blinker.hs @@ -11,8 +11,9 @@ data LedMode -- ^ After some period, turn on all disable LEDs, and vice versa deriving (Generic, NFDataX) --- Define a synthesis domain with a clock with a period of 20000 /ps/. -createDomain vSystem{vName="Input", vPeriod=20000} +-- Define a synthesis domain with a clock with a period of 20000 /ps/. Signal +-- coming from the reset button is low when pressed, and high when not pressed. +createDomain vSystem{vName="Input", vPeriod=20000, vResetPolarity=ActiveLow} -- Define a synthesis domain with a clock with a period of 50000 /ps/. createDomain vSystem{vName="Dom50", vPeriod=50000} @@ -34,7 +35,7 @@ topEntity -- -- Annotate with attributes to map the argument to the correct pin, with the -- correct voltage settings, on the DE0-Nano development kit. - -> Signal Input Bool + -> Reset Input `Annotate` 'StringAttr "chip_pin" "J15" `Annotate` 'StringAttr "altera_attribute" "-name IO_STANDARD \"3.3-V LVTTL\"" -- ^ Reset signal, straight from KEY0 @@ -51,11 +52,10 @@ topEntity -- bits of the result to the correct pins on the DE0-Nano development kit topEntity clk20 rstBtn modeBtn = exposeClockResetEnable - (mealy blinkerT initialStateBlinkerT . isRising 1) + (mealy blinkerT initialStateBlinkerT (isRising 1 modeBtn)) clk50 - rstSync + rst50 en - modeBtn where -- | Enable line for subcomponents: we'll keep it always running en = enableGen @@ -63,28 +63,12 @@ topEntity clk20 rstBtn modeBtn = -- Start with the first LED turned on, in rotate mode, with the counter on zero initialStateBlinkerT = (1, Rotate, 0) - -- Signal coming from the reset button is low when pressed, and high when - -- not pressed. We convert this signal to the polarity of our domain with - -- 'unsafeFromActiveLow'. - rst = unsafeFromActiveLow rstBtn - - -- Instantiate a PLL: this stabilizes the incoming clock signal and indicates - -- when the signal is stable. We're also using it to transform an incoming - -- clock signal running at 20 MHz to a clock signal running at 50 MHz. - (clk50, pllStable) = - altpll - @Dom50 - (SSymbol @"altpll50") - clk20 - rst - - -- Synchronize reset to clock signal coming from PLL. We want the reset to - -- remain active while the PLL is NOT stable, hence the conversion with - -- 'unsafeFromActiveLow' - rstSync = - resetSynchronizer - clk50 - (unsafeFromActiveLow pllStable) + -- Instantiate a PLL: this stabilizes the incoming clock signal and releases + -- the reset output when the signal is stable. We're also using it to + -- transform an incoming clock signal running at 20 MHz to a clock signal + -- running at 50 MHz. Since the type signature for topEntity already specifies + -- the domain, we don't need a type signature here. + (clk50, rst50) = altpllSync clk20 rstBtn flipMode :: LedMode -> LedMode flipMode Rotate = Complement