packages feed

clash-protocols-0.1: src/Protocols/Internal/TH.hs

{-# OPTIONS_HADDOCK hide #-}

module Protocols.Internal.TH where

import Clash.Prelude qualified as C
import Control.Monad (zipWithM)
import Control.Monad.Extra (concatMapM)
import Data.Proxy
import GHC.TypeNats
import Language.Haskell.TH
import Protocols.Experimental.Hedgehog.Types
import Protocols.Experimental.Simulate.Types
import Protocols.Internal.Types
import Protocols.Plugin

{- | Template haskell function to generate IdleCircuit instances for the tuples
n through m inclusive. To see a 2-tuple version of the pattern we generate,
see @Protocols.IdleCircuit@.
-}
idleCircuitTupleInstances :: Int -> Int -> DecsQ
idleCircuitTupleInstances n m = concatMapM idleCircuitTupleInstance [n .. m]

{- | Template Haskell function to generate an IdleCircuit instance for an
n-tuple.
-}
idleCircuitTupleInstance :: Int -> DecsQ
idleCircuitTupleInstance n =
  [d|
    instance ($instCtx) => IdleCircuit $instTy where
      idleFwd _ = $fwdExpr
      idleBwd _ = $bwdExpr
    |]
 where
  circTys = map (\i -> varT $ mkName $ "c" <> show i) [1 .. n]
  instCtx = foldl appT (tupleT n) $ map (\ty -> [t|IdleCircuit $ty|]) circTys
  instTy = foldl appT (tupleT n) circTys
  fwdExpr = tupE $ map mkFwdExpr circTys
  mkFwdExpr ty = [e|idleFwd $ Proxy @($ty)|]
  bwdExpr = tupE $ map mkBwdExpr circTys
  mkBwdExpr ty = [e|idleBwd $ Proxy @($ty)|]

simulateTupleInstances :: Int -> Int -> DecsQ
simulateTupleInstances n m = concatMapM simulateTupleInstance [n .. m]

simulateTupleInstance :: Int -> DecsQ
simulateTupleInstance n =
  [d|
    instance ($instCtx) => Simulate $instTy where
      type SimulateFwdType $instTy = $fwdType
      type SimulateBwdType $instTy = $bwdType
      type SimulateChannels $instTy = $channelSum

      simToSigFwd _ $(tildeP fwdPat0) = $(tupE $ zipWith (\ty expr -> [e|simToSigFwd (Proxy @($ty)) $expr|]) circTys fwdExpr)
      simToSigBwd _ $(tildeP bwdPat0) = $(tupE $ zipWith (\ty expr -> [e|simToSigBwd (Proxy @($ty)) $expr|]) circTys bwdExpr)
      sigToSimFwd _ $(tildeP fwdPat0) = $(tupE $ zipWith (\ty expr -> [e|sigToSimFwd (Proxy @($ty)) $expr|]) circTys fwdExpr)
      sigToSimBwd _ $(tildeP bwdPat0) = $(tupE $ zipWith (\ty expr -> [e|sigToSimBwd (Proxy @($ty)) $expr|]) circTys bwdExpr)

      stallC $(varP $ mkName "conf") $(varP $ mkName "rem0") = $stallCExpr
    |]
 where
  -- Generate the types for the instance
  circTys = map (\i -> varT $ mkName $ "c" <> show i) [1 .. n]
  instTy = foldl appT (tupleT n) circTys
  instCtx = foldl appT (tupleT n) $ map (\ty -> [t|Simulate $ty|]) circTys
  fwdType = foldl appT (tupleT n) $ map (\ty -> [t|SimulateFwdType $ty|]) circTys
  bwdType = foldl appT (tupleT n) $ map (\ty -> [t|SimulateBwdType $ty|]) circTys
  channelSum = foldl1 (\a b -> [t|$a + $b|]) $ map (\ty -> [t|SimulateChannels $ty|]) circTys

  -- Relevant expressions and patterns
  fwdPat0 = tupP $ map (\i -> varP $ mkName $ "fwd" <> show i) [1 .. n]
  bwdPat0 = tupP $ map (\i -> varP $ mkName $ "bwd" <> show i) [1 .. n]
  fwdExpr = map (\i -> varE $ mkName $ "fwd" <> show i) [1 .. n]
  bwdExpr = map (\i -> varE $ mkName $ "bwd" <> show i) [1 .. n]
  fwdExpr1 = map (\i -> varE $ mkName $ "fwdStalled" <> show i) [1 .. n]
  bwdExpr1 = map (\i -> varE $ mkName $ "bwdStalled" <> show i) [1 .. n]

  -- stallC Declaration: Split off the stall vectors from the large input vector
  mkStallVec i ty =
    [d|
      $[p|
         ( $(varP (mkName $ "stalls" <> show i))
           , $(varP (mkName $ if i == n then "_" else "rem" <> show i))
           )
         |] =
          C.splitAtI @(SimulateChannels $ty)
            $(varE $ mkName $ "rem" <> show (i - 1))
      |]

  -- stallC Declaration: Generate stalling circuits
  mkStallCircuit i ty =
    [d|
      $[p|Circuit $(varP $ mkName $ "stalled" <> show i)|] =
        stallC @($ty) conf $(varE $ mkName $ "stalls" <> show i)
      |]

  -- Generate the stallC expression
  stallCExpr = do
    stallVecs <-
      concat <$> zipWithM mkStallVec [1 .. n] circTys
    stallCircuits <-
      concat <$> zipWithM mkStallCircuit [1 .. n] circTys
    LetE (stallVecs <> stallCircuits)
      <$> [e|Circuit $ \(~($fwdPat0, $bwdPat0)) -> $circuitResExpr|]

  circuitResExpr = do
    stallCResultDecs <- concatMapM mkStallCResultDec [1 .. n]
    LetE stallCResultDecs <$> [e|($(tupE fwdExpr1), $(tupE bwdExpr1))|]

  mkStallCResultDec i =
    [d|
      $[p|
         ( $(varP $ mkName $ "fwdStalled" <> show i)
           , $(varP $ mkName $ "bwdStalled" <> show i)
           )
         |] =
          $(varE $ mkName $ "stalled" <> show i)
            ( $(varE $ mkName $ "fwd" <> show i)
            , $(varE $ mkName $ "bwd" <> show i)
            )
      |]

drivableTupleInstances :: Int -> Int -> DecsQ
drivableTupleInstances n m = concatMapM drivableTupleInstance [n .. m]

drivableTupleInstance :: Int -> DecsQ
drivableTupleInstance n =
  [d|
    instance ($instCtx) => Drivable $instTy where
      type
        ExpectType $instTy =
          $(foldl appT (tupleT n) $ map (\ty -> [t|ExpectType $ty|]) circTys)
      toSimulateType Proxy $(tupP circPats) = $toSimulateExpr

      fromSimulateType Proxy $(tupP circPats) = $fromSimulateExpr

      driveC $(varP $ mkName "conf") $(tupP fwdPats) = $(letE driveCDecs driveCExpr)
      sampleC conf (Circuit f) =
        let
          $(varP $ mkName "bools") = replicate (resetCycles conf) False <> repeat True
          $(tupP fwdPats) = snd $ f ((), $(tupE $ map mkSampleCExpr circTys))
         in
          $( tupE $
               zipWith (\ty fwd -> [|sampleC @($ty) conf (Circuit $ const ((), $fwd))|]) circTys fwdExprs
           )
    |]
 where
  circStrings = map (\i -> "c" <> show i) [1 .. n]
  circTys = map (varT . mkName) circStrings
  circPats = map (varP . mkName) circStrings
  circExprs = map (varE . mkName) circStrings
  instCtx = foldl appT (tupleT n) $ map (\ty -> [t|Drivable $ty|]) circTys
  instTy = foldl appT (tupleT n) circTys
  fwdPats = map (varP . mkName . ("fwd" <>)) circStrings
  fwdExprs = map (varE . mkName . ("fwd" <>)) circStrings
  bwdExprs = map (varE . mkName . ("bwd" <>)) circStrings
  bwdPats = map (varP . mkName . ("bwd" <>)) circStrings

  mkSampleCExpr ty = [e|boolsToBwd (Proxy @($ty)) bools|]
  driveCDecs =
    pure $
      valD
        (tupP $ map (\p -> [p|(Circuit $p)|]) circPats)
        (normalB $ tupE $ zipWith (\ty fwd -> [e|driveC @($ty) conf $fwd|]) circTys fwdExprs)
        []

  driveCExpr =
    [e|
      Circuit $ \(_, $(tildeP $ tupP bwdPats)) -> ((), $(tupE $ zipWith mkDriveCExpr circExprs bwdExprs))
      |]
  mkDriveCExpr c bwd = [e|snd ($c ((), $bwd))|]
  toSimulateExpr = tupE $ zipWith (\ty c -> [|toSimulateType (Proxy @($ty)) $c|]) circTys circExprs
  fromSimulateExpr = tupE $ zipWith (\ty c -> [|fromSimulateType (Proxy @($ty)) $c|]) circTys circExprs

backPressureTupleInstances :: Int -> Int -> DecsQ
backPressureTupleInstances n m = concatMapM backPressureTupleInstance [n .. m]

backPressureTupleInstance :: Int -> DecsQ
backPressureTupleInstance n =
  [d|
    instance ($instCtx) => Backpressure $instTy where
      boolsToBwd _ bs = $(tupE $ map (\ty -> [e|boolsToBwd (Proxy @($ty)) bs|]) circTys)
    |]
 where
  circTys = map (\i -> varT $ mkName $ "c" <> show i) [1 .. n]
  instCtx = foldl appT (tupleT n) $ map (\ty -> [t|Backpressure $ty|]) circTys
  instTy = foldl appT (tupleT n) circTys

testTupleInstances :: Int -> Int -> DecsQ
testTupleInstances n m = concatMapM testTupleInstance [n .. m]

testTupleInstance :: Int -> DecsQ
testTupleInstance n =
  [d|
    instance ($instCtx) => Test $instTy where
      expectN Proxy $(varP $ mkName "opts") $(tupP sampledPats) = $(doE stmts)
    |]
 where
  circStrings = map (\i -> "c" <> show i) [1 .. n]
  circTys = map (varT . mkName) circStrings
  instCtx = foldl appT (tupleT n) $ map (\ty -> [t|Test $ty|]) circTys
  instTy = foldl appT (tupleT n) circTys

  sampledPats = map (varP . mkName . ("sampled" <>)) circStrings
  sampledExprs = map (varE . mkName . ("sampled" <>)) circStrings
  trimmedPats = map (varP . mkName . ("trimmed" <>)) circStrings
  trimmedExprs = map (varE . mkName . ("trimmed" <>)) circStrings

  mkTrimStmt trim ty sam = bindS trim [e|expectN (Proxy @($ty)) opts $sam|]
  expectResult = noBindS [e|pure $(tupE trimmedExprs)|]
  stmts = zipWith3 mkTrimStmt trimmedPats circTys sampledExprs <> [expectResult]