-
Notifications
You must be signed in to change notification settings - Fork 178
[WIP] Implement RFC 41: lib.fixed
#1578
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
I noticed from amaranth import *
from amaranth.lib import fixed
from amaranth.lib.wiring import Component, In, Out
class C(Component):
o: Out(1)
def elaborate(self, platform):
m = Module()
self.s = Signal(2)
self.f = Signal(fixed.SQ(2, 2))
m.d.sync += self.o.eq(self.s)
return m
from amaranth.sim import Period, Simulator
dut = C()
async def bench(ctx):
for _ in range(1):
print(ctx.get(dut.s) == 0) # True
print(ctx.get(dut.f) == 0) # (== (s (slice (const 4'sd0) 0:4)) (cat (const 2'd0) (const 1'd0)))
await ctx.tick()
sim = Simulator(dut)
sim.add_clock(Period())
sim.add_testbench(bench)
sim.run() outputs:
|
@goekce Thanks for taking a look! I think this is happening because of the comparison outside the simulation context, rather than inside it, which means that
As an example for writing assertions, maybe take a look at the tests attached to this PR. That being said, this is still a work in progress and not quite ready for review yet! |
@goekce a related topic to your example's evaluation outside the simulation context is elaboration-time evaluation of constant expressions. which is something I'd like to leave out of this RFC, even if we could add it in the future. For example
|
I was not aware of the possibility that I can do a simulation-time comparison. Thanks Seb, this would solve my problem. As I understand, elaboration-time evaluation of constant expressions is extra work.👍 |
You can already do elaboration-time comparison of const expressions by doing |
For comparison with zero that makes sense but for other values, if we drop the
I think this kind of use case is better covered by
In the example from @goekce - if we really want to do the comparison outside the sim context for some reason
|
What I mean is that |
I am confused about how I thought
But it is probably not used:
Or do you just want to say that |
If a
from amaranth import *
from amaranth.lib import fixed
from amaranth.lib.wiring import Component, In, Out
class C(Component):
y: Out(fixed.SQ(8, 4))
def elaborate(self, platform):
m = Module()
self.a = Signal(self.y.shape())
self.b = Signal(self.y.shape())
self.c = Signal(self.y.shape())
m.d.comb += [
self.a.eq(fixed.Const(-1.125) + Mux(1, fixed.Const(1.375), 0)),
self.b.eq(fixed.Const(-1.125) + fixed.Const(1.375)),
self.c.eq(Mux(1, fixed.Const(1.375), 0)),
]
m.d.sync += self.y.eq(self.a)
return m
from amaranth.sim import Period, Simulator
dut = C()
async def bench(ctx):
await ctx.tick()
print(ctx.get(dut.a).as_float())
print(ctx.get(dut.b).as_float())
print(ctx.get(dut.c).as_float())
print(Mux(1, fixed.Const(1.375), 0).shape())
sim = Simulator(dut)
sim.add_clock(Period())
sim.add_testbench(bench)
sim.run() Result:
|
Co-authored-by: Gökçe Aydos <18174744+goekce@users.noreply.github.com>
Thanks, I haven't played with Ideally we want to attack this without touching anything outside It seems other types similarly lose the type information through
In this case it seems making |
Yeah, that sounds reasonable. We actually discussed the option of preserving shapes through |
If ...
self.c.eq(Mux(1, fixed.Const(1.375), 0)),
self.d.eq(Mux(1, fixed.Const(1.375, self.y.shape()), 0)),
self.e.eq(fixed.Const(-1.125) + Mux(1, fixed.Const(1.375, self.y.shape()), 0)),
self.f.eq(fixed.Const(-1.125, self.y.shape()) + Mux(1, fixed.Const(1.375, self.y.shape()), 0)),
self.g.eq(fixed.Const(-1.125, self.y.shape()) + Mux(1, fixed.Const(1.375, self.y.shape()), fixed.Const(0, self.y.shape()))),
...
print(ctx.get(dut.d).as_float())
... Outputs:
The workaround I found was to use an |
If one really wants to use a
On your example:
I would however not include such a workaround in this RFC. I think attacking this properly would imply modifying Whether |
Thanks for the IMHO |
Overview
lib.fixed
while the associated RFC is being worked on. It started as a fork of @zyp's early implementation of the RFC here, however a few things have changed since then.fixed.Value
-.saturate()
and.clamp()
- these are commonly needed in my DSP codebase, but it may make sense to punt these new methods to a future RFC..truncate()
is added as an alias for.reshape()
, the only difference being that it verifies there was a reduction off_bits
requested.numerator()
method is dropped, as I found a way to combine it withas_value()
reliably.lib.fixed
in this Tiliqua PR and tested it underneath my library of DSP cores, and will continue to use the learnings there in order to guide the RFC.Simple example
Consider the implementation of a simple Low-pass filter, where we wish to compute the difference equation
y = y[n-1] * beta + x * (1 - beta)
using fixed point representation: