Skip to content

Commit

Permalink
Improved Instant and Inspect/Debug traits (#2)
Browse files Browse the repository at this point in the history
* Improved Instant and Inspect/Debug traits

* Fixed fugit impl

* Fixed pull request triggers
  • Loading branch information
transistorfet committed Mar 20, 2024
1 parent 84e665c commit 1000066
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/clippy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
branches:
- main
pull_request:
types: [opened, synchronized, reopened, ready_for_review]
types: [opened, synchronize, reopened]

permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rustdoc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
branches:
- main
pull_request:
types: [opened, synchronized, reopened, ready_for_review]
types: [opened, synchronize, reopened]

permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rustfmt.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
branches:
- main
pull_request:
types: [opened, synchronized, reopened, ready_for_review]
types: [opened, synchronize, reopened]

permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
branches:
- main
pull_request:
types: [opened, synchronized, reopened, ready_for_review]
types: [opened, synchronize, reopened]

permissions:
contents: read
Expand Down
92 changes: 58 additions & 34 deletions emulator-hal/src/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,16 @@ where
/// Write the given information type to the given writer, or return an error
fn inspect(
&mut self,
writer: &mut Writer,
info: Self::InfoType,
bus: &mut Bus,
writer: &mut Writer,
) -> Result<(), Self::Error>;

/// Write a brief summary of the device's current state to the given writer, or return an error
fn brief_summary(&mut self, bus: &mut Bus, writer: &mut Writer) -> Result<(), Self::Error>;

/// Write a detailed summary of the device's current state to the given writer, or return an error
fn detailed_summary(&mut self, bus: &mut Bus, writer: &mut Writer) -> Result<(), Self::Error>;
}

/// Control the execution of a CPU device for debugging purposes
Expand All @@ -72,19 +78,27 @@ where
/// Sets the `Address` where execution will take place the next time `step()` is called
fn set_execution_address(&mut self, address: Address) -> Result<(), Self::DebugError>;

// TODO this is too vague
/// Perform a debug command
fn run_command(&mut self, bus: &mut Bus, args: &[&str]) -> Result<bool, Self::DebugError>;
/// Add a breakpoint
fn add_breakpoint(&mut self, address: Address);
/// Remove a breakpoint
fn remove_breakpoint(&mut self, address: Address);
/// Clear all breakpoints
fn clear_breakpoints(&mut self);

// todo this is too vague
// perform a debug command
//fn run_command(&mut self, command: Self::Command, bus: &mut bus) -> result<bool, self::debugerror>;
}

