{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}

{- |
Module      : NITTA.Model.ProcessorUnits.IO.SPI
Description :
Copyright   : (c) Aleksandr Penskoi, 2019
License     : BSD3
Maintainer  : aleksandr.penskoi@gmail.com
Stability   : experimental
-}
module NITTA.Model.ProcessorUnits.IO.SPI (
    SPI,
    anySPI,
    Ports (..),
    IOPorts (..),
    spiMasterPorts,
    spiSlavePorts,
) where

import Data.Aeson
import Data.Default
import Data.HashMap.Strict qualified as HM
import Data.Map.Strict qualified as M
import Data.Maybe (fromMaybe, mapMaybe)
import Data.Set qualified as S
import Data.String.Interpolate
import NITTA.Intermediate.Functions
import NITTA.Intermediate.Types
import NITTA.Model.Problems
import NITTA.Model.ProcessorUnits.IO.SimpleIO
import NITTA.Model.ProcessorUnits.Types
import NITTA.Model.Time
import NITTA.Project
import NITTA.Utils
import Prettyprinter

data SPIinterface

instance SimpleIOInterface SPIinterface

type SPI v x t = SimpleIO SPIinterface v x t

anySPI :: Time t => Int -> Maybe Int -> SPI v x t
anySPI :: forall t v x. Time t => Int -> Maybe Int -> SPI v x t
anySPI Int
bounceFilter Maybe Int
bufferSize =
    SimpleIO
        { Int
$sel:bounceFilter:SimpleIO :: Int
bounceFilter :: Int
bounceFilter
        , Maybe Int
$sel:bufferSize:SimpleIO :: Maybe Int
bufferSize :: Maybe Int
bufferSize
        , $sel:receiveQueue:SimpleIO :: [Q v x]
receiveQueue = []
        , $sel:receiveN:SimpleIO :: Int
receiveN = Int
0
        , $sel:isReceiveOver:SimpleIO :: Bool
isReceiveOver = Bool
False
        , $sel:sendQueue:SimpleIO :: [Q v x]
sendQueue = []
        , $sel:sendN:SimpleIO :: Int
sendN = Int
0
        , $sel:process_:SimpleIO :: Process t (StepInfo v x t)
process_ = forall a. Default a => a
def
        }

instance IOConnected (SPI v x t) where
    data IOPorts (SPI v x t)
        = SPIMaster
            { forall v x t. IOPorts (SPI v x t) -> OutputPortTag
master_mosi :: OutputPortTag
            , forall v x t. IOPorts (SPI v x t) -> InputPortTag
master_miso :: InputPortTag
            , forall v x t. IOPorts (SPI v x t) -> OutputPortTag
master_sclk :: OutputPortTag
            , forall v x t. IOPorts (SPI v x t) -> OutputPortTag
master_cs :: OutputPortTag
            }
        | SPISlave
            { forall v x t. IOPorts (SPI v x t) -> InputPortTag
slave_mosi :: InputPortTag
            , forall v x t. IOPorts (SPI v x t) -> OutputPortTag
slave_miso :: OutputPortTag
            , forall v x t. IOPorts (SPI v x t) -> InputPortTag
slave_sclk :: InputPortTag
            , forall v x t. IOPorts (SPI v x t) -> InputPortTag
slave_cs :: InputPortTag
            }
        deriving (Int -> IOPorts (SPI v x t) -> ShowS
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall v x t. Int -> IOPorts (SPI v x t) -> ShowS
forall v x t. [IOPorts (SPI v x t)] -> ShowS
forall v x t. IOPorts (SPI v x t) -> String
showList :: [IOPorts (SPI v x t)] -> ShowS
$cshowList :: forall v x t. [IOPorts (SPI v x t)] -> ShowS
show :: IOPorts (SPI v x t) -> String
$cshow :: forall v x t. IOPorts (SPI v x t) -> String
showsPrec :: Int -> IOPorts (SPI v x t) -> ShowS
$cshowsPrec :: forall v x t. Int -> IOPorts (SPI v x t) -> ShowS
Show)

    inputPorts :: IOPorts (SPI v x t) -> Set InputPortTag
