General
- Name of function to be made top entity:
topEntity Domain: synthesis domain- techmap, I guess
System: Useful for simulation
- Convert to VHDL:
clash --vhdl MAC.hs Signalvalues represent synchronous values. ie, values that change with respect to a clock.- Functions without signals are purely combinatorial.
- Combinational circuits: (recursive) functions
- Sequential circuits: composition of functions together with a finite state machine like a Mealy machine.
- Clock, Reset, Enable signals are implicit by default when using
HiddenClockResetEnable.- These signals can be exposed with functions like
exposeClockResetEnable.
- These signals can be exposed with functions like
NFDataX
To be synthesizable, data type needs to be an instance of NFDataX, a typeclass defined in clash-prelude.
It's like NFData. NFData says that the type has a normal form (ie, is fully evaluatable and doesn't diverge). (I guess NF stands for 'normal form').
This makes the evaluation eager?
NFDataX is like NFData from deepseq package, but with support for undefined values.
Simulating a Mealy machine
(cf: from here)
+---------------+
Input --->-| |------> Output
| Combinational |
| logic |
+---| |---+
| +---------------+ |
Current ^ V Next
state | +---------------+ | state
| | | |
+-<-| Memory |-<-+
| elements |
+---------------+
–
-- | Function to run a mealy machine
run _ _ [] = [] -- no input means no output
run func state (input:inputs) = output: outputs
where
(output, state′) = func input state
outputs = run func state′ inputs
-- | Transition function for a mealy machine
func :: InputSignals → State a → (OutputSignals, State a)
func input state = (output, state′)
where
...
...–
Type:
mealy
:: (KnownDomain dom,
GHC.Classes.IP (Clash.Signal.HiddenClockName dom) (Clock dom),
GHC.Classes.IP (Clash.Signal.HiddenEnableName dom) (Enable dom),
GHC.Classes.IP (Clash.Signal.HiddenResetName dom) (Reset dom),
NFDataX s) =>
(s -> i -> (s, o)) -> s -> Signal dom i -> Signal dom o
Transition function of the mealy machine:
(s -- ^ current state
-> i -- ^ next input
-> (s, o)) -- ^ next state, next output (produced /during/ the transition)
s -- ^ initial state of mealy machine
-> Signal dom i -- ^ input signal
-> Signal dom o -- ^ output signal
Types
From base itself:
- Int8, Int16, Int32, Int64
- Word8, Word16, Word32, Word64
From clash-prelude:
- Bit
- Signed, Unsigned
Assign names to signals
à la ports in VHDL, I guess.
Use :::. An example: https://github.com/clash-lang/clash-compiler/pull/2584/files
Annotations
Control name of ports in generated HDL
Example:
{-# ANN topEntity
(Synthesize
{ t_name = "topEntity"
, t_inputs = [ PortName "clk"
, PortName "rst"
, PortName "en"
, PortName "inp" ]
, t_output = PortName "res"
}) #-}blackbox
- Used to say that a particular definition is not handled by clash and it is to be treated as a blackbox.
- HDL code will plugged in instead
CLASHOPAQUE
Becomes:
- NOINLINE in GHC<9.4
- OPAQUE in newer GHC
- https://github.com/clash-lang/clash-compiler/pull/2511
Testbenches
- Function name has to be
testBench testBenchhas to be in root module
https://hackage.haskell.org/package/clash-prelude-1.8.1/docs/Clash-Tutorial.html
outputVerifier- Returns
Truewhen all test cases have been tested - Otherwise keeps returning
False - Also writes error message to stderr when output is not as expected
- Returns
Simulate
simulate: give aSignal a -> Signal band get a[a] -> [b]simulate_lazy: lazy version ofsimulatesample: Convert aSignalinto an infinite listL.take 5 $ sample 10 top
sampleN: get n samples of aSignalas a listsampleN @System 5 $ register 8 $ fromList [1,2,3,4]
fromList: convert a list to aSignal
Bundling signals
https://hackage.haskell.org/package/clash-prelude-1.6.3/docs/Clash-Signal-Bundle.html
bundle takes a tuple of signals and makes a signal of tuples. Something like: (Signal dom a, Signal dom b) becoming Signal dom (a, b).
Similarly unbundle does it in the other direction. Signal of tuples becomes tuple of signals. Something like: Signal dom (a, b) becoming (Signal dom a, Signal dom b).
bundle :: Unbundled dom a -> Signal dom a
-- Example:
bundle :: (Signal dom a, Signal dom b) -> Signal dom (a,b)
unbundle :: Signal dom a -> Unbundled dom a
-- Example:
unbundle :: Signal dom (a,b) -> (Signal dom a, Signal dom b)BitVector
- BitVector indexing:
!- Index is descending. Right-most index is 0 (ie, LSB is right-most)
bar :: BitVector 8 -> (BitVector 2, BitVector 3, BitVector 3)
bar bv = let (zyx, cba, qp) = unpack bv in (qp, cba, zyx)
-- Another option: Using `slice`
koo :: BitVector 8 -> (BitVector 2, BitVector 3, BitVector 3)
koo bv = (slice d1 d0 bv, slice d4 d2 bv, slice d7 d5 bv)
-- Convoluted
foo :: BitVector 8 -> (BitVector 2, BitVector 3, BitVector 3)
foo bv = case bv of
$(bitPattern "zyxc_baqp") -> (pack (q ++# p), pack (c ++# b ++# a), pack (z ++# y ++# x))
Packing/unpacking:
λ> pack (Left () :: Either () Bool)
0b0.
λ> pack (Right False :: Either () Bool)
0b10
λ> pack (Right True :: Either () Bool)
0b11
-- The . is a don't care
λ> pack (((), True) :: ((), Bool))
0b1
λ> pack (((), False) :: ((), Bool))
0b0
Misc
- DDR signals: https://hackage.haskell.org/package/clash-prelude-1.6.6/docs/Clash-Explicit-DDR.html
- Special signals. Looks like they are made such that they are updated twice in a cycle when compared to normal
Signaltype.
- Special signals. Looks like they are made such that they are updated twice in a cycle when compared to normal
- Lion: A RISC-V CPU?? made with Clash
register
register
:: (HiddenClockResetEnable dom, NFDataX a)
=> a -> Signal dom a -> Signal dom a
Clash.Prelude> sampleN @System 5 $ register 8 $ fromList [1,2,3,4]
[8,8,2,3,4]
Bitvectors
Conversion between Bitvector and Int:
Clash.Prelude> pack (59 :: Unsigned 8)
0b0011_1011
Clash.Prelude> :t pack (59 :: Unsigned 8)
pack (59 :: Unsigned 8) :: BitVector 8
Clash.Prelude> :t pack (59 :: Unsigned 9)
pack (59 :: Unsigned 9) :: BitVector 9
Clash.Prelude> unpack 0b101 ::Signed 3
-3
Clash.Prelude> unpack 0b101 ::Unsigned 3
5
—
Some operations:
-- Bitwise OR
Clash.Prelude Prelude> (Clash.Prelude..|.) 0b1001 0b1010
11—
bLit:
Clash.Prelude> import qualified Data.List as List
Clash.Prelude List> $(bLit (List.replicate 4 '0'))
0b11117 segment with clash
| a | b | c | d | g | e | f | g | |
|---|---|---|---|---|---|---|---|---|
| 0 | T | T | T | T | T | T | T | |
| 1 | T | T | ||||||
| 2 | T | T | T | T | T | |||
| 3 | T | T | T | T |
import Data.Char
import qualified Data.Map
a :: [(Char, Vec 7 Bool)]
-- a b c d e f g
a = [('0', True :> True :> True :> True :> True :> True :> True :> Nil),
('1', False :> True :> True :> False :> False :> False :> False :> Nil),
('?', True :> True :> False :> False :> True :> False :> True :> Nil)]
b = Data.Map.fromList aConstructing Signal manually
Not synthesizable. Shouldn't be used in production code!!
λ> import Clash.Signal.Internal
λ> :t (1 :- 2)
(1 :- 2) :: Num a => Signal dom a
λ> 1 :: Signal System Int
1 1 1 1 1 ...............More
- Lion: Formally verified, pipelined RISC-V processor
- Slides from a clash talk: https://github.com/fp-syd/meetings/blob/master/2024/2024-11-27-Clash_Talk-Alex_Mason.pdf