Using HDL Coder WFA to implement a distortion effect.

If you have read some posts on this blog, you can see that FPGAs are a very good choice if you want to develop some digital signal processing algorithm, but for some reason, maybe because they are (a bit) more complex than DSP, FPGAs they are not as popular as them. The advantages of using FPGA instead of DSP are many, starting with the parallelism that FPGAs natively allow, until the data types in FPGAs are not predefined, and we can operate using 24-bit signals, for example for audio. applications, no underused bits in registers.

One of the tools that MATLAB give us is HDL Coder. This tool allows us to develop an algorithm on Simulink, and then migrate it to an HDL, on the same way that we have seen on the line filtering project, but the difference is that in this case, we will migrate an entire Simulink model. This makes to develop any signal processing algorithm very easy. But, as FPGA developers, we know that the first before process the signal, is to acquire the signal. At this point, all of us have to work hard, first to understand what is exactly what the ADC waits on its input port, and then, understand the way that the ADC will return us the value of its analog channels. In other words, we have to be able to communicate with the ADC, whatever be the protocol. In order to implement the communications protocol, the option is clear, at least for me, the easiest way is to implement it on VHDL or Verilog. So, to develop an entire project for digital signal processing with HDL Coder, first we have to develop the communications protocol on Verilog or VHDL, then, migrate the algorithm from Simulink to HDL, and last, integrate all. MATLAB allow us to develop this entire workflow inside him, with the tool HDL Coder and Workflow Advisor.

On this post we are going to develop an audio processing algorithm. In this case we will develop a distortion effect to modify the sound of a guitar. The configuration of the effect will be managed through an AXI4 Lite bus. To acquire the sound signal, we are going to use the I2S2 PMOD from Digilent. This board is based on an Audio DAC and an Audio ADC. These two devices will communicate with the FPGA through a I2S protocol. The project will be implemented on a Picozed 7015 board, with the FMC Carrier board ver. 2.

Let’s first take a look at the effect we want to achieve. Distortion effect, in some places called override, is the effect of saturate the audio amplifier, by increasing the volume (amplitude), of the signal, converting the sinusoidal signal of the guitar, to almost square signal. The transition between the sinus wave to the flat part, or clipping, of the signal could be soft or hard, and this is related with the kind of amplifier. The old amplifiers based on valves, makes this transition on a soft way, but the newer transistor-based amplifiers achieve a much tougher transition.

Regarding the harmonic content, we can see first that soft signal has a dc content, but this is due to the window selected, so we can ignore this. Next, we can see a high 3th harmonic in both signals, and the rest of the harmonics attenuated.

In order to model this effect, I am going to use, a gain followed of a saturation, and then, I will implement a 4th order FIR filter, which cut frequency will be configurable. This configurable filter will allow us to modify the response of the effect from a hard clipping (higher cut frequency), to soft clipping (low cut frequency). We will ensure that the lower cut frequency has to be enough to keep the guitar frequencies with the minimum change possible.

This project will be based on a Simulink model, which later we will migrate to HDL. First, we need to generate a new Simulink model, and the next is acquire audio data to process it. In order to acquire the audio data, we will use the I2S2 Pmod from Digilent. We can find the driver to the I2S2 Pmod on Digilent’s Github. The driver designed by Digilent uses an AXI Stream protocol to send data outside the driver but, even AXI Stream protocol is easy enough to be implemented directly on the model, we are going to design a Verilog module to read the driver’s AXI Stream output, and generate 2 outputs with the stereo data acquired. Also, the module will implement the encoder to send data to the DAC, so our Verilog module will include also 2 inputs to send processed data.

In order to include Verilog modules in our Simulink model we have to include in our model a subsystem, and add to this subsystem model only the inputs and output corresponding with the Verilog top module, except clock input, clock enable input and reset. The name of this subsystem has to be the same as the Verilog module used.

Once we have the subsystem with all the inputs and outputs added, on the top model we have to change its architecture to Blackbox, through right click on subsystem, and HDL Code > HDL Block Properties. On the fields of this window, we have to select whether the module has Clock enable port, Clock port or Reset port.

Once we have the Verilog module “instantiated” on the Simulink model we have to add the rest of the components. First, we will generate a new subsystem to package the FIR filter. As I said before, I have implemented a 4th order FIR filter with a Direct-Form structure. When we add all the components, we have to ensure that the output format of each element is signed, with a width of 24 and a fractional width of 23, in MATLAB language fixdt(1,24,23). Also, we have to keep in mind that the delays of the filter will have the same clock signal of the entire system. In this case, clock signal is determined by the Verilog module, that needs a clock of 22.6MHz. Furthermore, Verilog module will provide a valid output at 44Ksps, so we need to reduce the clock speed of the FIR module. To do that, we can use delay blocks with enable input, that is essentially a clock enable signal for the delay, and on this port, we are going to connect a pulse signal. This signal has to be true only one clock cycle every 44Khz.

Other option is adding an enable block and configure it to hold the signals when enabling. This block generates a new input port on the subsystem. This option will make that all the subsystem will be execute when the signal on the enable port be greater than 0.

Once we have the FIR filter built, we have to create a new subsystem to package the entire distortion effect. As I shown you before, the model of the distortion effect will be built with a gain, followed by a saturation, and then the output will be filtered with the FIR filter designed. Also, I added at the output a switch with which we can enable or disable the effect. The entire effect model is shown on the next figure. Also, the enable signal will be designed on this subsystem with an HDL counter and a comparation operator. This counter will be configured as a Free Running counter with an output signal of 9 bits, that is corresponding to 22.6e6/512 = 44.1 kHz.

