clash-prelude 0.8.1 → 0.9
raw patch · 25 files changed
+2795/−447 lines, 25 filesdep +arrayPVP ok
version bump matches the API change (PVP)
Dependencies added: array
API changes (from Hackage documentation)
- CLaSH.Annotations.TopEntity: defClkAltera :: String -> String -> String -> ClockSource
- CLaSH.Annotations.TopEntity: defClkXilinx :: String -> String -> String -> ClockSource
- CLaSH.Sized.Internal.BitVector: mod# :: BitVector n -> BitVector n -> BitVector n
- CLaSH.Sized.Internal.Index: mod# :: Index n -> Index n -> Index n
- CLaSH.Sized.Internal.Unsigned: [unsafeToBitVector] :: Unsigned -> Integer
- CLaSH.Sized.Internal.Unsigned: mod# :: Unsigned n -> Unsigned n -> Unsigned n
+ CLaSH.Annotations.TopEntity: alteraPll :: String -> String -> String -> ClockSource
+ CLaSH.Annotations.TopEntity: altpll :: String -> String -> String -> ClockSource
+ CLaSH.Annotations.TopEntity: clockWizard :: String -> String -> String -> ClockSource
+ CLaSH.Prelude: asyncRam :: (KnownNat n, Enum addr) => SNat n -> Signal addr -> Signal addr -> Signal Bool -> Signal a -> Signal a
+ CLaSH.Prelude: asyncRamPow2 :: (KnownNat (2 ^ n), KnownNat n) => Signal (Unsigned n) -> Signal (Unsigned n) -> Signal Bool -> Signal a -> Signal a
+ CLaSH.Prelude: asyncRom :: (KnownNat n, Enum addr) => Vec n a -> addr -> a
+ CLaSH.Prelude: asyncRomFile :: (KnownNat m, Enum addr) => SNat n -> FilePath -> addr -> BitVector m
+ CLaSH.Prelude: asyncRomFilePow2 :: (KnownNat m, KnownNat n, KnownNat (2 ^ n)) => FilePath -> Unsigned n -> BitVector m
+ CLaSH.Prelude: asyncRomPow2 :: (KnownNat (2 ^ n), KnownNat n) => Vec (2 ^ n) a -> Unsigned n -> a
+ CLaSH.Prelude: blockRamFile :: (KnownNat m, Enum addr) => SNat n -> FilePath -> Signal addr -> Signal addr -> Signal Bool -> Signal (BitVector m) -> Signal (BitVector m)
+ CLaSH.Prelude: blockRamFilePow2 :: (KnownNat m, KnownNat n, KnownNat (2 ^ n)) => FilePath -> Signal (Unsigned n) -> Signal (Unsigned n) -> Signal Bool -> Signal (BitVector m) -> Signal (BitVector m)
+ CLaSH.Prelude: rom :: (KnownNat n, KnownNat m) => Vec n a -> Signal (Unsigned m) -> Signal a
+ CLaSH.Prelude: romFile :: (KnownNat m, KnownNat k) => SNat n -> FilePath -> Signal (Unsigned k) -> Signal (BitVector m)
+ CLaSH.Prelude: romFilePow2 :: (KnownNat m, KnownNat n, KnownNat (2 ^ n)) => FilePath -> Signal (Unsigned n) -> Signal (BitVector m)
+ CLaSH.Prelude: romPow2 :: (KnownNat (2 ^ n), KnownNat n) => Vec (2 ^ n) a -> Signal (Unsigned n) -> Signal a
+ CLaSH.Prelude.BlockRam: blockRam# :: KnownNat n => SClock clk -> Vec n a -> Signal' clk Int -> Signal' clk Int -> Signal' clk Bool -> Signal' clk a -> Signal' clk a
+ CLaSH.Prelude.BlockRam.File: blockRamFile :: (KnownNat m, Enum addr) => SNat n -> FilePath -> Signal addr -> Signal addr -> Signal Bool -> Signal (BitVector m) -> Signal (BitVector m)
+ CLaSH.Prelude.BlockRam.File: blockRamFile# :: KnownNat m => SClock clk -> SNat n -> FilePath -> Signal' clk Int -> Signal' clk Int -> Signal' clk Bool -> Signal' clk (BitVector m) -> Signal' clk (BitVector m)
+ CLaSH.Prelude.BlockRam.File: blockRamFile' :: (KnownNat m, Enum addr) => SClock clk -> SNat n -> FilePath -> Signal' clk addr -> Signal' clk addr -> Signal' clk Bool -> Signal' clk (BitVector m) -> Signal' clk (BitVector m)
+ CLaSH.Prelude.BlockRam.File: blockRamFilePow2 :: (KnownNat m, KnownNat n, KnownNat (2 ^ n)) => FilePath -> Signal (Unsigned n) -> Signal (Unsigned n) -> Signal Bool -> Signal (BitVector m) -> Signal (BitVector m)
+ CLaSH.Prelude.BlockRam.File: blockRamFilePow2' :: (KnownNat m, KnownNat n, KnownNat (2 ^ n)) => SClock clk -> FilePath -> Signal' clk (Unsigned n) -> Signal' clk (Unsigned n) -> Signal' clk Bool -> Signal' clk (BitVector m) -> Signal' clk (BitVector m)
+ CLaSH.Prelude.BlockRam.File: initMem :: KnownNat n => FilePath -> [BitVector n]
+ CLaSH.Prelude.Explicit: assert' :: (Eq a, Show a) => SClock t -> String -> Signal' t a -> Signal' t a -> Signal' t b -> Signal' t b
+ CLaSH.Prelude.Explicit: asyncFIFOSynchronizer :: _ => SNat addrSize -> SClock wclk -> SClock rclk -> Signal' wclk a -> Signal' wclk Bool -> Signal' rclk Bool -> (Signal' rclk a, Signal' rclk Bool, Signal' wclk Bool)
+ CLaSH.Prelude.Explicit: asyncRam' :: (KnownNat n, Enum addr) => SClock wclk -> SClock rclk -> SNat n -> Signal' wclk addr -> Signal' rclk addr -> Signal' wclk Bool -> Signal' wclk a -> Signal' rclk a
+ CLaSH.Prelude.Explicit: asyncRamPow2' :: (KnownNat n, KnownNat (2 ^ n)) => SClock wclk -> SClock rclk -> Signal' wclk (Unsigned n) -> Signal' rclk (Unsigned n) -> Signal' wclk Bool -> Signal' wclk a -> Signal' rclk a
+ CLaSH.Prelude.Explicit: blockRamFile' :: (KnownNat m, Enum addr) => SClock clk -> SNat n -> FilePath -> Signal' clk addr -> Signal' clk addr -> Signal' clk Bool -> Signal' clk (BitVector m) -> Signal' clk (BitVector m)
+ CLaSH.Prelude.Explicit: blockRamFilePow2' :: (KnownNat m, KnownNat n, KnownNat (2 ^ n)) => SClock clk -> FilePath -> Signal' clk (Unsigned n) -> Signal' clk (Unsigned n) -> Signal' clk Bool -> Signal' clk (BitVector m) -> Signal' clk (BitVector m)
+ CLaSH.Prelude.Explicit: dualFlipFlopSynchronizer :: SClock clk1 -> SClock clk2 -> a -> Signal' clk1 a -> Signal' clk2 a
+ CLaSH.Prelude.Explicit: rom' :: (KnownNat n, Enum addr) => SClock clk -> Vec n a -> Signal' clk addr -> Signal' clk a
+ CLaSH.Prelude.Explicit: romFile' :: (KnownNat m, Enum addr) => SClock clk -> SNat n -> FilePath -> Signal' clk addr -> Signal' clk (BitVector m)
+ CLaSH.Prelude.Explicit: romFilePow2' :: (KnownNat m, KnownNat n, KnownNat (2 ^ n)) => SClock clk -> FilePath -> Signal' clk (Unsigned n) -> Signal' clk (BitVector m)
+ CLaSH.Prelude.Explicit: romPow2' :: (KnownNat (2 ^ n), KnownNat n) => SClock clk -> Vec (2 ^ n) a -> Signal' clk (Unsigned n) -> Signal' clk a
+ CLaSH.Prelude.Explicit.Safe: asyncFIFOSynchronizer :: _ => SNat addrSize -> SClock wclk -> SClock rclk -> Signal' wclk a -> Signal' wclk Bool -> Signal' rclk Bool -> (Signal' rclk a, Signal' rclk Bool, Signal' wclk Bool)
+ CLaSH.Prelude.Explicit.Safe: asyncRam' :: (KnownNat n, Enum addr) => SClock wclk -> SClock rclk -> SNat n -> Signal' wclk addr -> Signal' rclk addr -> Signal' wclk Bool -> Signal' wclk a -> Signal' rclk a
+ CLaSH.Prelude.Explicit.Safe: asyncRamPow2' :: (KnownNat n, KnownNat (2 ^ n)) => SClock wclk -> SClock rclk -> Signal' wclk (Unsigned n) -> Signal' rclk (Unsigned n) -> Signal' wclk Bool -> Signal' wclk a -> Signal' rclk a
+ CLaSH.Prelude.Explicit.Safe: blockRam' :: (KnownNat n, Enum addr) => SClock clk -> Vec n a -> Signal' clk addr -> Signal' clk addr -> Signal' clk Bool -> Signal' clk a -> Signal' clk a
+ CLaSH.Prelude.Explicit.Safe: blockRamPow2' :: (KnownNat n, KnownNat (2 ^ n)) => SClock clk -> Vec (2 ^ n) a -> Signal' clk (Unsigned n) -> Signal' clk (Unsigned n) -> Signal' clk Bool -> Signal' clk a -> Signal' clk a
+ CLaSH.Prelude.Explicit.Safe: dualFlipFlopSynchronizer :: SClock clk1 -> SClock clk2 -> a -> Signal' clk1 a -> Signal' clk2 a
+ CLaSH.Prelude.Explicit.Safe: isFalling' :: (Bounded a, Eq a) => SClock clk -> a -> Signal' clk a -> Signal' clk Bool
+ CLaSH.Prelude.Explicit.Safe: isRising' :: (Bounded a, Eq a) => SClock clk -> a -> Signal' clk a -> Signal' clk Bool
+ CLaSH.Prelude.Explicit.Safe: mealy' :: SClock clk -> (s -> i -> (s, o)) -> s -> (Signal' clk i -> Signal' clk o)
+ CLaSH.Prelude.Explicit.Safe: mealyB' :: (Bundle i, Bundle o) => SClock clk -> (s -> i -> (s, o)) -> s -> (Unbundled' clk i -> Unbundled' clk o)
+ CLaSH.Prelude.Explicit.Safe: moore' :: SClock clk -> (s -> i -> s) -> (s -> o) -> s -> (Signal' clk i -> Signal' clk o)
+ CLaSH.Prelude.Explicit.Safe: mooreB' :: (Bundle i, Bundle o) => SClock clk -> (s -> i -> s) -> (s -> o) -> s -> (Unbundled' clk i -> Unbundled' clk o)
+ CLaSH.Prelude.Explicit.Safe: registerB' :: Bundle a => SClock clk -> a -> Unbundled' clk a -> Unbundled' clk a
+ CLaSH.Prelude.Explicit.Safe: rom' :: (KnownNat n, Enum addr) => SClock clk -> Vec n a -> Signal' clk addr -> Signal' clk a
+ CLaSH.Prelude.Explicit.Safe: romPow2' :: (KnownNat (2 ^ n), KnownNat n) => SClock clk -> Vec (2 ^ n) a -> Signal' clk (Unsigned n) -> Signal' clk a
+ CLaSH.Prelude.RAM: asyncRam :: (KnownNat n, Enum addr) => SNat n -> Signal addr -> Signal addr -> Signal Bool -> Signal a -> Signal a
+ CLaSH.Prelude.RAM: asyncRam# :: SClock wclk -> SClock rclk -> SNat n -> Signal' wclk Int -> Signal' rclk Int -> Signal' wclk Bool -> Signal' wclk a -> Signal' rclk a
+ CLaSH.Prelude.RAM: asyncRam' :: (KnownNat n, Enum addr) => SClock wclk -> SClock rclk -> SNat n -> Signal' wclk addr -> Signal' rclk addr -> Signal' wclk Bool -> Signal' wclk a -> Signal' rclk a
+ CLaSH.Prelude.RAM: asyncRamPow2 :: (KnownNat (2 ^ n), KnownNat n) => Signal (Unsigned n) -> Signal (Unsigned n) -> Signal Bool -> Signal a -> Signal a
+ CLaSH.Prelude.RAM: asyncRamPow2' :: (KnownNat n, KnownNat (2 ^ n)) => SClock wclk -> SClock rclk -> Signal' wclk (Unsigned n) -> Signal' rclk (Unsigned n) -> Signal' wclk Bool -> Signal' wclk a -> Signal' rclk a
+ CLaSH.Prelude.ROM: asyncRom :: (KnownNat n, Enum addr) => Vec n a -> addr -> a
+ CLaSH.Prelude.ROM: asyncRom# :: KnownNat n => Vec n a -> Int -> a
+ CLaSH.Prelude.ROM: asyncRomPow2 :: (KnownNat (2 ^ n), KnownNat n) => Vec (2 ^ n) a -> Unsigned n -> a
+ CLaSH.Prelude.ROM: rom :: (KnownNat n, KnownNat m) => Vec n a -> Signal (Unsigned m) -> Signal a
+ CLaSH.Prelude.ROM: rom# :: KnownNat n => SClock clk -> Vec n a -> Signal' clk Int -> Signal' clk a
+ CLaSH.Prelude.ROM: rom' :: (KnownNat n, Enum addr) => SClock clk -> Vec n a -> Signal' clk addr -> Signal' clk a
+ CLaSH.Prelude.ROM: romPow2 :: (KnownNat (2 ^ n), KnownNat n) => Vec (2 ^ n) a -> Signal (Unsigned n) -> Signal a
+ CLaSH.Prelude.ROM: romPow2' :: (KnownNat (2 ^ n), KnownNat n) => SClock clk -> Vec (2 ^ n) a -> Signal' clk (Unsigned n) -> Signal' clk a
+ CLaSH.Prelude.ROM.File: asyncRomFile :: (KnownNat m, Enum addr) => SNat n -> FilePath -> addr -> BitVector m
+ CLaSH.Prelude.ROM.File: asyncRomFile# :: KnownNat m => SNat n -> FilePath -> Int -> BitVector m
+ CLaSH.Prelude.ROM.File: asyncRomFilePow2 :: (KnownNat m, KnownNat n, KnownNat (2 ^ n)) => FilePath -> Unsigned n -> BitVector m
+ CLaSH.Prelude.ROM.File: romFile :: (KnownNat m, KnownNat k) => SNat n -> FilePath -> Signal (Unsigned k) -> Signal (BitVector m)
+ CLaSH.Prelude.ROM.File: romFile# :: KnownNat m => SClock clk -> SNat n -> FilePath -> Signal' clk Int -> Signal' clk (BitVector m)
+ CLaSH.Prelude.ROM.File: romFile' :: (KnownNat m, Enum addr) => SClock clk -> SNat n -> FilePath -> Signal' clk addr -> Signal' clk (BitVector m)
+ CLaSH.Prelude.ROM.File: romFilePow2 :: (KnownNat m, KnownNat n, KnownNat (2 ^ n)) => FilePath -> Signal (Unsigned n) -> Signal (BitVector m)
+ CLaSH.Prelude.ROM.File: romFilePow2' :: (KnownNat m, KnownNat n, KnownNat (2 ^ n)) => SClock clk -> FilePath -> Signal' clk (Unsigned n) -> Signal' clk (BitVector m)
+ CLaSH.Prelude.Safe: (<^>) :: (Bundle i, Bundle o) => (s -> i -> (s, o)) -> s -> (Unbundled i -> Unbundled o)
+ CLaSH.Prelude.Safe: asyncRam :: (KnownNat n, Enum addr) => SNat n -> Signal addr -> Signal addr -> Signal Bool -> Signal a -> Signal a
+ CLaSH.Prelude.Safe: asyncRamPow2 :: (KnownNat (2 ^ n), KnownNat n) => Signal (Unsigned n) -> Signal (Unsigned n) -> Signal Bool -> Signal a -> Signal a
+ CLaSH.Prelude.Safe: asyncRom :: (KnownNat n, Enum addr) => Vec n a -> addr -> a
+ CLaSH.Prelude.Safe: asyncRomPow2 :: (KnownNat (2 ^ n), KnownNat n) => Vec (2 ^ n) a -> Unsigned n -> a
+ CLaSH.Prelude.Safe: blockRam :: (KnownNat n, Enum addr) => Vec n a -> Signal addr -> Signal addr -> Signal Bool -> Signal a -> Signal a
+ CLaSH.Prelude.Safe: blockRamPow2 :: (KnownNat (2 ^ n), KnownNat n) => Vec (2 ^ n) a -> Signal (Unsigned n) -> Signal (Unsigned n) -> Signal Bool -> Signal a -> Signal a
+ CLaSH.Prelude.Safe: isFalling :: (Bounded a, Eq a) => a -> Signal a -> Signal Bool
+ CLaSH.Prelude.Safe: isRising :: (Bounded a, Eq a) => a -> Signal a -> Signal Bool
+ CLaSH.Prelude.Safe: mealy :: (s -> i -> (s, o)) -> s -> (Signal i -> Signal o)
+ CLaSH.Prelude.Safe: mealyB :: (Bundle i, Bundle o) => (s -> i -> (s, o)) -> s -> (Unbundled i -> Unbundled o)
+ CLaSH.Prelude.Safe: moore :: (s -> i -> s) -> (s -> o) -> s -> (Signal i -> Signal o)
+ CLaSH.Prelude.Safe: mooreB :: (Bundle i, Bundle o) => (s -> i -> s) -> (s -> o) -> s -> (Unbundled i -> Unbundled o)
+ CLaSH.Prelude.Safe: registerB :: Bundle a => a -> Unbundled a -> Unbundled a
+ CLaSH.Prelude.Safe: rom :: (KnownNat n, KnownNat m) => Vec n a -> Signal (Unsigned m) -> Signal a
+ CLaSH.Prelude.Safe: romPow2 :: (KnownNat (2 ^ n), KnownNat n) => Vec (2 ^ n) a -> Signal (Unsigned n) -> Signal a
+ CLaSH.Prelude.Synchronizer: asyncFIFOSynchronizer :: _ => SNat addrSize -> SClock wclk -> SClock rclk -> Signal' wclk a -> Signal' wclk Bool -> Signal' rclk Bool -> (Signal' rclk a, Signal' rclk Bool, Signal' wclk Bool)
+ CLaSH.Prelude.Synchronizer: dualFlipFlopSynchronizer :: SClock clk1 -> SClock clk2 -> a -> Signal' clk1 a -> Signal' clk2 a
+ CLaSH.Prelude.Testbench: assert' :: (Eq a, Show a) => SClock t -> String -> Signal' t a -> Signal' t a -> Signal' t b -> Signal' t b
+ CLaSH.Promoted.Nat: addSNat :: KnownNat (a + b) => SNat a -> SNat b -> SNat (a + b)
+ CLaSH.Promoted.Nat: mulSNat :: KnownNat (a * b) => SNat a -> SNat b -> SNat (a * b)
+ CLaSH.Promoted.Nat: powSNat :: KnownNat (a ^ b) => SNat a -> SNat b -> SNat (a ^ b)
+ CLaSH.Promoted.Nat: subSNat :: KnownNat (a - b) => SNat a -> SNat b -> SNat (a - b)
+ CLaSH.Signal.Explicit: freqCalc :: [Integer] -> [Integer]
+ CLaSH.Signal.Internal: joinSignal# :: Signal' clk (Signal' clk a) -> Signal' clk a
+ CLaSH.Sized.Fixed: fLitR :: (size ~ (int + frac), KnownNat frac, Bounded (rep size), Integral (rep size)) => Double -> Fixed rep int frac
+ CLaSH.Sized.Internal.Unsigned: [unsafeToInteger] :: Unsigned -> Integer
- CLaSH.Prelude: assert :: (Eq a, Show a) => Signal' t a -> Signal' t a -> Signal' t b -> Signal' t b
+ CLaSH.Prelude: assert :: (Eq a, Show a) => String -> Signal a -> Signal a -> Signal b -> Signal b
- CLaSH.Prelude: blockRam :: (KnownNat n, KnownNat m) => Vec n a -> Signal (Unsigned m) -> Signal (Unsigned m) -> Signal Bool -> Signal a -> Signal a
+ CLaSH.Prelude: blockRam :: (KnownNat n, Enum addr) => Vec n a -> Signal addr -> Signal addr -> Signal Bool -> Signal a -> Signal a
- CLaSH.Prelude.BitIndex: (!) :: (BitPack a, KnownNat (BitSize a), Integral i) => a -> i -> Bit
+ CLaSH.Prelude.BitIndex: (!) :: (BitPack a, KnownNat (BitSize a), Enum i) => a -> i -> Bit
- CLaSH.Prelude.BitIndex: replaceBit :: (BitPack a, KnownNat (BitSize a), Integral i) => i -> Bit -> a -> a
+ CLaSH.Prelude.BitIndex: replaceBit :: (BitPack a, KnownNat (BitSize a), Enum i) => i -> Bit -> a -> a
- CLaSH.Prelude.BlockRam: blockRam :: (KnownNat n, KnownNat m) => Vec n a -> Signal (Unsigned m) -> Signal (Unsigned m) -> Signal Bool -> Signal a -> Signal a
+ CLaSH.Prelude.BlockRam: blockRam :: (KnownNat n, Enum addr) => Vec n a -> Signal addr -> Signal addr -> Signal Bool -> Signal a -> Signal a
- CLaSH.Prelude.BlockRam: blockRam' :: (KnownNat n, KnownNat m) => SClock clk -> Vec n a -> Signal' clk (Unsigned m) -> Signal' clk (Unsigned m) -> Signal' clk Bool -> Signal' clk a -> Signal' clk a
+ CLaSH.Prelude.BlockRam: blockRam' :: (KnownNat n, Enum addr) => SClock clk -> Vec n a -> Signal' clk addr -> Signal' clk addr -> Signal' clk Bool -> Signal' clk a -> Signal' clk a
- CLaSH.Prelude.Explicit: blockRam' :: (KnownNat n, KnownNat m) => SClock clk -> Vec n a -> Signal' clk (Unsigned m) -> Signal' clk (Unsigned m) -> Signal' clk Bool -> Signal' clk a -> Signal' clk a
+ CLaSH.Prelude.Explicit: blockRam' :: (KnownNat n, Enum addr) => SClock clk -> Vec n a -> Signal' clk addr -> Signal' clk addr -> Signal' clk Bool -> Signal' clk a -> Signal' clk a
- CLaSH.Prelude.Testbench: assert :: (Eq a, Show a) => Signal' t a -> Signal' t a -> Signal' t b -> Signal' t b
+ CLaSH.Prelude.Testbench: assert :: (Eq a, Show a) => String -> Signal a -> Signal a -> Signal b -> Signal b
- CLaSH.Signal: mux :: Signal' clk Bool -> Signal' clk a -> Signal' clk a -> Signal' clk a
+ CLaSH.Signal: mux :: Applicative f => f Bool -> f a -> f a -> f a
- CLaSH.Signal.Delayed: antiDelay :: SNat d -> DSignal n a -> DSignal (n - d) a
+ CLaSH.Signal.Delayed: antiDelay :: SNat d -> DSignal (n + d) a -> DSignal n a
- CLaSH.Signal.Delayed: delay :: KnownNat m => Vec m a -> DSignal (n - m) a -> DSignal n a
+ CLaSH.Signal.Delayed: delay :: KnownNat d => Vec d a -> DSignal n a -> DSignal (n + d) a
- CLaSH.Signal.Delayed: delayI :: (Default a, KnownNat m) => DSignal (n - m) a -> DSignal n a
+ CLaSH.Signal.Delayed: delayI :: (Default a, KnownNat d) => DSignal n a -> DSignal (n + d) a
- CLaSH.Signal.Delayed: feedback :: (DSignal ((n - m) - 1) a -> (DSignal ((n - m) - 1) a, DSignal n a)) -> DSignal ((n - m) - 1) a
+ CLaSH.Signal.Delayed: feedback :: (DSignal n a -> (DSignal n a, DSignal ((n + m) + 1) a)) -> DSignal n a
- CLaSH.Signal.Internal: mux :: Signal' clk Bool -> Signal' clk a -> Signal' clk a -> Signal' clk a
+ CLaSH.Signal.Internal: mux :: Applicative f => f Bool -> f a -> f a -> f a
- CLaSH.Sized.Vector: (!!) :: (KnownNat n, Integral i) => Vec n a -> i -> a
+ CLaSH.Sized.Vector: (!!) :: (KnownNat n, Enum i) => Vec n a -> i -> a
- CLaSH.Sized.Vector: replace :: (KnownNat n, Integral i) => i -> a -> Vec n a -> Vec n a
+ CLaSH.Sized.Vector: replace :: (KnownNat n, Enum i) => i -> a -> Vec n a -> Vec n a
Files
- CHANGELOG.md +20/−6
- clash-prelude.cabal +165/−157
- src/CLaSH/Annotations/TopEntity.hs +70/−31
- src/CLaSH/Prelude.hs +21/−32
- src/CLaSH/Prelude/BitIndex.hs +4/−4
- src/CLaSH/Prelude/BlockRam.hs +421/−33
- src/CLaSH/Prelude/BlockRam/File.hs +300/−0
- src/CLaSH/Prelude/Explicit.hs +24/−53
- src/CLaSH/Prelude/Explicit/Safe.hs +111/−0
- src/CLaSH/Prelude/RAM.hs +149/−0
- src/CLaSH/Prelude/ROM.hs +173/−0
- src/CLaSH/Prelude/ROM/File.hs +312/−0
- src/CLaSH/Prelude/Safe.hs +176/−0
- src/CLaSH/Prelude/Synchronizer.hs +157/−0
- src/CLaSH/Prelude/Testbench.hs +56/−20
- src/CLaSH/Promoted/Nat.hs +21/−4
- src/CLaSH/Signal/Delayed.hs +14/−14
- src/CLaSH/Signal/Explicit.hs +39/−0
- src/CLaSH/Signal/Internal.hs +23/−2
- src/CLaSH/Sized/Fixed.hs +166/−0
- src/CLaSH/Sized/Internal/BitVector.hs +3/−6
- src/CLaSH/Sized/Internal/Index.hs +3/−6
- src/CLaSH/Sized/Internal/Unsigned.hs +5/−8
- src/CLaSH/Sized/Vector.hs +4/−4
- src/CLaSH/Tutorial.hs +358/−67
CHANGELOG.md view
@@ -1,18 +1,32 @@ # Changelog for [`clash-prelude` package](http://hackage.haskell.org/package/clash-prelude) +## 0.9 *June 25th 2015*+* New features:+ * Add operations on singleton natural numbers: `addSNat`, `subSNat`, `mulSNat`, and `powSNat`.+ * Add asynchronous RAM functions in `CLaSH.Prelude.RAM`, which have an asynchronous/combinational read port.+ * Add ROM functions in modules `CLaSH.Prelude.ROM` and `CLaSH.Prelude.ROM.File`, where the latter module contains functions that instantiate a ROM from the content specified in an external data-file.+ * Add BlockRam functions, in the `CLaSH.Prelude.BlockRam.File` module, whose content can be initialised with the content specified in an external data-file.+ * `assert` now takes an extra `String` argument so you can distinguish one `assert` from the others.+ Additionally, `assert'` is added which takes an additional `SClock` argument.+ This is needed, because `assert` now reports the clock cycle, and clock domain, when an assertion fails.+ * `defClkAltera` and `defClkXilinx` are replaced by, `altpll` and `alteraPll` for Altera clock sources, and `clockWizard` for Xilinx clock sources.+ These names correspond to the names of the generator utilities in Quartus and ISE/Vivado.+ * Add [Safe](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/safe-haskell.html) versions of the prelude modules: `CLaSH.Prelude.Safe` and `CLaSH.Prelude.Explicit.Safe`+ * Add synchronizers in the `CLaSH.Prelude.Synchronizer` module+ ## 0.8 *June 3rd 2015* * New features: * Make the (Bit)Vector argument the _last_ argument for the following functions: `slice`, `setSlice`, `replaceBit`, `replace`. The signatures for the above functions are now:- + ``` slice :: BitPack a => SNat m -> SNat n -> a -> BitVector (m + 1 - n) setSlice :: BitPack a => SNat m -> SNat n -> BitVector (m + 1 - n) -> a -> a replaceBit :: Integral i => i -> Bit -> a -> a replace :: Integral i => i -> a -> Vec n a -> Vec n a ```- + This allows for easier chaining, e.g.:- + ``` replaceBit 0 1 $ repleceBit 4 0 $@@ -28,16 +42,16 @@ > unpack 0x0F :: Vec 8 Bit <1,1,1,1,0,0,0,0> ```- + As of version 0.8, we have `head x == msb y`:- + ``` > 0x0F :: BitVector 8 0000_1111 > unpack 0x0F :: Vec 8 Bit <0,0,0,0,1,1,1,1> ```- + So converting for `Vec`tors of `Bit`s to `BitVector`s is no longer _index_-preserving, but it is _order_-preserving. * Add [QuickCheck](http://hackage.haskell.org/package/QuickCheck) `Arbitary` and `CoArbitary` instances for all data types * Add [lens](http://hackage.haskell.org/package/lens) `Ixed` instances for `BitVector`, `Signed`, `Unsigned`, and `Vec`
clash-prelude.cabal view
@@ -1,157 +1,165 @@-Name: clash-prelude -Version: 0.8.1 -Synopsis: CAES Language for Synchronous Hardware - Prelude library -Description: - 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 - . - 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". - Some circuit examples can be found in "CLaSH.Examples". -Homepage: http://www.clash-lang.org/ -bug-reports: http://github.com/clash-lang/clash-prelude/issues -License: BSD2 -License-file: LICENSE -Author: Christiaan Baaij -Maintainer: Christiaan Baaij <christiaan.baaij@gmail.com> -Copyright: Copyright © 2013-2015 University of Twente -Category: Hardware -Build-type: Simple - -Extra-source-files: README.md - CHANGELOG.md - AUTHORS.md - -extra-doc-files: doc/*.svg - -Cabal-version: >=1.10 - -source-repository head - type: git - location: https://github.com/clash-lang/clash-prelude.git - -flag doctests - description: - You can disable testing with doctests using `-f-doctests`. - default: True - manual: True - -flag doclinks - description: - Create hyperlinks to non-dependent packages using `-fdoclinks`. - default: False - manual: True - -Library - HS-Source-Dirs: src - - default-language: Haskell2010 - ghc-options: -Wall -fexpose-all-unfoldings - - Exposed-modules: CLaSH.Annotations.TopEntity - - CLaSH.Class.BitPack - CLaSH.Class.Num - CLaSH.Class.Resize - - CLaSH.Prelude - CLaSH.Prelude.BitIndex - CLaSH.Prelude.BitReduction - CLaSH.Prelude.BlockRam - CLaSH.Prelude.DataFlow - CLaSH.Prelude.Explicit - CLaSH.Prelude.Mealy - CLaSH.Prelude.Moore - CLaSH.Prelude.Testbench - - CLaSH.Promoted.Nat - CLaSH.Promoted.Nat.TH - CLaSH.Promoted.Nat.Literals - CLaSH.Promoted.Ord - CLaSH.Promoted.Symbol - - CLaSH.Signal - CLaSH.Signal.Bundle - CLaSH.Signal.Delayed - CLaSH.Signal.Explicit - CLaSH.Signal.Internal - - CLaSH.Sized.BitVector - CLaSH.Sized.Fixed - CLaSH.Sized.Index - CLaSH.Sized.Signed - CLaSH.Sized.Unsigned - CLaSH.Sized.Vector - - CLaSH.Sized.Internal.BitVector - CLaSH.Sized.Internal.Index - CLaSH.Sized.Internal.Signed - CLaSH.Sized.Internal.Unsigned - - CLaSH.Tutorial - CLaSH.Examples - - other-extensions: BangPatterns - DataKinds - ConstraintKinds - DefaultSignatures - DeriveDataTypeable - FlexibleContexts - GADTs - GeneralizedNewtypeDeriving - KindSignatures - MagicHash - MultiParamTypeClasses - Rank2Types - ScopedTypeVariables - StandaloneDeriving - TemplateHaskell - TupleSections - TypeFamilies - TypeOperators - UndecidableInstances - - Build-depends: base >= 4.8.0.0 && < 5, - data-default >= 0.5.3, - integer-gmp >= 0.5.1.0, - ghc-prim >= 0.3.1.0, - ghc-typelits-natnormalise >= 0.3, - lens >= 4.9, - QuickCheck >= 2.7 && <2.9, - singletons >= 1.0, - template-haskell >= 2.9.0.0, - th-lift >= 0.5.6 - - if flag(doclinks) - CPP-Options: -DDOCLINKS - build-depends: transformers >= 0.4.2.0 - -test-suite doctests - type: exitcode-stdio-1.0 - default-language: Haskell2010 - main-is: doctests.hs - ghc-options: -Wall - hs-source-dirs: tests - - if !flag(doctests) - buildable: False - else - build-depends: - base >= 4 && < 5, - doctest >= 0.9.1 && < 0.11, - Glob >= 0.7 && < 0.8 +Name: clash-prelude+Version: 0.9+Synopsis: CAES Language for Synchronous Hardware - Prelude library+Description:+ 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+ .+ 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".+ Some circuit examples can be found in "CLaSH.Examples".+Homepage: http://www.clash-lang.org/+bug-reports: http://github.com/clash-lang/clash-prelude/issues+License: BSD2+License-file: LICENSE+Author: Christiaan Baaij+Maintainer: Christiaan Baaij <christiaan.baaij@gmail.com>+Copyright: Copyright © 2013-2015 University of Twente+Category: Hardware+Build-type: Simple++Extra-source-files: README.md+ CHANGELOG.md+ AUTHORS.md++extra-doc-files: doc/*.svg++Cabal-version: >=1.10++source-repository head+ type: git+ location: https://github.com/clash-lang/clash-prelude.git++flag doctests+ description:+ You can disable testing with doctests using `-f-doctests`.+ default: True+ manual: True++flag doclinks+ description:+ Create hyperlinks to non-dependent packages using `-fdoclinks`.+ default: False+ manual: True++Library+ HS-Source-Dirs: src++ default-language: Haskell2010+ ghc-options: -Wall -fexpose-all-unfoldings++ Exposed-modules: CLaSH.Annotations.TopEntity++ CLaSH.Class.BitPack+ CLaSH.Class.Num+ CLaSH.Class.Resize++ CLaSH.Prelude+ CLaSH.Prelude.BitIndex+ CLaSH.Prelude.BitReduction+ CLaSH.Prelude.BlockRam+ CLaSH.Prelude.BlockRam.File+ CLaSH.Prelude.DataFlow+ CLaSH.Prelude.Explicit+ CLaSH.Prelude.Explicit.Safe+ CLaSH.Prelude.Mealy+ CLaSH.Prelude.Moore+ CLaSH.Prelude.RAM+ CLaSH.Prelude.ROM+ CLaSH.Prelude.ROM.File+ CLaSH.Prelude.Safe+ CLaSH.Prelude.Synchronizer+ CLaSH.Prelude.Testbench++ CLaSH.Promoted.Nat+ CLaSH.Promoted.Nat.TH+ CLaSH.Promoted.Nat.Literals+ CLaSH.Promoted.Ord+ CLaSH.Promoted.Symbol++ CLaSH.Signal+ CLaSH.Signal.Bundle+ CLaSH.Signal.Delayed+ CLaSH.Signal.Explicit+ CLaSH.Signal.Internal++ CLaSH.Sized.BitVector+ CLaSH.Sized.Fixed+ CLaSH.Sized.Index+ CLaSH.Sized.Signed+ CLaSH.Sized.Unsigned+ CLaSH.Sized.Vector++ CLaSH.Sized.Internal.BitVector+ CLaSH.Sized.Internal.Index+ CLaSH.Sized.Internal.Signed+ CLaSH.Sized.Internal.Unsigned++ CLaSH.Tutorial+ CLaSH.Examples++ other-extensions: BangPatterns+ DataKinds+ ConstraintKinds+ DefaultSignatures+ DeriveDataTypeable+ FlexibleContexts+ GADTs+ GeneralizedNewtypeDeriving+ KindSignatures+ MagicHash+ MultiParamTypeClasses+ Rank2Types+ ScopedTypeVariables+ StandaloneDeriving+ TemplateHaskell+ TupleSections+ TypeFamilies+ TypeOperators+ UndecidableInstances++ Build-depends: array >= 0.5.1.0,+ base >= 4.8.0.0 && < 5,+ data-default >= 0.5.3,+ integer-gmp >= 0.5.1.0,+ ghc-prim >= 0.3.1.0,+ ghc-typelits-natnormalise >= 0.3,+ lens >= 4.9,+ QuickCheck >= 2.7 && <2.9,+ singletons >= 1.0,+ template-haskell >= 2.9.0.0,+ th-lift >= 0.5.6++ if flag(doclinks)+ CPP-Options: -DDOCLINKS+ build-depends: transformers >= 0.4.2.0++test-suite doctests+ type: exitcode-stdio-1.0+ default-language: Haskell2010+ main-is: doctests.hs+ ghc-options: -Wall+ hs-source-dirs: tests++ if !flag(doctests)+ buildable: False+ else+ build-depends:+ base >= 4 && < 5,+ doctest >= 0.9.1 && < 0.11,+ Glob >= 0.7 && < 0.8
src/CLaSH/Annotations/TopEntity.hs view
@@ -89,7 +89,7 @@ , t_extraIn = [ (\"CLOCK_50\", 1) , (\"KEY0\" , 1) ]- , t_clocks = [ 'defClkAltera' "altpll50" "CLOCK_50(0)" "not KEY0(0)" ]+ , t_clocks = [ 'altpll' "altpll50" "CLOCK_50(0)" "not KEY0(0)" ] }) \#-\} @ @@ -173,8 +173,11 @@ , ClockSource (..) -- * Convenience functions , defTop- , defClkAltera- , defClkXilinx+ -- ** Altera clock sources+ , altpll+ , alteraPll+ -- ** Xilinx clock sources+ , clockWizard ) where @@ -196,22 +199,18 @@ -- -- So given a bit-width @n@, the port has type: --- -- @std_logic_vector (n-1 downto 0)@ in VHDL- --- -- and- --- -- @logic [n-1:0]@ in (System)Verilog.+ -- * __VHDL__: @std_logic_vector (n-1 downto 0)@+ -- * __Verilog__: @[n-1:0]@+ -- * __SystemVerilog__: @logic [n-1:0]@ , t_extraOut :: [(String,Int)] -- ^ Extra output ports, where every tuple holds the name of the input port -- and the number of bits are used for that input port. -- -- So given a bit-width @n@, the port has type: --- -- @std_logic_vector (n-1 downto 0)@ in VHDL- --- -- and- --- -- @logic [n-1:0]@ in (System)Verilog.+ -- * __VHDL__: @std_logic_vector (n-1 downto 0)@+ -- * __Verilog__: @[n-1:0]@+ -- * __SystemVerilog__: @logic [n-1:0]@ , t_clocks :: [ClockSource] -- ^ List of clock sources } deriving (Data,Show)@@ -275,10 +274,10 @@ , t_clocks = [] } --- | A clock source that corresponds to the Altera PLL component with default--- settings to provide a stable 'systemClock'.+-- | A clock source that corresponds to the Altera/Quartus \"ALTPLL\" component+-- with default settings to provide a stable 'systemClock'. ----- >>> defClkAltera "altpll50" "CLOCK(0)" "not KEY(0)"+-- >>> altpll "altpll50" "CLOCK(0)" "not KEY(0)" -- ClockSource {c_name = "altpll50", c_inp = Just ("inclk0","CLOCK(0)"), c_outp = [("c0","system1000")], c_reset = Just ("areset","not KEY(0)"), c_lock = "locked", c_sync = False} -- -- Will generate the following VHDL:@@ -292,7 +291,7 @@ -- -- If you are however generating (System)Verilog you should write: ----- >>> defClkAltera "altpll50" "CLOCK[0]" "~ KEY[0]"+-- >>> altpll "altpll50" "CLOCK[0]" "~ KEY[0]" -- ClockSource {c_name = "altpll50", c_inp = Just ("inclk0","CLOCK[0]"), c_outp = [("c0","system1000")], c_reset = Just ("areset","~ KEY[0]"), c_lock = "locked", c_sync = False} -- -- so that the following (System)Verilog is created:@@ -302,11 +301,11 @@ -- > ,.c0 (system1000) -- > ,.areset (~ KEY0[0]) -- > ,.locked (altpll50_locked));-defClkAltera :: String -- ^ Name of the component.- -> String -- ^ Clock Pin/Expression of the free running clock.- -> String -- ^ Reset Pin/Expression controlling the reset of the PLL.- -> ClockSource-defClkAltera pllName clkExpr resExpr = ClockSource+altpll :: String -- ^ Name of the component.+ -> String -- ^ Clock Pin/Expression of the free running clock.+ -> String -- ^ Reset Pin/Expression controlling the reset of the PLL.+ -> ClockSource+altpll pllName clkExpr resExpr = ClockSource { c_name = pllName , c_inp = Just ("inclk0",clkExpr) , c_outp = [("c0",show systemClock)]@@ -315,10 +314,50 @@ , c_sync = False } --- | A clock source that corresponds to the Xilinx PLL/MMCM component with--- settings to provide a stable 'systemClock'.+-- | A clock source that corresponds to the Altera \"Altera PLL\" component+-- with default settings to provide a stable 'systemClock'. ----- >>> defClkXilinx "clkwiz50" "CLOCK(0)" "not KEY(0)"+-- >>> alteraPll "alteraPll50" "CLOCK(0)" "not KEY(0)"+-- ClockSource {c_name = "alteraPll50", c_inp = Just ("refclk","CLOCK(0)"), c_outp = [("outclk_0","system1000")], c_reset = Just ("rst","not KEY(0)"), c_lock = "locked", c_sync = False}+--+-- Will generate the following VHDL:+--+-- > alteraPll50_inst : entity alteraPll+-- > port map+-- > (refclk => CLOCK_50(0)+-- > ,outclk_0 => system1000+-- > ,rst => not KEY0(0)+-- > ,locked => alteraPll50_locked);+--+-- If you are however generating (System)Verilog you should write:+--+-- >>> alteraPll "alteraPll50" "CLOCK[0]" "~ KEY[0]"+-- ClockSource {c_name = "alteraPll50", c_inp = Just ("refclk","CLOCK[0]"), c_outp = [("outclk_0","system1000")], c_reset = Just ("rst","~ KEY[0]"), c_lock = "locked", c_sync = False}+--+-- so that the following (System)Verilog is created:+--+-- > alteraPll50 alteraPll50_inst+-- > (.refclk (CLOCK_50[0])+-- > ,.outclk_0 (system1000)+-- > ,.rst (~ KEY0[0])+-- > ,.locked (alteraPll50_locked));+alteraPll :: String -- ^ Name of the component.+ -> String -- ^ Clock Pin/Expression of the free running clock.+ -> String -- ^ Reset Pin/Expression controlling the reset of the PLL.+ -> ClockSource+alteraPll pllName clkExpr resExpr = ClockSource+ { c_name = pllName+ , c_inp = Just ("refclk",clkExpr)+ , c_outp = [("outclk_0",show systemClock)]+ , c_reset = Just ("rst",resExpr)+ , c_lock = "locked"+ , c_sync = False+ }++-- | A clock source that corresponds to the Xilinx PLL/MMCM component created+-- with the \"Clock Wizard\", with settings to provide a stable 'systemClock'.+--+-- >>> clockWizard "clkwiz50" "CLOCK(0)" "not KEY(0)" -- ClockSource {c_name = "clkwiz50", c_inp = Just ("CLK_IN1","CLOCK(0)"), c_outp = [("CLK_OUT1","system1000")], c_reset = Just ("RESET","not KEY(0)"), c_lock = "LOCKED", c_sync = False} -- -- Will generate the following VHDL:@@ -332,7 +371,7 @@ -- -- If you are however generating (System)Verilog you should write: ----- >>> defClkXilinx "clkwiz50" "CLOCK[0]" "~ KEY[0]"+-- >>> clockWizard "clkwiz50" "CLOCK[0]" "~ KEY[0]" -- ClockSource {c_name = "clkwiz50", c_inp = Just ("CLK_IN1","CLOCK[0]"), c_outp = [("CLK_OUT1","system1000")], c_reset = Just ("RESET","~ KEY[0]"), c_lock = "LOCKED", c_sync = False} -- -- so that the following (System)Verilog is created:@@ -342,11 +381,11 @@ -- > ,.CLK_OUT1 (system1000) -- > ,.RESET (~ KEY0[0]) -- > ,.LOCKED (altpll50_locked));-defClkXilinx :: String -- ^ Name of the component.- -> String -- ^ Clock Pin/Expression of the free running clock.- -> String -- ^ Reset Pin/Expression controlling the reset of the PLL.- -> ClockSource-defClkXilinx pllName clkExpr resExpr = ClockSource+clockWizard :: String -- ^ Name of the component.+ -> String -- ^ Clock Pin/Expression of the free running clock.+ -> String -- ^ Reset Pin/Expression controlling the reset of the PLL.+ -> ClockSource+clockWizard pllName clkExpr resExpr = ClockSource { c_name = pllName , c_inp = Just ("CLK_IN1",clkExpr) , c_outp = [("CLK_OUT1",show systemClock)]
src/CLaSH/Prelude.hs view
@@ -40,9 +40,25 @@ , moore , mooreB , registerB+ -- * ROMs+ , asyncRom+ , asyncRomPow2+ , rom+ , romPow2+ -- ** ROMs initialised with a data file+ , asyncRomFile+ , asyncRomFilePow2+ , romFile+ , romFilePow2+ -- * RAM primitives with a combinational read port+ , asyncRam+ , asyncRamPow2 -- * BlockRAM primitives , blockRam , blockRamPow2+ -- ** BlockRAM primitives initialised with a data file+ , blockRamFile+ , blockRamFilePow2 -- * Utility functions , window , windowD@@ -115,12 +131,12 @@ import CLaSH.Class.Resize import CLaSH.Prelude.BitIndex import CLaSH.Prelude.BitReduction-import CLaSH.Prelude.BlockRam (blockRam, blockRamPow2)-import CLaSH.Prelude.Explicit (registerB', window', windowD', isRising',- isFalling')-import CLaSH.Prelude.Mealy (mealy, mealyB, (<^>))-import CLaSH.Prelude.Moore (moore, mooreB)+import CLaSH.Prelude.BlockRam.File (blockRamFile, blockRamFilePow2) import CLaSH.Prelude.DataFlow+import CLaSH.Prelude.Explicit (window', windowD')+import CLaSH.Prelude.ROM.File (asyncRomFile,asyncRomFilePow2,romFile,+ romFilePow2)+import CLaSH.Prelude.Safe import CLaSH.Prelude.Testbench (assert, stimuliGenerator, outputVerifier) import CLaSH.Promoted.Nat import CLaSH.Promoted.Nat.TH@@ -177,30 +193,3 @@ => Signal a -- ^ Signal to create a window over -> Vec (n + 1) (Signal a) -- ^ Window of at least size 1 windowD = windowD' systemClock--{-# INLINE registerB #-}--- | Create a 'register' function for product-type like signals (e.g. '(Signal a, Signal b)')------ > rP :: (Signal Int,Signal Int) -> (Signal Int, Signal Int)--- > rP = registerB (8,8)------ >>> simulateB rP [(1,1),(2,2),(3,3)] :: [(Int,Int)]--- [(8,8),(1,1),(2,2),(3,3)...-registerB :: Bundle a => a -> Unbundled a -> Unbundled a-registerB = registerB' systemClock--{-# INLINE isRising #-}--- | Give a pulse when the 'Signal' goes from 'minBound' to 'maxBound'-isRising :: (Bounded a, Eq a)- => a -- ^ Starting value- -> Signal a- -> Signal Bool-isRising = isRising' systemClock--{-# INLINE isFalling #-}--- | Give a pulse when the 'Signal' goes from 'maxBound' to 'minBound'-isFalling :: (Bounded a, Eq a)- => a -- ^ Starting value- -> Signal a- -> Signal Bool-isFalling = isFalling' systemClock
src/CLaSH/Prelude/BitIndex.hs view
@@ -39,8 +39,8 @@ -- 0 -- >>> (7 :: Unsigned 6) ! 6 -- *** Exception: (!): 6 is out of range [5..0]-(!) :: (BitPack a, KnownNat (BitSize a), Integral i) => a -> i -> Bit-(!) v i = index# (pack v) (fromIntegral i)+(!) :: (BitPack a, KnownNat (BitSize a), Enum i) => a -> i -> Bit+(!) v i = index# (pack v) (fromEnum i) {-# INLINE slice #-} -- | Get a slice between bit index @m@ and and bit index @n@.@@ -93,9 +93,9 @@ -- 01_1011 -- >>> replaceBit 6 0 (-5 :: Signed 6) -- *** Exception: replaceBit: 6 is out of range [5..0]-replaceBit :: (BitPack a, KnownNat (BitSize a), Integral i) => i -> Bit -> a+replaceBit :: (BitPack a, KnownNat (BitSize a), Enum i) => i -> Bit -> a -> a-replaceBit i b v = unpack (replaceBit# (pack v) (fromIntegral i) b)+replaceBit i b v = unpack (replaceBit# (pack v) (fromEnum i) b) {-# INLINE setSlice #-} -- | Set the bits between bit index @m@ and bit index @n@.
src/CLaSH/Prelude/BlockRam.hs view
@@ -1,25 +1,371 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MagicHash #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE Safe #-} +{-# OPTIONS_HADDOCK show-extensions #-}+ {-| Copyright : (C) 2013-2015, University of Twente License : BSD2 (see the file LICENSE) Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>++BlockRAM primitives++= Using RAMs #usingrams#++We will show a rather elaborate example on how you can, and why you might want+to use 'blockRam's. We will build a \"small\" CPU+Memory+Program ROM where we+will slowly evolve to using blockRams. Note that the code is /not/ meant as a+de-facto standard on how to do CPU design in CλaSH.++We start with the definition of the Instructions, Register names and machine+codes:++@+{\-\# LANGUAGE RecordWildCards \#-\}+module CPU where++import CLaSH.Prelude+import qualified Data.List as L++type InstrAddr = Unsigned 8+type MemAddr = Unsigned 5+type Value = Signed 8++data Instruction+ = Compute Operator Reg Reg Reg+ | Branch Reg Value+ | Jump Value+ | Load MemAddr Reg+ | Store Reg MemAddr+ | Nop+ deriving (Eq,Show)++data Reg+ = Zero+ | PC+ | RegA+ | RegB+ | RegC+ | RegD+ | RegE+ deriving (Eq,Show,Enum)++data Operator = Add | Sub | Incr | Imm | CmpGt+ deriving (Eq,Show)++data MachCode+ = MachCode+ { inputX :: Reg+ , inputY :: Reg+ , result :: Reg+ , aluCode :: Operator+ , ldReg :: Reg+ , rdAddr :: MemAddr+ , wrAddr :: MemAddr+ , wrEn :: Bool+ , jmpM :: Maybe Value+ }++nullCode = MachCode { inputX = Zero, inputY = Zero, result = Zero, aluCode = Imm+ , ldReg = Zero, wrAddr = 0, rdAddr = 0, wrEn = False+ , jmpM = Nothing+ }+@++Next we define the CPU and its ALU:++@+cpu :: Vec 7 Value -- ^ Register bank+ -> (Value,Instruction) -- ^ (Memory output, Current instruction)+ -> ( Vec 7 Value+ , (MemAddr,MemAddr,Bool,Value,InstrAddr)+ )+cpu regbank (memOut,instr) = ((regbank',(rdAddr,wrAddr,wrEn,aluOut,fromIntegral ipntr))+ where+ -- Current instruction pointer+ ipntr = regbank '!!' PC++ -- Decoder+ (MachCode {..}) = case instr of+ Compute op rx ry res -> nullCode {inputX=rx,inputY=ry,result=res,aluCode=op}+ Branch cr a -> nullCode {inputX=cr,jmpM=Just a}+ Jump a -> nullCode {aluCode=Incr,jmpM=Just a}+ Load a r -> nullCode {ldReg=r,rdAddr=a}+ Store r a -> nullCode {inputX=r,wrAddr=a,wrEn=True}+ Nop -> nullCode++ -- ALU+ regX = regbank '!!' inputX+ regY = regbank '!!' inputY+ aluOut = alu aluCode regX regY++ -- next instruction+ nextPC = case jmpM of+ Just a | aluOut /= 0 -> ipntr + a+ _ -> ipntr + 1++ -- update registers+ regbank' = 'replace' Zero 0+ $ 'replace' PC nextPC+ $ 'replace' result aluOut+ $ 'replace' ldReg memOut+ $ regbank++alu Add x y = x + y+alu Sub x y = x - y+alu Incr x _ = x + 1+alu Imm x _ = x+alu CmpGt x y = if x > y then 1 else 0+@++We initially create a memory out of simple registers:++@+dataMem :: Signal MemAddr -- ^ Read address+ -> Signal MemAddr -- ^ Write address+ -> Signal Bool -- ^ Write enable+ -> Signal Value -- ^ data in+ -> Signal Value -- ^ data out+dataMem wr rd en din = 'CLaSH.Prelude.Mealy.mealy' dataMemT ('replicate' d32 0) (bundle (wr,rd,en,din))+ where+ dataMemT mem (wr,rd,en,din) = (mem',dout)+ where+ dout = mem '!!' rd+ mem' | en = 'replace' wr din mem+ | otherwise = mem+@++And then connect everything:++@+system :: KnownNat n => Vec n Instruction -> Signal Value+system instrs = memOut+ where+ memOut = dataMem wrAddr rdAddr wrEn aluOut+ (rdAddr,wrAddr,wrEn,aluOut,ipntr) = 'CLaSH.Prelude.Mealy.mealyB' cpu (('replicate' d7 0),Zero) (memOut,instr)+ instr = 'CLaSH.Prelude.ROM.asyncRom' instrs '<$>' ipntr+@++Create a simple program that calculates the GCD of 4 and 6:++@+-- Compute GCD of 4 and 6+prog = -- 0 := 4+ Compute Incr Zero RegA RegA :>+ replicate d3 (Compute Incr RegA Zero RegA) +++ Store RegA 0 :>+ -- 1 := 6+ Compute Incr Zero RegA RegA :>+ replicate d5 (Compute Incr RegA Zero RegA) +++ Store RegA 1 :>+ -- A := 4+ Load 0 RegA :>+ -- B := 6+ Load 1 RegB :>+ -- start+ Compute CmpGt RegA RegB RegC :>+ Branch RegC 4 :>+ Compute CmpGt RegB RegA RegC :>+ Branch RegC 4 :>+ Jump 5 :>+ -- (a > b)+ Compute Sub RegA RegB RegA :>+ Jump (-6) :>+ -- (b > a)+ Compute Sub RegB RegA RegB :>+ Jump (-8) :>+ -- end+ Store RegA 2 :>+ Load 2 RegC :>+ Nil+@++And test our system:++@+__>>> L.take 31 $ sample $ system prog__+[0,0,0,0,0,4,4,4,4,4,4,4,4,4,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2]+@++to see that our system indeed calculates that the GCD of 6 and 4 is 2.++=== Improvement 1: using @asyncRam@++As you can see, it's fairly straightforward to build a memory using registers+and read ('!!') and write ('replace') logic. This might however not result in+the most efficient hardware structure, especially when building an ASIC.++Instead it is preferable to use the 'CLaSH.Prelude.RAM.asyncRam' function which+has the potential to be translated to a more efficient structure:++@+system2 :: KnownNat n => Vec n Instruction -> Signal Value+system2 instrs = memOut+ where+ memOut = 'CLaSH.Prelude.RAM.asyncRam' d32 wrAddr rdAddr wrEn aluOut+ (rdAddr,wrAddr,wrEn,aluOut,ipntr) = 'mealyB' cpu (('replicate' d7 0),Zero) (memOut,instr)+ instr = 'CLaSH.Prelude.ROM.asyncRom' instrs '<$>' ipntr+@++Again, we can simulate our system and see that it works. This time however,+we need to drop the first few output samples, because the initial content of an+'CLaSH.Prelude.RAM.asyncRam' is 'undefined', and consequently, the first few+output samples are also 'undefined'.++@+__>>> L.take 26 $ L.drop 5 $ sample $ system2 prog__+[4,4,4,4,4,4,4,4,4,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2]+@++=== Improvement 2: using @blockRam@++Finally we get to using 'blockRam'. On FPGAs, 'CLaSH.Prelude.RAM.asyncRam' will+be implemented in terms of LUTs, and therefore take up logic resources. FPGAs+also have large(r) memory structures called /Block RAMs/, which are preferred,+especially as the memories we need for our application get bigger. The+'blockRam' function will be translated to such a /Block RAM/.++One important aspect of Block RAMs have a /synchronous/ read port, meaning that,+unlike the behaviour of 'CLaSH.Prelude.RAM.asyncRam', given a read address @r@+at time @t@, the value @v@ in the RAM at address @r@ is only available at time+@t+1@.++For us that means we need to change the design of our CPU. Right now, upon a+load instruction we generate a read address for the memory, and the value at+that read address is immediately available to be put in the register bank.+Because we will be using a BlockRAM, the value is delayed until the next cycle.+We hence need to also delay the register address to which the memory address+is loaded:++@+cpu2 :: (Vec 7 Value,Reg) -- ^ (Register bank, Load reg addr)+ -> (Value,Instruction) -- ^ (Memory output, Current instruction)+ -> ( (Vec 7 Value,Reg)+ , (MemAddr,MemAddr,Bool,Value,InstrAddr)+ )+cpu2 (regbank,ldRegD) (memOut,instr) = (((regbank',ldRegD'),(rdAddr,wrAddr,wrEn,aluOut,fromIntegral ipntr))+ where+ -- Current instruction pointer+ ipntr = regbank '!!' PC++ -- Decoder+ (MachCode {..}) = case instr of+ Compute op rx ry res -> nullCode {inputX=rx,inputY=ry,result=res,aluCode=op}+ Branch cr a -> nullCode {inputX=cr,jmpM=Just a}+ Jump a -> nullCode {aluCode=Incr,jmpM=Just a}+ Load a r -> nullCode {ldReg=r,rdAddr=a}+ Store r a -> nullCode {inputX=r,wrAddr=a,wrEn=True}+ Nop -> nullCode++ -- ALU+ regX = regbank '!!' inputX+ regY = regbank '!!' inputY+ aluOut = alu aluCode regX regY++ -- next instruction+ nextPC = case jmpM of+ Just a | aluOut /= 0 -> ipntr + a+ _ -> ipntr + 1++ -- update registers+ ldRegD' = ldReg -- Delay the ldReg by 1 cycle+ regbank' = 'replace' Zero 0+ $ 'replace' PC nextPC+ $ 'replace' result aluOut+ $ 'replace' ldRegD memOut+ $ regbank+@++We can now finally instantiate our system with a 'blockRam':++@+system3 :: KnownNat n => Vec n Instruction -> Signal Value+system3 instrs = memOut+ where+ memOut = 'blockRam' (replicate d32 0) wrAddr rdAddr wrEn aluOut+ (rdAddr,wrAddr,wrEn,aluOut,ipntr) = 'mealyB' cpu (('replicate' d7 0),Zero) (memOut,instr)+ instr = 'CLaSH.Prelude.ROM.asyncRom' instrs '<$>' ipntr+@++We are, however, not done. We will also need to update our program. The reason+being that values that we try to load in our registers won't be loaded into the+register until the next cycle. This is a problem when the next instruction+immediately depended on this memory value. In our case, this was only the case+when the loaded the value @6@, which was stored at address @1@, into @RegB@.+Our updated program is thus:++@+prog2 = -- 0 := 4+ Compute Incr Zero RegA RegA :>+ replicate d3 (Compute Incr RegA Zero RegA) +++ Store RegA 0 :>+ -- 1 := 6+ Compute Incr Zero RegA RegA :>+ replicate d5 (Compute Incr RegA Zero RegA) +++ Store RegA 1 :>+ -- A := 4+ Load 0 RegA :>+ -- B := 6+ Load 1 RegB :>+ Nop :> -- Extra NOP+ -- start+ Compute CmpGt RegA RegB RegC :>+ Branch RegC 4 :>+ Compute CmpGt RegB RegA RegC :>+ Branch RegC 4 :>+ Jump 5 :>+ -- (a > b)+ Compute Sub RegA RegB RegA :>+ Jump (-6) :>+ -- (b > a)+ Compute Sub RegB RegA RegB :>+ Jump (-8) :>+ -- end+ Store RegA 2 :>+ Load 2 RegC :>+ Nil+@++When we simulate our system we see that it works. This time however,+we need to drop the first few sample, because the initial output of a+'blockRam' is 'undefined', and consequently, the first output sample is+also 'undefined'.++@+__>>> L.take 33 $ L.tail $ sample $ system3 prog2__+[4,4,4,4,4,4,4,4,4,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2]+@++This concludes the short introduction to using 'blockRam'.+ -}-module CLaSH.Prelude.BlockRam where+module CLaSH.Prelude.BlockRam+ ( -- * BlockRAM synchronised to the system clock+ blockRam+ , blockRamPow2+ -- * BlockRAM synchronised to an arbitrary clock+ , blockRam'+ , blockRamPow2'+ -- * Internal+ , blockRam#+ )+where +import Control.Monad (when)+import Control.Monad.ST.Lazy (ST,runST)+import Data.Array.MArray.Safe (newListArray,readArray,writeArray)+import Data.Array.ST.Safe (STArray) import GHC.TypeLits (KnownNat, type (^))-import Prelude hiding ((!!)) -import CLaSH.Prelude.Moore (moore') import CLaSH.Signal (Signal)-import CLaSH.Signal.Explicit (Signal', SClock, systemClock)+import CLaSH.Signal.Explicit (Signal', SClock, register', systemClock) import CLaSH.Signal.Bundle (bundle') import CLaSH.Sized.Unsigned (Unsigned)-import CLaSH.Sized.Vector (Vec, (!!), replace)+import CLaSH.Sized.Vector (Vec, maxIndex, toList) {-# INLINE blockRam #-} -- | Create a blockRAM with space for @n@ elements.@@ -32,15 +378,20 @@ -- -> 'Signal' 'CLaSH.Sized.BitVector.Bit' -> Signal 'CLaSH.Sized.BitVector.Bit' -- bram40 = 'blockRam' ('CLaSH.Sized.Vector.replicate' d40 1) -- @-blockRam :: (KnownNat n, KnownNat m)- => Vec n a -- ^ Initial content of the BRAM, also- -- determines the size, @n@, of the BRAM.- --- -- __NB__: __MUST__ be a constant.- -> Signal (Unsigned m) -- ^ Write address @w@- -> Signal (Unsigned m) -- ^ Read address @r@- -> Signal Bool -- ^ Write enable- -> Signal a -- ^ Value to write (at address @w@)+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- Block RAM.+blockRam :: (KnownNat n, Enum addr)+ => Vec n a -- ^ Initial content of the BRAM, also+ -- determines the size, @n@, of the BRAM.+ --+ -- __NB__: __MUST__ be a constant.+ -> Signal addr -- ^ Write address @w@+ -> Signal addr -- ^ 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@@ -57,6 +408,11 @@ -- -> 'Signal' 'CLaSH.Sized.BitVector.Bit' -> 'Signal' 'CLaSH.Sized.BitVector.Bit' -- bram32 = 'blockRamPow2' ('CLaSH.Sized.Vector.replicate' d32 1) -- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- Block RAM. blockRamPow2 :: (KnownNat (2^n), KnownNat n) => Vec (2^n) a -- ^ Initial content of the BRAM, also -- determines the size, @2^n@, of the BRAM.@@ -71,7 +427,7 @@ -- cycle blockRamPow2 = blockRam -{-# NOINLINE blockRam' #-}+{-# INLINE blockRam' #-} -- | Create a blockRAM with space for @n@ elements -- -- * __NB__: Read value is delayed by 1 cycle@@ -87,27 +443,26 @@ -- -> 'Signal'' ClkA Bool -> 'Signal'' ClkA 'CLaSH.Sized.BitVector.Bit' -> ClkA 'Signal'' 'CLaSH.Sized.BitVector.Bit' -- bram40 = 'blockRam'' clkA100 ('CLaSH.Sized.Vector.replicate' d40 1) -- @-blockRam' :: (KnownNat n, KnownNat m)- => SClock clk -- ^ 'Clock' to synchronize to- -> Vec n a -- ^ Initial content of the BRAM, also- -- determines the size, @n@, of the BRAM.- --- -- __NB__: __MUST__ be a constant.- -> Signal' clk (Unsigned m) -- ^ Write address @w@- -> Signal' clk (Unsigned m) -- ^ Read address @r@- -> Signal' clk Bool -- ^ Write enable- -> Signal' clk a -- ^ Value to write (at address @w@)+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- Block RAM.+blockRam' :: (KnownNat n, Enum addr)+ => SClock clk -- ^ 'Clock' to synchronize to+ -> Vec n a -- ^ Initial content of the BRAM, also+ -- determines the size, @n@, of the BRAM.+ --+ -- __NB__: __MUST__ be a constant.+ -> Signal' clk addr -- ^ Write address @w@+ -> Signal' clk addr -- ^ Read address @r@+ -> Signal' clk Bool -- ^ Write enable+ -> Signal' clk a -- ^ Value to write (at address @w@) -> Signal' clk a -- ^ Value of the @blockRAM@ at address @r@ from the previous clock -- cycle-blockRam' clk binit wr rd en din =- moore' clk bram' snd (binit,undefined) (bundle' clk (wr,rd,en,din))- where- bram' (ram,_) (w,r,e,d) = (ram',o')- where- ram' | e = replace w d ram- | otherwise = ram- o' = ram !! r+blockRam' clk content wr rd en din = blockRam# clk content (fromEnum <$> wr)+ (fromEnum <$> rd) en din {-# INLINE blockRamPow2' #-} -- | Create a blockRAM with space for 2^@n@ elements@@ -125,6 +480,11 @@ -- -> 'Signal'' ClkA Bool -> 'Signal'' ClkA 'CLaSH.Sized.BitVector.Bit' -> Signal' ClkA 'CLaSH.Sized.BitVector.Bit' -- bram32 = 'blockRamPow2'' clkA100 ('CLaSH.Sized.Vector.replicate' d32 1) -- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- Block RAM. blockRamPow2' :: (KnownNat n, KnownNat (2^n)) => SClock clk -- ^ 'Clock' to synchronize to -> Vec (2^n) a -- ^ Initial content of the BRAM, also@@ -140,3 +500,31 @@ -- ^ Value of the @blockRAM@ at address @r@ from the previous -- clock cycle blockRamPow2' = blockRam'++{-# NOINLINE blockRam# #-}+-- | blockRAM primitive+blockRam# :: KnownNat n+ => SClock clk -- ^ 'Clock' to synchronize to+ -> Vec n a -- ^ Initial content of the BRAM, also+ -- determines the size, @n@, of the BRAM.+ --+ -- __NB__: __MUST__ be a constant.+ -> Signal' clk Int -- ^ Write address @w@+ -> Signal' clk Int -- ^ Read address @r@+ -> Signal' clk Bool -- ^ Write enable+ -> Signal' clk a -- ^ Value to write (at address @w@)+ -> Signal' clk a+ -- ^ Value of the @blockRAM@ at address @r@ from the previous clock+ -- cycle+blockRam# clk content wr rd en din = register' clk undefined dout+ where+ szI = fromInteger $ maxIndex content+ dout = runST $ do+ arr <- newListArray (0,szI) (toList content)+ traverse (ramT arr) (bundle' clk (wr,rd,en,din))++ ramT :: STArray s Int e -> (Int,Int,Bool,e) -> ST s e+ ramT ram (w,r,e,d) = do+ d' <- readArray ram r+ when e (writeArray ram w d)+ return d'
+ src/CLaSH/Prelude/BlockRam/File.hs view
@@ -0,0 +1,300 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}++{-# LANGUAGE Unsafe #-}++{-# OPTIONS_HADDOCK show-extensions #-}++{-|+Copyright : (C) 2015, University of Twente+License : BSD2 (see the file LICENSE)+Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>++= Initialising a BlockRAM with a data file #usingramfiles#++BlockRAM primitives that can be initialised with a data file. The BNF grammar+for this data file is simple:++@+FILE = LINE++LINE = BIT++BIT = '0'+ | '1'+@++Consecutive @LINE@s correspond to consecutive memory addresses starting at @0@.+For example, a data file @memory.bin@ containing the 9-bit unsigned number+@7@ to @13@ looks like:++@+000000111+000001000+000001001+000001010+000001011+000001100+000001101+@++We can instantiate a BlockRAM using the content of the above file like so:++@+topEntity :: Signal (Unsigned 3) -> Signal (Unsigned 9)+topEntity rd = 'CLaSH.Class.BitPack.unpack' '<$>' 'blockRamFile' d7 \"memory.bin\" 0 rd (signal False) 0+@++In the example above, we basically treat the BlockRAM as an synchronous ROM.+We can see that it works as expected:++@+__>>> import qualified Data.List as L__+__>>> L.tail $ sampleN 4 $ topEntity (fromList [3..5])__+[10,11,12]+@++However, we can also interpret the same data as a tuple of a 6-bit unsigned+number, and a 3-bit signed number:++@+topEntity2 :: Signal (Unsigned 3) -> Signal (Unsigned 6,Signed 3)+topEntity2 rd = 'CLaSH.Class.BitPack.unpack' '<$>' 'blockRamFile' d7 \"memory.bin\" 0 rd (signal False) 0+@++And then we would see:++@+__>>> import qualified Data.List as L__+__>>> L.tail $ sampleN 4 $ topEntity2 (fromList [3..5])__+[(1,2),(1,3)(1,-4)]+@++-}+module CLaSH.Prelude.BlockRam.File+ ( -- * BlockRAM synchronised to the system clock+ blockRamFile+ , blockRamFilePow2+ -- * BlockRAM synchronised to an arbitrary clock+ , blockRamFile'+ , blockRamFilePow2'+ -- * Internal+ , blockRamFile#+ , initMem+ )+where+++import Control.Monad (when)+import Control.Monad.ST.Lazy (ST,runST)+import Data.Array.MArray (newListArray,readArray,writeArray)+import Data.Array.ST (STArray)+import Data.Char (digitToInt)+import Data.Maybe (listToMaybe)+import GHC.TypeLits (KnownNat, type (^))+import Numeric (readInt)+import System.IO.Unsafe (unsafePerformIO)++import CLaSH.Promoted.Nat (SNat,snat,snatToInteger)+import CLaSH.Sized.BitVector (BitVector)+import CLaSH.Signal (Signal)+import CLaSH.Signal.Explicit (Signal', SClock, register', systemClock)+import CLaSH.Signal.Bundle (bundle')+import CLaSH.Sized.Unsigned (Unsigned)++{-# INLINE blockRamFile #-}+-- | Create a blockRAM with space for @n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+-- * __NB__: This function might not work for specific combinations of+-- code-generation backends and hardware targets. Please check the support table+-- below:+--+-- @+-- | VHDL | Verilog | SystemVerilog |+-- ===============+==========+=========================+===============++-- Altera/Quartus | Broken | No Verilog-2005 support | Works |+-- Xilinx/ISE | Works | Works | Works |+-- ASIC | Untested | Untested | Untested |+-- ===============+==========+=========================+===============++-- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- Block RAM.+-- * See "CLaSH.Prelude.BlockRam.File#usingramfiles" for more information on how+-- to instantiate a Block RAM with the contents of a data file.+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" for ideas on how to create your+-- own data files.+blockRamFile :: (KnownNat m, Enum addr)+ => SNat n -- ^ Size of the blockRAM+ -> FilePath -- ^ File describing the initial content+ -- of the blockRAM+ -> Signal addr -- ^ Write address @w@+ -> Signal addr -- ^ Read address @r@+ -> Signal Bool -- ^ Write enable+ -> Signal (BitVector m) -- ^ Value to write (at address @w@)+ -> Signal (BitVector m)+ -- ^ Value of the @blockRAM@ at address @r@ from the previous clock+ -- cycle+blockRamFile = blockRamFile' systemClock++{-# INLINE blockRamFilePow2 #-}+-- | Create a blockRAM with space for 2^@n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+-- * __NB__: This function might not work for specific combinations of+-- code-generation backends and hardware targets. Please check the support table+-- below:+--+-- @+-- | VHDL | Verilog | SystemVerilog |+-- ===============+==========+=========================+===============++-- Altera/Quartus | Broken | No Verilog-2005 support | Works |+-- Xilinx/ISE | Works | Works | Works |+-- ASIC | Untested | Untested | Untested |+-- ===============+==========+=========================+===============++-- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- Block RAM.+-- * See "CLaSH.Prelude.BlockRam.File#usingramfiles" for more information on how+-- to instantiate a Block RAM with the contents of a data file.+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" for ideas on how to create your+-- own data files.+blockRamFilePow2 :: forall n m . (KnownNat m, KnownNat n, KnownNat (2^n))+ => FilePath -- ^ File describing the initial+ -- content of the blockRAM+ -> Signal (Unsigned n) -- ^ Write address @w@+ -> Signal (Unsigned n) -- ^ Read address @r@+ -> Signal Bool -- ^ Write enable+ -> Signal (BitVector m) -- ^ Value to write (at address @w@)+ -> Signal (BitVector m)+ -- ^ Value of the @blockRAM@ at address @r@ from the previous+ -- clock cycle+blockRamFilePow2 = blockRamFile' systemClock (snat :: SNat (2^n))++{-# INLINE blockRamFilePow2' #-}+-- | Create a blockRAM with space for 2^@n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+-- * __NB__: This function might not work for specific combinations of+-- code-generation backends and hardware targets. Please check the support table+-- below:+--+-- @+-- | VHDL | Verilog | SystemVerilog |+-- ===============+==========+=========================+===============++-- Altera/Quartus | Broken | No Verilog-2005 support | Works |+-- Xilinx/ISE | Works | Works | Works |+-- ASIC | Untested | Untested | Untested |+-- ===============+==========+=========================+===============++-- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- Block RAM.+-- * See "CLaSH.Prelude.BlockRam.File#usingramfiles" for more information on how+-- to instantiate a Block RAM with the contents of a data file.+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" for ideas on how to create your+-- own data files.+blockRamFilePow2' :: forall clk n m . (KnownNat m, KnownNat n, KnownNat (2^n))+ => SClock clk -- ^ 'Clock' to synchronize to+ -> FilePath -- ^ File describing the initial+ -- content of the blockRAM+ -> Signal' clk (Unsigned n) -- ^ Write address @w@+ -> Signal' clk (Unsigned n) -- ^ Read address @r@+ -> Signal' clk Bool -- ^ Write enable+ -> Signal' clk (BitVector m) -- ^ Value to write (at address @w@)+ -> Signal' clk (BitVector m)+ -- ^ Value of the @blockRAM@ at address @r@ from the previous+ -- clock cycle+blockRamFilePow2' clk = blockRamFile' clk (snat :: SNat (2^n))++{-# INLINE blockRamFile' #-}+-- | Create a blockRAM with space for @n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+-- * __NB__: This function might not work for specific combinations of+-- code-generation backends and hardware targets. Please check the support table+-- below:+--+-- @+-- | VHDL | Verilog | SystemVerilog |+-- ===============+==========+=========================+===============++-- Altera/Quartus | Broken | No Verilog-2005 support | Works |+-- Xilinx/ISE | Works | Works | Works |+-- ASIC | Untested | Untested | Untested |+-- ===============+==========+=========================+===============++-- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- Block RAM.+-- * See "CLaSH.Prelude.BlockRam.File#usingramfiles" for more information on how+-- to instantiate a Block RAM with the contents of a data file.+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" for ideas on how to create your+-- own data files.+blockRamFile' :: (KnownNat m, Enum addr)+ => SClock clk -- ^ 'Clock' to synchronize to+ -> SNat n -- ^ Size of the blockRAM+ -> FilePath -- ^ File describing the initial+ -- content of the blockRAM+ -> Signal' clk addr -- ^ Write address @w@+ -> Signal' clk addr -- ^ Read address @r@+ -> Signal' clk Bool -- ^ Write enable+ -> Signal' clk (BitVector m) -- ^ Value to write (at address @w@)+ -> Signal' clk (BitVector m)+ -- ^ Value of the @blockRAM@ at address @r@ from the previous+ -- clock cycle+blockRamFile' clk sz file wr rd en din = blockRamFile# clk sz file+ (fromEnum <$> wr)+ (fromEnum <$> rd)+ en din++{-# NOINLINE blockRamFile# #-}+-- | blockRamFile primitive+blockRamFile# :: KnownNat m+ => SClock clk -- ^ 'Clock' to synchronize to+ -> SNat n -- ^ Size of the blockRAM+ -> FilePath -- ^ File describing the initial+ -- content of the blockRAM+ -> Signal' clk Int -- ^ Write address @w@+ -> Signal' clk Int -- ^ Read address @r@+ -> Signal' clk Bool -- ^ Write enable+ -> Signal' clk (BitVector m) -- ^ Value to write (at address @w@)+ -> Signal' clk (BitVector m)+ -- ^ Value of the @blockRAM@ at address @r@ from the previous+ -- clock cycle+blockRamFile# clk sz file wr rd en din = register' clk undefined dout+ where+ szI = fromInteger $ snatToInteger sz+ dout = runST $ do+ arr <- newListArray (0,szI-1) (initMem file)+ traverse (ramT arr) (bundle' clk (wr,rd,en,din))++ ramT :: STArray s Int e -> (Int,Int,Bool,e) -> ST s e+ ramT ram (w,r,e,d) = do+ d' <- readArray ram r+ when e (writeArray ram w d)+ return d'++{-# NOINLINE initMem #-}+-- | __NB:__ Not synthesisable+initMem :: KnownNat n => FilePath -> [BitVector n]+initMem = unsafePerformIO . fmap (map parseBV . lines) . readFile+ where+ parseBV s = case parseBV' s of+ Just i -> fromInteger i+ Nothing -> error ("Failed to parse: " ++ s)+ parseBV' = fmap fst . listToMaybe . readInt 2 (`elem` "01") digitToInt
src/CLaSH/Prelude/Explicit.hs view
@@ -26,15 +26,31 @@ , moore' , mooreB' , registerB'+ -- * Synchronizer circuits for safe clock domain crossings+ , dualFlipFlopSynchronizer+ , asyncFIFOSynchronizer+ -- * ROMs+ , rom'+ , romPow2'+ -- ** ROMs initialised with a data file+ , romFile'+ , romFilePow2'+ -- * RAM primitives with a combinational read port+ , asyncRam'+ , asyncRamPow2' -- * BlockRAM primitives , blockRam' , blockRamPow2'+ -- ** BlockRAM primitives initialised with a data file+ , blockRamFile'+ , blockRamFilePow2' -- * Utility functions , window' , windowD' , isRising' , isFalling' -- * Testbench functions+ , assert' , stimuliGenerator' , outputVerifier' -- * Exported modules@@ -43,45 +59,24 @@ ) where -import Control.Applicative (liftA2)-import Data.Default (Default (..))-import GHC.TypeLits (KnownNat, type (+), natVal)-import Prelude hiding (repeat)+import Data.Default (Default (..))+import GHC.TypeLits (KnownNat, type (+), natVal)+import Prelude hiding (repeat) -import CLaSH.Prelude.BlockRam (blockRam', blockRamPow2')-import CLaSH.Prelude.Mealy (mealy', mealyB')-import CLaSH.Prelude.Moore (moore', mooreB')-import CLaSH.Prelude.Testbench (stimuliGenerator', outputVerifier')+import CLaSH.Prelude.Explicit.Safe+import CLaSH.Prelude.BlockRam.File (blockRamFile', blockRamFilePow2')+import CLaSH.Prelude.ROM.File (romFile', romFilePow2')+import CLaSH.Prelude.Testbench (assert', stimuliGenerator', outputVerifier') import CLaSH.Signal.Explicit-import CLaSH.Sized.Vector (Vec (..), (+>>), asNatProxy, repeat)+import CLaSH.Sized.Vector (Vec (..), (+>>), asNatProxy, repeat) -- $setup -- >>> :set -XDataKinds -- >>> type ClkA = Clk "A" 100 -- >>> let clkA = sclock :: SClock ClkA--- >>> let rP = registerB' clkA (8::Int,8::Int) -- >>> let window4 = window' clkA :: Signal' ClkA Int -> Vec 4 (Signal' ClkA Int) -- >>> let windowD3 = windowD' clkA :: Signal' ClkA Int -> Vec 3 (Signal' ClkA Int) -{-# INLINE registerB' #-}--- | Create a 'register' function for product-type like signals (e.g.--- @('Signal' a, 'Signal' b)@)------ @--- type ClkA = 'Clk' \"A\" 100------ clkA :: 'SClock' ClkA--- clkA = 'sclock'------ rP :: ('Signal'' ClkA Int, 'Signal'' ClkA Int) -> ('Signal'' ClkA Int, 'Signal'' ClkA Int)--- rP = 'registerB'' clkA (8,8)--- @------ >>> simulateB' clkA clkA rP [(1,1),(2,2),(3,3)] :: [(Int,Int)]--- [(8,8),(1,1),(2,2),(3,3)...-registerB' :: Bundle a => SClock clk -> a -> Unbundled' clk a -> Unbundled' clk a-registerB' clk i = unbundle' clk Prelude.. register' clk i Prelude.. bundle' clk- {-# INLINABLE window' #-} -- | Give a window over a 'Signal'' --@@ -134,27 +129,3 @@ where prev = registerB' clk (repeat def) next next = x +>> prev--{-# INLINABLE isRising' #-}--- | Give a pulse when the 'Signal'' goes from 'minBound' to 'maxBound'-isRising' :: (Bounded a, Eq a)- => SClock clk- -> a -- ^ Starting value- -> Signal' clk a- -> Signal' clk Bool-isRising' clk is s = liftA2 edgeDetect prev s- where- prev = register' clk is s- edgeDetect old new = old == minBound && new == maxBound--{-# INLINABLE isFalling' #-}--- | Give a pulse when the 'Signal'' goes from 'maxBound' to 'minBound'-isFalling' :: (Bounded a, Eq a)- => SClock clk- -> a -- ^ Starting value- -> Signal' clk a- -> Signal' clk Bool-isFalling' clk is s = liftA2 edgeDetect prev s- where- prev = register' clk is s- edgeDetect old new = old == maxBound && new == minBound
+ src/CLaSH/Prelude/Explicit/Safe.hs view
@@ -0,0 +1,111 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeOperators #-}++{-# LANGUAGE Safe #-}++{-# OPTIONS_HADDOCK show-extensions #-}++{-|+Copyright : (C) 2013-2015, University of Twente+License : BSD2 (see the file LICENSE)+Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>++__This is the <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/safe-haskell.html Safe> API only of "CLaSH.Prelude.Explicit"__++This module defines the explicitly clocked counterparts of the functions+defined in "CLaSH.Prelude".++This module uses the explicitly clocked 'Signal'' synchronous signals, as+opposed to the implicitly clocked 'Signal' 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.Safe+ ( -- * Creating synchronous sequential circuits+ mealy'+ , mealyB'+ , moore'+ , mooreB'+ , registerB'+ -- * Synchronizer circuits for safe clock domain crossing+ , dualFlipFlopSynchronizer+ , asyncFIFOSynchronizer+ -- * ROMs+ , rom'+ , romPow2'+ -- * RAM primitives with a combinational read port+ , asyncRam'+ , asyncRamPow2'+ -- * BlockRAM primitives+ , blockRam'+ , blockRamPow2'+ -- * Utility functions+ , isRising'+ , isFalling'+ -- * Exported modules+ -- ** Explicitly clocked synchronous signals+ , module CLaSH.Signal.Explicit+ )+where++import Control.Applicative (liftA2)+import Prelude hiding (repeat)++import CLaSH.Prelude.BlockRam (blockRam', blockRamPow2')+import CLaSH.Prelude.Mealy (mealy', mealyB')+import CLaSH.Prelude.Moore (moore', mooreB')+import CLaSH.Prelude.RAM (asyncRam',asyncRamPow2')+import CLaSH.Prelude.ROM (rom', romPow2')+import CLaSH.Prelude.Synchronizer (dualFlipFlopSynchronizer,+ asyncFIFOSynchronizer)+import CLaSH.Signal.Explicit++-- $setup+-- >>> :set -XDataKinds+-- >>> type ClkA = Clk "A" 100+-- >>> let clkA = sclock :: SClock ClkA+-- >>> let rP = registerB' clkA (8::Int,8::Int)++{-# INLINE registerB' #-}+-- | Create a 'register' function for product-type like signals (e.g.+-- @('Signal' a, 'Signal' b)@)+--+-- @+-- type ClkA = 'Clk' \"A\" 100+--+-- clkA :: 'SClock' ClkA+-- clkA = 'sclock'+--+-- rP :: ('Signal'' ClkA Int, 'Signal'' ClkA Int) -> ('Signal'' ClkA Int, 'Signal'' ClkA Int)+-- rP = 'registerB'' clkA (8,8)+-- @+--+-- >>> simulateB' clkA clkA rP [(1,1),(2,2),(3,3)] :: [(Int,Int)]+-- [(8,8),(1,1),(2,2),(3,3)...+registerB' :: Bundle a => SClock clk -> a -> Unbundled' clk a -> Unbundled' clk a+registerB' clk i = unbundle' clk Prelude.. register' clk i Prelude.. bundle' clk++{-# INLINABLE isRising' #-}+-- | Give a pulse when the 'Signal'' goes from 'minBound' to 'maxBound'+isRising' :: (Bounded a, Eq a)+ => SClock clk+ -> a -- ^ Starting value+ -> Signal' clk a+ -> Signal' clk Bool+isRising' clk is s = liftA2 edgeDetect prev s+ where+ prev = register' clk is s+ edgeDetect old new = old == minBound && new == maxBound++{-# INLINABLE isFalling' #-}+-- | Give a pulse when the 'Signal'' goes from 'maxBound' to 'minBound'+isFalling' :: (Bounded a, Eq a)+ => SClock clk+ -> a -- ^ Starting value+ -> Signal' clk a+ -> Signal' clk Bool+isFalling' clk is s = liftA2 edgeDetect prev s+ where+ prev = register' clk is s+ edgeDetect old new = old == maxBound && new == minBound
+ src/CLaSH/Prelude/RAM.hs view
@@ -0,0 +1,149 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++{-# LANGUAGE Safe #-}++{-# OPTIONS_HADDOCK show-extensions #-}++{-|+Copyright : (C) 2015, University of Twente+License : BSD2 (see the file LICENSE)+Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>++RAM primitives with a combinational read port.+-}+module CLaSH.Prelude.RAM+ ( -- * RAM synchronised to the system clock+ asyncRam+ , asyncRamPow2+ -- * RAM synchronised to an arbitrary clock+ , asyncRam'+ , asyncRamPow2'+ -- * Internal+ , asyncRam#+ )+where++import Control.Monad (when)+import Control.Monad.ST.Lazy (ST,runST)+import Data.Array.MArray.Safe (newArray_,readArray,writeArray)+import Data.Array.ST.Safe (STArray)+import GHC.TypeLits (KnownNat, type (^))++import CLaSH.Promoted.Nat (SNat,snat,snatToInteger)+import CLaSH.Signal (Signal)+import CLaSH.Signal.Bundle (bundle')+import CLaSH.Signal.Explicit (Signal', SClock, systemClock, unsafeSynchronizer)+import CLaSH.Sized.Unsigned (Unsigned)++{-# INLINE asyncRam #-}+-- | Create a RAM with space for @n@ elements.+--+-- * __NB__: Initial content of the RAM is 'undefined'+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- RAM.+asyncRam :: (KnownNat n, Enum addr)+ => SNat n -- ^ Size @n@ of the RAM+ -> Signal addr -- ^ Write address @w@+ -> Signal addr -- ^ Read address @r@+ -> Signal Bool -- ^ Write enable+ -> Signal a -- ^ Value to write (at address @w@)+ -> Signal a -- ^ Value of the @RAM@ at address @r@+asyncRam = asyncRam' systemClock systemClock++{-# INLINE asyncRamPow2 #-}+-- | Create a RAM with space for 2^@n@ elements+--+-- * __NB__: Initial content of the RAM is 'undefined'+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- RAM.+asyncRamPow2 :: forall n a . (KnownNat (2^n), KnownNat n)+ => Signal (Unsigned n) -- ^ Write address @w@+ -> Signal (Unsigned n) -- ^ Read address @r@+ -> Signal Bool -- ^ Write enable+ -> Signal a -- ^ Value to write (at address @w@)+ -> Signal a -- ^ Value of the @RAM@ at address @r@+asyncRamPow2 = asyncRam' systemClock systemClock (snat :: SNat (2^n))++{-# INLINE asyncRamPow2' #-}+-- | Create a RAM with space for 2^@n@ elements+--+-- * __NB__: Initial content of the RAM is 'undefined'+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- RAM.+asyncRamPow2' :: forall wclk rclk n a .+ (KnownNat n, KnownNat (2^n))+ => SClock wclk -- ^ 'Clock' to which to synchronise+ -- the write port of the RAM+ -> SClock rclk -- ^ 'Clock' to which the read+ -- address signal, @r@, is+ -- synchronised+ -> Signal' wclk (Unsigned n) -- ^ Write address @w@+ -> Signal' rclk (Unsigned n) -- ^ Read address @r@+ -> Signal' wclk Bool -- ^ Write enable+ -> Signal' wclk a -- ^ Value to write (at address @w@)+ -> Signal' rclk a+ -- ^ Value of the @RAM@ at address @r@+asyncRamPow2' wclk rclk = asyncRam' wclk rclk (snat :: SNat (2^n))++{-# INLINE asyncRam' #-}+-- | Create a RAM with space for @n@ elements+--+-- * __NB__: Initial content of the RAM is 'undefined'+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.BlockRam#usingrams" for more information on how to use a+-- RAM.+asyncRam' :: (KnownNat n, Enum addr)+ => SClock wclk -- ^ 'Clock' to which to synchronise the write+ -- port of the RAM+ -> SClock rclk -- ^ 'Clock' to which the read address signal,+ -- @r@, is synchronised+ -> SNat n -- ^ Size @n@ of the RAM+ -> Signal' wclk addr -- ^ Write address @w@+ -> Signal' rclk addr -- ^ Read address @r@+ -> Signal' wclk Bool -- ^ Write enable+ -> Signal' wclk a -- ^ Value to write (at address @w@)+ -> Signal' rclk a -- ^ Value of the @RAM@ at address @r@+asyncRam' wclk rclk sz wr rd en din = asyncRam# wclk rclk sz (fromEnum <$> wr)+ (fromEnum <$> rd) en din++{-# NOINLINE asyncRam# #-}+-- | RAM primitive+asyncRam# :: SClock wclk -- ^ 'Clock' to which to synchronise the write+ -- port of the RAM+ -> SClock rclk -- ^ 'Clock' to which the read address signal,+ -- @r@, is synchronised+ -> SNat n -- ^ Size @n@ of the RAM+ -> Signal' wclk Int -- ^ Write address @w@+ -> Signal' rclk Int -- ^ Read address @r@+ -> Signal' wclk Bool -- ^ Write enable+ -> Signal' wclk a -- ^ Value to write (at address @w@)+ -> Signal' rclk a -- ^ Value of the @RAM@ at address @r@+asyncRam# wclk rclk sz wr rd en din = unsafeSynchronizer wclk rclk dout+ where+ szI = fromInteger $ snatToInteger sz+ rd' = unsafeSynchronizer rclk wclk rd+ dout = runST $ do+ arr <- newArray_ (0,szI-1)+ traverse (ramT arr) (bundle' wclk (wr,rd',en,din))++ ramT :: STArray s Int e -> (Int,Int,Bool,e) -> ST s e+ ramT ram (w,r,e,d) = do+ d' <- readArray ram r+ when e (writeArray ram w d)+ return d'
+ src/CLaSH/Prelude/ROM.hs view
@@ -0,0 +1,173 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE TypeOperators #-}++{-# LANGUAGE Safe #-}++{-# OPTIONS_HADDOCK show-extensions #-}++{-|+Copyright : (C) 2015, University of Twente+License : BSD2 (see the file LICENSE)+Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>++ROMs+-}+module CLaSH.Prelude.ROM+ ( -- * Asynchronous ROM+ asyncRom+ , asyncRomPow2+ -- * Synchronous ROM synchronised to the system clock+ , rom+ , romPow2+ -- * Synchronous ROM synchronised to an arbitrary clock+ , rom'+ , romPow2'+ -- * Internal+ , asyncRom#+ , rom#+ )+where++import Data.Array ((!),listArray)+import GHC.TypeLits (KnownNat, type (^))++import CLaSH.Signal (Signal)+import CLaSH.Signal.Explicit (Signal', SClock, systemClock)+import CLaSH.Sized.Unsigned (Unsigned)+import CLaSH.Signal.Explicit (register')+import CLaSH.Sized.Vector (Vec, maxIndex, toList)++{-# INLINE asyncRom #-}+-- | An asynchronous/combinational ROM with space for @n@ elements+--+-- Additional helpful information:+--+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" and "CLaSH.Prelude.BlockRam#usingrams"+-- for ideas on how to use ROMs and RAMs+asyncRom :: (KnownNat n, Enum addr)+ => Vec n a -- ^ ROM content+ --+ -- __NB:__ must be a constant+ -> addr -- ^ Read address @rd@+ -> a -- ^ The value of the ROM at address @rd@+asyncRom content rd = asyncRom# content (fromEnum rd)++{-# INLINE asyncRomPow2 #-}+-- | An asynchronous/combinational ROM with space for 2^@n@ elements+--+-- Additional helpful information:+--+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" and "CLaSH.Prelude.BlockRam#usingrams"+-- for ideas on how to use ROMs and RAMs+asyncRomPow2 :: (KnownNat (2^n), KnownNat n)+ => Vec (2^n) a -- ^ ROM content+ --+ -- __NB:__ must be a constant+ -> Unsigned n -- ^ Read address @rd@+ -> a -- ^ The value of the ROM at address @rd@+asyncRomPow2 = asyncRom++{-# NOINLINE asyncRom# #-}+-- | asyncROM primitive+asyncRom# :: KnownNat n+ => Vec n a -- ^ ROM content+ --+ -- __NB:__ must be a constant+ -> Int -- ^ Read address @rd@+ -> a -- ^ The value of the ROM at address @rd@+asyncRom# content rd = arr ! rd+ where+ szI = fromInteger (maxIndex content)+ arr = listArray (0,szI) (toList content)++{-# INLINE rom #-}+-- | A ROM with a synchronous read port, with space for @n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+--+-- Additional helpful information:+--+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" and "CLaSH.Prelude.BlockRam#usingrams"+-- for ideas on how to use ROMs and RAMs+rom :: (KnownNat n, KnownNat m)+ => Vec n a -- ^ ROM content+ --+ -- __NB:__ must be a constant+ -> Signal (Unsigned m) -- ^ Read address @rd@+ -> Signal a -- ^ The value of the ROM at address @rd@+rom = rom' systemClock++{-# INLINE romPow2 #-}+-- | A ROM with a synchronous read port, with space for 2^@n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+--+-- Additional helpful information:+--+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" and "CLaSH.Prelude.BlockRam#usingrams"+-- for ideas on how to use ROMs and RAMs+romPow2 :: (KnownNat (2^n), KnownNat n)+ => Vec (2^n) a -- ^ ROM content+ --+ -- __NB:__ must be a constant+ -> Signal (Unsigned n) -- ^ Read address @rd@+ -> Signal a -- ^ The value of the ROM at address @rd@+romPow2 = rom' systemClock++{-# INLINE romPow2' #-}+-- | A ROM with a synchronous read port, with space for 2^@n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+--+-- Additional helpful information:+--+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" and "CLaSH.Prelude.BlockRam#usingrams"+-- for ideas on how to use ROMs and RAMs+romPow2' :: (KnownNat (2^n), KnownNat n)+ => SClock clk -- ^ 'Clock' to synchronize to+ -> Vec (2^n) a -- ^ ROM content+ --+ -- __NB:__ must be a constant+ -> Signal' clk (Unsigned n) -- ^ Read address @rd@+ -> Signal' clk a -- ^ The value of the ROM at address @rd@+romPow2' = rom'++{-# INLINE rom' #-}+-- | A ROM with a synchronous read port, with space for @n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+--+-- Additional helpful information:+--+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" and "CLaSH.Prelude.BlockRam#usingrams"+-- for ideas on how to use ROMs and RAMs+rom' :: (KnownNat n, Enum addr)+ => SClock clk -- ^ 'Clock' to synchronize to+ -> Vec n a -- ^ ROM content+ --+ -- __NB:__ must be a constant+ -> Signal' clk addr -- ^ Read address @rd@+ -> Signal' clk a+ -- ^ The value of the ROM at address @rd@ from the previous clock cycle+rom' clk content rd = rom# clk content (fromEnum <$> rd)++{-# NOINLINE rom# #-}+-- | ROM primitive+rom# :: KnownNat n+ => SClock clk -- ^ 'Clock' to synchronize to+ -> Vec n a -- ^ ROM content+ --+ -- __NB:__ must be a constant+ -> Signal' clk Int -- ^ Read address @rd@+ -> Signal' clk a+ -- ^ The value of the ROM at address @rd@ from the previous clock cycle+rom# clk content rd = register' clk undefined ((arr !) <$> rd)+ where+ szI = fromInteger (maxIndex content)+ arr = listArray (0,szI) (toList content)
+ src/CLaSH/Prelude/ROM/File.hs view
@@ -0,0 +1,312 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}++{-# LANGUAGE Unsafe #-}++{-# OPTIONS_HADDOCK show-extensions #-}++{-|+Copyright : (C) 2015, University of Twente+License : BSD2 (see the file LICENSE)+Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>++= Initialising a ROM with a data file #usingromfiles#++ROMs initialised with a data file. The BNF grammar for this data file is simple:++@+FILE = LINE++LINE = BIT++BIT = '0'+ | '1'+@++Consecutive @LINE@s correspond to consecutive memory addresses starting at @0@.+For example, a data file @memory.bin@ containing the 9-bit unsigned number+@7@ to @13@ looks like:++@+000000111+000001000+000001001+000001010+000001011+000001100+000001101+@++We can instantiate a synchronous ROM using the content of the above file like+so:++@+topEntity :: Signal (Unsigned 3) -> Signal (Unsigned 9)+topEntity rd = 'CLaSH.Class.BitPack.unpack' '<$>' 'romFile' d7 \"memory.bin\" rd+@++And see that it works as expected:++@+__>>> import qualified Data.List as L__+__>>> L.tail $ sampleN 4 $ topEntity (fromList [3..5])__+[10,11,12]+@++However, we can also interpret the same data as a tuple of a 6-bit unsigned+number, and a 3-bit signed number:++@+topEntity2 :: Signal (Unsigned 3) -> Signal (Unsigned 6,Signed 3)+topEntity2 rd = 'CLaSH.Class.BitPack.unpack' '<$>' 'romFile' d7 \"memory.bin\" rd+@++And then we would see:++@+__>>> import qualified Data.List as L__+__>>> L.tail $ sampleN 4 $ topEntity2 (fromList [3..5])__+[(1,2),(1,3)(1,-4)]+@+-}+module CLaSH.Prelude.ROM.File+ ( -- * Asynchronous ROM+ asyncRomFile+ , asyncRomFilePow2+ -- * Synchronous ROM synchronised to the system clock+ , romFile+ , romFilePow2+ -- * Synchronous ROM synchronised to an arbitrary clock+ , romFile'+ , romFilePow2'+ -- * Internal+ , asyncRomFile#+ , romFile#+ )+where++import Data.Array (listArray,(!))+import GHC.TypeLits (KnownNat, type (^))++import CLaSH.Prelude.BlockRam.File (initMem)+import CLaSH.Promoted.Nat (SNat,snat,snatToInteger)+import CLaSH.Sized.BitVector (BitVector)+import CLaSH.Signal (Signal)+import CLaSH.Signal.Explicit (Signal', SClock, register', systemClock)+import CLaSH.Sized.Unsigned (Unsigned)++{-# INLINE asyncRomFile #-}+-- | An asynchronous/combinational ROM with space for @n@ elements+--+-- * __NB__: This function might not work for specific combinations of+-- code-generation backends and hardware targets. Please check the support table+-- below:+--+-- @+-- | VHDL | Verilog | SystemVerilog |+-- ===============+==========+=========================+===============++-- Altera/Quartus | Broken | No Verilog-2005 support | Works |+-- Xilinx/ISE | Works | Works | Works |+-- ASIC | Untested | Untested | Untested |+-- ===============+==========+=========================+===============++-- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.ROM.File#usingromfiles" for more information on how+-- to instantiate a ROM with the contents of a data file.+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" for ideas on how to create your+-- own data files.+asyncRomFile :: (KnownNat m, Enum addr)+ => SNat n -- ^ Size of the ROM+ -> FilePath -- ^ File describing the content of the ROM+ -> addr -- ^ Read address @rd@+ -> BitVector m -- ^ The value of the ROM at address @rd@+asyncRomFile sz file rd = asyncRomFile# sz file (fromEnum rd)++{-# INLINE asyncRomFilePow2 #-}+-- | An asynchronous/combinational ROM with space for 2^@n@ elements+--+-- * __NB__: This function might not work for specific combinations of+-- code-generation backends and hardware targets. Please check the support table+-- below:+--+-- @+-- | VHDL | Verilog | SystemVerilog |+-- ===============+==========+=========================+===============++-- Altera/Quartus | Broken | No Verilog-2005 support | Works |+-- Xilinx/ISE | Works | Works | Works |+-- ASIC | Untested | Untested | Untested |+-- ===============+==========+=========================+===============++-- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.ROM.File#usingromfiles" for more information on how+-- to instantiate a ROM with the contents of a data file.+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" for ideas on how to create your+-- own data files.+asyncRomFilePow2 :: forall n m . (KnownNat m, KnownNat n, KnownNat (2^n))+ => FilePath -- ^ File describing the content of the ROM+ -> Unsigned n -- ^ Read address @rd@+ -> BitVector m -- ^ The value of the ROM at address @rd@+asyncRomFilePow2 = asyncRomFile (snat :: SNat (2^n))++{-# NOINLINE asyncRomFile# #-}+-- | asyncROMFile primitive+asyncRomFile# :: KnownNat m+ => SNat n -- ^ Size of the ROM+ -> FilePath -- ^ File describing the content of the ROM+ -> Int -- ^ Read address @rd@+ -> BitVector m -- ^ The value of the ROM at address @rd@+asyncRomFile# sz file rd = content ! rd+ where+ content = listArray (0,szI-1) (initMem file)+ szI = fromInteger (snatToInteger sz)++{-# INLINE romFile #-}+-- | A ROM with a synchronous read port, with space for @n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+-- * __NB__: This function might not work for specific combinations of+-- code-generation backends and hardware targets. Please check the support table+-- below:+--+-- @+-- | VHDL | Verilog | SystemVerilog |+-- ===============+==========+=========================+===============++-- Altera/Quartus | Broken | No Verilog-2005 support | Works |+-- Xilinx/ISE | Works | Works | Works |+-- ASIC | Untested | Untested | Untested |+-- ===============+==========+=========================+===============++-- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.ROM.File#usingromfiles" for more information on how+-- to instantiate a ROM with the contents of a data file.+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" for ideas on how to create your+-- own data files.+romFile :: (KnownNat m, KnownNat k)+ => SNat n -- ^ Size of the ROM+ -> FilePath -- ^ File describing the content of the ROM+ -> Signal (Unsigned k) -- ^ Read address @rd@+ -> Signal (BitVector m)+ -- ^ The value of the ROM at address @rd@ from the previous clock cycle+romFile = romFile' systemClock++{-# INLINE romFilePow2 #-}+-- | A ROM with a synchronous read port, with space for 2^@n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+-- * __NB__: This function might not work for specific combinations of+-- code-generation backends and hardware targets. Please check the support table+-- below:+--+-- @+-- | VHDL | Verilog | SystemVerilog |+-- ===============+==========+=========================+===============++-- Altera/Quartus | Broken | No Verilog-2005 support | Works |+-- Xilinx/ISE | Works | Works | Works |+-- ASIC | Untested | Untested | Untested |+-- ===============+==========+=========================+===============++-- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.ROM.File#usingromfiles" for more information on how+-- to instantiate a ROM with the contents of a data file.+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" for ideas on how to create your+-- own data files.+romFilePow2 :: forall n m . (KnownNat m, KnownNat n, KnownNat (2^n))+ => FilePath -- ^ File describing the content of the ROM+ -> Signal (Unsigned n) -- ^ Read address @rd@+ -> Signal (BitVector m)+ -- ^ The value of the ROM at address @rd@ from the previous clock cycle+romFilePow2 = romFile' systemClock (snat :: SNat (2^n))++{-# INLINE romFilePow2' #-}+-- | A ROM with a synchronous read port, with space for 2^@n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+-- * __NB__: This function might not work for specific combinations of+-- code-generation backends and hardware targets. Please check the support table+-- below:+--+-- @+-- | VHDL | Verilog | SystemVerilog |+-- ===============+==========+=========================+===============++-- Altera/Quartus | Broken | No Verilog-2005 support | Works |+-- Xilinx/ISE | Works | Works | Works |+-- ASIC | Untested | Untested | Untested |+-- ===============+==========+=========================+===============++-- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.ROM.File#usingromfiles" for more information on how+-- to instantiate a ROM with the contents of a data file.+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" for ideas on how to create your+-- own data files.+romFilePow2' :: forall clk n m . (KnownNat m, KnownNat n, KnownNat (2^n))+ => SClock clk -- ^ 'Clock' to synchronize to+ -> FilePath -- ^ File describing the content of+ -- the ROM+ -> Signal' clk (Unsigned n) -- ^ Read address @rd@+ -> Signal' clk (BitVector m)+ -- ^ The value of the ROM at address @rd@ from the previous clock+ -- cycle+romFilePow2' clk = romFile' clk (snat :: SNat (2^n))++{-# INLINE romFile' #-}+-- | A ROM with a synchronous read port, with space for @n@ elements+--+-- * __NB__: Read value is delayed by 1 cycle+-- * __NB__: Initial output value is 'undefined'+-- * __NB__: This function might not work for specific combinations of+-- code-generation backends and hardware targets. Please check the support table+-- below:+--+-- @+-- | VHDL | Verilog | SystemVerilog |+-- ===============+==========+=========================+===============++-- Altera/Quartus | Broken | No Verilog-2005 support | Works |+-- Xilinx/ISE | Works | Works | Works |+-- ASIC | Untested | Untested | Untested |+-- ===============+==========+=========================+===============++-- @+--+-- Additional helpful information:+--+-- * See "CLaSH.Prelude.ROM.File#usingromfiles" for more information on how+-- to instantiate a ROM with the contents of a data file.+-- * See "CLaSH.Sized.Fixed#creatingdatafiles" for ideas on how to create your+-- own data files.+romFile' :: (KnownNat m, Enum addr)+ => SClock clk -- ^ 'Clock' to synchronize to+ -> SNat n -- ^ Size of the ROM+ -> FilePath -- ^ File describing the content of the+ -- ROM+ -> Signal' clk addr -- ^ Read address @rd@+ -> Signal' clk (BitVector m)+ -- ^ The value of the ROM at address @rd@ from the previous clock cycle+romFile' clk sz file rd = romFile# clk sz file (fromEnum <$> rd)++{-# NOINLINE romFile# #-}+-- | romFile primitive+romFile# :: KnownNat m+ => SClock clk -- ^ 'Clock' to synchronize to+ -> SNat n -- ^ Size of the ROM+ -> FilePath -- ^ File describing the content of the+ -- ROM+ -> Signal' clk Int -- ^ Read address @rd@+ -> Signal' clk (BitVector m)+ -- ^ The value of the ROM at address @rd@ from the previous clock cycle+romFile# clk sz file rd = register' clk undefined ((content !) <$> rd)+ where+ content = listArray (0,szI-1) (initMem file)+ szI = fromInteger (snatToInteger sz)
+ src/CLaSH/Prelude/Safe.hs view
@@ -0,0 +1,176 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeOperators #-}++{-# LANGUAGE Safe #-}++{-# OPTIONS_HADDOCK show-extensions #-}++{-|+ Copyright : (C) 2013-2015, University of Twente+ License : BSD2 (see the file LICENSE)+ Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>++ __This is the <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/safe-haskell.html Safe> API only of "CLaSH.Prelude"__++ 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 preliminary version of a tutorial can be found in "CLaSH.Tutorial".+ Some circuit examples can be found in "CLaSH.Examples".+-}+module CLaSH.Prelude.Safe+ ( -- * Creating synchronous sequential circuits+ mealy+ , mealyB+ , (<^>)+ , moore+ , mooreB+ , registerB+ -- * ROMs+ , asyncRom+ , asyncRomPow2+ , rom+ , romPow2+ -- * RAM primitives with a combinational read port+ , asyncRam+ , asyncRamPow2+ -- * BlockRAM primitives+ , blockRam+ , blockRamPow2+ -- * Utility functions+ , isRising+ , isFalling+ -- * Exported modules+ -- ** Synchronous signals+ , module CLaSH.Signal+ , module CLaSH.Signal.Delayed+ -- ** DataFlow interface+ , module CLaSH.Prelude.DataFlow+ -- ** Datatypes+ -- *** Bit vectors+ , module CLaSH.Sized.BitVector+ , module CLaSH.Prelude.BitIndex+ , module CLaSH.Prelude.BitReduction+ -- *** Arbitrary-width numbers+ , module CLaSH.Sized.Signed+ , module CLaSH.Sized.Unsigned+ , module CLaSH.Sized.Index+ -- *** Fixed point numbers+ , module CLaSH.Sized.Fixed+ -- *** Fixed size vectors+ , module CLaSH.Sized.Vector+ -- ** Annotations+ , module CLaSH.Annotations.TopEntity+ -- ** 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+ -- ** Type classes+ -- *** CLaSH+ , module CLaSH.Class.BitPack+ , module CLaSH.Class.Num+ , module CLaSH.Class.Resize+ -- *** Other+ , module Control.Applicative+ , module Data.Bits+ -- ** Haskell Prelude+ -- $hiding+ , module Prelude+ )+where++import Control.Applicative+import Data.Bits+import GHC.TypeLits+import Prelude hiding ((++), (!!), concat, drop, foldl,+ foldl1, foldr, foldr1, head, init,+ iterate, last, length, map, repeat,+ replicate, reverse, scanl, scanr,+ splitAt, tail, take, unzip, unzip3,+ zip, zip3, zipWith, zipWith3)++import CLaSH.Annotations.TopEntity+import CLaSH.Class.BitPack+import CLaSH.Class.Num+import CLaSH.Class.Resize+import CLaSH.Prelude.BitIndex+import CLaSH.Prelude.BitReduction+import CLaSH.Prelude.BlockRam (blockRam, blockRamPow2)+import CLaSH.Prelude.Explicit.Safe (registerB', isRising', isFalling')+import CLaSH.Prelude.Mealy (mealy, mealyB, (<^>))+import CLaSH.Prelude.Moore (moore, mooreB)+import CLaSH.Prelude.RAM (asyncRam,asyncRamPow2)+import CLaSH.Prelude.ROM (asyncRom,asyncRomPow2,rom,romPow2)+import CLaSH.Prelude.DataFlow+import CLaSH.Promoted.Nat+import CLaSH.Promoted.Nat.TH+import CLaSH.Promoted.Nat.Literals+import CLaSH.Promoted.Ord+import CLaSH.Sized.BitVector+import CLaSH.Sized.Fixed+import CLaSH.Sized.Index+import CLaSH.Sized.Signed+import CLaSH.Sized.Unsigned+import CLaSH.Sized.Vector+import CLaSH.Signal+import CLaSH.Signal.Delayed+import CLaSH.Signal.Explicit (systemClock)++-- $setup+-- >>> let rP = registerB (8,8)++{- $hiding+"CLaSH.Prelude" re-exports most of the Haskell "Prelude" with the exception of+the following: (++), (!!), concat, drop, foldl, foldl1, foldr, foldr1, head,+init, iterate, last, length, map, repeat, replicate, reverse, scanl, scanr,+splitAt, tail, take, unzip, unzip3, zip, zip3, zipWith, zipWith3.++It instead exports the identically named functions defined in terms of+'CLaSH.Sized.Vector.Vec' at "CLaSH.Sized.Vector".+-}++{-# INLINE registerB #-}+-- | Create a 'register' function for product-type like signals (e.g. '(Signal a, Signal b)')+--+-- > rP :: (Signal Int,Signal Int) -> (Signal Int, Signal Int)+-- > rP = registerB (8,8)+--+-- >>> simulateB rP [(1,1),(2,2),(3,3)] :: [(Int,Int)]+-- [(8,8),(1,1),(2,2),(3,3)...+registerB :: Bundle a => a -> Unbundled a -> Unbundled a+registerB = registerB' systemClock++{-# INLINE isRising #-}+-- | Give a pulse when the 'Signal' goes from 'minBound' to 'maxBound'+isRising :: (Bounded a, Eq a)+ => a -- ^ Starting value+ -> Signal a+ -> Signal Bool+isRising = isRising' systemClock++{-# INLINE isFalling #-}+-- | Give a pulse when the 'Signal' goes from 'maxBound' to 'minBound'+isFalling :: (Bounded a, Eq a)+ => a -- ^ Starting value+ -> Signal a+ -> Signal Bool+isFalling = isFalling' systemClock
+ src/CLaSH/Prelude/Synchronizer.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PartialTypeSignatures #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++{-# LANGUAGE Safe #-}++{-# OPTIONS_GHC -fno-warn-partial-type-signatures #-}+{-# OPTIONS_HADDOCK show-extensions #-}++{-|+Copyright : (C) 2015, University of Twente+License : BSD2 (see the file LICENSE)+Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>++Synchronizer circuits for safe clock domain crossings+-}+module CLaSH.Prelude.Synchronizer+ ( -- * Bit-synchronizers+ dualFlipFlopSynchronizer+ -- * Word-synchronizers+ , asyncFIFOSynchronizer+ )+where++import Data.Bits (complement, shiftR, xor)+import GHC.TypeLits (KnownNat, type (+))++import CLaSH.Class.BitPack (pack)+import CLaSH.Class.Resize (zeroExtend)+import CLaSH.Prelude.BitIndex (slice)+import CLaSH.Prelude.Mealy (mealyB')+import CLaSH.Prelude.RAM (asyncRam')+import CLaSH.Promoted.Nat (SNat, powSNat, subSNat)+import CLaSH.Promoted.Nat.Literals (d0, d1, d2)+import CLaSH.Signal ((.&&.), not1)+import CLaSH.Signal.Explicit (Signal', SClock, register',+ unsafeSynchronizer)+import CLaSH.Sized.BitVector (BitVector, (++#))++-- * Dual flip-flop synchronizer++-- | Synchroniser based on two sequentially connected flip-flops.+--+-- * __NB__: This synchroniser can be used for __bit__-synchronization.+--+-- * __NB__: Although this synchroniser does reduce metastability, it does+-- not guarantee the proper synchronisation of a whole __word__. For+-- example, given that the output is sampled twice as fast as the input is+-- running, and we have two samples in the input stream that look like:+--+-- @[0111,1000]@+--+-- But the circuit driving the input stream has a longer propagation delay+-- on __msb__ compared to the __lsb__s. What can happen is an output stream+-- that looks like this:+--+-- @[0111,0111,0000,1000]@+--+-- Where the level-change of the __msb__ was not captured, but the level+-- change of the __lsb__s were.+--+-- If you want to have /safe/ __word__-synchronisation use+-- 'asyncFIFOSynchronizer'.+dualFlipFlopSynchronizer :: SClock clk1 -- ^ 'Clock' to which the incoming+ -- data is synchronised+ -> SClock clk2 -- ^ 'Clock' to which the outgoing+ -- data is synchronised+ -> a -- ^ Initial value of the two+ -- synchronisation registers+ -> Signal' clk1 a -- ^ Incoming data+ -> Signal' clk2 a -- ^ Outgoing, synchronised, data+dualFlipFlopSynchronizer clk1 clk2 i = register' clk2 i+ . register' clk2 i+ . unsafeSynchronizer clk1 clk2++-- * Asynchronous FIFO synchronizer++fifoMem :: _+ => SClock wclk+ -> SClock rclk+ -> SNat addrSize+ -> Signal' wclk (BitVector addrSize)+ -> Signal' rclk (BitVector addrSize)+ -> Signal' wclk Bool+ -> Signal' wclk Bool+ -> Signal' wclk a+ -> Signal' rclk a+fifoMem wclk rclk addrSize waddr raddr winc wfull wdata =+ asyncRam' wclk rclk+ (d2 `powSNat` addrSize)+ waddr raddr+ (winc .&&. not1 wfull)+ wdata++boolToBV :: (KnownNat n, KnownNat (n+1)) => Bool -> BitVector (n + 1)+boolToBV = zeroExtend . pack++ptrCompareT :: _+ => SNat addrSize+ -> (BitVector (addrSize + 1) -> BitVector (addrSize + 1) -> Bool)+ -> (BitVector (addrSize + 1), BitVector (addrSize + 1), Bool)+ -> (BitVector (addrSize + 1), Bool)+ -> ((BitVector (addrSize + 1), BitVector (addrSize + 1), Bool)+ ,(Bool, BitVector addrSize, BitVector (addrSize + 1)))+ptrCompareT addrSize flagGen (bin,ptr,flag) (s_ptr,inc) = ((bin',ptr',flag')+ ,(flag,addr,ptr))+ where+ -- GRAYSTYLE2 pointer+ bin' = bin + boolToBV (inc && not flag)+ ptr' = (bin' `shiftR` 1) `xor` bin'+ addr = slice (addrSize `subSNat` d1) d0 bin++ flag' = flagGen ptr' s_ptr+++-- FIFO full: when next pntr == synchonized {~wptr[addrSize:addrSize-1],wptr[addrSize-1:0]}+isFull :: _+ => SNat addrSize+ -> BitVector (addrSize + 1)+ -> BitVector (addrSize + 1)+ -> Bool+isFull addrSize ptr s_ptr =+ ptr == (complement (slice addrSize (addrSize `subSNat` d1) s_ptr) ++#+ slice (addrSize `subSNat` d2) d0 s_ptr)++-- | Synchroniser implemented as a FIFO around an asynchronous RAM. Based on the+-- design described in "CLaSH.Tutorial#multiclock", which is itself based on the+-- design described in <http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf>.+--+-- __NB__: This synchroniser can be used for __word__-synchronization.+asyncFIFOSynchronizer :: _+ => SNat addrSize -- ^ Size of the internally used+ -- addresses, the FIFO contains+ -- @2^addrSize@ elements.+ -> SClock wclk -- ^ 'Clock' to which the write port+ -- is synchronised+ -> SClock rclk -- ^ 'Clock' to which the read port+ -- is synchronised+ -> Signal' wclk a -- ^ Element to insert+ -> Signal' wclk Bool -- ^ Write request+ -> Signal' rclk Bool -- ^ Read request+ -> (Signal' rclk a, Signal' rclk Bool, Signal' wclk Bool)+ -- ^ (Oldest element in the FIFO, @empty@ flag, @full@ flag)+asyncFIFOSynchronizer addrSize wclk rclk wdata winc rinc = (rdata,rempty,wfull)+ where+ s_rptr = dualFlipFlopSynchronizer rclk wclk 0 rptr+ s_wptr = dualFlipFlopSynchronizer wclk rclk 0 wptr++ rdata = fifoMem wclk rclk addrSize waddr raddr winc wfull wdata++ (rempty,raddr,rptr) = mealyB' rclk (ptrCompareT addrSize (==)) (0,0,True)+ (s_wptr,rinc)++ (wfull,waddr,wptr) = mealyB' wclk (ptrCompareT addrSize (isFull addrSize))+ (0,0,False) (s_rptr,winc)
src/CLaSH/Prelude/Testbench.hs view
@@ -15,17 +15,17 @@ , stimuliGenerator , outputVerifier -- * Testbench functions for circuits synchronised to arbitrary clocks+ , assert' , stimuliGenerator' , outputVerifier' ) where -import Control.Applicative (liftA3) import Debug.Trace (trace) import GHC.TypeLits (KnownNat) import Prelude hiding ((!!)) -import CLaSH.Signal (Signal)+import CLaSH.Signal (Signal, fromList) import CLaSH.Signal.Explicit (Signal', SClock, register', systemClock) import CLaSH.Signal.Bundle (unbundle') import CLaSH.Sized.Index (Index)@@ -43,6 +43,22 @@ -- >>> let testInput' = stimuliGenerator' clkA $(v [(1::Int),3..21]) -- >>> let expectedOutput' = outputVerifier' clkA $(v ([70,99,2,3,4,5,7,8,9,10]::[Int])) +{-# INLINE assert #-}+-- | Compares the first two 'Signal's for equality and logs a warning when they+-- are not equal. The second 'Signal' is considered the expected value. This+-- function simply returns the third 'Signal' unaltered as its result. This+-- function is used by 'outputVerifier'.+--+--+-- __NB__: This function is /can/ be used in synthesizable designs.+assert :: (Eq a,Show a)+ => String -- ^ Additional message+ -> Signal a -- ^ Checked value+ -> Signal a -- ^ Expected value+ -> Signal b -- ^ Return valued+ -> Signal b+assert = assert' systemClock+ {-# INLINE 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@@ -77,16 +93,22 @@ -- >>> import qualified Data.List as List -- >>> sampleN 12 (expectedOutput (fromList ([0..10] List.++ [10,10,10]))) -- [+-- cycle(system1000): 0, outputVerifier -- expected value: 70, not equal to actual value: 0 -- False,+-- cycle(system1000): 1, outputVerifier -- expected value: 99, not equal to actual value: 1 -- False,False,False,False,False,+-- cycle(system1000): 6, outputVerifier -- expected value: 7, not equal to actual value: 6 -- False,+-- cycle(system1000): 7, outputVerifier -- expected value: 8, not equal to actual value: 7 -- False,+-- cycle(system1000): 8, outputVerifier -- expected value: 9, not equal to actual value: 8 -- False,+-- cycle(system1000): 9, outputVerifier -- expected value: 10, not equal to actual value: 9 -- False,True,True] outputVerifier :: forall l a . (KnownNat l, Eq a, Show a)@@ -95,26 +117,34 @@ -> Signal Bool -- ^ Indicator that all samples are verified outputVerifier = outputVerifier' systemClock -{-# NOINLINE assert #-}--- | 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'.+{-# NOINLINE assert' #-}+-- | Compares the first two 'Signal''s for equality and logs a warning when they+-- are not equal. The second 'Signal'' is considered the expected value. This+-- function simply returns the third 'Signal'' unaltered as its result. This+-- function is used by 'outputVerifier''. -- -- -- __NB__: This function is /can/ be used in synthesizable designs.-assert :: (Eq a,Show a)- => Signal' t a -- ^ Checked value- -> Signal' t a -- ^ Expected value- -> Signal' t b -- ^ Return valued- -> Signal' t b-assert = liftA3- (\a' b' c' -> if a' == b' then c'- else trace (concat [ "\nexpected value: "- , show b'- , ", not equal to actual value: "- , show a'- ]) c')+assert' :: (Eq a,Show a)+ => SClock t+ -> String -- ^ Additional message+ -> Signal' t a -- ^ Checked value+ -> Signal' t a -- ^ Expected value+ -> Signal' t b -- ^ Return valued+ -> Signal' t b+assert' clk msg checked expected returned =+ (\c e cnt r ->+ if c == e then r+ else trace (concat [ "\ncycle(" ++ show clk ++ "): "+ , show cnt+ , ", "+ , msg+ , "\nexpected value: "+ , show e+ , ", not equal to actual value: "+ , show c+ ]) r)+ <$> checked <*> expected <*> fromList [(0::Integer)..] <*> returned {-# INLINABLE stimuliGenerator' #-} -- | To be used as a one of the functions to create the \"magical\" 'testInput'@@ -173,16 +203,22 @@ -- >>> import qualified Data.List as List -- >>> sampleN 12 (expectedOutput' (fromList ([0..10] List.++ [10,10,10]))) -- [+-- cycle(A100): 0, outputVerifier -- expected value: 70, not equal to actual value: 0 -- False,+-- cycle(A100): 1, outputVerifier -- expected value: 99, not equal to actual value: 1 -- False,False,False,False,False,+-- cycle(A100): 6, outputVerifier -- expected value: 7, not equal to actual value: 6 -- False,+-- cycle(A100): 7, outputVerifier -- expected value: 8, not equal to actual value: 7 -- False,+-- cycle(A100): 8, outputVerifier -- expected value: 9, not equal to actual value: 8 -- False,+-- cycle(A100): 9, outputVerifier -- expected value: 10, not equal to actual value: 9 -- False,True,True] outputVerifier' :: forall l clk a . (KnownNat l, Eq a, Show a)@@ -194,7 +230,7 @@ outputVerifier' clk samples i = let (s,o) = unbundle' clk (genT <$> register' clk 0 s) (e,f) = unbundle' clk o- in assert i e (register' clk False f)+ in assert' clk "outputVerifier" i e (register' clk False f) where genT :: Index l -> (Index l,(a,Bool)) genT s = (s',(samples !! s,finished))
src/CLaSH/Promoted/Nat.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -15,7 +16,7 @@ Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com> -} module CLaSH.Promoted.Nat- ( SNat (..), snat, withSNat, snatToInteger+ ( SNat (..), snat, withSNat, snatToInteger, addSNat, subSNat, mulSNat, powSNat , UNat (..), toUNat, addUNat, multUNat, powUNat ) where@@ -66,7 +67,7 @@ fromI 0 = unsafeCoerce UZero fromI n = unsafeCoerce (USucc (fromI (n - 1))) --- | Add two singleton natural numbers+-- | Add two unary singleton natural numbers -- -- __NB__: Not synthesisable addUNat :: UNat n -> UNat m -> UNat (n + m)@@ -74,7 +75,7 @@ addUNat x UZero = x addUNat (USucc x) y = USucc (addUNat x y) --- | Multiply two singleton natural numbers+-- | Multiply two unary singleton natural numbers -- -- __NB__: Not synthesisable multUNat :: UNat n -> UNat m -> UNat (n * m)@@ -82,9 +83,25 @@ multUNat _ UZero = UZero multUNat (USucc x) y = addUNat y (multUNat x y) --- | Exponential of two singleton natural numbers+-- | Power of two unary singleton natural numbers -- -- __NB__: Not synthesisable powUNat :: UNat n -> UNat m -> UNat (n ^ m) powUNat _ UZero = USucc UZero powUNat x (USucc y) = multUNat x (powUNat x y)++-- | Add two singleton natural numbers+addSNat :: KnownNat (a + b) => SNat a -> SNat b -> SNat (a+b)+addSNat _ _ = snat++-- | Subtract two singleton natural numbers+subSNat :: KnownNat (a - b) => SNat a -> SNat b -> SNat (a-b)+subSNat _ _ = snat++-- | Multiply two singleton natural numbers+mulSNat :: KnownNat (a * b) => SNat a -> SNat b -> SNat (a*b)+mulSNat _ _ = snat++-- | Power of two singleton natural numbers+powSNat :: KnownNat (a ^ b) => SNat a -> SNat b -> SNat (a^b)+powSNat _ _ = snat
src/CLaSH/Signal/Delayed.hs view
@@ -40,7 +40,7 @@ import Data.Coerce (coerce) import Data.Default (Default(..)) import Control.Applicative (liftA2)-import GHC.TypeLits (KnownNat, Nat, type (-))+import GHC.TypeLits (KnownNat, Nat, type (+)) import Language.Haskell.TH.Syntax (Lift) import Prelude hiding (head, length, repeat) import Test.QuickCheck (Arbitrary, CoArbitrary)@@ -56,7 +56,7 @@ -- >>> :set -XTypeOperators -- >>> import CLaSH.Prelude -- >>> let delay3 = delay (0 :> 0 :> 0 :> Nil)--- >>> let delay2 = delayI :: DSignal (n - 2) Int -> DSignal n Int+-- >>> let delay2 = delayI :: DSignal n Int -> DSignal (n + 2) Int -- >>> :{ -- let mac :: DSignal 0 Int -> DSignal 0 Int -> DSignal 0 Int -- mac x y = feedback (mac' x y)@@ -98,19 +98,19 @@ dfromList :: [a] -> DSignal 0 a dfromList = coerce . fromList --- | Delay a 'DSignal' for @m@ periods.+-- | Delay a 'DSignal' for @d@ periods. -- -- @--- delay3 :: 'DSignal' (n - 3) Int -> 'DSignal' n Int+-- delay3 :: 'DSignal' n Int -> 'DSignal' (n + 3) Int -- delay3 = 'delay' (0 ':>' 0 ':>' 0 ':>' 'Nil') -- @ -- -- >>> sampleN 6 (delay3 (dfromList [1..])) -- [0,0,0,1,2,3]-delay :: forall a n m . KnownNat m- => Vec m a- -> DSignal (n - m) a+delay :: forall a n d . KnownNat d+ => Vec d a -> DSignal n a+ -> DSignal (n + d) a delay m ds = coerce (delay' (coerce ds)) where delay' :: Signal a -> Signal a@@ -123,15 +123,15 @@ -- | Delay a 'DSignal' for @m@ periods, where @m@ is derived from the context. -- -- @--- delay2 :: 'DSignal' (n - 2) Int -> 'DSignal' n Int+-- delay2 :: 'DSignal' n Int -> 'DSignal' (n + 2) Int -- delay2 = 'delayI' -- @ -- -- >>> sampleN 6 (delay2 (dfromList [1..])) -- [0,0,1,2,3,4]-delayI :: (Default a, KnownNat m)- => DSignal (n - m) a- -> DSignal n a+delayI :: (Default a, KnownNat d)+ => DSignal n a+ -> DSignal (n + d) a delayI = delay (repeat def) -- | Feed the delayed result of a function back to its input:@@ -148,8 +148,8 @@ -- -- >>> sampleN 6 (mac (dfromList [1..]) (dfromList [1..])) -- [0,1,5,14,30,55]-feedback :: (DSignal (n - m - 1) a -> (DSignal (n - m - 1) a,DSignal n a))- -> DSignal (n - m - 1) a+feedback :: (DSignal n a -> (DSignal n a,DSignal (n + m + 1) a))+ -> DSignal n a feedback f = let (o,r) = f (coerce r) in o -- | 'Signal's are not delayed@@ -178,5 +178,5 @@ -- acc' = (x * y) + 'antiDelay' d1 acc -- acc = 'delay' ('singleton' 0) acc' -- @-antiDelay :: SNat d -> DSignal n a -> DSignal (n - d) a+antiDelay :: SNat d -> DSignal (n + d) a -> DSignal n a antiDelay _ = coerce
src/CLaSH/Signal/Explicit.hs view
@@ -23,6 +23,7 @@ , withSClock , SystemClock , systemClock+ , freqCalc -- ** Synchronisation primitive , unsafeSynchronizer -- * Basic circuit functions@@ -136,6 +137,44 @@ -- | The singleton clock for 'SystemClock' systemClock :: SClock SystemClock systemClock = sclock++-- | Calculate relative periods given a list of frequencies.+--+-- So for example, you have one part of your design connected to an ADC running+-- at 20 MHz, one part of your design connected to a DAC running at 36 MHz, and+-- the rest of your system is running at 50 MHz. What are the relative+-- (integer) clock periods in CλaSH, such that their ratios correspond to the+-- ratios between the actual clock frequencies.+--+-- For this we use 'freqCalc':+--+-- >>> freqCalc [20,36,50]+-- [45,25,18]+--+-- So that we create the proper clocks:+--+-- @+-- type ADC20 = 'Clk' \"ADC\" 45+-- type DAC36 = 'Clk' \"DAC\" 25+-- type Sys50 = 'Clk' \"Sys\" 18+--+-- sys50 :: SClock System50+-- sys50 = 'sclock'+--+-- adc20 :: SClock ADC20+-- adc20 = 'sclock'+--+-- dac36 :: SClock DAC36+-- dac36 = 'sclock'+-- @+--+-- __NB__: This function is /not/ synthesisable+freqCalc :: [Integer] -> [Integer]+freqCalc xs = map (`div` g) ys+ where+ p = product xs+ ys = map (p `div`) xs+ g = foldr1 gcd ys -- ** Synchronisation primitive {-# NOINLINE unsafeSynchronizer #-}
src/CLaSH/Signal/Internal.hs view
@@ -70,6 +70,8 @@ , unsafeShiftR1 , rotateL1 , rotateR1+ -- * EXTREMELY EXPERIMENTAL+ , joinSignal# ) where @@ -140,6 +142,19 @@ appSignal# :: Signal' clk (a -> b) -> Signal' clk a -> Signal' clk b appSignal# (f :- fs) ~(a :- as) = f a :- appSignal# fs as +{-# NOINLINE joinSignal# #-}+-- | __WARNING: EXTREMELY EXPERIMENTAL__+--+-- The circuit semantics of this operation are unclear and/or non-existent.+-- There is a good reason there is no 'Monad' instance for 'Signal''.+--+-- Is currently treated as 'id' by the CLaSH compiler.+joinSignal# :: Signal' clk (Signal' clk a) -> Signal' clk a+joinSignal# ~(xs :- xss) = head# xs :- joinSignal# (mapSignal# tail# xss)+ where+ head# (x' :- _ ) = x'+ tail# (_ :- xs') = xs'+ instance Num a => Num (Signal' clk a) where (+) = liftA2 (+) (-) = liftA2 (-)@@ -219,9 +234,15 @@ s' = mux b s r {-# INLINE mux #-}--- | A multiplexer. Given "@'mux' b t f@", output @t@ when @b@ is 'True', and @f@+-- | The above type is a generalisation for:+--+-- @+-- __mux__ :: 'CLaSH.Signal.Signal' 'Bool' -> 'CLaSH.Signal.Signal' a -> 'CLaSH.Signal.Signal' a -> 'CLaSH.Signal.Signal' a+-- @+--+-- A multiplexer. Given "@'mux' b t f@", output @t@ when @b@ is 'True', and @f@ -- when @b@ is 'False'.-mux :: Signal' clk Bool -> Signal' clk a -> Signal' clk a -> Signal' clk a+mux :: Applicative f => f Bool -> f a -> f a -> f a mux = liftA3 (\b t f -> if b then t else f) {-# INLINE signal #-}
src/CLaSH/Sized/Fixed.hs view
@@ -47,6 +47,8 @@ , divide -- * Compile-time 'Double' conversion , fLit+ -- * Run-time 'Double' conversion (not synthesisable)+ , fLitR -- * 'Fixed' point wrapper , Fixed (..), resizeF, fracShift -- * Constraint synonyms@@ -591,6 +593,170 @@ truncated = truncate shifted :: Integer shifted = a * (2 ^ (natVal (Proxy :: Proxy frac))) +-- | Convert, at run-time, a 'Double' to a 'Fixed'-point.+--+-- __NB__: this functions is /not/ synthesisable+--+-- = Creating data-files #creatingdatafiles#+--+-- An example usage of this function is for example to convert a data file+-- containing 'Double's to a data file with ASCI-encoded binary numbers to be+-- used by a synthesisable function like 'CLaSH.Prelude.ROM.File.asyncRomFile'.+-- For example, given a file @Data.txt@ containing:+--+-- @+-- 1.2 2.0 3.0 4.0+-- -1.0 -2.0 -3.5 -4.0+-- @+--+-- which we want to put in a ROM, interpreting them as @8.8@ signed fixed point+-- numbers. What we do is that we first create a conversion utility,+-- @createRomFile@, which uses 'fLitR':+--+-- @createRomFile.hs@:+--+-- @+-- module Main where+--+-- import CLaSH.Prelude+-- import System.Environment+-- import qualified Data.List as L+--+-- createRomFile :: KnownNat n => (Double -> BitVector n)+-- -> FilePath -> FilePath -> IO ()+-- createRomFile convert fileR fileW = do+-- f <- readFile fileR+-- let ds :: [Double]+-- ds = L.concat . (L.map . L.map) read . L.map words $ lines f+-- bvs = L.map (filter (/= '_') . show . convert) ds+-- writeFile fileW (unlines bvs)+--+-- toSFixed8_8 :: Double -> SFixed 8 8+-- toSFixed8_8 = 'fLitR'+--+-- main :: IO ()+-- main = do+-- [fileR,fileW] <- getArgs+-- createRomFile ('pack' . toSFixed8_8) fileR fileW+-- @+--+-- We then compile this to an executable:+--+-- @+-- $ clash --make createRomFile.hs+-- @+--+-- We can then use this utility to convert our @Data.txt@ file which contains+-- 'Double's to a @Data.bin@ file which will containing the desired ASCI-encoded+-- binary data:+--+-- @+-- $ ./createRomFile \"Data.txt\" \"Data.bin\"+-- @+--+-- Which results in a @Data.bin@ file containing:+--+-- @+-- 0000000100110011+-- 0000001000000000+-- 0000001100000000+-- 0000010000000000+-- 1111111100000000+-- 1111111000000000+-- 1111110010000000+-- 1111110000000000+-- @+--+-- We can then use this @Data.bin@ file in for our ROM:+--+-- @+-- romF :: Unsigned 3 -> Unsigned 3 -> SFixed 8 8+-- romF rowAddr colAddr = 'unpack'+-- $ 'CLaSH.Prelude.ROM.File.asyncRomFile' d8 "Data.bin" ((rowAddr * 4) + colAddr)+-- @+--+-- And see that it works as expected:+--+-- @+-- __>>> romF 1 2__+-- -3.5+-- __>>> romF 0 0__+-- 1.19921875+-- @+--+-- == Using Template Haskell+--+-- For those of us who like to live on the edge, another option is to convert+-- our @Data.txt@ at compile-time using+-- <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/template-haskell.html Template Haskell>.+-- For this we first create a module @CreateRomFileTH.hs@:+--+-- @+-- module CreateRomFileTH (romDataFromFile) where+--+-- import CLaSH.Prelude+-- import qualified Data.List as L+-- import Language.Haskell.TH (ExpQ, litE, stringL)+-- import Language.Haskell.TH.Syntax (qRunIO)+--+-- createRomFile :: KnownNat n => (Double -> BitVector n)+-- -> FilePath -> FilePath -> IO ()+-- createRomFile convert fileR fileW = do+-- f <- readFile fileR+-- let ds :: [Double]+-- ds = L.concat . (L.map . L.map) read . L.map words $ lines f+-- bvs = L.map (filter (/= '_') . show . convert) ds+-- writeFile fileW (unlines bvs)+--+-- romDataFromFile :: KnownNat n => (Double -> BitVector n) -> String -> ExpQ+-- romDataFromFile convert fileR = do+-- let fileW = fileR L.++ ".bin"+-- bvF <- qRunIO (createRomFile convert fileR fileW)+-- litE (stringL fileW)+-- @+--+-- Instead of first converting @Data.txt@ to @Data.bin@, we will now use the+-- @romDataFromFile@ function to convert @Data.txt@ to a new file in the proper+-- format at compile-time of our new @romF'@ function:+--+-- @+-- import CLaSH.Prelude+-- import CreateRomFileTH+--+-- toSFixed8_8 :: Double -> SFixed 8 8+-- toSFixed8_8 = 'fLitR'+--+-- romF' :: Unsigned 3 -> Unsigned 3 -> SFixed 8 8+-- romF' rowAddr colAddr = unpack $+-- asyncRomFile d8+-- $(romDataFromFile (pack . toSFixed8_8) "Data.txt") -- Template Haskell splice+-- ((rowAddr * 4) + colAddr)+-- @+--+-- And see that it works just like the @romF@ function from earlier:+--+-- @+-- __>>> romF' 1 2__+-- -3.5+-- __>>> romF' 0 0__+-- 1.19921875+-- @+fLitR :: forall rep int frac size .+ ( size ~ (int + frac), KnownNat frac, Bounded (rep size)+ , Integral (rep size))+ => Double+ -> Fixed rep int frac+fLitR 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))) instance NumFixedC rep int frac => SaturatingNum (Fixed rep int frac) where satPlus w (Fixed a) (Fixed b) = Fixed (satPlus w a b)
src/CLaSH/Sized/Internal/BitVector.hs view
@@ -74,7 +74,6 @@ -- ** Integral , quot# , rem#- , mod# , toInteger# -- ** Bits , and#@@ -299,18 +298,16 @@ quot = quot# rem = rem# div = quot#- mod = mod#+ mod = rem# quotRem n d = (n `quot#` d,n `rem#` d)- divMod n d = (n `quot#` d,n `mod#` d)+ divMod n d = (n `quot#` d,n `rem#` d) toInteger = toInteger# -quot#,rem#,mod# :: BitVector n -> BitVector n -> BitVector n+quot#,rem# :: BitVector n -> BitVector n -> BitVector n {-# NOINLINE quot# #-} quot# (BV i) (BV j) = BV (i `quot` j) {-# NOINLINE rem# #-} rem# (BV i) (BV j) = BV (i `rem` j)-{-# NOINLINE mod# #-}-mod# (BV i) (BV j) = BV (i `mod` j) {-# NOINLINE toInteger# #-} toInteger# :: BitVector n -> Integer
src/CLaSH/Sized/Internal/Index.hs view
@@ -40,7 +40,6 @@ -- ** Integral , quot# , rem#- , mod# , toInteger# ) where@@ -175,18 +174,16 @@ quot = quot# rem = rem# div = quot#- mod = mod#+ mod = rem# quotRem n d = (n `quot#` d,n `rem#` d)- divMod n d = (n `quot#` d,n `mod#` d)+ divMod n d = (n `quot#` d,n `rem#` d) toInteger = toInteger# -quot#,rem#,mod# :: Index n -> Index n -> Index n+quot#,rem# :: Index n -> Index n -> Index n {-# NOINLINE quot# #-} (I a) `quot#` (I b) = I (a `div` b) {-# NOINLINE rem# #-} (I a) `rem#` (I b) = I (a `rem` b)-{-# NOINLINE mod# #-}-(I a) `mod#` (I b) = I (a `mod` b) {-# NOINLINE toInteger# #-} toInteger# :: Index n -> Integer
src/CLaSH/Sized/Internal/Unsigned.hs view
@@ -55,7 +55,6 @@ -- ** Integral , quot# , rem#- , mod# , toInteger# -- ** Bits , and#@@ -122,9 +121,9 @@ -- >>> satMin SatSymmetric 2 3 :: Unsigned 3 -- 0 newtype Unsigned (n :: Nat) =- -- | The constructor, 'U', and the field, 'unsafeToBitVector', are not+ -- | The constructor, 'U', and the field, 'unsafeToInteger', are not -- synthesisable.- U { unsafeToBitVector :: Integer }+ U { unsafeToInteger :: Integer } {-# NOINLINE size# #-} size# :: KnownNat n => Unsigned n -> Int@@ -272,18 +271,16 @@ quot = quot# rem = rem# div = quot#- mod = mod#+ mod = rem# quotRem n d = (n `quot#` d,n `rem#` d)- divMod n d = (n `quot#` d,n `mod#` d)+ divMod n d = (n `quot#` d,n `rem#` d) toInteger = toInteger# -quot#,rem#,mod# :: Unsigned n -> Unsigned n -> Unsigned n+quot#,rem# :: Unsigned n -> Unsigned n -> Unsigned n {-# NOINLINE quot# #-} quot# (U i) (U j) = U (i `quot` j) {-# NOINLINE rem# #-} rem# (U i) (U j) = U (i `rem` j)-{-# NOINLINE mod# #-}-mod# (U i) (U j) = U (i `mod` j) {-# NOINLINE toInteger# #-} toInteger# :: Unsigned n -> Integer
src/CLaSH/Sized/Vector.hs view
@@ -790,8 +790,8 @@ -- 2 -- >>> (1:>2:>3:>4:>5:>Nil) !! 14 -- *** Exception: CLaSH.Sized.Vector.(!!): index 14 is larger than maximum index 4-(!!) :: (KnownNat n, Integral i) => Vec n a -> i -> a-xs !! i = index_int xs (fromIntegral i)+(!!) :: (KnownNat n, Enum i) => Vec n a -> i -> a+xs !! i = index_int xs (fromEnum i) {-# NOINLINE maxIndex #-} -- | Index (subscript) of the last element in a 'Vec'tor@@ -837,8 +837,8 @@ -- <7,2,3,4,5> -- >>> replace 9 7 (1:>2:>3:>4:>5:>Nil) -- <1,2,3,4,*** Exception: CLaSH.Sized.Vector.replace: index 9 is larger than maximum index 4-replace :: (KnownNat n, Integral i) => i -> a -> Vec n a -> Vec n a-replace i y xs = replace_int xs (fromIntegral i) y+replace :: (KnownNat n, Enum i) => i -> a -> Vec n a -> Vec n a+replace i y xs = replace_int xs (fromEnum i) y {-# INLINABLE take #-} -- | 'take' @n@, applied to a vector @xs@, returns the @n@-length prefix of @xs@
src/CLaSH/Tutorial.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE NoImplicitPrelude, MagicHash #-} {-# OPTIONS_GHC -fno-warn-unused-imports #-} {-|@@ -27,7 +27,7 @@ -- *** Circuit testbench -- $mac4 - -- *** Generating SystemVerilog+ -- *** Generating Verilog and SystemVerilog -- $mac5 -- *** Alternative specifications@@ -42,6 +42,9 @@ -- * TopEntity annotations: controlling the VHDL\/Verilog\/SystemVerilog generation. -- $annotations + -- * Multiple clock domains+ -- $multiclock+ -- * Advanced: Primitives -- $primitives @@ -67,9 +70,11 @@ import CLaSH.Prelude import CLaSH.Prelude.Explicit+import CLaSH.Prelude.BlockRam import Data.Char import Data.Int import GHC.Word+import Data.Default -- $setup -- >>> :set -XTemplateHaskell@@ -524,10 +529,13 @@ >>> sampleN 7 $ expectedOutput (topEntity testInput) [False,False,False,False,+cycle(system1000): 4, outputVerifier expected value: 14, not equal to actual value: 30 True,+cycle(system1000): 5, outputVerifier expected value: 14, not equal to actual value: 46 True,+cycle(system1000): 6, outputVerifier expected value: 14, not equal to actual value: 62 True] @@ -713,7 +721,7 @@ which, together with 'bundle' and 'unbundle' 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 'bundle' and 'unbundle'.+and can be converted from, or to, the other using 'bundle' and 'unbundle'. Instances of this 'Bundle' type-class are defined as /isomorphisms/ for: @@ -999,72 +1007,73 @@ 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 'cblockRam' as an example, for which the Haskell/CλaSH code is:+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--- * \_\_NB\_\_: Initial output value is \'undefined\'------ \@--- type ClkA = Clk \\\"A\\\" 100------ clkA100 :: SClock ClkA--- clkA100 = sclock------ bram40 :: Signal' ClkA (Unsigned 6) -> Signal' ClkA (Unsigned 6)--- -> Signal' ClkA Bool -> Signal' ClkA Bit -> Signal' ClkA Bit--- bram40 = \'blockRam'' clkA100 (\'CLaSH.Sized.Vector.replicate\' d40 1)--- \@-blockRam' :: ('GHC.TypeLits.KnownNat' n, 'GHC.TypeLits.KnownNat' m)- => 'SClock' clk -- ^ \'Clock\' to synchronize to- -> 'Vec' n a -- ^ Initial content of the BRAM, also- -- determines the size, \@n\@, of the BRAM.- --- -- \_\_NB\_\_: \_\_MUST\_\_ be a constant.- -> 'Signal'' clk ('Unsigned' m) -- ^ Write address \@w\@- -> 'Signal'' clk ('Unsigned' m) -- ^ Read address \@r\@- -> 'Signal'' clk Bool -- ^ Write enable- -> 'Signal'' clk a -- ^ Value to write (at address \@w\@)+{\-\# NOINLINE blockRam# \#-\}+-- | blockRAM primitive+blockRam' :: 'GHC.TypeLits.KnownNat' n+ => 'SClock' clk -- ^ \'Clock\' to synchronize to+ -> 'Vec' n a -- ^ Initial content of the BRAM, also+ -- determines the size, \@n\@, of the BRAM.+ --+ -- \_\_NB\_\_: \_\_MUST\_\_ be a constant.+ -> 'Signal'' clk 'Int' -- ^ Write address \@w\@+ -> 'Signal'' clk 'Int' -- ^ Read address \@r\@+ -> 'Signal'' clk Bool -- ^ Write enable+ -> 'Signal'' clk a -- ^ Value to write (at address \@w\@) -> 'Signal'' clk a -- ^ Value of the \@blockRAM\@ at address \@r\@ from the previous clock -- cycle-blockRam' clk binit wr rd en din =- 'mealy'' clk bram' (binit,undefined) ('bundle'' clk (wr,rd,en,din))+blockRam' clk binit wr rd en din = 'register'' clk undefined dout where- bram' (ram,o) (w,r,e,d) = ((ram',o'),o)- where- ram' | e = 'replace' ram w d- | otherwise = ram- o' = ram '!!' r+ szI = fromInteger $ 'maxIndex' content+ dout = runST $ do+ arr <- newListArray (0,szI) ('toList' content)+ traverse (ramT arr) ('bundle'' clk (wr,rd,en,din))++ ramT :: STArray s Int e -> (Int,Int,Bool,e) -> ST s e+ ramT ram (w,r,e,d) = do+ d' <- readArray ram r+ when e (writeArray ram w d)+ return d' @ And for which the /definition/ primitive is: @ { \"BlackBox\" :- { "name" : "CLaSH.Prelude.BlockRam.blockRam'"+ { "name" : "CLaSH.Prelude.BlockRam.blockRam#" , "templateD" : "blockRam_~COMPNAME_~SYM[0] : block- signal ~SYM[1] : ~TYP[3] := ~LIT[3]; -- ram- signal ~SYM[2] : ~TYP[7]; -- inp- signal ~SYM[3] : ~TYP[7]; -- outp+ signal RAM : ~TYP[2] := ~LIT[2];+ signal dout : ~TYP[6];+ signal wr : integer range 0 to ~LIT[0] - 1;+ signal rd : integer range 0 to ~LIT[0] - 1; begin- ~SYM[2] <= ~ARG[7];+ wr <= ~ARG[3]+ -- pragma translate_off+ mod ~LIT[0]+ -- pragma translate_on+ ; - process(~CLK[2])+ rd <= ~ARG[4]+ -- pragma translate_off+ mod ~LIT[0]+ -- pragma translate_on+ ;++ blockRam_~SYM[1] : process(~CLK[1]) begin- if rising_edge(~CLK[2]) then- if ~ARG[6] then- ~SYM[1](to_integer(~ARG[4])) <= ~SYM[2];+ if rising_edge(~CLK[1]) then+ if ~ARG[5] then+ RAM(wr) <= ~ARG[6]; end if;- ~SYM[3] <= ~SYM[1](to_integer(~ARG[5]));+ dout <= RAM(rd); end if; end process; - ~RESULT <= ~SYM[3];+ ~RESULT <= dout; end block;" } }@@ -1138,29 +1147,30 @@ @ { \"BlackBox\" :- { "name" : "CLaSH.Prelude.BlockRam.blockRam'"+ { "name" : "CLaSH.Prelude.BlockRam.blockRam#" , "templateD" :-"// blockRam-reg ~TYPO ~SYM[0] [0:~LIT[0]-1];-reg ~SIGD[~SYM[1]][7];+"// blockRam begin+reg ~TYPO RAM_~SYM[0] [0:~LIT[0]-1];+reg ~TYPO dout_~SYM[1]; -reg ~TYP[3] ~SYM[2];+reg ~TYP[2] ram_init_~SYM[2]; integer ~SYM[3]; initial begin- ~SYM[2] = ~ARG[3];+ ram_init_~SYM[2] = ~ARG[2]; for (~SYM[3]=0; ~SYM[3] < ~LIT[0]; ~SYM[3] = ~SYM[3] + 1) begin- ~SYM[0][~LIT[0]-1-~SYM[3]] = ~SYM[2][~SYM[3]*~SIZE[~TYPO]+:~SIZE[~TYPO]];+ RAM_~SYM[0][~LIT[0]-1-~SYM[3]] = ram_init_~SYM[2][~SYM[3]*~SIZE[~TYPO]+:~SIZE[~TYPO]]; end end -always @(posedge ~CLK[2]) begin : blockRam_~COMPNAME_~SYM[4]- if (~ARG[6]) begin- ~SYM[0][~ARG[4]] <= ~ARG[7];+always @(posedge ~CLK[1]) begin : blockRam_~COMPNAME_~SYM[4]+ if (~ARG[5]) begin+ RAM_~SYM[0][~ARG[3]] <= ~ARG[6]; end- ~SYM[1] <= ~SYM[0][~ARG[5]];+ dout_~SYM[1] <= RAM_~SYM[0][~ARG[4]]; end -assign ~RESULT = ~SYM[1];"+assign ~RESULT = dout_~SYM[1];+// blockRam end" } } @@@ -1184,23 +1194,304 @@ { "name" : "CLaSH.Prelude.BlockRam.blockRam'" , "templateD" : "// blockRam-~SIGD[~SYM[0]][3];-~SIGD[~SYM[1]][7];+~SIGD[RAM_~SYM[0]][2];+~SIGD[dout_~SYM[1]][6]; initial begin ~SYM[0] = ~LIT[3]; end -always @(posedge ~CLK[2]) begin- if (~ARG[6]) begin- ~SYM[0][~ARG[4]] <= ~ARG[7];+always @(posedge ~CLK[1]) begin : blockRam_~COMPNAME_~SYM[3]+ if (~ARG[5]) begin+ RAM_~SYM[0][~ARG[3]] <= ~ARG[6]; end- ~SYM[1] <= ~SYM[0][~ARG[5]];+ dout_~SYM[1] <= RAM_~SYM[0][~ARG[4]]; end -assign ~RESULT = ~SYM[1];"+assign ~RESULT = dout_~SYM[1];" } }+@++-}++{- $multiclock #multiclock#+CλaSH supports multi-clock designs, though perhaps in a slightly limited form.+What is possible is:++* Explicitly assign clocks to memory primitives.+* Synchronise between differently-clocked parts of your design in a type-safe+ way.++What is /not/ possible is:++* Generate a clock signal in module A, and assign this clock signal to a memory+ primitive in module B.++What this means is that when CλaSH converts your design to VHDL/(System)Verilog,+you end up with a top-level module/entity with multiple clock and reset ports+for the different clock domains. If you're targeting an FPGA, you can use e.g. a+<https://www.altera.com/literature/ug/ug_altpll.pdf PPL> or+<http://www.xilinx.com/support/documentation/user_guides/ug472_7Series_Clocking.pdf MMCM>+to provide the clock signals.++== Building a FIFO synchroniser++This part of the tutorial assumes you know what <https://en.wikipedia.org/wiki/Metastability_in_electronics metastability>+is, and how it can never truly be avoided in any asynchronous circuit. Also+it assumes that you are familiar with the design of synchronizer circuits, and+why a dual flip-flop synchroniser only works for bit-synchronisation and not+word-synchronisation.+The explicitly clocked versions of all synchronous functions and primitives can+be found in "CLaSH.Prelude.Explicit", which also re-exports the functions in+"CLaSH.Signal.Explicit". We will use those functions to create a FIFO where+the read and write port are synchronised to different clocks. Below you can find+the code to build the FIFO synchroniser based on the design described in:+<http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf>++We start with enable a few options that will make wring the type-signatures for+our components a bit easier. We'll also import the standard "CLaSH.Prelude"+module, and the "CLaSH.Prelude.Explicit" module for our explicitly clocked+synchronous functions:++@+{\-\# LANGUAGE PartialTypeSignatures \#-\}+{\-\# OPTIONS_GHC -fno-warn-partial-type-signatures \#-\}+module MultiClockFifo where++import CLaSH.Prelude+import CLaSH.Prelude.Explicit+@++Then we'll start with the /hart/ of the FIFO synchroniser, an asynchronous RAM+in the form of 'asyncRam''. It's called an asynchronous RAM because the read+port is not synchronised to any clock (though the write port is). Note that in+CλaSH we don't really have asynchronous logic, there is only combinational and+synchronous logic. As a consequence, we see in the type signature of 'asyncRam'':++ *++ @+ __asyncRam'__ :: _ => SClock wclk -- ^ Clock to which to synchronise the write port of the RAM+ -> SClock rclk -- ^ Clock to which the read address signal __r__ is synchronised+ -> SNat n -- ^ Size __n__ of the RAM+ -> Signal' wclk addr -- ^ Write address __w__+ -> Signal' rclk addr -- ^ Read address __r__+ -> Signal' wclk Bool -- ^ Write enable+ -> Signal' wclk a -- ^ Value to write (at address __w__)+ -> Signal' rclk a -- ^ Value of the RAM at address __r__+ @++that the signal containing the read address __r__ is synchronised to a different+clock. That is, there is __no__ such thing as an @AsyncSignal@ in CλaSH.++We continue by instantiating the 'asyncRam'':++@+fifoMem wclk rclk addrSize waddr raddr winc wfull wdata =+ 'asyncRam'' wclk rclk+ (d2 ``powSNat`` addrSize)+ waddr raddr+ (winc '.&&.' 'not1' wfull)+ wdata+@++We see that we give it @2^addrSize@ elements, where @addrSize@ is the bit-size+of the address. Also, we only write new values to the ram when a new write is+requested, indicated by @winc@, and the buffer is not full, indicated by+@wfull@.++The next part of the design calculates the read and write address for the+asynchronous RAM, and creates the flags indicating whether the FIFO is full+or empty. We start with a function that converts 'Bool'eans to @n + 1@ bit+bitvectors:++@+boolToBV :: (KnownNat n, KnownNat (n+1)) => Bool -> BitVector (n + 1)+boolToBV = 'zeroExtend' . 'pack'+@++followed by the actual address and flag generator in 'mealy' machine style:++@+ptrCompareT addrSize flagGen (bin,ptr,flag) (s_ptr,inc) = ((bin',ptr',flag')+ ,(flag,addr,ptr))+ where+ -- GRAYSTYLE2 pointer+ bin' = bin + boolToBV (inc && not flag)+ ptr' = (bin' \`shiftR\` 1) \`xor\` bin'+ addr = 'slice' (addrSize ``subSNat`` d1) d0 bin++ flag' = flagGen ptr' s_ptr+@++It is parametrised in both address size, @addrSize@, and status flag generator,+@flagGen@. It has two inputs, @s_ptr@, the synchronised pointer from the other+clock domain, and @inc@, which indicates we want to perform a write or read of+the FIFO. It creates three outputs: @flag@, the full or empty flag, @addr@, the+read or write address into the RAM, and @ptr@, the Gray-encoded version of the+read or write address which will be synchronised between the two clock domains.++Next follow the initial states of address generators, and the flag generators+for the empty and full flags:++@+-- FIFO empty: when next pntr == synchronized wptr or on reset+isEmpty = (==)+rptrEmptyInit = (0,0,True)++-- FIFO full: when next pntr == synchonized {~wptr[addrSize:addrSize-1],wptr[addrSize-1:0]}+isFull addrSize ptr s_ptr = ptr == ('complement' ('slice' addrSize (addrSize ``subSNat`` d1) s_ptr) '++#'+ 'slice' (addrSize ``subSNat`` d2) d0 s_ptr)+wptrFullInit = (0,0,False)+@++We create a dual flip-flop synchroniser to be used to synchronise the+Gray-encoded pointers between the two clock domains:++@+ptrSync clk1 clk2 = 'register'' clk2 0+ . 'register'' clk2 0+ . 'unsafeSynchronizer' clk1 clk2+@++It uses the 'unsafeSynchroniser' primitive, which is needed to go from one clock+domain to the other. All synchronizers are specified in terms of+'unsafeSynchronizer' (see for example the <src/CLaSH-Prelude-RAM.html#line-103 source of asyncRam#>).+The 'unsafeSynchronizer' primitive is turned into a (bundle of) wire(s) by the+CλaSH compiler, so developers must ensure that it is only used as part of a+proper synchronizer.++Finally we combine all the component in:++@+fifo :: _+ => SNat addrSize -> SClock wclk -> SClock rclk+ -> Signal' wclk a -> Signal' wclk Bool+ -> Signal' rclk Bool+ -> (Signal' rclk a, Signal' rclk Bool, Signal' wclk Bool)+fifo addrSize wclk rclk wdata winc rinc = (rdata,rempty,wfull)+ where+ s_rptr = ptrSync wclk rclk rptr+ s_wptr = ptrSync rclk wclk wptr++ rdata = fifoMem wclk rclk addrSize waddr raddr winc wfull wdata++ (rempty,raddr,rptr) = 'mealyB'' rclk (ptrCompareT addrSize isEmpty) rptrEmptyInit+ (s_wptr,rinc)++ (wfull,waddr,wptr) = 'mealyB'' wclk (ptrCompareT addrSize (isFull addrSize))+ wptrFullInit (s_rptr,winc)+@++where we first specify the synchronisation of the read and the write pointers,+instantiate the asynchronous RAM, and instantiate the read address/pointer/flag+generator and write address/pointer/flag generator.++Ultimately, the whole file containing our FIFO design will look like this:++@+{\-\# LANGUAGE PartialTypeSignatures \#-\}+{\-\# OPTIONS_GHC -fno-warn-partial-type-signatures \#-\}+module MultiClockFifo where++import CLaSH.Prelude+import CLaSH.Prelude.Explicit++fifoMem wclk rclk addrSize waddr raddr winc wfull wdata =+ 'asyncRam'' wclk rclk+ (d2 ``powSNat`` addrSize)+ waddr raddr+ (winc '.&&.' 'not1' wfull)+ wdata++boolToBV :: (KnownNat n, KnownNat (n+1)) => Bool -> BitVector (n + 1)+boolToBV = 'zeroExtend' . 'pack'++ptrCompareT addrSize flagGen (bin,ptr,flag) (s_ptr,inc) = ((bin',ptr',flag')+ ,(flag,addr,ptr))+ where+ -- GRAYSTYLE2 pointer+ bin' = bin + boolToBV (inc && not flag)+ ptr' = (bin' \`shiftR\` 1) \`xor\` bin'+ addr = 'slice' (addrSize ``subSNat`` d1) d0 bin++ flag' = flagGen ptr' s_ptr++-- FIFO empty: when next pntr == synchronized wptr or on reset+isEmpty = (==)+rptrEmptyInit = (0,0,True)++-- FIFO full: when next pntr == synchonized {~wptr[addrSize:addrSize-1],wptr[addrSize-1:0]}+isFull addrSize ptr s_ptr = ptr == ('complement' ('slice' addrSize (addrSize ``subSNat`` d1) s_ptr) '++#'+ 'slice' (addrSize ``subSNat`` d2) d0 s_ptr)+wptrFullInit = (0,0,False)++-- Dual flip-flip synchroniser+ptrSync clk1 clk2 = 'register'' clk2 0+ . 'register'' clk2 0+ . 'unsafeSynchronizer' clk1 clk2++-- Async FIFO synchroniser+fifo :: _+ => SNat addrSize -> SClock wclk -> SClock rclk+ -> Signal' wclk a -> Signal' wclk Bool+ -> Signal' rclk Bool+ -> (Signal' rclk a, Signal' rclk Bool, Signal' wclk Bool)+fifo addrSize wclk rclk wdata winc rinc = (rdata,rempty,wfull)+ where+ s_rptr = ptrSync rclk wclk rptr+ s_wptr = ptrSync wclk rclk wptr++ rdata = fifoMem wclk rclk addrSize waddr raddr winc wfull wdata++ (rempty,raddr,rptr) = 'mealyB'' rclk (ptrCompareT addrSize isEmpty) rptrEmptyInit+ (s_wptr,rinc)++ (wfull,waddr,wptr) = 'mealyB'' wclk (ptrCompareT addrSize (isFull addrSize))+ wptrFullInit (s_rptr,winc)+@++== Instantiating a FIFO synchroniser++Having finished our FIFO synchroniser it's time to instantiate with concrete+clock domains. Let us assume we have part of our system connected to an ADC+which runs at 20 MHz, and we have created an FFT component running at only 9 MHz,+while the rest of our system runs at 50 MHz. What we want to do connect part+of our design connected to the ADC, and running at 20 MHz, to part of our design+connected to the FFT running at 9 MHz.++First, we must calculate the relative clock periods using 'freqCalc':++>>> freqCalc [20,9,50]+[45,100,18]++We can then create the clocks:++@+type ClkADC = 'Clk \"ADC\" 45+type ClkFFT = 'Clk \"FFT\" 100+type ClkSys = 'Clk \"System\" 18++clkADC :: SClock ClkADC+clkADC = sclock++clkFFT :: SClock ClkFFT+clkFFT = sclock++clkSys :: SClock ClkSys+clkSys = sclock+@++and subsequently a 256-space FIFO synchroniser that safely bridges the ADC clock+domain and to the FFT clock domain:++@+adcToFFT :: Signal' ClkADC (SFixed 8 8)+ -> Signal' ClkADC Bool+ -> Signal' ClkFFT Bool+ -> (Signal' ClkFFT (SFixed 8 8), Signal' ClkFFT Bool, Signal' ClkADC Bool)+adcToFFT = fifo d8 clkADC clkFFT @ -}