amdgpu_sysfs/gpu_handle/overdrive/
gcn.rs1use super::{parse_range_line, push_level_line, ClocksLevel, ClocksTable, ClocksTableGen, Range};
3use crate::{
4 error::{Error, ErrorKind::ParseError},
5 Result,
6};
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9use std::{cmp, io::Write, str::FromStr};
10
11#[derive(Debug, Clone)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub struct Table {
15 pub sclk_levels: Vec<ClocksLevel>,
17 pub mclk_levels: Vec<ClocksLevel>,
19 pub od_range: OdRange,
21}
22
23impl ClocksTable for Table {
24 fn write_commands<W: Write>(
25 &self,
26 writer: &mut W,
27 _previous_table: &ClocksTableGen,
28 ) -> Result<()> {
29 for (i, level) in self.sclk_levels.iter().enumerate() {
30 let command = level_command(*level, i, 's');
31 writer.write_all(command.as_bytes())?;
32 }
33
34 for (i, level) in self.mclk_levels.iter().enumerate() {
35 let command = level_command(*level, i, 'm');
36 writer.write_all(command.as_bytes())?;
37 }
38
39 Ok(())
40 }
41
42 fn get_max_sclk_range(&self) -> Option<Range> {
43 Some(self.od_range.sclk)
44 }
45
46 fn get_min_sclk_range(&self) -> Option<Range> {
47 Some(self.od_range.sclk)
48 }
49
50 fn get_max_mclk_range(&self) -> Option<Range> {
51 self.od_range.mclk
52 }
53
54 fn get_min_mclk_range(&self) -> Option<Range> {
55 self.od_range.mclk
56 }
57
58 fn get_max_voltage_range(&self) -> Option<Range> {
59 self.od_range.vddc
60 }
61
62 fn get_min_voltage_range(&self) -> Option<Range> {
63 self.od_range.vddc
64 }
65
66 fn get_current_voltage_range(&self) -> Option<Range> {
67 let min = self.sclk_levels.first().map(|level| level.voltage);
68 let max = self.sclk_levels.last().map(|level| level.voltage);
69 Some(Range { min, max })
70 }
71
72 fn get_current_sclk_range(&self) -> Range {
73 let min = self.sclk_levels.first().map(|level| level.clockspeed);
74 let max = self.sclk_levels.last().map(|level| level.clockspeed);
75 Range { min, max }
76 }
77
78 fn get_current_mclk_range(&self) -> Range {
79 let min = self.mclk_levels.first().map(|level| level.clockspeed);
80 let max = self.mclk_levels.last().map(|level| level.clockspeed);
81 Range { min, max }
82 }
83
84 fn set_max_sclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
85 let len = self.sclk_levels.len();
86 if len == 0 {
87 return Ok(());
88 }
89
90 self.sclk_levels[len - 1].clockspeed = clockspeed;
91 for clock_level in &mut self.sclk_levels[0..len - 1] {
92 clock_level.clockspeed = cmp::min(clock_level.clockspeed, clockspeed);
93 }
94
95 Ok(())
96 }
97
98 fn set_min_sclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
99 let len = self.sclk_levels.len();
100 if len == 0 {
101 return Ok(());
102 }
103
104 self.sclk_levels[0].clockspeed = clockspeed;
105 for clock_level in &mut self.sclk_levels[1..len] {
106 clock_level.clockspeed = cmp::max(clock_level.clockspeed, clockspeed);
107 }
108
109 Ok(())
110 }
111
112 fn set_max_mclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
113 let len = self.mclk_levels.len();
114 if len == 0 {
115 return Ok(());
116 }
117
118 self.mclk_levels[len - 1].clockspeed = clockspeed;
119 for clock_level in &mut self.mclk_levels[0..len - 1] {
120 clock_level.clockspeed = cmp::min(clock_level.clockspeed, clockspeed);
121 }
122
123 Ok(())
124 }
125
126 fn set_min_mclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
127 let len = self.mclk_levels.len();
128 if len == 0 {
129 return Ok(());
130 }
131
132 self.mclk_levels[0].clockspeed = clockspeed;
133 for clock_level in &mut self.mclk_levels[1..len] {
134 clock_level.clockspeed = cmp::max(clock_level.clockspeed, clockspeed);
135 }
136
137 Ok(())
138 }
139
140 fn set_max_voltage_unchecked(&mut self, voltage: i32) -> Result<()> {
141 let len = self.sclk_levels.len();
142 if len == 0 {
143 return Ok(());
144 }
145
146 self.sclk_levels[len - 1].voltage = voltage;
147 for clock_level in &mut self.sclk_levels[0..len - 1] {
148 clock_level.voltage = cmp::min(clock_level.voltage, voltage);
149 }
150
151 Ok(())
152 }
153
154 fn set_min_voltage_unchecked(&mut self, voltage: i32) -> Result<()> {
155 let len = self.sclk_levels.len();
156 if len == 0 {
157 return Ok(());
158 }
159
160 self.sclk_levels[0].voltage = voltage;
161 for clock_level in &mut self.sclk_levels[1..len - 1] {
162 clock_level.voltage = cmp::max(clock_level.voltage, voltage);
163 }
164
165 Ok(())
166 }
167
168 fn get_max_sclk_voltage(&self) -> Option<i32> {
169 self.sclk_levels.last().map(|level| level.voltage)
170 }
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq)]
175#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
176pub struct OdRange {
177 pub sclk: Range,
179 pub mclk: Option<Range>,
181 pub vddc: Option<Range>,
183}
184
185impl FromStr for Table {
186 type Err = Error;
187
188 fn from_str(s: &str) -> Result<Self> {
189 let mut sclk_levels = Vec::with_capacity(7);
190 let mut mclk_levels = Vec::with_capacity(2);
191 let mut sclk_range = None;
192 let mut mclk_range = None;
193 let mut vddc_range = None;
194
195 let mut current_section = None;
196
197 let mut i = 1;
198 for line in s.lines().map(str::trim).filter(|line| !line.is_empty()) {
199 match line {
200 "OD_SCLK:" => current_section = Some(Section::Sclk),
201 "OD_MCLK:" => current_section = Some(Section::Mclk),
202 "OD_RANGE:" => current_section = Some(Section::Range),
203 line => match current_section {
204 Some(Section::Sclk) => {
205 push_level_line(line, &mut sclk_levels, i)?;
206 }
207 Some(Section::Mclk) => {
208 push_level_line(line, &mut mclk_levels, i)?;
209 }
210 Some(Section::Range) => {
211 let (range, name) = parse_range_line(line, i)?;
212 match name {
213 "SCLK" => sclk_range = Some(range),
214 "MCLK" => mclk_range = Some(range),
215 "VDDC" => vddc_range = Some(range),
216 other => {
217 return Err(ParseError {
218 msg: format!("Unexpected range item: {other}"),
219 line: i,
220 }
221 .into())
222 }
223 }
224 }
225 None => {
226 return Err(ParseError {
227 msg: "Could not find section".to_owned(),
228 line: i,
229 }
230 .into())
231 }
232 },
233 }
234 i += 1;
235 }
236
237 sclk_levels.shrink_to_fit();
238 mclk_levels.shrink_to_fit();
239
240 let od_range = OdRange {
241 sclk: sclk_range.ok_or_else(|| ParseError {
242 msg: "No sclk range found".to_owned(),
243 line: i,
244 })?,
245 mclk: mclk_range,
246 vddc: vddc_range,
247 };
248
249 Ok(Self {
250 sclk_levels,
251 mclk_levels,
252 od_range,
253 })
254 }
255}
256
257fn level_command(level: ClocksLevel, i: usize, symbol: char) -> String {
258 let ClocksLevel {
259 clockspeed,
260 voltage,
261 } = level;
262 format!("{symbol} {i} {clockspeed} {voltage}\n")
263}
264
265#[derive(PartialEq)]
266enum Section {
267 Sclk,
268 Mclk,
269 Range,
270}
271
272#[cfg(test)]
273mod tests {
274 use super::{ClocksLevel, Table};
275 use crate::{
276 gpu_handle::overdrive::{arr_commands, gcn::OdRange, ClocksTable, Range},
277 include_table,
278 };
279 use pretty_assertions::assert_eq;
280 use std::str::FromStr;
281
282 const TABLE_RX580: &str = include_table!("rx580");
283
284 #[test]
285 fn parse_full_table() {
286 let table = Table::from_str(TABLE_RX580).unwrap();
287
288 let sclk_levels = [
289 (300, 750),
290 (600, 769),
291 (900, 912),
292 (1145, 1125),
293 (1215, 1150),
294 (1257, 1150),
295 (1300, 1150),
296 (1366, 1150),
297 ]
298 .map(|(clockspeed, voltage)| ClocksLevel {
299 clockspeed,
300 voltage,
301 });
302 let mclk_levels =
303 [(300, 750), (1000, 825), (1750, 975)].map(|(clockspeed, voltage)| ClocksLevel {
304 clockspeed,
305 voltage,
306 });
307 let ranges = OdRange {
308 sclk: Range::full(300, 2000),
309 mclk: Some(Range::full(300, 2250)),
310 vddc: Some(Range::full(750, 1200)),
311 };
312
313 assert_eq!(table.sclk_levels, sclk_levels);
314 assert_eq!(table.mclk_levels, mclk_levels);
315 assert_eq!(table.od_range, ranges);
316 }
317
318 #[test]
319 fn table_into_commands() {
320 let mut table = Table::from_str(TABLE_RX580).unwrap();
321
322 table.set_max_sclk(1500).unwrap();
323 table.set_max_mclk(2250).unwrap();
324
325 table.set_min_sclk(350).unwrap();
326 table.set_min_mclk(360).unwrap();
327
328 table.set_min_voltage(800).unwrap();
329 table.set_max_voltage(1200).unwrap();
330
331 let mut buf = Vec::new();
332 table
333 .write_commands(&mut buf, &table.clone().into())
334 .unwrap();
335 let commands = String::from_utf8(buf).unwrap();
336
337 let expected_commands = arr_commands([
338 "s 0 350 800",
339 "s 1 600 800",
340 "s 2 900 912",
341 "s 3 1145 1125",
342 "s 4 1215 1150",
343 "s 5 1257 1150",
344 "s 6 1300 1150",
345 "s 7 1500 1200",
346 "m 0 360 750",
347 "m 1 1000 825",
348 "m 2 2250 975",
349 ]);
350
351 assert_eq!(expected_commands, commands);
352 }
353
354 #[test]
355 fn generic_actions() {
356 let mut table = Table::from_str(TABLE_RX580).unwrap();
357 let sclk = table.get_max_sclk().unwrap();
358 assert_eq!(sclk, 1366);
359 let mclk = table.get_max_mclk().unwrap();
360 assert_eq!(mclk, 1750);
361 let voltage = table.get_max_sclk_voltage().unwrap();
362 assert_eq!(voltage, 1150);
363
364 table.set_max_sclk(1400).unwrap();
365 let sclk = table.get_max_sclk().unwrap();
366 assert_eq!(sclk, 1400);
367 assert_eq!(table.sclk_levels[7].clockspeed, 1400);
368
369 table.set_max_mclk(1800).unwrap();
370 let mclk = table.get_max_mclk().unwrap();
371 assert_eq!(mclk, 1800);
372 assert_eq!(table.mclk_levels[2].clockspeed, 1800);
373
374 let sclk_range = table.get_max_sclk_range();
375 let mclk_range = table.get_max_mclk_range();
376 let voltage_range = table.get_max_voltage_range();
377 assert_eq!(sclk_range, Some(Range::full(300, 2000)));
378 assert_eq!(mclk_range, Some(Range::full(300, 2250)));
379 assert_eq!(voltage_range, Some(Range::full(750, 1200)));
380 }
381
382 #[test]
383 fn undervolt_normalize() {
384 let mut table = Table::from_str(TABLE_RX580).unwrap();
385 table.set_max_voltage(1100).unwrap();
386 assert!(table.sclk_levels.iter().all(|level| level.voltage <= 1100));
387 }
388
389 #[test]
390 fn underclock_normalize() {
391 let mut table = Table::from_str(TABLE_RX580).unwrap();
392 table.set_max_sclk(1200).unwrap();
393 assert!(table
394 .sclk_levels
395 .iter()
396 .all(|level| level.clockspeed <= 1200));
397 }
398
399 #[test]
400 fn underclock_memory_normalize() {
401 let mut table = Table::from_str(TABLE_RX580).unwrap();
402 table.set_max_mclk(900).unwrap();
403 assert!(table
404 .mclk_levels
405 .iter()
406 .all(|level| level.clockspeed <= 900));
407 }
408
409 #[test]
410 fn min_voltage_normalize() {
411 let mut table = Table::from_str(TABLE_RX580).unwrap();
412 table.set_min_voltage(800).unwrap();
413 assert!(table.sclk_levels.iter().all(|level| level.voltage >= 800));
414 }
415
416 #[test]
417 fn min_clockspeed_normalize() {
418 let mut table = Table::from_str(TABLE_RX580).unwrap();
419 table.set_min_sclk(750).unwrap();
420 assert!(table
421 .sclk_levels
422 .iter()
423 .all(|level| level.clockspeed >= 750));
424 }
425
426 #[test]
427 fn min_memory_clockspeed_normalize() {
428 let mut table = Table::from_str(TABLE_RX580).unwrap();
429 table.set_min_mclk(1100).unwrap();
430 assert!(table
431 .mclk_levels
432 .iter()
433 .all(|level| level.clockspeed >= 1100));
434 }
435}