clash-prelude 0.4 → 0.5
raw patch · 21 files changed
+2958/−570 lines, 21 files
Files
- CHANGELOG.md +7/−0
- README.md +1/−1
- clash-prelude.cabal +16/−3
- src/CLaSH/Class/BitVector.hs +1/−1
- src/CLaSH/Class/Num.hs +9/−1
- src/CLaSH/Prelude.hs +213/−50
- src/CLaSH/Prelude/Explicit.hs +393/−0
- src/CLaSH/Promoted/Bool.hs +0/−11
- src/CLaSH/Promoted/Nat.hs +11/−2
- src/CLaSH/Promoted/Nat/Literals.hs +12/−2
- src/CLaSH/Promoted/Nat/TH.hs +24/−18
- src/CLaSH/Promoted/Ord.hs +1/−2
- src/CLaSH/Signal.hs +0/−286
- src/CLaSH/Signal/Explicit.hs +355/−0
- src/CLaSH/Signal/Implicit.hs +248/−0
- src/CLaSH/Signal/Types.hs +96/−0
- src/CLaSH/Sized/Fixed.hs +471/−106
- src/CLaSH/Sized/Signed.hs +19/−9
- src/CLaSH/Sized/Unsigned.hs +15/−5
- src/CLaSH/Sized/Vector.hs +206/−73
- src/CLaSH/Tutorial.hs +860/−0
CHANGELOG.md view
@@ -1,5 +1,12 @@ # Changelog for [`clash-prelude` package](http://hackage.haskell.org/package/clash-prelude) +## 0.5 *April 3rd 2014*+ * Add explicitly clocked synchronous signals for multi-clock circuits++## 0.4.1 *March 27th 2014*+ * Add saturation to fixed-point operators+ * Finalize most documentation+ ## 0.4 *March 20th 2014* * Add fixed-point integers * Extend documentation
README.md view
@@ -1,2 +1,2 @@ = WARNING =-Only works with latest version of GHC-HEAD (https://github.com/ghc/ghc)!+Only works with GHC-7.8 (http://www.haskell.org/ghc/dist/7.8.1-rc2/)!
clash-prelude.cabal view
@@ -1,5 +1,5 @@ Name: clash-prelude-Version: 0.4+Version: 0.5 Synopsis: CAES Language for Synchronous Hardware - Prelude library Description: CλaSH (pronounced ‘clash’) is a functional hardware description language that@@ -12,6 +12,15 @@ This package provides: . * Prelude library containing datatypes and functions for circuit design+ .+ To use the library:+ .+ * Import "CLaSH.Prelude"+ * Additionally import "CLaSH.Prelude.Explicit" if you want to design+ explicitly clocked circuits in a multi-clock setting+ .+ A preliminary version of a tutorial can be found in "CLaSH.Tutorial", for a+ general overview of the library you should however check out "CLaSH.Prelude". Homepage: http://christiaanb.github.io/clash2/ bug-reports: http://github.com/christiaanb/clash-prelude/issues License: BSD3@@ -41,16 +50,20 @@ CLaSH.Class.BitVector CLaSH.Class.Num CLaSH.Prelude- CLaSH.Promoted.Bool+ CLaSH.Prelude.Explicit CLaSH.Promoted.Nat CLaSH.Promoted.Nat.TH CLaSH.Promoted.Nat.Literals CLaSH.Promoted.Ord- CLaSH.Signal+ CLaSH.Signal.Explicit+ CLaSH.Signal.Implicit CLaSH.Sized.Fixed CLaSH.Sized.Signed CLaSH.Sized.Unsigned CLaSH.Sized.Vector+ CLaSH.Tutorial++ Other-modules: CLaSH.Signal.Types Build-depends: base >= 4.7.0.0 && < 5, data-default >= 0.5.3,
src/CLaSH/Class/BitVector.hs view
@@ -9,7 +9,7 @@ import CLaSH.Sized.Vector import GHC.TypeLits --- | Convert types from and to a vector of @Bit@s+-- | Convert types from and to a 'Vec'tor of 'Bit's class BitVector a where -- | Number of 'Bit's needed to represents elements of type @a@ type BitSize a :: Nat
src/CLaSH/Class/Num.hs view
@@ -1,14 +1,22 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE TypeFamilies #-} module CLaSH.Class.Num where +import GHC.TypeLits++-- | Implicitly adding or subtracting values of two different (sub-)types. class Add a b where type AResult a b plus :: a -> b -> AResult a b minus :: a -> b -> AResult a b +-- | Implicitly multiplying values of two different (sub-)types. class Mult a b where type MResult a b mult :: a -> b -> MResult a b++-- | Coerce a value to be represented by a different number of bits+class Resize f where+ resize :: (KnownNat a, KnownNat b) => f a -> f b
src/CLaSH/Prelude.hs view
@@ -1,11 +1,32 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ImplicitParams #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE KindSignatures #-} {-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -O0 -fno-omit-interface-pragmas #-} +{- |+ CλaSH (pronounced ‘clash’) is a functional hardware description language that+ borrows both its syntax and semantics from the functional programming language+ Haskell. The merits of using a functional language to describe hardware comes+ from the fact that combinational circuits can be directly modeled as+ mathematical functions and that functional languages lend themselves very well+ at describing and (de-)composing mathematical functions.++ This package provides:++ * Prelude library containing datatypes and functions for circuit design++ To use the library:++ * Import "CLaSH.Prelude"+ * Additionally import "CLaSH.Prelude.Explicit" if you want to design+ explicitly clocked circuits in a multi-clock setting++ For now, "CLaSH.Prelude" is also the best starting point for exploring the+ library. A tutorial module will be added within due time.+-} module CLaSH.Prelude ( -- * Creating synchronous sequential circuits (<^>)@@ -23,40 +44,75 @@ -- * Utility functions , window , windowD- , module Exported+ -- * Testbench functions+ , sassert+ , stimuliGenerator+ , outputVerifier+ -- * Exported modules+ -- ** Implicitly clocked synchronous signals+ , module CLaSH.Signal.Implicit+ -- ** Datatypes+ , module CLaSH.Bit+ -- *** Arbitrary-width numbers+ , module CLaSH.Sized.Signed+ , module CLaSH.Sized.Unsigned+ -- *** Fixed point numbers+ , module CLaSH.Sized.Fixed+ -- *** Fixed size vectors+ , module CLaSH.Sized.Vector+ -- ** Type-level natural numbers+ , module GHC.TypeLits+ , module CLaSH.Promoted.Nat+ , module CLaSH.Promoted.Nat.Literals+ , module CLaSH.Promoted.Nat.TH+ -- ** Type-level functions+ , module CLaSH.Promoted.Ord+ -- ** Template Haskell+ , Lift (..), deriveLift+ -- ** Type classes+ -- *** CLaSH+ , module CLaSH.Class.BitVector+ , module CLaSH.Class.Num+ -- *** Other+ , module Control.Arrow+ , module Control.Applicative+ , module Data.Bits+ , module Data.Default ) where -import Control.Arrow as Exported-import Control.Applicative as Exported+import Control.Arrow+import Control.Applicative import Control.Category as Category-import Data.Bits as Exported-import Data.Default as Exported-import CLaSH.Class.BitVector as Exported-import CLaSH.Class.Num as Exported-import CLaSH.Promoted.Bool as Exported-import CLaSH.Promoted.Nat as Exported-import CLaSH.Promoted.Nat.TH as Exported-import CLaSH.Promoted.Nat.Literals as Exported-import CLaSH.Promoted.Ord as Exported-import CLaSH.Sized.Fixed as Exported-import CLaSH.Sized.Signed as Exported-import CLaSH.Sized.Unsigned as Exported-import CLaSH.Sized.Vector as Exported-import CLaSH.Bit as Exported-import CLaSH.Signal as Exported-import GHC.TypeLits as Exported+import Data.Bits+import Data.Default+import Debug.Trace (trace)+import CLaSH.Class.BitVector+import CLaSH.Class.Num+import CLaSH.Promoted.Nat+import CLaSH.Promoted.Nat.TH+import CLaSH.Promoted.Nat.Literals+import CLaSH.Promoted.Ord+import CLaSH.Sized.Fixed+import CLaSH.Sized.Signed+import CLaSH.Sized.Unsigned+import CLaSH.Sized.Vector+import CLaSH.Bit+import CLaSH.Signal.Implicit+import GHC.TypeLits+import Language.Haskell.TH.Lift (Lift(..),deriveLift) {-# INLINABLE window #-} -- | Give a window over a 'Signal' -- -- > window4 :: Signal Int -> Vec 4 (Signal Int) -- > window4 = window--- >--- > simulateP window4 [1,2,3,4,5,... == [1:>0:>0:>0:>Nil, 2:>1:>0:>0:>Nil, 3:>2:>1:>0:>Nil, 4:>3:>2:>1:>Nil, 5:>4:>3:>2:>Nil,...+--+-- >>> simulateP window4 [1,2,3,4,5,...+-- [<1,0,0,0>, <2,1,0,0>, <3,2,1,0>, <4,3,2,1>, <5,4,3,2>,... window :: (KnownNat (n + 1), Default a)- => Signal a- -> Vec ((n + 1) + 1) (Signal a)+ => Signal a -- ^ Signal to create a window over+ -> Vec ((n + 1) + 1) (Signal a) -- ^ Window of at least size 2 window x = x :> prev where prev = registerP (vcopyI def) next@@ -67,11 +123,12 @@ -- -- > windowD3 :: Signal Int -> Vec 3 (Signal Int) -- > windowD3 = windowD--- >--- > simulateP windowD3 [1,2,3,4,... == [0:>0:>0:>Nil, 1:>0:>0:>Nil, 2:>1:>0:>Nil, 3:>2:>1:>Nil, 4:>3:>2:>Nil,...+--+-- >>> simulateP windowD3 [1,2,3,4,...+-- [<0,0,0>, <1,0,0>, <2,1,0>, <3,2,1>, <4,3,2>,... windowD :: (KnownNat (n + 1), Default a)- => Signal a- -> Vec (n + 1) (Signal a)+ => Signal a -- ^ Signal to create a window over+ -> Vec (n + 1) (Signal a) -- ^ Window of at least size 1 windowD x = prev where prev = registerP (vcopyI def) next@@ -90,9 +147,10 @@ -- > -- > topEntity :: (Signal Int, Signal Int) -> Signal Int -- > topEntity = mac <^> 0--- >--- > simulateP topEntity [(1,1),(2,2),(3,3),(4,4),... == [0,1,5,14,30,... --+-- >>> simulateP topEntity [(1,1),(2,2),(3,3),(4,4),...+-- [0,1,5,14,30,...+-- -- Synchronous sequential functions can be composed just like their combinational counterpart: -- -- > dualMac :: (Signal Int, Signal Int)@@ -106,7 +164,7 @@ => (s -> i -> (s,o)) -- ^ Transfer function in mealy machine form: @state -> input -> (newstate,output)@ -> s -- ^ Initial state -> (SignalP i -> SignalP o) -- ^ Synchronous sequential function with input and output matching that of the mealy machine-f <^> iS = \i -> let (s',o) = unpack $ f <$> s <*> (pack i)+f <^> iS = \i -> let (s',o) = unpack $ f <$> s <*> pack i s = register iS s' in unpack o @@ -115,8 +173,9 @@ -- -- > rP :: (Signal Int,Signal Int) -> (Signal Int, Signal Int) -- > rP = registerP (8,8)--- >--- > simulateP rP [(1,1),(2,2),(3,3),... == [(8,8),(1,1),(2,2),(3,3),...+--+-- >>> simulateP rP [(1,1),(2,2),(3,3),...+-- [(8,8),(1,1),(2,2),(3,3),... registerP :: Pack a => a -> SignalP a -> SignalP a registerP i = unpack Prelude.. register i Prelude.. pack @@ -125,7 +184,7 @@ -- -- > bram40 :: Signal (Unsigned 6) -> Signal (Unsigned 6) -> Signal Bool -> Signal a -> Signal a -- > bram40 = blockRam d50-blockRam :: forall n m a . (KnownNat n, KnownNat m, Pack a)+blockRam :: forall n m a . (KnownNat n, KnownNat m, Pack a, Default a) => SNat n -- ^ Size @n@ of the blockram -> Signal (Unsigned m) -- ^ Write address @w@ -> Signal (Unsigned m) -- ^ Read address @r@@@ -135,7 +194,7 @@ blockRam n wr rd en din = pack $ (bram' <^> binit) (wr,rd,en,din) where binit :: (Vec n a,a)- binit = (vcopy n (error "uninitialized ram"),error "uninitialized ram")+ binit = (vcopy n def,def) bram' :: (Vec n a,a) -> (Unsigned m, Unsigned m, Bool, a) -> (((Vec n a),a),a)@@ -145,11 +204,12 @@ | otherwise = ram o' = ram ! r +{-# DEPRECATED blockRamC "'Comp' is deprecated, use 'blockRam' instead" #-} -- | Create a blockRAM with space for @n@ elements -- -- > bramC40 :: Comp (Unsigned 6, Unsigned 6, Bool, a) a -- > bramC40 = blockRamC d50-blockRamC :: (KnownNat n, KnownNat m, Pack a)+blockRamC :: (KnownNat n, KnownNat m, Pack a, Default a) => SNat n -- ^ Size @n@ of the blockram -> Comp (Unsigned m, Unsigned m, Bool, a) a blockRamC n = C ((\(wr,rd,en,din) -> blockRam n wr rd en din) Prelude.. unpack)@@ -159,8 +219,8 @@ -- -- > bram32 :: Signal (Unsigned 5) -> Signal (Unsigned 5) -> Signal Bool -> Signal a -> Signal a -- > bram32 = blockRamPow2 d32-blockRamPow2 :: (KnownNat n, KnownNat (2^n), Pack a)- => SNat ((2^n) :: Nat) -- ^ Size @2^n@ of the blockram+blockRamPow2 :: (KnownNat n, KnownNat (2^n), Pack a, Default a)+ => SNat (2^n) -- ^ Size @2^n@ of the blockram -> Signal (Unsigned n) -- ^ Write address @w@ -> Signal (Unsigned n) -- ^ Read address @r@ -> Signal Bool -- ^ Write enable@@ -168,29 +228,29 @@ -> Signal a -- ^ Value of the 'blockRAM' at address @r@ from the previous clock cycle blockRamPow2 = blockRam +{-# DEPRECATED blockRamPow2C "'Comp' is deprecated, use 'blockRamPow2' instead" #-} -- | Create a blockRAM with space for 2^@n@ elements -- -- > bramC32 :: Comp (Unsigned 5, Unsigned 5, Bool, a) a -- > bramC32 = blockRamPow2C d32-blockRamPow2C :: (KnownNat n, KnownNat (2^n), Pack a)- => SNat ((2^n) :: Nat) -- ^ Size @2^n@ of the blockram+blockRamPow2C :: (KnownNat n, KnownNat (2^n), Pack a, Default a)+ => SNat (2^n) -- ^ Size @2^n@ of the blockram -> Comp (Unsigned n, Unsigned n, Bool, a) a blockRamPow2C n = C ((\(wr,rd,en,din) -> blockRamPow2 n wr rd en din) Prelude.. unpack) +{-# DEPRECATED Comp "Use 'Applicative' interface and ('<^>') instead" #-} -- | 'Comp'onent: an 'Arrow' interface to synchronous sequential functions-newtype Comp a b = C { asFunction :: Signal a -> Signal b }+newtype Comp a b = C { asFunction :: Signal a -> Signal b } instance Category Comp where id = C Prelude.id (C f) . (C g) = C (f Prelude.. g) -infixr 8 ><-(><) :: (a -> b) -> (c -> d) -> (a, c) -> (b, d)-(f >< g) (x,y) = (f x,g y)- instance Arrow Comp where arr = C Prelude.. fmap first (C f) = C $ pack Prelude.. (f >< Prelude.id) Prelude.. unpack+ where+ (g >< h) (x,y) = (g x,h y) instance ArrowLoop Comp where loop (C f) = C $ simpleLoop (unpack Prelude.. f Prelude.. pack)@@ -198,21 +258,26 @@ simpleLoop g b = let ~(c,d) = g (b,d) in c +{-# DEPRECATED registerC "'Comp' is deprecated, use 'register' instead" #-} -- | Create a 'register' 'Comp'onent -- -- > rC :: Comp (Int,Int) (Int,Int) -- > rC = registerC (8,8)--- >--- > simulateC rP [(1,1),(2,2),(3,3),... == [(8,8),(1,1),(2,2),(3,3),...+--+-- >>> simulateC rP [(1,1),(2,2),(3,3),...+-- [(8,8),(1,1),(2,2),(3,3),... registerC :: a -> Comp a a registerC = C Prelude.. register +{-# DEPRECATED simulateC "'Comp' is deprecated, use 'simulate' instead" #-} -- | Simulate a 'Comp'onent given a list of samples ----- > simulateC (registerC 8) [1, 2, 3, ... == [8, 1, 2, 3, ...+-- >>> simulateC (registerC 8) [1, 2, 3, ...+-- [8, 1, 2, 3, ... simulateC :: Comp a b -> [a] -> [b] simulateC f = simulate (asFunction f) +{-# DEPRECATED (^^^) "Use 'Applicative' interface and ('<^>') instead" #-} {-# INLINABLE (^^^) #-} -- | Create a synchronous 'Comp'onent from a combinational function describing -- a mealy machine@@ -226,9 +291,10 @@ -- > -- > topEntity :: Comp (Int,Int) Int -- > topEntity = mac ^^^ 0--- >--- > simulateC topEntity [(1,1),(2,2),(3,3),(4,4),... == [0,1,5,14,30,... --+-- >>> simulateC topEntity [(1,1),(2,2),(3,3),(4,4),...+-- [0,1,5,14,30,...+-- -- Synchronous sequential must be composed using the 'Arrow' syntax -- -- > dualMac :: Comp (Int,Int,Int,Int) Int@@ -242,3 +308,100 @@ f ^^^ sI = C $ \i -> let (s',o) = unpack $ f <$> s <*> i s = register sI s' in o++{-# NOINLINE sassert #-}+-- | Compares the first two arguments for equality and logs a warning when they+-- are not equal. The second argument is considered the expected value. This+-- function simply returns the third argument unaltered as its result. This+-- function is used by 'outputVerifier'.+--+-- This function is translated to the following VHDL:+--+-- > sassert_block : block+-- > begin+-- > -- pragma translate_off+-- > process(clk_1000,reset_1000,arg0,arg1) is+-- > begin+-- > if (rising_edge(clk_1000) or rising_edge(reset_1000)) then+-- > assert (arg0 = arg1) report ("expected: " & to_string (arg1) & \", actual: \" & to_string (arg0)) severity error;+-- > end if;+-- > end process;+-- > -- pragma translate_on+-- > result <= arg2;+-- > end block;+--+-- And can, due to the pragmas, be used in synthesizable designs+sassert :: (Eq a, Show a)+ => Signal a -- ^ Checked value+ -> Signal a -- ^ Expected value+ -> Signal b -- ^ Returned value+ -> Signal b+sassert = liftA3+ (\a' b' c' -> if a' == b' then c'+ else trace ("\nexpected value: " ++ show b' ++ ", not equal to actual value: " ++ show a') c')++{-# INLINABLE stimuliGenerator #-}+-- | To be used as a one of the functions to create the \"magical\" 'testInput'+-- value, which the CλaSH compilers looks for to create the stimulus generator+-- for the generated VHDL testbench.+--+-- Example:+--+-- > testInput :: Signal Int+-- > testInput = stimuliGenerator $(v [(1::Int),3..21])+--+-- >>> sample testInput+-- [1,3,5,7,9,11,13,15,17,19,21,21,21,...+stimuliGenerator :: forall l a . KnownNat l+ => Vec l a -- ^ Samples to generate+ -> Signal a -- ^ Signal of given samples+stimuliGenerator samples =+ let (r,o) = unpack (genT <$> register (fromInteger (maxIndex samples)) r)+ in o+ where+ genT :: Unsigned l -> (Unsigned l, a)+ genT s = (s',samples ! s)+ where+ s' = if s > 0 then s - 1+ else s++{-# INLINABLE outputVerifier #-}+-- | To be used as a functions to generate the \"magical\" 'expectedOutput'+-- function, which the CλaSH compilers looks for to create the signal verifier+-- for the generated VHDL testbench.+--+-- Example:+--+-- > expectedOutput :: Signal Int -> Signal Bool+-- > expectedOutput = outputVerifier $(v ([70,99,2,3,4,5,7,8,9,10]::[Int]))+--+-- >>> sample (expectedOutput (fromList ([0..10] ++ [10,10,10])))+-- [+-- expected value: 70, not equal to actual value: 0+-- False,+-- expected value: 99, not equal to actual value: 1+-- False,False,False,False,False,+-- expected value: 7, not equal to actual value: 6+-- False,+-- expected value: 8, not equal to actual value: 7+-- False,+-- expected value: 9, not equal to actual value: 8+-- False,+-- expected value: 10, not equal to actual value: 9+-- False,True,True,...+outputVerifier :: forall l a . (KnownNat l, Eq a, Show a)+ => Vec l a -- ^ Samples to compare with+ -> Signal a -- ^ Signal to verify+ -> Signal Bool -- ^ Indicator that all samples are verified+outputVerifier samples i =+ let (s,o) = unpack (genT <$> register (fromInteger (maxIndex samples)) s)+ (e,f) = unpack o+ in sassert i e (register False f)+ where+ genT :: Unsigned l -> (Unsigned l, (a,Bool))+ genT s = (s',(samples ! s,finished))+ where+ s' = if s >= 1 then s - 1+ else s++ finished = s == 0
+ src/CLaSH/Prelude/Explicit.hs view
@@ -0,0 +1,393 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}++{-# OPTIONS_GHC -O0 -fno-omit-interface-pragmas #-}++{- |+ This module defines the explicitly clocked counterparts of the functions+ defined in "CLaSH.Prelude".++ This module uses the explicitly clocked 'CSignal's synchronous signals, as+ opposed to the implicitly clocked 'Signal's used in "CLaSH.Prelude". Take a+ look at "CLaSH.Signal.Explicit" to see how you can make multi-clock designs+ using explicitly clocked signals.+-}+module CLaSH.Prelude.Explicit+ ( -- * Creating synchronous sequential circuits+ sync+ , cregisterP+ -- * 'Arrow' interface for synchronous sequential circuits+ , CComp (..)+ , syncA+ , cregisterC+ , csimulateC+ -- * BlockRAM primitives+ , cblockRam+ , cblockRamPow2+ , blockRamCC+ , blockRamPow2CC+ -- * Utility functions+ , cwindow+ , cwindowD+ -- * Testbench functions+ , csassert+ , cstimuliGenerator+ , coutputVerifier+ -- * Exported modules+ -- ** Explicitly clocked synchronous signals+ , module CLaSH.Signal.Explicit+ )+where++import Data.Default (Default (..))+import Debug.Trace (trace)+import Control.Applicative (Applicative (..), (<$>),liftA3)+import Control.Arrow (Arrow (..), ArrowLoop (..))+import Control.Category as Category+import GHC.TypeLits (KnownNat,type (^), type (+))++import CLaSH.Promoted.Nat (SNat, snat)+import CLaSH.Signal.Explicit+import CLaSH.Sized.Unsigned (Unsigned)+import CLaSH.Sized.Vector (Vec (..), (!), (+>>), maxIndex, vcopy, vcopyI, vreplace)++{-# INLINABLE sync #-}+-- | Create a synchronous function from a combinational function describing+-- a mealy machine+--+-- > mac :: Int -- Current state+-- > -> (Int,Int) -- Input+-- > -> (Int,Int) -- (Updated state, output)+-- > mac s (x,y) = (s',s)+-- > where+-- > s' = x * y + s+-- >+-- > clk100 = Clock d100+-- >+-- > topEntity :: (CSignal 100 Int, CSignal 100 Int) -> CSignal 100 Int+-- > topEntity = sync clk100 mac 0+--+-- >>> csimulateP clk100 clk100 topEntity [(1,1),(2,2),(3,3),(4,4),...+-- [0,1,5,14,30,...+--+-- Synchronous sequential functions can be composed just like their combinational counterpart:+--+-- > dualMac :: (CSignal 100 Int, CSignal 100 Int)+-- > -> (CSignal 100 Int, CSignal 100 Int)+-- > -> CSignal 100 Int+-- > dualMac (a,b) (x,y) = s1 + s2+-- > where+-- > s1 = sync clk100 mac 0 (a,b)+-- > s2 = sync clk100 mac 0 (x,y)+sync :: (CPack i, CPack o)+ => Clock clk -- ^ 'Clock' to synchronize to+ -> (s -> i -> (s,o)) -- ^ Transfer function in mealy machine form: @state -> input -> (newstate,output)@+ -> s -- ^ Initial state+ -> (CSignalP clk i -> CSignalP clk o) -- ^ Synchronous sequential function with input and output matching that of the mealy machine+sync clk f iS = \i -> let (s',o) = cunpack clk $ f <$> s <*> cpack clk i+ s = cregister clk iS s'+ in cunpack clk o++{-# INLINABLE cregisterP #-}+-- | Create a 'register' function for product-type like signals (e.g. '(Signal a, Signal b)')+--+-- > clk100 = Clock d100+-- >+-- > rP :: (CSignal 100 Int, CSignal 100 Int) -> (CSignal 100 Int, CSignal 100 Int)+-- > rP = cregisterP d100 (8,8)+--+-- >>> csimulateP clk100 clk100 rP [(1,1),(2,2),(3,3),...+-- [(8,8),(1,1),(2,2),(3,3),...+cregisterP :: CPack a => Clock clk -> a -> CSignalP clk a -> CSignalP clk a+cregisterP clk i = cunpack clk Prelude.. cregister clk i Prelude.. cpack clk++{-# DEPRECATED CComp "Use 'Applicative' interface and 'sync' instead" #-}+-- | 'CComp'onent: an 'Arrow' interface to explicitly clocked synchronous+-- sequential functions+newtype CComp t a b = CC { asCFunction :: CSignal t a -> CSignal t b }++instance Category (CComp t) where+ id = CC Prelude.id+ (CC f) . (CC g) = CC (f Prelude.. g)++instance KnownNat t => Arrow (CComp t) where+ arr = CC Prelude.. fmap+ first (CC f) = let clk = Clock snat+ in CC $ cpack clk Prelude.. (f >< Prelude.id) Prelude.. cunpack clk+ where+ (g >< h) (x,y) = (g x,h y)++instance KnownNat t => ArrowLoop (CComp t) where+ loop (CC f) = let clk = Clock snat+ in CC $ simpleLoop (cunpack clk Prelude.. f Prelude.. cpack clk)+ where+ simpleLoop g b = let ~(c,d) = g (b,d)+ in c++{-# DEPRECATED syncA "Use 'Applicative' interface and 'sync' instead" #-}+{-# INLINABLE syncA #-}+-- | Create a synchronous 'CComp'onent from a combinational function describing+-- a mealy machine+--+-- > mac :: Int -- Current state+-- > -> (Int,Int) -- Input+-- > -> (Int,Int) -- (Updated state, output)+-- > mac s (x,y) = (s',s)+-- > where+-- > s' = x * y + s+-- >+-- > clk100 = Clock d100+-- >+-- > topEntity :: CComp 100 (Int,Int) Int+-- > topEntity = syncA clk100 mac 0+--+-- >>> simulateC topEntity [(1,1),(2,2),(3,3),(4,4),...+-- [0,1,5,14,30,...+--+-- Synchronous sequential must be composed using the 'Arrow' syntax+--+-- > dualMac :: CComp 100 (Int,Int,Int,Int) Int+-- > dualMac = proc (a,b,x,y) -> do+-- > rec s1 <- syncA clk100 mac 0 -< (a,b)+-- > s2 <- syncA clk100 mac 0 -< (x,y)+-- > returnA -< (s1 + s2)+syncA :: Clock clk -- ^ 'Clock' to synchronize to+ -> (s -> i -> (s,o)) -- ^ Transfer function in mealy machine form: @state -> input -> (newstate,output)@+ -> s -- ^ Initial state+ -> CComp clk i o -- ^ Synchronous sequential 'Comp'onent with input and output matching that of the mealy machine+syncA clk f sI = CC $ \i -> let (s',o) = cunpack clk $ f <$> s <*> i+ s = cregister clk sI s'+ in o++{-# DEPRECATED cregisterC "'CComp' is deprecated, use 'cregister' instead" #-}+-- | Create a 'cregister' 'CComp'onent+--+-- > clk100 = Clock d100+-- >+-- > rC :: CComp 100 (Int,Int) (Int,Int)+-- > rC = cregisterC clk100 (8,8)+--+-- >>> simulateC rP [(1,1),(2,2),(3,3),...+-- [(8,8),(1,1),(2,2),(3,3),...+cregisterC :: Clock clk -> a -> CComp clk a a+cregisterC clk = CC Prelude.. cregister clk++{-# DEPRECATED csimulateC "'CComp' is deprecated, use 'csimulate' instead" #-}+-- | Simulate a 'Comp'onent given a list of samples+--+-- > clk100 = Clock d100+-- >>> csimulateC (cregisterC clk100 8) [1, 2, 3, ...+-- [8, 1, 2, 3, ...+csimulateC :: CComp clk a b -> [a] -> [b]+csimulateC f = csimulate (asCFunction f)++{-# NOINLINE cblockRam #-}+-- | Create a blockRAM with space for @n@ elements+--+-- > clk100 = Clock d100+-- >+-- > bram40 :: CSignal 100 (Unsigned 6) -> CSignal 100 (Unsigned 6)+-- > -> CSignal 100 Bool -> CSignal 100 a -> 100 CSignal a+-- > bram40 = cblockRam clk100 d50+cblockRam :: forall n m a clk . (KnownNat n, KnownNat m, CPack a, Default a)+ => Clock clk -- ^ 'Clock' to synchronize to+ -> SNat n -- ^ Size @n@ of the blockram+ -> CSignal clk (Unsigned m) -- ^ Write address @w@+ -> CSignal clk (Unsigned m) -- ^ Read address @r@+ -> CSignal clk Bool -- ^ Write enable+ -> CSignal clk a -- ^ Value to write (at address @w@)+ -> CSignal clk a -- ^ Value of the 'blockRAM' at address @r@ from the previous clock cycle+cblockRam clk n wr rd en din = cpack clk $ (sync clk bram' binit) (wr,rd,en,din)+ where+ binit :: (Vec n a,a)+ binit = (vcopy n def,def)++ bram' :: (Vec n a,a) -> (Unsigned m, Unsigned m, Bool, a)+ -> (((Vec n a),a),a)+ bram' (ram,o) (w,r,e,d) = ((ram',o'),o)+ where+ ram' | e = vreplace ram w d+ | otherwise = ram+ o' = ram ! r++{-# DEPRECATED blockRamCC "'CComp' is deprecated, use 'cblockRam' instead" #-}+-- | Create a blockRAM with space for @n@ elements+--+-- > clk100 = Clock 100+-- >+-- > bramC40 :: CComp 100 (Unsigned 6, Unsigned 6, Bool, a) a+-- > bramC40 = blockRamCC clk100 d50+blockRamCC :: (KnownNat n, KnownNat m, CPack a, Default a)+ => Clock clk -- ^ 'Clock' to synchronize to+ -> SNat n -- ^ Size @n@ of the blockram+ -> CComp clk (Unsigned m, Unsigned m, Bool, a) a+blockRamCC clk n = CC ((\(wr,rd,en,din) -> cblockRam clk n wr rd en din) Prelude.. cunpack clk)++{-# INLINABLE cblockRamPow2 #-}+-- | Create a blockRAM with space for 2^@n@ elements+--+-- > bram32 :: Signal (Unsigned 5) -> Signal (Unsigned 5) -> Signal Bool -> Signal a -> Signal a+-- > bram32 = cblockRamPow2 d32+cblockRamPow2 :: (KnownNat n, KnownNat (2^n), CPack a, Default a)+ => Clock clk -- ^ 'Clock' to synchronize to+ -> SNat (2^n) -- ^ Size @2^n@ of the blockram+ -> CSignal clk (Unsigned n) -- ^ Write address @w@+ -> CSignal clk (Unsigned n) -- ^ Read address @r@+ -> CSignal clk Bool -- ^ Write enable+ -> CSignal clk a -- ^ Value to write (at address @w@)+ -> CSignal clk a -- ^ Value of the 'blockRAM' at address @r@ from the previous clock cycle+cblockRamPow2 = cblockRam++{-# DEPRECATED blockRamPow2CC "'CComp' is deprecated, use 'cblockRamPow2' instead" #-}+-- | Create a blockRAM with space for 2^@n@ elements+--+-- > clk100 = Clock d100+-- >+-- > bramC32 :: CComp 100 (Unsigned 5, Unsigned 5, Bool, a) a+-- > bramC32 = blockRamPow2CC clk100 d32+blockRamPow2CC :: (KnownNat n, KnownNat (2^n), CPack a, Default a)+ => Clock clk -- ^ 'Clock' to synchronize to+ -> SNat (2^n) -- ^ Size @2^n@ of the blockram+ -> CComp clk (Unsigned n, Unsigned n, Bool, a) a+blockRamPow2CC clk n = CC ((\(wr,rd,en,din) -> cblockRamPow2 clk n wr rd en din) Prelude.. cunpack clk)++{-# INLINABLE cwindow #-}+-- | Give a window over a 'CSignal'+--+-- > window4 :: Signal Int -> Vec 4 (Signal Int)+-- > window4 = window+--+-- >>> csimulateP window4 [1,2,3,4,5,...+-- [<1,0,0,0>, <2,1,0,0>, <3,2,1,0>, <4,3,2,1>, <5,4,3,2>,...+cwindow :: (KnownNat (n + 1), Default a)+ => Clock clk -- ^ Clock to which the incoming signal is synchronized+ -> CSignal clk a -- ^ Signal to create a window over+ -> Vec ((n + 1) + 1) (CSignal clk a) -- ^ Window of at least size 2+cwindow clk x = x :> prev+ where+ prev = cregisterP clk (vcopyI def) next+ next = x +>> prev++{-# INLINABLE cwindowD #-}+-- | Give a delayed window over a 'CSignal'+--+-- > windowD3 :: Signal Int -> Vec 3 (Signal Int)+-- > windowD3 = windowD+--+-- >>> csimulateP windowD3 [1,2,3,4,...+-- [<0,0,0>, <1,0,0>, <2,1,0>, <3,2,1>, <4,3,2>,...+cwindowD :: (KnownNat (n + 1), Default a)+ => Clock clk -- ^ Clock to which the incoming signal is synchronized+ -> CSignal clk a -- ^ Signal to create a window over+ -> Vec (n + 1) (CSignal clk a) -- ^ Window of at least size 1+cwindowD clk x = prev+ where+ prev = cregisterP clk (vcopyI def) next+ next = x +>> prev++{-# NOINLINE csassert #-}+-- | Compares the first two arguments for equality and logs a warning when they+-- are not equal. The second argument is considered the expected value. This+-- function simply returns the third argument unaltered as its result. This+-- function is used by 'coutputVerifier'.+--+--+-- This function is translated to the following VHDL:+--+-- > csassert_block : block+-- > begin+-- > -- pragma translate_off+-- > process(clk_t,reset_t,arg0,arg1) is+-- > begin+-- > if (rising_edge(clk_t) or rising_edge(reset_t)) then+-- > assert (arg0 = arg1) report ("expected: " & to_string (arg1) & \", actual: \" & to_string (arg0)) severity error;+-- > end if;+-- > end process;+-- > -- pragma translate_on+-- > result <= arg2;+-- > end block;+--+-- And can, due to the pragmas, be used in synthesizable designs+csassert :: (Eq a,Show a)+ => CSignal t a -- ^ Checked value+ -> CSignal t a -- ^ Expected value+ -> CSignal t b -- ^ Return valued+ -> CSignal t b+csassert = liftA3+ (\a' b' c' -> if a' == b' then c'+ else trace ("\nexpected value: " ++ show b' ++ ", not equal to actual value: " ++ show a') c')++{-# INLINABLE cstimuliGenerator #-}+-- | To be used as a one of the functions to create the \"magical\" 'testInput'+-- value, which the CλaSH compilers looks for to create the stimulus generator+-- for the generated VHDL testbench.+--+-- Example:+--+-- > clk2 = Clock d2+-- >+-- > testInput :: CSignal 2 Int+-- > testInput = cstimuliGenerator $(v [(1::Int),3..21]) clk2+--+-- >>> csample testInput+-- [1,3,5,7,9,11,13,15,17,19,21,21,21,...+cstimuliGenerator :: forall l clk a . KnownNat l+ => Vec l a -- ^ Samples to generate+ -> Clock clk -- ^ Clock to synchronize the output signal to+ -> CSignal clk a -- ^ Signal of given samples+cstimuliGenerator samples clk =+ let (r,o) = cunpack clk (genT <$> cregister clk (fromInteger (maxIndex samples)) r)+ in o+ where+ genT :: Unsigned l -> (Unsigned l,a)+ genT s = (s',samples ! s)+ where+ s' = if s > 0 then s - 1+ else s++{-# INLINABLE coutputVerifier #-}+-- | To be used as a functions to generate the \"magical\" 'expectedOutput'+-- function, which the CλaSH compilers looks for to create the signal verifier+-- for the generated VHDL testbench.+--+-- Example:+--+-- > clk7 = Clock d7+-- >+-- > expectedOutput :: CSignal 7 Int -> CSignal 7 Bool+-- > expectedOutput = coutputVerifier $(v ([70,99,2,3,4,5,7,8,9,10]::[Int])) clk7+--+-- >>> csample (expectedOutput (cfromList ([0..10] ++ [10,10,10])))+-- [+-- expected value: 70, not equal to actual value: 0+-- False,+-- expected value: 99, not equal to actual value: 1+-- False,False,False,False,False,+-- expected value: 7, not equal to actual value: 6+-- False,+-- expected value: 8, not equal to actual value: 7+-- False,+-- expected value: 9, not equal to actual value: 8+-- False,+-- expected value: 10, not equal to actual value: 9+-- False,True,True,...+coutputVerifier :: forall l clk a . (KnownNat l, Eq a, Show a)+ => Vec l a -- ^ Samples to compare with+ -> Clock clk -- ^ Clock the input signal is synchronized to+ -> CSignal clk a -- ^ Signal to verify+ -> CSignal clk Bool -- ^ Indicator that all samples are verified+coutputVerifier samples clk i =+ let (s,o) = cunpack clk (genT <$> cregister clk (fromInteger (maxIndex samples)) s)+ (e,f) = cunpack clk o+ in csassert i e (cregister clk False f)+ where+ genT :: Unsigned l -> (Unsigned l,(a,Bool))+ genT s = (s',(samples ! s,finished))+ where+ s' = if s >= 1 then s - 1+ else s++ finished = s == 0
− src/CLaSH/Promoted/Bool.hs
@@ -1,11 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE PolyKinds #-}-module CLaSH.Promoted.Bool where---- | Type-level if-then-else-type family If (x :: Bool) (y :: k) (z :: k) :: k- where- If True y z = y- If False y z = z
src/CLaSH/Promoted/Nat.hs view
@@ -4,7 +4,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} module CLaSH.Promoted.Nat- ( SNat, snat, withSNat+ ( SNat, snat, withSNat, snatToInteger , UNat (..), toUNat, addUNat, multUNat, powUNat ) where@@ -14,15 +14,24 @@ import Unsafe.Coerce -- | Singleton value for a type-level natural number 'n'+--+-- * "CLaSH.Promoted.Nat.Literals" contains a list of predefined 'SNat' literals+-- * "CLaSH.Promoted.Nat.TH" has functions to easily create large ranges of new 'SNat' literals data SNat (n :: Nat) = KnownNat n => SNat (Proxy n) --- | Singleton value for a type-level natural number+instance Show (SNat n) where+ show (SNat p) = 'd' : show (natVal p)++-- | Create a singleton literal for a type-level natural number snat :: KnownNat n => SNat n snat = SNat Proxy -- | Supply a function with a singleton natural 'n' according to the context withSNat :: KnownNat n => (SNat n -> a) -> a withSNat f = f (SNat Proxy)++snatToInteger :: SNat n -> Integer+snatToInteger (SNat p) = natVal p -- | Unary representation of a type-level natural data UNat :: Nat -> * where
src/CLaSH/Promoted/Nat/Literals.hs view
@@ -1,7 +1,17 @@ {-# LANGUAGE TemplateHaskell, DataKinds #-}--- | Predefined 'SNat' singleton literals+-- | Predefined 'SNat' singleton literals in the range [0 .. 1024]+--+-- Defines:+--+-- > d0 = snat :: SNat 0+-- > d1 = snat :: SNat 1+-- > d2 = snat :: SNat 2+-- > ...+-- > d1024 = snat :: SNat 1024+--+-- You can generate more 'SNat' literals using 'decLiteralsD' from "CLaSH.Promoted.Nat.TH" module CLaSH.Promoted.Nat.Literals where import CLaSH.Promoted.Nat.TH -$(decLiteralsD "d" 0 1024)+$(decLiteralsD 0 1024)
src/CLaSH/Promoted/Nat/TH.hs view
@@ -5,27 +5,33 @@ import CLaSH.Promoted.Nat --- | Create an 'SNat' constant+-- | Create an 'SNat' literal ----- > $(decLiteralD "d" 1200) == d1200 = snat :: SNat 1200-decLiteralD :: String- -> Integer+-- > $(decLiteralD "d" 1200)+--+-- >>> :t d1200+-- d1200 :: SNat 1200+decLiteralD :: Integer -> Q [Dec]-decLiteralD valPrefix n =- do let suffix = if n < 0 then error ("Can't make negative SNat: " ++ show n) else show n- valName = mkName $ valPrefix ++ suffix- sig <- sigD valName (appT (conT ''SNat) (litT (numTyLit n)))- val <- valD (varP valName) (normalB [| snat |]) []- return [ sig, val ]+decLiteralD n = do+ let suffix = if n < 0 then error ("Can't make negative SNat: " ++ show n) else show n+ valName = mkName $ 'd':suffix+ sig <- sigD valName (appT (conT ''SNat) (litT (numTyLit n)))+ val <- valD (varP valName) (normalB [| snat |]) []+ return [ sig, val ] --- | Create an 'SNat' constants+-- | Create a range of 'SNat' literals ----- > $(decLiteralsD "d" 1200 1202) == d1200 = snat :: SNat 1200--- > d1201 = snat :: SNat 1201--- > d1202 = snat :: SNat 1202-decLiteralsD :: String- -> Integer+-- > $(decLiteralsD 1200 1202)+--+-- >>> :t d1200+-- d1200 :: SNat 1200+-- >>> :t d1201+-- d1201 :: SNat 1201+-- >>> :t d1202+-- d1202 :: SNat 1202+decLiteralsD :: Integer -> Integer -> Q [Dec]-decLiteralsD valPrefix from to =- fmap concat $ sequence $ [ decLiteralD valPrefix n | n <- [from..to] ]+decLiteralsD from to =+ fmap concat $ sequence $ [ decLiteralD n | n <- [from..to] ]
src/CLaSH/Promoted/Ord.hs view
@@ -4,9 +4,8 @@ {-# LANGUAGE UndecidableInstances #-} module CLaSH.Promoted.Ord where +import Data.Type.Bool import GHC.TypeLits--import CLaSH.Promoted.Bool -- | Type-level 'min' function for natural numbers type family Min (x :: Nat) (y :: Nat) :: Nat
− src/CLaSH/Signal.hs
@@ -1,286 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeFamilies #-}--module CLaSH.Signal- ( Signal- , sample- , sampleN- , fromList- , signal- , register- , simulate- , Pack(..)- , simulateP- , (<^), (^>)- )-where--import Data.Default-import Control.Applicative-import Language.Haskell.TH.Syntax(Lift(..))--import CLaSH.Bit (Bit)-import CLaSH.Sized.Signed (Signed)-import CLaSH.Sized.Unsigned (Unsigned)-import CLaSH.Sized.Vector (Vec(..), vmap, vhead, vtail)--{-# NOINLINE register #-}-{-# NOINLINE signal #-}-{-# NOINLINE mapSignal #-}-{-# NOINLINE appSignal #-}--infixr 5 :---- | A synchronized signal with elements of type @a@-data Signal a = a :- Signal a---- | 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]-fromList :: [a] -> Signal a-fromList [] = error "finite list"-fromList (x:xs) = x :- fromList xs--instance Show a => Show (Signal a) where- show (x :- xs) = show x ++ " " ++ show xs--instance Lift a => Lift (Signal a) where- lift ~(x :- _) = [| signal x |]--instance Default a => Default (Signal a) where- def = signal def---- | Get an infinite list of samples from a 'Signal'------ The elements in the list correspond to the values of the 'Signal' at--- consecutive clock cycles------ > sample s == [s0, s1, s2, s3, ...-sample :: Signal a -> [a]-sample ~(x :- xs) = x : sample xs---- | Get a list of @n@ samples from a 'Signal'------ The elements in the list correspond to the values of the 'Signal' at--- consecutive clock cycles------ > sampleN 3 s == [s0, s1, s2]-sampleN :: Int -> Signal a -> [a]-sampleN 0 _ = []-sampleN n ~(x :- xs) = x : (sampleN (n-1) xs)---- | Create a constant 'Signal' from a combinational value------ > sample (signal 4) == [4, 4, 4, 4, ...-signal :: a -> Signal a-signal a = a :- signal a--mapSignal :: (a -> b) -> Signal a -> Signal b-mapSignal f (a :- as) = f a :- mapSignal f as--appSignal :: Signal (a -> b) -> Signal a -> Signal b-appSignal (f :- fs) ~(a :- as) = f a :- appSignal fs as--instance Functor Signal where- fmap = mapSignal--instance Applicative Signal where- pure = signal- (<*>) = appSignal--unSignal :: Signal a -> a-unSignal (a :- _) = a--next :: Signal a -> Signal a-next (_ :- as) = as--diag :: Signal (Signal a) -> Signal a-diag (xs :- xss) = unSignal xs :- diag (fmap next xss)--instance Monad Signal where- return = signal- xs >>= f = diag (fmap f xs)---- | 'register' @i s@ delays the values in 'Signal' @s@ for one cycle, and sets--- the value at time 0 to @i@------ > sampleN 3 (register 8 (fromList [1,2,3,4])) == [8,1,2]-register :: a -> Signal a -> Signal a-register i s = i :- s---- | Simulate a ('Signal' -> 'Signal') function given a list of samples------ > simulate (register 8) [1, 2, 3, ... == [8, 1, 2, 3, ...-simulate :: (Signal a -> Signal b) -> [a] -> [b]-simulate f = sample . f . fromList---- | Conversion between a 'Signal' of a product type (e.g. a tuple) and a--- product type of 'Signal's-class Pack a where- type SignalP a- -- | > pack :: (Signal a, Signal b) -> Signal (a,b)- -- However:- --- -- > pack :: Signal Bit -> Signal Bit- pack :: SignalP a -> Signal a- -- | > unpack :: Signal (a,b) -> (Signal a, Signal b)- -- However:- --- -- > unpack :: Signal Bit -> Signal Bit- unpack :: Signal a -> SignalP a---- | Simulate a ('SignalP' -> 'SignalP') function given a list of samples------ > simulateP (unpack . register (8,8) . pack) [(1,1), (2,2), (3,3), ... == [(8,8), (1,1), (2,2), (3,3), ...-simulateP :: (Pack a, Pack b) => (SignalP a -> SignalP b) -> [a] -> [b]-simulateP f = simulate (pack . f . unpack)--instance Pack Bit where- type SignalP Bit = Signal Bit- pack = id- unpack = id--instance Pack (Signed n) where- type SignalP (Signed n) = Signal (Signed n)- pack = id- unpack = id--instance Pack (Unsigned n) where- type SignalP (Unsigned n) = Signal (Unsigned n)- pack = id- unpack = id--instance Pack Bool where- type SignalP Bool = Signal Bool- pack = id- unpack = id--instance Pack Integer where- type SignalP Integer = Signal Integer- pack = id- unpack = id--instance Pack Int where- type SignalP Int = Signal Int- pack = id- unpack = id--instance Pack Float where- type SignalP Float = Signal Float- pack = id- unpack = id--instance Pack Double where- type SignalP Double = Signal Double- pack = id- unpack = id--instance Pack () where- type SignalP () = Signal ()- pack = id- unpack = id--instance Pack (a,b) where- type SignalP (a,b) = (Signal a, Signal b)- pack = uncurry (liftA2 (,))- unpack tup = (fmap fst tup, fmap snd tup)--instance Pack (a,b,c) where- type SignalP (a,b,c) = (Signal a, Signal b, Signal c)- pack (a,b,c) = (,,) <$> a <*> b <*> c- unpack tup = (fmap (\(x,_,_) -> x) tup- ,fmap (\(_,x,_) -> x) tup- ,fmap (\(_,_,x) -> x) tup- )--instance Pack (a,b,c,d) where- type SignalP (a,b,c,d) = (Signal a, Signal b, Signal c, Signal d)- pack (a,b,c,d) = (,,,) <$> a <*> b <*> c <*> d- unpack tup = (fmap (\(x,_,_,_) -> x) tup- ,fmap (\(_,x,_,_) -> x) tup- ,fmap (\(_,_,x,_) -> x) tup- ,fmap (\(_,_,_,x) -> x) tup- )--instance Pack (a,b,c,d,e) where- type SignalP (a,b,c,d,e) = (Signal a, Signal b, Signal c, Signal d, Signal e)- pack (a,b,c,d,e) = (,,,,) <$> a <*> b <*> c <*> d <*> e- unpack tup = (fmap (\(x,_,_,_,_) -> x) tup- ,fmap (\(_,x,_,_,_) -> x) tup- ,fmap (\(_,_,x,_,_) -> x) tup- ,fmap (\(_,_,_,x,_) -> x) tup- ,fmap (\(_,_,_,_,x) -> x) tup- )--instance Pack (a,b,c,d,e,f) where- type SignalP (a,b,c,d,e,f) = (Signal a, Signal b, Signal c, Signal d, Signal e, Signal f)- pack (a,b,c,d,e,f) = (,,,,,) <$> a <*> b <*> c <*> d <*> e <*> f- unpack tup = (fmap (\(x,_,_,_,_,_) -> x) tup- ,fmap (\(_,x,_,_,_,_) -> x) tup- ,fmap (\(_,_,x,_,_,_) -> x) tup- ,fmap (\(_,_,_,x,_,_) -> x) tup- ,fmap (\(_,_,_,_,x,_) -> x) tup- ,fmap (\(_,_,_,_,_,x) -> x) tup- )--instance Pack (a,b,c,d,e,f,g) where- type SignalP (a,b,c,d,e,f,g) = (Signal a, Signal b, Signal c, Signal d, Signal e, Signal f, Signal g)- pack (a,b,c,d,e,f,g) = (,,,,,,) <$> a <*> b <*> c <*> d <*> e <*> f <*> g- unpack tup = (fmap (\(x,_,_,_,_,_,_) -> x) tup- ,fmap (\(_,x,_,_,_,_,_) -> x) tup- ,fmap (\(_,_,x,_,_,_,_) -> x) tup- ,fmap (\(_,_,_,x,_,_,_) -> x) tup- ,fmap (\(_,_,_,_,x,_,_) -> x) tup- ,fmap (\(_,_,_,_,_,x,_) -> x) tup- ,fmap (\(_,_,_,_,_,_,x) -> x) tup- )--instance Pack (a,b,c,d,e,f,g,h) where- type SignalP (a,b,c,d,e,f,g,h) = (Signal a, Signal b, Signal c, Signal d, Signal e, Signal f, Signal g, Signal h)- pack (a,b,c,d,e,f,g,h) = (,,,,,,,) <$> a <*> b <*> c <*> d <*> e <*> f <*> g <*> h- unpack tup = (fmap (\(x,_,_,_,_,_,_,_) -> x) tup- ,fmap (\(_,x,_,_,_,_,_,_) -> x) tup- ,fmap (\(_,_,x,_,_,_,_,_) -> x) tup- ,fmap (\(_,_,_,x,_,_,_,_) -> x) tup- ,fmap (\(_,_,_,_,x,_,_,_) -> x) tup- ,fmap (\(_,_,_,_,_,x,_,_) -> x) tup- ,fmap (\(_,_,_,_,_,_,x,_) -> x) tup- ,fmap (\(_,_,_,_,_,_,_,x) -> x) tup- )--instance Pack (Vec n a) where- type SignalP (Vec n a) = Vec n (Signal a)- pack vs = vmap unSignal vs :- pack (vmap next vs)- unpack (Nil :- _) = Nil- unpack vs@((_ :> _) :- _) = fmap vhead vs :> (unpack (fmap vtail vs))----- | Operator lifting, use in conjunction with '(^>)'------ > add2 :: Signal Int -> Signal Int--- > add2 x = x <^(+)^> (signal 2)--- >--- > simulate add2 [1,2,3,... == [3,4,5,...-(<^) :: Applicative f => f a -> (a -> b -> c) -> f b -> f c-v <^ f = liftA2 f v---- | Operator lifting, use in conjunction with '(<^)'------ > add2 :: Signal Int -> Signal Int--- > add2 x = x <^(+)^> (signal 2)--- >--- > simulate add2 [1,2,3,... == [3,4,5,...-(^>) :: Applicative f => (f a -> f b) -> f a -> f b-f ^> v = f v--instance Num a => Num (Signal a) where- (+) = liftA2 (+)- (-) = liftA2 (-)- (*) = liftA2 (*)- negate = fmap negate- abs = fmap abs- signum = fmap signum- fromInteger = signal . fromInteger
+ src/CLaSH/Signal/Explicit.hs view
@@ -0,0 +1,355 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}++module CLaSH.Signal.Explicit+ ( -- * Explicitly clocked synchronous signal+ -- $relativeclocks+ CSignal+ -- * Clock domain crossing+ , Clock (..)+ , veryUnsafeSynchronizer+ , fromImplicit+ , fromExplicit+ -- * Basic circuit functions+ , csignal+ , cregister+ , CPack (..)+ -- * Simulation functions+ , csimulate+ , csimulateP+ -- * List \<-\> CSignal conversion+ , csample+ , csampleN+ , cfromList+ )+where++import Data.Coerce+import Control.Applicative (Applicative (..), (<$>), liftA2)+import GHC.TypeLits (Nat)++import CLaSH.Bit (Bit)+import CLaSH.Promoted.Nat (snatToInteger)+import CLaSH.Sized.Fixed (Fixed)+import CLaSH.Sized.Signed (Signed)+import CLaSH.Sized.Unsigned (Unsigned)+import CLaSH.Sized.Vector (Vec(..), vmap, vhead, vtail)++import CLaSH.Signal.Implicit+import CLaSH.Signal.Types++{-# NOINLINE cregister #-}++{-# NOINLINE veryUnsafeSynchronizer #-}+{-# NOINLINE fromImplicit #-}+{-# NOINLINE fromExplicit #-}++{- $relativeclocks #relativeclocks#+CλaSH supports explicitly clocked 'Signal's in the form of: \"@'CSignal' clk a@\",+where @clk@ is a 'Nat'ural number corresponding to the clock period of the clock+the signal is synchronized to. NB: \"Bad things\"™ happen when you actually use+a clock period of @0@, so don't do that!++The clock periods are however dimension-less, they do not refer to any explicit+time-scale (e.g. nano-seconds). The reason for the lack of an explicit time-scale+is that the CλaSH compiler would not be able guarantee that the circuit can run+at the specified frequency.++The clock periods are just there to indicate relative frequency differences+between two different clocks. That is, a \"@'CSignal' 500 a@\" is synchronized+to a clock that runs 6.5 times faster than the clock to which a+\"@'CSignal' 3250 a@\" is synchronized to. NB: You should be judicious using a+clock with period @1@ as you can never create a clock that runs faster later on!+-}++-- | Create a 'CSignal' from a list+--+-- Every element in the list will correspond to a value of the signal for one+-- clock cycle.+--+-- NB: Simulation only!+--+-- >>> csampleN 2 (cfromList [1,2,3,4,5])+-- [1,2]+cfromList :: [a] -> CSignal t a+cfromList = coerce . fromList++-- | Get an infinite list of samples from a 'CSignal'+--+-- The elements in the list correspond to the values of the 'CSignal' at+-- consecutive clock cycles+--+-- > csample s == [s0, s1, s2, s3, ...+csample :: CSignal t a -> [a]+csample = sample . coerce++-- | Get a list of @n@ samples from a 'CSignal'+--+-- The elements in the list correspond to the values of the 'CSignal' at+-- consecutive clock cycles+--+-- > csampleN 3 s == [s0, s1, s2]+csampleN :: Int -> CSignal t a -> [a]+csampleN n = sampleN n . coerce++-- | 'cregister' @i s@ delays the values in 'CSignal' @s@ for one cycle, and sets+-- the value at time 0 to @i@+--+-- > clk100 = Clock d100+--+-- >>> csampleN 3 (cregister d100 8 (fromList [1,2,3,4]))+-- [8,1,2]+cregister :: Clock clk -> a -> CSignal clk a -> CSignal clk a+cregister _ i s = coerce (register i (coerce s))++-- | Simulate a (@'CSignal' clk1 a -> 'Signal' clk2 b@) function given a list of+-- samples of type @a@+--+-- >>> simulate (register 8) [1, 2, 3, ...+-- [8, 1, 2, 3, ...+csimulate :: (CSignal clk1 a -> CSignal clk2 b) -> [a] -> [b]+csimulate f = csample . f . cfromList++-- | Isomorphism between a @'CSignal' clk@ of a product type (e.g. a tuple) and a+-- product type of @'CSignal' clk@'s+class CPack a where+ type CSignalP (clk :: Nat) a+ type CSignalP clk a = CSignal clk a+ -- | Example:+ --+ -- > cpack :: Clock clk -> (CSignal clk a, CSignal clk b) -> CSignal clk (a,b)+ --+ -- However:+ --+ -- > cpack :: Clock clk -> CSignal clk Bit -> CSignal clk Bit+ cpack :: Clock clk -> CSignalP clk a -> CSignal clk a+ -- | Example:+ --+ -- > cunpack :: Clock clk -> CSignal clk (a,b) -> (CSignal clk a, CSignal clk b)+ --+ -- However:+ --+ -- > cunpack :: Clock clk -> CSignal clk Bit -> CSignal clk Bit+ cunpack :: Clock clk -> CSignal clk a -> CSignalP clk a++-- | Simulate a (@'CSignalP' clk1 a -> 'CSignalP' clk2 b@) function given a list+-- of samples of type @a@+--+-- > clk100 = Clock d100+--+-- >>> csimulateP clk100 clk100 (cunpack clk100 . cregister clk100 (8,8) . cpack clk100) [(1,1), (2,2), (3,3), ...+-- [(8,8), (1,1), (2,2), (3,3), ...+csimulateP :: (CPack a, CPack b)+ => Clock clk1 -- ^ 'Clock' of the incoming signal+ -> Clock clk2 -- ^ 'Clock' of the outgoing signal+ -> (CSignalP clk1 a -> CSignalP clk2 b) -- ^ Function to simulate+ -> [a] -> [b]+csimulateP clk1 clk2 f = csimulate (cpack clk2 . f . cunpack clk1)++instance CPack Bit where+ cpack _ = id+ cunpack _ = id++instance CPack (Signed n) where+ cpack _ = id+ cunpack _ = id++instance CPack (Unsigned n) where+ cpack _ = id+ cunpack _ = id++instance CPack (Fixed frac rep size) where+ cpack _ = id+ cunpack _ = id++instance CPack Bool where+ cpack _ = id+ cunpack _ = id++instance CPack Integer where+ cpack _ = id+ cunpack _ = id++instance CPack Int where+ cpack _ = id+ cunpack _ = id++instance CPack Float where+ cpack _ = id+ cunpack _ = id++instance CPack Double where+ cpack _ = id+ cunpack _ = id++instance CPack () where+ cpack _ = id+ cunpack _ = id++instance CPack (a,b) where+ type CSignalP t (a,b) = (CSignal t a, CSignal t b)+ cpack _ = uncurry (liftA2 (,))+ cunpack _ tup = (fmap fst tup, fmap snd tup)++instance CPack (a,b,c) where+ type CSignalP t (a,b,c) = (CSignal t a, CSignal t b, CSignal t c)+ cpack _ (a,b,c) = (,,) <$> a <*> b <*> c+ cunpack _ tup = (fmap (\(x,_,_) -> x) tup+ ,fmap (\(_,x,_) -> x) tup+ ,fmap (\(_,_,x) -> x) tup+ )++instance CPack (a,b,c,d) where+ type CSignalP t (a,b,c,d) = (CSignal t a, CSignal t b, CSignal t c, CSignal t d)+ cpack _ (a,b,c,d) = (,,,) <$> a <*> b <*> c <*> d+ cunpack _ tup = (fmap (\(x,_,_,_) -> x) tup+ ,fmap (\(_,x,_,_) -> x) tup+ ,fmap (\(_,_,x,_) -> x) tup+ ,fmap (\(_,_,_,x) -> x) tup+ )++instance CPack (a,b,c,d,e) where+ type CSignalP t (a,b,c,d,e) = (CSignal t a, CSignal t b, CSignal t c, CSignal t d, CSignal t e)+ cpack _ (a,b,c,d,e) = (,,,,) <$> a <*> b <*> c <*> d <*> e+ cunpack _ tup = (fmap (\(x,_,_,_,_) -> x) tup+ ,fmap (\(_,x,_,_,_) -> x) tup+ ,fmap (\(_,_,x,_,_) -> x) tup+ ,fmap (\(_,_,_,x,_) -> x) tup+ ,fmap (\(_,_,_,_,x) -> x) tup+ )++instance CPack (a,b,c,d,e,f) where+ type CSignalP t (a,b,c,d,e,f) = (CSignal t a, CSignal t b, CSignal t c, CSignal t d, CSignal t e, CSignal t f)+ cpack _ (a,b,c,d,e,f) = (,,,,,) <$> a <*> b <*> c <*> d <*> e <*> f+ cunpack _ tup = (fmap (\(x,_,_,_,_,_) -> x) tup+ ,fmap (\(_,x,_,_,_,_) -> x) tup+ ,fmap (\(_,_,x,_,_,_) -> x) tup+ ,fmap (\(_,_,_,x,_,_) -> x) tup+ ,fmap (\(_,_,_,_,x,_) -> x) tup+ ,fmap (\(_,_,_,_,_,x) -> x) tup+ )++instance CPack (a,b,c,d,e,f,g) where+ type CSignalP t (a,b,c,d,e,f,g) = (CSignal t a, CSignal t b, CSignal t c, CSignal t d, CSignal t e, CSignal t f, CSignal t g)+ cpack _ (a,b,c,d,e,f,g) = (,,,,,,) <$> a <*> b <*> c <*> d <*> e <*> f <*> g+ cunpack _ tup = (fmap (\(x,_,_,_,_,_,_) -> x) tup+ ,fmap (\(_,x,_,_,_,_,_) -> x) tup+ ,fmap (\(_,_,x,_,_,_,_) -> x) tup+ ,fmap (\(_,_,_,x,_,_,_) -> x) tup+ ,fmap (\(_,_,_,_,x,_,_) -> x) tup+ ,fmap (\(_,_,_,_,_,x,_) -> x) tup+ ,fmap (\(_,_,_,_,_,_,x) -> x) tup+ )++instance CPack (a,b,c,d,e,f,g,h) where+ type CSignalP t (a,b,c,d,e,f,g,h) = (CSignal t a, CSignal t b, CSignal t c, CSignal t d, CSignal t e, CSignal t f, CSignal t g, CSignal t h)+ cpack _ (a,b,c,d,e,f,g,h) = (,,,,,,,) <$> a <*> b <*> c <*> d <*> e <*> f <*> g <*> h+ cunpack _ tup = (fmap (\(x,_,_,_,_,_,_,_) -> x) tup+ ,fmap (\(_,x,_,_,_,_,_,_) -> x) tup+ ,fmap (\(_,_,x,_,_,_,_,_) -> x) tup+ ,fmap (\(_,_,_,x,_,_,_,_) -> x) tup+ ,fmap (\(_,_,_,_,x,_,_,_) -> x) tup+ ,fmap (\(_,_,_,_,_,x,_,_) -> x) tup+ ,fmap (\(_,_,_,_,_,_,x,_) -> x) tup+ ,fmap (\(_,_,_,_,_,_,_,x) -> x) tup+ )++instance CPack (Vec n a) where+ type CSignalP t (Vec n a) = Vec n (CSignal t a)+ cpack clk vs = mkCSignal (vmap (shead . coerce) vs) (cpack clk (vmap cstail vs))+ cunpack _ (CSignal (Nil :- _)) = Nil+ cunpack clk vs@(CSignal ((_ :> _) :- _)) = fmap vhead vs :> cunpack clk (fmap vtail vs)++-- | Synchronisation function that is basically a represented by a (bundle of)+-- wire(s) in hardware. This function should only be used as part of a proper+-- synchronisation component, such as a dual flip-flop synchronizer, or a FIFO+-- with an asynchronous memory element:+--+-- > dualFlipFlop :: Clock clk1 -> Clock clk2+-- > -> CSignal clk1 Bit -> CSignal clk2 Bit+-- > dualFlipFlop clk1 clk2 = cregister clk2 L . cregister clk2 L . veryUnsafeSynchronizer clk1 clk2+--+-- The 'veryUnsafeSynchronizer' works in such a way that, given 2 clocks:+--+-- > clk7 = Clock d7+-- > clk2 = Clock d2+--+-- Oversampling followed by compression is the identity function plus 2 initial values:+--+-- > cregister clk7 i $+-- > veryUnsafeSynchronizer clk2 clk7 $+-- > cregister clk2 j $+-- > veryUnsafeSynchronizer clk7 clk2 $+-- > cregister clk7 k s+-- >+-- > ==+-- >+-- > i :- j :- s+--+-- Something we can easily observe:+--+-- > oversampling = cregister clk2 99 . veryUnsafeSynchronizer clk7 clk2 . cregister clk7 50+-- > almostId = cregister clk7 70 . veryUnsafeSynchronizer clk2 clk7+-- > . cregister clk2 99 . veryUnsafeSynchronizer clk7 clk2 . cregister clk7 50+-- >+--+-- >>> csample (oversampling (cfromList [1..10]))+-- [99, 50,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, ...+-- >>> csample (almostId (cfromList [1..10]))+-- [70, 99,1,2,3,4,5,6,7,8,9,10,...+veryUnsafeSynchronizer :: Clock clk1 -- ^ 'Clock' of the incoming signal+ -> Clock clk2 -- ^ 'Clock' of the outgoing signal+ -> CSignal clk1 a+ -> CSignal clk2 a+veryUnsafeSynchronizer (Clock clk1) (Clock clk2) s = s'+ where+ t1 = fromInteger (snatToInteger clk1)+ t2 = fromInteger (snatToInteger clk2)+ s' | t1 < t2 = compress t2 t1 s+ | t1 > t2 = oversample t1 t2 s+ | otherwise = same s++same :: CSignal clk1 a -> CSignal clk2 a+same (CSignal s) = CSignal s++oversample :: Int -> Int -> CSignal clk1 a -> CSignal clk2 a+oversample high low (CSignal (s :- ss)) = CSignal (s :- oversampleS (reverse (repSchedule high low)) ss)++oversampleS :: [Int] -> Signal a -> Signal a+oversampleS sched = oversample' sched+ where+ oversample' [] s = oversampleS sched s+ oversample' (d:ds) (s:-ss) = prefixN d s (oversample' ds ss)++ prefixN 0 _ s = s+ prefixN n x s = x :- prefixN (n-1) x s++compress :: Int -> Int -> CSignal clk1 a -> CSignal clk2 a+compress high low (CSignal s) = CSignal (compressS (repSchedule high low) s)++compressS :: [Int] -> Signal a -> Signal a+compressS sched = compress' sched+ where+ compress' [] s = compressS sched s+ compress' (d:ds) ss@(s :- _) = s :- compress' ds (dropS d ss)++ dropS 0 s = s+ dropS n (_ :- ss) = dropS (n-1) ss++repSchedule :: Int -> Int -> [Int]+repSchedule high low = take low $ repSchedule' low high 1+ where+ repSchedule' cnt th rep+ | cnt < th = repSchedule' (cnt+low) th (rep + 1)+ | otherwise = rep : repSchedule' (cnt + low) (th + high) 1++-- | Implicitly clocked signals have a clock with period 1000+fromImplicit :: Signal a -> CSignal 1000 a+fromImplicit s = CSignal s++-- | Implicitly clocked signals have a clock with period 1000+fromExplicit :: CSignal 1000 a -> Signal a+fromExplicit (CSignal s) = s
+ src/CLaSH/Signal/Implicit.hs view
@@ -0,0 +1,248 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE TypeFamilies #-}++module CLaSH.Signal.Implicit+ ( -- * Implicitly clocked synchronous signal+ Signal+ -- * Basic circuit functions+ , signal+ , register+ , Pack(..)+ , (<^), (^>)+ -- * Simulation functions+ , simulate+ , simulateP+ -- * List \<-\> Signal conversion+ , sample+ , sampleN+ , fromList+ )+where++import Control.Applicative++import CLaSH.Bit (Bit)+import CLaSH.Sized.Signed (Signed)+import CLaSH.Sized.Unsigned (Unsigned)+import CLaSH.Sized.Vector (Vec(..), vmap, vhead, vtail)++import CLaSH.Signal.Types++{-# NOINLINE register #-}++-- | Create a 'Signal' from a list+--+-- Every element in the list will correspond to a value of the signal for one+-- clock cycle.+--+-- NB: Simulation only!+--+-- >>> sampleN 2 (fromList [1,2,3,4,5])+-- [1,2]+fromList :: [a] -> Signal a+fromList [] = error "finite list"+fromList (x:xs) = x :- fromList xs++-- | Get an infinite list of samples from a 'Signal'+--+-- The elements in the list correspond to the values of the 'Signal' at+-- consecutive clock cycles+--+-- > sample s == [s0, s1, s2, s3, ...+sample :: Signal a -> [a]+sample ~(x :- xs) = x : sample xs++-- | Get a list of @n@ samples from a 'Signal'+--+-- The elements in the list correspond to the values of the 'Signal' at+-- consecutive clock cycles+--+-- > sampleN 3 s == [s0, s1, s2]+sampleN :: Int -> Signal a -> [a]+sampleN 0 _ = []+sampleN n ~(x :- xs) = x : (sampleN (n-1) xs)++-- | 'register' @i s@ delays the values in 'Signal' @s@ for one cycle, and sets+-- the value at time 0 to @i@+--+-- >>> sampleN 3 (register 8 (fromList [1,2,3,4]))+-- [8,1,2]+register :: a -> Signal a -> Signal a+register i s = i :- s++-- | Simulate a (@'Signal' a -> 'Signal' b@) function given a list of samples of+-- type @a@+--+-- >>> simulate (register 8) [1, 2, 3, ...+-- [8, 1, 2, 3, ...+simulate :: (Signal a -> Signal b) -> [a] -> [b]+simulate f = sample . f . fromList++-- | Isomorphism between a 'Signal' of a product type (e.g. a tuple) and a+-- product type of 'Signal's+class Pack a where+ type SignalP a+ -- | Example:+ --+ -- > pack :: (Signal a, Signal b) -> Signal (a,b)+ --+ -- However:+ --+ -- > pack :: Signal Bit -> Signal Bit+ pack :: SignalP a -> Signal a+ -- | Example:+ --+ -- > unpack :: Signal (a,b) -> (Signal a, Signal b)+ --+ -- However:+ --+ -- > unpack :: Signal Bit -> Signal Bit+ unpack :: Signal a -> SignalP a++-- | Simulate a (@'SignalP' a -> 'SignalP' b@) function given a list of samples+-- of type @a@+--+-- >>> simulateP (unpack . register (8,8) . pack) [(1,1), (2,2), (3,3), ...+-- [(8,8), (1,1), (2,2), (3,3), ...+simulateP :: (Pack a, Pack b) => (SignalP a -> SignalP b) -> [a] -> [b]+simulateP f = simulate (pack . f . unpack)++instance Pack Bit where+ type SignalP Bit = Signal Bit+ pack = id+ unpack = id++instance Pack (Signed n) where+ type SignalP (Signed n) = Signal (Signed n)+ pack = id+ unpack = id++instance Pack (Unsigned n) where+ type SignalP (Unsigned n) = Signal (Unsigned n)+ pack = id+ unpack = id++instance Pack Bool where+ type SignalP Bool = Signal Bool+ pack = id+ unpack = id++instance Pack Integer where+ type SignalP Integer = Signal Integer+ pack = id+ unpack = id++instance Pack Int where+ type SignalP Int = Signal Int+ pack = id+ unpack = id++instance Pack Float where+ type SignalP Float = Signal Float+ pack = id+ unpack = id++instance Pack Double where+ type SignalP Double = Signal Double+ pack = id+ unpack = id++instance Pack () where+ type SignalP () = Signal ()+ pack = id+ unpack = id++instance Pack (a,b) where+ type SignalP (a,b) = (Signal a, Signal b)+ pack = uncurry (liftA2 (,))+ unpack tup = (fmap fst tup, fmap snd tup)++instance Pack (a,b,c) where+ type SignalP (a,b,c) = (Signal a, Signal b, Signal c)+ pack (a,b,c) = (,,) <$> a <*> b <*> c+ unpack tup = (fmap (\(x,_,_) -> x) tup+ ,fmap (\(_,x,_) -> x) tup+ ,fmap (\(_,_,x) -> x) tup+ )++instance Pack (a,b,c,d) where+ type SignalP (a,b,c,d) = (Signal a, Signal b, Signal c, Signal d)+ pack (a,b,c,d) = (,,,) <$> a <*> b <*> c <*> d+ unpack tup = (fmap (\(x,_,_,_) -> x) tup+ ,fmap (\(_,x,_,_) -> x) tup+ ,fmap (\(_,_,x,_) -> x) tup+ ,fmap (\(_,_,_,x) -> x) tup+ )++instance Pack (a,b,c,d,e) where+ type SignalP (a,b,c,d,e) = (Signal a, Signal b, Signal c, Signal d, Signal e)+ pack (a,b,c,d,e) = (,,,,) <$> a <*> b <*> c <*> d <*> e+ unpack tup = (fmap (\(x,_,_,_,_) -> x) tup+ ,fmap (\(_,x,_,_,_) -> x) tup+ ,fmap (\(_,_,x,_,_) -> x) tup+ ,fmap (\(_,_,_,x,_) -> x) tup+ ,fmap (\(_,_,_,_,x) -> x) tup+ )++instance Pack (a,b,c,d,e,f) where+ type SignalP (a,b,c,d,e,f) = (Signal a, Signal b, Signal c, Signal d, Signal e, Signal f)+ pack (a,b,c,d,e,f) = (,,,,,) <$> a <*> b <*> c <*> d <*> e <*> f+ unpack tup = (fmap (\(x,_,_,_,_,_) -> x) tup+ ,fmap (\(_,x,_,_,_,_) -> x) tup+ ,fmap (\(_,_,x,_,_,_) -> x) tup+ ,fmap (\(_,_,_,x,_,_) -> x) tup+ ,fmap (\(_,_,_,_,x,_) -> x) tup+ ,fmap (\(_,_,_,_,_,x) -> x) tup+ )++instance Pack (a,b,c,d,e,f,g) where+ type SignalP (a,b,c,d,e,f,g) = (Signal a, Signal b, Signal c, Signal d, Signal e, Signal f, Signal g)+ pack (a,b,c,d,e,f,g) = (,,,,,,) <$> a <*> b <*> c <*> d <*> e <*> f <*> g+ unpack tup = (fmap (\(x,_,_,_,_,_,_) -> x) tup+ ,fmap (\(_,x,_,_,_,_,_) -> x) tup+ ,fmap (\(_,_,x,_,_,_,_) -> x) tup+ ,fmap (\(_,_,_,x,_,_,_) -> x) tup+ ,fmap (\(_,_,_,_,x,_,_) -> x) tup+ ,fmap (\(_,_,_,_,_,x,_) -> x) tup+ ,fmap (\(_,_,_,_,_,_,x) -> x) tup+ )++instance Pack (a,b,c,d,e,f,g,h) where+ type SignalP (a,b,c,d,e,f,g,h) = (Signal a, Signal b, Signal c, Signal d, Signal e, Signal f, Signal g, Signal h)+ pack (a,b,c,d,e,f,g,h) = (,,,,,,,) <$> a <*> b <*> c <*> d <*> e <*> f <*> g <*> h+ unpack tup = (fmap (\(x,_,_,_,_,_,_,_) -> x) tup+ ,fmap (\(_,x,_,_,_,_,_,_) -> x) tup+ ,fmap (\(_,_,x,_,_,_,_,_) -> x) tup+ ,fmap (\(_,_,_,x,_,_,_,_) -> x) tup+ ,fmap (\(_,_,_,_,x,_,_,_) -> x) tup+ ,fmap (\(_,_,_,_,_,x,_,_) -> x) tup+ ,fmap (\(_,_,_,_,_,_,x,_) -> x) tup+ ,fmap (\(_,_,_,_,_,_,_,x) -> x) tup+ )++instance Pack (Vec n a) where+ type SignalP (Vec n a) = Vec n (Signal a)+ pack vs = vmap shead vs :- pack (vmap stail vs)+ unpack (Nil :- _) = Nil+ unpack vs@((_ :> _) :- _) = fmap vhead vs :> (unpack (fmap vtail vs))+++-- | Operator lifting, use in conjunction with ('^>')+--+-- > add2 :: Signal Int -> Signal Int+-- > add2 x = x <^(+)^> (signal 2)+--+-- >>> simulate add2 [1,2,3,...+-- [3,4,5,...+(<^) :: Applicative f => f a -> (a -> b -> c) -> f b -> f c+v <^ f = liftA2 f v++-- | Operator lifting, use in conjunction with ('<^')+--+-- > add2 :: Signal Int -> Signal Int+-- > add2 x = x <^(+)^> (signal 2)+--+-- >>> simulate add2 [1,2,3,...+-- [3,4,5,...+(^>) :: Applicative f => (f a -> f b) -> f a -> f b+f ^> v = f v
+ src/CLaSH/Signal/Types.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE TemplateHaskell #-}+module CLaSH.Signal.Types where++import Data.Coerce (coerce)+import Data.Default (Default (..))+import Control.Applicative (Applicative (..), liftA2)+import GHC.TypeLits (Nat)+import Language.Haskell.TH.Syntax (Lift (..))++import CLaSH.Promoted.Nat (SNat)++infixr 5 :-+-- | A synchronized signal with samples of type @a@, implicitly synchronized to+-- an unnamed global clock+data Signal a = a :- Signal a++-- | A synchronized signal with samples of type @a@, explicitly synchronized to+-- a clock with period @clk@+newtype CSignal (clk :: Nat) a = CSignal (Signal a)+ deriving (Show,Default,Lift,Functor,Applicative)++-- | A clock with period @clk@+newtype Clock (clk :: Nat) = Clock (SNat clk)++instance Show a => Show (Signal a) where+ show (x :- xs) = show x ++ " " ++ show xs++instance Lift a => Lift (Signal a) where+ lift ~(x :- _) = [| signal x |]++instance Default a => Default (Signal a) where+ def = signal def++{-# NOINLINE signal #-}+{-# NOINLINE mapSignal #-}+{-# NOINLINE appSignal #-}++-- | Create a constant 'Signal' from a combinational value+--+-- >>> sample (signal 4)+-- [4, 4, 4, 4, ...+signal :: a -> Signal a+signal a = let s = a :- s in s++mapSignal :: (a -> b) -> Signal a -> Signal b+mapSignal f (a :- as) = f a :- mapSignal f as++appSignal :: Signal (a -> b) -> Signal a -> Signal b+appSignal (f :- fs) ~(a :- as) = f a :- appSignal fs as++instance Functor Signal where+ fmap = mapSignal++instance Applicative Signal where+ pure = signal+ (<*>) = appSignal++shead :: Signal a -> a+shead (x :- _) = x++stail :: Signal a -> Signal a+stail (_ :- xs) = xs++mkCSignal :: a -> CSignal clk a -> CSignal clk a+mkCSignal a (CSignal s) = CSignal (a :- s)++cstail :: CSignal t a -> CSignal t a+cstail (CSignal s) = CSignal (stail s)++-- | Create a constant 'CSignal' from a combinational value+--+-- >>> csample (csignal 4)+-- [4, 4, 4, 4, ...+csignal :: a -> CSignal t a+csignal a = coerce (signal a)++instance Num a => Num (Signal a) where+ (+) = liftA2 (+)+ (-) = liftA2 (-)+ (*) = liftA2 (*)+ negate = fmap negate+ abs = fmap abs+ signum = fmap signum+ fromInteger = signal . fromInteger++instance Num a => Num (CSignal t a) where+ (+) = liftA2 (+)+ (-) = liftA2 (-)+ (*) = liftA2 (*)+ negate = fmap negate+ abs = fmap abs+ signum = fmap signum+ fromInteger = csignal . fromInteger
src/CLaSH/Sized/Fixed.hs view
@@ -1,157 +1,522 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++{-# OPTIONS_GHC -fexpose-all-unfoldings -fno-strictness #-}++-- | Fixed point numbers+--+-- * The 'Num' operators for the given types saturate on overflow,+-- and use truncation as the rounding method.+--+-- * Use @$$('fLit' d)@ to create 'Fixed' point number literals.+-- * Use <#constraintsynonyms Constraint synonyms> when writing type signatures+-- for polymorphic functions that use 'Fixed' point numbers.+--+-- BEWARE: rounding by truncation introduces a sign bias!+--+-- * Truncation for positive numbers effectively results in: round towards zero.+-- * Truncation for negative numbers effectively results in: round towards -infinity. module CLaSH.Sized.Fixed- ( -- * Signed fixed point+ ( -- * 'SFixed': 'Signed' 'Fixed' point numbers SFixed, sf, unSF- -- * Unsigned fixed point+ -- * 'UFixed': 'Unsigned' 'Fixed' point numbers , UFixed, uf, unUF+ -- * 'Fixed' point literals+ , fLit+ -- * 'Fixed' point wrapper+ , Fixed (..), resizeF, fracShift, satN2+ -- * Constraint synonyms+ -- $constraintsynonyms++ -- ** Constraint synonyms for 'SFixed'+ , NumSFixed, AddSFixed, MultSFixed, ResizeSFC+ -- ** Constraint synonyms for 'UFixed'+ , NumUFixed, AddUFixed, MultUFixed, ResizeUFC+ -- ** Constraint synonyms for 'Fixed' wrapper+ , NumFixed, AddFixed, MultFixed, ResizeFC, SatN2C+ -- ** Constraint synonyms for 'Signed' and 'Unsigned'+ , SatN2SC, SatN2UC+ -- * Proxy+ , asFracProxy, asRepProxy ) where import Control.Arrow import Data.Bits import Data.Default-import Data.Function import Data.List import Data.Maybe import Data.Proxy import Data.Ratio+import Data.Typeable import GHC.TypeLits import Language.Haskell.TH import Language.Haskell.TH.Syntax(Lift(..)) +import CLaSH.Bit import CLaSH.Class.BitVector import CLaSH.Class.Num-import CLaSH.Signal+import CLaSH.Promoted.Nat+import CLaSH.Promoted.Ord import CLaSH.Sized.Signed import CLaSH.Sized.Unsigned+import CLaSH.Sized.Vector --- | Fixed point signed integer with @i@ integer bits and @f@ fractional bits+-- | 'Fixed'-point number ----- For now, overflow behaviour for the 'Num' functions is wrap-around, not saturate-newtype SFixed (i :: Nat) (f :: Nat) = SF { unSF :: Signed (i + f) }+-- Where:+--+-- * @frac@ denotes the position of the virtual @point@ counting from the LSB+--+-- * @rep@ is the underlying representation+--+-- * @size@ is the number of bits used to represent the number+--+-- The 'Num' operators for this type saturate on overflow,+-- and use truncation as the rounding method.+newtype Fixed (frac :: Nat) (rep :: Nat -> *) (size :: Nat) = Fixed { unFixed :: rep size } deriving (Eq,Ord) --- | Fixed point unsigned integer with @i@ integer bits and @f@ fractional bits+-- | Signed 'Fixed'-point number, with @int@ integer bits (including sign-bit)+-- and @frac@ fractional bits. ----- For now, overflow behaviour for the 'Num' functions is wrap-around, not saturate-newtype UFixed (i :: Nat) (f :: Nat) = UF { unUF :: Unsigned (i + f) }- deriving (Eq,Ord)+-- * The range 'SFixed' @int@ @frac@ numbers is: [-(2^(@int@ -1)) .. 2^(@int@-1) - 2^-@frac@ ]+-- * The resolution of 'SFixed' @int@ @frac@ numbers is: 2^@frac@+-- * The 'Num' operators for this type saturate on overflow,+-- and use truncation as the rounding method.+--+-- >>> maxBound :: SFixed 3 4+-- 3.9375+-- >>> minBound :: SFixed 3 4+-- -4.0+-- >>> (1 :: SFixed 3 4) + (2 :: SFixed 3 4)+-- 3.0+-- >>> (2 :: SFixed 3 4) + (3 :: SFixed 3 4)+-- 3.9375+-- >>> (-2 :: SFixed 3 4) + (-3 :: SFixed 3 4)+-- -4.0+-- >>> ($$(fLit 1.375) :: SFixed 3 4) * ($$(fLit -0.8125) :: SFixed 3 4)+-- -1.125+-- >>> ($$(fLit 1.375) :: SFixed 3 4) `mult` ($$(fLit -0.8125) :: SFixed 3 4) :: SFixed 6 8+-- -1.1171875+-- >>> (2 :: SFixed 3 4) `plus` (3 :: SFixed 3 4) :: SFixed 4 4+-- 5.0+-- >>> (-2 :: SFixed 3 4) `plus` (-3 :: SFixed 3 4) :: SFixed 4 4+-- -5.0+type SFixed int frac = Fixed frac Signed (int + frac) -fracShift :: KnownNat n => proxy n -> Int-fracShift = fromInteger . natVal+-- | Unsigned 'Fixed'-point number, with @int@ integer bits and @frac@ fractional bits+--+-- * The range 'UFixed' @int@ @frac@ numbers is: [0 .. 2^@int@ - 2^-@frac@ ]+-- * The resolution of 'UFixed' @int@ @frac@ numbers is: 2^@frac@+-- * The 'Num' operators for this type saturate on overflow,+-- and use truncation as the rounding method.+--+-- >>> maxBound :: UFixed 3 4+-- 7.9375+-- >>> minBound :: UFixed 3 4+-- 0.0+-- >>> (1 :: UFixed 3 4) + (2 :: UFixed 3 4)+-- 3.0+-- >>> (2 :: UFixed 3 4) + (6 :: UFixed 3 4)+-- 7.9375+-- >>> (1 :: UFixed 3 4) - (3 :: UFixed 3 4)+-- 0.0+-- >>> ($$(fLit 1.375) :: UFixed 3 4) * ($$(fLit 0.8125) :: UFixed 3 4)+-- 1.0625+-- >>> ($$(fLit 1.375) :: UFixed 3 4) `mult` ($$(fLit 0.8125) :: UFixed 3 4) :: UFixed 6 8+-- 1.1171875+-- >>> (2 :: UFixed 3 4) `plus` (6 :: UFixed 3 4) :: UFixed 4 4+-- 8.0+--+-- However, 'minus' does not saturate to 'minBound' on underflow:+--+-- >>> (1 :: UFixed 3 4) `minus` (3 :: UFixed 3 4) :: UFixed 4 4+-- 14.0+type UFixed int frac = Fixed frac Unsigned (int + frac) -sf :: Signed (i + f) -> SFixed i f-sf = SF+-- | Treat a 'Signed' integer as a @Signed@ 'Fixed'-@point@ integer+--+-- >>> sf d4 (-22 :: Signed 7)+-- -1.375+sf :: SNat frac -- ^ Position of the virtual @point@+ -> Signed (int + frac) -- ^ The 'Signed' integer+ -> SFixed int frac+sf _ fRep = Fixed fRep -uf :: Unsigned (i + f) -> UFixed i f-uf = UF+-- | See the underlying representation of a Signed Fixed-point integer+unSF :: SFixed int frac+ -> Signed (int + frac)+unSF (Fixed fRep) = fRep -showFixed :: (Bits a, KnownNat n, Show a, Integral a) =>- (proxy n -> a) -> proxy n -> [Char]-showFixed unRep f = show i ++ "." ++ (uncurry pad . second (show . numerator) .- fromJust . find ((==1) . denominator . snd) .- iterate (succ *** (*10)) . (,) 0 $ (nom % denom))+-- | Treat an 'Unsigned' integer as a @Unsigned@ 'Fixed'-@point@ number+--+-- >>> uf d4 (92 :: Unsigned 7)+-- 5.75+uf :: SNat frac -- ^ Position of the virtual @point@+ -> Unsigned (int + frac) -- ^ The 'Unsigned' integer+ -> UFixed int frac+uf _ fRep = Fixed fRep++-- | See the underlying representation of an Unsigned Fixed-point integer+unUF :: UFixed int frac+ -> Unsigned (int + frac)+unUF (Fixed fRep) = fRep++asFracProxy :: Fixed frac rep size -> Proxy frac+asFracProxy _ = Proxy++asRepProxy :: Fixed frac rep size -> Proxy rep+asRepProxy _ = Proxy++-- | Get the position of the virtual @point@ of a 'Fixed'-@point@ number+fracShift :: KnownNat frac => Fixed frac rep size -> Int+fracShift f = fromInteger (natVal (asFracProxy f))++instance ( Show (rep size), Bits (rep size), KnownNat frac+ , Integral (rep size)+ ) => Show (Fixed frac rep size) where+ show f@(Fixed fRep) = i ++ "." ++ (uncurry pad . second (show . numerator) .+ fromJust . find ((==1) . denominator . snd) .+ iterate (succ *** (*10)) . (,) 0 $ (nom % denom)) where pad n str = replicate (n - length str) '0' ++ str+ nF = fracShift f- rep = unRep f- i = rep `shiftR` nF- nom = toInteger rep .&. ((2 ^ nF) - 1)+ fRepI = toInteger fRep+ fRepI_abs = abs fRepI+ i = if fRepI < 0 then '-' : show (fRepI_abs `shiftR` nF)+ else show (fRepI `shiftR` nF)+ nom = if fRepI < 0 then fRepI_abs .&. ((2 ^ nF) - 1)+ else fRepI .&. ((2 ^ nF) - 1) denom = 2 ^ nF -instance (KnownNat (i + f), KnownNat f) => Show (SFixed i f) where- show = showFixed unSF+{- $constraintsynonyms #constraintsynonyms#+Writing polymorphic functions over fixed point numbers can be a potentially+verbose due to the many class constraints induced by the functions and operators+of this module. -instance (KnownNat (i + f), KnownNat f) => Show (UFixed i f) where- show = showFixed unUF+Writing a simple multiply-and-accumulate function can already give rise to many+lines of constraints: -multFixedS :: (KnownNat ((i + f) + (i + f)), KnownNat (i + f), KnownNat f)- => SFixed i f -> SFixed i f -> SFixed i f-multFixedS (SF a) (SF b) = res- where- resM = mult a b- resS = resM `shiftR` (fracShift res)- res = SF (resizeS resS)+@+mac :: ( 1 <= (int + frac), (((int + frac) + 1) + 1) ~ ((int + frac) + 2)+ , KnownNat (frac + frac), KnownNat ((int + frac) + (int + frac))+ , KnownNat ((int + frac) + 2), KnownNat (int + frac), KnownNat frac+ )+ => SFixed int frac+ -> SFixed int frac+ -> SFixed int frac+ -> SFixed int frac+mac s x y = s + (x * y)+@ -multFixedU :: (KnownNat ((i + f) + (i + f)), KnownNat (i + f), KnownNat f)- => UFixed i f -> UFixed i f -> UFixed i f-multFixedU (UF a) (UF b) = res- where- resM = mult a b- resS = resM `shiftR` (fracShift res)- res = UF (resizeU resS)+But with constraint synonyms, you can write the type signature like this: -fixedFromInteger :: (Bits a, KnownNat n, Num a)- => (a -> proxy n) -> Integer -> proxy n-fixedFromInteger toF i = res- where- res = toF (fromInteger i `shiftL` fracShift res)+@+mac :: NumSFixed int frac+ => SFixed int frac+ -> SFixed int frac+ -> SFixed int frac+ -> SFixed int frac+mac s x y = s + (x * y)+@ -instance (KnownNat ((i + f) + (i + f)), KnownNat (i + f), KnownNat f) => Num (SFixed i f) where- (+) = (SF .) . on (+) unSF- (*) = multFixedS- (-) = (SF .) . on (-) unSF- negate = SF . negate . unSF- abs = SF . abs . unSF- signum = SF . signum . unSF- fromInteger = fixedFromInteger SF+Where 'NumSFixed' refers to the @Constraints@ needed by the operators of+the 'Num' class for the 'SFixed' datatype.+-} -instance (KnownNat ((i + f) + (i + f)), KnownNat (i + f), KnownNat f) => Num (UFixed i f) where- (+) = (UF .) . on (+) unUF- (*) = multFixedU- (-) = (UF .) . on (-) unUF- negate = UF . negate . unUF- abs = UF . abs . unUF- signum = UF . signum . unUF- fromInteger = fixedFromInteger UF+-- | Constraint for the 'Mult' instance of 'Fixed'+type MultFixed rep (frac1 :: Nat) (frac2 :: Nat) (size1 :: Nat) (size2 :: Nat)+ = ( Mult (rep size1) (rep size2)+ , MResult (rep size1) (rep size2) ~ rep (size1 + size2)+ ) -instance BitVector (SFixed i f) where- type BitSize (SFixed i f) = i + f- toBV (SF s) = toBV s- fromBV bv = SF (fromBV bv)+-- | Constraint for the 'Mult' instance of 'SFixed'+type MultSFixed int1 frac1 int2 frac2 = MultFixed Signed frac1 frac2 (int1 + frac1) (int2 + frac2) -instance BitVector (UFixed i f) where- type BitSize (UFixed i f) = i + f- toBV (UF s) = toBV s- fromBV bv = UF (fromBV bv)+-- | Constraint for the 'Mult' instance of 'UFixed'+type MultUFixed int1 frac1 int2 frac2 = MultFixed Unsigned frac1 frac2 (int1 + frac1) (int2 + frac2) -instance Pack (SFixed i f) where- type SignalP (SFixed i f) = Signal (SFixed i f)- pack = id- unpack = id+-- | When used in a polymorphic setting, use the following <CLaSH-Sized-Fixed.html#constraintsynonyms Constraint synonyms>+-- for less verbose type signatures:+--+-- * @'MultFixed' rep frac1 frac2 size1 size2@ for: 'Fixed'+-- * @'MultSFixed' int1 frac1 int2 frac2@ for: 'SFixed'+-- * @'MultUFixed' int1 frac1 int2 frac2@ for: 'UFixed'+instance MultFixed rep frac1 frac2 size1 size2 => Mult (Fixed frac1 rep size1) (Fixed frac2 rep size2) where+ type MResult (Fixed frac1 rep size1) (Fixed frac2 rep size2) = Fixed (frac1 + frac2) rep (size1 + size2)+ mult (Fixed fRep1) (Fixed fRep2) = Fixed (mult fRep1 fRep2) -instance Pack (UFixed i f) where- type SignalP (UFixed i f) = Signal (UFixed i f)- pack = id- unpack = id+-- | Constraint for the 'Add' instance of 'Fixed'+type AddFixed rep (frac1 :: Nat) (frac2 :: Nat) (size1 :: Nat) (size2 :: Nat)+ = ( ResizeFC rep frac1 (Max frac1 frac2) size1 ((Max size1 size2) + 1)+ , ResizeFC rep frac2 (Max frac1 frac2) size2 ((Max size1 size2) + 1)+ , Num (rep (Max size1 size2 + 1))+ ) -instance (KnownNat i, KnownNat f, KnownNat (i + f)) => Lift (SFixed i f) where- lift s@(SF i) = sigE [| SF i |] (decSFixed (natVal (asProxy s)) (natVal s))- where- asProxy :: SFixed n a -> Proxy n- asProxy _ = Proxy+-- | Constraint for the 'Add' instance of 'SFixed'+type AddSFixed int1 frac1 int2 frac2 = AddFixed Signed frac1 frac2 (int1 + frac1) (int2 + frac2) -instance (KnownNat i, KnownNat f, KnownNat (i + f)) => Lift (UFixed i f) where- lift s@(UF i) = sigE [| UF i |] (decUFixed (natVal (asProxy s)) (natVal s))- where- asProxy :: UFixed n a -> Proxy n- asProxy _ = Proxy+-- | Constraint for the 'Add' instance of 'UFixed'+type AddUFixed int1 frac1 int2 frac2 = AddFixed Unsigned frac1 frac2 (int1 + frac1) (int2 + frac2) -decSFixed :: Integer -> Integer -> TypeQ-decSFixed i f = appT (appT (conT ''SFixed) (litT $ numTyLit i)) (litT $ numTyLit f)+-- | When used in a polymorphic setting, use the following <CLaSH-Sized-Fixed.html#constraintsynonyms Constraint synonyms>+-- for less verbose type signatures:+--+-- * @'AddFixed' rep frac1 frac2 size1 size2@ for: 'Fixed'+-- * @'AddSFixed' int1 frac1 int2 frac2@ for: 'SFixed'+-- * @'AddUFixed' int1 frac1 int2 frac2@ for: 'UFixed'+instance AddFixed rep frac1 frac2 size1 size2 => Add (Fixed frac1 rep size1) (Fixed frac2 rep size2) where+ type AResult (Fixed frac1 rep size1) (Fixed frac2 rep size2) = Fixed (Max frac1 frac2) rep ((Max size1 size2) + 1)+ plus f1 f2 = let (Fixed f1R) = resizeF f1 :: Fixed (Max frac1 frac2) rep ((Max size1 size2) + 1)+ (Fixed f2R) = resizeF f2 :: Fixed (Max frac1 frac2) rep ((Max size1 size2) + 1)+ in Fixed (f1R + f2R)+ minus f1 f2 = let (Fixed f1R) = resizeF f1 :: Fixed (Max frac1 frac2) rep ((Max size1 size2) + 1)+ (Fixed f2R) = resizeF f2 :: Fixed (Max frac1 frac2) rep ((Max size1 size2) + 1)+ in Fixed (f1R - f2R) -decUFixed :: Integer -> Integer -> TypeQ-decUFixed i f = appT (appT (conT ''SFixed) (litT $ numTyLit i)) (litT $ numTyLit f)+-- | Constraint for the 'Num' instance of 'Fixed'+type NumFixed (frac :: Nat) rep (size :: Nat)+ = ( SatN2C rep size+ , ResizeFC rep (frac + frac) frac (size + size) size+ , Num (rep size)+ , Num (rep (size + 2))+ , Mult (rep size) (rep size)+ , MResult (rep size) (rep size) ~ rep (size + size)+ ) -instance KnownNat (i + f) => Default (SFixed i f) where- def = SF 0+-- | Constraint for the 'Num' instance of 'SFixed'+type NumSFixed int frac = ( 1 <= (int + frac), (((int + frac) + 1) + 1) ~ ((int + frac) + 2)+ , KnownNat (frac + frac), KnownNat ((int + frac) + (int + frac))+ , KnownNat ((int + frac) + 2), KnownNat (int + frac), KnownNat frac+ )+-- | Constraint for the 'Num' instance of 'UFixed'+type NumUFixed int frac = ( 1 <= (int + frac), (((int + frac) + 1) + 1) ~ ((int + frac) + 2)+ , KnownNat (frac + frac), KnownNat ((int + frac) + (int + frac))+ , KnownNat ((int + frac) + 2), KnownNat (int + frac), KnownNat frac+ ) -instance KnownNat (i + f) => Default (UFixed i f) where- def = UF 0+-- | The operators of this instance saturate on overflow, and use truncation as the rounding method.+--+-- When used in a polymorphic setting, use the following <CLaSH-Sized-Fixed.html#constraintsynonyms Constraint synonyms>+-- for less verbose type signatures:+--+-- * @'NumFixed' frac rep size@ for: @'Fixed' frac rep size@+-- * @'NumSFixed' int frac@ for: @'SFixed' int frac@+-- * @'NumUFixed' int frac@ for: @'UFixed' int frac@+instance (NumFixed frac rep size) => Num (Fixed frac rep size) where+ (Fixed a) + (Fixed b) = Fixed (satN2 (resize a + resize b))+ (Fixed a) * (Fixed b) = resizeF (Fixed (a `mult` b) :: Fixed (frac + frac) rep (size + size))+ (Fixed a) - (Fixed b) = Fixed (satN2 (resize a - resize b))+ negate (Fixed a) = Fixed (satN2 (negate (resize a)))+ abs (Fixed a) = Fixed (satN2 (abs (resize a)))+ signum (Fixed a) = Fixed (signum a)+ fromInteger i = let fSH = fromInteger (natVal (Proxy :: Proxy frac))+ res = Fixed (fromInteger i `shiftL` fSH)+ in res++instance (BitVector (rep size)) => BitVector (Fixed frac rep size) where+ type BitSize (Fixed frac rep size) = BitSize (rep size)+ toBV (Fixed fRep) = toBV fRep+ fromBV bv = Fixed (fromBV bv)++instance (Lift (rep size), KnownNat frac, KnownNat size, Typeable rep) =>+ Lift (Fixed frac rep size) where+ lift f@(Fixed fRep) = sigE [| Fixed fRep |] (decFixed (natVal (asFracProxy f)) (typeRep (asRepProxy f)) (natVal f))++decFixed :: Integer -> TypeRep -> Integer -> TypeQ+decFixed f r s = do+ foldl appT (conT ''Fixed) [litT (numTyLit f), conT (mkName (show r)), litT (numTyLit s)]++instance Default (rep size) => Default (Fixed frac rep size) where+ def = Fixed def++instance Bounded (rep size) => Bounded (Fixed frac rep size) where+ minBound = Fixed minBound+ maxBound = Fixed maxBound++-- | Constraint for the 'resizeF' function+type ResizeFC rep frac1 frac2 size1 size2+ = ( Bounded (rep size2), Eq (rep size1), Ord (rep size1)+ , Num (rep size1), Bits (rep size1), Resize rep+ , KnownNat size2, KnownNat size1, Bits (rep size2)+ , KnownNat frac2, KnownNat frac1, Bounded (rep size1)+ )++-- | Constraint for the 'resizeF' function, specialized for 'SFixed'+type ResizeSFC int1 frac1 int2 frac2 = (KnownNat (int2 + frac2), KnownNat (int1 + frac1), KnownNat frac1, KnownNat frac2)++-- | Constraint for the 'resizeF' function, specialized for 'UFixed'+type ResizeUFC int1 frac1 int2 frac2 = (KnownNat (int2 + frac2), KnownNat (int1 + frac1), KnownNat frac1, KnownNat frac2)++-- | Saturating resize operation, truncates for rounding+--+-- >>> $$(fLit 0.8125) :: SFixed 3 4+-- 0.8125+-- >>> resizeF ($$(fLit 0.8125) :: SFixed 3 4) :: SFixed 2 3+-- 0.75+-- >>> $$(fLit 3.4) :: SFixed 3 4+-- 3.375+-- >>> resizeF ($$(fLit 3.4) :: SFixed 3 4) :: SFixed 2 3+-- 1.875+-- >>> maxBound :: SFixed 2 3+-- 1.875+--+-- When used in a polymorphic setting, use the following <#constraintsynonyms Constraint synonyms>+-- for less verbose type signatures:+--+-- * @'ResizeFC' rep frac1 frac2 size1 size2@ for: @'Fixed' frac1 rep size1 -> 'Fixed' frac2 rep size2@+-- * @'ResizeSFC' int1 frac1 int2 frac2@ for: @'SFixed' int1 frac1 -> 'SFixed' int2 frac2@+-- * @'ResizeUFC' int1 frac1 int2 frac2@ for: @'UFixed' int1 frac1 -> 'UFixed' int2 frac2@+resizeF :: forall frac1 frac2 rep size1 size2 .+ ResizeFC rep frac1 frac2 size1 size2+ => Fixed frac1 rep size1+ -> Fixed frac2 rep size2+resizeF (Fixed fRep) = Fixed sat+ where+ argSZ = natVal (Proxy :: Proxy size1)+ resSZ = natVal (Proxy :: Proxy size2)++ argFracSZ = fromInteger (natVal (Proxy :: Proxy frac1))+ resFracSZ = fromInteger (natVal (Proxy :: Proxy frac2))++ -- All size and frac comparisons and related if-then-else statements should+ -- be optimized away by the compiler+ sat = if argSZ <= resSZ+ -- if the argument is smaller than the result, resize before shift+ then if argFracSZ <= resFracSZ+ then resize fRep `shiftL` (resFracSZ - argFracSZ)+ else resize fRep `shiftR` (argFracSZ - resFracSZ)+ -- if the argument is bigger than the result, shift before resize+ else let fMax = maxBound+ fMin = minBound+ mask = complement (resize fMax) :: rep size1+ in if argFracSZ <= resFracSZ+ then let shiftedL = fRep `shiftL` (resFracSZ - argFracSZ)+ shiftedL_masked = shiftedL .&. mask+ shiftedL_resized = resize shiftedL+ in if fRep >= 0+ then if shiftedL_masked == 0+ then shiftedL_resized+ else fMax+ else if shiftedL_masked == mask+ then shiftedL_resized+ else fMin+ else let shiftedR = fRep `shiftR` (argFracSZ - resFracSZ)+ shiftedR_masked = shiftedR .&. mask+ shiftedR_resized = resize shiftedR+ in if fRep >= 0+ then if shiftedR_masked == 0+ then shiftedR_resized+ else fMax+ else if shiftedR_masked == mask+ then shiftedR_resized+ else fMin++-- | Constraint for the 'satN2' function+type SatN2C rep n+ = ( 1 <= n+ , ((n + 1) + 1) ~ (n + 2)+ , BitVector (rep n)+ , BitVector (rep (n + 2))+ , BitSize (rep n) ~ n+ , BitSize (rep (n + 2)) ~ (n + 2)+ , KnownNat n+ , KnownNat (n + 2)+ , Bounded (rep n)+ , Bits (rep (n + 2))+ )++-- | Constraint for the 'satN2' function, specialized for 'Signed'+type SatN2SC n = (1 <= n, ((n + 1) + 1) ~ (n + 2), KnownNat n, KnownNat (n + 2))++-- | Constraint for the 'satN2' function, specialized for 'Unsigned'+type SatN2UC n = (1 <= n, ((n + 1) + 1) ~ (n + 2), KnownNat n, KnownNat (n + 2))++-- | Resize an (N+2)-bits number to an N-bits number, saturates to+-- 'minBound' or 'maxBound' when the argument does not fit within+-- the representations bounds of the result.+--+-- Uses cheaper saturation than 'resizeF', which is made possible by knowing+-- that we only reduce the size by 2 bits.+--+-- >>> (2 :: Unsigned 2) + (3 :: Unsigned 2)+-- 1+-- >>> satN2 (resize (2 :: Unsigned 2) + resize (3 :: Unsigned 2)) :: Unsigned 2+-- 3+-- >>> satN2 (resize (1 :: Unsigned 2) + resize (1 :: Unsigned 2)) :: Unsigned 2+-- 2+-- >>> (2 :: Unsigned 2) - (3 :: Unsigned 2)+-- 3+-- >>> satN2 (resize (2 :: Unsigned 2) - resize (3 :: Unsigned 2)) :: Unsigned 2+-- 0+-- >>> (2 :: Signed 3) + (3 :: Signed 3)+-- -3+-- >>> satN2 (resize (2 :: Signed 3) + resize (3 :: Signed 3)) :: Signed 3+-- 3+--+-- When used in a polymorphic setting, use the following <#constraintsynonyms Constraint synonyms>+-- for less verbose type signatures:+--+-- * 'SatN2C' for: @rep (n+2) -> rep n@+-- * 'SatN2SC' for: @'Signed' (n+2) -> 'Signed' n@+-- * 'SatN2UC' for: @'Unsigned' (n+2) -> 'Unsigned' n@+satN2 :: SatN2C rep n+ => rep (n + 2)+ -> rep n+satN2 rep = if isSigned rep+ then case (cS,sn) of+ (L,H) -> maxBound+ (H,L) -> minBound+ _ -> fromBV s+ else case (cS,cU) of+ (H,H) -> minBound+ (L,H) -> maxBound+ _ -> fromBV s+ where+ repBV = toBV rep+ cS = vhead repBV+ cU = vhead (vtail repBV)+ s = vtail (vtail repBV)+ sn = vhead' s++-- | Convert, at compile-time, a 'Double' literal to a 'Fixed'-point literal.+-- The conversion saturates on overflow, and uses truncation as its rounding method.+--+-- So when you type:+--+-- > n = $$(fLit 2.2867) :: SFixed 4 4+--+-- The compiler sees:+--+-- > n = Fixed (fromInteger 45) :: SFixed 4 4+--+-- Upon evaluation you see that the value is rounded / truncated in accordance+-- to the fixed point representation:+--+-- >>> n+-- 2.8125+fLit :: forall frac rep size .+ (KnownNat frac, Num (rep size), Bounded (rep size), Integral (rep size))+ => Double+ -> Q (TExp (Fixed frac rep size))+fLit a = [|| Fixed (fromInteger sat) ||]+ where+ rMax = toInteger (maxBound :: rep size)+ rMin = toInteger (minBound :: rep size)+ sat = if truncated > rMax+ then rMax+ else if truncated < rMin+ then rMin+ else truncated+ truncated = truncate shifted :: Integer+ shifted = a * (2 ^ (natVal (Proxy :: Proxy frac)))
src/CLaSH/Sized/Signed.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-}@@ -10,13 +11,13 @@ module CLaSH.Sized.Signed ( Signed- , resizeS , resizeS_wrap ) where import Data.Bits import Data.Default+import Data.Typeable import Language.Haskell.TH import Language.Haskell.TH.Syntax(Lift(..)) import GHC.TypeLits@@ -27,8 +28,15 @@ import CLaSH.Promoted.Ord import CLaSH.Sized.Vector --- | Arbitrary-width signed integer represented by @n@ bits+-- | Arbitrary-width signed integer represented by @n@ bits, including the sign bit.+--+-- Uses standard 2-complements representation. Meaning that, given @n@ bits,+-- a 'Signed' @n@ number has a range of: [-(2^(@n@-1)) .. 2^(@n@-1)-1]+--+-- NB: The 'Num' operators perform @wrap-around@ on overflow. If you want saturation+-- on overflow, check out the 'CLaSH.Sized.Fixed.satN2' function in "CLaSH.Sized.Fixed". newtype Signed (n :: Nat) = S Integer+ deriving Typeable instance Eq (Signed n) where (==) = eqS@@ -69,6 +77,7 @@ {-# NOINLINE maxBoundS #-} maxBoundS = let res = S $ 2 ^ (natVal res - 1) - 1 in res +-- | Operators do @wrap-around@ on overflow instance KnownNat n => Num (Signed n) where (+) = plusS (-) = minS@@ -248,7 +257,7 @@ {-# NOINLINE finiteBitSizeS #-} finiteBitSizeS :: KnownNat n => Signed n -> Int-finiteBitSizeS i = let res = fromInteger (natVal i) in res+finiteBitSizeS = fromInteger . natVal instance Show (Signed n) where show (S n) = show n@@ -284,11 +293,6 @@ ] {-# NOINLINE resizeS #-}--- | A sign-preserving resize operation------ Increasing the size of the number replicates the sign bit to the left.--- Truncating a number to length L keeps the sign bit and the rightmost L-1 bits.--- resizeS :: (KnownNat n, KnownNat m) => Signed n -> Signed m resizeS s@(S n) | n' <= m' = extend | otherwise = trunc@@ -305,6 +309,12 @@ -- -- Increasing the size of the number replicates the sign bit to the left. -- Truncating a number of length N to a length L just removes the leftmost N-L bits.--- resizeS_wrap :: KnownNat m => Signed n -> Signed m resizeS_wrap (S n) = fromIntegerS_inlineable n++-- | A sign-preserving resize operation+--+-- Increasing the size of the number replicates the sign bit to the left.+-- Truncating a number to length L keeps the sign bit and the rightmost L-1 bits.+instance Resize Signed where+ resize = resizeS
src/CLaSH/Sized/Unsigned.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-}@@ -10,12 +11,12 @@ module CLaSH.Sized.Unsigned ( Unsigned- , resizeU ) where import Data.Bits import Data.Default+import Data.Typeable import Language.Haskell.TH import Language.Haskell.TH.Syntax(Lift(..)) import GHC.TypeLits@@ -27,7 +28,13 @@ import CLaSH.Sized.Vector -- | Arbitrary-width unsigned integer represented by @n@ bits+--+-- Given @n@ bits, an 'Unsigned' @n@ number has a range of: [0 .. 2^@n@-1]+--+-- NB: The 'Num' operators perform @wrap-around@ on overflow. If you want saturation+-- on overflow, check out the 'CLaSH.Sized.Fixed.satN2' function in "CLaSH.Sized.Fixed". newtype Unsigned (n :: Nat) = U Integer+ deriving Typeable instance Eq (Unsigned n) where (==) = eqU@@ -66,6 +73,7 @@ maxBoundU :: KnownNat n => Unsigned n maxBoundU = let res = U ((2 ^ natVal res) - 1) in res +-- | Operators do @wrap-around@ on overflow instance KnownNat n => Num (Unsigned n) where (+) = plusU (-) = minU@@ -263,11 +271,13 @@ ] {-# NOINLINE resizeU #-}--- | A resize operation that is zero-extends on extension, and wraps on truncation.+resizeU :: KnownNat m => Unsigned n -> Unsigned m+resizeU (U n) = fromIntegerU_inlineable n++-- | A resize operation that zero-extends on extension, and wraps on truncation. -- -- Increasing the size of the number extends with zeros to the left. -- Truncating a number of length N to a length L just removes the left -- (most significant) N-L bits.----resizeU :: KnownNat m => Unsigned n -> Unsigned m-resizeU (U n) = fromIntegerU_inlineable n+instance Resize Unsigned where+ resize = resizeU
src/CLaSH/Sized/Vector.hs view
@@ -10,22 +10,32 @@ {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-} module CLaSH.Sized.Vector- ( Vec(..), (<:)+ ( -- * 'Vec'tor constructors+ Vec(..), (<:)+ -- * Standard 'Vec'tor functions+ -- ** Extracting sub-'Vec'tors , vhead, vtail, vlast, vinit- , (+>>), (<<+), (<++>), vconcat+ , vtake, vtakeI, vdrop, vdropI, vexact, vselect, vselectI+ -- ** Combining 'Vec'tors+ , (+>>), (<<+), (<++>), vconcat, vzip, vunzip+ -- ** Splitting 'Vec'tors , vsplit, vsplitI, vunconcat, vunconcatI, vmerge- , vreverse, vmap, vzipWith+ -- ** Applying functions to 'Vec'tor elements+ , vmap, vzipWith , vfoldr, vfoldl, vfoldr1, vfoldl1- , vzip, vunzip+ -- ** Indexing 'Vec'tors , (!), vreplace, maxIndex, vlength- , vtake, vtakeI, vdrop, vdropI, vexact, vselect, vselectI+ -- ** Generating 'Vec'tors , vcopy, vcopyI, viterate, viterateI, vgenerate, vgenerateI- , toList, v, lazyV, asNatProxy+ -- ** Misc+ , vreverse, toList, v, lazyV, asNatProxy+ -- * Alternative 'Vec'tor functions+ , vhead' ) where import Control.Applicative-import Data.Traversable+-- import Data.Traversable import Data.Foldable hiding (toList) import Data.Proxy import GHC.TypeLits@@ -35,6 +45,16 @@ import CLaSH.Promoted.Nat +-- | Fixed size vectors+--+-- * Lists with their length encoded in their type+-- * 'Vec'tor elements have a descending subscript starting from 'maxIndex' ('vlength' - 1)+-- and ending at 0+--+-- >>> (3:>4:>5:>Nil)+-- <3,4,5>+-- >>> :t (3:>4:>5:>Nil)+-- (3:>4:>5:>Nil) :: Num a => Vec 3 a data Vec :: Nat -> * -> * where Nil :: Vec 0 a (:>) :: a -> Vec n a -> Vec (n + 1) a@@ -56,37 +76,58 @@ pure = vcopyI (<*>) = vzipWith ($) -instance Traversable (Vec n) where- traverse _ Nil = pure Nil- traverse f (x :> xs) = (:>) <$> f x <*> traverse f xs+-- instance Traversable (Vec n) where+-- traverse _ Nil = pure Nil+-- traverse f (x :> xs) = (:>) <$> f x <*> traverse f xs instance Foldable (Vec n) where- foldMap = foldMapDefault+ foldr = vfoldr instance Functor (Vec n) where- fmap = fmapDefault+ fmap = vmap {-# NOINLINE vhead #-} -- | Extract the first element of a vector ----- > vhead (1:>2:>3:>Nil) == 1--- > vhead Nil == TYPE ERROR+-- >>> vhead (1:>2:>3:>Nil)+-- 1+-- >>> vhead Nil+-- <interactive>+-- Couldn't match type ‘1’ with ‘0’+-- Expected type: Vec (0 + 1) a+-- Actual type: Vec 0 a+-- In the first argument of ‘vhead’, namely ‘Nil’+-- In the expression: vhead Nil vhead :: Vec (n + 1) a -> a vhead (x :> _) = x {-# NOINLINE vtail #-} -- | Extract the elements after the head of a vector ----- > vtail (1:>2:>3:>Nil) == (2:>3:>Nil)--- > vtail Nil == TYPE ERROR+-- >>> vtail (1:>2:>3:>Nil)+-- <2,3>+-- >>> vtail Nil+-- <interactive>+-- Couldn't match type ‘1’ with ‘0’+-- Expected type: Vec (0 + 1) a+-- Actual type: Vec 0 a+-- In the first argument of ‘vtail’, namely ‘Nil’+-- In the expression: vtail Nil vtail :: Vec (n + 1) a -> Vec n a vtail (_ :> xs) = unsafeCoerce xs {-# NOINLINE vlast #-} -- | Extract the last element of a vector ----- > vlast (1:>2:>3:>Nil) == 3--- > vlast Nil == TYPE ERROR+-- >>> vlast (1:>2:>3:>Nil)+-- 3+-- >>> vlast Nil+-- <interactive>+-- Couldn't match type ‘1’ with ‘0’+-- Expected type: Vec (0 + 1) a+-- Actual type: Vec 0 a+-- In the first argument of ‘vlast’, namely ‘Nil’+-- In the expression: vlast Nil vlast :: Vec (n + 1) a -> a vlast (x :> Nil) = x vlast (_ :> y :> ys) = vlast (y :> ys)@@ -94,8 +135,15 @@ {-# NOINLINE vinit #-} -- | Extract all the elements of a vector except the last element ----- > vinit (1:>2:>3:>Nil) == (1:>2:>Nil)--- > vinit Nil == TYPE ERROR+-- >>> vinit (1:>2:>3:>Nil)+-- <1,2>+-- >>> vinit Nil+-- <interactive>+-- Couldn't match type ‘1’ with ‘0’+-- Expected type: Vec (0 + 1) a+-- Actual type: Vec 0 a+-- In the first argument of ‘vinit’, namely ‘Nil’+-- In the expression: vinit Nil vinit :: Vec (n + 1) a -> Vec n a vinit (_ :> Nil) = unsafeCoerce Nil vinit (x :> y :> ys) = unsafeCoerce (x :> vinit (y :> ys))@@ -112,8 +160,10 @@ -- | Add an element to the head of the vector, and extract all elements of the -- resulting vector except the last element ----- > 1 +>> (3:>4:>5:>Nil) == (1:>3:>4:>Nil)--- > 1 +>> Nil == Nil+-- >>> 1 +>> (3:>4:>5:>Nil)+-- <1,3,4>+-- >>> 1 +>> Nil+-- <> (+>>) :: a -> Vec n a -> Vec n a s +>> xs = shiftIntoL s xs @@ -127,8 +177,10 @@ {-# INLINEABLE (<:) #-} -- | Add an element to the tail of the vector ----- > (3:>4:>5:>Nil) <: 1 == (3:>4:>5:>1:>Nil)--- > Nil <: 1 == (1:>Nil)+-- >>> (3:>4:>5:>Nil) <: 1+-- <3,4,5,1>+-- >>> :t (3:>4:>5:>Nil) <: 1+-- (3:>4:>5:>Nil) <: 1 :: Num a => Vec 4 a (<:) :: Vec n a -> a -> Vec (n + 1) a xs <: s = snoc s xs @@ -144,8 +196,10 @@ -- | Add an element to the tail of the vector, and extract all elements of the -- resulting vector except the first element ----- > (3:>4:>5:>Nil) <<+ 1 == (4:>5:>1:>Nil)--- > Nil <<+ 1 == Nil+-- >>> (3:>4:>5:>Nil) <<+ 1+-- <4,5,1>+-- >>> Nil <<+ 1+-- <> (<<+) :: Vec n a -> a -> Vec n a xs <<+ s = shiftIntoR s xs @@ -159,15 +213,18 @@ {-# INLINE (<++>) #-} -- | Append two vectors ----- > (1:>2:>3:>Nil) <++> (7:>8:>Nil) = (1:>2:>3:>7:>8:>Nil)+-- >>> (1:>2:>3:>Nil) <++> (7:>8:>Nil)+-- <1,2,3,7,8> (<++>) :: Vec n a -> Vec m a -> Vec (n + m) a xs <++> ys = vappend xs ys {-# NOINLINE vsplit #-} -- | Split a vector into two vectors at the given point ----- > vsplit (snat :: SNat 3) (1:>2:>3:>7:>8:>Nil) == (1:>2:>3:>Nil, 7:>8:>Nil)--- > vsplit d3 (1:>2:>3:>7:>8:>Nil) == (1:>2:>3:>Nil, 7:>8:>Nil)+-- >>> vsplit (snat :: SNat 3) (1:>2:>3:>7:>8:>Nil)+-- (<1,2,3>, <7,8>)+-- >>> vsplit d3 (1:>2:>3:>7:>8:>Nil)+-- (<1,2,3>, <7,8>) vsplit :: SNat m -> Vec (m + n) a -> (Vec m a, Vec n a) vsplit n xs = vsplitU (toUNat n) xs @@ -179,17 +236,17 @@ {-# INLINEABLE vsplitI #-} -- | Split a vector into two vectors where the length of the two is determined -- by the context+--+-- >>> vsplitI (1:>2:>3:>7:>8:>Nil) :: (Vec 2 Int, Vec 3 Int)+-- (<1,2>,<3,7,8>) vsplitI :: KnownNat m => Vec (m + n) a -> (Vec m a, Vec n a) vsplitI = withSNat vsplit {-# NOINLINE vconcat #-} -- | Concatenate a vector of vectors ----- > vunconcat ((1:>2:>3:>Nil) :>--- > (4:>5:>6:>Nil) :>--- > (7:>8:>9:>Nil) :>--- > (10:>11:>12:>Nil) :>--- > Nil) == (1:>2:>3:>4:>5:>6:>7:>8:>9:>10:>11:>12:>Nil)+-- >>> vconcat ((1:>2:>3:>Nil) :> (4:>5:>6:>Nil) :> (7:>8:>9:>Nil) :> (10:>11:>12:>Nil) :> Nil)+-- <1,2,3,4,5,6,7,8,9,10,11,12> vconcat :: Vec n (Vec m a) -> Vec (n * m) a vconcat Nil = Nil vconcat (x :> xs) = unsafeCoerce (vappend x (vconcat xs))@@ -198,10 +255,8 @@ -- | Split a vector of (n * m) elements into a vector of vectors with length m, -- where m is given ----- > vunconcat d4 (1:>2:>3:>4:>5:>6:>7:>8:>9:>10:>11:>12:>Nil) == ((1:>2:>3:>4:>Nil) :>--- > (5:>6:>7:>8:>Nil) :>--- > (9:>10:>11:>12:>Nil) :>--- > Nil)+-- >>> vunconcat d4 (1:>2:>3:>4:>5:>6:>7:>8:>9:>10:>11:>12:>Nil)+-- <<1,2,3,4>,<5,6,7,8>,<9,10,11,12>> vunconcat :: KnownNat n => SNat m -> Vec (n * m) a -> Vec n (Vec m a) vunconcat n xs = vunconcatU (withSNat toUNat) (toUNat n) xs @@ -213,20 +268,26 @@ {-# INLINEABLE vunconcatI #-} -- | Split a vector of (n * m) elements into a vector of vectors with length m, -- where m is determined by the context+--+-- >>> vunconcatI (1:>2:>3:>4:>5:>6:>7:>8:>9:>10:>11:>12:>Nil) :: Vec 2 (Vec 6 Int)+-- <<1,2,3,4,5,6>,<7,8,9,10,11,12>> vunconcatI :: (KnownNat n, KnownNat m) => Vec (n * m) a -> Vec n (Vec m a) vunconcatI = withSNat vunconcat {-# NOINLINE vmerge #-} -- | Merge two vectors, alternating their elements, i.e., ----- > vmerge (xn :> ... :> x2 :> x1 :> Nil) (yn :> ... :> y2 :> y1 :> Nil) == (xn :> yn :> ... :> x2 :> y2 :> x1 :> y1 :> Nil)---+-- >>> vmerge (1 :> 2 :> 3 :> 4 :> Nil) (5 :> 6 :> 7 :> 8 :> Nil)+-- <1,5,2,6,3,7,4,8> vmerge :: Vec n a -> Vec n a -> Vec (n + n) a vmerge Nil Nil = Nil vmerge (x :> xs) (y :> ys) = unsafeCoerce (x :> y :> (vmerge xs (unsafeCoerce ys))) {-# NOINLINE vreverse #-} -- | Returns the elements in a list in reverse order+--+-- >>> vreverse (1:>2:>3:>4:>Nil)+-- <4,3,2,1> vreverse :: Vec n a -> Vec n a vreverse Nil = Nil vreverse (x :> xs) = vreverse xs <: x@@ -297,7 +358,8 @@ {-# NOINLINE vzip #-} -- | 'vzip' takes two lists and returns a list of corresponding pairs. ----- > vzip (xn :> ... :> x2 :> x1 :> Nil) (yn :> ... :> y2 :> y1 :> Nil) == ((xn,yn) :> ... :> ... (x2,y2) :> (x1,y1) :> Nil)+-- >>> vzip (1:>2:>3:>4:>Nil) (4:>3:>2:>1:>Nil)+-- <(1,4),(2,3),(3,2),(4,1)> vzip :: Vec n a -> Vec n b -> Vec n (a,b) vzip Nil Nil = Nil vzip (x :> xs) (y :> ys) = (x,y) :> (vzip xs (unsafeCoerce ys))@@ -306,7 +368,8 @@ -- | 'vunzip' transforms a list of pairs into a list of first components -- and a list of second components. ----- > vunzip ((xn,yn) :> ... :> ... (x2,y2) :> (x1,y1) :> Nil) == (xn :> ... :> x2 :> x1 :> Nil, yn :> ... :> y2 :> y1 :> Nil)+-- >>> vunzip ((1,4):>(2,3):>(3,2):>(4,1):>Nil)+-- (<1,2,3,4>,<4,3,2,1>) vunzip :: Vec n (a,b) -> (Vec n a, Vec n b) vunzip Nil = (Nil,Nil) vunzip ((a,b) :> xs) = let (as,bs) = vunzip xs@@ -328,24 +391,30 @@ -- | Vector index (subscript) operator, descending from 'maxIndex', where the -- last element has subscript 0. ----- > (1:>2:>3:>4:>5:>Nil) ! 4 == 1--- > (1:>2:>3:>4:>5:>Nil) ! maxIndex == 1--- > (1:>2:>3:>4:>5:>Nil) ! 1 == 4--- > (1:>2:>3:>4:>5:>Nil) ! 14 == RUNTIME ERROR: Out of bounds+-- >>> (1:>2:>3:>4:>5:>Nil) ! 4+-- 1+-- >>> (1:>2:>3:>4:>5:>Nil) ! maxIndex+-- 1+-- >>> (1:>2:>3:>4:>5:>Nil) ! 1+-- 4+-- >>> (1:>2:>3:>4:>5:>Nil) ! 14+-- *** Exception: index out of bounds (!) :: (KnownNat n, Integral i) => Vec n a -> i -> a xs ! i = vindex_integer xs (toInteger i) {-# NOINLINE maxIndex #-} -- | Index (subscript) of the head of the 'Vec'tor ----- > maxIndex (6 :> 7 :> 8 :> Nil) == 2+-- >>> maxIndex (6 :> 7 :> 8 :> Nil)+-- 2 maxIndex :: KnownNat n => Vec n a -> Integer maxIndex = subtract 1 . vlength {-# NOINLINE vlength #-} -- | Length of a 'Vec'tor as an Integer ----- > vlength (6 :> 7 :> 8 :> Nil) == 3+-- >>> vlength (6 :> 7 :> 8 :> Nil)+-- 3 vlength :: KnownNat n => Vec n a -> Integer vlength = natVal . asNatProxy @@ -364,51 +433,85 @@ Nothing -> error "index out of bounds" {-# INLINEABLE vreplace #-}--- | Replace an element of a vector at the given index (subscript), NB: vector--- elements have a descending subscript starting from 'maxIndex' and ending at 0+-- | Replace an element of a vector at the given index (subscript). ----- > vreplace (1:>2:>3:>4:>5:>Nil) 3 7 == (1:>7:>3:>4:>5:>Nil)--- > vreplace (1:>2:>3:>4:>5:>Nil) 0 7 == (1:>2:>3:>4:>7:>Nil)--- > vreplace (1:>2:>3:>4:>5:>Nil) 9 7 == RUNTIME ERROR: Out of bounds+-- NB: vector elements have a descending subscript starting from 'maxIndex' and+-- ending at 0+--+-- >>> vreplace (1:>2:>3:>4:>5:>Nil) 3 7+-- <1,7,3,4,5>+-- >>> vreplace (1:>2:>3:>4:>5:>Nil) 0 7+-- <1,2,3,4,7>+-- >>> vreplace (1:>2:>3:>4:>5:>Nil) 9 7+-- <*** Exception: index out of bounds vreplace :: (KnownNat n, Integral i) => Vec n a -> i -> a -> Vec n a vreplace xs i y = vreplace_integer xs (toInteger i) y {-# NOINLINE vtake #-} -- | 'vtake' @n@, applied to a vector @xs@, returns the @n@-length prefix of @xs@ ----- > vtake (snat :: SNat 3) (1:>2:>3:>4:>5:>Nil) == (1:>2:>3:>Nil)--- > vtake d3 (1:>2:>3:>4:>5:>Nil) == (1:>2:>3:>Nil)--- > vtake d0 (1:>2:>Nil) == Nil--- > vtake d4 (1:>2:>Nil) == TYPE ERROR+-- >>> vtake (snat :: SNat 3) (1:>2:>3:>4:>5:>Nil)+-- <1,2,3>+-- >>> vtake d3 (1:>2:>3:>4:>5:>Nil)+-- <1,2,3>+-- >>> vtake d0 (1:>2:>Nil)+-- <>+-- >>> vtake d4 (1:>2:>Nil)+-- <interactive>+-- Couldn't match type ‘4 + n0’ with ‘2’+-- The type variable ‘n0’ is ambiguous+-- Expected type: Vec (4 + n0) a+-- Actual type: Vec (1 + 1) a+-- In the second argument of ‘vtake’, namely ‘(1 :> 2 :> Nil)’+-- In the expression: vtake d4 (1 :> 2 :> Nil)+-- In an equation for ‘it’: it = vtake d4 (1 :> 2 :> Nil) vtake :: SNat m -> Vec (m + n) a -> Vec m a vtake n = fst . vsplit n {-# INLINEABLE vtakeI #-} -- | 'vtakeI' @xs@, returns the prefix of @xs@ as demanded by the context+--+-- >>> vtakeI (1:>2:>3:>4:>5:>Nil) :: Vec 2 Int+-- <1,2> vtakeI :: KnownNat m => Vec (m + n) a -> Vec m a vtakeI = withSNat vtake {-# NOINLINE vdrop #-} -- | 'vdrop' @n xs@ returns the suffix of @xs@ after the first @n@ elements ----- > vdrop (snat :: SNat 3) (1:>2:>3:>4:>5:>Nil) == (4:>5:>Nil)--- > vdrop d3 (1:>2:>3:>4:>5:>Nil) == (4:>5:>Nil)--- > vdrop d0 (1:>2:>Nil) == (1:>2:>Nil)--- > vdrop d4 (1:>2:>Nil) == TYPE ERROR+-- >>> vdrop (snat :: SNat 3) (1:>2:>3:>4:>5:>Nil)+-- <4,5>+-- >>> vdrop d3 (1:>2:>3:>4:>5:>Nil)+-- <4,5>+-- >>> vdrop d0 (1:>2:>Nil)+-- <1,2>+-- >>> vdrop d4 (1:>2:>Nil)+-- <interactive>+-- Couldn't match expected type ‘2’ with actual type ‘4 + n0’+-- The type variable ‘n0’ is ambiguous+-- In the first argument of ‘print’, namely ‘it’+-- In a stmt of an interactive GHCi command: print it vdrop :: SNat m -> Vec (m + n) a -> Vec n a vdrop n = snd . vsplit n {-# INLINEABLE vdropI #-} -- | 'vdropI' @xs@, returns the suffix of @xs@ as demanded by the context+--+-- >>> vdropI (1:>2:>3:>4:>5:>Nil) :: Vec 2 Int+-- <4,5> vdropI :: KnownNat m => Vec (m + n) a -> Vec n a vdropI = withSNat vdrop {-# NOINLINE vexact #-}--- | 'vexact' @n xs@ returns @n@'th element of @xs@, NB: vector elements--- have a descending subscript starting from 'maxIndex' and ending at 0+-- | 'vexact' @n xs@ returns @n@'th element of @xs@ ----- > vexact (snat :: SNat 1) (1:>2:>3:>4:>5:>Nil) == 4--- > vexact d1 (1:>2:>3:>4:>5:>Nil) == 4+-- NB: vector elements have a descending subscript starting from 'maxIndex' and+-- ending at 0+--+-- >>> vexact (snat :: SNat 1) (1:>2:>3:>4:>5:>Nil)+-- 4+-- >>> vexact d1 (1:>2:>3:>4:>5:>Nil)+-- 4 vexact :: SNat m -> Vec (m + (n + 1)) a -> a vexact n xs = vhead $ snd $ vsplit n (vreverse xs) @@ -416,8 +519,10 @@ -- | 'vselect' @f s n xs@ selects @n@ elements with stepsize @s@ and -- offset @f@ from @xs@ ----- > vselect (snat :: SNat 1) (snat :: SNat 2) (snat :: SNat 3) (1:>2:>3:>4:>5:>6:>7:>8:>Nil) == (2:>4:>6:>Nil)--- > vselect d1 d2 d3 (1:>2:>3:>4:>5:>6:>7:>8:>Nil) == (2:>4:>6:>Nil)+-- >>> vselect (snat :: SNat 1) (snat :: SNat 2) (snat :: SNat 3) (1:>2:>3:>4:>5:>6:>7:>8:>Nil)+-- <2,4,6>+-- >>> vselect d1 d2 d3 (1:>2:>3:>4:>5:>6:>7:>8:>Nil)+-- <2,4,6> vselect :: ((f + (s * n) + 1) <= i) => SNat f -> SNat s@@ -433,6 +538,9 @@ {-# NOINLINE vselectI #-} -- | 'vselectI' @f s xs@ selects as many elements as demanded by the context -- with stepsize @s@ and offset @f@ from @xs@+--+-- >>> vselectI d1 d2 (1:>2:>3:>4:>5:>6:>7:>8:>Nil) :: Vec 2 Int+-- <2,4> vselectI :: ((f + (s * n) + 1) <= i, KnownNat (n + 1)) => SNat f -> SNat s@@ -443,8 +551,10 @@ {-# NOINLINE vcopy #-} -- | 'vcopy' @n a@ returns a vector that has @n@ copies of @a@ ----- > vcopy (snat :: SNat 3) 6 == (6:>6:>6:>Nil)--- > vcopy d3 6 == (6:>6:>6:>Nil)+-- >>> vcopy (snat :: SNat 3) 6+-- <6,6,6>+-- >>> vcopy d3 6+-- <6,6,6> vcopy :: SNat n -> a -> Vec n a vcopy n a = vreplicateU (toUNat n) a @@ -455,6 +565,9 @@ {-# INLINEABLE vcopyI #-} -- | 'vcopyI' @a@ creates a vector with as many copies of @a@ as demanded by the -- context+--+-- >>> vcopy 6 :: Vec 5 Int+-- <6,6,6,6,6> vcopyI :: KnownNat n => a -> Vec n a vcopyI = withSNat vcopy @@ -474,6 +587,8 @@ {-# INLINEABLE viterateI #-} -- | 'viterate' @f x@ returns a vector starting with @x@ followed by @n@ -- repeated applications of @f@ to @x@, where @n@ is determined by the context+--+-- > viterateI f x :: Vec 3 a == (x :> f x :> f (f x) :> Nil) viterateI :: KnownNat n => (a -> a) -> a -> Vec n a viterateI = withSNat viterate @@ -489,17 +604,27 @@ {-# INLINEABLE vgenerateI #-} -- | 'vgenerate' @f x@ returns a vector with @n@ repeated applications of @f@ -- to @x@, where @n@ is determined by the context+--+-- > vgenerateI f x :: Vec 3 a == (f x :> f (f x) :> f (f (f x)) :> Nil) vgenerateI :: KnownNat n => (a -> a) -> a -> Vec n a vgenerateI = withSNat vgenerate {-# INLINEABLE toList #-} -- | Convert a vector to a list+--+-- >>> toList (1:>2:>3:>Nil)+-- [1,2,3] toList :: Vec n a -> [a] toList = vfoldr (:) [] -- | Create a vector literal from a list literal -- -- > $(v [1::Signed 8,2,3,4,5]) == (8:>2:>3:>4:>5:>Nil) :: Vec 5 (Signed 8)+--+-- >>> [1 :: Signed 8,2,3,4,5]+-- [1,2,3,4,5]+-- >>> $(v [1::Signed 8,2,3,4,5])+-- <1,2,3,4,5> v :: Lift a => [a] -> ExpQ v [] = [| Nil |] v (x:xs) = [| x :> $(v xs) |]@@ -526,8 +651,8 @@ -- -- Will not terminate because 'vzipWith' is too strict in its left argument: ----- > *Main> sortV (4 :> 1 :> 2 :> 3 :> Nil)--- > <*** Exception: <<loop>>+-- >>> sortV (4 :> 1 :> 2 :> 3 :> Nil)+-- <*** Exception: <<loop>> -- -- In this case, adding 'lazyV' on 'vzipWith's left argument: --@@ -539,8 +664,8 @@ -- -- Results in a successful computation: ----- > *Main> sortVL (4 :> 1 :> 2 :> 3 :> Nil)--- > <1,2,3,4>+-- >>> sortVL (4 :> 1 :> 2 :> 3 :> Nil)+-- <1,2,3,4> lazyV :: KnownNat n => Vec n a -> Vec n a@@ -549,3 +674,11 @@ lazyV' :: Vec n a -> Vec n a -> Vec n a lazyV' Nil _ = Nil lazyV' (_ :> xs) ys = vhead ys :> lazyV' xs (vtail ys)++{-# NOINLINE vhead' #-}+-- | Same as 'vhead', but with a "@(1 <= n)@" constraint and "@Vec n a@" argument,+-- instead of a "@Vec (n + 1) a@" argument+vhead' :: (1 <= n)+ => Vec n a+ -> a+vhead' (x :> _) = x
+ src/CLaSH/Tutorial.hs view
@@ -0,0 +1,860 @@+{-# OPTIONS_GHC -fno-warn-unused-imports #-}++{-|+Copyright : © Christiaan Baaij, 2014+Licence : Creative Commons 4.0 (CC BY-NC 4.0) (http://creativecommons.org/licenses/by-nc/4.0/)+-}+module CLaSH.Tutorial (+ -- * Introduction+ -- $introduction++ -- * Installation+ -- $installation++ -- * Working with this tutorial+ -- $working++ -- * Your first circuit+ -- $mac_example++ -- *** Sequential circuit+ -- $mac2++ -- *** Creating VHDL+ -- $mac3++ -- *** Circuit testbench+ -- $mac4++ -- *** Alternative specifications+ -- $mac5++ -- * Higher-order functions+ -- $higher_order++ -- * Composition of sequential circuits+ -- $composition_sequential++ -- * Conclusion+ -- $conclusion++ -- * Troubleshooting+ -- $errorsandsolutions++ -- * Unsupported Haskell features+ -- $unsupported+ )+where++import CLaSH.Prelude+import CLaSH.Prelude.Explicit++{- $introduction+CλaSH (pronounced ‘clash’) is a functional hardware description language that+borrows both its syntax and semantics from the functional programming language+Haskell. The merits of using a functional language to describe hardware comes+from the fact that combinational circuits can be directly modeled as+mathematical functions and that functional languages lend themselves very well+at describing and (de-)composing mathematical functions.++Although we say that CλaSH borrows the semantics of Haskell, that statement+should be taken with a grain of salt. What we mean to say is that the CλaSH+compiler views a circuit description as /structural/ description. This means,+in an academic handwavy way, that every function denotes a component and every+function application denotes an instantiation of said component. Now, this has+consequences on how we view /recursive/ functions: structurally, a recursive+function would denote an /infinitely/ deep / structured component, something+that cannot be turned into an actual circuit (See also <#unsupported Unsupported Haskell features>).+Of course there are variants of recursion that cab be completely unfolded at+compile-time with a finite amount of steps and hence could be converted to a+realisable circuit. Sadly, this last feature is missing in the current version+of the compiler.++On the other hand, Haskell's by-default non-strict evaluation corresponds very+well to the simulation of the feedback loops which are ubiquitous in digital+circuits. That is, when we take our structural view to circuit descriptions,+value-recursion corresponds directly to a feedback loop:++@+counter = s+ where+ s = register 0 (s + 1)+@++Over time, you will get a better feeling of what exact the consequences are of+taking a \structural\ view on circuit descriptions. What is always important to+remember is that every applied functions results in an instantiated component,+and also that the compiler will never infer / invent more logic than what is+specified in the circuit description.++With that out of the way, let us continue with installing CλaSH and building+our first circuit.+-}++{- $installation+The CλaSH compiler and Prelude library for circuit design only work with the+<http://haskell.org/ghc GHC> Haskell compiler version 7.8.1 and up.++ (1) Install __GHC (version 7.8.1 or higher)__++ * Download and install <http://www.haskell.org/ghc/download GHC for your platform>.+ Unix user can use @./configure prefix=\<LOCATION\>@ to set the installation+ location.++ * Make sure that the @bin@ directory of __GHC__ is in your @PATH@.++ (2) Install __Cabal__++ * Windows:++ * Download the binary for <http://www.haskell.org/cabal/download.html cabal-install>+ * Put the binary in a location mentioned in your @PATH@++ * Unix:++ * Download the sources for <http://hackage.haskell.org/package/cabal-install cabal-install>+ * Unpack (@tar xf@) the archive and @cd@ to the directory+ * Run @sh bootstrap.sh@+ * Follow the instructions to add @cabal@ to your @PATH@++ * Run @cabal update@++ (3) Install __CλaSH__++ * Run @cabal install clash-ghc@++ (4) Verify that everything is working by:++ * Downloading the <https://raw.github.com/christiaanb/clash2/master/examples/FIR.hs Fir.hs> example+ * Run @clash --interactive FIR.hs@+ * Execute, in the interpreter, the @:vhdl@ command.+ * Exit the interpreter using @:q@+ * Examin the VHDL code in the @vhdl@ directory++-}++{- $working+This tutorial can be followed best whilst having the CλaSH interpreter running+at the same time. If you followed the installation instructions, you already+know how to start the CλaSH compiler in interpretive mode:++@+clash --interactive+@++For those familiar with Haskell/GHC, this is indeed just @GHCi@, with one added+command (@:vhdl@). You can load files into the interpreter using the+@:l \<FILENAME\>@ command. Now, depending on your choice in editor, the following+@edit-load-run@ cycle probably work best for you:++ * __Commandline (e.g. emacs, vim):__++ * You can run system commands using @:!@, for example @:! touch \<FILENAME\>@+ * Set the /editor/ mode to your favourite editor using: @:set editor \<EDITOR\>@+ * You can load files using @:l@ as noted above.+ * You can go into /editor/ mode using: @:e@+ * Leave the editor mode by quitting the editor (e.g. @:wq@ in @vim@)++ * __GUI (e.g. SublimeText, Notepad++):__++ * Just create new files in your editor.+ * Load the files using @:l@ as noted above.+ * Once a file has been edited and saved, type @:r@ to reload the files in+ the interpreter++You are of course free to deviate from these suggestions as you see fit :-) It+is just recommended that you have the CλaSH interpreter open during this+tutorial.+-}++{- $mac_example+The very first circuit that we will build is the \"classic\" multiply-and-accumulate+(MAC) circuit. This circuit is as simple as it sounds, it multiplies its inputs+and accumulates them. Before we describe any logic, we must first create the+file we will be working on and input some preliminaries:++ * Create the file:++ @+ MAC.hs+ @++ * Write on the first line the module header:++ @+ module MAC where+ @++ Module names must always start with a __C__apital letter. Also make sure that+ the file name corresponds to the module name.++ * Add the import statement for the CλaSH prelude library:++ @+ import CLaSH.Prelude+ @++ This imports all the necessary functions and datatypes for circuit description.++We can now finally start describing the logic of our circuit, starting with just+the multiplication and addition:++@+ma acc (x,y) = acc + x * y+@++If you followed the instructions of running the interpreter side-by-side, you+can already test this function:++>>> ma 4 (8,9)+76+>>> ma 2 (3,4)+14++We can also examine the inferred type of @ma@ in the interpreter:++>>> :t ma+ma :: Num a => a -> a -> a++Talking about /types/ also brings us to one of the most important parts of this+tutorial: /types/ and /synchronous sequential logic/. Especially how we can+always determine, through the types of a specification, if it describes+combinational logic or (synchronous) sequential logic. We do this by examining+the type of one of the sequential primitives, the @register@ function:++@+register :: a -> Signal a -> Signal a+regiser i s = ...+@++Where we see that the second argument and the result are not just of the+/polymorphic/ @a@ type, but of the type: @'Signal' a@. All (synchronous)+sequential circuits work on values of type @'Signal' a@. Combinational+circuits always work on values of, well, not of type @'Signal' a@. A 'Signal'+is an (infinite) list of samples, where the samples correspond to the values+of the 'Signal' at discrete, consecutive, ticks of the /clock/. All (sequential)+components in the circuit are synchronized to this global /clock/. For the+rest of this tutorial, and probably at any moment where you will be working with+CλaSH, you should probably not actively think about 'Signal's as infinite lists+of samples, but just as values that are manipulated by sequential circuits. To+make this even easier, it actually not possible to manipulate the underlying+representation directly: you can only modify 'Signal' values through a set of+primitives such as the 'register' function above.++Now, let us get back to the functionality of the 'register' function: it is+a simple @latch@ that only changes state at the tick of the global /clock/, and+it has an initial value @a@ which is its output at time 0. We can further+examine the 'register' function by taking a look at the first 4 samples of the+'register' functions applied to a constant signal with the value 8:++>>> sampleN 4 (register 0 (signal 8))+[0,8,8,8]++Where we see that the initial value of the signal is the specified 0 value,+followed by 8's.+-}++{- $mac2+The 'register' function is our primary sequential building block to capture+/state/. It is used internally by one of the "CLaSH.Prelude" function that we+will use to describe our MAC circuit. Note that the following paragraphs will+only show one of many ways to specify a sequential circuit, at the section we+will show a couple more.++A principled way to describe a sequential circuit is to use one of the classic+machine models, within the CλaSH prelude library offer standard function to+support the <http://en.wikipedia.org/wiki/Mealy_machine Mealy machine>.+To improve sharing, we will combine the transition function and output function+into one. This gives rise to the following Mealy specification of the MAC+circuit:++@+macT acc (x,y) = (acc',o)+ where+ acc' = ma acc (x,y)+ o = acc+@++Note that the @where@ clause and explicit tuple are just for demonstrative+purposes, without loss of sharing we could've also written:++@+macT acc inp = (ma acc inp,acc)+@++Going back to the original specification we note the following:++ * 'acc' is the current /state/ of the circuit.+ * '(x,y)' is its input.+ * 'acc'' is the updated, or next, /state/.+ * 'o' is the output.++When we examine the type of 'macT' we see that is still completely combinational:++>>> :t macT+macT :: Num t => t -> (t, t) -> (t, t)++The "CLaSH.Prelude" library contains a function that creates a sequential+circuit from a combinational circuit that has the same Mealy machine type /+shape of 'macT':++@+(\<^\>) :: (Pack i, Pack o)+ => (s -> i -> (s,o))+ -> s+ -> (SignalP i -> SignalP o)+f \<^\> initS = ...+@++The complete sequential MAC circuit can now be specified as:++@+mac = macT \<^\> 0+@++Where the LHS of '<^>' is our 'macT' function, and the RHS is the initial state,+in this case 0. We can see it is functioning correctly in our interpreter:++>>> take 4 $ simulateP mac [(1::Int,1),(2,2),(3,3),(4,4)] :: [Int]+[0,1,5,14]++Where we simulate our sequential circuit over a list of input samples and take+the first 4 output samples. We have now completed our first sequential circuit+and have made an initial confirmation that it is working as expected.++The observant reader already saw that the '<^>' operator does not create a+function that works on 'Signal's, but on on 'SignalP's. Indeed, when we look at+the type of our 'mac' circuit:++>>> :t mac+mac :: (Pack o, Num o) => (Signal o, Signal o) -> SignalP o++We see that our 'mac' function work on a two-tuple of 'Signal's and not on a+'Signal' of a two-tuple. Indeed, the CλaSH prelude library defines that:++@+type instance SignalP (a,b) = (Signal a, Signal b)+@++'SignalP' is an <http://www.haskell.org/ghc/docs/latest/html/users_guide/type-families.html#assoc-decl associcated type family>+belonging to the 'Pack' <http://en.wikipedia.org/wiki/Type_class type class>,+which, together with 'pack' and 'unpack' defines the isomorphism between a+product type of 'Signal's and a 'Signal' of a product type. That is, while+@(Signal a, Signal b)@ and @Signal (a,b)@ are not equal, they are /isomorphic/+and can be converted from on to the other using 'pack' and 'unpack'. Instances+of this 'Pack' type-class are defined as /isomorphisms/ for:++ * All tuples until and including 8-tuples+ * The 'Vec'tor type++But they are defined as /identities/ for:++ * All elementary / primitive types such as: 'Bit', 'Bool', @'Signed' n@, etc.++That is:++@+instance Pack Bool where+ type SignalP Bool = Signal Bool+ pack :: SignalP Bool -> Signal Bool+ pack = 'id'+ unpack :: Signal Bool -> SignalP Bool+ unpack = 'id'+@++We will see later why this 'Pack' type class is so convenient, for now, you just+have to remember that it exists. And more importantly, that you understand that+a product type of 'Signal's is not equal to a 'Signal' of a product type, but+that the functions of the 'Pack' type class allow easy conversion between the+two.+-}++{- $mac3+We are now almost at the point that we can create actual hardware, in the form+of a <http://en.wikipedia.org/wiki/VHDL VHDL> netlist, from our sequential+circuit specification. The first thing we have to do is create a function+called 'topEntity' and ensure that it has a __monomorphic__ type. In our case+that means that we have to give it an explicit type annotation. It might now+always be needed, you can always check the type with the @:t@ command and see+if the function is monomorphic:++@+topEntity :: (Signal (Signed 9),Signal (Signed 9)) -> Signal (Signed 9)+topEntity = mac+@++Which makes our circuit work on 9-bit signed integers. Including the above+definition, our complete @MAC.hs@ should now have the following content:++@+module MAC where++import CLaSH.Prelude++ma acc (x,y) = acc + x * y++macT acc (x,y) = (acc',o)+ where+ acc' = ma acc (x,y)+ o = acc++mac = macT \<^\> 0++topEntity :: (Signal (Signed 9),Signal (Signed 9)) -> Signal (Signed 9)+topEntity = mac+@++The 'topEntity' function is the starting point for the CλaSH compiler to+transform your circuit description into a VHDL netlist. It must meet the+following restrictions in order for the CλaSH compiler to work:++ * It must be completely monomorphic+ * It must be completely first-order++Our 'topEntity' meets those restrictions, and so we can convert it successfully+to VHDL by executing the @:vhdl@ command in the interpreter. This will create+a directory called 'vhdl', which contains a directory called @MAC@, which+ultimately contains all the generated VHDL files. You can now load these files+(except @testbench.vhdl@) into your favourite VHDL synthesis tool, marking+@topEntity_0.vhdl@ as the file containing the top level entity.+-}++{- $mac4+There are multiple reasons as to why might you want to create a so-called+/testbench/ for the VHDL:++ * You want to compare post-synthesis / post-place&route behaviour to that of+ the behaviour of the original VHDL.+ * Need representative stimuli for your dynamic power calculations+ * Verify that the VHDL output of the CλaSH compiler has the same behaviour as+ the Haskell / CλaSH specification.++For these purposes, you can have CλaSH compiler generate a @testbench.vhdl@+file which contains a stimulus generator and an expected output verifier. The+CλaSH compiler looks for the following functions to generate these to aspects:++ 1. @testInput@ for the stimulus generator.+ 2. @expectedOutput@ for the output verification.++Given a 'topEntity' with the type:++@+topEntity :: SignalP a -> SignalP b+@++Where @a@ and @b@ are placeholders for monomorphics types: the 'topEntity' is+not allowed to be polymorphic. So given the above type for the 'topEntity', the+type of 'testInput' should be:++@+testInput :: Signal a+@++And the type of 'expectedOutput' should be:++@+expectedOutput :: Signal b -> Signal Bool+@++Where the 'expectedOutput' function should assert to 'True' once it has verified+all expected values. The "CLaSH.Prelude" module contains two standard functions+to serve the above purpose, but a user is free to use any CλaSH specification+to describe these two functions. For this tutorial we will be using the+functions specified in the "CLaSH.Prelude" module, which are 'stimuliGenerator'+and 'outputVerifier':++@+testInput :: Signal (Signed 9,Signed 9)+testInput = stimuliGenerator $(v [(1,1) :: (Signed 9,Signed 9),(2,2),(3,3),(4,4)])++expectedOutput :: Signal (Signed 9) -> Signal Bool+expectedOutput = outputVerifier $(v [0 :: Signed 9,1,5,14])+@++This will create a stimulus generator that creates the same inputs as we used+earlier for the simulation of the circuit, and creates an output verifier that+compares against the results we got from our earlier simulation. We can even+simulate the behaviour of the /testbench/:++>>> sampleN 7 $ expectedOutput (topEntity $ unpack testInput)+[False,False,False,False,+expected value: 14, not equal to actual value: 30+True,+expected value: 14, not equal to actual value: 46+True,+expected value: 14, not equal to actual value: 62+True]++We can see that for the first 4 samples, everything is working as expected,+after which warnings are being reported. The reason is that 'stimuliGenerator'+will keep on producing the last sample, (4,4), while the 'outputVerifier' will+keep on expecting the last sample, 14. In the VHDL testbench these errors won't+show, as the the global clock will be stopped after 4 ticks.++You should now again run @:vhdl@ in the interpreter; this time the compiler+will take a bit longer to generate all the circuits. After it is finished you+can load all the files in your favourite VHDL simulation tool that has support+for VHDL-2008. VHDL-2008 support is required because the output verifier will+use the VHDL-2008-only @to_string@ function. Once all files are loaded into+the VHDL simulator, run the simulation on the @testbench@ entity. On questasim /+modelsim: doing a @run -all@ will finish once the output verifier will assert+its output to @true@. The generated testbench, modulo the clock signal+generator(s), is completely synthesizable. This means that if you want to test+your circuit on an FPGA, you will only have to replace the clock signal+generator(s) by actual clock sources, such as an onboard PLL.++This concludes the main part of this section on \"Your first circuit\", read on+for alternative specifications for the same 'mac' circuit, or just skip to the+next section where we will describe another DSP classic: an FIR filter structure.+-}++{- $mac5+* __'Num' instance__:++ @'Signal' a@ is also also considered a 'Num'eric type as long as @a@ is also.+ This means that we can also use the standard numeric operators, such as ('*')+ and ('+'), directly on signals. An alternative specification of the 'mac'+ circuit will also use the 'register' function directly:++ @+ macN (x,y) = acc+ where+ acc = register 0 (acc + x * y)+ @++* __'Applicative' interface__:++ We can also mix the combinational 'ma' function, with the sequential+ 'register' function, by lifting the 'ma' function to the sequential 'Signal'+ domain using the operators of the 'Applicative' type class:++ @+ macA (x,y) = acc+ where+ acc = register 0 acc'+ acc' = ma \<$\> acc \<*\> pack (x,y)+ @++* __<http://hackage.haskell.org/package/mtl/docs/Control-Monad-State-Lazy.html#t:State State> Monad__++ We can also implement the original 'macT' function as a <http://hackage.haskell.org/package/mtl/docs/Control-Monad-State-Lazy.html#t:State State>+ monadic computation. First we must an extra import statement, right after+ the import of "CLaSH.Prelude":++ @+ import Control.Monad.State+ @++ We can then implement macT as follows:++ @+ macTS (x,y) = do+ acc <- get+ put (acc + x * y)+ return acc+ @++ We can use the '<^>' operator again, although we will have to change+ position of the arguments and result:++ @+ asStateM f i = g \<^\> i+ where+ g s x = let (o,s') = runState (f x) s+ in (s',o)+ @++ We can then create the complete 'mac' circuit as:++ @+ macS = asStateM macTS 0+ @+-}++{- $higher_order+An FIR filter is defined as: the dot-product of a set of filter coefficients and+a window over the input, where the size of the window matches the number+of coefficients.++@+dotp as bs = vfoldl (+) 0 (vzipWith (*) as bs)++fir coeffs x_t = y_t+ where+ y_t = dotp coeffs xs+ xs = window x_t++topEntity :: Signal (Signed 16) -> Signal (Signed 16)+topEntity = fir $(v [0::Signal (Signed 16),1,2,3])+@++Here we can see that, although the CλaSH compiler does not support recursion,+many of the regular patterns that we often encounter in circuit design are+already captured by the higher-order functions that are present for the 'Vec'tor+type.+-}++{- $composition_sequential++First we define some types:++@+module CalculatorTypes where++import CLaSH.Prelude++type Word = Signed 4+data OPC a = ADD | MUL | Imm a | Pop | Push++deriveLift ''OPC+@++Now we define the actual calculator:++@+module Calculator where++import CLaSH.Prelude+import CalculatorTypes++(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d+(f .: g) a b = f (g a b)++infixr 9 .:++alu :: Num a => OPC a -> a -> a -> Maybe a+alu ADD = Just .: (+)+alu MUL = Just .: (*)+alu (Imm i) = const . const (Just i)+alu _ = const . const Nothing++pu :: (Num a, Num b)+ => (OPC a -> a -> a -> Maybe a)+ -> (a, a, b) -- Current state+ -> (a, OPC a) -- Input+ -> ( (a, a, b) -- New state+ , (b, Maybe a) -- Output+ )+pu alu (op1,op2,cnt) (dmem,Pop) = ((dmem,op1,cnt-1),(cnt,Nothing))+pu alu (op1,op2,cnt) (dmem,Push) = ((op1,op2,cnt+1) ,(cnt,Nothing))+pu alu (op1,op2,cnt) (dmem,opc) = ((op1,op2,cnt) ,(cnt,alu opc op1 op2))++datamem :: (KnownNat n, Integral i)+ => Vec n a -- Current state+ -> (i, Maybe a) -- Input+ -> (Vec n a, a) -- (New state, Output)+datamem mem (addr,Nothing) = (mem ,mem ! addr)+datamem mem (addr,Just val) = (vreplace mem addr val,mem ! addr)++topEntity :: Signal (OPC Word) -> Signal (Maybe Word)+topEntity i = val+ where+ (addr,val) = (pu alu \<^\> (0,0,0 :: Unsigned 3)) (mem,i)+ mem = (datamem \<^\> initMem) (addr,val)+ initMem = vcopy d8 0+@++Here we can finally see the advantage of having the '<^>' return a function+of type @'SignalP' i -> 'SignalP' o@:++ * We can use normal pattern matching to get parts of the result, and,+ * We can use normal tuple-constructors to build the input values for the+ circuits.+-}++{- $conclusion+For now, this is the end of this tutorial. We will be adding updates over time,+so check back from time to time. For now we recommend that you continue with+exploring the "CLaSH.Prelude" module however, and like that hopefully get a feel+of what CλaSH is capable of.+-}++{- $errorsandsolutions+A list often encountered errors and their solutions:++* __Type error: Couldn't match expected type ‘Signal (a,b)’ with actual type__+ __‘(Signal a, Signal b)’__:++ Signals of product types and product types (to which tuples belong) of+ signals are __isomorphic__ due to synchronisity principle, but are not+ (structurally) equal. Use the 'pack' function to convert from a product type+ to the signal type. So if your code which gives the error looks like:++ @+ ... = f a b (c,d)+ @++ add the 'pack' function like so:++ @+ ... = f a b (pack (c,d))+ @++ Product types supported by 'pack' are:++ * All tuples until and including 8-tuples+ * The 'Vec'tor type++ NB: Use 'cpack' when you are using explicitly clocked 'CSignal's++* __Type error: Couldn't match expected type ‘(Signal a, Signal b)’ with__+ __ actual type ‘Signal (a,b)’__:++ Product types (to which tuples belong) of signals and signals of product+ types are __isomorphic__ due to synchronisity principle, but are not+ (structurally) equal. Use the 'unpack' function to convert from a signal+ type to the product type. So if your code which gives the error looks like:++ @+ (c,d) = f a b+ @++ add the 'unpack' function like so:++ @+ (c,d) = unpack (f a b)+ @++ Product types supported by 'unpack' are:++ * All tuples until and including 8-tuples+ * The 'Vec'tor type++ NB: Use 'cunpack' when you are using explicitly clocked 'CSignal's++* __CLaSH.Normalize(94): Expr belonging to bndr: \<FUNCTION\> remains__+ __recursive after normalization__:++ * If you actually wrote a recursive function, rewrite it to a non-recursive+ one :-)++ * You defined a recursively defined value, but left it polymorphic:++ @+ topEntity x y = acc+ where+ acc = register 3 (x*y + acc)+ @++ The above function, works for any number-like type. This means that @acc@ is+ a recursively defined __polymorphic__ value. Adding a monomorphic type+ annotation makes the error go away.++ @+ topEntity :: Signal (Signed 8) -> Signal (Signed 8) -> Signal (Signed 8)+ topEntity x y = acc+ where+ acc = register 3 (x*y + acc)+ @++* __CLaSH.Normalize.Transformations(155): InlineNonRep: \<FUNCTION\> already__+ __inlined 100 times in:\<FUNCTION\>, \<TYPE\>__:++ You left the @topEntity@ function polymorphic or higher-order: use+ @:t topEntity@ to check if the type is indeed polymorphic or higher-order.+ If it is, add a monomorphic type signature, and / or supply higher-order+ arguments.++* __Can't make testbench for: \<LONG_VERBATIM_COMPONENT_DESCRIPTION\>__:++ * Don't worry, it's actually only a warning.++ * The @topEntity@ function does __not__ have exactly 1 argument. If your+ @topEntity@ has no arguments, you're out of luck for now. If it has+ multiple arguments, consider bundling them in a tuple.++* __\<*** Exception: \<\<loop\>\>__++ You are using value-recursion, but one of the 'Vec'tor functions that you+ are using is too /strict/ in one of the recursive arguments. For example:++ @+ -- Bubble sort for 1 iteration+ sortV xs = vmap fst sorted <: (snd (vlast sorted))+ where+ lefts = vhead xs :> vmap snd (vinit sorted)+ rights = vtail xs+ sorted = vzipWith compareSwapL lefts rights++ -- Compare and swap+ compareSwapL a b = if a < b then (a,b)+ else (b,a)+ @++ Will not terminate because 'vzipWith' is too strict in its left argument:++ >>> sortV (4 :> 1 :> 2 :> 3 :> Nil)+ <*** Exception: <<loop>>++ In this case, adding 'lazyV' on 'vzipWith's left argument:++ @+ sortVL xs = vmap fst sorted <: (snd (vlast sorted))+ where+ lefts = vhead xs :> vmap snd (vinit sorted)+ rights = vtail xs+ sorted = vzipWith compareSwapL (lazyV lefts) rights+ @++ Results in a successful computation:++ >>> sortVL (4 :> 1 :> 2 :> 3 :> Nil)+ <1,2,3,4>+-}++{- $unsupported #unsupported#+Here is a list of Haskell features which the CλaSH compiler cannot synthesize+to VHDL (for now):++ [@Recursive functions@]++ Although it seems rather bad that a compiler for a+ functional language does not support recursion, this bug/feature of the+ CλaSH compiler is amortized by the builtin knowledge of all the functions+ listed in "CLaSH.Sized.Vector". And as you saw in this tutorial, the+ higher-order functions of "CLaSH.Sized.Vector" can cope with many of the+ recursive design patterns found in circuit design.++ Also note that although recursive functions are not supported, recursively+ (tying-the-knot) defined values are supported (as long as these values do+ not have a function type). An example that uses recursively defined values+ is the following function that performs one iteration of bubble sort:++ @+ sortVL xs = vmap fst sorted <: (snd (vlast sorted))+ where+ lefts = vhead xs :> vmap snd (vinit sorted)+ rights = vtail xs+ sorted = vzipWith compareSwapL (lazyV lefts) rights+ @++ Where we can clearly see that 'lefts' and 'sorted' are defined in terms of+ each other.++ [@Recursive datatypes@]++ The CλaSH compiler needs to be able to determine a bit-size for any value+ that will be represented in the eventual circuit. More specifically, we need+ to know the maximum number of bits needed to represent a value. While this+ is trivial for values of the elementary types, sum types, and product types,+ putting a fixed upper bound on recursive types is not (always) feasible.+ This means that the ubiquitous list type is unsupported! The only recursive+ type that is currently supported by the CλaSH compiler is the 'Vec'tor type,+ for which the compiler has hard-coded knowledge.++ For \"easy\" 'Vec'tor literals you should use Template Haskell splices and+ the 'v' /meta/-function that as we have seen earlier in this tutorial.++ [@GADT pattern matching@]++ While pattern matching for regular ADTs is supported, pattern matching for+ GADTs is __not__. The 'Vec'tor type, which is also a GADT, is __no__+ exception! You can use the extraction and indexing functions of+ "CLaSH.Sized.Vector" to get access to individual ranges / elements of a+ 'Vec'tor.++ [@Floating point types@]++ There is no support for the 'Float' and 'Double' type, if you need numbers+ with a /fractional/ part you can use the 'Fixed' point type.+-}