Verilog Hacks
=============

Because of the limited capability of FPGA computation, compromises often need to
made in the actual Verilog implementation. The most used techniques include
quantization and look up table. In |project|, these approximations are used.


Magnitude Estimation
--------------------

- **Module**: :file:`complex_to_mag.v`
- **Input**: ``i (32), q (32)``
- **Output**: ``mag (32)``

In the ``sync_short`` module, we need to calculate the magnitude of the
``prod_avg``, whose real and imagine part are both 32-bits. To avoid 32-bit
multiplication, we use the `Magnitude Estimator Trick from DSP Guru
<https://dspguru.com/dsp/tricks/magnitude-estimator/>`_. In particular, the
magnitude of complex number :math:`\langle I, Q\rangle` is estimated as:

.. math::

    M \approx \alpha*max(|I|, |Q|) + \beta*min(|I|, |Q|)

And we set :math:`\alpha = 1` and :math:`\beta = 0.25` so that only simple
bit-shift is needed.

.. _fig_complex_to_mag_wave:
.. figure:: /images/complex_to_mag_wave.png
    :align: center

    Waveform of ``complex_to_mag`` Module

:numref:`fig_complex_to_mag_wave` shows the waveform of the ``complex_to_mag``
module. In the first clock cycle, we calculate ``abs_i`` and ``abs_q``. In the
second cycle, ``max`` and ``min`` are determined. In the final cycle, the
magnitude is calculated.


.. _sec_phase:

Phase Estimation
----------------

- **Module**: :file:`phase.v`
- **Input**: ``i (32), q (32)``
- **Output**: ``phase (32)``
- **Note**: The returned phase is scaled up by 512 (i.e., :math:`int(\theta *512)`)

When correcting the frequency offset, we need to estimate the phase of a complex
number. The *right* way of doing this is probably using the `CORDIC
<https://dspguru.com/dsp/faqs/cordic/>`_ algorithm. In |project|, we use look up
table.

More specifically, we calculate the phase using the :math:`arctan` function. 


.. math::

    \theta = \angle(\langle I, Q\rangle) = arctan(\frac{Q}{I})

The overall steps are:

1. Project the complex number to the :math:`[0, \pi/4]` range, so that the
   :math:`tan(\theta)` range is :math:`[0, 1]`.
#. Calculate :math:`arctan` (division required)
#. Looking up the quantized :math:`arctan` table
#. Project the phase back to the :math:`[-\pi, \pi)` range

Here we use both quantization and look up table techniques.

Step 1 can be achieved by this transformation:

.. math::

    \langle I, Q\rangle \rightarrow \langle max(|I|, |Q|), min(|I|, |Q|)\rangle


In the lookup table used in step 3, we use :math:`int(tan(\theta)*256)` as the
key, which effectively maps the :math:`[0.0, 1.0]` range of :math:`tan` function
to the integer range of :math:`[0, 256]`. In other words, we quantize the
:math:`[0, \pi/4]` quadrant into 256 slices.

This :math:`arctan` look up table is generated using the
``scripts/gen_atan_lut.py`` script. The core logic is as follows:

.. code-block:: python
    :linenos:

    SIZE = 2**8
    SCALE = SIZE*2
    data = []
    for i in range(SIZE):
        key = float(i)/SIZE
        val = int(round(math.atan(key)*SCALE))
        data.append(val)


Note that we also scale up the :math:`arctan` values to distinguish adjacent
values. This also systematically scale up :math:`\pi` in |project|. In fact,
:math:`\pi` is defined as :math:`1608=int(\pi*512)` in
:file:`verilog/common_params.v`.

The generated lookup table is stored in the ``verilog/atan_lut.coe``
file (see `COE File Syntax
<https://www.xilinx.com/support/documentation/sw_manuals/xilinx11/cgn_r_coe_file_syntax.htm>`_).
Refer to `this guide
<https://www.xilinx.com/itp/xilinx10/isehelp/cgn_p_memed_single_block.htm>`_ on
how to create a look up table in Xilinx ISE. The generated module is stored in
:file:`verilog/coregen/atan_lut.v`.



.. _rotate:

Rotation
--------

- **Module**: :file:`/verilog/rotate.v`
- **Input**: ``i (16), q (16), phase (32)``
- **Output**: ``out_i (16), out_q (16)``
- **Note**: The input phase is assumed to be scaled up by 512.

To rotate a complex number :math:`C=I+jQ` by :math:`\theta` degree, we can
multiply it by :math:`e^{j\theta}`, as shown in :eq:`eq_rot`.

.. math::
    :label: eq_rot

    C' = (I+jQ)\times(\cos(\theta)+j\sin(\theta))

Again, this can be done using the CORDIC algorithm. But similar to
:ref:`sec_phase`, we use the look up table.


.. _fig_quadrant:
.. figure:: /images/quadrant.png
    :align: center
    :scale: 60%

    Quadrant in I/Q Plane

As shown in :numref:`fig_quadrant`, we split the I/Q plane into 8 quadrants,
:math:`\pi/4` each. To avoid storing nearly duplicate entries in the table, we
first map the phase to be rotated (:math:`[-\pi, \pi]`) into the :math:`[0,
\pi/4]` range. Next, since the incoming phase is scaled up by 512, each quadrant
is further split into :math:`402=int(\pi/4*512)` sectors. And the
:math:`\cos(\theta)` and :math:`\sin(\theta)` values (scaled up by 2048) are
stored in the look up table. The table is generated by the
:file:`scripts/gen_rot_lut.py`.
