Skip to main content

ab_riscv_interpreter/rv64/a/
zalrsc.rs

1//! RV64 Zalrsc extension
2
3#[cfg(test)]
4mod tests;
5
6use crate::rv32::a::{ReservationSet, amo_helpers};
7use crate::{
8    ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands, ExecutionError,
9    ExecutionResult, FetchInstructionResult, InstructionFetcher, OpaqueThreadedExecutionResult,
10    PackedAddress, RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands,
11    ThreadedExecutableInstruction, ThreadedExecutionResult, VirtualMemory,
12};
13use ab_riscv_macros::instruction_execution;
14use ab_riscv_primitives::prelude::*;
15
16#[instruction_execution]
17const impl<Reg> ExecutableInstructionOperands for Rv64ZalrscInstruction<Reg> where
18    Reg: Register<Type = u64>
19{
20}
21
22#[instruction_execution]
23const impl<Reg, Env> ExecutableInstructionCsr<Env> for Rv64ZalrscInstruction<Reg> where
24    Reg: Register<Type = u64>
25{
26}
27
28#[instruction_execution]
29const impl<Reg, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
30    for Rv64ZalrscInstruction<Reg>
31where
32    Reg: [const] Register<Type = u64>,
33    Regs: [const] RegisterFile<Reg>,
34    Memory: [const] VirtualMemory,
35    Env: [const] ReservationSet<Reg>,
36{
37    #[inline(always)]
38    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
39    fn execute(
40        self,
41        Rs1Rs2OperandValues {
42            rs1_value,
43            rs2_value,
44        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
45        _regs: &mut Regs,
46        env: &mut Env,
47        memory: &mut Memory,
48        _program_counter: &mut PC,
49    ) -> ExecutionResult<Self::Reg> {
50        match self {
51            Self::Lr {
52                rd,
53                rs1: _,
54                aq: _,
55                rl: _,
56            } => {
57                if !rs1_value.is_multiple_of(4) {
58                    ::core::hint::cold_path();
59                    return ExecutionResult::Err(ExecutionError::MisalignedRead {
60                        address: PackedAddress::new(rs1_value),
61                    });
62                }
63                let value = memory.read::<i32>(rs1_value)?;
64                env.set_reservation(rs1_value);
65                ExecutionResult::Continue {
66                    rd,
67                    value: i64::from(value).cast_unsigned(),
68                }
69            }
70            Self::Sc {
71                rd,
72                rs1: _,
73                rs2: _,
74                aq: _,
75                rl: _,
76            } => {
77                if !rs1_value.is_multiple_of(4) {
78                    ::core::hint::cold_path();
79                    return ExecutionResult::Err(ExecutionError::MisalignedWrite {
80                        address: PackedAddress::new(rs1_value),
81                    });
82                }
83                let success = env.reservation() == Some(rs1_value);
84                env.clear_reservation();
85
86                if success {
87                    memory.write(rs1_value, rs2_value as u32)?;
88                    ExecutionResult::Continue { rd, value: 0 }
89                } else {
90                    // A failing `sc` (lost/no reservation) still checks its target address for a
91                    // Store/AMO access fault, exactly as a successful one would - only the actual
92                    // write is skipped. `amo_read` (not a plain `memory.read`) both probes the
93                    // same address range a write would use, side-effect-free, and classifies any
94                    // fault as Store/AMO rather than Load, matching a real `sc`'s own fault cause.
95                    amo_helpers::amo_read::<u32, _, _>(memory, rs1_value)?;
96                    ExecutionResult::Continue { rd, value: 1 }
97                }
98            }
99            Self::LrD {
100                rd,
101                rs1: _,
102                aq: _,
103                rl: _,
104            } => {
105                if !rs1_value.is_multiple_of(8) {
106                    ::core::hint::cold_path();
107                    return ExecutionResult::Err(ExecutionError::MisalignedRead {
108                        address: PackedAddress::new(rs1_value),
109                    });
110                }
111                let value = memory.read::<u64>(rs1_value)?;
112                env.set_reservation(rs1_value);
113                ExecutionResult::Continue { rd, value }
114            }
115            Self::ScD {
116                rd,
117                rs1: _,
118                rs2: _,
119                aq: _,
120                rl: _,
121            } => {
122                if !rs1_value.is_multiple_of(8) {
123                    ::core::hint::cold_path();
124                    return ExecutionResult::Err(ExecutionError::MisalignedWrite {
125                        address: PackedAddress::new(rs1_value),
126                    });
127                }
128                let success = env.reservation() == Some(rs1_value);
129                env.clear_reservation();
130
131                if success {
132                    memory.write(rs1_value, rs2_value)?;
133                    ExecutionResult::Continue { rd, value: 0 }
134                } else {
135                    // See the comment in the `Sc` (32-bit) arm above.
136                    amo_helpers::amo_read::<u64, _, _>(memory, rs1_value)?;
137                    ExecutionResult::Continue { rd, value: 1 }
138                }
139            }
140        }
141    }
142}