Skip to main content

ab_riscv_interpreter/rv64/
zabha.rs

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