{-|
Module      : Ice40.IO
Description : Ice40 IO hard IP primitives
Copyright   : (c) David Cox, 2021-2024
License     : BSD 3-Clause
Maintainer  : standardsemiconductor@gmail.com

IO hard IP primitive from [Lattice Ice Technology Library](https://github.com/standardsemiconductor/VELDT-info/blob/master/SBTICETechnologyLibrary201708.pdf)
-}
module Ice40.IO where

import Clash.Prelude
import Clash.Annotations.Primitive
import Data.String.Interpolate (i)
import Data.String.Interpolate.Util (unindent)

{-# ANN ioPrim (InlinePrimitive [Verilog] $ unindent [i|
  [  { "BlackBox" :
       { "name" : "Ice40.IO.ioPrim"
       , "kind" : "Declaration"
       , "type" :
  "ioPrim
    :: BitVector 6         -- ARG[0]  pinType
    -> Bit                 -- ARG[1]  pullup
    -> Bit                 -- ARG[2]  negTrigger
    -> String              -- ARG[3]  ioStandard
    -> Signal domIn Bit    -- ARG[4]  latchInputValue
    -> Signal domEn Bit    -- ARG[5]  clockEnable
    -> Clock domIn         -- ARG[6]  inputClk
    -> Clock domOut        -- ARG[7]  outputClk
    -> Signal domOut Bit   -- ARG[8]  outputEnable
    -> Signal domOut Bit   -- ARG[9]  dOut0
    -> Signal domOut Bit   -- ARG[10] dOut1
    -> ( Signal domPin Bit -- packagePin
       , Signal domIn Bit  -- dIn0
       , Signal domIn Bit  -- dIn1
       )"
       , "template" :
  "//SB_IO begin
  wire ~GENSYM[package_pin][0];
  wire ~GENSYM[d_in_0][1];
  wire ~GENSYM[d_in_1][2];

  SB_IO #(
    .PIN_TYPE         ( ~ARG[0]  ),
    .PULLUP           ( ~ARG[1]  ),
    .NEG_TRIGGER      ( ~ARG[2]  ),
    .IO_STANDARD      ( ~ARG[3]  )
  ) ~GENSYM[sb_io_inst][3] (
    .PACKAGE_PIN      ( ~SYM[0]  ),
    .LATCH_INPUT_VALUE( ~ARG[4]  ),
    .CLOCK_ENABLE     ( ~ARG[5]  ),
    .INPUT_CLK        ( ~ARG[6]  ),
    .OUTPUT_CLK       ( ~ARG[7]  ),
    .OUTPUT_ENABLE    ( ~ARG[8]  ),
    .D_OUT_0          ( ~ARG[9]  ),
    .D_OUT_1          ( ~ARG[10] ),
    .D_IN_0           ( ~SYM[1]  ),
    .D_IN_1           ( ~SYM[2]  )
  );

  assign ~RESULT = { ~SYM[0], ~SYM[1], ~SYM[2] };
  //SB_IO end"
       }
     }
  ]
  |]) #-}

