Skip to main content

ternaria_dev/
timer.rs

1//! A monotonic tick counter.
2//!
3//! | Offset | Register | Behaviour |
4//! |--------|----------|-----------|
5//! | 0..3 | `TICKS` | The count, as three trytes forming a word. Read-only. |
6//! | 3..6 | `COMPARE` | Raise an interrupt once the count reaches this. Zero disarms. |
7//!
8//! Occupying three trytes means an ordinary `lw` at the timer base reads the
9//! whole count.
10//!
11//! The counter is advanced by the host rather than by a wall clock, so runs are
12//! reproducible. [`Bus::tick`](crate::Bus::tick) advances it once per executed
13//! instruction.
14
15use crate::{Device, unmapped};
16use ternaria_arith::{Tryte, Word};
17use ternaria_mem::MemoryError;
18
19/// Offset of the first tryte of the tick count.
20pub const TICKS: i64 = 0;
21/// Offset of the first tryte of the compare register.
22///
23/// Word-sized, so it starts three trytes after `TICKS` and stays word-aligned.
24pub const COMPARE: i64 = 3;
25
26/// A tick counter.
27#[derive(Default)]
28pub struct Timer {
29    ticks: i64,
30    compare: i64,
31}
32
33impl Timer {
34    /// A timer starting at zero.
35    pub fn new() -> Timer {
36        Timer::default()
37    }
38
39    /// Advances the counter by one, saturating at the top of the word range.
40    pub fn tick(&mut self) {
41        if self.ticks < Word::MAX.value() {
42            self.ticks += 1;
43        }
44    }
45
46    /// The current count.
47    pub fn ticks(&self) -> i64 {
48        self.ticks
49    }
50
51    /// The value the count is compared against.
52    pub fn compare(&self) -> i64 {
53        self.compare
54    }
55
56    /// True while the timer is asserting an interrupt.
57    ///
58    /// Zero disarms, so a machine that never writes `COMPARE` is never
59    /// interrupted. The test is `>=` rather than `==` so that a compare value
60    /// already in the past fires at once instead of being missed.
61    pub fn is_firing(&self) -> bool {
62        self.compare != 0 && self.ticks >= self.compare
63    }
64
65    /// Replaces one tryte of the compare register.
66    fn set_compare_tryte(&mut self, index: usize, value: Tryte) {
67        let current = Word::from_value(self.compare);
68        let mut trytes = [Tryte::ZERO; Word::TRYTES];
69        for (i, slot) in trytes.iter_mut().enumerate() {
70            *slot = current.tryte(i);
71        }
72        trytes[index] = value;
73        self.compare = Word::from_trytes(trytes).value();
74    }
75}
76
77impl Device for Timer {
78    fn name(&self) -> &'static str {
79        "timer"
80    }
81
82    fn read(&mut self, offset: i64) -> Result<Tryte, MemoryError> {
83        match offset {
84            0..=2 => Ok(Word::from_value(self.ticks).tryte(offset as usize)),
85            3..=5 => Ok(Word::from_value(self.compare).tryte((offset - COMPARE) as usize)),
86            _ => Err(unmapped(offset)),
87        }
88    }
89
90    fn write(&mut self, offset: i64, value: Tryte) -> Result<(), MemoryError> {
91        match offset {
92            3..=5 => {
93                self.set_compare_tryte((offset - COMPARE) as usize, value);
94                Ok(())
95            }
96            0..=2 => Err(MemoryError::Device {
97                addr: offset,
98                reason: "the tick count is read-only",
99            }),
100            _ => Err(unmapped(offset)),
101        }
102    }
103}