Copyright | (C) 2013-2016 University of Twente 2016 Myrtle Software 2017 Google Inc. |
---|---|
License | BSD2 (see the file LICENSE) |
Maintainer | Christiaan Baaij <christiaan.baaij@gmail.com> |
Safe Haskell | Trustworthy |
Language | Haskell2010 |
Extensions |
|
CλaSH has synchronous Signal
s in the form of:
Signal
(domain ::Domain
) a
Where a is the type of the value of the Signal
, for example Int or Bool,
and domain is the clock- (and reset-) domain to which the memory elements
manipulating these Signal
s belong.
The type-parameter, domain, is of the kind Domain
which has types of the
following shape:
data Domain = Dom { domainName ::Symbol
, clkPeriod ::Nat
}
Where domainName is a type-level string (Symbol
) representing
the name of the clock- (and reset-) domain, and clkPeriod is a type-level
natural number (Nat
) representing the clock period (in ps)
of the clock lines in the clock-domain.
- NB: "Bad things"™ happen when you actually use a clock period of
0
, so do not do that! - NB: You should be judicious using a clock with period of
1
as you can never create a clock that goes any faster!
Explicit clocks and resets, and meta-stability
When clocks and resets are implicitly routed using the mechanisms provided by the clash-prelude, then clocks and resets are also implicitly unique.
The protection against accidental
metastability
offered by Clash's domain annotation on Signal
s is based on the uniqueness
of clocks and resets. But with explicit clock and reset lines, there are
ways to (accidentally) introduce situations that are prone to metastability.
There are four different clock and reset lines:
Reset
domainSynchronous
Reset
domainAsynchronous
Clock
domainSource
Clock
domainGated
We now go over the combinations over these clock and reset line combinations and explain when they can potentially introduce situations prone to meta-stability:
Reset situation 1:
f :: Reset domain Synchronous -> Reset domain Synchronous -> .. f x y = ..
There are no problems here, because although x and y can have different values, components to these reset lines are reset synchronously, and there is no metastability situation.
Reset situation 2:
g :: Reset domain Asynchronous -> Reset domain Asynchronous -> .. g x y = ..
This situation can be prone to metastability, because although x and y belong to the same domain according to their type, there is no guarantee that they actually originate from the same source. This means that one component can enter its reset state asynchronously to another component, inducing metastability in the other component.
- The Clash compiler will give a warning whenever a function has a type-signature similar to the one above.
- This is the reason why
unsafeFromAsyncReset
is prefixed with the word unsafe.
Reset situation 3:
h :: Reset domain Asynchronous -> Reset domain Synchronous -> .. h x y = ..
Also this situation is prone to metastability, because again, one component can enter its reset state asynchronously to the other, inducing metastability in the other component.
- The Clash compiler will give a warning whenever a function has a type-signature similar to the one above.
- Although in a standalone context, converting between
andReset
domainSynchronous
would be safe from a metastability point of view, it is not when we're in a context where there are also asynchronous resets. That is whySignal
domainBool
unsafeToSyncReset
is prefixed with the word unsafe.
Clock situations 1, 2, and 3:
k :: Clock domain Source -> Clock domain source -> .. k x y = .. l :: Clock domain Source -> Clock domain Gated -> .. l x y = .. m :: Clock domain Gated -> Clock domain Gated -> .. m x y = ..
All the above situations are potentially prone to metastability, because even though x and y belong to the same domain according to their type, there is no guarantee that they actually originate from the same source. They could hence be connected to completely unrelated clock sources, and components can then induce metastable states in others.
- The Clash compiler will give a warning whenever a function has a type-signature similar to one of the above three situations.
- data Signal (domain :: Domain) a
- data Domain = Dom {
- domainName :: Symbol
- clkPeriod :: Nat
- type System = Dom "system" 10000
- data Clock (domain :: Domain) (gated :: ClockKind)
- data ClockKind
- freqCalc :: Double -> Integer
- unsafeSynchronizer :: Clock domain1 gated1 -> Clock domain2 gated2 -> Signal domain1 a -> Signal domain2 a
- clockGate :: Clock domain gated -> Signal domain Bool -> Clock domain Gated
- data Reset (domain :: Domain) (synchronous :: ResetKind)
- data ResetKind
- unsafeFromAsyncReset :: Reset domain Asynchronous -> Signal domain Bool
- unsafeToAsyncReset :: Signal domain Bool -> Reset domain Asynchronous
- fromSyncReset :: Reset domain Synchronous -> Signal domain Bool
- unsafeToSyncReset :: Signal domain Bool -> Reset domain Synchronous
- resetSynchronizer :: Clock domain gated -> Reset domain Asynchronous -> Reset domain Asynchronous
- delay :: HasCallStack => Clock domain gated -> Signal domain a -> Signal domain a
- register :: HasCallStack => Clock domain gated -> Reset domain synchronous -> a -> Signal domain a -> Signal domain a
- regMaybe :: HasCallStack => Clock domain gated -> Reset domain synchronous -> a -> Signal domain (Maybe a) -> Signal domain a
- regEn :: Clock domain clk -> Reset domain synchronous -> a -> Signal domain Bool -> Signal domain a -> Signal domain a
- clockGen :: (domain ~ Dom nm period, KnownSymbol nm, KnownNat period) => Clock domain Source
- tbClockGen :: (domain ~ Dom nm period, KnownSymbol nm, KnownNat period) => Signal domain Bool -> Clock domain Source
- asyncResetGen :: Reset domain Asynchronous
- syncResetGen :: (domain ~ Dom n clkPeriod, KnownNat clkPeriod) => Reset domain Synchronous
- systemClockGen :: Clock System Source
- tbSystemClockGen :: Signal System Bool -> Clock System Source
- systemResetGen :: Reset System Asynchronous
- (.&&.) :: Applicative f => f Bool -> f Bool -> f Bool
- (.||.) :: Applicative f => f Bool -> f Bool -> f Bool
- class Bundle a where
- simulate :: (NFData a, NFData b) => (Signal domain1 a -> Signal domain2 b) -> [a] -> [b]
- simulateB :: (Bundle a, Bundle b, NFData a, NFData b) => (Unbundled domain1 a -> Unbundled domain2 b) -> [a] -> [b]
- simulate_lazy :: (Signal domain1 a -> Signal domain2 b) -> [a] -> [b]
- simulateB_lazy :: (Bundle a, Bundle b) => (Unbundled domain1 a -> Unbundled domain2 b) -> [a] -> [b]
- sample :: (Foldable f, NFData a) => f a -> [a]
- sampleN :: (Foldable f, NFData a) => Int -> f a -> [a]
- fromList :: NFData a => [a] -> Signal domain a
- sample_lazy :: Foldable f => f a -> [a]
- sampleN_lazy :: Foldable f => Int -> f a -> [a]
- fromList_lazy :: [a] -> Signal domain a
- testFor :: Foldable f => Int -> f Bool -> Property
- (.==.) :: (Eq a, Applicative f) => f a -> f a -> f Bool
- (./=.) :: (Eq a, Applicative f) => f a -> f a -> f Bool
- (.<.) :: (Ord a, Applicative f) => f a -> f a -> f Bool
- (.<=.) :: (Ord a, Applicative f) => f a -> f a -> f Bool
- (.>=.) :: (Ord a, Applicative f) => f a -> f a -> f Bool
- (.>.) :: (Ord a, Applicative f) => f a -> f a -> f Bool
Synchronous signal
data Signal (domain :: Domain) a Source #
CλaSH has synchronous Signal
s in the form of:
Signal
(domain ::Domain
) a
Where a is the type of the value of the Signal
, for example Int or Bool,
and domain is the clock- (and reset-) domain to which the memory elements
manipulating these Signal
s belong.
The type-parameter, domain, is of the kind Domain
which has types of the
following shape:
data Domain = Dom { domainName ::Symbol
, clkPeriod ::Nat
}
Where domainName is a type-level string (Symbol
) representing
the name of the clock- (and reset-) domain, and clkPeriod is a type-level
natural number (Nat
) representing the clock period (in ps)
of the clock lines in the clock-domain.
- NB: "Bad things"™ happen when you actually use a clock period of
0
, so do not do that! - NB: You should be judicious using a clock with period of
1
as you can never create a clock that goes any faster!
Functor (Signal domain) Source # | |
Applicative (Signal domain) Source # | |
Foldable (Signal domain) Source # | NB: Not synthesisable NB: In "
|
Traversable (Signal domain) Source # | |
Fractional a => Fractional (Signal domain a) Source # | |
Num a => Num (Signal domain a) Source # | |
Show a => Show (Signal domain a) Source # | |
Lift a => Lift (Signal domain a) Source # | |
Arbitrary a => Arbitrary (Signal domain a) Source # | |
CoArbitrary a => CoArbitrary (Signal domain a) Source # | |
Default a => Default (Signal domain a) Source # | |
A domain with a name (Symbol
) and a clock period (Nat
) in ps
Dom | |
|
Clock
Distinction between gated and ungated clocks
freqCalc :: Double -> Integer Source #
Calculate the period, in ps, given a frequency in Hz
i.e. to calculate the clock period for a circuit to run at 240 MHz we get
>>>
freqCalc 240e6
4167
NB: This function is not synthesisable
Synchronisation primitive
:: Clock domain1 gated1 |
|
-> Clock domain2 gated2 |
|
-> Signal domain1 a | |
-> Signal domain2 a |
The unsafeSynchronizer
function is a primitive that must be used to
connect one clock domain to the other, and will be synthesised to a (bundle
of) wire(s) in the eventual circuit. This function should only be used as
part of a proper synchronisation component, such as the following dual
flip-flop synchronizer:
dualFlipFlop :: Clock domA gatedA -> Clock domB gatedB -> Signal domA Bit -> Signal domB Bit dualFlipFlop clkA clkB =delay
clkB .delay
clkB .unsafeSynchronizer
clkA clkB
The unsafeSynchronizer
works in such a way that, given 2 clocks:
type Dom7 =Dom
"dom" 7 clk7 ::Clock
Dom7 Source clk7 =clockGen
and
type Dom2 =Dom
"dom" 2 clk2 ::Clock
Dom2 Source clk2 =clockGen
Oversampling followed by compression is the identity function plus 2 initial values:
delay
clkB $unsafeSynchronizer
clkA clkB $delay
clkA $unsafeSynchronizer
clkB clkA $delay
clkB s == X :- X :- s
Something we can easily observe:
oversampling clkA clkB =delay
clkB .unsafeSynchronizer
clkA clkB .delay
clkA almostId clkA clkB =delay
clkB .unsafeSynchronizer
clkA clkB .delay
clkA .unsafeSynchronizer
clkB clkA .delay
clkB
>>>
printX (sampleN 37 (oversampling clk7 clk2 (fromList [(1::Int)..10])))
[X,X,1,1,1,2,2,2,2,3,3,3,4,4,4,4,5,5,5,6,6,6,6,7,7,7,8,8,8,8,9,9,9,10,10,10,10]>>>
printX (sampleN 12 (almostId clk2 clk7 (fromList [(1::Int)..10])))
[X,X,1,2,3,4,5,6,7,8,9,10]
Clock gating
clockGate :: Clock domain gated -> Signal domain Bool -> Clock domain Gated Source #
Clock gating primitive
Reset
The "kind" of reset
Given a situation where a reset is asserted, and then de-asserted at the active flank of the clock, we can observe the difference between a synchronous reset and an asynchronous reset:
Synchronous reset
registerS :: Clock domain gated -> Reset domain Synchronous -> Signal domain Int -> Signal domain Int registerS = register
>>>
printX (sampleN 4 (registerS (clockGen @System) (syncResetGen @System) 0 (fromList [1,2,3])))
[X,0,2,3]
Asynchronous reset
registerA :: Clock domain gated -> Reset domain Asynchronous -> Signal domain Int -> Signal domain Int registerA = register
>>>
sampleN 4 (registerA (clockGen @System) (asyncResetGen @System) 0 (fromList [1,2,3]))
[0,1,2,3]
Synchronous | Components with a synchronous reset port produce the reset value when:
|
Asynchronous | Components with an asynchronous reset port produce the reset value when:
|
unsafeFromAsyncReset :: Reset domain Asynchronous -> Signal domain Bool Source #
unsafeFromAsyncReset
is unsafe because it can introduce:
unsafeToAsyncReset :: Signal domain Bool -> Reset domain Asynchronous Source #
unsafeToAsyncReset
is unsafe because it can introduce:
- combinational loops
Example
resetSynchronizer
:: Clock domain gated
-> Reset domain 'Asynchronous
-> Reset domain 'Asynchronous
resetSynchronizer clk rst =
let r1 = register clk rst True (pure False)
r2 = register clk rst True r1
in unsafeToAsyncReset
r2
fromSyncReset :: Reset domain Synchronous -> Signal domain Bool Source #
It is safe to treat synchronous resets as Bool
signals
unsafeToSyncReset :: Signal domain Bool -> Reset domain Synchronous Source #
unsafeToSyncReset
is unsafe because:
- It can lead to meta-stability issues in the presence of asynchronous resets.
resetSynchronizer :: Clock domain gated -> Reset domain Asynchronous -> Reset domain Asynchronous Source #
Normally, asynchronous resets can be both asynchronously asserted and
de-asserted. Asynchronous de-assertion can induce meta-stability in the
component which is being reset. To ensure this doesn't happen,
resetSynchronizer
ensures that de-assertion of a reset happens
synchronously. Assertion of the reset remains asynchronous.
Note that asynchronous assertion does not induce meta-stability in the component whose reset is asserted. However, when a component "A" in another clock or reset domain depends on the value of a component "B" being reset, then asynchronous assertion of the reset of component "B" can induce meta-stability in component "A". To prevent this from happening you need to use a proper synchronizer, for example one of the synchronizers in Clash.Explicit.Synchronizer
NB: Assumes the component(s) being reset have an active-high reset port, which all components in clash-prelude have.
Example
topEntity
:: Clock System Source
-> Reset System Asynchronous
-> Signal System Bit
-> Signal System (BitVector 8)
topEntity clk rst key1 =
let (pllOut,pllStable) = altpll (SSymbol @ "altpll50") clk rst
rstSync = resetSynchronizer
pllOut (unsafeToAsyncReset pllStable)
in exposeClockReset leds pllOut rstSync
where
key1R = isRising 1 key1
leds = mealy blinkerT (1,False,0) key1R
Basic circuit functions
:: HasCallStack | |
=> Clock domain gated | Clock |
-> Signal domain a | |
-> Signal domain a |
:: HasCallStack | |
=> Clock domain gated | Clock |
-> Reset domain synchronous | Reset (active-high), |
-> a | Reset value |
-> Signal domain (Maybe a) | |
-> Signal domain a |
Version of register
that only updates its content when its fourth
argument is a Just
value. So given:
sometimes1 clk rst = s where s =register
clk rst Nothing (switch<$>
s) switch Nothing = Just 1 switch _ = Nothing countSometimes clk rst = s where s =regMaybe
clk rst 0 (plusM (pure
<$>
s) (sometimes1 clk rst)) plusM = liftA2 (liftA2 (+))
We get:
>>>
sampleN 8 (sometimes1 systemClockGen systemResetGen)
[Nothing,Just 1,Nothing,Just 1,Nothing,Just 1,Nothing,Just 1]>>>
sampleN 8 (count systemClockGen systemResetGen)
[0,0,1,1,2,2,3,3]
:: Clock domain clk | Clock |
-> Reset domain synchronous | Reset (active-high), |
-> a | Reset value |
-> Signal domain Bool | Enable signal |
-> Signal domain a | |
-> Signal domain a |
Version of register
that only updates its content when its fourth
argument is asserted. So given:
oscillate clk rst = let s = register
clk rst False (not <$> s) in s
count clk rst = let s = 'regEn clk rst 0 (oscillate clk rst) (s + 1) in s
We get:
>>>
sampleN 8 (oscillate systemClockGen systemResetGen)
[False,True,False,True,False,True,False,True]>>>
sampleN 8 (count systemClockGen systemResetGen)
[0,0,1,1,2,2,3,3]
Simulation and testbench functions
clockGen :: (domain ~ Dom nm period, KnownSymbol nm, KnownNat period) => Clock domain Source Source #
Clock generator for simulations. Do not use this clock generator for
for the testBench function, use tbClockGen
instead.
To be used like:
type DomA = Dom "A" 1000 clkA = clockGen @DomA
tbClockGen :: (domain ~ Dom nm period, KnownSymbol nm, KnownNat period) => Signal domain Bool -> Clock domain Source Source #
Clock generator to be used in the testBench function.
To be used like:
type DomA = Dom "A" 1000 clkA en = clockGen @DomA en
Example
type DomA1 = Dom "A" 1 -- fast, twice as fast as slow type DomB2 = Dom "B" 2 -- slow topEntity :: Clock DomA1 Source -> Reset DomA1 Asynchronous -> Clock DomB2 Source -> Signal DomA1 (Unsigned 8) -> Signal DomB2 (Unsigned 8, Unsigned 8) topEntity clk1 rst1 clk2 i = let h = register clk1 rst1 0 (register clk1 rst1 0 i) l = register clk1 rst1 0 i in unsafeSynchronizer clk1 clk2 (bundle (h,l)) testBench :: Signal DomB2 Bool testBench = done where testInput = stimuliGenerator clkA1 rstA1 $(listToVecTH [1::Unsigned 8,2,3,4,5,6,7,8]) expectedOutput = outputVerifier clkB2 rstB2 $(listToVecTH [(0,0) :: (Unsigned 8, Unsigned 8),(1,2),(3,4),(5,6),(7,8)]) done = expectedOutput (topEntity clkA1 rstA1 clkB2 testInput) done' = not <$> done clkA1 =tbClockGen
@DomA1 (unsafeSynchronizer clkB2 clkA1 done') clkB2 =tbClockGen
@DomB2 done' rstA1 = asyncResetGen @DomA1 rstB2 = asyncResetGen @DomB2
asyncResetGen :: Reset domain Asynchronous Source #
Asynchronous reset generator, for simulations and the testBench function.
To be used like:
type DomA = Dom "A" 1000 rstA = asyncResetGen @DomA
NB: Can only be used for components with an active-high reset port, which all clash-prelude components are.
Example
type Dom2 = Dom "dom" 2 type Dom7 = Dom "dom" 7 type Dom9 = Dom "dom" 9 topEntity :: Clock Dom2 Source -> Clock Dom7 Source -> Clock Dom9 Source -> Signal Dom7 Integer -> Signal Dom9 Integer topEntity clk2 clk7 clk9 i = delay clk9 (unsafeSynchronizer clk2 clk9 (delay clk2 (unsafeSynchronizer clk7 clk2 (delay clk7 i)))) {--} testBench :: Signal Dom9 Bool testBench = done where testInput = stimuliGenerator clk7 rst7 $(listToVecTH [(1::Integer)..10]) expectedOutput = outputVerifier clk9 rst9 ((undefined :> undefined :> Nil) ++ $(listToVecTH ([2,3,4,5,7,8,9,10]::[Integer]))) done = expectedOutput (topEntity clk2 clk7 clk9 testInput) done' = not <$> done clk2 = tbClockGen @Dom2 (unsafeSynchronizer clk9 clk2 done') clk7 = tbClockGen @Dom7 (unsafeSynchronizer clk9 clk7 done') clk9 = tbClockGen @Dom9 done' rst7 =asyncResetGen
@Dom7 rst9 =asyncResetGen
@Dom9
syncResetGen :: (domain ~ Dom n clkPeriod, KnownNat clkPeriod) => Reset domain Synchronous Source #
Synchronous reset generator, for simulations and the testBench function.
To be used like:
type DomA = Dom "A" 1000 rstA = syncResetGen @DomA
NB: Can only be used for components with an active-high reset port, which all clash-prelude components are.
systemClockGen :: Clock System Source Source #
Clock generator for the System
clock domain.
NB: should only be used for simulation, and not for the testBench
function. For the testBench function, used tbSystemClockGen
tbSystemClockGen :: Signal System Bool -> Clock System Source Source #
Clock generator for the System
clock domain.
NB: can be used in the testBench function
Example
topEntity :: Vec 2 (Vec 3 (Unsigned 8)) -> Vec 6 (Unsigned 8)
topEntity = concat
testBench :: Signal System Bool
testBench = done
where
testInput = pure ((1 :> 2 :> 3 :> Nil) :> (4 :> 5 :> 6 :> Nil) :> Nil)
expectedOutput = outputVerifier ((1:>2:>3:>4:>5:>6:>Nil):>Nil)
done = exposeClockReset (expectedOutput (topEntity $ testInput)) clk rst
clk = tbSystemClockGen
(not <$> done)
rst = systemResetGen
systemResetGen :: Reset System Asynchronous Source #
Reset generator for the System
clock domain.
NB: should only be used for simulation or the testBench function.
Example
topEntity :: Vec 2 (Vec 3 (Unsigned 8)) -> Vec 6 (Unsigned 8)
topEntity = concat
testBench :: Signal System Bool
testBench = done
where
testInput = pure ((1 :> 2 :> 3 :> Nil) :> (4 :> 5 :> 6 :> Nil) :> Nil)
expectedOutput = outputVerifier ((1:>2:>3:>4:>5:>6:>Nil):>Nil)
done = exposeClockReset (expectedOutput (topEntity $ testInput)) clk rst
clk = tbSystemClockGen (not <$> done)
rst = systemResetGen
Boolean connectives
Product/Signal isomorphism
Isomorphism between a Signal
of a product type (e.g. a tuple) and a
product type of Signal'
s.
Instances of Bundle
must satisfy the following laws:
bundle
.unbundle
=id
unbundle
.bundle
=id
By default, bundle
and unbundle
, are defined as the identity, that is,
writing:
data D = A | B instance Bundle D
is the same as:
data D = A | B instance Bundle D where typeUnbundled'
clk D =Signal'
clk Dbundle
_ s = sunbundle
_ s = s
bundle :: Unbundled domain a -> Signal domain a Source #
Example:
bundle :: (Signal
domain a,Signal
domain b) ->Signal
clk (a,b)
However:
bundle ::Signal
domainBit
->Signal
domainBit
bundle :: Signal domain a ~ Unbundled domain a => Unbundled domain a -> Signal domain a Source #
Example:
bundle :: (Signal
domain a,Signal
domain b) ->Signal
clk (a,b)
However:
bundle ::Signal
domainBit
->Signal
domainBit
unbundle :: Signal domain a -> Unbundled domain a Source #
Example:
unbundle ::Signal
domain (a,b) -> (Signal
domain a,Signal
domain b)
However:
unbundle ::Signal
domainBit
->Signal
domainBit
unbundle :: Unbundled domain a ~ Signal domain a => Signal domain a -> Unbundled domain a Source #
Bundle Bool Source # | |
Bundle Double Source # | |
Bundle Float Source # | |
Bundle Int Source # | |
Bundle Integer Source # | |
Bundle () Source # | Note that: bundle :: () -> Signal domain () unbundle :: Signal domain () -> () |
Bundle Bit Source # | |
Bundle (Maybe a) Source # | |
Bundle (BitVector n) Source # | |
Bundle (Index n) Source # | |
Bundle (Unsigned n) Source # | |
Bundle (Signed n) Source # | |
Bundle (Either a b) Source # | |
Bundle (a, b) Source # | |
KnownNat n => Bundle (Vec n a) Source # | |
KnownNat d => Bundle (RTree d a) Source # | |
Bundle (a, b, c) Source # | |
Bundle (Fixed rep int frac) Source # | |
Bundle (a, b, c, d) Source # | |
Bundle (a, b, c, d, e) Source # | |
Bundle (a, b, c, d, e, f) Source # | |
Bundle (a, b, c, d, e, f, g) Source # | |
Bundle (a, b, c, d, e, f, g, h) Source # | |
Simulation functions (not synthesisable)
lazy versions
simulate_lazy :: (Signal domain1 a -> Signal domain2 b) -> [a] -> [b] Source #
List <-> Signal conversion (not synthesisable)
fromList :: NFData a => [a] -> Signal domain a Source #
Create a Signal
from a list
Every element in the list will correspond to a value of the signal for one clock cycle.
>>>
sampleN 2 (fromList [1,2,3,4,5])
[1,2]
NB: This function is not synthesisable
lazy versions
sample_lazy :: Foldable f => f a -> [a] Source #
sampleN_lazy :: Foldable f => Int -> f a -> [a] Source #
fromList_lazy :: [a] -> Signal domain a Source #
Create a Signal
from a list
Every element in the list will correspond to a value of the signal for one clock cycle.
>>>
sampleN 2 (fromList [1,2,3,4,5])
[1,2]
NB: This function is not synthesisable