-- | IO primitive, see io for wrapper
{-# NOINLINE ioPrim #-}
ioPrim
  :: BitVector 6         -- ^ pinType
  -> Bit                 -- ^ pullup
  -> Bit                 -- ^ negTrigger
  -> String              -- ^ ioStandard
  -> Signal domIn Bit    -- ^ latchInputValue
  -> Signal domEn Bit    -- ^ clockEnable
  -> Clock domIn         -- ^ inputClk
  -> Clock domOut        -- ^ outputClk
  -> Signal domOut Bit   -- ^ outputEnable
  -> Signal domOut Bit   -- ^ dOut0
  -> Signal domOut Bit   -- ^ dOut1
  -> ( Signal domPin Bit -- packagePin
     , Signal domIn Bit  -- dIn0
     , Signal domIn Bit  -- dIn1
     ) -- ^ (packagePin, dIn0, dIn1)
ioPrim :: forall (domIn :: Domain) (domEn :: Domain) (domOut :: Domain)
       (domPin :: Domain).
BitVector 6
-> Bit
-> Bit
-> String
-> Signal domIn Bit
-> Signal domEn Bit
-> Clock domIn
-> Clock domOut
-> Signal domOut Bit
-> Signal domOut Bit
-> Signal domOut Bit
-> (Signal domPin Bit, Signal domIn Bit, Signal domIn Bit)
ioPrim !BitVector 6
_ !Bit
_ !Bit
_ !String
_ !Signal domIn Bit
_ !Signal domEn Bit
_ !Clock domIn
_ !Clock domOut
_ !Signal domOut Bit
_ !Signal domOut Bit
_ !Signal domOut Bit
_ = (Bit -> Signal domPin Bit
forall a. a -> Signal domPin a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure Bit
0, Bit -> Signal domIn Bit
forall a. a -> Signal domIn a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure Bit
0, Bit -> Signal domIn Bit
forall a. a -> Signal domIn a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure Bit
0)

-- | Input pin configuration parameter
data PinInput = PinInput -- ^ Simple Input pin dIn0
              | PinInputLatch -- ^ Disables Internal data changes on the physical input pin by latching the value
              | PinInputRegistered -- ^ Input data is registered in input cell
              | PinInputRegisteredLatch -- ^ Disables internal data changes on the physical input pin by latching the value on the input register
              | PinInputDDR -- ^ Input DDR data is clocked out on rising and falling clock edges. Use the dIn0 and dIn1 pins for DDR operation
  deriving stock ((forall x. PinInput -> Rep PinInput x)
-> (forall x. Rep PinInput x -> PinInput) -> Generic PinInput
forall x. Rep PinInput x -> PinInput
forall x. PinInput -> Rep PinInput x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. PinInput -> Rep PinInput x
from :: forall x. PinInput -> Rep PinInput x
$cto :: forall x. Rep PinInput x -> PinInput
to :: forall x. Rep PinInput x -> PinInput
Generic, Int -> PinInput -> ShowS
[PinInput] -> ShowS
PinInput -> String
(Int -> PinInput -> ShowS)
-> (PinInput -> String) -> ([PinInput] -> ShowS) -> Show PinInput
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PinInput -> ShowS
showsPrec :: Int -> PinInput -> ShowS
$cshow :: PinInput -> String
show :: PinInput -> String
$cshowList :: [PinInput] -> ShowS
showList :: [PinInput] -> ShowS
Show, ReadPrec [PinInput]
ReadPrec PinInput
Int -> ReadS PinInput
ReadS [PinInput]
(Int -> ReadS PinInput)
-> ReadS [PinInput]
-> ReadPrec PinInput
-> ReadPrec [PinInput]
-> Read PinInput
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS PinInput
readsPrec :: Int -> ReadS PinInput
$creadList :: ReadS [PinInput]
readList :: ReadS [PinInput]
$creadPrec :: ReadPrec PinInput
readPrec :: ReadPrec PinInput
$creadListPrec :: ReadPrec [PinInput]
readListPrec :: ReadPrec [PinInput]
Read, PinInput -> PinInput -> Bool
(PinInput -> PinInput -> Bool)
-> (PinInput -> PinInput -> Bool) -> Eq PinInput
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PinInput -> PinInput -> Bool
== :: PinInput -> PinInput -> Bool
$c/= :: PinInput -> PinInput -> Bool
/= :: PinInput -> PinInput -> Bool
Eq)
  deriving anyclass HasCallStack => String -> PinInput
PinInput -> Bool
PinInput -> ()
PinInput -> PinInput
(HasCallStack => String -> PinInput)
-> (PinInput -> Bool)
-> (PinInput -> PinInput)
-> (PinInput -> ())
-> NFDataX PinInput
forall a.
(HasCallStack => String -> a)
-> (a -> Bool) -> (a -> a) -> (a -> ()) -> NFDataX a
$cdeepErrorX :: HasCallStack => String -> PinInput
deepErrorX :: HasCallStack => String -> PinInput
$chasUndefined :: PinInput -> Bool
hasUndefined :: PinInput -> Bool
$censureSpine :: PinInput -> PinInput
ensureSpine :: PinInput -> PinInput
$crnfX :: PinInput -> ()
rnfX :: PinInput -> ()
NFDataX

-- | Convert `PinInput` to underlying `BitVector`
fromPinInput :: PinInput -> BitVector 2
fromPinInput :: PinInput -> BitVector 2
fromPinInput = \case
  PinInput
PinInput                -> BitVector 2
0b01
  PinInput
PinInputLatch           -> BitVector 2
0b11
  PinInput
PinInputRegistered      -> BitVector 2
0b00
  PinInput
PinInputRegisteredLatch -> BitVector 2
0b10
  PinInput
PinInputDDR             -> BitVector 2
0b00

-- | Output pin configuration parameter
data PinOutput = PinNoOutput -- ^Disables the output function
               | PinOutput -- ^ Simple output pin (no enable)
               | PinOutputTristate -- ^ The output pin may be tristated using the enable
               | PinOutputEnableRegistered -- ^ The output pin may be tristated using a registered enable signal
               | PinOutputRegistered -- ^ Output registered (no enable)
               | PinOutputRegisteredEnable -- ^ Output registered with enable (the enable is not registered)
               | PinOutputRegisteredEnableRegistered -- ^ Output registered and enable registered
               | PinOutputDDR -- ^ Output DDR data is clocked out on rising and falling clock edges
               | PinOutputDDREnable -- ^ Output data is clocked out on rising and falling clock edges
               | PinOutputDDREnableRegistered -- ^ Output DDR data with registered enable signal
               | PinOutputRegisteredInverted -- ^ Output registered signal is inverted
               | PinOutputRegisteredEnableInverted -- ^ Output signal is registered and inverted (no enable function)
               | PinOutputRegisteredEnableRegisteredInverted -- ^ Output signal is registered and inverted, the enable/tristate control is registered
  deriving stock ((forall x. PinOutput -> Rep PinOutput x)
-> (forall x. Rep PinOutput x -> PinOutput) -> Generic PinOutput
forall x. Rep PinOutput x -> PinOutput
forall x. PinOutput -> Rep PinOutput x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. PinOutput -> Rep PinOutput x
from :: forall x. PinOutput -> Rep PinOutput x
$cto :: forall x. Rep PinOutput x -> PinOutput
to :: forall x. Rep PinOutput x -> PinOutput
Generic, Int -> PinOutput -> ShowS
[PinOutput] -> ShowS
PinOutput -> String
(Int -> PinOutput -> ShowS)
-> (PinOutput -> String)
-> ([PinOutput] -> ShowS)
-> Show PinOutput
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PinOutput -> ShowS
showsPrec :: Int -> PinOutput -> ShowS
$cshow :: PinOutput -> String
show :: PinOutput -> String
$cshowList :: [PinOutput] -> ShowS
showList :: [PinOutput] -> ShowS
Show, ReadPrec [PinOutput]
ReadPrec PinOutput
Int -> ReadS PinOutput
ReadS [PinOutput]
(Int -> ReadS PinOutput)
-> ReadS [PinOutput]
-> ReadPrec PinOutput
-> ReadPrec [PinOutput]
-> Read PinOutput
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS PinOutput
readsPrec :: Int -> ReadS PinOutput
$creadList :: ReadS [PinOutput]
readList :: ReadS [PinOutput]
$creadPrec :: ReadPrec PinOutput
readPrec :: ReadPrec PinOutput
$creadListPrec :: ReadPrec [PinOutput]
readListPrec :: ReadPrec [PinOutput]
Read, PinOutput -> PinOutput -> Bool
(PinOutput -> PinOutput -> Bool)
-> (PinOutput -> PinOutput -> Bool) -> Eq PinOutput
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PinOutput -> PinOutput -> Bool
== :: PinOutput -> PinOutput -> Bool
$c/= :: PinOutput -> PinOutput -> Bool
/= :: PinOutput -> PinOutput -> Bool
Eq)
  deriving anyclass HasCallStack => String -> PinOutput
PinOutput -> Bool
PinOutput -> ()
PinOutput -> PinOutput
(HasCallStack => String -> PinOutput)
-> (PinOutput -> Bool)
-> (PinOutput -> PinOutput)
-> (PinOutput -> ())
-> NFDataX PinOutput
forall a.
(HasCallStack => String -> a)
-> (a -> Bool) -> (a -> a) -> (a -> ()) -> NFDataX a
$cdeepErrorX :: HasCallStack => String -> PinOutput
deepErrorX :: HasCallStack => String -> PinOutput
$chasUndefined :: PinOutput -> Bool
hasUndefined :: PinOutput -> Bool
$censureSpine :: PinOutput -> PinOutput
ensureSpine :: PinOutput -> PinOutput
$crnfX :: PinOutput -> ()
rnfX :: PinOutput -> ()
NFDataX

-- | Convert `PinOutput` to underlying `BitVector`
fromPinOutput :: PinOutput -> BitVector 4
fromPinOutput :: PinOutput -> BitVector 4
fromPinOutput = \case
  PinOutput
PinNoOutput                                 -> BitVector 4
0b0000
  PinOutput
PinOutput                                   -> BitVector 4
0b0110
  PinOutput
PinOutputTristate                           -> BitVector 4
0b1010
  PinOutput
PinOutputEnableRegistered                   -> BitVector 4
0b1110
  PinOutput
PinOutputRegistered                         -> BitVector 4
0b0101
  PinOutput
PinOutputRegisteredEnable                   -> BitVector 4
0b1001
  PinOutput
PinOutputRegisteredEnableRegistered         -> BitVector 4
0b1101
  PinOutput
PinOutputDDR                                -> BitVector 4
0b0100
  PinOutput
PinOutputDDREnable                          -> BitVector 4
0b1000
  PinOutput
PinOutputDDREnableRegistered                -> BitVector 4
0b1100
  PinOutput
PinOutputRegisteredInverted                 -> BitVector 4
0b0111
  PinOutput
PinOutputRegisteredEnableInverted           -> BitVector 4
0b1011
  PinOutput
PinOutputRegisteredEnableRegisteredInverted -> BitVector 4
0b1111

-- | Input-Output Standards
data IOStandard = SBLVCMOS
                | SBLVDSINPUT
  deriving ((forall x. IOStandard -> Rep IOStandard x)
-> (forall x. Rep IOStandard x -> IOStandard) -> Generic IOStandard
forall x. Rep IOStandard x -> IOStandard
forall x. IOStandard -> Rep IOStandard x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. IOStandard -> Rep IOStandard x
from :: forall x. IOStandard -> Rep IOStandard x
$cto :: forall x. Rep IOStandard x -> IOStandard
to :: forall x. Rep IOStandard x -> IOStandard
Generic, Int -> IOStandard -> ShowS
[IOStandard] -> ShowS
IOStandard -> String
(Int -> IOStandard -> ShowS)
-> (IOStandard -> String)
-> ([IOStandard] -> ShowS)
-> Show IOStandard
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> IOStandard -> ShowS
showsPrec :: Int -> IOStandard -> ShowS
$cshow :: IOStandard -> String
show :: IOStandard -> String
$cshowList :: [IOStandard] -> ShowS
showList :: [IOStandard] -> ShowS
Show, ReadPrec [IOStandard]
ReadPrec IOStandard
Int -> ReadS IOStandard
ReadS [IOStandard]
(Int -> ReadS IOStandard)
-> ReadS [IOStandard]
-> ReadPrec IOStandard
-> ReadPrec [IOStandard]
-> Read IOStandard
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS IOStandard
readsPrec :: Int -> ReadS IOStandard
$creadList :: ReadS [IOStandard]
readList :: ReadS [IOStandard]
$creadPrec :: ReadPrec IOStandard
readPrec :: ReadPrec IOStandard
$creadListPrec :: ReadPrec [IOStandard]
readListPrec :: ReadPrec [IOStandard]
Read, IOStandard -> IOStandard -> Bool
(IOStandard -> IOStandard -> Bool)
-> (IOStandard -> IOStandard -> Bool) -> Eq IOStandard
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: IOStandard -> IOStandard -> Bool
== :: IOStandard -> IOStandard -> Bool
$c/= :: IOStandard -> IOStandard -> Bool
/= :: IOStandard -> IOStandard -> Bool
Eq)

-- | Convert `IOStandard` to underlying `String`
fromIOStandard :: IOStandard -> String
fromIOStandard :: IOStandard -> String
fromIOStandard = \case
  IOStandard
SBLVCMOS    -> String
"SB_LVCMOS"
  IOStandard
SBLVDSINPUT -> String
"SB_LVDS_INPUT"

-- | IO primitive
io
  :: PinInput
  -> PinOutput
  -> Bit                 -- ^ pullUp
  -> Bit                 -- ^ negTrigger
  -> IOStandard
  -> Signal domIn Bit    -- ^ latchInputValue
  -> Signal domEn Bit    -- ^ clockEnable
  -> Clock domIn         -- ^ inputClk
  -> Clock domOut        -- ^ outputClk
  -> Signal domOut Bit   -- ^ outputEnable
  -> Signal domOut Bit   -- ^ dOut0
  -> Signal domOut Bit   -- ^ dOut1
  -> ( Signal domPin Bit -- packagePin
     , Signal domIn Bit  -- dIn0
     , Signal domIn Bit  -- dIn1
     ) -- ^ (packagePin, dIn0, dIn1)
io :: forall (domIn :: Domain) (domEn :: Domain) (domOut :: Domain)
       (domPin :: Domain).
PinInput
-> PinOutput
-> Bit
-> Bit
-> IOStandard
-> Signal domIn Bit
-> Signal domEn Bit
-> Clock domIn
-> Clock domOut
-> Signal domOut Bit
-> Signal domOut Bit
-> Signal domOut Bit
-> (Signal domPin Bit, Signal domIn Bit, Signal domIn Bit)
io PinInput
pinInput PinOutput
pinOutput Bit
pullUp Bit
negTrigger IOStandard
ioStandard
  = BitVector 6
-> Bit
-> Bit
-> String
-> Signal domIn Bit
-> Signal domEn Bit
-> Clock domIn
-> Clock domOut
-> Signal domOut Bit
-> Signal domOut Bit
-> Signal domOut Bit
-> (Signal domPin Bit, Signal domIn Bit, Signal domIn Bit)
forall (domIn :: Domain) (domEn :: Domain) (domOut :: Domain)
       (domPin :: Domain).
BitVector 6
-> Bit
-> Bit
-> String
-> Signal domIn Bit
-> Signal domEn Bit
-> Clock domIn
-> Clock domOut
-> Signal domOut Bit
-> Signal domOut Bit
-> Signal domOut Bit
-> (Signal domPin Bit, Signal domIn Bit, Signal domIn Bit)
ioPrim
      (PinOutput -> BitVector 4
fromPinOutput PinOutput
pinOutput BitVector 4 -> BitVector 2 -> BitVector (4 + 2)
forall (m :: Nat) (n :: Nat).
KnownNat m =>
BitVector n -> BitVector m -> BitVector (n + m)
++# PinInput -> BitVector 2
fromPinInput PinInput
pinInput)
      Bit
pullUp
      Bit
negTrigger
      (IOStandard -> String
fromIOStandard IOStandard
ioStandard)