1use super::{
3 parse_line_item, parse_range_line, push_level_line, ClocksLevel, ClocksTable, ClocksTableGen,
4 Range,
5};
6use crate::{
7 error::{Error, ErrorContext, ErrorKind::ParseError},
8 gpu_handle::trim_sysfs_line,
9 Result,
10};
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13use std::{cmp, io::Write, str::FromStr};
14
15#[derive(Debug, Clone)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18pub struct Table {
19 pub current_sclk_range: Range,
21 pub sclk_offset: Option<i32>,
23 pub current_mclk_range: Range,
25 pub vddc_curve: Vec<ClocksLevel>,
27 pub voltage_offset: Option<i32>,
32 pub od_range: OdRange,
34}
35
36impl ClocksTable for Table {
37 fn write_commands<W: Write>(
38 &self,
39 writer: &mut W,
40 previous_table: &ClocksTableGen,
41 ) -> Result<()> {
42 let ClocksTableGen::Rdna(previous_table) = previous_table else {
43 return Err(Error::not_allowed(
44 "Mismatched clocks table format".to_owned(),
45 ));
46 };
47
48 let mut clocks_commands = Vec::with_capacity(4);
49
50 if let (Some(current_sclk_min), Some(old_sclk_max)) = (
53 self.current_sclk_range.min,
54 previous_table.current_sclk_range.max,
55 ) {
56 if current_sclk_min > old_sclk_max {
57 clocks_commands.push((self.current_sclk_range.max, 's', 1));
58 }
59 }
60
61 clocks_commands.extend([
62 (self.current_sclk_range.min, 's', 0),
63 (self.current_sclk_range.max, 's', 1),
64 ]);
65
66 if let (Some(current_mclk_min), Some(old_mclk_max)) = (
67 self.current_mclk_range.min,
68 previous_table.current_mclk_range.max,
69 ) {
70 if current_mclk_min > old_mclk_max {
71 clocks_commands.push((self.current_mclk_range.max, 'm', 1));
72 }
73 }
74
75 clocks_commands.extend([
76 (self.current_mclk_range.min, 'm', 0),
77 (self.current_mclk_range.max, 'm', 1),
78 ]);
79
80 if let Some(sclk_offset) = self.sclk_offset {
81 let line = format!("s {sclk_offset}\n");
82 writer
83 .write_all(line.as_bytes())
84 .context("Could not write sclk offset")?;
85 }
86
87 for (maybe_clockspeed, symbol, index) in clocks_commands {
88 if let Some(clockspeed) = maybe_clockspeed {
89 let line = clockspeed_line(symbol, index, clockspeed);
90 writer
91 .write_all(line.as_bytes())
92 .with_context(|| format!("Error when writing clockspeed line `{line}`"))?;
93 }
94 }
95
96 for (i, level) in self.vddc_curve.iter().enumerate() {
97 let line = vddc_curve_line(i, level.clockspeed, level.voltage);
98 writer
99 .write_all(line.as_bytes())
100 .with_context(|| format!("Error when writing VDDC line `{line}`"))?;
101 }
102
103 if let Some(offset) = self.voltage_offset {
104 let line = voltage_offset_line(offset);
105 writer
106 .write_all(line.as_bytes())
107 .with_context(|| format!("Error when writing voltage offset `{line}`"))?;
108 }
109
110 Ok(())
111 }
112
113 fn get_max_sclk_range(&self) -> Option<Range> {
114 self.od_range
115 .curve_sclk_points
116 .last()
117 .copied()
118 .or(self.od_range.sclk)
119 }
120
121 fn get_min_sclk_range(&self) -> Option<Range> {
122 self.od_range
123 .curve_sclk_points
124 .first()
125 .copied()
126 .or(self.od_range.sclk)
127 }
128
129 fn get_max_mclk_range(&self) -> Option<Range> {
130 self.od_range.mclk
131 }
132
133 fn get_min_mclk_range(&self) -> Option<Range> {
134 self.od_range.mclk
135 }
136
137 fn get_max_voltage_range(&self) -> Option<Range> {
138 self.od_range.curve_voltage_points.last().copied()
139 }
140
141 fn get_min_voltage_range(&self) -> Option<Range> {
142 self.od_range.curve_voltage_points.first().copied()
143 }
144
145 fn get_current_voltage_range(&self) -> Option<Range> {
146 let min = self.vddc_curve.first().map(|level| level.voltage)?;
147 let max = self.vddc_curve.last().map(|level| level.voltage)?;
148 Some(Range::full(min, max))
149 }
150
151 fn get_current_sclk_range(&self) -> Range {
152 self.current_sclk_range
153 }
154
155 fn get_current_mclk_range(&self) -> Range {
156 self.current_mclk_range
157 }
158
159 fn set_max_sclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
160 self.current_sclk_range.max = Some(clockspeed);
161 if let Some(point) = self.vddc_curve.last_mut() {
162 point.clockspeed = clockspeed;
163 }
164 Ok(())
165 }
166
167 fn set_min_sclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
168 self.current_sclk_range.min = Some(clockspeed);
169 if let Some(point) = self.vddc_curve.first_mut() {
170 point.clockspeed = clockspeed;
171 }
172 Ok(())
173 }
174
175 fn set_max_mclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
176 self.current_mclk_range.max = Some(clockspeed);
177 Ok(())
178 }
179
180 fn set_min_mclk_unchecked(&mut self, clockspeed: i32) -> Result<()> {
181 self.current_mclk_range.min = Some(clockspeed);
182 Ok(())
183 }
184
185 fn set_max_voltage_unchecked(&mut self, voltage: i32) -> Result<()> {
186 self.vddc_curve
187 .last_mut()
188 .ok_or_else(|| {
189 Error::not_allowed("The GPU did not report any voltage curve points".to_owned())
190 })?
191 .voltage = voltage;
192 Ok(())
193 }
194
195 fn set_min_voltage_unchecked(&mut self, voltage: i32) -> Result<()> {
196 self.vddc_curve
197 .first_mut()
198 .ok_or_else(|| {
199 Error::not_allowed("The GPU did not report any voltage curve points".to_owned())
200 })?
201 .voltage = voltage;
202 Ok(())
203 }
204
205 fn get_max_sclk_voltage(&self) -> Option<i32> {
206 self.vddc_curve.last().map(|level| level.voltage)
207 }
208}
209
210impl Table {
211 pub fn set_voltage_offset(&mut self, offset: i32) -> Result<()> {
215 if let Some(offset_range) = self.od_range.voltage_offset {
216 if let Some((min, max)) = offset_range.into_full() {
217 if !(min..=max).contains(&offset) {
218 return Err(Error::not_allowed(format!("Provided voltage offset {offset} is out of range, should be between {min} and {max}")));
219 }
220 }
221 }
222
223 self.voltage_offset = Some(offset);
224 Ok(())
225 }
226}
227
228impl FromStr for Table {
229 type Err = Error;
230
231 fn from_str(s: &str) -> Result<Self> {
232 let mut current_section = None;
233
234 let mut current_sclk_range = None;
235 let mut current_mclk_range = None;
236 let mut allowed_sclk_range = None;
237 let mut allowed_sclk_offset_range = None;
238 let mut allowed_mclk_range = None;
239
240 let mut vddc_curve = Vec::with_capacity(3);
241 let mut curve_sclk_points = Vec::with_capacity(3);
242 let mut curve_voltage_points = Vec::with_capacity(3);
243
244 let mut sclk_offset = None;
245 let mut voltage_offset = None;
246 let mut voltage_offset_range = None;
247
248 let mut lines = s
249 .lines()
250 .map(trim_sysfs_line)
251 .filter(|line| !line.is_empty());
252
253 let mut i = 1;
254 while let Some(line) = lines.next() {
255 match line {
256 "OD_SCLK:" => current_section = Some(Section::Sclk),
257 "OD_SCLK_OFFSET:" => current_section = Some(Section::SclkOffset),
258 "OD_MCLK:" => current_section = Some(Section::Mclk),
259 "OD_RANGE:" => current_section = Some(Section::Range),
260 "OD_VDDC_CURVE:" => current_section = Some(Section::VddcCurve),
261 "OD_VDDGFX_OFFSET:" => current_section = Some(Section::VddGfxOffset),
262 line => match current_section {
263 Some(Section::Range) if line.starts_with("VDDC_CURVE_SCLK") => {
265 let (range, _) = parse_range_line(line, i)?;
266 curve_sclk_points.push(range);
267 }
268 Some(Section::Range)
269 if line.starts_with("VDDC_CURVE_VOLT")
270 || (line.starts_with("VDDC_CURVE:") && line.contains("mv")) =>
271 {
272 let (range, _) = parse_range_line(line, i)?;
273 curve_voltage_points.push(range);
274 }
275 Some(Section::Range) if line.starts_with("CCLK_RANGE") => {
276 lines.next();
277 lines.next();
278 }
279 Some(Section::Range) => {
280 let (range, name) = parse_range_line(line, i)?;
281 match name {
282 "SCLK" => allowed_sclk_range = Some(range),
283 "SCLK_OFFSET" => allowed_sclk_offset_range = Some(range),
284 "MCLK" => allowed_mclk_range = Some(range),
285 "VDDGFX_OFFSET" => voltage_offset_range = Some(range),
286 "CCLK" => (), other => {
288 return Err(ParseError {
289 msg: format!("Unexpected range item: {other}"),
290 line: i,
291 }
292 .into())
293 }
294 }
295 }
296 Some(Section::Sclk) => parse_min_max_line(line, i, &mut current_sclk_range)?,
297 Some(Section::SclkOffset) => {
298 let line = line.to_ascii_lowercase();
299 let raw_value = line.trim_end_matches("mhz");
300 let value = raw_value
301 .parse()
302 .context("Could not parse sclk offset value")?;
303 sclk_offset = Some(value);
304 }
305 Some(Section::Mclk) => parse_min_max_line(line, i, &mut current_mclk_range)?,
306 Some(Section::VddcCurve) => {
307 let _ = push_level_line(line, &mut vddc_curve, i);
308 }
309 Some(Section::VddGfxOffset) => {
310 let offset = parse_voltage_offset_line(line, i)?;
311 voltage_offset = Some(offset);
312 }
313 None => {
314 return Err(ParseError {
315 msg: "Unexpected line without section".to_owned(),
316 line: i,
317 }
318 .into())
319 }
320 },
321 }
322 i += 1;
323 }
324
325 let od_range = OdRange {
326 sclk: allowed_sclk_range,
327 sclk_offset: allowed_sclk_offset_range,
328 mclk: allowed_mclk_range,
329 curve_sclk_points,
330 curve_voltage_points,
331 voltage_offset: voltage_offset_range,
332 };
333
334 Ok(Self {
335 current_sclk_range: current_sclk_range.unwrap_or_default(),
336 sclk_offset,
337 current_mclk_range: current_mclk_range.unwrap_or_default(),
338 vddc_curve,
339 od_range,
340 voltage_offset,
341 })
342 }
343}
344
345impl Table {
346 pub fn clear(&mut self) {
352 self.current_sclk_range = Range::empty();
353 self.current_mclk_range = Range::empty();
354 self.sclk_offset = None;
355 self.voltage_offset = None;
356 }
357
358 pub fn normalize_vddc_curve(&mut self) {
361 for (i, point) in self.vddc_curve.iter_mut().enumerate() {
362 if let Some(sclk_range) = self.od_range.curve_sclk_points.get(i) {
363 let normalized_clockspeed = normalize_value(point.clockspeed, *sclk_range);
364 point.clockspeed = normalized_clockspeed;
365 }
366
367 if let Some(voltage_range) = self.od_range.curve_voltage_points.get(i) {
368 let normalized_voltage = normalize_value(point.voltage, *voltage_range);
369 point.voltage = normalized_voltage;
370 }
371 }
372 }
373}
374
375fn normalize_value(mut value: i32, range: Range) -> i32 {
376 if let Some(min_allowed) = range.min {
377 value = cmp::max(min_allowed, value);
378 }
379 if let Some(max_allowed) = range.max {
380 value = cmp::min(max_allowed, value);
381 }
382
383 value
384}
385
386#[derive(Debug, Clone, PartialEq, Eq)]
388#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
389pub struct OdRange {
390 pub sclk: Option<Range>,
392 pub sclk_offset: Option<Range>,
394 pub mclk: Option<Range>,
396 pub curve_sclk_points: Vec<Range>,
398 pub curve_voltage_points: Vec<Range>,
400 pub voltage_offset: Option<Range>,
402}
403
404#[derive(Debug)]
405enum Section {
406 Sclk,
407 SclkOffset,
408 Mclk,
409 VddcCurve,
410 Range,
411 VddGfxOffset,
412}
413
414fn parse_clockspeed_line(line: &str, i: usize) -> Result<(i32, usize)> {
415 let mut split = line.split_whitespace();
416 let num = parse_line_item(&mut split, i, "level number", &[":"])?;
417 let clockspeed = parse_line_item(&mut split, i, "clockspeed", &["mhz"])?;
418
419 Ok((clockspeed, num))
420}
421
422fn parse_min_max_line(line: &str, i: usize, range: &mut Option<Range>) -> Result<()> {
423 let (clockspeed, num) = parse_clockspeed_line(line, i)?;
424 match num {
425 0 => {
426 *range = Some(Range::min(clockspeed));
427 Ok(())
428 }
429 1 => {
430 if let Some(range) = range {
431 range.max = Some(clockspeed);
432 } else {
433 *range = Some(Range::max(clockspeed));
434 }
435 Ok(())
436 }
437 _ => Err(ParseError {
438 msg: format!("Unexpected range number {num}"),
439 line: i,
440 }
441 .into()),
442 }
443}
444
445fn parse_voltage_offset_line(line: &str, i: usize) -> Result<i32> {
446 match line.to_lowercase().strip_suffix("mv") {
447 Some(raw_value) => Ok(raw_value.parse()?),
448 None => Err(ParseError {
449 msg: format!("Could not find expected `mV` suffix in offset line {line}"),
450 line: i,
451 }
452 .into()),
453 }
454}
455
456fn clockspeed_line(symbol: char, index: usize, clockspeed: i32) -> String {
457 format!("{symbol} {index} {clockspeed}\n")
458}
459
460fn vddc_curve_line(index: usize, clockspeed: i32, voltage: i32) -> String {
461 format!("vc {index} {clockspeed} {voltage}\n")
462}
463
464fn voltage_offset_line(offset: i32) -> String {
465 format!("vo {offset}\n")
466}
467
468#[cfg(test)]
469mod tests {
470 use super::{OdRange, Table};
471 use crate::{
472 gpu_handle::overdrive::{
473 arr_commands, tests::TABLE_PHOENIX, ClocksLevel, ClocksTable, Range,
474 },
475 include_table,
476 };
477 use insta::assert_yaml_snapshot;
478 use pretty_assertions::assert_eq;
479 use std::str::FromStr;
480
481 const TABLE_5500XT: &str = include_table!("rx5500xt");
482 const TABLE_5700XT: &str = include_table!("rx5700xt");
483 const TABLE_6900XT: &str = include_table!("rx6900xt");
484 const TABLE_6700XT: &str = include_table!("rx6700xt");
485 const TABLE_6800: &str = include_table!("rx6800");
486 const TABLE_7900XTX: &str = include_table!("rx7900xtx");
487 const TABLE_7900XT: &str = include_table!("rx7900xt");
488 const TABLE_7800XT: &str = include_table!("rx7800xt");
489 const TABLE_9070XT: &str = include_table!("rx9070xt");
490 const TABLE_VANGOGH: &str = include_table!("vangogh");
491
492 #[test]
493 fn parse_5700xt_full() {
494 let table = Table::from_str(TABLE_5700XT).unwrap();
495
496 assert_eq!(table.current_sclk_range, Range::full(800, 2100));
497 assert_eq!(table.current_mclk_range, Range::max(875));
498
499 let vddc_curve = [(800, 711), (1450, 801), (2100, 1191)]
500 .map(|(clockspeed, voltage)| ClocksLevel::new(clockspeed, voltage));
501 assert_eq!(table.vddc_curve, vddc_curve);
502
503 let curve_sclk_points = vec![
504 Range::full(800, 2150),
505 Range::full(800, 2150),
506 Range::full(800, 2150),
507 ];
508 let curve_voltage_points = vec![
509 Range::full(750, 1200),
510 Range::full(750, 1200),
511 Range::full(750, 1200),
512 ];
513
514 let od_range = OdRange {
515 sclk: Some(Range::full(800, 2150)),
516 mclk: Some(Range::full(625, 950)),
517 curve_sclk_points,
518 curve_voltage_points,
519 sclk_offset: None,
520 voltage_offset: None,
521 };
522 assert_eq!(table.od_range, od_range);
523 }
524
525 #[test]
526 fn generic_actions_5700xt() {
527 let mut table = Table::from_str(TABLE_5700XT).unwrap();
528 assert_eq!(table.get_max_sclk(), Some(2100));
529 assert_eq!(table.get_max_mclk(), Some(875));
530 assert_eq!(table.get_max_sclk_voltage(), Some(1191));
531
532 table.set_max_sclk(2050).unwrap();
533 assert_eq!(table.get_max_sclk(), Some(2050));
534 assert_eq!(table.current_sclk_range.max, Some(2050));
535
536 table.set_max_mclk(950).unwrap();
537 assert_eq!(table.get_max_mclk(), Some(950));
538 assert_eq!(table.current_mclk_range.max, Some(950));
539
540 table.set_max_voltage(1150).unwrap();
541 assert_eq!(table.vddc_curve[2].voltage, 1150);
542
543 let sclk_range = table.get_max_sclk_range();
544 let mclk_range = table.get_max_mclk_range();
545 let voltage_range = table.get_max_voltage_range();
546 assert_eq!(sclk_range, Some(Range::full(800, 2150)));
547 assert_eq!(mclk_range, Some(Range::full(625, 950)));
548 assert_eq!(voltage_range, Some(Range::full(750, 1200)));
549 }
550
551 #[test]
552 fn write_commands_5700xt() {
553 let mut table = Table::from_str(TABLE_5700XT).unwrap();
554
555 table.set_max_sclk(2150).unwrap();
556 table.set_min_sclk(850).unwrap();
557 table.set_max_mclk(950).unwrap();
558 table.set_max_voltage(1200).unwrap();
559
560 let mut buf = Vec::new();
561 table
562 .write_commands(&mut buf, &table.clone().into())
563 .unwrap();
564 let commands = String::from_utf8(buf).unwrap();
565
566 let expected_commands = arr_commands([
567 "s 0 850",
568 "s 1 2150",
569 "m 1 950",
570 "vc 0 850 711",
571 "vc 1 1450 801",
572 "vc 2 2150 1200",
573 ]);
574
575 assert_eq!(expected_commands, commands);
576 }
577
578 #[test]
579 fn normalize_vddc_curve_5700xt() {
580 let mut table = Table::from_str(TABLE_5700XT).unwrap();
581 let voltage_range = table.od_range.curve_voltage_points[0];
582 assert!(table.vddc_curve.iter().any(|level| {
584 level.voltage < voltage_range.min.unwrap() || level.voltage > voltage_range.max.unwrap()
585 }));
586
587 table.normalize_vddc_curve();
588
589 assert!(!table.vddc_curve.iter().any(|level| {
591 level.voltage < voltage_range.min.unwrap() || level.voltage > voltage_range.max.unwrap()
592 }));
593 assert_eq!(750, table.vddc_curve[0].voltage);
594 }
595
596 #[test]
597 fn write_commands_5500xt() {
598 let mut table = Table::from_str(TABLE_5500XT).unwrap();
599 table.clear();
600 table.set_max_sclk(1900).unwrap();
601 table.set_max_voltage(1140).unwrap();
602
603 let commands = table.get_commands(&table.clone().into()).unwrap();
604 let expected_commands = vec![
605 "s 1 1900",
606 "vc 0 500 710",
607 "vc 1 1162 794",
608 "vc 2 1900 1140",
609 ];
610 assert_eq!(expected_commands, commands);
611 }
612
613 #[test]
614 fn write_commands_custom_5700xt() {
615 let table = Table {
616 current_sclk_range: Range::empty(),
617 current_mclk_range: Range::full(500, 1000),
618 sclk_offset: None,
619 vddc_curve: vec![ClocksLevel::new(300, 600), ClocksLevel::new(1000, 1000)],
620 voltage_offset: None,
621 od_range: OdRange {
622 sclk: None,
623 sclk_offset: None,
624 mclk: None,
625 curve_sclk_points: Vec::new(),
626 curve_voltage_points: Vec::new(),
627 voltage_offset: None,
628 },
629 };
630
631 let mut buf = Vec::new();
632 table
633 .write_commands(&mut buf, &table.clone().into())
634 .unwrap();
635 let commands = String::from_utf8(buf).unwrap();
636
637 let expected_commands =
638 arr_commands(["m 0 500", "m 1 1000", "vc 0 300 600", "vc 1 1000 1000"]);
639
640 assert_eq!(expected_commands, commands);
641 }
642
643 #[test]
644 fn parse_6900xt_full() {
645 let table = Table::from_str(TABLE_6900XT).unwrap();
646 assert_yaml_snapshot!(table);
647 }
648
649 #[test]
650 fn write_commands_6900xt_default() {
651 let table = Table::from_str(TABLE_6900XT).unwrap();
652 let commands = table.get_commands(&table.clone().into()).unwrap();
653
654 assert_yaml_snapshot!(commands);
655 }
656
657 #[test]
658 fn write_commands_6900xt_custom() {
659 let mut table = Table::from_str(TABLE_6900XT).unwrap();
660 table.clear();
661
662 table.set_min_sclk(800).unwrap();
663 table.set_max_sclk(2400).unwrap();
664 table.set_max_mclk(900).unwrap();
665 assert!(table.set_min_voltage(1000).is_err());
666
667 let commands = table.get_commands(&table.clone().into()).unwrap();
668 assert_yaml_snapshot!(commands);
669 }
670
671 #[test]
672 fn parse_6700xt_full() {
673 let table = Table::from_str(TABLE_6700XT).unwrap();
674 assert_yaml_snapshot!(table);
675 }
676
677 #[test]
678 fn generic_actions_6700xt() {
679 let table = Table::from_str(TABLE_6700XT).unwrap();
680
681 let max_sclk = table.get_max_sclk().unwrap();
682 assert_eq!(max_sclk, 2725);
683 let sclk_range = table.get_max_sclk_range().unwrap();
684 assert_eq!(sclk_range, Range::full(500, 2800));
685
686 let max_mclk = table.get_max_mclk().unwrap();
687 assert_eq!(max_mclk, 1000);
688 let mclk_range = table.get_max_mclk_range().unwrap();
689 assert_eq!(mclk_range, Range::full(674, 1075));
690
691 assert!(table.get_max_sclk_voltage().is_none());
692
693 let current_sclk_range = table.get_current_sclk_range();
694 assert_eq!(current_sclk_range, Range::full(500, 2725));
695
696 let current_mclk_range = table.get_current_mclk_range();
697 assert_eq!(current_mclk_range, Range::full(97, 1000));
698 }
699
700 #[test]
701 fn write_only_max_values_6700xt() {
702 let mut table = Table::from_str(TABLE_6700XT).unwrap();
703
704 table.clear();
705 table.set_max_sclk(2800).unwrap();
706 table.set_max_mclk(1075).unwrap();
707
708 let commands = table.get_commands(&table.clone().into()).unwrap();
709 assert_yaml_snapshot!(commands);
710 }
711
712 #[test]
713 fn write_new_min_over_old_max_7900xt() {
714 let original_table = Table::from_str(TABLE_7900XT).unwrap();
715
716 let mut new_table = original_table.clone();
717 new_table.clear();
718
719 new_table.set_min_mclk(1350).unwrap();
720 new_table.set_max_mclk(1350).unwrap();
721
722 new_table.set_min_sclk(3000).unwrap();
723 new_table.set_max_sclk(3000).unwrap();
724
725 let commands = new_table.get_commands(&original_table.into()).unwrap();
726 assert_yaml_snapshot!(commands);
727 }
728
729 #[test]
730 fn parse_6800_full() {
731 let table = Table::from_str(TABLE_6800).unwrap();
732 assert_yaml_snapshot!(table);
733 }
734
735 #[test]
736 fn set_max_values_6800() {
737 let mut table = Table::from_str(TABLE_6800).unwrap();
738
739 table.clear();
740 table.set_max_sclk(2400).unwrap();
741 assert!(table.set_max_sclk(2700).is_err());
742 table.set_max_mclk(1050).unwrap();
743 table.voltage_offset = Some(10);
744
745 assert_yaml_snapshot!(table.get_commands(&table.clone().into()).unwrap());
746 }
747
748 #[test]
749 fn parse_7900xtx_full() {
750 let table = Table::from_str(TABLE_7900XTX).unwrap();
751 assert_yaml_snapshot!(table);
752 }
753
754 #[test]
755 fn parse_7900xt_full() {
756 let table = Table::from_str(TABLE_7900XT).unwrap();
757 assert_yaml_snapshot!(table);
758 }
759
760 #[test]
761 fn parse_7800xt_full() {
762 let table = Table::from_str(TABLE_7800XT).unwrap();
763 assert_yaml_snapshot!(table);
764 }
765
766 #[test]
767 fn parse_9070xt_full() {
768 let table = Table::from_str(TABLE_9070XT).unwrap();
769 assert_yaml_snapshot!(table);
770 }
771
772 #[test]
773 fn set_clock_offset_9070xt() {
774 let mut table = Table::from_str(TABLE_9070XT).unwrap();
775 table.clear();
776 table.sclk_offset = Some(200);
777 table.voltage_offset = Some(-50);
778 assert_yaml_snapshot!(table.get_commands(&table.clone().into()).unwrap());
779 }
780
781 #[test]
782 fn set_7800xt_voltage() {
783 let mut table = Table::from_str(TABLE_7800XT).unwrap();
784 table.set_voltage_offset(-300).unwrap();
785 table.set_voltage_offset(100).unwrap_err();
786 }
787
788 #[test]
789 fn parse_phoenix_full() {
790 let table = Table::from_str(TABLE_PHOENIX).unwrap();
791 assert_yaml_snapshot!(table);
792 }
793
794 #[test]
795 fn parse_vangogh_full() {
796 let table = Table::from_str(TABLE_VANGOGH).unwrap();
797 assert_yaml_snapshot!(table);
798 }
799}