packages feed

lion-0.3.0.0: src/Lion/Core.hs

{-|
Module      : Lion.Core
Description : Lion RISC-V Core
Copyright   : (c) David Cox, 2021
License     : BSD-3-Clause
Maintainer  : standardsemiconductor@gmail.com

The Lion core is a 32-bit [RISC-V](https://riscv.org/about/) processor written in Haskell using [Clash](https://clash-lang.org). Note, all peripherals and memory must have single cycle latency. See [lion-soc](https://github.com/standardsemiconductor/lion/tree/main/lion-soc) for an example of using the Lion core in a system.
-}

module Lion.Core 
  ( core
  , defaultCoreConfig
  , P.defaultPipeConfig
  , CoreConfig(..)
  , AluConfig(..)
  , P.PipeConfig(..)
  , FromCore(..)
  , P.ToMem(..)
  , P.MemoryAccess(..)
  , Alu
  ) where

import Clash.Prelude
import Data.Proxy
import Data.Maybe
import Data.Monoid
import Lion.Alu
import Lion.Rvfi
import qualified Lion.Pipe as P
import qualified Lion.Instruction as I (Op(Add))

-- | Core configuration
--
-- ALU configuration default: `Soft`
newtype CoreConfig (startPC :: Nat) (a :: AluConfig) = CoreConfig
  { pipeConfig :: P.PipeConfig (startPC :: Nat) -- ^ pipeline configuration
  }
  deriving stock (Generic, Show, Eq)

-- | Default core configuration
--
-- ALU configuration = `Soft`
--
-- `pipeConfig` = `defaultPipeConfig`
defaultCoreConfig :: CoreConfig 0 'Soft
defaultCoreConfig = CoreConfig
  { pipeConfig = P.defaultPipeConfig
  }

-- | Core outputs
data FromCore dom = FromCore
  { toMem  :: Signal dom (Maybe P.ToMem) -- ^ shared memory and instruction bus, output from core to memory and peripherals
  , toRvfi :: Signal dom Rvfi -- ^ formal verification interface output, see [lion-formal](https://github.com/standardsemiconductor/lion/tree/main/lion-formal) for usage
  }

-- | RISC-V Core: RV32I
core
  :: forall a startPC dom
   . HiddenClockResetEnable dom
  => Alu a
  => (KnownNat startPC, startPC <= 0xFFFFFFFF)
  => CoreConfig (startPC :: Nat) (a :: AluConfig) -- ^ core configuration
  -> Signal dom (BitVector 32)   -- ^ core input, from memory/peripherals
  -> FromCore dom                -- ^ core output
core config toCore = FromCore
  { toMem  = getFirst . P._toMem <$> fromPipe
  , toRvfi = fromMaybe mkRvfi . getFirst . P._toRvfi <$> fromPipe
  }
  where
    -- alu connection
    aluOp = fromMaybe I.Add . getFirst . P._toAluOp     <$> fromPipe
    aluInput1 = fromMaybe 0 . getFirst . P._toAluInput1 <$> fromPipe
    aluInput2 = fromMaybe 0 . getFirst . P._toAluInput2 <$> fromPipe
    aluOutput = alu (Proxy :: Proxy a) aluOp aluInput1 aluInput2
    -- reg bank connection
    rs1Addr = fromMaybe 0 . getFirst . P._toRs1Addr <$> fromPipe
    rs2Addr = fromMaybe 0 . getFirst . P._toRs2Addr <$> fromPipe
    rdWrM = getFirst . P._toRd <$> fromPipe
    (rs1Data, rs2Data) = regBank rs1Addr rs2Addr rdWrM

    -- pipeline connection
    fromPipe = P.pipe (pipeConfig config) $ P.ToPipe <$> rs1Data 
                                                     <*> rs2Data 
                                                     <*> aluOutput
                                                     <*> toCore

-- | Register bank
regBank
  :: HiddenClockResetEnable dom
  => Signal dom (Unsigned 5)                        -- ^ Rs1 Addr
  -> Signal dom (Unsigned 5)                        -- ^ Rs2 Addr
  -> Signal dom (Maybe (Unsigned 5, BitVector 32))  -- ^ Rd Write
  -> Unbundled dom (BitVector 32, BitVector 32)     -- ^ (Rs1Data, Rs2Data)
regBank rs1Addr rs2Addr rdWrM = (regFile rs1Addr, regFile rs2Addr)
  where
    regFile = flip (readNew (blockRamPow2 (repeat 0))) rdWrM