diff --git a/core/examples/pc/hardware.rs b/core/examples/pc/hardware.rs index ff48ffa..e0081f8 100644 --- a/core/examples/pc/hardware.rs +++ b/core/examples/pc/hardware.rs @@ -9,7 +9,7 @@ use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, }; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::{Duration, Instant}; use rgy::{Key, Stream, VRAM_HEIGHT, VRAM_WIDTH}; @@ -23,6 +23,7 @@ pub struct Hardware { color: bool, gamepad: Arc>, gamepad_id: Option, + instant: Instant, } struct Gui { @@ -151,6 +152,7 @@ impl Hardware { escape, gamepad, gamepad_id: None, + instant: Instant::now(), } } @@ -214,10 +216,7 @@ impl rgy::Hardware for Hardware { } fn clock(&mut self) -> u64 { - let epoch = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("Couldn't get epoch"); - epoch.as_micros() as u64 + self.instant.elapsed().as_micros() as u64 } fn load_ram(&mut self, size: usize) -> Vec { diff --git a/core/examples/pc/main.rs b/core/examples/pc/main.rs index 83cb0f4..d569583 100644 --- a/core/examples/pc/main.rs +++ b/core/examples/pc/main.rs @@ -20,12 +20,9 @@ pub struct Opt { /// Cpu frequency #[structopt(short = "f", long = "freq", default_value = "4200000")] freq: u64, - /// Sampling rate for cpu frequency controller - #[structopt(short = "s", long = "sample", default_value = "4200")] - sample: u64, - /// Delay unit for cpu frequency controller - #[structopt(short = "u", long = "delayunit", default_value = "50")] - delay_unit: u64, + /// Interval to refill the token bucket for CPU rate-limiting. + #[structopt(short = "i", long = "interval", default_value = "20000")] + interval: u64, /// Don't adjust cpu frequency #[structopt(short = "n", long = "native")] native_speed: bool, @@ -44,8 +41,7 @@ fn to_cfg(opt: Opt) -> rgy::Config { rgy::Config::new() .color(opt.color) .freq(opt.freq) - .sample(opt.sample) - .delay_unit(opt.delay_unit) + .rate_limit_interval(opt.interval) .native_speed(opt.native_speed) } diff --git a/core/src/fc.rs b/core/src/fc.rs index 35078d3..ddbcdac 100644 --- a/core/src/fc.rs +++ b/core/src/fc.rs @@ -1,15 +1,12 @@ use crate::hardware::HardwareHandle; use crate::system::Config; -use log::*; pub struct FreqControl { hw: HardwareHandle, last: u64, - cycles: u64, - sample: u64, - delay: u64, - delay_unit: u64, + refill_interval: u64, target_freq: u64, + remaining_cycles: u64, } impl FreqControl { @@ -17,11 +14,9 @@ impl FreqControl { Self { hw, last: 0, - cycles: 0, - delay: 0, - sample: cfg.sample, - delay_unit: cfg.delay_unit, + refill_interval: cfg.rate_limit_interval, target_freq: cfg.freq, + remaining_cycles: 0, } } @@ -30,35 +25,26 @@ impl FreqControl { } pub fn adjust(&mut self, time: usize) { - self.cycles += time as u64; + let consumed_cycles = time as u64; - for _ in 0..self.delay { - let _ = unsafe { core::ptr::read_volatile(&self.sample) }; - } - - if self.cycles > self.sample { - self.cycles -= self.sample; + self.try_fill(); - let now = self.hw.get().borrow_mut().clock(); - let (diff, of) = now.overflowing_sub(self.last); - if of || diff == 0 { - warn!("Overflow: {} - {}", self.last, now); - self.last = now; - return; - } + while self.remaining_cycles < consumed_cycles { + self.try_fill(); + } - // get cycles per second - let freq = self.sample * 1_000_000 / diff; + self.remaining_cycles -= consumed_cycles; + } - debug!("Frequency: {}", freq); + fn try_fill(&mut self) { + let now = self.hw.get().borrow_mut().clock(); - self.delay = if freq > self.target_freq { - self.delay.saturating_add(self.delay_unit) - } else { - self.delay.saturating_sub(self.delay_unit) - }; + let diff = now.saturating_sub(self.last); + if diff >= self.refill_interval { self.last = now; + + self.remaining_cycles += self.target_freq * diff / 1_000_000; } } } diff --git a/core/src/system.rs b/core/src/system.rs index aa76ba6..5102446 100644 --- a/core/src/system.rs +++ b/core/src/system.rs @@ -9,10 +9,8 @@ use log::*; pub struct Config { /// CPU frequency. pub(crate) freq: u64, - /// Cycle sampling count in the CPU frequency controller. - pub(crate) sample: u64, - /// Delay unit in CPU frequency controller. - pub(crate) delay_unit: u64, + /// Interval to refill tokens in the token bucket for CPU rate-limiting in micro-seconds. + pub(crate) rate_limit_interval: u64, /// Don't adjust CPU frequency. pub(crate) native_speed: bool, /// Emulate Gameboy Color @@ -32,8 +30,7 @@ impl Config { Self { freq, - sample: freq / 1000, - delay_unit: 10, + rate_limit_interval: 20_000, native_speed: false, color: false, } @@ -45,15 +42,9 @@ impl Config { self } - /// Set the sampling count of the CPU frequency controller. - pub fn sample(mut self, sample: u64) -> Self { - self.sample = sample; - self - } - - /// Set the delay unit. - pub fn delay_unit(mut self, delay: u64) -> Self { - self.delay_unit = delay; + /// Interval to refill tokens in the token bucket for CPU rate-limiting in micro-seconds. + pub fn rate_limit_interval(mut self, interval: u64) -> Self { + self.rate_limit_interval = interval; self }