PYNQ: PYTHON PRODUCTIVITY

Using Fixed-Point Data in Python

Hello,

I’m wondering for HLS overlays that use arbitrary precision fixed-point types, what is the proper way to format the data in Python? Say I have these types:

typedef ap_fixed<16, 6, AP_TRN_ZERO, AP_SAT> FIX_16;
typedef ap_fixed<8, 1, AP_TRN_ZERO, AP_SAT> FIX_8;

How do I define values in Python such that they are appropriate for passing into a function using these types as arguments? Does defining an np.uint8/np.uint16 work? Also, how does HLS handle doing operations with arguments of both FIX_16 and FIX_8 types (e.g. multiplying a FIX_16 and FIX_8 number into a FIX_16 output)?

Thanks in advance for any help.

My Best,
David

There’s no nice way for dealing with fixed-point types in Python. The easiest thing to do is to use a numpy array of the correct size and then wrap the conversion logic inside of a function where possible. If you are reading and writing registers you can also manually shift the result to get a floating point number. This is what we did for the colour-space conversion driver. In that case we had 12-bit fixed point numbers so we also needed to sign-extend the result to get the correct Python value.

Peter

2 Likes

Thanks for the response.

It seems that numpy arrays require a dtype for its members. So for example for FIX_8, I could use uint8 and then write a method for converting float to uint8, then load it into the numpy array? Does that sound about right?

That is correct. Fixed-points types are unfortunately difficult for numpy to support natively so we have to work around it.

If performance is a concern you can create the array of float and then do your assignment as an array operation.

buf[:] = a * (1 << FRAC_BITS)

Peter

1 Like

Sounds good, thanks a ton

You can look at my Fractal github for one way to do it, see juliabrot.py.

I am using 256 bit fixed point. Python does not have 256-bit precision without using additional modules, all of which were broken when I tried and no time to write my own :slight_smile: The technique I use here can however be applied to any number of bits as along as it is scaled correctly, see _to_fixed256() and _fixed256_to_int32_oct(). This also demonstrates how to pack the large fixed bit numbers into a 32-bit AXI stream. The data type on the HLS code (which is not open-source) used is ap_fixed<256,3>.

Kind regards

3 Likes

This is quite helpful for dealing with arbitrary precision in Python. Thanks a lot!

Glad you found it useful. Don’t get hung up on my weird integer clamp at +/- 3 in _to_fixed256(). The Mandelbrot set doesn’t have anything interesting going on outside that range. I was debugging the system and never went back and changed it to just saturate the fractional as well.