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