Skip to content

Commit f4e3de1

Browse files
authored
RFC #66: Simulation time.
2 parents be658d7 + 7089512 commit f4e3de1

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed

text/0066-simulation-time.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
- Start Date: 2024-07-29
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/1469)
4+
5+
# Simulation time
6+
7+
## Summary
8+
[summary]: #summary
9+
10+
Add a type for representing time durations (including clock periods) 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 `Period` is introduced.
25+
It is exported from `amaranth.hdl` and also re-exported from `amaranth.sim`.
26+
It is immutable and has a constructor accepting at most one named argument, giving the following valid forms of construction:
27+
- `Period()`
28+
- Constructs a zero period.
29+
- `Period(s: numbers.Real)`
30+
- `Period(ms: numbers.Real)`
31+
- `Period(us: numbers.Real)`
32+
- `Period(ns: numbers.Real)`
33+
- `Period(ps: numbers.Real)`
34+
- `Period(fs: numbers.Real)`
35+
- The argument will be scaled according to its SI prefix and used to calculate the closest integer femtosecond representation.
36+
- `Period(Hz: numbers.Real)`
37+
- `Period(kHz: numbers.Real)`
38+
- `Period(MHz: numbers.Real)`
39+
- `Period(GHz: numbers.Real)`
40+
- The argument will be scaled according to its SI prefix and its reciprocal used to calculate the closest integer femtosecond representation.
41+
- A value of zero will raise `ZeroDivisionError`.
42+
- A negative value will raise `ValueError`.
43+
44+
To convert it back to a number, the following properties are available:
45+
- `.seconds -> float`
46+
- `.milliseconds -> float`
47+
- `.microseconds -> float`
48+
- `.nanoseconds -> float`
49+
- `.picoseconds -> float`
50+
- `.femtoseconds -> int`
51+
52+
To calculate the reciprocal frequency, the following properties are available:
53+
- `.hertz -> float`
54+
- `.kilohertz -> float`
55+
- `.megahertz -> float`
56+
- `.gigahertz -> float`
57+
- Accessing these properties when the period is zero will raise `ZeroDivisionError`.
58+
- Accessing these properties when the period is negative will raise `ValueError`.
59+
60+
The following operators are defined:
61+
- `.__lt__(other: Period) -> bool`
62+
- `.__le__(other: Period) -> bool`
63+
- `.__eq__(other: Period) -> bool`
64+
- `.__ne__(other: Period) -> bool`
65+
- `.__gt__(other: Period) -> bool`
66+
- `.__ge__(other: Period) -> bool`
67+
- `.__hash__() -> int`
68+
- `.__bool__() -> bool`
69+
- `.__neg__() -> Period`
70+
- `.__pos__() -> Period`
71+
- `.__abs__() -> Period`
72+
- `.__add__(other: Period) -> Period`
73+
- `.__sub__(other: Period) -> Period`
74+
- `.__mul__(other: numbers.Real) -> Period`
75+
- `.__rmul__(other: numbers.Real) -> Period`
76+
- `.__truediv__(other: numbers.Real) -> Period`
77+
- `.__truediv__(other: Period) -> float`
78+
- `.__floordiv__(other: Period) -> int`
79+
- `.__mod__(other: Period) -> Period`
80+
- Operators on `Period`s are performed on the underlying femtosecond values.
81+
- Operators involving `numbers.Real` operands have the result rounded back to the closest integer femtosecond representation.
82+
- Operators given unsupported operand combinations will return `NotImplemented`.
83+
- `.__str__() -> str`
84+
- Equivalent to `.__format__("")`
85+
- `.__format__(format_spec: str) -> str`
86+
- The format specifier format is `[width][.precision][ ][unit]`.
87+
- An invalid format specifier raises `ValueError`.
88+
- If `width` is specified, the string is left-padded with space to at least the requested width.
89+
- If `precision` is specified, the requested number of decimal digits will be emitted.
90+
Otherwise, duration units will emit as many digits required for an exact result, while frequency units will defer to default `float` formatting.
91+
- If a space is present in the format specifier, the formatted string will have a space between the number and the unit.
92+
- `unit` can be specified as any of the argument names accepted by the constructor.
93+
If a unit is not specified, the largest duration unit that has a nonzero integer part is used.
94+
Formatting frequency units have the same restrictions and exception behavior as accessing frequency properties.
95+
96+
`SimulatorContext` have an `.elapsed_time() -> Period` method added that returns the elapsed time since start of simulation.
97+
98+
These methods that has a `period` argument currently taking seconds as a `float` are updated to take a `Period`:
99+
- `Simulator.add_clock()`
100+
- `SimulatorContext.delay()`
101+
102+
These methods that has a `frequency` argument currently taking Hz as a `float` are updated to take a `Period`:
103+
- `ResourceManager.add_clock_constraint()`
104+
- `Clock.__init__()`
105+
106+
The ability to pass seconds or Hz directly as a `float` is deprecated and removed in a future Amaranth version.
107+
108+
The `Clock.period` property is updated to return a `Period` and the `Clock.frequency` property is deprecated.
109+
110+
Consequently, `Platform.default_clk_frequency()` is also deprecated and replaced with a new method `Platform.default_clk_period() -> Period`.
111+
112+
113+
## Drawbacks
114+
[drawbacks]: #drawbacks
115+
116+
- Deprecating being able to pass seconds or Hz directly as a `float` creates churn.
117+
118+
## Rationale and alternatives
119+
[rationale-and-alternatives]: #rationale-and-alternatives
120+
121+
- `.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.
122+
- We could add a separate `Frequency` type, but in practice it would likely almost exclusively be used to calculate the reciprocal `Period`.
123+
124+
- Accepting a `numbers.Real` when we're converting from a number lets us pass in any standard suitable number type.
125+
126+
- The supported set of operators are the ones that have meaningful and useful semantics:
127+
- Comparing, adding and subtracting time periods makes sense.
128+
- Multiplying a time period with or dividing it by a real number scales the time period.
129+
- Dividing time periods gives the ratio between them.
130+
- Modulo of time periods is the remainder and thus still a time period.
131+
Potentially useful to calculate the phase offset between a timestamp and a clock period.
132+
- Reflected operators that only accept `Period` operands are redundant and omitted.
133+
134+
- `.elapsed_time()` is only available from within a testbench/process, where it is well defined.
135+
136+
- Instead of named constructor arguments, we could use a classmethod for each SI prefix.
137+
This was proposed in an earlier revision of this RFC.
138+
139+
- Instead of returning `float`, we could return `fractions.Fraction` to ensure no precision loss when the number of femtoseconds is larger than the `float` significand.
140+
This was proposed in an earlier revision of this RFC.
141+
- Rounding to integer femtoseconds is already lossy and a further precision loss from converting to a `float` is negligible in most real world use cases.
142+
For any applications requiring an exact conversion of a `Period`, the `.femtoseconds` property is always exact.
143+
144+
## Prior art
145+
[prior-art]: #prior-art
146+
147+
None.
148+
149+
## Unresolved questions
150+
[unresolved-questions]: #unresolved-questions
151+
152+
None.
153+
154+
## Future possibilities
155+
[future-possibilities]: #future-possibilities
156+
157+
None.

0 commit comments

Comments
 (0)