Conception d'un filtre Butterworth dans Matlab et obtention de coefficients de filtre [ab] en tant qu'entiers pour le générateur de code Verilog HDL en ligne

15

J'ai conçu un filtre Butterworth passe-bas très simple en utilisant Matlab. L'extrait de code suivant montre ce que j'ai fait.

fs = 2.1e6;
flow = 44 * 1000;
fNorm =  flow / (fs / 2);
[b,a] = butter(10, fNorm, 'low');

Dans [b, a] sont stockés les coefficients de filtre. Je voudrais obtenir [b, a] sous forme d'entiers afin de pouvoir utiliser un générateur de code HDL en ligne pour générer du code dans Verilog.

Les valeurs de Matlab [b, a] semblent trop petites pour être utilisées avec le générateur de code en ligne (le script Perl côté serveur refuse de générer du code avec les coefficients), et je me demande s'il serait possible d'obtenir [b, a] sous une forme qui peut être utilisée comme entrée appropriée.

Les coefficients a que j'obtiens dans Matlab sont:

1.0000
-9.1585
37.7780
-92.4225
148.5066
-163.7596
125.5009
-66.0030
22.7969
-4.6694
0.4307

Les coefficients b que j'obtiens dans Matlab sont:

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

En utilisant le générateur en ligne, je voudrais concevoir un filtre avec une largeur de bit de 12 bits et une forme de filtre I ou II. Je ne sais pas ce que l'on entend par les "bits fractionnaires" sur le lien ci-dessus.

En exécutant le générateur de code (http://www.spiral.net/hardware/filter.html) avec les coefficients [b, a] répertoriés ci-dessus, avec des bits fractionnaires définis à 20 et une largeur de bit de 12, je reçois l'erreur d'exécution suivante :

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. 

Comment puis-je modifier ma conception afin que cette erreur ne se produise pas?

MISE À JOUR: En utilisant Matlab pour générer un filtre Butterworth de 6e ordre, j'obtiens les coefficients suivants:

Pour un:

1.0000
-5.4914
12.5848
-15.4051
10.6225
-3.9118
0.6010 

pour b:

0.0064e-005
0.0382e-005
0.0954e-005
0.1272e-005
0.0954e-005
0.0382e-005
0.0064e-005

En exécutant le générateur de code en ligne (http://www.spiral.net/hardware/filter.html), je reçois maintenant l'erreur suivante (avec des bits fractionnaires à 8 et une largeur de bit de 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.

Peut-être que les coefficients b sont trop petits, ou peut-être que le générateur de code (http://www.spiral.net/hardware/filter.html) veut le [b, a] dans un autre format?

MISE À JOUR:

Peut-être que je dois faire est de mettre à l'échelle les coefficients [b, a] par le nombre de bits fractionnaires pour obtenir les coefficients sous forme d'entiers.

a .* 2^12
b .* 2^12

Cependant, je pense toujours que les coefficients b sont extrêmement faibles. Qu'est-ce que je fais mal ici?

Peut-être qu'un autre type de filtre (ou méthode de conception de filtre) conviendrait mieux? Quelqu'un pourrait-il faire une suggestion?

MISE À JOUR: Comme suggéré par Jason R et Christopher Felton dans les commentaires ci-dessous, un filtre SOS serait plus approprié. J'ai maintenant écrit du code Matlab pour obtenir un filtre SOS.

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);

La matrice SOS que je reçois est:

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

Est-il possible d'utiliser toujours l'outil de génération de code Verilog (http://www.spiral.net/hardware/filter.html) pour implémenter ce filtre SOS, ou dois-je simplement écrire le Verilog à la main? Une bonne référence est-elle disponible?

Je me demande s'il serait préférable d'utiliser un filtre FIR dans cette situation.

DE PLUS: Les filtres IIR récursifs peuvent être implémentés en utilisant des mathématiques entières en exprimant les coefficients sous forme de fractions. (Voir l'excellent livre de Smith sur le traitement du signal DSP pour plus de détails: http://www.dspguide.com/ch19/5.htm )

Le programme Matlab suivant convertit les coefficients du filtre de Butterworth en parties fractionnaires à l'aide de la fonction Matlab rat (). Ensuite, comme mentionné dans les commentaires, des sections de second ordre peuvent être utilisées pour implémenter numériquement le filtre (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);
Nicholas Kinar
la source
4
Les filtres IIR d'ordre supérieur comme celui-ci sont généralement implémentés à l'aide de sections de second ordre ; vous obtenez le filtre que vous souhaitez en cascadant plusieurs étapes de second ordre (avec une seule étape de premier ordre si l'ordre souhaité est impair). Il s'agit généralement d'une implémentation plus robuste que l'implémentation directe du filtre d'ordre supérieur.
Jason R
3
Si vous ne faites pas ce que @JasonR suggère, vous aurez de très grandes tailles de mots. Des filtres comme celui-ci peuvent échouer en virgule flottante simple précision lorsqu'ils sont implémentés avec une structure IIR de base, vous avez besoin du SOS.
Christopher Felton
@JasonR: Merci d'avoir suggéré cela. J'ai mis à jour par réponse ci-dessus.
Nicholas Kinar
@ChristopherFelton: Merci d'avoir aidé à renforcer cela.
Nicholas Kinar
Oui, avec votre nouvelle matrice SOS, vous pouvez créer 3 filtres à partir du site. Ou vous pouvez utiliser mon code ici . Il fonctionnera de la même manière que le site Web. Je serai ravi de mettre à jour le script en dehors de la matrice SOS.
Christopher Felton

Réponses:

5

Comme nous l'avons vu, il est préférable d'utiliser la somme des sections, c'est-à-dire de diviser le filtre d'ordre supérieur en filtres de second ordre en cascade. La question mise à jour a la matrice SOS. En utilisant ce code et un exemple ici, l'objet Python peut être utilisé pour générer les sections individuelles.

En matlab

save SOS

En Python

import shutil
import numpy
from scipy.io import loadmat
from siir import SIIR

matfile = loadmat('SOS.mat')  
SOS = matfile['SOS']
b = numpy.zeros((3,3))
a = numpy.zeros((3,3))
section = [None for ii in range(3)]
for ii in xrange(3):
    b[ii] = SOS[ii,0:3]
    a[ii] = SOS[ii,3:6]

    section[ii] = SIIR(b=b[ii], a=a[ii], W=(24,0))
    section[ii].Convert()  # Create the Verilog for the section
    shutil.copyfile('siir_hdl.v', 'iir_sos_section%d.v'%(ii))

Des informations supplémentaires sur les virgules fixes sont disponibles ici

Christopher Felton
la source
Merci beaucoup pour tous les liens perspicaces et pour le code Python; J'espère que votre réponse (et les autres réponses publiées ici) serviront de bonnes références pour beaucoup d'autres. Je souhaite pouvoir marquer toutes les réponses ici comme acceptées.
Nicholas Kinar
1
Si vous avez des problèmes, faites-le moi savoir et je mettrai à jour / corrigerai le code s'il ne fonctionne pas pour vous. Je vais le modifier (relativement bientôt, doh) pour accepter directement une matrice SOS.
Christopher Felton
1
J'ai essayé d'implémenter ma propre version à partir de votre exemple. Sur mon système, j'ai dû utiliser "from numpy import zeros" et changer loatmat en loadmat (). La matrice SOS est-elle fournie par Matlab ( mathworks.com/help/toolbox/signal/ref/ss2sos.html ) au même format que prévu? Je reçois l'erreur suivante lorsque j'essaie d'accéder à la matrice SOS: "TypeError: type non partageable" lorsque l'interpréteur atteint la ligne "b [ii] = SOS [0: 3, ii]"
Nicholas Kinar
1
Cela dépendrait du format du fichier SOS.mat. Si vous imprimez simplement (matfile), il vous montrera les clés dans le fichier .mat chargé. Scipy.io.loadmat se charge toujours en tant que dictionnaire (BOMK).
Christopher Felton
1
Oui, c'est correct, la sortie de 0 est l'entrée à 1 et ainsi de suite. Il faut réfléchir à la largeur du mot. La valeur par défaut est deux utilise une fraction de 24 bits (0 entier, 23 fraction, 1 signe). Je crois que vous vouliez à l'origine utiliser une largeur de mot plus petite.
Christopher Felton
10

Les «bits fractionnaires» sont le nombre de bits dans un bus que vous avez dédié pour représenter la partie fractionnaire d'un nombre (par exemple, le 0,75 en 3,75).

Disons que vous avez un bus numérique de 4 bits de large, quel nombre 1001représente? Cela pourrait signifier «9» si vous le traitez comme un entier positif (2 ^ 3 + 2 ^ 0 = 8 + 1 = 9). Ou cela pourrait signifier -7 dans la notation du complément à deux: (-2 ^ 3 + 2 ^ 0 = -8 + 1 = -7).

Qu'en est-il des nombres contenant des fractions, c'est-à-dire des nombres «réels»? Les nombres réels peuvent être représentés dans le matériel comme «virgule fixe» ou «virgule flottante». Il semble que ces générateurs de filtres utilisent des points fixes.

Retour à notre bus 4 bits ( 1001). Introduisons un point binaire pour que nous obtenions 1.001. Cela signifie que nous utilisions maintenant les bits sur le RHS du point pour construire des entiers, et les bits sur le LHS pour construire une fraction. Le nombre représenté par un bus numérique réglé sur 1.001est 1,125 ( 1* 2 ^ 0 + 0* 2 ^ -1 + 0* 2 ^ -2 + 1* 2 ^ -3 = 1 + 0,125 = 1,125). Dans ce cas, sur les 4 bits du bus, nous en utilisons 3 pour représenter la partie fractionnaire d'un nombre. Ou, nous avons 3 bits fractionnaires.

Donc, si vous avez une liste de nombres réels comme vous l'avez ci-dessus, vous devez maintenant décider combien de bits fractionnaires vous souhaitez les représenter. Et voici le compromis: plus vous utilisez de bits fractionnaires, plus vous pouvez représenter le nombre que vous voulez, mais plus votre circuit devra être grand. Et de plus, moins vous utilisez de bits fractionnaires, plus la réponse en fréquence réelle du filtre sera différente de celle que vous avez conçue au départ!

Et pour aggraver les choses, vous cherchez à créer un filtre de réponse impulsionnelle infinie (IIR). Ceux-ci peuvent en fait devenir instables si vous n'avez pas assez de bits fractionnaires et entiers!

Marty
la source
Merci d'avoir fourni cette réponse perspicace. J'ai essayé d'exécuter le générateur de code en utilisant les petits coefficients b ci-dessus, et je reçois toujours des erreurs. Pourriez-vous suggérer quelque chose que je pourrais faire pour faire fonctionner correctement le générateur? Je mettrai à jour la réponse ci-dessus pour montrer ce que j'ai fait.
10

Alors Marty a bien pris soin de la question des bits. Sur le filtre lui-même, je pense que vous recevez probablement un avertissement ou une plainte de matlab au sujet de coefficients mal mis à l'échelle? Quand je trace le filtre, de scipy pas matlab mais c'est probablement très similaire.

Réponse

Ce qui est de 100 dB vers le bas dans la bande passante! Donc, vous voudrez peut-être vous assurer que vous voulez un filtre d'ordre plus petit, ce qui vous aidera de toute façon à la mise en œuvre. Lorsque j'arrive à un filtre de 6e ordre, j'arrête de recevoir des plaintes concernant les mauvais coefficients. Essayez peut-être de réduire la commande et voyez si elle répond toujours à vos besoins.

macduff
la source
Merci d'avoir suggéré ça! Je pense qu'un filtre de 6e ordre fonctionnerait tout aussi bien. En utilisant fvtool de matlab, je pense que la réponse est bonne pour mon application. J'ai maintenant mis à jour ma réponse ci-dessus. Cependant, quelque chose ne va toujours pas avec le générateur de code Verilog HDL ( spiral.net/hardware/filter.html ). Peut-être veut-il le [b, a] dans un autre format. De plus, +1 pour l'utilisation de SciPy.