Next on a top subsystem we will make all connections between the effect generator and the ‘instantiated’ Verilog module. Also, we will create all the outputs and inputs of the model, that are, the configuration ports we are going to configure through AXI4 Lite, which have a width of 32 bits, so, we have to convert these values to fixdt(1,24,23) format, and also the digital input and output ports to manage the PMOD, which have Boolean format. Besides the configuration and input/output ports, I also have generated 2 outputs for debug, which are output_effect, to verify the behavior of the algorithm we have created, and the input to this algorithm which is corresponding with the L channel of the I2S controller.

Finally, the top level of the model, which also contains all the inputs and outputs that we will want to connect.

Once we have our model created, we can start with HDL Workflow Advisor. The HDL Workflow Advisor, according its webpage, offers a workflow so that you can check your algorithm for HDL compatibility, generate HDL code, verify the code, and then deploy the code to your target platform. This tool uses several tools, one of these we have already seen on the blog, the FPGA Data Capture, but not only allow to read information from the FPGA, but also allows to write on AXI4 Lite registers through the IP JTAG to AXI Master. To run HDL Workflow Advisor, first we have to open HDL Coder application from APPS tab, and then, click on Workflow Advisor button.

Once executed, a new window will be opened to configure the workflow we want to perform, in this case IP Core Generation, and the target platform. In my case, I going to use a Picozed 7015 board that previously I have installed. The rest of the configurations will remain by default.

On the next step we have to turn on the option Insert JTAG MATLAB as AXI MASTER. This option will allow us to configure out IP through AXI4 Lite bus.

Next, on Target Interface we have to define which kind of interface we want to use for each port. In case of the PMOD ports, we have to write the corresponding FPGA pin with the format {’PIN’}. Signals we want to connect o to FPGA data capture, the interface we have to select is FPGA Data capture – JTAG.

Another option to add a signal to FPGA Data Capture is by right click on the wire, and enable Test point.

On the next step we have to select the clock frequency of our design. As we are using the Verilog Module from Digilent, we have to select a clock compatible with this module, and in this case, the module is designed to work at 22.59 Mhz, so the target Frequency selected will be configured on 22.6 MHz.

Next, we will jump to the point 3.1.1, where we will configure Verilog as the language of the design.

The next point we will configure is the point 3.2, where we will add the corresponding Verilog sources, and we will select the buffer size for the FPGA Data Capture. Also, we will check the box to enable the readback of the write registers. This option is only for debug in our case, to ensure that the written value is correct.

Before continue, we have to delete the second line on the file axis_i2s2.v, since the code generated by MATLAB is not compatible with this line.

`default_nettype none

Finally, right click on the 3.2. point and click on Run to selected task.

If all was good, all the steps will be executed without errors. The next is create the Vivado project, generate and save the software interface, and generate the bitstream. All these steps with the default options.

Finally, when the bitstream is generated, we can also program the board from this window.

Now, we will verify the design. To write the AXI4 lite registers, I have generated this script that writes all the values with the selected. Filter coefficients are calculated with fir1 command.

close all
clear all
clc
 
%% Fir taps compute
 
taps = fir1(4,0.000001) 

%% AXI registers configuration
h = aximaster('Xilinx')
 
enable = 1;
 
% FIR configuration
b0 = taps(1);
b1 = taps(2);
b2 = taps(3);
b3 = taps(4);
b4 = taps(5);
 
% Convert to fixed point
b0_qq = floor(b0*2^22);
b1_qq = floor(b1*2^22);
b2_qq = floor(b2*2^22);
b3_qq = floor(b3*2^22);
b4_qq = floor(b4*2^22);
 
% write AXI registers
h.writememory('40010118',0)
 
h.writememory('40010104',b0_qq)
h.writememory('40010108',b1_qq)
h.writememory('4001011C',b2_qq)
h.writememory('4001010C',b3_qq)
h.writememory('40010110',b4_qq)
 
h.writememory('40010118',enable)
 
release(h);

The algorithm will be verified through FPGA Data Capture, where we have stored the input and the output of the effect algorithm.

When I had all the design finished, I have notice that the design doesn’t reach a soft clipping properly. That is because the order of the filter, since even I design for a low cut frequency, the response of the FIR filter is limited. To get softer responses, we have several options, the easiest at this point is to increase the FIR order to 8, and add 4 more coefficients tot the AXI interface. If this were a hard mission, we can change the type of filter to an 2nd order IIR filter with the same number of coefficients, and try to reach the desired response by changing Q and wc.

In this case, I’m going to modify the FIR diagram to an 8th order FIR filter, and I have assigned new AXI4 registers to the 4 new coefficients. On the script, the fir1 instruction has to be changed to an 8th order filter, and the new coefficients have to be sent to the new addresses generated by MATLAB.

First, we test the output when the effect is disabled, and we can see how the output is the same signal as the output.

Next, I tested the design with 2 different types of clipping. First, with a high cut frequency, we can see how the design perform a hard clipping.

The next DFT diagram is corresponding with a lower cut frequency, that means a soft clipping. We can see how the high frequency harmonics are attenuated by eliminating the high odd harmonics of the hard clipping; this is the same as say that the signal is more sinusoidal.

On this post we have seen how to integrate an entire design on MATLAB from algorithm design and verify, integrate all the elements of the design through the use of Blackboxes, and then verify the response of the algorithm with FPGA Data Capture and the IP JTAG to AXI. It is very interesting how HDL Coder allow the designers verify the signal processing algorithms with real signals, and then use all the MATLAB power to verify the behavior of the algorithm in detail. Also, is really easy to perform changes at any step of the design, even when the design is in the verify process.

Leave a Reply