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}