inputPorts SPISlave{OutputPortTag
InputPortTag
slave_cs :: InputPortTag
slave_sclk :: InputPortTag
slave_miso :: OutputPortTag
slave_mosi :: InputPortTag
$sel:slave_cs:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:slave_sclk:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:slave_miso:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:slave_mosi:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
..} = forall a. Ord a => [a] -> Set a
S.fromList [InputPortTag
slave_mosi, InputPortTag
slave_sclk, InputPortTag
slave_cs]
    inputPorts SPIMaster{OutputPortTag
InputPortTag
master_cs :: OutputPortTag
master_sclk :: OutputPortTag
master_miso :: InputPortTag
master_mosi :: OutputPortTag
$sel:master_cs:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:master_sclk:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:master_miso:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:master_mosi:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
..} = forall a. Ord a => [a] -> Set a
S.fromList [InputPortTag
master_miso]

    outputPorts :: IOPorts (SPI v x t) -> Set OutputPortTag
outputPorts SPISlave{OutputPortTag
InputPortTag
slave_cs :: InputPortTag
slave_sclk :: InputPortTag
slave_miso :: OutputPortTag
slave_mosi :: InputPortTag
$sel:slave_cs:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:slave_sclk:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:slave_miso:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:slave_mosi:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
..} = forall a. Ord a => [a] -> Set a
S.fromList [OutputPortTag
slave_miso]
    outputPorts SPIMaster{OutputPortTag
InputPortTag
master_cs :: OutputPortTag
master_sclk :: OutputPortTag
master_miso :: InputPortTag
master_mosi :: OutputPortTag
$sel:master_cs:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:master_sclk:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:master_miso:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:master_mosi:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
..} = forall a. Ord a => [a] -> Set a
S.fromList [OutputPortTag
master_mosi, OutputPortTag
master_sclk, OutputPortTag
master_cs]

spiMasterPorts :: Text -> IOPorts (SPI v x t)
spiMasterPorts Text
tag =
    SPIMaster
        { $sel:master_mosi:SPIMaster :: OutputPortTag
master_mosi = Text -> OutputPortTag
OutputPortTag forall a b. (a -> b) -> a -> b
$ Text
tag forall a. Semigroup a => a -> a -> a
<> Text
"_mosi"
        , $sel:master_miso:SPIMaster :: InputPortTag
master_miso = Text -> InputPortTag
InputPortTag forall a b. (a -> b) -> a -> b
$ Text
tag forall a. Semigroup a => a -> a -> a
<> Text
"_miso"
        , $sel:master_sclk:SPIMaster :: OutputPortTag
master_sclk = Text -> OutputPortTag
OutputPortTag forall a b. (a -> b) -> a -> b
$ Text
tag forall a. Semigroup a => a -> a -> a
<> Text
"_sclk"
        , $sel:master_cs:SPIMaster :: OutputPortTag
master_cs = Text -> OutputPortTag
OutputPortTag forall a b. (a -> b) -> a -> b
$ Text
tag forall a. Semigroup a => a -> a -> a
<> Text
"_cs"
        }

spiSlavePorts :: Text -> IOPorts (SPI v x t)
spiSlavePorts Text
tag =
    SPISlave
        { $sel:slave_mosi:SPIMaster :: InputPortTag
slave_mosi = Text -> InputPortTag
InputPortTag forall a b. (a -> b) -> a -> b
$ Text
tag forall a. Semigroup a => a -> a -> a
<> Text
"_mosi"
        , $sel:slave_miso:SPIMaster :: OutputPortTag
slave_miso = Text -> OutputPortTag
OutputPortTag forall a b. (a -> b) -> a -> b
$ Text
tag forall a. Semigroup a => a -> a -> a
<> Text
"_miso"
        , $sel:slave_sclk:SPIMaster :: InputPortTag
slave_sclk = Text -> InputPortTag
InputPortTag forall a b. (a -> b) -> a -> b
$ Text
tag forall a. Semigroup a => a -> a -> a
<> Text
"_sclk"
        , $sel:slave_cs:SPIMaster :: InputPortTag
slave_cs = Text -> InputPortTag
InputPortTag forall a b. (a -> b) -> a -> b
$ Text
tag forall a. Semigroup a => a -> a -> a
<> Text
"_cs"
        }

