- Author: Uri Shaked
- Description: Calculates `z = z^2 + c` on every clock cycle using 32-bit IEEE 754 floating point arithmetic.
- GitHub repository
- Clock: 20000000 Hz

A mandelbrot set is a set of complex numbers that satisfy a certain mathematical property. The set is defined by iterating a function on a complex number, and checking if the result of the iteration is bounded. If the result is bounded, the complex number is part of the mandelbrot set. If the result is unbounded, the complex number is not part of the mandelbrot set.

This project calculates the function `z = z^2 + c`

iteratively, where `z`

and `c`

are complex numbers. The function is iterated on
every clock cycle, and the result is checked to see if it is bounded (`|z| <= 2`

). When the result is unbounded, the `unbounded`

signal is set high, and the `iter`

signal is set to the number of iterations it took for the result to become unbounded.

All the calculations are done in 32-bit IEEE 754 floating point format. The floating point addition code is taken from Caravel FPU, and the floating point multiplication code was generated by GPT-4o.

Load the value of the complex number `c`

that you want to test into the `Cr`

and `Ci`

registers. Each register holds a 32-bit IEEE 754 floating point number. The value of `c`

is `Cr + Ci * i`

, where `i`

is the imaginary unit.

The registers are shifted in LSB first, 8 bits at a time. When shifting the last byte, the corresponding `load`

signal should be set high to load the value into the register.

For example, to load the real part of `c`

into `Cr`

, you would need four clock cycles:

- Set
`data_in`

to the least significant byte (`Cr[7:0]`

) - Set
`data_in`

to the second byte (`Cr[15:8]`

) - Set
`data_in`

to the third byte (`Cr[23:16]`

) - Set
`data_in`

to the most significant byte (`Cr[31:24]`

), and set`load_Cr`

high to load the value into the register.

Do the same for the imaginary part of `c`

and `Ci`

. In case you want to load the same value into both `Cr`

and `Ci`

, you can set `load_Cr`

and `load_Ci`

high at the same time.

Strobe the `start`

signal to begin the calculation. The design will iterate the function `z = z^2 + c`

, one iteration per clock cycle. When the result is unbounded, the `unbounded`

signal will be set high. For numbers that are part of the mandelbrot set, the `unbounded`

signal will remain low as the design iterates the function.

The values of `Cr`

and `Ci`

are buffered, so you can load new values into the registers while the design is calculating the mandelbrot set for the previous values. When you strobe the `start`

signal, the design will begin calculating the mandelbrot set for the new values of `c`

.

The following example illustrates how to load the value `c = 1.2 + 1.4i`

into the registers and start the calculation:

Clock | `data_in` |
`load_Cr` |
`load_Ci` |
`start` |
`unbounded` |
---|---|---|---|---|---|

1 | 0x9A | 0 | 0 | 0 | 0 |

2 | 0x99 | 0 | 0 | 0 | 0 |

3 | 0x99 | 0 | 0 | 0 | 0 |

4 | 0x3F | 1 | 0 | 0 | 0 |

5 | 0x33 | 0 | 0 | 0 | 0 |

6 | 0x33 | 0 | 0 | 0 | 0 |

7 | 0xB3 | 0 | 0 | 0 | 0 |

8 | 0x3F | 0 | 1 | 1 | 0 |

9 | 0x00 | 0 | 0 | 0 | 0 |

10 | 0x00 | 0 | 0 | 0 | 1 |

Where:

- 0x3F99999A is the IEEE 754 floating point representation of the real part of
`c`

(1.2) - 0x3FB33333 is the IEEE 754 floating point representation of the imaginary part of
`c`

(1.4) - Unbounded goes high two clock cycles after the start signal is strobed, indicating that the result is unbounded.

When the calculation concludes, the `iter`

signal will hold the number of iterations it took for the result to become unbounded. The `iter`

signal is valid when the `unbounded`

signal is set high.

None

# | Input | Output | Bidirectional |
---|---|---|---|

0 | start | unbounded | data_in[0] |

1 | load_Cr | iter[0] | data_in[1] |

2 | load_Ci | iter[1] | data_in[2] |

3 | iter[2] | data_in[3] | |

4 | iter[3] | data_in[4] | |

5 | iter[4] | data_in[5] | |

6 | iter[5] | data_in[6] | |

7 | iter[6] | data_in[7] |