clash-prelude 0.5 → 0.5.0.1
raw patch · 9 files changed
+383/−80 lines, 9 files
Files
- README.md +1/−1
- clash-prelude.cabal +2/−1
- src/CLaSH/Bit.hs +2/−2
- src/CLaSH/Prelude.hs +17/−9
- src/CLaSH/Prelude/Explicit.hs +16/−8
- src/CLaSH/Signal/Explicit.hs +7/−0
- src/CLaSH/Signal/Implicit.hs +8/−1
- src/CLaSH/Sized/Vector.hs +4/−4
- src/CLaSH/Tutorial.hs +326/−54
README.md view
@@ -1,2 +1,2 @@ = WARNING =-Only works with GHC-7.8 (http://www.haskell.org/ghc/dist/7.8.1-rc2/)!+Only works with GHC-7.8.* (http://www.haskell.org/ghc/download_ghc_7_8_2)!
clash-prelude.cabal view
@@ -1,5 +1,5 @@ Name: clash-prelude-Version: 0.5+Version: 0.5.0.1 Synopsis: CAES Language for Synchronous Hardware - Prelude library Description: CλaSH (pronounced ‘clash’) is a functional hardware description language that@@ -16,6 +16,7 @@ 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 .
src/CLaSH/Bit.hs view
@@ -11,8 +11,8 @@ import Language.Haskell.TH.Lift -- | Two-level logic-data Bit = H -- ^ High- | L -- ^ Low+data Bit = L -- ^ Low+ | H -- ^ High instance Eq Bit where (==) = eqBit
src/CLaSH/Prelude.hs view
@@ -180,10 +180,12 @@ registerP i = unpack Prelude.. register i Prelude.. pack {-# NOINLINE blockRam #-}--- | Create a blockRAM with space for @n@ elements+-- | Create a blockRAM with space for @n@ elements. --+-- NB: Read value is delayed by 1 cycle+-- -- > bram40 :: Signal (Unsigned 6) -> Signal (Unsigned 6) -> Signal Bool -> Signal a -> Signal a--- > bram40 = blockRam d50+-- > bram40 = blockRam d40 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@@@ -204,11 +206,13 @@ | otherwise = ram o' = ram ! r -{-# DEPRECATED blockRamC "'Comp' is deprecated, use 'blockRam' instead" #-}+{-# DEPRECATED blockRamC "'Comp' is deprecated and will be removed in version 0.6, use 'blockRam' instead" #-} -- | Create a blockRAM with space for @n@ elements --+-- NB: Read value is delayed by 1 cycle+-- -- > bramC40 :: Comp (Unsigned 6, Unsigned 6, Bool, a) a--- > bramC40 = blockRamC d50+-- > bramC40 = blockRamC d40 blockRamC :: (KnownNat n, KnownNat m, Pack a, Default a) => SNat n -- ^ Size @n@ of the blockram -> Comp (Unsigned m, Unsigned m, Bool, a) a@@ -217,6 +221,8 @@ {-# INLINABLE blockRamPow2 #-} -- | Create a blockRAM with space for 2^@n@ elements --+-- NB: Read value is delayed by 1 cycle+-- -- > bram32 :: Signal (Unsigned 5) -> Signal (Unsigned 5) -> Signal Bool -> Signal a -> Signal a -- > bram32 = blockRamPow2 d32 blockRamPow2 :: (KnownNat n, KnownNat (2^n), Pack a, Default a)@@ -228,9 +234,11 @@ -> Signal a -- ^ Value of the 'blockRAM' at address @r@ from the previous clock cycle blockRamPow2 = blockRam -{-# DEPRECATED blockRamPow2C "'Comp' is deprecated, use 'blockRamPow2' instead" #-}+{-# DEPRECATED blockRamPow2C "'Comp' is deprecated and will be removed in version 0.6, use 'blockRamPow2' instead" #-} -- | Create a blockRAM with space for 2^@n@ elements --+-- NB: Read value is delayed by 1 cycle+-- -- > bramC32 :: Comp (Unsigned 5, Unsigned 5, Bool, a) a -- > bramC32 = blockRamPow2C d32 blockRamPow2C :: (KnownNat n, KnownNat (2^n), Pack a, Default a)@@ -238,7 +246,7 @@ -> 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" #-}+{-# DEPRECATED Comp "Will be removed in version 0.6. Use 'Applicative' interface and ('<^>') instead" #-} -- | 'Comp'onent: an 'Arrow' interface to synchronous sequential functions newtype Comp a b = C { asFunction :: Signal a -> Signal b } @@ -258,7 +266,7 @@ simpleLoop g b = let ~(c,d) = g (b,d) in c -{-# DEPRECATED registerC "'Comp' is deprecated, use 'register' instead" #-}+{-# DEPRECATED registerC "'Comp' is deprecated and will be removed in version 0.6, use 'register' instead" #-} -- | Create a 'register' 'Comp'onent -- -- > rC :: Comp (Int,Int) (Int,Int)@@ -269,7 +277,7 @@ registerC :: a -> Comp a a registerC = C Prelude.. register -{-# DEPRECATED simulateC "'Comp' is deprecated, use 'simulate' instead" #-}+{-# DEPRECATED simulateC "'Comp' is deprecated and will be removed in version 0.6, use 'simulate' instead" #-} -- | Simulate a 'Comp'onent given a list of samples -- -- >>> simulateC (registerC 8) [1, 2, 3, ...@@ -277,7 +285,7 @@ simulateC :: Comp a b -> [a] -> [b] simulateC f = simulate (asFunction f) -{-# DEPRECATED (^^^) "Use 'Applicative' interface and ('<^>') instead" #-}+{-# DEPRECATED (^^^) "Will be removed in version 0.6. Use 'Applicative' interface and ('<^>') instead" #-} {-# INLINABLE (^^^) #-} -- | Create a synchronous 'Comp'onent from a combinational function describing -- a mealy machine
src/CLaSH/Prelude/Explicit.hs view
@@ -104,7 +104,7 @@ 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" #-}+{-# DEPRECATED CComp "Will be removed in version 0.6. 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 }@@ -127,7 +127,7 @@ simpleLoop g b = let ~(c,d) = g (b,d) in c -{-# DEPRECATED syncA "Use 'Applicative' interface and 'sync' instead" #-}+{-# DEPRECATED syncA "Will be removed in version 0.6. Use 'Applicative' interface and 'sync' instead" #-} {-# INLINABLE syncA #-} -- | Create a synchronous 'CComp'onent from a combinational function describing -- a mealy machine@@ -162,7 +162,7 @@ s = cregister clk sI s' in o -{-# DEPRECATED cregisterC "'CComp' is deprecated, use 'cregister' instead" #-}+{-# DEPRECATED cregisterC "'CComp' is deprecated and will be removed in version 0.6, use 'cregister' instead" #-} -- | Create a 'cregister' 'CComp'onent -- -- > clk100 = Clock d100@@ -175,7 +175,7 @@ cregisterC :: Clock clk -> a -> CComp clk a a cregisterC clk = CC Prelude.. cregister clk -{-# DEPRECATED csimulateC "'CComp' is deprecated, use 'csimulate' instead" #-}+{-# DEPRECATED csimulateC "'CComp' is deprecated and will be removed in version 0.6, use 'csimulate' instead" #-} -- | Simulate a 'Comp'onent given a list of samples -- -- > clk100 = Clock d100@@ -187,11 +187,13 @@ {-# NOINLINE cblockRam #-} -- | Create a blockRAM with space for @n@ elements --+-- NB: Read value is delayed by 1 cycle+-- -- > 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+-- > bram40 = cblockRam clk100 d40 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@@ -213,13 +215,15 @@ | otherwise = ram o' = ram ! r -{-# DEPRECATED blockRamCC "'CComp' is deprecated, use 'cblockRam' instead" #-}+{-# DEPRECATED blockRamCC "'CComp' is deprecated and will be removed in version 0.6, use 'cblockRam' instead" #-} -- | Create a blockRAM with space for @n@ elements --+-- NB: Read value is delayed by 1 cycle+-- -- > clk100 = Clock 100 -- > -- > bramC40 :: CComp 100 (Unsigned 6, Unsigned 6, Bool, a) a--- > bramC40 = blockRamCC clk100 d50+-- > bramC40 = blockRamCC clk100 d40 blockRamCC :: (KnownNat n, KnownNat m, CPack a, Default a) => Clock clk -- ^ 'Clock' to synchronize to -> SNat n -- ^ Size @n@ of the blockram@@ -229,6 +233,8 @@ {-# INLINABLE cblockRamPow2 #-} -- | Create a blockRAM with space for 2^@n@ elements --+-- NB: Read value is delayed by 1 cycle+-- -- > 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)@@ -241,8 +247,10 @@ -> 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" #-}+{-# DEPRECATED blockRamPow2CC "'CComp' is deprecated and will be removed in version 0.6, use 'cblockRamPow2' instead" #-} -- | Create a blockRAM with space for 2^@n@ elements+--+-- NB: Read value is delayed by 1 cycle -- -- > clk100 = Clock d100 -- >
src/CLaSH/Signal/Explicit.hs view
@@ -115,6 +115,13 @@ -- | Isomorphism between a @'CSignal' clk@ of a product type (e.g. a tuple) and a -- product type of @'CSignal' clk@'s+--+-- Instances of 'CPack' must satisfy the following laws:+--+-- @+-- cpack clk . cunpack clk = 'id'+-- cunpack clk . cpack clk = 'id'+-- @ class CPack a where type CSignalP (clk :: Nat) a type CSignalP clk a = CSignal clk a
src/CLaSH/Signal/Implicit.hs view
@@ -79,7 +79,14 @@ simulate f = sample . f . fromList -- | Isomorphism between a 'Signal' of a product type (e.g. a tuple) and a--- product type of 'Signal's+-- product type of 'Signal's.+--+-- Instances of 'Pack' must satisfy the following laws:+--+-- @+-- pack . unpack = 'id'+-- unpack . pack = 'id'+-- @ class Pack a where type SignalP a -- | Example:
src/CLaSH/Sized/Vector.hs view
@@ -385,7 +385,7 @@ vindex_integer :: KnownNat n => Vec n a -> Integer -> a vindex_integer xs i = case vindexM_integer xs (maxIndex xs - i) of Just a -> a- Nothing -> error "index out of bounds"+ Nothing -> error ("(!): Index " ++ show i ++ " is out of bounds 0 and " ++ show (maxIndex xs)) {-# INLINEABLE (!) #-} -- | Vector index (subscript) operator, descending from 'maxIndex', where the@@ -430,7 +430,7 @@ vreplace_integer :: KnownNat n => Vec n a -> Integer -> a -> Vec n a vreplace_integer xs i a = case vreplaceM_integer xs (maxIndex xs - i) a of Just ys -> ys- Nothing -> error "index out of bounds"+ Nothing -> error ("vreplace: Index " ++ show i ++ " is out of bounds 0 and " ++ show (maxIndex xs)) {-# INLINEABLE vreplace #-} -- | Replace an element of a vector at the given index (subscript).@@ -649,12 +649,12 @@ -- > compareSwapL a b = if a < b then (a,b) -- > else (b,a) ----- Will not terminate because 'vzipWith' is too strict in its left argument:+-- Will not terminate because 'vzipWith' is too strict in its second argument: -- -- >>> sortV (4 :> 1 :> 2 :> 3 :> Nil) -- <*** Exception: <<loop>> ----- In this case, adding 'lazyV' on 'vzipWith's left argument:+-- In this case, adding 'lazyV' on 'vzipWith's second argument: -- -- > sortVL xs = vmap fst sorted <: (snd (vlast sorted)) -- > where
src/CLaSH/Tutorial.hs view
@@ -35,6 +35,9 @@ -- * Composition of sequential circuits -- $composition_sequential + -- * Advanced: VHDL primitives+ -- $vhdlprimitives+ -- * Conclusion -- $conclusion @@ -43,19 +46,27 @@ -- * Unsupported Haskell features -- $unsupported++ -- * CλaSH vs Lava+ -- $vslava ) where import CLaSH.Prelude import CLaSH.Prelude.Explicit+import Data.Char+import Data.Int+import GHC.Word + {- $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.+at describing and (de-)composing mathematical functions. The CλaSH compiler+transforms these high-level descriptions to low-level synthesizable VHDL. 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@@ -65,13 +76,13 @@ 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+Of course there are variants of recursion that could 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+On the other hand, Haskell's by-default non-strict evaluation works very well+for 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: @@ -81,10 +92,10 @@ 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+Over time, you will get a better feeling for the consequences 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+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@@ -93,9 +104,9 @@ {- $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.+<http://haskell.org/ghc GHC> Haskell compiler version 7.8.* and up. - (1) Install __GHC (version 7.8.1 or higher)__+ (1) Install __GHC (version 7.8.* 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@@ -105,12 +116,12 @@ (2) Install __Cabal__ - * Windows:+ * Windows and OS X Mavericks: * Download the binary for <http://www.haskell.org/cabal/download.html cabal-install> * Put the binary in a location mentioned in your @PATH@ - * Unix:+ * Other Unix systems: * Download the sources for <http://hackage.haskell.org/package/cabal-install cabal-install> * Unpack (@tar xf@) the archive and @cd@ to the directory@@ -129,7 +140,7 @@ * 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+ * Examine the VHDL code in the @vhdl@ directory -} @@ -173,28 +184,28 @@ 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:+* Create the file: - @- MAC.hs- @+ @+ MAC.hs+ @ - * Write on the first line the module header:+* Write on the first line the module header: - @- module MAC where- @+ @+ 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.+ 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:+* Add the import statement for the CλaSH prelude library: - @- import CLaSH.Prelude- @+ @+ import CLaSH.Prelude+ @ - This imports all the necessary functions and datatypes for circuit description.+ 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:@@ -214,7 +225,7 @@ We can also examine the inferred type of @ma@ in the interpreter: >>> :t ma-ma :: Num a => a -> a -> a+ma :: Num a => 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@@ -336,7 +347,7 @@ 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>+'SignalP' is an <http://www.haskell.org/ghc/docs/latest/html/users_guide/type-families.html#assoc-decl associated 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@@ -442,7 +453,7 @@ topEntity :: SignalP a -> SignalP b @ -Where @a@ and @b@ are placeholders for monomorphics types: the 'topEntity' is+Where @a@ and @b@ are placeholders for monomorphic types: the 'topEntity' is not allowed to be polymorphic. So given the above type for the 'topEntity', the type of 'testInput' should be: @@ -505,16 +516,18 @@ 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.+next section where we will describe another DSP classic: an FIR filter+structure. -} {- $mac5-* __'Num' instance__:+* __'Num' instance for 'Signal'__: - @'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:+ @'Signal' a@ is also also considered a 'Num'eric type as long as the value+ type /a/ is also 'Num'eric. 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@@ -522,11 +535,12 @@ acc = register 0 (acc + x * y) @ -* __'Applicative' interface__:+* __'Applicative' instance for 'Signal'__: 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:+ domain using the operators ('<$>' and '<*>') of the 'Applicative' type+ class: @ macA (x,y) = acc@@ -537,7 +551,8 @@ * __<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>+ 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": @@ -558,6 +573,10 @@ position of the arguments and result: @+ asStateM :: (Pack o, Pack i)+ => (i -> State s o)+ -> s+ -> (SignalP i -> SignalP o) asStateM f i = g \<^\> i where g s x = let (o,s') = runState (f x) s@@ -655,22 +674,196 @@ @ Here we can finally see the advantage of having the '<^>' return a function-of type @'SignalP' i -> 'SignalP' o@:+of type: @('SignalP' i -> 'SignalP' o)@ (instead of:+@('Signal' i -> 'Signal' 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. -} +{- $vhdlprimitives+There are times when you already have an existing piece of IP, or there are+times where you need the VHDL to have a specific shape so that the VHDL+synthesis tool can infer a specific component. In these specific cases you can+resort to defining your own VHDL primitives. Actually, most of the primitives+in CλaSH are specified in the same way as you will read about in this section.+There are perhaps 10 (at most) functions which are truly hard-coded into the+CλaSH compiler. You can take a look at the files in+<http://github.com/christiaanb/clash2/tree/master/primitives>+if you want to know which functions are defined as \"regular\" primitives. The+compiler looks for primitives in two locations:++* The official install location: e.g.+ @$CABAL_DIR\/share\/\<GHC_VERSION\>\/clash-ghc\-<VERSION\>\/primitives@+* The current directory (the location given by @pwd@)++Where redefined primitives in the current directory will overwrite those in+the official install location. For now, files containing primitive definitions+must end in the @.json@ file-extension.++CλaSH differentiates between two types of primitives, /expression/ primitives+and /declaration/ primitives, corresponding to whether the primitive is a VHDL+/expression/ or a VHDL /declaration/. We will first explore /expression/+primitives, using 'Signed' multiplication ('*') as an example. The+"CLaSH.Sized.Signed" module specifies multiplication as follows:++@+{\-# NOINLINE timesS #-\}+timesS :: KnownNat n => Signed n -> Signed n -> Signed n+timesS (S a) (S b) = fromIntegerS_inlineable (a * b)+@++For which the /expression/ primitive is:++@+{ \"BlackBox\" :+ { "name" : "CLaSH.Sized.Signed.timesS"+ , "templateE" : "resize(~ARG[1] * ~ARG[2], ~LIT[0])"+ }+}+@++The @name@ of the primitive is the /fully qualified/ name of the function you+are creating the primitive for. Because we are creating an /expression/+primitive we define a @template__E__@ field. As the name suggest, it is a VHDL+/template/, meaning that the compiler must fill in the holes heralded by the+tilde (~). Here:++ * @~ARG[1]@ denotes the second argument given to the @timesS@ function, which+ corresponds to the LHS of the ('*') operator.+ * @~ARG[2]@ denotes the third argument given to the @timesS@ function, which+ corresponds to the RHS of the ('*') operator.+ * @~LIT[0]@ denotes the first argument given to the @timesS@ function, with+ the extra condition that it must be a @LIT@eral. If for some reason this+ first argument does not turn out to be a literal then the compiler will+ raise an error. This first arguments corresponds to the \"@'KnownNat' n@\"+ class constraint.++An extensive list with all of the template holes will be given the end of this+section. What we immediately notice is that class constraints are counted as+normal arguments in the primitive definition. This is because these class+constraints are actually represented by ordinary record types, with fields+corresponding to the methods of the type class. In the above case, 'KnownNat'+is actually just like a @newtype@ wrapper for 'Integer'.++The second kind of primitive that we will explore is the /declaration/ primitive.+We will use 'blockRam' as an example, for which the Haskell/CλaSH code is:++@+{\-# NOINLINE blockRam #-\}+-- | Create a blockRAM with space for \@n\@ elements+--+-- NB: Read value is delayed by 1 cycle+--+-- > bram40 :: Signal (Unsigned 6) -> Signal (Unsigned 6) -> Signal Bool -> Signal a -> Signal a+-- > bram40 = blockRam d40+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\@+ -> Signal Bool -- ^ Write enable+ -> Signal a -- ^ Value to write (at address \@w\@)+ -> Signal a -- ^ Value of the 'blockRAM' at address \@r\@ from the previous clock cycle+blockRam n wr rd en din = pack $ (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+@++And for which the /definition/ primitive is:++@+{ \"BlackBox\" :+ { "name" : "CLaSH.Prelude.blockRam"+ , "templateD" :+"~SYM[0]_block : block+ type ram_array is array (natural range <>) of ~TYP[8];+ signal ~SYM[1] : ram_array((~ARG[0]-1) downto 0) := (others => ~ARG[3]); -- ram+ signal ~SYM[2] : ~TYP[8]; -- inp+ signal ~SYM[3] : ~TYP[8] := ~ARG[3]; -- outp+begin+ ~SYM[2] <= ~ARG[8];++ process(~CLKO)+ begin+ if rising_edge(~CLKO) then+ if ~ARG[7] then+ ~SYM[1](to_integer(~ARG[5])) <= ~SYM[2];+ end if;+ ~SYM[3] <= ~SYM[1](to_integer(~ARG[6]));+ end if;+ end process;++ ~RESULT <= ~SYM[3];+end block;"+ }+}+@++Again, the @name@ of the primitive is the fully qualified name of the function+you are creating the primitive for. Because we are creating a /declaration/+primitive we define a @template__D__@ field. Instead of discussing what the+individual template holes mean in the above context, we will instead just give+a general listing of the available template holes:++* @~RESULT@: VHDL signal to which the result of a primitive must be assigned+ to. NB: Only used in a /definition/ primitive.+* @~ARG[N]@: @(N+1)@'th argument to the function.+* @~LIT[N]@: @(N+1)@'th argument to the function An extra condition that must+ hold is that this @(N+1)@'th argument is an (integer) literal.+* @~CLK[N]@: Clock signal to which the @(N+1)@'th argument is synchronized to.+* @~CLKO@: Clock signal to which the result is synchronized to.+* @~RST[N]@: Asynchronous reset signal to the clock to which the @(N+1)@'th+ argument is synchronized to.+* @~RSTO@: Asynchronous reset signal to the clock to which the result is+ synchronized to.+* @~TYP[N]@: VHDL type of the @(N+1)@'th argument.+* @~TYPO@: VHDL type of the result.+* @~TYPM[N]@: VHDL type/name/ of the @(N+1)@'th argument; used in /type/+ /qualification/.+* @~TYPM@: VHDL type/name/ of the result; used in /type qualification/.+* @~DEF[N]@: Default value for the VHDL type of the @(N+1)@'th argument. NB:+ Does not correspond per se to the value of 'def' of the 'Default' type class+ for the Haskell type.+* @~DEFO@: Default value for the VHDL type of the result. NB: Does not+ correspond per se to the value of the 'def' of the 'Default' type class for+ the Haskell type.+* @~SYM[N]@: Randomly generated, but unique, symbol. Multiple occurrences of+ @~SYM[N]@ in the same primitive definition all refer to the same random, but+ unique, symbol.++Some final remarks to end this section: VHDL primitives are there to instruct the+CλaSH compiler to use the given VHDL template, instead of trying to do normal+synthesis. As a consequence you can use constructs inside the Haskell+definitions that are normally not synthesizable by the CλaSH compiler. However,+VHDL primitives do not give us /co-simulation/: where you would be able to+simulate VHDL and Haskell in a /single/ environment. If you still want to+simulate your design in Haskell, you will have to describe, in a cycle- and+bit-accurate way, the behaviour of that (potentially complex) IP you are trying+to include in your design.++Perhaps in the future, someone will figure out how to connect the two simulation+worlds, using e.g. VHDL's foreign function interface VHPI.+-}+ {- $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.+so check back from time to time. For now, we recommend that you continue with+exploring the "CLaSH.Prelude" module, and get a better understanding of the+capabilities of CλaSH in the process. -} {- $errorsandsolutions-A list often encountered errors and their solutions:+A list of often encountered errors and their solutions: * __Type error: Couldn't match expected type ‘Signal (a,b)’ with actual type__ __‘(Signal a, Signal b)’__:@@ -701,7 +894,7 @@ __ 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+ types are __isomorphic__ due to synchronicity 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: @@ -722,31 +915,54 @@ NB: Use 'cunpack' when you are using explicitly clocked 'CSignal's +* __CLaSH.Netlist(..): Not in normal form: \<REASON\>: \<EXPR\>__:++ A function could not be transformed into the expected normal form. This+ usually means one of the following:++ * The @topEntity@ has residual polymorphism.+ * The @topEntity@ has higher-order arguments, or a higher-order result.+ * You are using types which cannot be represented in hardware.++ The solution for all the above listed reasons is quite simple: remove them.+ That is, make sure that the @topEntity@ is completely monomorphic and+ first-order. Also remove any variables and constants/literals that have a+ non-representable type, see <#unsupported Unsupported Haskell features> to+ find out which types are not representable.+ * __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 :-)+ one using e.g. one of the higher-order functions in "CLaSH.Sized.Vector" :-) * You defined a recursively defined value, but left it polymorphic: @ topEntity x y = acc- where- acc = register 3 (x*y + acc)+ where+ acc = register 3 (acc + x * y) @ 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.+ 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)+ where+ acc = register 3 (acc + x * y) @ + Or, alternatively:++ @+ topEntity x y = acc+ where+ acc = register (3 :: Signed 8) (acc + x * y)+ @+ * __CLaSH.Normalize.Transformations(155): InlineNonRep: \<FUNCTION\> already__ __inlined 100 times in:\<FUNCTION\>, \<TYPE\>__: @@ -781,12 +997,12 @@ else (b,a) @ - Will not terminate because 'vzipWith' is too strict in its left argument:+ Will not terminate because 'vzipWith' is too strict in its second argument: >>> sortV (4 :> 1 :> 2 :> 3 :> Nil) <*** Exception: <<loop>> - In this case, adding 'lazyV' on 'vzipWith's left argument:+ In this case, adding 'lazyV' on 'vzipWith's second argument: @ sortVL xs = vmap fst sorted <: (snd (vlast sorted))@@ -855,6 +1071,62 @@ [@Floating point types@] - There is no support for the 'Float' and 'Double' type, if you need numbers+ There is no support for the 'Float' and 'Double' types, if you need numbers with a /fractional/ part you can use the 'Fixed' point type.++ [@Other primitive types@]++ Most primitive types are not supported, with the exception of 'Int',+ @<http://hackage.haskell.org/package/ghc-prim/docs/GHC-Prim.html#t:Int-35- Int#>@,+ and 'Integer'. This means that types such as: 'Word', 'Word8', 'Int8', 'Char',+ @<http://hackage.haskell.org/package/array/docs/Data-Array.html#t:Array Array>@,+ etc. cannot to translated to hardware.++ The translations of 'Int',+ @<http://hackage.haskell.org/package/ghc-prim/docs/GHC-Prim.html#t:Int-35- Int#>@,+ and 'Integer' are also incorrect: they are translated to the VHDL @integer@+ type, which can only represent 32-bit integer values. Use these types with+ due diligence.++ [@Side-effects: 'IO', <http://hackage.haskell.org/package/base/docs/Control-Monad-ST.html#t:ST ST>, etc.@]++ There is no support for side-effecting computations such as those in the+ 'IO' or @<http://hackage.haskell.org/package/base/docs/Control-Monad-ST.html#t:ST ST>@+ monad. There is also no support for Haskell's+ <http://www.haskell.org/haskellwiki/Foreign_Function_Interface FFI>.+-}++{- $vslava+In Haskell land the most well-known way of describing digital circuits is the+Lava family of languages:++* <http://hackage.haskell.org/package/chalmers-lava2000 Chalmers Lava>+* <http://hackage.haskell.org/package/xilinx-lava Xilinx Lava>+* <http://hackage.haskell.org/package/york-lava York Lava>+* <http://hackage.haskell.org/package/kansas-lava Kansas Lava>++The big difference between CλaSH and Lava is that CλaSH uses a \"standard\"+compiler (static analysis) approach towards synthesis, where Lava is an+embedded domain specific language. One downside of static analysis vs. the+embedded language approach is already clearly visible: synthesis of recursive+descriptions does not come for \"free\". This will be implemented in CλaSH in+due time, but that doesn't help the circuit designer right now. As already+mentioned earlier, the lack of support for recursive functions is amortized by+the built-in support for the higher-order in "CLaSH.Sized.Vector".++The big upside of CλaSH and its static analysis approach is that CλaSH can+do synthesis of \"normal\" functions: there is no forced encasing datatype (often+called /Signal/ in Lava) on all the arguments and results of a synthesizable+function. This enables the following features not available to Lava:++* Automatic synthesis for user-defined ADTs+* Synthesis of all choice constructs (pattern matching, guards, etc.)+* 'Applicative' instance for the 'Signal' type+* Working with \"normal\" functions permits the use of e.g. the+ <http://hackage.haskell.org/package/mtl/docs/Control-Monad-State-Lazy.html#t:State State>+ monad to describe the functionality of a circuit.++Although there are Lava alternatives to some of the above features (e.g.+first-class patterns to replace pattern matching) they are not as \"beautiful\"+and / or easy to use as the standard Haskell features. -}