]>
Commit | Line | Data |
---|---|---|
1 | //----------------------------------------------------------------------------- | |
2 | // Copyright (C) 2014 iZsh <izsh at fail0verflow.com> | |
3 | // | |
4 | // This code is licensed to you under the terms of the GNU GPL, version 2 or, | |
5 | // at your option, any later version. See the LICENSE.txt file for the text of | |
6 | // the license. | |
7 | //----------------------------------------------------------------------------- | |
8 | // Butterworth low pass IIR filter | |
9 | // input: 8bit ADC signal, 1MS/s | |
10 | // output: 8bit value, Fc=20khz | |
11 | // | |
12 | // coef: (using http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html) | |
13 | // Recurrence relation: | |
14 | // y[n] = ( 1 * x[n- 2]) | |
15 | // + ( 2 * x[n- 1]) | |
16 | // + ( 1 * x[n- 0]) | |
17 | ||
18 | // + ( -0.8371816513 * y[n- 2]) | |
19 | // + ( 1.8226949252 * y[n- 1]) | |
20 | // | |
21 | // therefore: | |
22 | // a = [1,2,1] | |
23 | // b = [-0.8371816513, 1.8226949252] | |
24 | // b is approximated to b = [-0xd6/0x100, 0x1d3 / 0x100] (for optimization) | |
25 | // gain = 2.761139367e2 | |
26 | // | |
27 | // See details about its design see | |
28 | // https://fail0verflow.com/blog/2014/proxmark3-fpga-iir-filter.html | |
29 | module lp20khz_1MSa_iir_filter(input clk, input [7:0] adc_d, output rdy, output [7:0] out); | |
30 | ||
31 | // clk is 24Mhz, the IIR filter is designed for 1MS/s | |
32 | // hence we need to divide it by 24 | |
33 | // using a shift register takes less area than a counter | |
34 | reg [23:0] cnt = 1; | |
35 | assign rdy = cnt[0]; | |
36 | always @(posedge clk) | |
37 | cnt <= {cnt[22:0], cnt[23]}; | |
38 | ||
39 | reg [7:0] x0 = 0; | |
40 | reg [7:0] x1 = 0; | |
41 | reg [16:0] y0 = 0; | |
42 | reg [16:0] y1 = 0; | |
43 | ||
44 | always @(posedge clk) | |
45 | begin | |
46 | if (rdy) | |
47 | begin | |
48 | x0 <= x1; | |
49 | x1 <= adc_d; | |
50 | y0 <= y1; | |
51 | y1 <= | |
52 | // center the signal: | |
53 | // input range is [0; 255] | |
54 | // We want "128" to be at the center of the 17bit register | |
55 | // (128+z)*gain = 17bit center | |
56 | // z = (1<<16)/gain - 128 = 109 | |
57 | // We could use 9bit x registers for that, but that would be | |
58 | // a waste, let's just add the constant during the computation | |
59 | // (x0+109) + 2*(x1+109) + (x2+109) = x0 + 2*x1 + x2 + 436 | |
60 | x0 + {x1, 1'b0} + adc_d + 436 | |
61 | // we want "- y0 * 0xd6 / 0x100" using only shift and add | |
62 | // 0xd6 == 0b11010110 | |
63 | // so *0xd6/0x100 is equivalent to | |
64 | // ((x << 1) + (x << 2) + (x << 4) + (x << 6) + (x << 7)) >> 8 | |
65 | // which is also equivalent to | |
66 | // (x >> 7) + (x >> 6) + (x >> 4) + (x >> 2) + (x >> 1) | |
67 | - ((y0 >> 7) + (y0 >> 6) + (y0 >> 4) + (y0 >> 2) + (y0 >> 1)) // - y0 * 0xd6 / 0x100 | |
68 | // we want "+ y1 * 0x1d3 / 0x100" | |
69 | // 0x1d3 == 0b111010011 | |
70 | // so this is equivalent to | |
71 | // ((x << 0) + (x << 1) + (x << 4) + (x << 6) + (x << 7) + (x << 8)) >> 8 | |
72 | // which is also equivalent to | |
73 | // (x >> 8) + (x >> 7) + (x >> 4) + (x >> 2) + (x >> 1) + (x >> 0) | |
74 | + ((y1 >> 8) + (y1 >> 7) + (y1 >> 4) + (y1 >> 2) + (y1 >> 1) + y1); | |
75 | end | |
76 | end | |
77 | ||
78 | // output: reduce to 8bit | |
79 | assign out = y1[16:9]; | |
80 | ||
81 | endmodule |