Addatone is a Eurorack oscillator using additive synthesis. It is a two channel oscillator with channel one outputting fundamental and odd harmonics and channel two fundamental and even harmonics.
Each channel has potentiometer and CV control over the number of harmonics with higher harmonics progressively attenuated. In addition a 'Count' pot and CV input hard-limit the total number of audible harmonics. A warp control progressively detunes the odd harmonics up and even harmonics down.
Channel one has a mix switch which allows a mix of both channels to be output from channel one. Channel two has a multiply switch which allows a multiple of both channels to be output from channel two (ie a ring modulation effect).
The final controls are an octave potentiometer covering a five octave range and a fine tune control covering around an octave.
The core sound generator is an ICE40UP5K FPGA from Lattice. This is responsible for generating the harmonics, mixing/multiplying and then outputting to the DAC via I2S protocol.
The module also contains an STM32F446 microcontroller which holds the FPGA bitstream in internal flash memory; this is used to program the FPGA on start-up via SPI. In addition the STM32 is used as an ADC to digitise and scale all potentiometer and CV control data. This is periodically transferred to the FPGA via SPI. The STM32 is clocked from an external 12MHz crystal. This is also used to drive the FPGA clock via the STM32 MCO (Master Clock Output) peripheral.
The DAC is a Texas Instruments PCM5100APW, operating 2 channels at 16 bits, 48kHz. Two op-amps are used to scale input CV voltages and to amplify the DAC output to Eurorack levels (8V peak to peak).
A look-up table in block RAM (Sine_LUT) holds 2048 16 bit samples containing a single cycle sine wave. Individual harmonics are generated by traversing the LUT at a speed relative to the harmonic frequency.
The SamplePos_RAM 256 x 16 bit look-up table holds the current Sine LUT position of each harmonic. This position is advanced according to the harmonic's frequency at each sample and the sine sample is scaled and added to an accumulator. The initial values of the sample position LUT are pre-calculated to minimise 'crest factor'. If each harmonic started at position 0 of a sine wave then peaks and troughs would coincide to create a wave with an amplitude greater than the dynamic range of the system.
A graphical javascript tool to visualise and calculate minimal crest factor sample positions is available here: https://htmlpreview.github.io/?https://raw.githubusercontent.com/dchwebb/Addatone/master/SamplePosLUT/SineLUT.htm
The FPGA receives its control data from the ADCs processed in the STM32. These are periodically sent via SPI and decoded using a custom peripheral (ADC_SPI_In).
The DAC_I2S module is a custom peripheral used to dispatch the final sample values to the DAC via the I2S protocol.
The 12MHz clock received from the STM32 MCO is scaled to the ICE40's maximum clock frequency of 48MHz.
The ICE40 bitstream is developed in Lattice's Radiant IDE. Full source code here: https://github.com/dchwebb/Addatone/tree/master/Addatone_ICE40
The module includes an STM32F446 MCU that carries out various helper roles. The firmware was developed in STM's CubeIDE and available here: https://github.com/dchwebb/Addatone/tree/master/Addatone_STM32F446
The FPGA bitstream is stored in the MCU's flash memory and is programmed into the FPGA at startup using an STM32 SPI peripheral (see fpgaHandler::ProgramBitstream method). Once the FPGA is programmed the ADC data derived from external CV control and potentiometers is processed, scaled and transmitted in a periodic packet to the FPGA via SPI (see fpgaHandler::SendControls method).