CHOICE - Chalmers Optical Fiber Channel Emulator

Department of Computer Science and Engineering

Chalmers University of Technology, SE-41296 Göteborg, Sweden

Chalmers University of Technology, SE-41296 Göteborg, Sweden

erikbor@chalmers.se

CHOICE is a system for digitally emulating a fiber-optic communication channel on an FPGA or ASIC. An FPGA implementation of the system can be used to reduce simulation times of DSP implementations where e.g. low bit-error rate data is needed. A git repo containing the system components is available at http://www.cse.chalmers.se/~erikbor/choice/choice.git.

Date | Version | Content |
---|---|---|

2019-07-04 | 0.1 | First version |

2019-09-20 | 1.0 | Added support for changing awgn and phase noise scaling at run time. |

2020-09-01 | 1.1 | Rewrite of UART component for improved long-term stability |

2021-01-10 | 1.2 | Update phase noise component scaling factor to support smaller linewidth symbol-duration products. |

Verification and testing of complex digital signal processing (DSP) implementations becomes time consuming as the implementations become more complex, especially for designs where low bit-error rate (BER) data is needed. Such designs can be e.g. forward error correction decoders or detection of cycle slips for carrier phase recovery algorithms. The standard way of testing an algorithm implemented in a hardware description language (HDL) is through MATLAB-HDL co-simulation, where the input data is generated in MATLAB, processed by a simulation of the HDL implementation and imported back into MATLAB for post-processing. For low-BER simulations the runtime of these simulations can, however, easily become prohibitively long.

The Chalmers Optical Fiber Channel Emulator (CHOICE), originally presented in [1], aims to reduce the runtime of low-BER simulations by using an FPGA or ASIC to *digitally emulate the fiber impairments*, using fixed-point representations for all signals. CHOICE generates a long pseudo-random bitstream, modulates it and adds emulated fiber impairments in actual hardware. This emulation can, in conjunction with running the DSP implementation on the same hardware, reduce the simulation time by multiple orders of magnitude.

Currently, CHOICE consists of a pseudo-random bitstream generation component (*rng*), a modulation component (*modulator*), an additive white Gaussian noise generator (*awgn*), a phase noise generator (*phase_noise*), a demodulator (*demodulator*) and an error counter (*error_counter*). All applicable components can be parallelized and the parallelism, i.e. the number of symbols processed per clock cycle, is set by the PAR generic in each parallelizable component. The size of each component is kept as small as possible to be able to test large DSP systems or to run multiple instances of CHOICE simultaneously. Supplied with the package is also a UART controller (*uart*) and system examples. The latter can be found in the `src/examples` folder. For the future, the plan is to develop the emulator further by adding more fiber impairments and further developed analysis tools.

CHOICE is written by Erik Börjeson from the Department of Computer Science and Engineering at Chalmers University of Technology and is released under version 3 of the GNU General Public License (GPLv3). If you use CHOICE in your work, please cite [1].

The git repo can be cloned by doing

`git clone http://www.cse.chalmers.se/~erikbor/choice/choice.git`.

To also include the Gaussian noise generator (GNG) IP core used, do

`git clone --recurse http://www.cse.chalmers.se/~erikbor/choice/choice.git`.

More information about the GNG IP core can be found under the description of the AWGN component.

This section describes the components of the CHOICE system and how to use them. An example of how a CHOICE system can be implemented is shown in Fig. 1. The binary data is generated in the *rng* component and fed to the *modulator* component. AWGN and phase noise are added and symbols are fed to the *dsp* unit under test. The output from the tested unit is demodulated and the errors are counted. Each component takes both the modulated signal (`i_in` and `q_in`) as well as the transmitted bits (`bits_in`) as inputs. The bits are delayed to match the symbols so that the output of each block contains both the symbols (`i_out` and `q_out`) and the matching transmitted bits (`bits_out`). Valid signals (`valid_in` and `valid_out`) are used to tell the next block in the chain when processing can begin.

