Skip to main content

ab_riscv_interpreter/rv32/
zabha.rs

1//! RV32 Zabha extension
2
3#[cfg(test)]
4mod tests;
5
6use crate::rv32::a::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 Rv32ZabhaInstruction<Reg> where
18    Reg: Register<Type = u32>
19{
20}
21
22#[instruction_execution]
23const impl<Reg, Env> ExecutableInstructionCsr<Env> for Rv32ZabhaInstruction<Reg> where
24    Reg: Register<Type = u32>
25{
26}
27
28#[instruction_execution]
29const impl<Reg, Regs, Env, Memory, PC> ExecutableInstruction<Regs, Env, Memory, PC>
30    for Rv32ZabhaInstruction<Reg>
31where
32    Reg: [const] Register<Type = u32>,
33    Regs: [const] RegisterFile<Reg>,
34    Memory: [const] VirtualMemory,
35{
36    #[inline(always)]
37    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
38    fn execute(
39        self,
40        Rs1Rs2OperandValues {
41            rs1_value,
42            rs2_value,
43        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
44        regs: &mut Regs,
45        _env: &mut Env,
46        memory: &mut Memory,
47        _program_counter: &mut PC,
48    ) -> ExecutionResult<Self::Reg> {
49        match self {
50            Self::AmoswapB {
51                rd,
52                rs1: _,
53                rs2: _,
54                aq: _,
55                rl: _,
56            } => {
57                let old = amo_helpers::amo_read::<i8, _, _>(memory, u64::from(rs1_value))?;
58                memory.write(u64::from(rs1_value), rs2_value as u8)?;
59                ExecutionResult::Continue {
60                    rd,
61                    value: i32::from(old).cast_unsigned(),
62                }
63            }
64            Self::AmoswapH {
65                rd,
66                rs1: _,
67                rs2: _,
68                aq: _,
69                rl: _,
70            } => {
71                // The 2-byte access must not cross a misaligned atomicity granule (4096 bytes)
72                // boundary
73                if rs1_value / 4096 != (rs1_value + 1) / 4096 {
74                    ::core::hint::cold_path();
75                    return ExecutionResult::Err(ExecutionError::MisalignedAtomic {
76                        address: PackedAddress::new(u64::from(rs1_value)),
77                    });
78                }
79                let old = amo_helpers::amo_read::<i16, _, _>(memory, u64::from(rs1_value))?;
80                memory.write(u64::from(rs1_value), rs2_value as u16)?;
81                ExecutionResult::Continue {
82                    rd,
83                    value: i32::from(old).cast_unsigned(),
84                }
85            }
86            Self::AmoaddB {
87                rd,
88                rs1: _,
89                rs2: _,
90                aq: _,
91                rl: _,
92            } => {
93                let old = amo_helpers::amo_read::<i8, _, _>(memory, u64::from(rs1_value))?;
94                let new = old.cast_unsigned().wrapping_add(rs2_value as u8);
95                memory.write(u64::from(rs1_value), new)?;
96                ExecutionResult::Continue {
97                    rd,
98                    value: i32::from(old).cast_unsigned(),
99                }
100            }
101            Self::AmoaddH {
102                rd,
103                rs1: _,
104                rs2: _,
105                aq: _,
106                rl: _,
107            } => {
108                // The 2-byte access must not cross a misaligned atomicity granule (4096 bytes)
109                // boundary
110                if rs1_value / 4096 != (rs1_value + 1) / 4096 {
111                    ::core::hint::cold_path();
112                    return ExecutionResult::Err(ExecutionError::MisalignedAtomic {
113                        address: PackedAddress::new(u64::from(rs1_value)),
114                    });
115                }
116                let old = amo_helpers::amo_read::<i16, _, _>(memory, u64::from(rs1_value))?;
117                let new = old.cast_unsigned().wrapping_add(rs2_value as u16);
118                memory.write(u64::from(rs1_value), new)?;
119                ExecutionResult::Continue {
120                    rd,
121                    value: i32::from(old).cast_unsigned(),
122                }
123            }
124            Self::AmoxorB {
125                rd,
126                rs1: _,
127                rs2: _,
128                aq: _,
129                rl: _,
130            } => {
131                let old = amo_helpers::amo_read::<i8, _, _>(memory, u64::from(rs1_value))?;
132                let new = old.cast_unsigned() ^ (rs2_value as u8);
133                memory.write(u64::from(rs1_value), new)?;
134                ExecutionResult::Continue {
135                    rd,
136                    value: i32::from(old).cast_unsigned(),
137                }
138            }
139            Self::AmoxorH {
140                rd,
141                rs1: _,
142                rs2: _,
143                aq: _,
144                rl: _,
145            } => {
146                // The 2-byte access must not cross a misaligned atomicity granule (4096 bytes)
147                // boundary
148                if rs1_value / 4096 != (rs1_value + 1) / 4096 {
149                    ::core::hint::cold_path();
150                    return ExecutionResult::Err(ExecutionError::MisalignedAtomic {
151                        address: PackedAddress::new(u64::from(rs1_value)),
152                    });
153                }
154                let old = amo_helpers::amo_read::<i16, _, _>(memory, u64::from(rs1_value))?;
155                let new = old.cast_unsigned() ^ (rs2_value as u16);
156                memory.write(u64::from(rs1_value), new)?;
157                ExecutionResult::Continue {
158                    rd,
159                    value: i32::from(old).cast_unsigned(),
160                }
161            }
162            Self::AmoandB {
163                rd,
164                rs1: _,
165                rs2: _,
166                aq: _,
167                rl: _,
168            } => {
169                let old = amo_helpers::amo_read::<i8, _, _>(memory, u64::from(rs1_value))?;
170                let new = old.cast_unsigned() & (rs2_value as u8);
171                memory.write(u64::from(rs1_value), new)?;
172                ExecutionResult::Continue {
173                    rd,
174                    value: i32::from(old).cast_unsigned(),
175                }
176            }
177            Self::AmoandH {
178                rd,
179                rs1: _,
180                rs2: _,
181                aq: _,
182                rl: _,
183            } => {
184                // The 2-byte access must not cross a misaligned atomicity granule (4096 bytes)
185                // boundary
186                if rs1_value / 4096 != (rs1_value + 1) / 4096 {
187                    ::core::hint::cold_path();
188                    return ExecutionResult::Err(ExecutionError::MisalignedAtomic {
189                        address: PackedAddress::new(u64::from(rs1_value)),
190                    });
191                }
192                let old = amo_helpers::amo_read::<i16, _, _>(memory, u64::from(rs1_value))?;
193                let new = old.cast_unsigned() & (rs2_value as u16);
194                memory.write(u64::from(rs1_value), new)?;
195                ExecutionResult::Continue {
196                    rd,
197                    value: i32::from(old).cast_unsigned(),
198                }
199            }
200            Self::AmoorB {
201                rd,
202                rs1: _,
203                rs2: _,
204                aq: _,
205                rl: _,
206            } => {
207                let old = amo_helpers::amo_read::<i8, _, _>(memory, u64::from(rs1_value))?;
208                let new = old.cast_unsigned() | (rs2_value as u8);
209                memory.write(u64::from(rs1_value), new)?;
210                ExecutionResult::Continue {
211                    rd,
212                    value: i32::from(old).cast_unsigned(),
213                }
214            }
215            Self::AmoorH {
216                rd,
217                rs1: _,
218                rs2: _,
219                aq: _,
220                rl: _,
221            } => {
222                // The 2-byte access must not cross a misaligned atomicity granule (4096 bytes)
223                // boundary
224                if rs1_value / 4096 != (rs1_value + 1) / 4096 {
225                    ::core::hint::cold_path();
226                    return ExecutionResult::Err(ExecutionError::MisalignedAtomic {
227                        address: PackedAddress::new(u64::from(rs1_value)),
228                    });
229                }
230                let old = amo_helpers::amo_read::<i16, _, _>(memory, u64::from(rs1_value))?;
231                let new = old.cast_unsigned() | (rs2_value as u16);
232                memory.write(u64::from(rs1_value), new)?;
233                ExecutionResult::Continue {
234                    rd,
235                    value: i32::from(old).cast_unsigned(),
236                }
237            }
238            Self::AmominB {
239                rd,
240                rs1: _,
241                rs2: _,
242                aq: _,
243                rl: _,
244            } => {
245                let old = amo_helpers::amo_read::<i8, _, _>(memory, u64::from(rs1_value))?;
246                let new = if old < (rs2_value as u8).cast_signed() {
247                    old.cast_unsigned()
248                } else {
249                    rs2_value as u8
250                };
251                memory.write(u64::from(rs1_value), new)?;
252                ExecutionResult::Continue {
253                    rd,
254                    value: i32::from(old).cast_unsigned(),
255                }
256            }
257            Self::AmominH {
258                rd,
259                rs1: _,
260                rs2: _,
261                aq: _,
262                rl: _,
263            } => {
264                // The 2-byte access must not cross a misaligned atomicity granule (4096 bytes)
265                // boundary
266                if rs1_value / 4096 != (rs1_value + 1) / 4096 {
267                    ::core::hint::cold_path();
268                    return ExecutionResult::Err(ExecutionError::MisalignedAtomic {
269                        address: PackedAddress::new(u64::from(rs1_value)),
270                    });
271                }
272                let old = amo_helpers::amo_read::<i16, _, _>(memory, u64::from(rs1_value))?;
273                let new = if old < (rs2_value as u16).cast_signed() {
274                    old.cast_unsigned()
275                } else {
276                    rs2_value as u16
277                };
278                memory.write(u64::from(rs1_value), new)?;
279                ExecutionResult::Continue {
280                    rd,
281                    value: i32::from(old).cast_unsigned(),
282                }
283            }
284            Self::AmomaxB {
285                rd,
286                rs1: _,
287                rs2: _,
288                aq: _,
289                rl: _,
290            } => {
291                let old = amo_helpers::amo_read::<i8, _, _>(memory, u64::from(rs1_value))?;
292                let new = if old > (rs2_value as u8).cast_signed() {
293                    old.cast_unsigned()
294                } else {
295                    rs2_value as u8
296                };
297                memory.write(u64::from(rs1_value), new)?;
298                ExecutionResult::Continue {
299                    rd,
300                    value: i32::from(old).cast_unsigned(),
301                }
302            }
303            Self::AmomaxH {
304                rd,
305                rs1: _,
306                rs2: _,
307                aq: _,
308                rl: _,
309            } => {
310                // The 2-byte access must not cross a misaligned atomicity granule (4096 bytes)
311                // boundary
312                if rs1_value / 4096 != (rs1_value + 1) / 4096 {
313                    ::core::hint::cold_path();
314                    return ExecutionResult::Err(ExecutionError::MisalignedAtomic {
315                        address: PackedAddress::new(u64::from(rs1_value)),
316                    });
317                }
318                let old = amo_helpers::amo_read::<i16, _, _>(memory, u64::from(rs1_value))?;
319                let new = if old > (rs2_value as u16).cast_signed() {
320                    old.cast_unsigned()
321                } else {
322                    rs2_value as u16
323                };
324                memory.write(u64::from(rs1_value), new)?;
325                ExecutionResult::Continue {
326                    rd,
327                    value: i32::from(old).cast_unsigned(),
328                }
329            }
330            Self::AmominuB {
331                rd,
332                rs1: _,
333                rs2: _,
334                aq: _,
335                rl: _,
336            } => {
337                let old = amo_helpers::amo_read::<i8, _, _>(memory, u64::from(rs1_value))?;
338                let new = if old.cast_unsigned() < (rs2_value as u8) {
339                    old.cast_unsigned()
340                } else {
341                    rs2_value as u8
342                };
343                memory.write(u64::from(rs1_value), new)?;
344                ExecutionResult::Continue {
345                    rd,
346                    value: i32::from(old).cast_unsigned(),
347                }
348            }
349            Self::AmominuH {
350                rd,
351                rs1: _,
352                rs2: _,
353                aq: _,
354                rl: _,
355            } => {
356                // The 2-byte access must not cross a misaligned atomicity granule (4096 bytes)
357                // boundary
358                if rs1_value / 4096 != (rs1_value + 1) / 4096 {
359                    ::core::hint::cold_path();
360                    return ExecutionResult::Err(ExecutionError::MisalignedAtomic {
361                        address: PackedAddress::new(u64::from(rs1_value)),
362                    });
363                }
364                let old = amo_helpers::amo_read::<i16, _, _>(memory, u64::from(rs1_value))?;
365                let new = if old.cast_unsigned() < (rs2_value as u16) {
366                    old.cast_unsigned()
367                } else {
368                    rs2_value as u16
369                };
370                memory.write(u64::from(rs1_value), new)?;
371                ExecutionResult::Continue {
372                    rd,
373                    value: i32::from(old).cast_unsigned(),
374                }
375            }
376            Self::AmomaxuB {
377                rd,
378                rs1: _,
379                rs2: _,
380                aq: _,
381                rl: _,
382            } => {
383                let old = amo_helpers::amo_read::<i8, _, _>(memory, u64::from(rs1_value))?;
384                let new = if old.cast_unsigned() > (rs2_value as u8) {
385                    old.cast_unsigned()
386                } else {
387                    rs2_value as u8
388                };
389                memory.write(u64::from(rs1_value), new)?;
390                ExecutionResult::Continue {
391                    rd,
392                    value: i32::from(old).cast_unsigned(),
393                }
394            }
395            Self::AmomaxuH {
396                rd,
397                rs1: _,
398                rs2: _,
399                aq: _,
400                rl: _,
401            } => {
402                // The 2-byte access must not cross a misaligned atomicity granule (4096 bytes)
403                // boundary
404                if rs1_value / 4096 != (rs1_value + 1) / 4096 {
405                    ::core::hint::cold_path();
406                    return ExecutionResult::Err(ExecutionError::MisalignedAtomic {
407                        address: PackedAddress::new(u64::from(rs1_value)),
408                    });
409                }
410                let old = amo_helpers::amo_read::<i16, _, _>(memory, u64::from(rs1_value))?;
411                let new = if old.cast_unsigned() > (rs2_value as u16) {
412                    old.cast_unsigned()
413                } else {
414                    rs2_value as u16
415                };
416                memory.write(u64::from(rs1_value), new)?;
417                ExecutionResult::Continue {
418                    rd,
419                    value: i32::from(old).cast_unsigned(),
420                }
421            }
422            Self::AmocasB {
423                rd,
424                rs1: _,
425                rs2: _,
426                aq: _,
427                rl: _,
428            } => {
429                let compare = regs.read(rd) as u8;
430                let old = amo_helpers::amo_read::<i8, _, _>(memory, u64::from(rs1_value))?;
431                if old.cast_unsigned() == compare {
432                    memory.write(u64::from(rs1_value), rs2_value as u8)?;
433                }
434                ExecutionResult::Continue {
435                    rd,
436                    value: i32::from(old).cast_unsigned(),
437                }
438            }
439            Self::AmocasH {
440                rd,
441                rs1: _,
442                rs2: _,
443                aq: _,
444                rl: _,
445            } => {
446                let compare = regs.read(rd) as u16;
447                // The 2-byte access must not cross a misaligned atomicity granule (4096 bytes)
448                // boundary
449                if rs1_value / 4096 != (rs1_value + 1) / 4096 {
450                    ::core::hint::cold_path();
451                    return ExecutionResult::Err(ExecutionError::MisalignedAtomic {
452                        address: PackedAddress::new(u64::from(rs1_value)),
453                    });
454                }
455                let old = amo_helpers::amo_read::<i16, _, _>(memory, u64::from(rs1_value))?;
456                if old.cast_unsigned() == compare {
457                    memory.write(u64::from(rs1_value), rs2_value as u16)?;
458                }
459                ExecutionResult::Continue {
460                    rd,
461                    value: i32::from(old).cast_unsigned(),
462                }
463            }
464        }
465    }
466}