CHOICE - Chalmers Optical Fiber Channel Emulator
Erik Börjeson
Department of Computer Science and Engineering
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.
Change Log
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.

Introduction

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.

License

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].

Cloning the Git Repo

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.

Components

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.

Block diagram of an example system.
Block diagram of an example system.

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.

Random Data Generation

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 264-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;
Generics and their functions for the rng component.
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).
Source files for the rng component.
Filename Function
src/datagen/rng.vhdl Main rng component source.
src/datagen/xorshift.vhdl Subcomponent used to instantiate the Xorshift RNGs.

Modulation

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

The modulator component instantiates the chosen modulator and outputs a modulated I/Q signal. When using BPSK, the q_out signal can be ignored. A component declaration template is shown below.

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;
Generics and their functions for the modulator component.
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).
Source files for the modulator.
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.

Additive White Gaussian Noise

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 2175. 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;
Generics and their functions for the awgn component.
Generic Function
PAR Parallelization factor.
WIDTH Wordlength of the I/Q inputs and outputs.
BITS Wordlength of the bits_in and bits_out ports.
Source files and IP for the awgn component.
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.

Phase Noise

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;
Block diagram of the phase noise generator component.
Block diagram of the phase noise generator component [1].
Generics and their functions for the phase noise emulator.
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.
Source files and IP for the phase noise emulator
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

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;
Generics and their function for the 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).
Source files for the demodulator.
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.

Error Counter

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;
Generics and their function for the 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 2BITS_CNT_WIDTH-1.
ERROR_CNT_WIDTH Wordlength of the error counter, maximum value of the counter is 2ERROR_CNT_WIDTH-1.
Source files for the error counter.
Filename Function
src/analysis/error_counter.vhdl Main error_counter component source.

Development Boards

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

References

[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.