An example of how a complete system can be implemented is provided in `src/examples/emulator.vhdl` with a testbench provided in `src/examples/testbench.vhdl`. It uses an example DSP component (`src/examples/dsp.vhdl`), that adds a delay to the symbol stream.

A second example of the same type of system is provided in `src/examples/emulator_uart.vhdl`, which illustrates the use of the *uart* component. This component is a UART controller that can be used to transfer data from the FPGA to e.g. a computer for later analysis. The example *com* component illustrate how the settings can be transmitted to the emulation systems and results from the *error_counter* block can be sent back. Setting of the SNR and phase noise scaling is done by sending `0x01 0xXXXX 0xYYYY`, where XXXX is the SNR scaling and YYYY is the phase noise scaling. Both XXXX and YYYY are 16-bit values that can calculated using the scripts in the `scripts` folder.
The scaling factors used in the emulator are not updated until the simulation is reset by sending `0x00`. To read the counter data from the emulator send `0x02`. The *com* component will respond with 16 bytes, where the first 8 represents the bit counter and the last 8 bytes represents the error counter.

Data generation is handled in the *rng* component using the Xorshift random number generation (RNG) method described by Marsaglia [2]. The *rng* component works as a wrapper for the *xorshift* component to generate the needed number of Xorshift RNGs. It also control the generation of the `valid_out` signal. The periodicity of the pseudo-random bit sequence is 2^{64}-1 for an Xorshift register wordlength (N) of 64. The *rng* component is declared as

component rng is
generic (BITS : positive;
N : positive;
A : positive;
B : positive;
C : positive;
SEED : std_logic_vector);
port (clk : in std_logic;
rst : in std_logic;
bits_out : out std_logic_vector;
valid_out : out std_logic);
end component rng;

Generic | Function |
---|---|

BITS | Number of bits to be generated each clock cycle. |

N | Width of the register in the Xorshift RNGs. |

A, B, C | Shift steps for the Xorshift RNGs, see [2] for suggested values. |

SEED | Seed for the Xorshift RNGs, needs to have a wordlength of N·ceil(BITS/N). |

Filename | Function |
---|---|

src/datagen/rng.vhdl | Main rng component source. |

src/datagen/xorshift.vhdl | Subcomponent used to instantiate the Xorshift RNGs. |

Modulation of the bitstream generated in the *rng* is handled by the *modulator* component. Currently, these modulation formats are supported:

- BPSK
- QPSK
- 16QAM
- 64QAM
- 256QAM

component modulator is
generic (PAR : positive;
BITS : positive;
MOD_BITS : positive;
MOD_TYPE : string;
WIDTH : positive;
MAX_AMP : real);
port (clk : in std_logic;
rst : in std_logic;
bits_in : in std_logic_vector;
valid_in : in std_logic;
i_out : out std_logic_vector;
q_out : out std_logic_vector;
bits_out : out std_logic_vector;
valid_out : out std_logic);
end component modulator;

Generic | Function |
---|---|

PAR | Parallelization factor of the modulator. |

BITS | Wordlength of the bits_in input. |

MOD_BITS | Number of bits encoded on each symbol. |

MOD_TYPE | Modulation format (BPSK, QPSK, 16QAM, 64QAM, 256QAM) |

WIDTH | Wordlength of the modulated symbols. |

MAX_AMP | Fraction of the i_out and q_out signals to use for the symbol with the largest amplitude (0.0 - 1.0). |

Filename | Function |
---|---|

src/mod/modulator.vhdl | Main modulator component source code. |

src/mod/mod_BPSK.vhdl | Subcomponent for BPSK modulation. |

src/mod/mod_QPSK.vhdl | Subcomponent for QPSK modulation. |

src/mod/mod_16QAM.vhdl | Subcomponent for 16QAM modulation. |

src/mod/mod_64QAM.vhdl | Subcomponent for 64QAM modulation. |

src/mod/mod_256QAM.vhdl | Subcomponent for 256QAM modulation. |

