Skip to main content

ch32rv_debug/
arch.rs

1//! en: A minimal RISC-V RV32 architecture for gdbstub: 32 integer GPRs plus the PC, all u32,
2//! in the order GDB's `riscv:rv32` core.xml expects (x0..x31, then pc). FPU/CSR registers are
3//! not exposed yet (docs/architecture.ja.md §1.3 notes V4F FPU needs a custom Arch later).
4//! ja: gdbstub 用の最小 RISC-V RV32 定義。GPR 32 本 + PC(すべて u32)を GDB の core.xml 順
5//! (x0..x31, pc)で並べる。FPU/CSR は未対応(V4F FPU は将来の課題)。
6
7use core::num::NonZeroUsize;
8
9use gdbstub::arch::{Arch, RegId, Registers};
10
11/// RV32 core register file: x0..x31 and pc.
12#[derive(Debug, Default, Clone, PartialEq, Eq)]
13pub struct Rv32CoreRegs {
14    pub x: [u32; 32],
15    pub pc: u32,
16}
17
18impl Registers for Rv32CoreRegs {
19    type ProgramCounter = u32;
20
21    fn pc(&self) -> u32 {
22        self.pc
23    }
24
25    fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
26        for r in self.x.iter().chain(core::iter::once(&self.pc)) {
27            for b in r.to_le_bytes() {
28                write_byte(Some(b));
29            }
30        }
31    }
32
33    fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
34        // 33 registers x 4 bytes = 132 bytes expected.
35        if bytes.len() < 33 * 4 {
36            return Err(());
37        }
38        for (i, chunk) in bytes.chunks(4).enumerate().take(33) {
39            if chunk.len() < 4 {
40                break;
41            }
42            let v = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
43            if i < 32 {
44                self.x[i] = v;
45            } else {
46                self.pc = v;
47            }
48        }
49        Ok(())
50    }
51}
52
53/// Register identifier: a GPR index 0..31, or the PC (id 32).
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum Rv32RegId {
56    Gpr(u8),
57    Pc,
58}
59
60impl RegId for Rv32RegId {
61    fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
62        let size = NonZeroUsize::new(4);
63        match id {
64            0..=31 => Some((Rv32RegId::Gpr(id as u8), size)),
65            32 => Some((Rv32RegId::Pc, size)),
66            _ => None,
67        }
68    }
69
70    fn to_raw_id(&self) -> Option<usize> {
71        Some(match self {
72            Rv32RegId::Gpr(n) => *n as usize,
73            Rv32RegId::Pc => 32,
74        })
75    }
76}
77
78/// The RV32 architecture marker for gdbstub (zero-variant, used at the type level only).
79pub enum Rv32 {}
80
81impl Arch for Rv32 {
82    type Usize = u32;
83    type Registers = Rv32CoreRegs;
84    type BreakpointKind = usize;
85    type RegId = Rv32RegId;
86
87    fn target_description_xml() -> Option<&'static str> {
88        // Lets GDB auto-detect the architecture without `set architecture`.
89        Some(r#"<target version="1.0"><architecture>riscv:rv32</architecture></target>"#)
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    #![allow(clippy::unwrap_used)]
96    use super::*;
97
98    #[test]
99    fn core_regs_serialize_roundtrip() {
100        let mut regs = Rv32CoreRegs::default();
101        for (i, x) in regs.x.iter_mut().enumerate() {
102            *x = 0x1000_0000 + i as u32;
103        }
104        regs.pc = 0x0800_0000;
105
106        // Serialize: 33 registers x 4 bytes, little-endian, x0..x31 then pc.
107        let mut bytes = Vec::new();
108        regs.gdb_serialize(|b| {
109            if let Some(b) = b {
110                bytes.push(b);
111            }
112        });
113        assert_eq!(bytes.len(), 33 * 4);
114        assert_eq!(&bytes[0..4], &0x1000_0000u32.to_le_bytes()); // x0
115        assert_eq!(&bytes[128..132], &0x0800_0000u32.to_le_bytes()); // pc last
116
117        // Round-trip back.
118        let mut back = Rv32CoreRegs::default();
119        back.gdb_deserialize(&bytes).unwrap();
120        assert_eq!(back, regs);
121    }
122
123    #[test]
124    fn deserialize_rejects_short_input() {
125        let mut regs = Rv32CoreRegs::default();
126        assert!(regs.gdb_deserialize(&[0u8; 131]).is_err());
127    }
128
129    #[test]
130    fn reg_id_mapping() {
131        assert_eq!(
132            Rv32RegId::from_raw_id(0).map(|(r, _)| r),
133            Some(Rv32RegId::Gpr(0))
134        );
135        assert_eq!(
136            Rv32RegId::from_raw_id(31).map(|(r, _)| r),
137            Some(Rv32RegId::Gpr(31))
138        );
139        assert_eq!(
140            Rv32RegId::from_raw_id(32).map(|(r, _)| r),
141            Some(Rv32RegId::Pc)
142        );
143        assert!(Rv32RegId::from_raw_id(33).is_none());
144    }
145}