packages feed

clash-shockwaves-1.0.1: src/Clash/Shockwaves/Internal/Trace/CRE.hs

{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE OverloadedStrings #-}

{- |
Copyright  :  (C) 2025-2026, QBayLogic B.V.
License    :  BSD2 (see the file LICENSE)
Maintainer :  QBayLogic B.V. <devops@qbaylogic.com>

Some functions for creating signals for clocks and reset and enable signals.
-}
module Clash.Shockwaves.Internal.Trace.CRE where

import Clash.Explicit.Prelude (noReset, register)
import Clash.Prelude hiding (register, traceSignal)
import qualified Data.List as L
import Data.Tuple.Extra (uncurry3)
import Data.Typeable

import Clash.Shockwaves.LUT
import Clash.Shockwaves.Trace
import Clash.Shockwaves.Waveform hiding (tConst)

{- | A type for displaying clock cycles.
The styles can be configured through style variables @clk_pre@, @clk_a@ and @clk_b@.

__NB__: This is not a traditional clock wave! The clock signal alternates
/every cycle/, rather than going high and low /within/ a cycle.
-}
data ClockWave
  = ClockWave Bool
  | ClockInit
  deriving (Generic, Typeable, BitPack, NFDataX)

{- | A type for displaying a reset signal.
The styles can be configured through style variables @reset_off@ and @reset_on@.
-}
newtype ResetWave (dom :: Domain) = ResetWave Bool
  deriving (Generic, Typeable, BitPack, NFDataX)

{- | A type for displaying an enable signal.
The styles can be configured through style variables @enable_off@ and @enable_on@.
-}
newtype EnableWave = EnableWave Bool
  deriving (Generic, Typeable, BitPack, NFDataX)

{- | A type for displaying clock, reset and enable signals.
See t'ClockWave', t'ResetWave' and t'EnableWave'.
It contains these signals as subsignals. The toplevel signal displays the clock
during normal operation, reset when it is active, and enable when it is low.
The combined style of both being active can be configured through the style
vairable @reset_on_enable_off@.
-}
data CREWave dom = CREWave {clock :: ClockWave, reset :: ResetWave dom, enable :: EnableWave}
  deriving (Generic, Typeable, BitPack, NFDataX)
  deriving (Waveform) via (WaveformForLUT (CREWave dom))

vConst :: Render -> Translator
vConst r = Translator 0 $ TConst $ tConst r
tConst :: Render -> Translation
tConst r = Translation r []

-- the render values used
clkI :: Render
clkI = Just ("NOT RUNNING", WSVar "clk_pre" "#888", 11)
clkA :: Render
clkA = Just ("", WSVar "clk_a" "#fff", 11)
clkB :: Render
clkB = Just ("", WSVar "clk_b" "#83b", 11)

rstOff :: Render
rstOff = Just ("DEASSERTED", WSVar "reset_off" WSDefault, 11)
rstOn :: Render
rstOn = Just ("ASSERTED", WSVar "reset_on" WSWarn, 11)
rstOn' :: Render
rstOn' = Just ("RESET", WSVar "reset_on" WSWarn, 11)

enOn :: Render
enOn = Just ("ENABLED", WSVar "enable_on" WSDefault, 11)
enOff :: Render
enOff = Just ("DISABLED", WSVar "enable_off" WSWarn, 11)

rstAndDis :: Render
rstAndDis = Just ("DISABLED|RESET", WSVar "reset_on_enable_off" WSWarn, 11)

{- | Control the styles of the clock wave through style variables
@clk_pre@, @clk_a@ and @clk_b@.
-}
instance Waveform ClockWave where
  translator =
    Translator 2
      $ TSum
        [ Translator 1
            $ TSum
              [ vConst clkA
              , vConst clkB
              ]
        , vConst clkI
        ]

{- | Control the styles of the reset wave through style variables
@reset_on@ and @reset_off@.
-}
instance (KnownDomain dom) => Waveform (ResetWave dom) where
  translator =
    Translator 1
      $ TSum
      $ L.map
        vConst
        ( case resetPolarity @dom of
            SActiveHigh -> [rstOff, rstOn]
            SActiveLow -> [rstOn, rstOff]
        )

{- | Control the styles of the enable wave through style variables
@enable_on@ and @enable_off@.
-}
instance Waveform EnableWave where
  translator =
    Translator 1
      $ TSum
        [ vConst enOff
        , vConst enOn
        ]

{- FOURMOLU_DISABLE -}
-- | Control the style of a combined disable and reset through style variable
-- @reset_on_enable_off@.
instance KnownDomain dom => WaveformLUT (CREWave dom) where
  translateL = translateWith displayL splitL
    where 
      displayL (CREWave c r (EnableWave e)) = case (c,isRst r,e) of
        (_              ,True,False) -> rstAndDis
        (_              ,True,_    ) -> rstOn'
        (_              ,_   ,False) -> enOff
        (ClockInit      ,_   ,_    ) -> clkI
        (ClockWave False,_   ,_    ) -> clkA
        (ClockWave True ,_   ,_    ) -> clkB
        where
          isRst (ResetWave r') = case resetPolarity @dom of
            SActiveHigh -> r'
            SActiveLow  -> not r'
{- FOURMOLU_ENABLE -}

-- | Produce an alternating signal for a clock.
clkSignal :: (KnownDomain dom) => Clock dom -> Signal dom ClockWave
clkSignal clk = s
 where
  s = register clk noReset enableGen ClockInit s'
  s' = next <$> s
  next val = case val of
    ClockInit -> ClockWave False
    ClockWave b -> ClockWave (not b)

{- | Trace a clock signal. Keep in mind that the clock has to be evaluated in order for
the signal to show up. Alternatively, use 'seq' to force evaluation.

The styles can be configured through style variables @clk_pre@, @clk_a@ and @clk_b@.

__NB__: This is not a traditional clock wave! The clock signal alternates
/every cycle/, rather than going high and low /within/ a cycle.
-}
traceClock :: (KnownDomain dom) => String -> Clock dom -> Clock dom
traceClock lbl clk =
  traceSignal lbl (clkSignal clk)
    `seq` clk

{- | Trace a reset signal. Keep in mind that the reset has to be evaluated in order for
the signal to show up. Alternatively, use 'seq' to force evaluation.

The styles can be configured through style variables @reset_off@ and @reset_on@.
-}
traceReset :: forall dom. (KnownDomain dom) => String -> Reset dom -> Reset dom
traceReset lbl rst =
  traceSignal lbl (ResetWave @dom <$> unsafeFromReset rst)
    `seq` rst

{- | Trace an enable signal. Keep in mind that the enable has to be evaluated in order for
the signal to show up. Alternatively, use 'seq' to force evaluation.

The styles can be configured through style variables @enable_off@ and @enable_on@.
-}
traceEnable :: (KnownDomain dom) => String -> Enable dom -> Enable dom
traceEnable lbl en =
  traceSignal lbl (EnableWave <$> fromEnable en)
    `seq` en

{- | Create a signal displaying the clock, reset and enable signals.

Example:

> traceClockResetEnable "cre" myDesign clockGen resetGen enableGen

The style of a combined disable and reset can be configured through style variable
@reset_on_enable_off@. For other options, see 'traceClock', 'traceReset' and
'traceEnable'.

__NB__: This does not contain a traditional clock wave! The clock signal alternates
/every cycle/, rather than going high and low /within/ a cycle.
-}
traceClockResetEnable ::
  forall dom a.
  (KnownDomain dom) =>
  String ->
  (Clock dom -> Reset dom -> Enable dom -> a) ->
  (Clock dom -> Reset dom -> Enable dom -> a)
traceClockResetEnable lbl f c r e =
  traceSignal
    lbl
    ( uncurry3 CREWave
        <$> bundle
          ( clkSignal c
          , ResetWave <$> unsafeFromReset r
          , EnableWave <$> fromEnable e
          ) ::
        Signal dom (CREWave dom)
    )
    `seq` f c r e

-- | Trace a hidden clock signal. See 'traceClock'.
traceHiddenClock :: (KnownDomain dom, HiddenClock dom) => String -> r -> r
traceHiddenClock lbl x = traceClock lbl hasClock `seq` x

-- | Trace a hidden reset signal. See 'traceReset'.
traceHiddenReset :: (KnownDomain dom, HiddenReset dom) => String -> r -> r
traceHiddenReset lbl x = traceReset lbl hasReset `seq` x

-- | Trace a hidden enable signal. See 'traceEnable'.
traceHiddenEnable :: (KnownDomain dom, HiddenEnable dom) => String -> r -> r
traceHiddenEnable lbl x = traceEnable lbl hasEnable `seq` x

-- | Trace hidden clock, reset and enable signals. See 'traceClockResetEnable'.
traceHiddenClockResetEnable ::
  (KnownDomain dom, HiddenClockResetEnable dom) => String -> r -> r
traceHiddenClockResetEnable lbl = hideClockResetEnable . traceClockResetEnable lbl . exposeClockResetEnable