- digital IC design engineer
- interested in FOSS, long time GNU/Linux user
- familiar with functional programming
- Clash is a functional hardware description language that borrows both its syntax and semantics from the functional programming language Haskell.
- compile a subset of Haskell to synthesizable HDL(verilog/VHDL/systemverilog)
- Strongly typed
- Interactive REPL(clashi)
- Low-level access(blackbox)
- ghc: Glasgow Haskell Compiler
- ghci: REPL from ghc
- :t, type
- :i, info
- every function has no side effect
- do not apply unless necessary
- list & infinite list
- function is variable
- ordinary variable is function with 0 args
- so, variable is immutable
- f x y = (f x) y
- f x y z = (f x y) z = ((f x) y) z
- let add = (+)
- let f5 = add 5
- f5 6 == 11
- container with some type
- []
- Maybe
- fmap (+ 1) [1,2,3] == [2,3,4]
- fmap (+ 1) $ Just 4 == Just 5
- fmap (+ 1) None == None
- fmap (fmap f5) [Just 5, Nothing, Just 3]
- container, can appy inside container
- [(* 2)] <*> [1, 2, 3]
- Just (+ 5) <*> Just 6
- pure (+) <*> Just 5 <*> Just 6
-- reciprocal calculate, 1/d
f d x = x * (2 - d * x)
f 0.7 1
(f 0.7) 1
f 0.7 $ 1
f 0.7 $ f 0.7 $ 1
f 0.7 $ f 0.7 $ f 0.7 $ 1
(f 0.7) . (f 0.7) . (f 0.7) $ 1
g = f 0.7
g . g . g $ 1
foldr (.) id [f 0.7, f 0.7, f 0.7] $ 1
foldr (.) id (replicate 3 $ f 0.7) $ 1
recipI n d = foldr (.) id (replicate n $ f d) $ 1
data Signal (dom :: Domain) a
= a :- Signal dom a
head# :: Signal dom a -> a
head# (x' :- _ ) = x'
tail# :: Signal dom a -> Signal dom a
tail# (_ :- xs') = xs'
instance Functor (Signal dom)
instance Applicative (Signal dom)
data [] a = [] | a : [a]
- sampleN @System 5 (pure 4)
- a = fromList [1..]
- sampleN @System 10 a
- b = fromList [2..]
- sampleN @System 10 b
- c = a * (pure 2)
- sampleN @System 10 c
- sampleN @System 10 $ b + c
instance Num a => Num (Signal dom a)
instance Fractional a => Fractional (Signal dom a)
instance Functor (Signal dom)
instance Applicative (Signal dom)
- sampleN @System 10 $ fmap not $ pure False
- sampleN @System 10 $ not <$> pure False
- sampleN @System 10 $ Just <$> fromList [1..]
- sampleN @System 10 $ pure not <*> pure False
- sampleN @System 10 $ pure 5 .>. fromList [1..]
register ::
(HiddenClockResetEnable dom, NFDataX a) =>
a -> Signal dom a -> Signal dom a
-- Defined in ‘Clash.Signal’
infixr 3 `register`
- a = fromList [1..]
- b = register 8 a
- sampleN @System 10 b – NOTE, first 1 power-up value, then reset value
- simulateN @System 10 (register 8) [1..] – power-up value not included
- x = register False $ not <$> x
- sampleN @System 10 x
fir coeffs x = dotp coeffs (window x)
where
dotp as bs = sum (zipWith (*) as bs)
-- inferred: Signal dom Int -> Signal dom Int
fir3int = fir (3 :> 4 :> 5 :> Nil)
-- inferred: Signal dom Float -> Signal dom Float
fir4float = fir (3.5 :> 4.2 :> 3.0 :> 6.1 :> Nil)
see ./src/FIR.hs
see ./src/Recip.hs
import Data.List as L
import Prelude as P
recipStep d x = x * (2 - d * x)
recipI :: (Num a) => Int -> a -> a
recipI n d = L.foldr (.) id (L.replicate n $ recipStep d) $ 1
recipFull :: (Scalable a) => Int -> a -> a
recipFull k x = scaleReverse (recipI k y) n
where
(y, n) = unscale x
class (RealFrac a, Integral (ScaleIndex a)) => Scalable a where
type ScaleIndex a :: Type
unscale :: a -> (a, ScaleIndex a)
scale :: a -> (ScaleIndex a) -> a
scaleReverse :: a -> (ScaleIndex a) -> a
-- scale (fst $ unscale x) (snd $ unscale x) == x
-- scale x n * scaleReverse x n == x * x
- default implementation for Double, Float & Ratio
- different implementation for UFixed, make synthesizable
recipI 4 0.3 :: Double
recipI 6 0.3 :: Double
recipI 4 3 :: Double
recipI 6 3 :: Double
recipFull 6 0.7 :: Double
recipFull 6 0.7 :: UFixed 8 16
recipFull 6 (7 % 10)
recipFull 6 3 :: Double
recipFull 6 3 :: UFixed 8 16
recipFull 6 (3 % 1)
{-# ANN recip4_
(Synthesize
{ t_name = "recip4_"
, t_inputs = [PortName "x"]
, t_output = PortName "y"
}) #-}
recip4_ :: UFixed 8 24 -> UFixed 8 24
recip4_ = recipI 4
{-# ANN recip4
(Synthesize
{ t_name = "recip4"
, t_inputs = [PortName "x"]
, t_output = PortName "y"
}) #-}
recip4 :: UFixed 8 24 -> UFixed 8 24
recip4 = recipFull 4
L.map recip4_ [0.1, 0.3, 0.5, 0.7, 1.0, 1.8, 2.5, 3.0]
mapM_ (print . recip4_) [0.1, 0.3, 0.5, 0.7, 1.0, 1.8, 2.5, 3.0]
recipI 4 (2.5 :: Double)
recipI 1 (2.5 :: Double)
recipI 1 (2.5 :: UFixed 8 24)
(0.5 :: UFixed 8 24) - 1.0
mapM_ (print . recip4) [0.1, 0.3, 0.5, 0.7, 1.0, 1.8, 2.5, 3.0]
module tb;
wire [31:0] out1, out2;
reg [31:0] in;
localparam SCALE = 'h100_0000;
recip4_ dut1(.x(in), .y(out1));
recip4 dut2(.x(in), .y(out2));
task t(input real x);
begin
in = x * SCALE;
#1;
$display("%f:\t%.9f\t%.9f", x, 1.0 * out1 / SCALE, 1.0 * out2 / SCALE);
end
endtask
initial begin
$display("input:\t\trecip4_\t\trecip4");
t(0.1);
t(0.3);
t(0.5);
t(0.7);
t(1.0);
t(1.8);
t(2.5);
t(3.0);
end
endmodule
- one iteration per cycle
- register unscale step
- register scale step
- chisel DecoupledIO alike IO
- in_data, in_esp, in_valid, in_ready(out)
- out_data, out_cnt, out_valid, out_ready(in)
y
, unscale from in_data, when input readyn
, unscale from in_data, when input readyesp
, register when input readystate
, state of state machineres
, unscaled resultout_cnt
out_data
, finally output, fromres
&n
- state
- IDLE
- BUSY
- out_cnt <= out_cnt + 1
- res <= res * (2 - y * res) – res’
- DONE
- output
- in_ready <= state == IDLE
- out_valid <= state == DONE
- transition
- IDLE -> BUSY condition: in_ready & in_valid state: (y, n) <= unscale in_data, res <= 1.0, out_cnt <= 0
- BUSY -> DONE condition: abs(res - res’) < esp -> DONE state: out_data <= scaleReverse res n
- DONE -> IDLE condition: out_valid & out_ready
see ./src/RecipSeq.hs for implementation
testRecipSeq :: (Scalable a, Num b) =>
a -> a -> SS (Bool, a, b, Bool)
sampleN 10 $ testRecipSeq 0.7 (1e-5 :: Double)
sampleN 10 $ testRecipSeq 0.7 (1e-5 :: UFixed 8 24)
sampleN 10 $ testRecipSeq (7 % 10) 1e-5
sampleN 10 $ testRecipSeq 3 (1e-10 :: Double)
(intentionally blank)
see ./src/Recip.hs
- https://downloads.haskell.org/ghc/latest/docs/html/users_guide/
- https://hackage.haskell.org/package/clash-prelude-1.6.3/docs/Clash-Prelude.html
- https://hackage.haskell.org/package/clash-prelude-1.6.3/docs/Clash-Signal.html
- https://hackage.haskell.org/package/clash-prelude-1.6.3/docs/src/Clash.Signal.Internal.html#Signal
- https://hackage.haskell.org/package/clash-prelude-1.6.3/docs/Clash-Annotations-TopEntity.html#v:Synthesize
- https://github.com/yuchangyuan/short_intro_to_clash