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