instance Time t => Default (SPI v x t) where
    def :: SPI v x t
def = forall t v x. Time t => Int -> Maybe Int -> SPI v x t
anySPI Int
0 forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a
Just Int
6

instance (ToJSON v, VarValTime v x t) => TargetSystemComponent (SPI v x t) where
    moduleName :: Text -> SPI v x t -> Text
moduleName Text
_ SPI v x t
_ = Text
"pu_spi"
    hardware :: Text -> SPI v x t -> Implementation
hardware Text
_tag SPI v x t
_pu =
        Maybe String -> [Implementation] -> Implementation
Aggregate
            forall a. Maybe a
Nothing
            [ String -> Implementation
FromLibrary String
"spi/pu_slave_spi_driver.v"
            , String -> Implementation
FromLibrary String
"spi/spi_slave_driver.v"
            , String -> Implementation
FromLibrary String
"spi/i2n_splitter.v"
            , String -> Implementation
FromLibrary String
"spi/buffer.v"
            , String -> Implementation
FromLibrary String
"spi/bounce_filter.v"
            , String -> Implementation
FromLibrary String
"spi/spi_master_driver.v"
            , String -> Implementation
FromLibrary String
"spi/n2i_splitter.v"
            , String -> Implementation
FromLibrary String
"spi/pu_slave_spi.v"
            , String -> Implementation
FromLibrary String
"spi/pu_master_spi.v"
            ]

    software :: Text -> SPI v x t -> Implementation
software Text
tag SPI v x t
pu = forall i v x t.
(VarValTime v x t, SimpleIOInterface i, ToJSON v) =>
Text -> SimpleIO i v x t -> Text -> Implementation
protocolDescription Text
tag SPI v x t
pu Text
"SPI Processor Unit"

    hardwareInstance :: Text -> SPI v x t -> UnitEnv (SPI v x t) -> Verilog
hardwareInstance
        Text
