{-# LANGUAGE ScopedTypeVariables, FlexibleContexts, TypeFamilies, TypeSynonymInstances, FlexibleInstances, GADTs, RankNTypes, UndecidableInstances #-} -- | This module implements an Ready protocol. In this producer/consumer model, -- the consumer issues a Ready signal to the producer, at which time the -- producer can drive the data input to the consumer, signaling data valid with -- an Enable. The producer will hold the consumer data input steady until it -- receives another Ready. module Language.KansasLava.Protocols.ReadyBox where import Language.KansasLava.Rep import Language.KansasLava.Signal import Language.KansasLava.Types import Language.KansasLava.Protocols.Enabled import Language.KansasLava.Protocols.Types import Language.KansasLava.Protocols.Patch import Language.KansasLava.Probes import Language.KansasLava.Utils ------------------------------------------------------------------------------------ {- The convention with ReadyBoxn signals is ... -> (lhs_inp, rhs_inp) -> (lhs_out, rhs_out) OR -> (lhs_inp, control_in, rhs_inp) -> (lhs_out, control_out, rhs_out) -} -- | Take a list of shallow values and create a stream which can be sent into -- a FIFO, respecting the write-ready flag that comes out of the FIFO. toReadyBox :: (Rep a, Clock c, sig ~ Signal c) => Patch [Maybe a] (sig (Enabled a)) () (sig Ready) toReadyBox = toReadyBox' [] -- | A readybox that goes through a sequence of intermediate states after -- issuing each enable, and before it looks for the next Ready. toReadyBox' :: (Rep a, Clock c, sig ~ Signal c) => [Int] -- ^ list wait states after every succesful post -> Patch [Maybe a] (sig (Enabled a)) () (sig Ready) toReadyBox' pauses ~(ys,full) = ((),toS (fn ys (fromS full) pauses)) where -- fn xs cs ps | trace (show ("fn",take 5 ps)) False = undefined -- send the value *before* checking the Ready fn xs fs ps = case fs of (Nothing:_) -> error "toReadyBox: bad protocol state (1)" (Just (Ready True) : fs') -> case (xs,ps) of (x:xs',0:ps') -> x : fn xs' fs' ps' -- write it (it may be Nothing) (Nothing:xs',p:ps') -> Nothing : fn xs' fs' (pred p : ps') (_:_,p:ps') -> Nothing : fn xs fs' (pred p : ps') (_:_,[]) -> fn xs fs (repeat 0) (_,_) -> Nothing : fn xs fs' ps -- nothing to write (Just (Ready False) : fs') -> Nothing : fn xs fs' ps -- not ready yet [] -> error "toReadyBox: Ready seq should never end" -- | Take stream from a FIFO and return an asynchronous read-ready flag, which -- is given back to the FIFO, and a shallow list of values. -- I'm sure this space-leaks. fromReadyBox :: forall a c sig . (Rep a, Clock c, sig ~ Signal c) => Patch (sig (Enabled a)) [Maybe a] (sig Ready) () fromReadyBox = fromReadyBox' (repeat 0) -- | Like fromReadyBox, but which goes through a series of intermediate states -- after receiving an enable before issuing another Ready. fromReadyBox' :: forall a c sig . (Rep a, Clock c, sig ~ Signal c) => [Int] -> Patch (sig (Enabled a)) [Maybe a] (sig Ready) () fromReadyBox' ps ~(inp,_) = (toS (map fst internal), map snd internal) where internal = fn (fromS inp) ps -- pretty simple API fn :: [Maybe (Enabled a)] -> [Int] -> [(Ready,Maybe a)] fn xs (0:ps') = (Ready True,v) : rest where (v,rest) = case xs of (Nothing:_) -> error "found an unknown value in ReadyBox input" (Just Nothing:xs') -> (Nothing,fn xs' (0:ps')) -- nothing read yet (Just v':xs') -> (v',fn xs' ps') [] -> error "fromReadyBox: Ready sequences should never end" fn xs (p:ps') = (Ready False,Nothing) : fn (Prelude.tail xs) (pred p:ps') fn xs [] = fn xs (repeat 0) -- | Introduces protocol-compliant delays (in the shallow embedding) shallowReadyBoxBridge :: forall sig c a . (Rep a, Clock c, sig ~ Signal c, Show a) => ([Int],[Int]) -> Patch (sig (Enabled a)) (sig (Enabled a)) (sig Ready) (sig Ready) shallowReadyBoxBridge (lhsF,rhsF) = patch where patch = fromReadyBox' lhsF $$ toReadyBox' rhsF -- | 'probeReadyBoxPatch' creates a patch with a named probe, probing the data and ready -- signals in a Ready interface. probeReadyBoxP :: forall sig a c . ( Rep a, Clock c, sig ~ Signal c) => String -> Patch (sig (Enabled a)) (sig (Enabled a)) (sig Ready) (sig Ready) probeReadyBoxP probeName ~(inp, ready_in) = (ready_out, out) where (out, _) = unpack probed ready_out = ready_in probed :: sig (Enabled a, Ready) probed = probeS probeName $ pack (inp, ready_in) -- A simple way of running a patch runReadyBoxP :: forall sig c a b . (c ~ CLK, sig ~ Signal c, Rep a, Rep b) => Patch (sig (Enabled a)) (sig (Enabled b)) (sig Ready) (sig Ready) -> [a] -> [b] runReadyBoxP p as = [ b | Just b <- bs' ] where as' = map Just as bs' = runP (outputP as' $$ toReadyBox $$ globalClockP $$ p $$ fromReadyBox) -- | A sink patch throws away its data input (generating a () data -- output). 'sinkReadyP' uses an enabled/ready protocol. sinkReadyP :: forall a c sig . (Rep a, Clock c, sig ~ Signal c) => Patch (sig (Enabled a)) () (sig Ready) () sinkReadyP ~(_, ()) = (toReady ready, ()) where ready = high -- | A source patch takes no input and generates a stream of values. It -- corresponds to a top-level input port. 'alwaysReadyP' uses the -- ready/enabled protocol. alwaysReadyP :: forall a c sig . ( Rep a, Clock c, sig ~ Signal c) => a -> Patch () (sig (Enabled a)) () (sig Ready) alwaysReadyP baseVal ~((), ready_in) = ((), out) where out = packEnabled (fromReady ready_in) (pureS baseVal) -- | stub, no data ever sent. neverReadyP :: forall a c sig . (Rep a, Clock c, sig ~ Signal c) => Patch () (sig (Enabled a)) () (sig Ready) neverReadyP (_,_) = ((),disabledS)