[Libre-silicon-devel] [Librecores Discussion] IEEE754 FPU in nmigen

Luke Kenneth Casson Leighton lkcl at lkcl.net
Tue Feb 19 08:35:45 CET 2019

Rudi wrote:

> Curious: What is the cycle time of the generated code versus
> native verilog code ?

hi rudi, nice to see you around, i haven't forgotten you :)

it's exactly the same, as it's clk-based.

the layout however with nmigen is... well... it's auto-generated in
nmigen via yosys, where migen has its own built-in back-end, and goes
to some lengths to generate verilog that looks reasonably similar to
the migen it came from: that's gone in nmigen.

so, in nmigen, each signal that is assigned is separated out into its
own auto-generated code-section (migen keeps them all together).
resetting is auto-generated and need not be added manually.  declaring
a signal "resetless" may be done if it is definitely known that it
requires no reset; the value of the reset may be over-ridden at
declaration time: x = Signal(32, reset=0xf00baa)

in nmigen, all tests, subexpressions and consequently most assignments
are done with combinatorial auto-generated wires / registers (of the
correct / required / calculated / auto-calculated width), with the
original python line number and file name inserted as a comment next
to the expression assignment "\$483 = x[1:0] // foo.py:135"

in nmigen, all if elif elif elif statements are translated to casez
statements comprising a concatenated sequence of tests (derived from
the if part of every single if-elif-elif-elif statement), followed by
case 0b1zzzzzz case 0bz1zzzzzz case 0bzz1zzzzz and so on.

three quirks which are down to python:

* n/migen works through operator overloading of python syntax.
assignment of python variables does *not* have an operator over-ride,
so, annoyingly, an "eq" function had to be provided.  "m.d.sync +=
x.eq(y)" instead of "x = y", because "x = y" would make the *PYTHON*
variable x equal to y.

* operator-overloading of the "not", "and" and "or" operators
unfortunately returns a boolean rather than an object.  however the
"&" and "|" bit-wise operators, when overloaded, *do* actually return
the objects that are returned from the overload-function.
consequently, you *have* to do tests as "m.If((modulus[0:31] == 0x0) &
(exponent == -127))" of course including the brackets because & and |
operators take higher precedence.

* python slices go from *LSB* on the left to *MSB PLUS ONE* on the
right, where verilog goes from MSB (inclusive) *down* to LSB
(inclusive).  this so that you can do consecutive slices without
needing to subtract one: "x = x[y:] + x[:y]" is a barrel-shifter for
example.  note the lack of use of "y-1".

so... it's... quirky.  however, the prevalence and huge adoption of
python, the advantages of OO, the fact that to generate online
documentation i can use a single command from a 20-year-old
tried-and-tested program named "epydoc" (or any of its maaaany
Not-Invented-Here quotes replacements quotes), these for me *far*
outweigh the quirks and inconveniences.

on a separate note: multiply was added yesterday, along with the
original unit test process that jon dawson developed (which
hilariously was as python programs).  i cut out the use of a c++
program and replaced it with sfpy (berkeley softfloat 3 python

so, that's *all* of add, add64, div, div64, mul and mul64, all done,
all converted to nmigen, and all unit tests passing.

i've run up to 250,000 add and add64 random-input unit tests.

the reason for using sfpy is to avoid having to call out to a
subprocess (nmigen contains its own simulator), and also, sfpy
supports setting of different overflow modes, which is one of the next
things to add.

also, now we can add FP16, which is supported by sfpy, *without*
dependence on a particular feature of a c / c++ compiler (not all c /
c++ compilers *have* an FP16 type).  adding FP16 is literally a matter
of adding a few lines in the base class to tell it what to do when
FPAdd(16) is instantiated.


More information about the Libre-silicon-devel mailing list