packages feed

libriscv-0.1.0.0: lib/LibRISCV/Semantics/RV_I/Default.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE QualifiedDo #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}

module LibRISCV.Semantics.RV_I.Default where

import Control.Monad.Freer
import LibRISCV.Effects.Decoding.Language (Decoding)
import LibRISCV.Effects.Expressions.Expr
import LibRISCV.Effects.Expressions.Language (ExprEval, unlessExprM, whenExprM)
import LibRISCV.Effects.Logging.Language (LogInstructionFetch)
import LibRISCV.Effects.Operations.Language (Operations (..), Size (..), ebreak, ecall, exception, readPC)
import LibRISCV.Internal.Decoder.Opcodes (RV_I (..))
import LibRISCV.Semantics.Utils

instrSemantics ::
    forall v r.
    ( Member (Operations v) r
    , Member LogInstructionFetch r
    , Member (Decoding v) r
    , Member (ExprEval v) r
    ) =>
    Int ->
    v ->
    RV_I ->
    Eff r ()
instrSemantics width pc =
    let
        fromUInt :: Integer -> Expr v
        fromUInt = FromInt width

        -- False if a given address is not aligned at the four-byte boundary.
        isMisaligned :: Expr v -> Expr v
        isMisaligned addr = (addr `And` fromUInt 0x3) `Uge` fromUInt 1
     in
        \case
            ADDI -> do
                (r1, rd, imm) <- decodeAndReadIType @v
                writeRegister rd $ r1 `addImm` imm
            SLTI -> do
                (r1, rd, imm) <- decodeAndReadIType @v
                let cond = r1 `slt` imm
                writeRegister rd cond
            SLTIU -> do
                (r1, rd, imm) <- decodeAndReadIType @v
                let cond = r1 `ult` imm
                writeRegister rd cond
            ANDI -> do
                (r1, rd, imm) <- decodeAndReadIType @v
                writeRegister rd $ r1 `andImm` imm
            ORI -> do
                (r1, rd, imm) <- decodeAndReadIType @v
                writeRegister rd $ r1 `orImm` imm
            XORI -> do
                (r1, rd, imm) <- decodeAndReadIType @v
                writeRegister rd $ r1 `xorImm` imm
            LUI -> do
                (rd, imm) <- decodeUType @v
                writeRegister rd $ FromImm imm
            AUIPC -> do
                (rd, imm) <- decodeUType
                writeRegister rd $ pc `addImm` imm
            ADD -> do
                (r1, r2, rd) <- decodeAndReadRType @v
                writeRegister rd $ r1 `addImm` r2
            SLT -> do
                (r1, r2, rd) <- decodeAndReadRType
                let cond = r1 `slt` r2 :: Expr v
                writeRegister rd cond
            SLTU -> do
                (r1, r2, rd) <- decodeAndReadRType
                let cond = r1 `ult` r2 :: Expr v
                writeRegister rd cond
            AND -> do
                (r1, r2, rd) <- decodeAndReadRType @v
                writeRegister rd $ r1 `andImm` r2
            OR -> do
                (r1, r2, rd) <- decodeAndReadRType @v
                writeRegister rd $ r1 `orImm` r2
            XOR -> do
                (r1, r2, rd) <- decodeAndReadRType @v
                writeRegister rd $ r1 `xorImm` r2
            SLL -> do
                (r1, r2, rd) <- decodeAndReadRType @v
                writeRegister rd $ FromImm r1 `LShl` regShamt width (FromImm r2)
            SRL -> do
                (r1, r2, rd) <- decodeAndReadRType @v
                writeRegister rd $ FromImm r1 `LShr` regShamt width (FromImm r2)
            SUB -> do
                (r1, r2, rd) <- decodeAndReadRType @v
                writeRegister rd $ r1 `sub` r2
            SRA -> do
                (r1, r2, rd) <- decodeAndReadRType @v
                writeRegister rd $ FromImm r1 `AShr` regShamt width (FromImm r2)
            JAL -> do
                nextInstr <- readPC
                (rd, imm) <- decodeJType

                let newPC = pc `addImm` imm
                writePC newPC
                whenExprM (isMisaligned newPC) $
                    exception pc "misaligned PC"
                writeRegister rd (FromImm nextInstr)
            JALR -> do
                nextInstr <- readPC
                (r1, rd, imm) <- decodeAndReadIType

                let newPC = (r1 `addImm` imm) `And` fromUInt 0xfffffffe
                writePC newPC
                whenExprM (isMisaligned newPC) $
                    exception pc "misaligned PC"
                writeRegister rd $ FromImm nextInstr
            LB -> do
                (r1, rd, imm) <- decodeAndReadIType @v
                byte <- load Byte $ r1 `addImm` imm
                -- TODO: Alignment handling
                writeRegister rd $ sextImm 24 byte
            LBU -> do
                (r1, rd, imm) <- decodeAndReadIType @v
                -- TODO: Alignment handling
                byte <- load Byte $ r1 `addImm` imm
                writeRegister rd $ zextImm 24 byte
            LH -> do
                (r1, rd, imm) <- decodeAndReadIType @v
                -- TODO: Alignment handling
                half <- load Half $ r1 `addImm` imm
                writeRegister rd $ sextImm 16 half
            LHU -> do
                (r1, rd, imm) <- decodeAndReadIType @v
                -- TODO: Alignment handling
                half <- load Half $ r1 `addImm` imm
                writeRegister rd $ zextImm 16 half
            LW -> do
                (r1, rd, imm) <- decodeAndReadIType @v

                -- TODO: Alignment handling
                word <- load Word $ r1 `addImm` imm
                writeRegister rd $ FromImm word
            SB -> do
                (r1, r2, imm) <- decodeAndReadSType @v

                -- TODO: Alignment handling
                store Byte (r1 `addImm` imm) $ FromImm r2
            SH -> do
                (r1, r2, imm) <- decodeAndReadSType @v

                -- TODO: Alignment handling
                store Half (r1 `addImm` imm) $ FromImm r2
            SW -> do
                (r1, r2, imm) <- decodeAndReadSType @v
                -- TODO: Alignment handling
                store Word (r1 `addImm` imm) $ FromImm r2
            BEQ -> do
                (r1, r2, imm) <- decodeAndReadBType

                let addr = pc `add` imm
                whenExprM (r1 `eq` r2) $ do
                    writePC addr
                    whenExprM (isMisaligned addr) $
                        exception pc "misaligned PC"
            BNE -> do
                (r1, r2, imm) <- decodeAndReadBType

                let addr = pc `add` imm
                unlessExprM (r1 `eq` r2) $ do
                    writePC addr
                    whenExprM (isMisaligned addr) $
                        exception pc "misaligned PC"
            BLT -> do
                (r1, r2, imm) <- decodeAndReadBType

                let addr = pc `add` imm
                whenExprM (r1 `slt` r2) $ do
                    writePC addr
                    whenExprM (isMisaligned addr) $
                        exception pc "misaligned PC"
            BLTU -> do
                (r1, r2, imm) <- decodeAndReadBType

                let addr = pc `add` imm
                whenExprM (r1 `ult` r2) $ do
                    writePC @v $ addr
                    whenExprM (isMisaligned addr) $
                        exception pc "misaligned PC"
            BGE -> do
                (r1, r2, imm) <- decodeAndReadBType

                let addr = pc `add` imm
                whenExprM (r1 `sge` r2) $ do
                    writePC addr
                    whenExprM (isMisaligned addr) $
                        exception pc "misaligned PC"
            BGEU -> do
                (r1, r2, imm) <- decodeAndReadBType

                let addr = pc `add` imm
                whenExprM (r1 `uge` r2) $ do
                    writePC addr
                    whenExprM (isMisaligned addr) $
                        exception pc "misaligned PC"
            FENCE -> pure () -- XXX: ignore for now
            ECALL -> ecall pc
            EBREAK -> ebreak pc