The *awgn* component adds additive white Gaussian noise to the input I/Q symbols. The noise amplitude is calculated from the generic constants EB and SNR. The component use an Gaussian noise generator IP core, written by Guangxi Liu, based on the principle presented in [3]. The noise generator is available at GitHub under the GNU Lesser General Public License (LGPLv2.1). The periodicity of the GNG is approximately 2^{175}. If you have already cloned the CHOICE repo and want to download the GNG to the appropriate folder in the repo, do `git submodule init` followed by `git submodule update` in the CHOICE repo.

Seeds for the two GNG generators needed on each parallel lane for the I and Q signals are taken from the *seeds_pkg* package, located in the `src/support/` folder. Each GNG instance needs three 64-bit vectors as seeds and the *awgn* and *phase noise* components are designed to read different seeds from the package. A new seed file can be generated by the `scripts/seeds.py` script, which takes the number of 64-bit vectors needed as an argument. The 16-bit *scaling* input signal sets the generated SNR and can be calculated using `scripts/snr_scaling.py`. A component declaration of the *awgn* component is shown below.

component awgn is
generic (PAR : positive;
WIDTH : positive;
BITS : positive);
port (clk : in std_logic;
rst : in std_logic;
i_in : in std_logic_vector;
q_in : in std_logic_vector;
bits_in : in std_logic_vector;
valid_in : in std_logic;
scaling : in std_logic_vector;
i_out : out std_logic_vector;
q_out : out std_logic_vector;
bits_out : out std_logic_vector;
valid_out : out std_logic);
end component awgn;

Generic | Function |
---|---|

PAR | Parallelization factor. |

WIDTH | Wordlength of the I/Q inputs and outputs. |

BITS | Wordlength of the bits_in and bits_out ports. |

Filename | Function |
---|---|

src/channel/awgn.vhdl | Main awgn component source code. |

ip/gng/ | Path to the Gaussian noise generator IP. |

src/support/seeds_pkg.vhdl | Package containing seeds for the GNG. |

CHOICE emulates the phase noise as a Weiner process, using the GNG IP core described above to simulate the random walk. One GNG component is needed for each parallel lane, and the seeds are fetched from the *seeds_pkg* package. The parallelization of the *phase_noise* component is currently the limiting factor for the parallelization of the complete system. This limitation is due to the feedback loop present in the implementation, shown in Fig. 2. The 16-bit *scaling* input signal sets the generated phase noise and can be calculated using `scripts/pn_scaling.py`. A component declaration template for the *phase_noise* component is shown below.

component phase_noise is
generic (PAR : positive;
WIDTH : positive;
BITS : positive;
PHASE_WIDTH : positive;
LUT_WIDTH : positive);
port (clk : in std_logic;
rst : in std_logic;
i_in : in std_logic_vector;
q_in : in std_logic_vector;
bits_in : in std_logic_vector;
valid_in : in std_logic;
scaling : in std_logic_vector;
i_out : out std_logic_vector;
q_out : out std_logic_vector;
bits_out : out std_logic_vector;
valid_out : out std_logic);
end component phase_noise;

Generic | Function |
---|---|

PAR | Parallelization factor. |

WIDTH | Wordlength of the I/Q inputs and outputs. |

BITS | Wordlength of the bits_in and bits_out ports. |

PHASE_WIDTH | Wordlength of the register storing the current phase rotation. |

LUT_WIDTH | Wordlength of the LUT storing rotation vectors. |

Filename | Function |
---|---|

src/channel/awgn.vhdl | Main awgn component source code. |

src/channel/ang_to_iq.vhdl | Component used to convert an angle to an I/Q representation. |

ip/gng/ | Path to the Gaussian Noise Generator IP. |

src/support/seeds_pkg.vhdl | Package containing seeds for the GNG |

Demodulation of the symbols is performed by the *demodulator* component, supporting the same modulation formats as the *modulation* component. It instantiates the chosen demodulator and outputs a demodulated bitstream. When using BPSK, the `q_in` input signal can be connected to a constant. Shown below is an example component declaration of the *demodulator*.