#[cfg(test)]
mod test {
use super::*;

use crate::bus::{self, BusAdapter, SimpleBusError};
use crate::time::Instant;
use std::ops::Range;
use std::str;
use std::time::{Duration, Instant};
use std::time::Duration;

#[derive(Clone, Debug)]
enum Error {
Expand All @@ -95,12 +109,12 @@ mod test {

struct Memory(Vec<u8>);

impl BusAccess<u32, Instant> for Memory {
impl BusAccess<u32, Duration> for Memory {
type Error = SimpleBusError;

fn read(
&mut self,
_now: Instant,
_now: Duration,
addr: u32,
data: &mut [u8],
) -> Result<usize, Self::Error> {
Expand All @@ -109,7 +123,7 @@ mod test {
Ok(data.len())
}

fn write(&mut self, _now: Instant, addr: u32, data: &[u8]) -> Result<usize, Self::Error> {
fn write(&mut self, _now: Duration, addr: u32, data: &[u8]) -> Result<usize, Self::Error> {
let addr = addr as usize;
self.0[addr..addr + data.len()].copy_from_slice(data);
Ok(data.len())
Expand All @@ -125,19 +139,19 @@ mod test {

struct Output();

impl BusAccess<u16, Instant> for Output {
impl BusAccess<u16, Duration> for Output {
type Error = OutputError;

fn read(
&mut self,
_now: Instant,
_now: Duration,
_addr: u16,
_data: &mut [u8],
) -> Result<usize, Self::Error> {
Ok(0)
}

fn write(&mut self, _now: Instant, _addr: u16, data: &[u8]) -> Result<usize, Self::Error> {
fn write(&mut self, _now: Duration, _addr: u16, data: &[u8]) -> Result<usize, Self::Error> {
let string = str::from_utf8(data).map_err(|_| OutputError::Utf8Error)?;
print!("{}", string);
Ok(data.len())
Expand All @@ -149,10 +163,15 @@ mod test {
memory: Memory,
}

impl BusAccess<u64, Instant> for FixedBus {
impl BusAccess<u64, Duration> for FixedBus {
type Error = Error;

fn read(&mut self, now: Instant, addr: u64, data: &mut [u8]) -> Result<usize, Self::Error> {
fn read(
&mut self,
now: Duration,
addr: u64,
data: &mut [u8],
) -> Result<usize, Self::Error> {
if (0..0x1_0000).contains(&addr) {
self.memory
.read(now, addr as u32 % 0x1_0000, data)
Expand All @@ -164,7 +183,7 @@ mod test {
}
}

fn write(&mut self, now: Instant, addr: u64, data: &[u8]) -> Result<usize, Self::Error> {
fn write(&mut self, now: Duration, addr: u64, data: &[u8]) -> Result<usize, Self::Error> {
if (0..0x1_0000).contains(&addr) {
self.memory
.write(now, addr as u32 % 0x1_0000, data)
Expand All @@ -178,13 +197,18 @@ mod test {
}

struct DynamicBus {
devices: Vec<(Range<u64>, Box<dyn BusAccess<u64, Instant, Error = Error>>)>,
devices: Vec<(Range<u64>, Box<dyn BusAccess<u64, Duration, Error = Error>>)>,
}

impl BusAccess<u64, Instant> for DynamicBus {
impl BusAccess<u64, Duration> for DynamicBus {
type Error = Error;

fn read(&mut self, now: Instant, addr: u64, data: &mut [u8]) -> Result<usize, Self::Error> {
fn read(
&mut self,
now: Duration,
addr: u64,
data: &mut [u8],
) -> Result<usize, Self::Error> {
for (range, device) in self.devices.iter_mut() {
if range.contains(&addr) {
return device.read(now, addr, data).map_err(|_| Error::BusError);
Expand All @@ -193,7 +217,7 @@ mod test {
Ok(0)
}

fn write(&mut self, now: Instant, addr: u64, data: &[u8]) -> Result<usize, Self::Error> {
fn write(&mut self, now: Duration, addr: u64, data: &[u8]) -> Result<usize, Self::Error> {
for (range, device) in self.devices.iter_mut() {
if range.contains(&addr) {
return device.write(now, addr, data).map_err(|_| Error::BusError);
Expand All @@ -210,9 +234,9 @@ mod test {
running: bool,
}

impl<Bus> Step<u64, Instant, Bus> for Cpu
impl<Bus> Step<u64, Duration, Bus> for Cpu
where
Bus: BusAccess<u64, Instant>,
Bus: BusAccess<u64, Duration>,
Error: From<Bus::Error>,
{
type Error = Error;
Expand All @@ -221,13 +245,13 @@ mod test {
self.running
}

fn reset(&mut self, now: Instant, bus: &mut Bus) -> Result<(), Self::Error> {
fn reset(&mut self, now: Duration, bus: &mut Bus) -> Result<(), Self::Error> {
self.running = true;
self.pc = bus.read_beu32(now, 0x0000)? as u64;
Ok(())
}

fn step(&mut self, now: Instant, bus: &mut Bus) -> Result<Instant, Self::Error> {
fn step(&mut self, now: Duration, bus: &mut Bus) -> Result<Duration, Self::Error> {
if self.running {
let value = bus.read_beu32(now, self.pc)?;
self.pc += 4;
Expand All @@ -253,26 +277,26 @@ mod test {

let location = 0x100;
bus.memory
.write_beu32(Instant::now(), 0x0000, location as u32)
.write_beu32(Duration::START, 0x0000, location as u32)
.unwrap();

for i in 0..100 {
bus.memory
.write_beu32(Instant::now(), location + 4 * i as u32, 1 + i as u32)
.write_beu32(Duration::START, location + 4 * i as u32, 1 + i as u32)
.unwrap();
}

fn run_static_test<A, B, C>(bus: &mut B, cpu: &mut C) -> Result<(), C::Error>
where
A: Copy,
B: BusAccess<A, Instant>,
C: Step<A, Instant, B>,
B: BusAccess<A, Duration>,
C: Step<A, Duration, B>,
C::Error: From<B::Error>,
{
cpu.reset(Instant::now(), bus)?;
cpu.reset(Duration::START, bus)?;

while cpu.is_running() {
cpu.step(Instant::now(), bus)?;
cpu.step(Duration::START, bus)?;
}
Ok(())
}
Expand Down Expand Up @@ -312,26 +336,26 @@ mod test {
let mut cpu = Cpu::default();

let location = 0x100 as u64;
bus.write_beu32(Instant::now(), 0x0000, location as u32)
bus.write_beu32(Duration::START, 0x0000, location as u32)
.unwrap();

for i in 0..100 {
bus.write_beu32(Instant::now(), location + 4 * i as u64, 1 + i as u32)
bus.write_beu32(Duration::START, location + 4 * i as u64, 1 + i as u32)
.unwrap();
}

type Bus = Box<dyn BusAccess<u64, Instant, Error = Error>>;
type Bus = Box<dyn BusAccess<u64, Duration, Error = Error>>;

//let _trait_obj_cpu: &mut dyn Step<Bus, Error = Error> = &mut cpu;

fn run_dynamic_test(
mut bus: Bus,
cpu: &mut dyn Step<u64, Instant, Bus, Error = Error>,
cpu: &mut dyn Step<u64, Duration, Bus, Error = Error>,
) -> Result<(), Error> {
cpu.reset(Instant::now(), &mut bus)?;
cpu.reset(Duration::START, &mut bus)?;

while cpu.is_running() {
cpu.step(Instant::now(), &mut bus)?;
cpu.step(Duration::START, &mut bus)?;
}
Ok(())
}
Expand Down
56 changes: 47 additions & 9 deletions emulator-hal/src/time.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
//! Traits and implementations for coordinating time between emulated components

use core::ops::Add;
use core::ops::{Add, Mul};
use core::time::Duration;

/// Represents a monotonic instant in time
pub trait Instant: Add<Self::Duration> {
pub trait Instant: Add<Self::Duration, Output = Self> + Copy {
/// The start of the epoch according to this time representation
const START: Self;

/// Represents a duration that can be added to an instant of this type
type Duration;
type Duration: Mul<u32, Output = Self::Duration>;

/// Returns the duration of one period of the given frequency is hertz
fn hertz_to_duration(hertz: u64) -> Self::Duration;
}

/*
Expand All @@ -22,22 +29,53 @@ impl<T: Instant> InstantType for T {
}
*/

#[cfg(feature = "std")]
impl Instant for std::time::Instant {
type Duration = std::time::Duration;
impl Instant for Duration {
const START: Self = Duration::from_nanos(0);

type Duration = Duration;

fn hertz_to_duration(hertz: u64) -> Self::Duration {
Duration::from_nanos(1_000_000_000 / hertz)
}
}

#[cfg(feature = "fugit")]
impl<T, const NOM: u32, const DENOM: u32> Instant for fugit::Instant<T, NOM, DENOM>
impl<const NOM: u32, const DENOM: u32> Instant for fugit::Instant<u32, NOM, DENOM>
where
Self: Add<fugit::Duration<T, NOM, DENOM>>,
Self: Add<fugit::Duration<u32, NOM, DENOM>, Output = Self>,
{
type Duration = fugit::Duration<T, NOM, DENOM>;
const START: Self = fugit::Instant::<u32, NOM, DENOM>::from_ticks(0);

type Duration = fugit::Duration<u32, NOM, DENOM>;

fn hertz_to_duration(hertz: u64) -> Self::Duration {
fugit::Duration::<u32, NOM, DENOM>::from_ticks(DENOM / hertz as u32)
}
}

#[cfg(feature = "fugit")]
impl<const NOM: u32, const DENOM: u32> Instant for fugit::Instant<u64, NOM, DENOM>
where
Self: Add<fugit::Duration<u64, NOM, DENOM>, Output = Self>,
{
const START: Self = fugit::Instant::<u64, NOM, DENOM>::from_ticks(0);

type Duration = fugit::Duration<u64, NOM, DENOM>;

fn hertz_to_duration(hertz: u64) -> Self::Duration {
fugit::Duration::<u64, NOM, DENOM>::from_ticks(DENOM as u64 / hertz)
}
}

#[cfg(feature = "femtos")]
impl Instant for femtos::Instant {
const START: Self = femtos::Instant::START;

type Duration = femtos::Duration;

fn hertz_to_duration(hertz: u64) -> Self::Duration {
femtos::Duration::from_femtos(1_000_000_000_000_000 / hertz as femtos::Femtos)
}
}

#[cfg(test)]
Expand Down

0 comments on commit 1000066

Please sign in to comment.