Clash


General

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:

From clash-prelude:

Assign names to signals

à la ports in VHDL, I guess.

Use :::. An example: https://github.com/clash-lang/clash-compiler/pull/2584/files

Annotations

https://hackage-content.haskell.org/package/clash-prelude-1.8.2/docs/Clash-Annotations-Primitive.html

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

CLASHOPAQUE

Becomes:

Testbenches

https://hackage.haskell.org/package/clash-prelude-1.8.1/docs/Clash-Tutorial.html

Simulate

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

https://hackage-content.haskell.org/package/clash-prelude-1.8.2/docs/Clash-Class-BitPack.html#t:BitPack

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

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'))
0b1111

7 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 a

Constructing 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