riscfetch_core/
parsing.rs

1//! ISA string parsing functions (pure functions for testing)
2
3/// Standard extension definitions
4pub const STANDARD_EXTENSIONS: [(&char, &str, &str); 11] = [
5    (&'i', "I", "Base Integer Instructions"),
6    (&'e', "E", "Embedded (16 registers)"),
7    (&'m', "M", "Integer Multiply/Divide"),
8    (&'a', "A", "Atomic Instructions"),
9    (&'f', "F", "Single-Precision Float"),
10    (&'d', "D", "Double-Precision Float"),
11    (&'q', "Q", "Quad-Precision Float"),
12    (&'c', "C", "Compressed (16-bit)"),
13    (&'b', "B", "Bit Manipulation"),
14    (&'v', "V", "Vector (SIMD)"),
15    (&'h', "H", "Hypervisor"),
16];
17
18/// Z-extension definitions
19pub const Z_EXTENSIONS: [(&str, &str, &str); 38] = [
20    ("zicsr", "Zicsr", "CSR Instructions"),
21    ("zifencei", "Zifencei", "Instruction-Fetch Fence"),
22    ("zicntr", "Zicntr", "Base Counters/Timers"),
23    ("zihpm", "Zihpm", "Hardware Perf Counters"),
24    ("zicbom", "Zicbom", "Cache-Block Management"),
25    ("zicboz", "Zicboz", "Cache-Block Zero"),
26    ("zicond", "Zicond", "Conditional Operations"),
27    ("zihintpause", "Zihintpause", "Pause Hint"),
28    ("zba", "Zba", "Address Generation"),
29    ("zbb", "Zbb", "Basic Bit Manipulation"),
30    ("zbc", "Zbc", "Carry-less Multiply"),
31    ("zbs", "Zbs", "Single-bit Operations"),
32    ("zbkb", "Zbkb", "Bit Manip for Crypto"),
33    ("zbkc", "Zbkc", "Carry-less for Crypto"),
34    ("zbkx", "Zbkx", "Crossbar for Crypto"),
35    ("zfh", "Zfh", "Half-Precision Float"),
36    ("zfhmin", "Zfhmin", "Minimal Half-Precision"),
37    ("zkt", "Zkt", "Constant-Time Execution"),
38    ("zca", "Zca", "Compressed Base"),
39    ("zcb", "Zcb", "Compressed Basic Ops"),
40    ("zcd", "Zcd", "Compressed Double FP"),
41    ("zcf", "Zcf", "Compressed Single FP"),
42    ("zve32f", "Zve32f", "Vector 32-bit Float"),
43    ("zve32x", "Zve32x", "Vector 32-bit Int"),
44    ("zve64d", "Zve64d", "Vector 64-bit Double"),
45    ("zve64f", "Zve64f", "Vector 64-bit Float"),
46    ("zve64x", "Zve64x", "Vector 64-bit Int"),
47    ("zvfh", "Zvfh", "Vector Half-Precision"),
48    ("zvfhmin", "Zvfhmin", "Min Vector Half-Prec"),
49    ("zvkt", "Zvkt", "Vector Constant-Time"),
50    ("zvl128b", "Zvl128b", "VLEN >= 128 bits"),
51    ("zvl256b", "Zvl256b", "VLEN >= 256 bits"),
52    ("zvl512b", "Zvl512b", "VLEN >= 512 bits"),
53    ("svinval", "Svinval", "Fine-Grained TLB"),
54    ("svnapot", "Svnapot", "NAPOT Translation"),
55    ("svpbmt", "Svpbmt", "Page-Based Mem Types"),
56    ("sscofpmf", "Sscofpmf", "Count Overflow/Filter"),
57    ("sstc", "Sstc", "Supervisor Timer"),
58];
59
60/// Strip rv32/rv64 prefix from ISA base part to get extension letters only
61pub fn strip_rv_prefix(base: &str) -> &str {
62    base.strip_prefix("rv64")
63        .or_else(|| base.strip_prefix("rv32"))
64        .unwrap_or(base)
65}
66
67/// Parse extensions from ISA string (pure function for testing)
68pub fn parse_extensions_compact(isa: &str) -> String {
69    let isa = isa.to_lowercase();
70    let mut exts = Vec::new();
71
72    // Get the base part before any underscore
73    let base = isa.split('_').next().unwrap_or(&isa);
74    let ext_part = strip_rv_prefix(base);
75
76    // G is shorthand for IMAFD (per RISC-V spec)
77    let has_g = ext_part.contains('g');
78
79    // Standard extensions in canonical order
80    // Note: E and I are mutually exclusive
81    let standard = [
82        ('i', "I", false), // (char, name, implied_by_g)
83        ('e', "E", false), // E = embedded (16 registers)
84        ('m', "M", true),
85        ('a', "A", true),
86        ('f', "F", true),
87        ('d', "D", true),
88        ('q', "Q", false),
89        ('c', "C", false),
90        ('b', "B", false),
91        ('v', "V", false),
92        ('h', "H", false),
93    ];
94
95    for (ch, name, implied_by_g) in standard {
96        if ext_part.contains(ch) || (has_g && implied_by_g) {
97            exts.push(name);
98        }
99    }
100
101    // If G is present but I wasn't explicitly added, add I (G implies IMAFD)
102    if has_g && !exts.contains(&"I") && !exts.contains(&"E") {
103        exts.insert(0, "I");
104    }
105
106    exts.join(" ")
107}
108
109/// Parse Z-extensions from ISA string (pure function for testing)
110pub fn parse_z_extensions(isa: &str) -> String {
111    let isa = isa.to_lowercase();
112    let mut z_exts = Vec::new();
113
114    // Check if G is present (G implies Zicsr_Zifencei per RISC-V spec)
115    let base = isa.split('_').next().unwrap_or(&isa);
116    let ext_part = strip_rv_prefix(base);
117    let has_g = ext_part.contains('g');
118
119    // Add implied Z-extensions from G
120    if has_g {
121        z_exts.push("zicsr".to_string());
122        z_exts.push("zifencei".to_string());
123    }
124
125    // Add explicit Z-extensions and S-extensions
126    for part in isa.split('_') {
127        if (part.starts_with('z') || part.starts_with('s')) && !z_exts.contains(&part.to_string()) {
128            z_exts.push(part.to_string());
129        }
130    }
131
132    z_exts.join(" ")
133}
134
135/// Parse extensions with explanations (pure function for testing)
136pub fn parse_extensions_explained(isa: &str) -> Vec<(String, String)> {
137    let isa = isa.to_lowercase();
138    let base = isa.split('_').next().unwrap_or(&isa);
139    let ext_part = strip_rv_prefix(base);
140    let mut exts = Vec::new();
141
142    for (ch, name, desc) in STANDARD_EXTENSIONS {
143        if ext_part.contains(*ch) {
144            exts.push((name.to_string(), desc.to_string()));
145        }
146    }
147
148    exts
149}
150
151/// Parse Z-extensions with explanations (pure function for testing)
152pub fn parse_z_extensions_explained(isa: &str) -> Vec<(String, String)> {
153    let isa = isa.to_lowercase();
154    let mut z_exts = Vec::new();
155
156    for (pattern, name, desc) in Z_EXTENSIONS {
157        if isa.contains(pattern) {
158            z_exts.push((name.to_string(), desc.to_string()));
159        }
160    }
161
162    z_exts
163}
164
165/// Parse vector details from ISA string (pure function for testing)
166/// Returns None if no vector extension, Some(details) otherwise
167pub fn parse_vector_from_isa(isa: &str) -> Option<String> {
168    let isa = isa.to_lowercase();
169    let base = isa.split('_').next().unwrap_or(&isa);
170    let ext_part = strip_rv_prefix(base);
171
172    // Check for V extension in the extension part, or zve in Z-extensions
173    if !ext_part.contains('v') && !isa.contains("zve") {
174        return None;
175    }
176
177    let mut details = vec!["Enabled".to_string()];
178
179    // Detect VLEN from zvl* extensions (use largest value)
180    // If no zvl* specified, VLEN is implementation-defined (do not display)
181    if isa.contains("zvl1024b") {
182        details.push("VLEN>=1024".to_string());
183    } else if isa.contains("zvl512b") {
184        details.push("VLEN>=512".to_string());
185    } else if isa.contains("zvl256b") {
186        details.push("VLEN>=256".to_string());
187    } else if isa.contains("zvl128b") {
188        details.push("VLEN>=128".to_string());
189    } else if isa.contains("zvl64b") {
190        details.push("VLEN>=64".to_string());
191    } else if isa.contains("zvl32b") {
192        details.push("VLEN>=32".to_string());
193    }
194    // No default VLEN - it's implementation-defined per RISC-V spec
195
196    Some(details.join(", "))
197}