packages feed

ice40-prim-0.2.0.0: src/Ice40/Spram.hs

{-|
Module      : Ice40.Spram
Description : Ice40 single-port RAM hard IP primitive
Copyright   : (c) David Cox, 2021
License     : BSD 3-Clause
Maintainer  : standardsemiconductor@gmail.com

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

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

{-# ANN spramPrim (InlinePrimitive [Verilog] $ unindent [i|
  [  { "BlackBox" :
       { "name" : "Ice40.Spram.spramPrim"
       , "kind" : "Declaration"
       , "type" :
  "spramPrim
  :: KnownDomain dom            -- ARG[0] 
  => Clock dom                  -- ARG[1]  clock
  -> Signal dom (BitVector 14)  -- ARG[2]  address
  -> Signal dom (BitVector 16)  -- ARG[3]  dataIn
  -> Signal dom (BitVector 4)   -- ARG[4]  maskWrEn
  -> Signal dom Bit             -- ARG[5]  wrEN
  -> Signal dom Bit             -- ARG[6]  chipSelect
  -> Signal dom Bit             -- ARG[7]  standBy
  -> Signal dom Bit             -- ARG[8]  sleep
  -> Signal dom Bit             -- ARG[9] powerOff
  -> Signal dom (BitVector 16)  -- dataOut"
       , "template" :
  "//SB_SPRAM begin
  SB_SPRAM256KA ~GENSYM[sb_spram_inst][0] (
    .ADDRESS   (~ARG[2]),
    .DATAIN    (~ARG[3]),
    .MASKWREN  (~ARG[4]),
    .WREN      (~ARG[5]),
    .CHIPSELECT(~ARG[6]),
    .CLOCK     (~ARG[1]),
    .STANDBY   (~ARG[7]),
    .SLEEP     (~ARG[8]),
    .POWEROFF  (~ARG[9]),
    .DATAOUT   (~RESULT)
  );
  //SB_SPRAM end"
       }
    }
  ]
  |]) #-}

data Nyb = Nyb3 | Nyb2 | Nyb1 | Nyb0

-- | Single port RAM primitive
{-# NOINLINE spramPrim #-}
spramPrim
  :: KnownDomain dom            -- ARG[0] 
  => Clock dom                  -- ^ clock
  -> Signal dom (BitVector 14)  -- ^ address
  -> Signal dom (BitVector 16)  -- ^ dataIn
  -> Signal dom (BitVector 4)   -- ^ maskWrEn
  -> Signal dom Bit             -- ^ wrEN
  -> Signal dom Bit             -- ^ chipSelect
  -> Signal dom Bit             -- ^ standBy
  -> Signal dom Bit             -- ^ sleep
  -> Signal dom Bit             -- ^ powerOff
  -> Signal dom (BitVector 16)  -- ^ dataOut
spramPrim clock address dataIn maskWrEn wrEn chipSelect !_ !_ !_
  = concat4 <$> nyb3 <*> nyb2 <*> nyb1 <*> nyb0
  where
    addressU = unpack <$> address
    concat4 a b c d = a ++# b ++# c ++# d
    ramEn = toEnable $ bitToBool <$> chipSelect
    nybRam = withClock clock $ withEnable ramEn (blockRamPow2 (repeat 0) addressU)
    nyb3 = nybRam wrM3
    nyb2 = nybRam wrM2
    nyb1 = nybRam wrM1
    nyb0 = nybRam wrM0
    wrM3 = writeGuard Nyb3 <$> dataIn <*> addressU <*> wrEn <*> maskWrEn
    wrM2 = writeGuard Nyb2 <$> dataIn <*> addressU <*> wrEn <*> maskWrEn
    wrM1 = writeGuard Nyb1 <$> dataIn <*> addressU <*> wrEn <*> maskWrEn
    wrM0 = writeGuard Nyb0 <$> dataIn <*> addressU <*> wrEn <*> maskWrEn
    nybSlice = \case
      Nyb3 -> slice d15 d12
      Nyb2 -> slice d11 d8
      Nyb1 -> slice d7  d4
      Nyb0 -> slice d3  d0
    nybMask = \case
      Nyb3 -> (!(3 :: Index 4))
      Nyb2 -> (!(2 :: Index 4))
      Nyb1 -> (!(1 :: Index 4))
      Nyb0 -> (!(0 :: Index 4))
    writeGuard n dIn addr en mask
      | bitToBool en && (not.bitToBool) (nybMask n mask) = Just (addr, nybSlice n dIn)
      | otherwise = Nothing