clash-prelude-0.11: src/CLaSH/Prelude/Moore.hs
{-|
Copyright : (C) 2013-2016, University of Twente
License : BSD2 (see the file LICENSE)
Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com>
Whereas the output of a Mealy machine depends on /current transition/, the
output of a Moore machine depends on the /previous state/.
Moore machines are strictly less expressive, but may impose laxer timing
requirements.
-}
{-# LANGUAGE Safe #-}
module CLaSH.Prelude.Moore
( -- * Moore machine synchronised to the system clock
moore
, mooreB
, medvedev
, medvedevB
-- * Moore machine synchronised to an arbitrary clock
, moore'
, mooreB'
, medvedev'
, medvedevB'
)
where
import CLaSH.Signal (Signal, Unbundled)
import CLaSH.Signal.Explicit (Signal', SClock, register', systemClock)
import CLaSH.Signal.Bundle (Bundle (..), Unbundled')
{- $setup
>>> :set -XDataKinds
>>> import CLaSH.Prelude
>>> :{
let mac s (x,y) = x * y + s
topEntity = moore mac id 0
:}
>>> import CLaSH.Prelude.Explicit
>>> type ClkA = Clk "A" 100
>>> let clkA = sclock :: SClock ClkA
>>> let mac s (x,y) = x * y + s
>>> let topEntity = moore' clkA mac id 0
-}
{-# INLINE moore #-}
-- | Create a synchronous function from a combinational function describing
-- a moore machine
--
-- @
-- mac :: Int -- Current state
-- -> (Int,Int) -- Input
-- -> Int -- Updated state
-- mac s (x,y) = x * y + s
--
-- topEntity :: 'Signal' (Int, Int) -> 'Signal' Int
-- topEntity = 'moore' mac id 0
-- @
--
-- >>> simulate topEntity [(1,1),(2,2),(3,3),(4,4)]
-- [0,1,5,14...
-- ...
--
-- Synchronous sequential functions can be composed just like their
-- combinational counterpart:
--
-- @
-- dualMac :: ('Signal' Int, 'Signal' Int)
-- -> ('Signal' Int, 'Signal' Int)
-- -> 'Signal' Int
-- dualMac (a,b) (x,y) = s1 + s2
-- where
-- s1 = 'moore' mac id 0 ('CLaSH.Signal.bundle' (a,x))
-- s2 = 'moore' mac id 0 ('CLaSH.Signal.bundle' (b,y))
-- @
moore :: (s -> i -> s) -- ^ Transfer function in moore machine form:
-- @state -> input -> newstate@
-> (s -> o) -- ^ Output function in moore machine form:
-- @state -> output@
-> s -- ^ Initial state
-> (Signal i -> Signal o)
-- ^ Synchronous sequential function with input and output matching that
-- of the moore machine
moore = moore' systemClock
{-# INLINE medvedev #-}
-- | Create a synchronous function from a combinational function describing
-- a moore machine without any output logic
medvedev :: (s -> i -> s) -> s -> Signal i -> Signal s
medvedev tr st = moore tr id st
{-# INLINE mooreB #-}
-- | A version of 'moore' that does automatic 'Bundle'ing
--
-- Given a functions @t@ and @o@ of types:
--
-- @
-- __t__ :: Int -> (Bool, Int) -> Int
-- __o__ :: Int -> (Int, Bool)
-- @
--
-- When we want to make compositions of @t@ and @o@ in @g@ using 'moore', we have to
-- write:
--
-- @
-- g a b c = (b1,b2,i2)
-- where
-- (i1,b1) = 'CLaSH.Signal.unbundle' ('moore' t o 0 ('CLaSH.Signal.bundle' (a,b)))
-- (i2,b2) = 'CLaSH.Signal.unbundle' ('moore' t o 3 ('CLaSH.Signal.bundle' (i1,c)))
-- @
--
-- Using 'mooreB' however we can write:
--
-- @
-- g a b c = (b1,b2,i2)
-- where
-- (i1,b1) = 'mooreB' t o 0 (a,b)
-- (i2,b2) = 'mooreB' t o 3 (i1,c)
-- @
mooreB :: (Bundle i, Bundle o)
=> (s -> i -> s) -- ^ Transfer function in moore machine form:
-- @state -> input -> newstate@
-> (s -> o) -- ^ Output function in moore machine form:
-- @state -> output@
-> s -- ^ Initial state
-> (Unbundled i -> Unbundled o)
-- ^ Synchronous sequential function with input and output matching that
-- of the moore machine
mooreB = mooreB' systemClock
{-# INLINE medvedevB #-}
-- | A version of 'medvedev' that does automatic 'Bundle'ing
medvedevB :: (Bundle i, Bundle s) => (s -> i -> s) -> s -> Unbundled i -> Unbundled s
medvedevB tr st = mooreB tr id st
{-# INLINABLE moore' #-}
-- | Create a synchronous function from a combinational function describing
-- a moore machine
--
-- @
-- mac :: Int -- Current state
-- -> (Int,Int) -- Input
-- -> (Int,Int) -- Updated state
-- mac s (x,y) = x * y + s
--
-- type ClkA = 'CLaSH.Signal.Explicit.Clk' \"A\" 100
--
-- clkA :: 'SClock' ClkA
-- clkA = 'CLaSH.Signal.Explicit.sclock'
--
-- topEntity :: 'Signal'' ClkA (Int, Int) -> 'Signal'' ClkA Int
-- topEntity = 'moore'' clkA mac id 0
-- @
--
-- >>> simulate topEntity [(1,1),(2,2),(3,3),(4,4)]
-- [0,1,5,14...
-- ...
--
-- Synchronous sequential functions can be composed just like their
-- combinational counterpart:
--
-- @
-- dualMac :: ('Signal'' clkA Int, 'Signal'' clkA Int)
-- -> ('Signal'' clkA Int, 'Signal'' clkA Int)
-- -> 'Signal'' clkA Int
-- dualMac (a,b) (x,y) = s1 + s2
-- where
-- s1 = 'moore'' clkA mac id 0 ('CLaSH.Signal.Explicit.bundle'' clkA (a,x))
-- s2 = 'moore'' clkA mac id 0 ('CLaSH.Signal.Explicit.bundle'' clkA (b,y))
-- @
moore' :: SClock clk -- ^ 'Clock' to synchronize to
-> (s -> i -> s) -- ^ Transfer function in moore machine form:
-- @state -> input -> newstate@
-> (s -> o) -- ^ Output function in moore machine form:
-- @state -> output@
-> s -- ^ Initial state
-> (Signal' clk i -> Signal' clk o)
-- ^ Synchronous sequential function with input and output matching that
-- of the moore machine
moore' clk ft fo iS = \i -> let s' = ft <$> s <*> i
s = register' clk iS s'
in fo <$> s
{-# INLINE medvedev' #-}
-- | Create a synchronous function from a combinational function describing
-- a moore machine without any output logic
medvedev' :: SClock clk -> (s -> i -> s) -> s -> (Signal' clk i -> Signal' clk s)
medvedev' clk tr st = moore' clk tr id st
{-# INLINE mooreB' #-}
-- | A version of 'moore'' that does automatic 'Bundle'ing
--
-- Given a functions @t@ and @o@ of types:
--
-- @
-- __t__ :: Int -> (Bool, Int) -> Int
-- __o__ :: Int -> (Int, Bool)
-- @
--
-- When we want to make compositions of @t@ and @o@ in @g@ using 'moore'', we have to
-- write:
--
-- @
-- g clk a b c = (b1,b2,i2)
-- where
-- (i1,b1) = 'CLaSH.Signal.Explicit.unbundle'' clk (moore' clk t o 0 ('CLaSH.Signal.Explicit.bundle'' clk (a,b)))
-- (i2,b2) = 'CLaSH.Signal.Explicit.unbundle'' clk (moore' clk t o 3 ('CLaSH.Signal.Explicit.bundle'' clk (i1,c)))
-- @
--
-- Using 'mooreB'' however we can write:
--
-- @
-- g clk a b c = (b1,b2,i2)
-- where
-- (i1,b1) = 'mooreB'' clk t o 0 (a,b)
-- (i2,b2) = 'mooreB'' clk to 3 (i1,c)
-- @
mooreB' :: (Bundle i, Bundle o)
=> SClock clk
-> (s -> i -> s) -- ^ Transfer function in moore machine form:
-- @state -> input -> newstate@
-> (s -> o) -- ^ Output function in moore machine form:
-- @state -> output@
-> s -- ^ Initial state
-> (Unbundled' clk i -> Unbundled' clk o)
-- ^ Synchronous sequential function with input and output matching that
-- of the moore machine
mooreB' clk ft fo iS i = unbundle (moore' clk ft fo iS (bundle i))
{-# INLINE medvedevB' #-}
-- | A version of 'medvedev'' that does automatic 'Bundle'ing
medvedevB' :: (Bundle i, Bundle s) => SClock clk -> (s -> i -> s) -> s -> Unbundled' clk i -> Unbundled' clk s
medvedevB' clk tr st = mooreB' clk tr id st