tag
        SimpleIO{Int
bounceFilter :: Int
$sel:bounceFilter:SimpleIO :: forall i v x t. SimpleIO i v x t -> Int
bounceFilter, Int
sendN :: Int
$sel:sendN:SimpleIO :: forall i v x t. SimpleIO i v x t -> Int
sendN, Int
receiveN :: Int
$sel:receiveN:SimpleIO :: forall i v x t. SimpleIO i v x t -> Int
receiveN}
        UnitEnv
            { Text
sigClk :: forall m. UnitEnv m -> Text
sigClk :: Text
sigClk
            , Text
sigRst :: forall m. UnitEnv m -> Text
sigRst :: Text
sigRst
            , Text
sigCycleBegin :: forall m. UnitEnv m -> Text
sigCycleBegin :: Text
sigCycleBegin
            , Text
sigInCycle :: forall m. UnitEnv m -> Text
sigInCycle :: Text
sigInCycle
            , Text
sigCycleEnd :: forall m. UnitEnv m -> Text
sigCycleEnd :: Text
sigCycleEnd
            , valueIn :: forall m. UnitEnv m -> Maybe (Text, Text)
valueIn = Just (Text
dataIn, Text
attrIn)
            , valueOut :: forall m. UnitEnv m -> Maybe (Text, Text)
valueOut = Just (Text
dataOut, Text
attrOut)
            , ctrlPorts :: forall m. UnitEnv m -> Maybe (Ports m)
ctrlPorts = Just SimpleIOPorts{String
SignalTag
$sel:stop:SimpleIOPorts :: forall i v x t. Ports (SimpleIO i v x t) -> String
$sel:oe:SimpleIOPorts :: forall i v x t. Ports (SimpleIO i v x t) -> SignalTag
$sel:wr:SimpleIOPorts :: forall i v x t. Ports (SimpleIO i v x t) -> SignalTag
stop :: String
oe :: SignalTag
wr :: SignalTag
..}
            , ioPorts :: forall m. UnitEnv m -> Maybe (IOPorts m)
ioPorts = Just IOPorts (SPI v x t)
ioPorts
            } =
            [__i|
                #{ module_ ioPorts } \#
                        ( .DATA_WIDTH( #{ dataWidth (def :: x) } )
                        , .ATTR_WIDTH( #{ attrWidth (def :: x) } )
                        , .BOUNCE_FILTER( #{ show bounceFilter } )
                        , .DISABLED( #{ if sendN == 0 && receiveN == 0 then (1 :: Int) else 0 } )
                        ) #{ tag }
                    ( .clk( #{ sigClk } )
                    , .rst( #{ sigRst } )
                    , .flag_stop( #{ stop } )
                    , .signal_cycle_begin( #{ sigCycleBegin } )
                    , .signal_in_cycle( #{ sigInCycle  } )
                    , .signal_cycle_end( #{ sigCycleEnd } )
                    , .signal_oe( #{ oe } )
                    , .signal_wr( #{ wr } )
                    , .data_in( #{ dataIn } ), .attr_in( #{ attrIn } )
                    , .data_out( #{ dataOut } ), .attr_out( #{ attrOut } )
                    #{ nest 4 $ extIO ioPorts  }
                    );
            |]
            where
                module_ :: IOPorts (SPI v x t) -> Verilog
module_ SPISlave{} = Verilog
"pu_slave_spi" :: Verilog
                module_ SPIMaster{} = Verilog
"pu_master_spi"
                extIO :: IOPorts (SPI v x t) -> Verilog
extIO SPISlave{OutputPortTag
InputPortTag
slave_cs :: InputPortTag
slave_sclk :: InputPortTag
slave_miso :: OutputPortTag
slave_mosi :: InputPortTag
$sel:slave_cs:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:slave_sclk:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:slave_miso:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:slave_mosi:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
..} =
                    [__i|
                        , .mosi( #{ slave_mosi } )
                        , .miso( #{ slave_miso } )
                        , .sclk( #{ slave_sclk } )
                        , .cs( #{ slave_cs } )
                    |] ::
                        Verilog
                extIO SPIMaster{OutputPortTag
InputPortTag
master_cs :: OutputPortTag
master_sclk :: OutputPortTag
master_miso :: InputPortTag
master_mosi :: OutputPortTag
$sel:master_cs:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:master_sclk:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:master_miso:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:master_mosi:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
..} =
                    [__i|
                        , .mosi( #{ master_mosi } )
                        , .miso( #{ master_miso } )
                        , .sclk( #{ master_sclk } )
                        , .cs( #{ master_cs } )
                    |]
    hardwareInstance Text
_title SPI v x t
_pu UnitEnv (SPI v x t)
_env = forall a. HasCallStack => String -> a
error String
"internal error"

instance VarValTime v x t => IOTestBench (SPI v x t) v x where
    testEnvironmentInitFlag :: Text -> SPI v x t -> Maybe Text
testEnvironmentInitFlag Text
tag SPI v x t
_pu = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Text
tag forall a. Semigroup a => a -> a -> a
<> Text
"_env_init_flag"

    testEnvironment :: Text
-> SPI v x t
-> UnitEnv (SPI v x t)
-> TestEnvironment v x
-> Maybe Verilog
testEnvironment
        Text
tag
        sio :: SPI v x t
sio@SimpleIO{Process t (StepInfo v x t)
process_ :: Process t (StepInfo v x t)
$sel:process_:SimpleIO :: forall i v x t. SimpleIO i v x t -> Process t (StepInfo v x t)
process_, Int
bounceFilter :: Int
$sel:bounceFilter:SimpleIO :: forall i v x t. SimpleIO i v x t -> Int
bounceFilter}
        UnitEnv
            { Text
sigClk :: Text
sigClk :: forall m. UnitEnv m -> Text
sigClk
            , Text
sigRst :: Text
sigRst :: forall m. UnitEnv m -> Text
sigRst
            , ctrlPorts :: forall m. UnitEnv m -> Maybe (Ports m)
ctrlPorts = Just SimpleIOPorts{}
            , ioPorts :: forall m. UnitEnv m -> Maybe (IOPorts m)
ioPorts = Just IOPorts (SPI v x t)
ioPorts
            }
        TestEnvironment{teCntx :: forall v x. TestEnvironment v x -> Cntx v x
teCntx = cntx :: Cntx v x
cntx@Cntx{Int
cntxCycleNumber :: forall v x. Cntx v x -> Int
cntxCycleNumber :: Int
cntxCycleNumber, [CycleCntx v x]
cntxProcess :: forall v x. Cntx v x -> [CycleCntx v x]
cntxProcess :: [CycleCntx v x]
cntxProcess}, Int
teComputationDuration :: forall v x. TestEnvironment v x -> Int
teComputationDuration :: Int
teComputationDuration} =
            let receivedVariablesSeq :: [v]
receivedVariablesSeq =
                    forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe
                        ( \F v x
f -> case forall (f :: * -> * -> *) v x.
(Typeable f, Typeable v, Typeable x) =>
F v x -> Maybe (f v x)
castF F v x
f of
                            Just Receive{} -> forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall {c}. Set c -> c
oneOf forall a b. (a -> b) -> a -> b
$ forall a v. Variables a v => a -> Set v
variables F v x
f
                            Maybe (Receive v x)
_ -> forall a. Maybe a
Nothing
                        )
                        forall a b. (a -> b) -> a -> b
$ forall a f. WithFunctions a f => a -> [f]
functions Process t (StepInfo v x t)
process_
                receivedVarsValues :: [Map v x]
receivedVarsValues = forall a. Int -> [a] -> [a]
take Int
cntxCycleNumber forall a b. (a -> b) -> a -> b
$ forall v x. Ord v => Cntx v x -> [Map v x]
cntxReceivedBySlice Cntx v x
cntx
                sendedVariableSeq :: [v]
sendedVariableSeq =
                    forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe
                        ( \case
                            (Target v
v) -> forall a. a -> Maybe a
Just v
v
                            EndpointRole v
_ -> forall a. Maybe a
Nothing
                        )
                        forall a b. (a -> b) -> a -> b
$ forall {b} {v} {x} {t}.
Ord b =>
Process b (StepInfo v x t) -> [EndpointRole v]
getEndpoints Process t (StepInfo v x t)
process_
                sendedVarsValues :: [HashMap v x]
sendedVarsValues = forall a. Int -> [a] -> [a]
take Int
cntxCycleNumber forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall v x. CycleCntx v x -> HashMap v x
cycleCntx [CycleCntx v x]
cntxProcess
                wordWidth :: Int
wordWidth = forall x. Val x => x -> Int
dataWidth (forall a. Default a => a
def :: x)
                frameWordCount :: Int
frameWordCount = forall a. Ord a => a -> a -> a
max (forall (t :: * -> *) a. Foldable t => t a -> Int
length [v]
receivedVariablesSeq) forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t a -> Int
length [v]
sendedVariableSeq
                frameWidth :: Int
frameWidth = Int
frameWordCount forall a. Num a => a -> a -> a
* Int
wordWidth
                timeLag :: Int
timeLag = Int
10 :: Int
                sendingDuration :: Int
sendingDuration =
                    forall a. Ord a => a -> a -> a
max
                        (Int
teComputationDuration forall a. Num a => a -> a -> a
+ Int
2)
                        (Int
frameWidth forall a. Num a => a -> a -> a
* Int
2 forall a. Num a => a -> a -> a
+ Int
bounceFilter forall a. Num a => a -> a -> a
+ Int
2)

                toVerilogLiteral :: [x] -> Doc Any
toVerilogLiteral [x]
xs =
                    let xs' :: [Doc Any]
xs' = forall a b. (a -> b) -> [a] -> [b]
map x -> Doc Any
toVerilogLiteral' [x]
xs
                        placeholder :: [Doc Any]
placeholder = forall a. Int -> a -> [a]
replicate (Int
frameWordCount forall a. Num a => a -> a -> a
- forall (t :: * -> *) a. Foldable t => t a -> Int
length [x]
xs) [i|#{ wordWidth }'d00|]
                     in forall ann. [Doc ann] -> Doc ann
hsep forall a b. (a -> b) -> a -> b
$ forall ann. Doc ann -> [Doc ann] -> [Doc ann]
punctuate Doc Any
", " ([Doc Any]
xs' forall a. Semigroup a => a -> a -> a
<> [Doc Any]
placeholder)
                toVerilogLiteral' :: x -> Doc Any
toVerilogLiteral' x
x
                    | forall a. Num a => a -> a
abs x
x forall a. Eq a => a -> a -> Bool
/= x
x = [i|-#{ wordWidth }'sd#{ dataLiteral (-x) }|]
                    | Bool
otherwise = [i|#{ wordWidth }'sd#{ dataLiteral x }|]

                disable :: Verilog
disable =
                    [__i|
                        initial begin
                            @(negedge #{ sigRst });
                            #{ envInitFlagName } <= 1;
                        end
                    |]

                envInitFlagName :: Text
envInitFlagName =
                    forall a. a -> Maybe a -> a
fromMaybe (forall a. HasCallStack => String -> a
error String
"SPI: testEnvironment: internal error") forall a b. (a -> b) -> a -> b
$
                        forall pu v x. IOTestBench pu v x => Text -> pu -> Maybe Text
testEnvironmentInitFlag Text
tag SPI v x t
sio
             in case IOPorts (SPI v x t)
ioPorts of
                    SPISlave{OutputPortTag
InputPortTag
slave_cs :: InputPortTag
slave_sclk :: InputPortTag
slave_miso :: OutputPortTag
slave_mosi :: InputPortTag
$sel:slave_cs:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:slave_sclk:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:slave_miso:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:slave_mosi:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
..} ->
                        let receiveCycle :: Map v x -> Doc Any
receiveCycle Map v x
transmit =
                                let xs :: [x]
xs = forall a b. (a -> b) -> [a] -> [b]
map (\v
v -> forall a. a -> Maybe a -> a
fromMaybe forall a. Default a => a
def forall a b. (a -> b) -> a -> b
$ Map v x
transmit forall k a. Ord k => Map k a -> k -> Maybe a
M.!? v
v) [v]
receivedVariablesSeq
                                 in [__i|
                                        $display( "set data for sending #{ viaShow xs } by #{ tag }_io_test_input" );
                                        #{ tag }_io_test_input = { #{ toVerilogLiteral xs } }; // #{ viaShow xs }
                                        #{ tag }_io_test_start_transaction = 1;                           @(posedge #{ sigClk });
                                        #{ tag }_io_test_start_transaction = 0;                           @(posedge #{ sigClk });
                                        repeat( #{ sendingDuration } ) @(posedge #{ sigClk });

                                    |]

                            sendingAssert :: HashMap v x -> Doc Any
sendingAssert HashMap v x
transmit =
                                let xs :: [x]
xs = forall a b. (a -> b) -> [a] -> [b]
map (\v
v -> forall a. a -> Maybe a -> a
fromMaybe forall a. Default a => a
def forall a b. (a -> b) -> a -> b
$ forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup v
v HashMap v x
transmit) [v]
sendedVariableSeq
                                 in [__i|
                                        @(posedge #{ tag }_io_test_start_transaction);
                                            $write( "#{ tag }_io_test_output actual: %H except: %H ({ #{ toVerilogLiteral xs } })",
                                                #{ tag }_io_test_output, { #{ toVerilogLiteral xs } } );
                                            if ( #{ tag }_io_test_output != { #{ toVerilogLiteral xs } } ) $display("\t\tFAIL");
                                            else $display();

                                    |]

                            endDeviceInstance :: Verilog
endDeviceInstance =
                                [__i|
                                    /*
                                    #{ pretty sio }
                                    */
                                    reg #{ tag }_io_test_start_transaction;
                                    reg  [#{ frameWidth }-1:0] #{ tag }_io_test_input;
                                    wire #{ tag }_io_test_ready;
                                    wire [#{ frameWidth }-1:0] #{ tag }_io_test_output;
                                    initial #{ envInitFlagName } <= 0; // should be defined on the testbench level.
                                    spi_master_driver \#
                                            ( .DATA_WIDTH( #{ frameWidth } )
                                            , .SCLK_HALFPERIOD( 1 )
                                            ) #{ tag }_io_test
                                        ( .clk( #{ sigClk } )
                                        , .rst( #{ sigRst } )
                                        , .start_transaction( #{ tag }_io_test_start_transaction )
                                        , .data_in( #{ tag }_io_test_input )
                                        , .data_out( #{ tag }_io_test_output )
                                        , .ready( #{ tag }_io_test_ready )
                                        , .mosi( #{ slave_mosi } )
                                        , .miso( #{ slave_miso } )
                                        , .sclk( #{ slave_sclk } )
                                        , .cs( #{ slave_cs } )
                                        );
                                    initial #{ tag }_io_test.inner.shiftreg <= 0;
                                |]

                            envDeviceControl :: Verilog
envDeviceControl =
                                [__i|
                                    initial begin
                                        #{ tag }_io_test_start_transaction <= 0;
                                        #{ tag }_io_test_input <= 0;
                                        @(negedge #{ sigRst });
                                        repeat(#{ timeLag }) @(posedge #{ sigClk });

                                        #{ nest 4 $ vsep $ map receiveCycle receivedVarsValues }
                                        repeat ( 5 ) begin
                                            #{ nest 8 $ receiveCycle def }
                                        end

                                        // $finish; // DON'T DO THAT (with this line test can pass without data checking)
                                    end
                                |]
                            envDeviceCheck :: Verilog
envDeviceCheck =
                                [__i|
                                    initial begin
                                        @(negedge #{ sigRst });
                                        repeat ( OUTPUT_LATENCY ) @(posedge #{ tag }_io_test_start_transaction); // latency

                                        #{ nest 4 $ vsep $ map sendingAssert sendedVarsValues }
                                        forever begin
                                            @(posedge spi_io_test_start_transaction);
                                            $display( "#{ tag }_io_test_output actual: %H", #{ tag }_io_test_output );
                                        end
                                    end
                                |]
                         in -- FIXME: do not check output signals when we drop data
                            forall a. a -> Maybe a
Just
                                [__i|
                                    ////////////////////////////////////////
                                    // SPI test environment
                                    localparam NITTA_LATENCY = 1;
                                    localparam OUTPUT_LATENCY = 3;

                                    // SPI device in test environment
                                    #{ endDeviceInstance :: Verilog }

                                    // SPI device in test environment control
                                    #{ if frameWordCount == 0 then disable else envDeviceControl }

                                    // SPI device in test environment check
                                    #{ if frameWordCount == 0 then disable else envDeviceCheck }

                                    // SPI environment initialization flag set
                                    initial begin
                                        repeat ( NITTA_LATENCY ) @(posedge spi_io_test_start_transaction);
                                        spi_env_init_flag <= 1;
                                    end
                                |]
                    SPIMaster{OutputPortTag
InputPortTag
master_cs :: OutputPortTag
master_sclk :: OutputPortTag
master_miso :: InputPortTag
master_mosi :: OutputPortTag
$sel:master_cs:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:master_sclk:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
$sel:master_miso:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> InputPortTag
$sel:master_mosi:SPIMaster :: forall v x t. IOPorts (SPI v x t) -> OutputPortTag
..} ->
                        let receiveCycle :: Map v x -> Doc Any
receiveCycle Map v x
transmit =
                                let xs :: [x]
xs = forall a b. (a -> b) -> [a] -> [b]
map (\v
v -> forall a. a -> Maybe a -> a
fromMaybe forall a. Default a => a
def forall a b. (a -> b) -> a -> b
$ Map v x
transmit forall k a. Ord k => Map k a -> k -> Maybe a
M.!? v
v) [v]
receivedVariablesSeq
                                 in [__i|
                                        #{ tag }_io_test_input = { #{ toVerilogLiteral xs } }; // #{ xs }
                                        @(posedge #{ tag }_io_test_ready);
                                    |]

                            sendingAssert :: HashMap v x -> Doc Any
sendingAssert HashMap v x
transmit =
                                let xs :: [x]
xs = forall a b. (a -> b) -> [a] -> [b]
map (\v
v -> forall a. a -> Maybe a -> a
fromMaybe forall a. Default a => a
def forall a b. (a -> b) -> a -> b
$ forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup v
v HashMap v x
transmit) [v]
sendedVariableSeq
                                 in [__i|
                                        @(posedge #{ tag }_io_test_ready);
                                            $display( "#{ tag }_io_test_output except: %H ({ #{ toVerilogLiteral xs } })", { #{ toVerilogLiteral xs } } );
                                            $display( "#{ tag }_io_test_output actual: %H", #{ tag }_io_test_output );
                                            if ( #{ tag }_io_test_output !=  { #{ toVerilogLiteral xs } } )
                                                $display("                       FAIL");
                                            $display();
                                    |]

                            envInstance :: Verilog
envInstance =
                                [__i|
                                    /*
                                    #{ pretty sio }
                                    */
                                    reg #{ tag }_io_test_start_transaction;
                                    reg  [#{ frameWidth }-1:0] #{ tag }_io_test_input;
                                    wire #{ tag }_io_test_ready;
                                    wire [#{ frameWidth }-1:0] #{ tag }_io_test_output;
                                    initial #{ envInitFlagName } <= 0; // should be defined on the testbench level.
                                    spi_slave_driver \#
                                            ( .DATA_WIDTH( #{ frameWidth } )
                                            ) #{ tag }_io_test_slave
                                        ( .clk( #{ sigClk } )
                                        , .rst( #{ sigRst } )
                                        , .data_in( #{ tag }_io_test_input )
                                        , .data_out( #{ tag }_io_test_output )
                                        , .ready( #{ tag }_io_test_ready )
                                        , .mosi( #{ master_mosi } )
                                        , .miso( #{ master_miso } )
                                        , .sclk( #{ master_sclk } )
                                        , .cs( #{ master_cs } )
                                        );
                                |]

                            interactions :: Verilog
interactions =
                                [__i|
                                    // SPI Input signal generation
                                    initial begin
                                        @(negedge #{ sigRst });
                                        #{ nest 4 $ receiveCycle $ head receivedVarsValues }
                                        #{ envInitFlagName } <= 1;

                                        #{ nest 4 $ vsep $ map receiveCycle $ tail receivedVarsValues }
                                        repeat(70) @(posedge #{ sigClk });
                                        // $finish; // DON'T DO THAT (with this line test can pass without data checking)
                                    end

                                    // SPI Output signal checking
                                    initial begin
                                        @(negedge #{ sigRst });
                                        repeat(2) @(posedge #{ tag }_io_test_ready);
                                        #{ nest 4 $ vsep $ map sendingAssert sendedVarsValues }
                                    end
                                |]
                         in forall a. a -> Maybe a
Just (Verilog
envInstance forall a. Semigroup a => a -> a -> a
<> forall ann. Doc ann
line forall a. Semigroup a => a -> a -> a
<> forall ann. Doc ann
line forall a. Semigroup a => a -> a -> a
<> if Int
frameWordCount forall a. Eq a => a -> a -> Bool
== Int
0 then Verilog
disable else Verilog
interactions)
    testEnvironment Text
_title SPI v x t
_pu UnitEnv (SPI v x t)
_env TestEnvironment v x
_tEnv = forall a. HasCallStack => String -> a
error String
"internal error"