component demodulator is
generic (PAR : positive;
BITS : positive;
MOD_BITS : positive;
MOD_TYPE : string;
WIDTH : positive;
MAX_AMP : real);
port (clk : in std_logic;
rst : in std_logic;
i_in : in std_logic_vector;
q_in : in std_logic_vector;
bits_in : in std_logic_vector;
valid_in : in std_logic;
demod_out : out std_logic_vector;
bits_out : out std_logic_vector;
valid_out : out std_logic);
end component demodulator;

Generic | Function |
---|---|

PAR | Parallelization factor of the demodulator. |

BITS | Wordlength of the bits_in input. |

MOD_BITS | Number of bits encoded on each symbol. |

MOD_TYPE | Modulation format (BPSK, QPSK, 16QAM, 64QAM, 256QAM) |

WIDTH | Wordlength of the modulated symbols. |

MAX_AMP | Fraction of the i_out and q_out signals to use for the symbol with the largest amplitude (0.0 - 1.0). |

Filename | Function |
---|---|

src/demod/demodulator.vhdl | Main demodulator component source code. |

src/demod/demod_BPSK.vhdl | Subcomponent for BPSK demodulation. |

src/demod/demod_QPSK.vhdl | Subcomponent for QPSK demodulation. |

src/demod/demod_16QAM.vhdl | Subcomponent for 16QAM demodulation. |

src/demod/demod_64QAM.vhdl | Subcomponent for 64QAM demodulation. |

src/demod/demod_256QAM.vhdl | Subcomponent for 256QAM demodulation. |

The output from the demodulator can be connected to the *error_counter* component. This component counts the number of processed bits and the number of errors detected. The output can be used to analyze the resulting BER. It can be read using a debugger such as Xilinx ChipScope or Intel Quartus SignalTap (pdf). It can also be output from the FPGA using the UART controller provided with CHOICE (`src/uart/uart.vhdl`), or using other protocols. The component is declared as shown below.

component error_counter is
generic (BITS : positive;
BITS_CNT_WIDTH : positive;
ERRORS_CNT_WIDTH : positive);
port (clk : in std_logic;
rst : in std_logic;
input0 : in std_logic_vector;
input1 : in std_logic_vector;
valid_in0 : in std_logic;
valid_in1 : in std_logic;
bits_cnt : out std_logic_vector;
errors_cnt : out std_logic_vector);
end component error_counter;

Generic | Function |
---|---|

BITS | Wordlength of the bits_in input. |

BITS_CNT_WIDTH | Wordlength of the bits counter, maximum value of the counter is 2^{BITS_CNT_WIDTH}-1. |

ERROR_CNT_WIDTH | Wordlength of the error counter, maximum value of the counter is 2^{ERROR_CNT_WIDTH}-1. |

Filename | Function |
---|---|

src/analysis/error_counter.vhdl | Main error_counter component source. |

The emulator have been successfully implemented as an 22 nm ASIC design and on the following FPGA development boards:

[1] E. Börjeson et al., "Towards FPGA Emulation of Fiber-Optic Channels for Deep-BER Evaluation of DSP Implementations," presented at Signal Processing in Photonic Communications (SPPCom), San Francisco, CA, USA, pp. SpTh1E.4, Aug 2019.

[2] G. Marsaglia, "Xorshift RNGs,"*Journal of Statistical Software*, vol. 8, no. 14, pp. 1-6, 2003.

[3] R. Gutierrez et al., "Hardware Architecture of a Gaussian Noise Generator Based on the Inversion Method,"*IEEE Transactions on Circuits and Systems II: Express Briefs*, vol. 59, no. 8, pp. 501-505, Aug 2012.

[2] G. Marsaglia, "Xorshift RNGs,"

[3] R. Gutierrez et al., "Hardware Architecture of a Gaussian Noise Generator Based on the Inversion Method,"