-
Notifications
You must be signed in to change notification settings - Fork 147
/
Signal.hs
975 lines (910 loc) · 28.5 KB
/
Signal.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
{-|
Copyright : (C) 2013-2016, University of Twente,
2016-2019, Myrtle Software,
2017-2022, Google Inc.
2020 , Ben Gamari,
2021-2023, QBayLogic B.V.
License : BSD2 (see the file LICENSE)
Maintainer : QBayLogic B.V. <devops@qbaylogic.com>
Clash has synchronous 'Signal's in the form of:
@
'Signal' (dom :: 'GHC.TypeLits.Symbol') a
@
Where /a/ is the type of the value of the 'Signal', for example /Int/ or /Bool/,
and /dom/ is the /clock-/ (and /reset-/) domain to which the memory elements
manipulating these 'Signal's belong.
The type-parameter, /dom/, is of the kind 'Domain' - a simple string. That
string refers to a single /synthesis domain/. A synthesis domain describes the
behavior of certain aspects of memory elements in it. More specifically, a
domain looks like:
@
'DomainConfiguration'
{ _name:: 'GHC.TypeLits.Symbol'
-- ^ Domain name
, _period :: 'GHC.TypeLits.Nat'
-- ^ Clock period in /ps/
, _edge :: 'ActiveEdge'
-- ^ Active edge of the clock
, _reset :: 'ResetKind'
-- ^ Whether resets are synchronous (edge-sensitive) or asynchronous (level-sensitive)
, _init :: 'InitBehavior'
-- ^ Whether the initial (or "power up") value of memory elements is
-- unknown/undefined, or configurable to a specific value
, _polarity :: 'ResetPolarity'
-- ^ Whether resets are active high or active low
}
@
Check the documentation of each of the types to see the various options Clash
provides. In order to specify a domain, an instance of 'KnownDomain' should be
made. Clash provides a standard implementation, called 'System', that is
configured as follows:
@
instance KnownDomain "System" where
type KnownConf "System" = 'DomainConfiguration "System" 10000 'Rising 'Asynchronous 'Defined 'ActiveHigh
knownDomain = 'SDomainConfiguration' SSymbol SNat 'SRising' 'SAsynchronous' 'SDefined' 'SActiveHigh'
@
In words, \"System\" is a synthesis domain with a clock running with a period
of 10000 /ps/ (100 MHz). Memory elements update their state on the rising edge
of the clock, can be reset asynchronously with regards to the clock, and have
defined power up values if applicable.
In order to create a new domain, you don't have to instantiate it explicitly.
Instead, you can have 'createDomain' create a domain for you. You can also use
the same function to subclass existing domains.
* __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!
* __NB__: For the best compatibility make sure your period is divisible by 2,
because some VHDL simulators don't support fractions of picoseconds.
* __NB__: Whether 'System' has good defaults depends on your target platform.
Check out 'IntelSystem' and 'XilinxSystem' too!
=== Explicit clocks and resets, and meta-stability #metastability#
When using multiple clocks and/or reset lines there are ways to accidentally
introduce situations that are prone to
<https://en.wikipedia.org/wiki/Metastability_in_electronics metastability>.
These bugs are incredibly hard to debug as they often cannot be simulated, so
it's best to prevent them in the first place. This section outlines the
situations in which metastability arises and how to prevent it.
Two types of resets exist: synchronous and asynchronous resets. These reset
types are encoded in a synthesis domain. For the following examples we assume
the following exist:
@
'DomainConfiguration' \"SyncExample\" _period _edge 'Synchronous' _init
'DomainConfiguration' \"AsyncExample\" _period _edge 'Asynchronous' _init
@
See the previous section on how to use domains.
We now go over the clock and reset line combinations and explain when they
can potentially introduce situations prone to meta-stability:
* /Reset situation 1/:
@
f :: 'Reset' \"SyncExample\" -> 'Reset' \"SyncExample\" -> ..
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' \"AsyncExample\" -> 'Reset' \"AsyncExample\" -> ..
g x y = ..
@
This situation can be prone to metastability, because although /x/ and
/y/ belong to the same /domain/ according to their domain, 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.
* /Clock situation/:
@
k :: 'Clock' dom -> 'Clock' dom -> ..
k x y = ..
@
The situation above is potentially prone to metastability, because
even though /x/ and /y/ belong to the same /domain/ according to their
domain, 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.
-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE Trustworthy #-}
{-# OPTIONS_GHC -fplugin=GHC.TypeLits.Normalise #-}
{-# OPTIONS_GHC -fplugin=GHC.TypeLits.KnownNat.Solver #-}
{-# OPTIONS_GHC -Wno-orphans #-}
{-# OPTIONS_HADDOCK show-extensions #-}
module Clash.Explicit.Signal
( -- * Synchronous signal
Signal
, BiSignalIn
, BiSignalOut
, BiSignalDefault(..)
-- * Domain
, Domain
, KnownDomain(..)
, KnownConfiguration
, ActiveEdge(..)
, SActiveEdge(..)
, InitBehavior(..)
, SInitBehavior(..)
, ResetKind(..)
, SResetKind(..)
, ResetPolarity(..)
, SResetPolarity(..)
, DomainConfiguration(..)
, SDomainConfiguration(..)
-- ** Configuration type families
, DomainPeriod
, DomainActiveEdge
, DomainResetKind
, DomainInitBehavior
, DomainResetPolarity
-- ** Default domains
, System
, XilinxSystem
, IntelSystem
, vSystem
, vIntelSystem
, vXilinxSystem
-- ** Domain utilities
, VDomainConfiguration(..)
, vDomain
, createDomain
, knownVDomain
, clockPeriod
, activeEdge
, resetKind
, initBehavior
, resetPolarity
-- ** Enabling
, Enable
, toEnable
, fromEnable
, enableGen
-- * Clock
, Clock
, DiffClock
, periodToHz
, hzToPeriod
-- ** Synchronization primitive
, unsafeSynchronizer
, veryUnsafeSynchronizer
-- * Reset
, Reset
, unsafeToReset
, unsafeFromReset
, unsafeToActiveHigh
, unsafeToActiveLow
, unsafeFromActiveHigh
, unsafeFromActiveLow
-- * Basic circuit functions
, andEnable
, enable -- DEPRECATED
, dflipflop
, delay
, delayMaybe
, delayEn
, register
, regMaybe
, regEn
, mux
-- * Simulation and testbench functions
, clockGen
, resetGen
, resetGenN
, systemClockGen
, systemResetGen
-- * Boolean connectives
, (.&&.), (.||.)
-- * Product/Signal isomorphism
, Bundle(..)
, EmptyTuple(..)
, TaggedEmptyTuple(..)
-- * Simulation functions (not synthesizable)
, simulate
, simulateB
, simulateWithReset
, simulateWithResetN
, runUntil
-- ** lazy versions
, simulate_lazy
, simulateB_lazy
-- ** Automaton
, signalAutomaton
-- * List \<-\> Signal conversion (not synthesizable)
, sample
, sampleN
, sampleWithReset
, sampleWithResetN
, fromList
, fromListWithReset
-- ** lazy versions
, sample_lazy
, sampleN_lazy
, fromList_lazy
-- * QuickCheck combinators
, testFor
-- * Type classes
-- ** 'Eq'-like
, (.==.), (./=.)
-- ** 'Ord'-like
, (.<.), (.<=.), (.>=.), (.>.)
-- * Bisignal functions
, veryUnsafeToBiSignalIn
, readFromBiSignal
, writeToBiSignal
, mergeBiSignalOuts
-- * Deprecated
, unsafeFromHighPolarity
, unsafeFromLowPolarity
, unsafeToHighPolarity
, unsafeToLowPolarity
)
where
import Data.Bifunctor (bimap)
import Data.Int (Int64)
import Data.Maybe (isJust)
import GHC.TypeLits (type (<=))
import Clash.Annotations.Primitive (hasBlackBox)
import Clash.Promoted.Nat (SNat(..), snatToNum)
import Clash.Signal.Bundle
(Bundle (..), EmptyTuple(..), TaggedEmptyTuple(..), vecBundle#)
import Clash.Signal.BiSignal
import Clash.Signal.Internal
import Clash.Signal.Internal.Ambiguous
(knownVDomain, clockPeriod, activeEdge, resetKind, initBehavior, resetPolarity)
import qualified Clash.Sized.Vector
import Clash.XException
(NFDataX, deepErrorX, fromJustX, seqX, ShowX(..))
{- $setup
>>> :set -XDataKinds -XTypeApplications -XFlexibleInstances -XMultiParamTypeClasses -XTypeFamilies
>>> :set -fno-warn-deprecations
>>> :m -Prelude
>>> import Clash.Explicit.Prelude
>>> import Clash.Promoted.Nat (SNat(..))
>>> import qualified Data.List as L
>>> :{
instance KnownDomain "Dom2" where
type KnownConf "Dom2" = 'DomainConfiguration "Dom2" 2 'Rising 'Asynchronous 'Defined 'ActiveHigh
knownDomain = SDomainConfiguration SSymbol SNat SRising SAsynchronous SDefined SActiveHigh
:}
>>> :{
instance KnownDomain "Dom7" where
type KnownConf "Dom7" = 'DomainConfiguration "Dom7" 7 'Rising 'Asynchronous 'Defined 'ActiveHigh
knownDomain = SDomainConfiguration SSymbol SNat SRising SAsynchronous SDefined SActiveHigh
:}
>>> type Dom2 = "Dom2"
>>> type Dom7 = "Dom7"
>>> let clk2 = clockGen @Dom2
>>> let clk7 = clockGen @Dom7
>>> let en2 = enableGen @Dom2
>>> let en7 = enableGen @Dom7
>>> let oversampling clkA clkB enA enB dflt = delay clkB enB dflt . unsafeSynchronizer clkA clkB . delay clkA enA dflt
>>> let almostId clkA clkB enA enB dflt = delay clkB enB dflt . unsafeSynchronizer clkA clkB . delay clkA enA dflt . unsafeSynchronizer clkB clkA . delay clkB enB dflt
>>> let oscillate clk rst en = let s = register clk rst en False (not <$> s) in s
>>> let count clk rst en = let s = regEn clk rst en 0 (oscillate clk rst en) (s + 1) in s
>>> :{
sometimes1 clk rst en = s where
s = register clk rst en Nothing (switch <$> s)
switch Nothing = Just 1
switch _ = Nothing
:}
>>> :{
countSometimes clk rst en = s where
s = regMaybe clk rst en 0 (plusM (pure <$> s) (sometimes1 clk rst en))
plusM = liftA2 (liftA2 (+))
:}
-}
-- **Clock
-- | 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 'Clash.Explicit.Testbench.tbSystemClockGen'
systemClockGen
:: Clock System
systemClockGen = clockGen
-- | Reset generator for use in simulation, for the 'System' clock domain.
-- Asserts the reset for a single cycle.
--
-- __NB__: While this can be used in the @testBench@ function, it cannot be
-- synthesized to hardware.
--
-- === __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 = exposeClockResetEnable (expectedOutput (topEntity \<$\> testInput)) clk rst
-- clk = tbSystemClockGen (not <\$\> done)
-- rst = 'systemResetGen'
-- @
systemResetGen ::Reset System
systemResetGen = resetGen
-- ** Synchronization primitive
-- | The 'unsafeSynchronizer' function is a primitive that must be used to
-- connect one clock domain to the other, and will be synthesized to a (bundle
-- of) wire(s) in the eventual circuit. This function should only be used as
-- part of a proper synchronization component, such as the following dual
-- flip-flop synchronizer:
--
-- @
-- dualFlipFlop
-- :: Clock domA
-- -> Clock domB
-- -> Enable domA
-- -> Enable domB
-- -> Bit
-- -> Signal domA Bit
-- -> Signal domB Bit
-- dualFlipFlop clkA clkB enA enB dflt =
-- 'delay' clkB enB dflt . 'delay' clkB enB dflt . 'unsafeSynchronizer' clkA clkB
-- @
--
-- The 'unsafeSynchronizer' works in such a way that, given 2 clocks:
--
-- @
-- createDomain vSystem{vName=\"Dom7\", vPeriod=7}
--
-- clk7 :: 'Clock' Dom7
-- clk7 = 'clockGen'
--
-- en7 :: 'Enable' Dom7
-- en7 = 'enableGen'
-- @
--
-- and
--
-- @
-- createDomain vSystem{vName=\"Dom2\", vPeriod=2}
--
-- clk2 :: 'Clock' Dom2
-- clk2 = 'clockGen'
--
-- en2 :: 'Enable' Dom2
-- en2 = 'enableGen'
-- @
--
-- Oversampling followed by compression is the identity function plus 2 initial
-- values:
--
-- @
-- 'delay' clkB enB dflt $
-- 'unsafeSynchronizer' clkA clkB $
-- 'delay' clkA enA dflt $
-- 'unsafeSynchronizer' clkB clkA $
-- 'delay' clkB enB s
--
-- ==
--
-- dflt :- dflt :- s
-- @
--
-- Something we can easily observe:
--
-- @
-- oversampling clkA clkB enA enB dflt =
-- 'delay' clkB enB dflt
-- . 'unsafeSynchronizer' clkA clkB
-- . 'delay' clkA enA dflt
-- almostId clkA clkB enA enB dflt =
-- 'delay' clkB enB dflt
-- . 'unsafeSynchronizer' clkA clkB
-- . 'delay' clkA enA dflt
-- . 'unsafeSynchronizer' clkB clkA
-- . 'delay' clkB enB dflt
-- @
--
-- >>> sampleN 37 (oversampling clk7 clk2 en7 en2 0 (fromList [(1::Int)..10]))
-- [0,0,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]
-- >>> sampleN 12 (almostId clk2 clk7 en2 en7 0 (fromList [(1::Int)..10]))
-- [0,0,1,2,3,4,5,6,7,8,9,10]
unsafeSynchronizer
:: forall dom1 dom2 a
. ( KnownDomain dom1
, KnownDomain dom2 )
=> Clock dom1
-- ^ 'Clock' of the incoming signal
-> Clock dom2
-- ^ 'Clock' of the outgoing signal
-> Signal dom1 a
-> Signal dom2 a
unsafeSynchronizer clk1 clk2 =
go (clockTicks clk1 clk2)
where
go :: [ClockAB] -> Signal dom1 a -> Signal dom2 a
go [] _ = error "unsafeSynchronizer.go: `ticks` should have been an infinite list"
go (tick:ticks) ass@(~(a :- as)) =
case tick of
ClockA -> go ticks as
ClockB -> a :- go ticks ass
ClockAB -> go (ClockB:ClockA:ticks) ass
-- See: https://github.com/clash-lang/clash-compiler/pull/2511
{-# CLASH_OPAQUE unsafeSynchronizer #-}
{-# ANN unsafeSynchronizer hasBlackBox #-}
-- | Same as 'unsafeSynchronizer', but with manually supplied clock periods.
--
-- Note: this unsafeSynchronizer is defined to be consistent with the vhdl and verilog
-- implementations however as only synchronous signals are represented in Clash this
-- cannot be done precisely and can lead to odd behavior. For example,
--
-- @
-- sample $ unsafeSynchronizer @Dom2 @Dom7 . unsafeSynchronizer @Dom7 @Dom2 $ fromList [0..10]
-- > [0,4,4,4,7,7,7,7,11,11,11..
-- @
--
-- is quite different from the identity,
--
-- @
-- sample $ fromList [0..10]
-- > [0,1,2,3,4,5,6,7,8,9,10..
-- @
--
-- with values appearing from the "future".
veryUnsafeSynchronizer
:: Either Int (Signal dom1 Int)
-- ^ Period of clock belonging to @dom1@. 'Left' if clock has a static period,
-- 'Right' if periods are dynamic.
-> Either Int (Signal dom2 Int)
-- ^ Period of clock belonging to @dom2@. 'Left' if clock has a static period,
-- 'Right' if periods are dynamic.
-> Signal dom1 a
-> Signal dom2 a
veryUnsafeSynchronizer t1e t2e =
go (clockTicksEither (toInt64 t1e) (toInt64 t2e))
where
-- TODO: deprecate 'veryUnsafeSynchronizer' or change its type signature to use
-- 'Int64' to prevent issues down the road if/when we switch to represent
-- clock periods using femtoseconds.
toInt64 ::
forall dom .
Either Int (Signal dom Int) ->
Either Int64 (Signal dom Int64)
toInt64 = bimap fromIntegral (fmap fromIntegral)
go :: [ClockAB] -> Signal dom1 a -> Signal dom2 a
go [] _ = error "veryUnsafeSynchronizer.go: `ticks` should have been an infinite list"
go (tick:ticks) ass@(~(a :- as)) =
case tick of
ClockA -> go ticks as
ClockB -> a :- go ticks ass
ClockAB -> go (ClockB:ClockA:ticks) ass
-- See: https://github.com/clash-lang/clash-compiler/pull/2511
{-# CLASH_OPAQUE veryUnsafeSynchronizer #-}
{-# ANN veryUnsafeSynchronizer hasBlackBox #-}
-- * Basic circuit functions
-- | Merge enable signal with signal of bools by applying the boolean AND
-- operation.
andEnable
:: Enable dom
-> Signal dom Bool
-> Enable dom
andEnable e0 e1 =
toEnable (fromEnable e0 .&&. e1)
{-# INLINE enable #-}
-- | Merge enable signal with signal of bools by applying the boolean AND
-- operation.
enable
:: Enable dom
-> Signal dom Bool
-> Enable dom
enable = andEnable
{-# DEPRECATED enable
"Use 'andEnable' instead. This function will be removed in Clash 1.8." #-}
-- | Special version of 'delay' that doesn't take enable signals of any kind.
-- Initial value will be undefined.
dflipflop
:: ( KnownDomain dom
, NFDataX a )
=> Clock dom
-> Signal dom a
-> Signal dom a
dflipflop = \clk i ->
delay#
clk
(toEnable (pure True))
(deepErrorX "First value of dflipflop undefined")
i
{-# INLINE dflipflop #-}
-- | \"@'delay' clk s@\" delays the values in 'Signal' /s/ for once cycle, the
-- value at time 0 is /dflt/.
--
-- >>> sampleN 3 (delay systemClockGen enableGen 0 (fromList [1,2,3,4]))
-- [0,1,2]
delay
:: ( KnownDomain dom
, NFDataX a )
=> Clock dom
-- ^ Clock
-> Enable dom
-- ^ Global enable
-> a
-- ^ Initial value
-> Signal dom a
-> Signal dom a
delay = delay#
{-# INLINE delay #-}
-- | Version of 'delay' that only updates when its third argument is a 'Just'
-- value.
--
-- >>> let input = fromList [Just 1, Just 2, Nothing, Nothing, Just 5, Just 6, Just (7::Int)]
-- >>> sampleN 7 (delayMaybe systemClockGen enableGen 0 input)
-- [0,1,2,2,2,5,6]
delayMaybe
:: ( KnownDomain dom
, NFDataX a )
=> Clock dom
-- ^ Clock
-> Enable dom
-- ^ Global enable
-> a
-- ^ Initial value
-> Signal dom (Maybe a)
-> Signal dom a
delayMaybe = \clk gen dflt i ->
delay# clk (enable gen (isJust <$> i)) dflt (fromJustX <$> i)
{-# INLINE delayMaybe #-}
-- | Version of 'delay' that only updates when its third argument is asserted.
--
-- >>> let input = fromList [1,2,3,4,5,6,7::Int]
-- >>> let enable = fromList [True,True,False,False,True,True,True]
-- >>> sampleN 7 (delayEn systemClockGen enableGen 0 enable input)
-- [0,1,2,2,2,5,6]
delayEn
:: ( KnownDomain dom
, NFDataX a )
=> Clock dom
-- ^ Clock
-> Enable dom
-- ^ Global enable
-> a
-- ^ Initial value
-> Signal dom Bool
-- ^ Enable
-> Signal dom a
-> Signal dom a
delayEn = \clk gen dflt en i ->
delay# clk (enable gen en) dflt i
{-# INLINE delayEn #-}
-- | \"@'register' clk rst en i s@\" delays the values in 'Signal' /s/ for one
-- cycle, and sets the value to @i@ the moment the reset becomes 'False'.
--
-- >>> sampleN 5 (register systemClockGen resetGen enableGen 8 (fromList [1,1,2,3,4]))
-- [8,8,1,2,3]
register
:: ( KnownDomain dom
, NFDataX a )
=> Clock dom
-- ^ clock
-> Reset dom
-- ^ Reset, 'register' outputs the reset value when the reset is active
-> Enable dom
-- ^ Global enable
-> a
-- ^ Reset value. If the domain has initial values enabled, the reset value
-- will also be the initial value.
-> Signal dom a
-> Signal dom a
register = \clk rst gen initial i ->
register# clk rst gen initial initial i
{-# INLINE register #-}
-- | Version of 'register' that only updates its content when its fourth
-- argument is a 'Just' value. So given:
--
-- @
-- sometimes1 clk rst en = s where
-- s = 'register' clk rst en Nothing (switch '<$>' s)
--
-- switch Nothing = Just 1
-- switch _ = Nothing
--
-- countSometimes clk rst en = s where
-- s = 'regMaybe' clk rst en 0 (plusM ('pure' '<$>' s) (sometimes1 clk rst en))
-- plusM = liftA2 (liftA2 (+))
-- @
--
-- We get:
--
-- >>> sampleN 9 (sometimes1 systemClockGen resetGen enableGen)
-- [Nothing,Nothing,Just 1,Nothing,Just 1,Nothing,Just 1,Nothing,Just 1]
-- >>> sampleN 9 (count systemClockGen resetGen enableGen)
-- [0,0,0,1,1,2,2,3,3]
regMaybe
:: ( KnownDomain dom
, NFDataX a )
=> Clock dom
-- ^ Clock
-> Reset dom
-- ^ Reset, 'regMaybe' outputs the reset value when the reset value is active
-> Enable dom
-- ^ Global enable
-> a
-- ^ Reset value. If the domain has initial values enabled, the reset value
-- will also be the initial value.
-> Signal dom (Maybe a)
-> Signal dom a
regMaybe = \clk rst en initial iM ->
register# clk rst (enable en (fmap isJust iM)) initial initial (fmap fromJustX iM)
{-# INLINE regMaybe #-}
-- | Version of 'register' that only updates its content when its fourth
-- argument is asserted. So given:
--
-- @
-- oscillate clk rst en = let s = 'register' clk rst en False (not \<$\> s) in s
-- count clk rst en = let s = 'regEn clk rst en 0 (oscillate clk rst en) (s + 1) in s
-- @
--
-- We get:
--
-- >>> sampleN 9 (oscillate systemClockGen resetGen enableGen)
-- [False,False,True,False,True,False,True,False,True]
-- >>> sampleN 9 (count systemClockGen resetGen enableGen)
-- [0,0,0,1,1,2,2,3,3]
regEn
:: ( KnownDomain dom
, NFDataX a
)
=> Clock dom
-- ^ Clock
-> Reset dom
-- ^ Reset, 'regEn' outputs the reset value when the reset value is active
-> Enable dom
-- ^ Global enable
-> a
-- ^ Reset value. If the domain has initial values enabled, the reset value
-- will also be the initial value.
-> Signal dom Bool
-- ^ Enable signal
-> Signal dom a
-> Signal dom a
regEn = \clk rst gen initial en i ->
register# clk rst (enable gen en) initial initial i
{-# INLINE regEn #-}
-- * Simulation functions
-- | Same as 'simulate', but with the reset line asserted for /n/ cycles. Similar
-- to 'simulate', 'simulateWithReset' will drop the output values produced while
-- the reset is asserted. While the reset is asserted, the first value from
-- @[a]@ is fed to the circuit.
simulateWithReset
:: forall dom a b m
. ( KnownDomain dom
, NFDataX a
, NFDataX b
, 1 <= m )
=> SNat m
-- ^ Number of cycles to assert the reset
-> a
-- ^ Reset value
-> ( KnownDomain dom
=> Clock dom
-> Reset dom
-> Enable dom
-> Signal dom a
-> Signal dom b )
-- ^ Circuit to simulate
-> [a]
-> [b]
simulateWithReset m resetVal f as =
drop (snatToNum m) out
where
inp = replicate (snatToNum m) resetVal ++ as
rst = resetGenN @dom m
clk = clockGen
en = enableGen
out = simulate (f clk rst en) inp
-- See: https://github.com/clash-lang/clash-compiler/pull/2511
{-# CLASH_OPAQUE simulateWithReset #-}
-- | Same as 'simulateWithReset', but only sample the first /Int/ output values.
simulateWithResetN
:: ( KnownDomain dom
, NFDataX a
, NFDataX b
, 1 <= m )
=> SNat m
-- ^ Number of cycles to assert the reset
-> a
-- ^ Reset value
-> Int
-- ^ Number of cycles to simulate (excluding cycle spent in reset)
-> ( KnownDomain dom
=> Clock dom
-> Reset dom
-> Enable dom
-> Signal dom a
-> Signal dom b )
-- ^ Circuit to simulate
-> [a]
-> [b]
simulateWithResetN nReset resetVal nSamples f as =
take nSamples (simulateWithReset nReset resetVal f as)
{-# INLINE simulateWithResetN #-}
-- | Simulate a (@'Unbundled' a -> 'Unbundled' b@) function given a list of
-- samples of type /a/
--
-- >>> simulateB (unbundle . register systemClockGen resetGen enableGen (8,8) . bundle) [(1,1), (1,1), (2,2), (3,3)] :: [(Int,Int)]
-- [(8,8),(8,8),(1,1),(2,2),(3,3)...
-- ...
--
-- __NB__: This function is not synthesizable
simulateB
:: (Bundle a, Bundle b, NFDataX a, NFDataX b)
=> (Unbundled dom1 a -> Unbundled dom2 b)
-- ^ The function we want to simulate
-> [a]
-- ^ Input samples
-> [b]
simulateB f = simulate (bundle . f . unbundle)
-- | /Lazily/ simulate a (@'Unbundled' a -> 'Unbundled' b@) function given a
-- list of samples of type /a/
--
-- >>> simulateB (unbundle . register systemClockGen resetGen enableGen (8,8) . bundle) [(1,1), (1,1), (2,2), (3,3)] :: [(Int,Int)]
-- [(8,8),(8,8),(1,1),(2,2),(3,3)...
-- ...
--
-- __NB__: This function is not synthesizable
simulateB_lazy
:: (Bundle a, Bundle b)
=> (Unbundled dom1 a -> Unbundled dom2 b)
-- ^ The function we want to simulate
-> [a]
-- ^ Input samples
-> [b]
simulateB_lazy f = simulate_lazy (bundle . f . unbundle)
-- | Like 'fromList', but resets on reset and has a defined reset value.
--
-- >>> let rst = unsafeFromActiveHigh (fromList [True, True, False, False, True, False])
-- >>> let res = fromListWithReset @System rst Nothing [Just 'a', Just 'b', Just 'c']
-- >>> sampleN 6 res
-- [Nothing,Nothing,Just 'a',Just 'b',Nothing,Just 'a']
--
-- __NB__: This function is not synthesizable
fromListWithReset
:: forall dom a
. (KnownDomain dom, NFDataX a)
=> Reset dom
-> a
-> [a]
-> Signal dom a
fromListWithReset rst resetValue vals =
go (unsafeToActiveHigh rst) vals
where
go (r :- rs) _ | r = resetValue :- go rs vals
go (_ :- rs) [] = deepErrorX "fromListWithReset: input ran out" :- go rs []
go (_ :- rs) (a : as) = a :- go rs as
-- | Get a list of samples from a 'Signal', while asserting the reset line
-- for /n/ clock cycles. 'sampleWithReset' does not return the first /n/ cycles,
-- i.e., when the reset is asserted.
--
-- __NB__: This function is not synthesizable
sampleWithReset
:: forall dom a m
. ( KnownDomain dom
, NFDataX a
, 1 <= m )
=> SNat m
-- ^ Number of cycles to assert the reset
-> (KnownDomain dom
=> Clock dom
-> Reset dom
-> Enable dom
-> Signal dom a)
-- ^ 'Signal' to sample
-> [a]
sampleWithReset nReset f0 =
let f1 = f0 clockGen (resetGenN @dom nReset) enableGen in
drop (snatToNum nReset) (sample f1)
-- See: https://github.com/clash-lang/clash-compiler/pull/2511
{-# CLASH_OPAQUE sampleWithReset #-}
-- | Get a fine list of /m/ samples from a 'Signal', while asserting the reset line
-- for /n/ clock cycles. 'sampleWithReset' does not return the first /n/ cycles,
-- i.e., while the reset is asserted.
--
-- __NB__: This function is not synthesizable
sampleWithResetN
:: forall dom a m
. ( KnownDomain dom
, NFDataX a
, 1 <= m )
=> SNat m
-- ^ Number of cycles to assert the reset
-> Int
-- ^ Number of samples to produce
-> (KnownDomain dom
=> Clock dom
-> Reset dom
-> Enable dom
-> Signal dom a)
-- ^ 'Signal' to sample
-> [a]
sampleWithResetN nReset nSamples f =
take nSamples (sampleWithReset nReset f)
-- | Simulate a component until it matches a condition
--
-- It prints a message of the form
--
-- @
-- Signal sampled for N cycles until value X
-- @
--
-- __NB__: This function is not synthesizable
--
-- === __Example with test bench__
--
-- A common usage is with a test bench using
-- 'Clash.Explicit.Testbench.outputVerifier'.
--
-- __NB__: Since this uses 'Clash.Explicit.Testbench.assert', when using
-- @clashi@, read the note at "Clash.Explicit.Testbench#assert-clashi".
--
-- @
-- import Clash.Prelude
-- import Clash.Explicit.Testbench
--
-- topEntity
-- :: 'Signal' 'System' Int
-- -> 'Signal' 'System' Int
-- topEntity = id
--
-- testBench
-- :: 'Signal' 'System' Bool
-- testBench = done
-- where
-- testInput = 'Clash.Explicit.Testbench.stimuliGenerator' clk rst $('Clash.Sized.Vector.listToVecTH' [1 :: Int .. 10])
-- expectedOutput =
-- 'Clash.Explicit.Testbench.outputVerifier'' clk rst $('Clash.Sized.Vector.listToVecTH' $ [1 :: Int .. 9] '<>' [42])
-- done = expectedOutput $ topEntity testInput
-- clk = 'Clash.Explicit.Testbench.tbSystemClockGen' (not \<$\> done)
-- rst = 'systemResetGen'
-- @
--
-- @
-- > runUntil id testBench
--
--
-- cycle(\<Clock: System\>): 10, outputVerifier
-- expected value: 42, not equal to actual value: 10
-- Signal sampled for 11 cycles until value True
-- @
--
-- When you need to verify multiple test benches, the following invocations come
-- in handy:
--
-- @
-- > 'mapM_' (runUntil id) [ testBenchA, testBenchB ]
-- @
--
-- or when the test benches are in different clock domains:
--
-- @
-- testBenchA :: Signal DomA Bool
-- testBenchB :: Signal DomB Bool
-- @
--
-- @
-- > 'sequence_' [ runUntil id testBenchA, runUntil id testBenchB ]
-- @
runUntil
:: forall dom a
. (KnownDomain dom, NFDataX a, ShowX a)
=> (a -> Bool)
-- ^ Condition checking function, should return @True@ to finish run
-> Signal dom a
-- ^ 'Signal' we want to sample for the condition
-> IO ()
runUntil check s =
-- Ensure invocations of 'trace' are printed before the result message
value `seqX`
putStrLn msg
where
msg = ("Signal sampled for " ++) . shows nSamples
. (" cycles until value " ++) $ showX value
(before, after) = break check $ sample s
nSamples = length before
value = head after
{-# RULES "sequenceAVecSignal" Clash.Sized.Vector.traverse# (\x -> x) = vecBundle# #-}