I've designed a very simple low-pass Butterworth filter using Matlab. The following code snippet demonstrates what I've done.
fs = 2.1e6; flow = 44 * 1000; fNorm = flow / (fs / 2); [b,a] = butter(10, fNorm, 'low');
In [b,a] are stored the filter coefficients. I would like to obtain [b,a] as integers so that I can use an online HDL code generator to generate code in Verilog.
The Matlab [b,a] values seem to be too small to use with the online code generator (the server-side Perl script refuses to generate code with the coefficients), and I am wondering if it would be possible to obtain [b,a] in a form that can be used as a proper input.
The a coefficients that I get in Matlab are:
1.0000 -9.1585 37.7780 -92.4225 148.5066 -163.7596 125.5009 -66.0030 22.7969 -4.6694 0.4307
The b coefficients that I get in Matlab are:
1.0167e-012 1.0167e-011 4.5752e-011 1.2201e-010 2.1351e-010 2.5621e-010 2.1351e-010 1.2201e-010 4.5752e-011 1.0167e-011 1.0167e-012
Using the online generator, I would like to design a filter with a 12-bit bitwidth and I or II filter form. I don't know what is meant by the "fractional bits" at the above link.
Running the code generator (http://www.spiral.net/hardware/filter.html) with the [b,a] coefficients listed above, with fractional bits set at 20 and a bitwidth of 12, I receive the following run error:
Integer A constants: 1048576 -9603383 39613104 -96912015 155720456 -171714386 131597231 -69209161 23904282 -4896220 451621 Integer B constants: 0 0 0 0 0 0 0 0 0 0 0 Error: constants wider than 26 bits are not allowed, offending constant = -69209161, effective bitwidth = 7 mantissa + 20 fractional = 27 total. An error has occurred - please revise the input parameters.
How might I change my design so that this error does not occur?
UPDATE: Using Matlab to generate a 6th-order Butterworth filter, I get the following coefficients:
1.0000 -5.4914 12.5848 -15.4051 10.6225 -3.9118 0.6010
0.0064e-005 0.0382e-005 0.0954e-005 0.1272e-005 0.0954e-005 0.0382e-005 0.0064e-005
Running the online code generator (http://www.spiral.net/hardware/filter.html), I now receive the following error (with fractional bits as 8 and bitwidth of 20):
./iirGen.pl -A 256 '-1405' '3221' '-3943' '2719' '-1001' '153' -B '0' '0' '0' '0' '0' '0' '0' -moduleName acm_filter -fractionalBits 8 -bitWidth 20 -inData inData -inReg -outReg -outData outData -clk clk -reset reset -reset_edge negedge -filterForm 1 -debug -outFile ../outputs/filter_1330617505.v 2>&1 At least 1 non-zero-valued constant is required. Please check the inputs and try again.
Perhaps the b-coefficients are too small, or perhaps the code generator (http://www.spiral.net/hardware/filter.html) wants the [b,a] in another format?
Perhaps what I need to do is scale the [b,a] coefficients by the number of fractional bits to obtain the coefficients as integers.
a .* 2^12 b .* 2^12
However, I still think that the b coefficients are extremely small. What am I doing wrong here?
Perhaps another type of filter (or filter design method) would be more suitable? Could anyone make a suggestion?
UPDATE: As suggested by Jason R and Christopher Felton in the comments below, an SOS filter would be more suitable. I've now written some Matlab code to obtain an SOS filter.
fs = 2.1e6; flow = 44 * 1000; fNorm = flow / (fs / 2); [A,B,C,D] = butter(10, fNorm, 'low'); [sos,g] = ss2sos(A,B,C,D);
The SOS matrix that I get is:
1.0000 3.4724 3.1253 1.0000 -1.7551 0.7705 1.0000 2.5057 1.9919 1.0000 -1.7751 0.7906 1.0000 1.6873 1.0267 1.0000 -1.8143 0.8301 1.0000 1.2550 0.5137 1.0000 -1.8712 0.8875 1.0000 1.0795 0.3046 1.0000 -1.9428 0.9598
Is it possible to still use the Verilog code generation tool (http://www.spiral.net/hardware/filter.html) to implement this SOS filter, or should I simply write the Verilog by hand? Is a good reference available?
I would wonder if an FIR filter would be better to use in this situation.
MOREOVER: Recursive IIR filters can be implemented using integer math by expressing coefficients as fractions. (See Smith's excellent DSP signal processing book for further details: http://www.dspguide.com/ch19/5.htm)
The following Matlab program converts Butterworth filter coefficients into fractional parts using the Matlab rat() function. Then as mentioned in the comments, second order sections can be used to numerically implement the filter (http://en.wikipedia.org/wiki/Digital_biquad_filter).
% variables % variables fs = 2.1e6; % sampling frequency flow = 44 * 1000; % lowpass filter % pre-calculations fNorm = flow / (fs / 2); % normalized freq for lowpass filter % uncomment this to look at the coefficients in fvtool % compute [b,a] coefficients % [b,a] = butter(7, fNorm, 'low'); % fvtool(b,a) % compute SOS coefficients (7th order filter) [z,p,k] = butter(7, fNorm, 'low'); % NOTE that we might have to scale things to make sure % that everything works out well (see zp2sos help for 'up' and 'inf' options) sos = zp2sos(z,p,k, 'up', 'inf'); [n,d] = rat(sos); sos_check = n ./ d; % this should be the same as SOS matrix % by here, n is the numerator and d is the denominator coefficients % as an example, write the the coefficients into a C code header file % for prototyping the implementation % write the numerator and denominator matices into a file [rownum, colnum] = size(n); % d should be the same sections = rownum; % the number of sections is the same as the number of rows fid = fopen('IIR_coeff.h', 'w'); fprintf(fid, '#ifndef IIR_COEFF_H\n'); fprintf(fid, '#define IIR_COEFF_H\n\n\n'); for i = 1:rownum for j = 1:colnum if(j <= 3) % b coefficients bn = ['b' num2str(j-1) num2str(i) 'n' ' = ' num2str(n(i,j))]; bd = ['b' num2str(j-1) num2str(i) 'd' ' = ' num2str(d(i,j))]; fprintf(fid, 'const int32_t %s;\n', bn); fprintf(fid, 'const int32_t %s;\n', bd); end if(j >= 5) % a coefficients if(j == 5) colstr = '1'; end if(j == 6) colstr = '2'; end an = ['a' colstr num2str(i) 'n' ' = ' num2str(n(i,j))]; ad = ['a' colstr num2str(i) 'd' ' = ' num2str(d(i,j))]; fprintf(fid, 'const int32_t %s;\n', an); fprintf(fid, 'const int32_t %s;\n', ad); end end end % write the end of the file fprintf(fid, '\n\n\n#endif'); fclose(fid);