Skip to content

Commit 52efa4d

Browse files
committed
RFC #66: Simulation time
1 parent be658d7 commit 52efa4d

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

text/0066-simulation-time.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
- Start Date: (fill in with date at which the RFC is merged, YYYY-MM-DD)
2+
- RFC PR: [amaranth-lang/rfcs#66](https://github.com/amaranth-lang/rfcs/pull/66)
3+
- Amaranth Issue: [amaranth-lang/amaranth#0000](https://github.com/amaranth-lang/amaranth/issues/0000)
4+
5+
# Simulation time
6+
7+
## Summary
8+
[summary]: #summary
9+
10+
Add a type for representing time durations and simulator methods to query the elapsed time.
11+
12+
## Motivation
13+
[motivation]: #motivation
14+
15+
In [RFC #36](0036-async-testbench-functions.md), there were a plan to introduce a `.time()` method to query the current simulation time, but it got deferred due to the lack of a suitable return type.
16+
17+
Internally simulation time is an integer number of femtoseconds, but exposing that directly is not ergonomic and making it a `float` of seconds sacrifices precision the longer a simulation runs.
18+
19+
Also, to quote @whitequark: Time is not a number.
20+
21+
## Guide- and reference-level explanation
22+
[guide-level-explanation]: #guide-level-explanation
23+
24+
A new type `amaranth.sim.Period` is introduced.
25+
It is immutable, has no public constructor and is instead constructed through the following classmethods:
26+
- `.s(duration: numbers.Real)`
27+
- `.ms(duration: numbers.Real)`
28+
- `.us(duration: numbers.Real)`
29+
- `.ns(duration: numbers.Real)`
30+
- `.ps(duration: numbers.Real)`
31+
- `.fs(duration: numbers.Real)`
32+
- `.Hz(frequency: numbers.Real)`
33+
- `.kHz(frequency: numbers.Real)`
34+
- `.MHz(frequency: numbers.Real)`
35+
- `.GHz(frequency: numbers.Real)`
36+
- The argument will be scaled according to the SI prefix on the method and the `duration` or reciprocal of `frequency` then used to calculate the closest integer femtosecond representation.
37+
38+
To convert it back to a number, the following properties are available:
39+
- `.seconds -> fractions.Fraction`
40+
- `.milliseconds -> fractions.Fraction`
41+
- `.microseconds -> fractions.Fraction`
42+
- `.nanoseconds -> fractions.Fraction`
43+
- `.picoseconds -> fractions.Fraction`
44+
- `.femtoseconds -> int`
45+
46+
The following operators are defined:
47+
- `.__lt__(other: Period) -> bool`
48+
- `.__le__(other: Period) -> bool`
49+
- `.__eq__(other: Period) -> bool`
50+
- `.__ne__(other: Period) -> bool`
51+
- `.__gt__(other: Period) -> bool`
52+
- `.__ge__(other: Period) -> bool`
53+
- `.__hash__() -> int`
54+
- `.__bool__() -> bool`
55+
- `.__neg__() -> Period`
56+
- `.__pos__() -> Period`
57+
- `.__abs__() -> Period`
58+
- `.__add__(other: Period) -> Period`
59+
- `.__sub__(other: Period) -> Period`
60+
- `.__mul__(other: numbers.Real) -> Period`
61+
- `.__rmul__(other: numbers.Real) -> Period`
62+
- `.__truediv__(other: numbers.Real) -> Period`
63+
- `.__truediv__(other: Period) -> fractions.Fraction`
64+
- `.__floordiv__(other: Period) -> int`
65+
- `.__mod__(other: Period) -> Period`
66+
- Operators on `Period`s are performed on the underlying femtosecond values.
67+
- Operators involving `numbers.Real` operands have the result rounded back to the closest integer femtosecond representation.
68+
- Operators given unsupported operand combinations will return `NotImplemented`.
69+
70+
`Simulator` and `SimulatorContext` both have a `.time() -> Period` method added that returns the elapsed time since start of simulation.
71+
72+
The methods that currently take seconds as a `float` are updated to take a `Period`:
73+
- `.add_clock()`
74+
- `.delay()`
75+
76+
The ability to pass seconds as a `float` is deprecated and removed in a future Amaranth version.
77+
78+
## Drawbacks
79+
[drawbacks]: #drawbacks
80+
81+
- Deprecating being able to pass seconds as a `float` creates churn.
82+
83+
## Rationale and alternatives
84+
[rationale-and-alternatives]: #rationale-and-alternatives
85+
86+
- `.add_clock()` is usually passed the reciprocal of a frequency, so being able to construct a `Period` from a frequency instead of a duration is useful.
87+
- We could add a separate `Frequency` type, but in practice it would likely almost exclusively be used to calculate the reciprocal `Period`.
88+
89+
- Accepting a `numbers.Real` when we're converting from a number lets us pass in any standard suitable number type.
90+
91+
- When converting to a number, `fractions.Fraction` is a standard `numbers.Real` type that is guaranteed to represent any possible value exactly, and is convertible to `int` or `float`.
92+
- Returning `int` would force a potentially undesirable truncation/rounding upon the user.
93+
- Returning `float` would lose precision when the number of femtoseconds are larger than the significand.
94+
- `decimal.Decimal` is decimal floating point and thus has the same issue as `float`.
95+
- `.femtoseconds` returns `int` because that's a perfect representation and returning a fraction with a denominator of 1 adds no value.
96+
97+
- The supported set of operators are the ones that have meaningful and useful semantics:
98+
- Comparing, adding and subtracting time periods makes sense.
99+
- Multiplying a time period with or dividing it by a real number scales the time period.
100+
- Dividing time periods gives the ratio between them.
101+
- Modulo of time periods is the remainder and thus still a time period.
102+
Potentially useful to calculate the phase offset between a timestamp and a clock period.
103+
- Reflected operators that only accept `Period` operands are redundant and omitted.
104+
105+
## Prior art
106+
[prior-art]: #prior-art
107+
108+
None.
109+
110+
## Unresolved questions
111+
[unresolved-questions]: #unresolved-questions
112+
113+
- The usual bikeshedding.
114+
- Case, e.g. `.MHz` vs `.mhz`.
115+
116+
## Future possibilities
117+
[future-possibilities]: #future-possibilities
118+
119+
None.

0 commit comments

Comments
 (0)