diff --git a/filter_blocks/fda/filter_hw.py b/filter_blocks/fda/filter_hw.py index 71ce166..bb5a6c8 100644 --- a/filter_blocks/fda/filter_hw.py +++ b/filter_blocks/fda/filter_hw.py @@ -48,17 +48,22 @@ def __init__(self, b=None, a=None): # A reference to the HDL block self.hardware = None - def set_coefficients(self, coeff_b, coeff_a=None): + def set_coefficients(self, coeff_b = None, coeff_a = None, sos = None): """Set filter coefficients. Args: coeff_b (list): list of numerator filter coefficients coeff_a (list): list of denominator filter coefficients """ - self.b = tuple(coeff_b) + if coeff_b is not None: + self.b = tuple(coeff_b) + if coeff_a is not None: self.a = tuple(coeff_a) + if sos is not None: + self.sos = sos + def set_stimulus(self, sigin): """Set filter stimulus diff --git a/filter_blocks/fda/fir.py b/filter_blocks/fda/fir.py index 94252aa..68b3623 100644 --- a/filter_blocks/fda/fir.py +++ b/filter_blocks/fda/fir.py @@ -24,6 +24,7 @@ def __init__(self, b = None, a = None): self.direct_form_type = 1 self.response = [] + def get_response(self): """Return filter output. @@ -32,11 +33,26 @@ def get_response(self): """ return self.response + def info(self): + """Print filter info""" + print("Filter type :", self.filter_type, "\n" + "Filter order :", len(self.b), "\n" + "Arithmatic :", "fixed", "\n" + "Coefficient format :", self.coef_word_format ,"\n" + "Input format :", self.input_word_format ,"\n" + "Accumulator size :", "\n" + "Output format :", self.output_word_format ,"\n" + "Round mode :", "no rounding", "\n" + "Overflow mode :" "no overflow" + ) + def run_sim(self): """Run filter simulation""" + testfil = self.filter_block() testfil.run_sim() + def convert(self, **kwargs): """Convert the HDL description to Verilog and VHDL. """ @@ -46,7 +62,7 @@ def convert(self, **kwargs): imax = 2**(w[0]-1) # small top-level wrapper - def filter_fir_top(hdl, clock, reset, x, xdv, y, ydv): + def filter_fir_top(hdl , clock, reset, x, xdv, y, ydv): sigin = Samples(x.min, x.max, self.input_word_format) sigin.data, sigin.data_valid = x, xdv sigout = Samples(y.min, y.max, self.output_word_format) @@ -55,7 +71,7 @@ def filter_fir_top(hdl, clock, reset, x, xdv, y, ydv): rst = reset glbl = Global(clk, rst) - # choose appropriate filter + #choose appropriate filter fir_hdl = fir_df1.filter_fir fir = fir_hdl(glbl, sigin, sigout, self.b, self.coef_word_format, @@ -63,11 +79,13 @@ def filter_fir_top(hdl, clock, reset, x, xdv, y, ydv): fir.convert(**kwargs) + clock = Clock(0, frequency=50e6) reset = Reset(1, active=0, async=True) x = Signal(intbv(0, min=-imax, max=imax)) y = Signal(intbv(0, min=-omax, max=omax)) xdv, ydv = Signal(bool(0)), Signal(bool(0)) + if self.hdl_target.lower() == 'verilog': filter_fir_top(hdl, clock, reset, x, xdv, y, ydv) @@ -77,20 +95,22 @@ def filter_fir_top(hdl, clock, reset, x, xdv, y, ydv): else: raise ValueError('incorrect target HDL {}'.format(self.hdl_target)) + + @hdl.block def filter_block(self): - """ - This elaboration code will select the different structure and - implementations - """ + """ this elaboration code will select the different structure and implementations""" w = self.input_word_format w_out = self.output_word_format + #print(self.input_word_format) + #print(self.coef_word_format) ymax = 2**(w[0]-1) vmax = 2**(2*w[0]) omax = 2**(w_out[0]-1) xt = Samples(min=-ymax, max=ymax, word_format=self.input_word_format) yt = Samples(min=-omax, max=omax, word_format=self.output_word_format) + #yt = Samples(min=-vmax, max=vmax) xt.valid = bool(1) clock = Clock(0, frequency=50e6) reset = Reset(1, active=0, async=True) @@ -100,26 +120,31 @@ def filter_block(self): # set numsample numsample = len(self.sigin) - # process to record output in buffer + #process to record output in buffer rec_insts = yt.process_record(clock, num_samples=numsample) + if self.filter_type == 'direct_form': if self.direct_form_type == 1: # all filters will need the same interface ports, this should be do able dfilter = fir_df1.filter_fir - else: - raise NotImplementedError if self.n_cascades > 0: filter_insts = [None for _ in range(self.n_cascades)] for ii in range(self.n_cascades): pass + # filter_insts[ii] = fir_df1.filter_fir( + # glbl, sigin[ii], sigout[ii], b + # ) else: filter_insts = dfilter(glbl, xt, yt, self.b, self.coef_word_format) + + + @hdl.instance def stimulus(): - """record output in numpy array yt.sample_buffer""" + "record output in numpy array yt.sample_buffer" for k in self.sigin: xt.data.next = int(k) xt.valid = bool(1) @@ -127,7 +152,7 @@ def stimulus(): yt.record = True yt.valid = True yield clock.posedge - # Collect a sample from each filter + #Collect a sample from each filter yt.record = False yt.valid = False @@ -139,44 +164,3 @@ def stimulus(): raise StopSimulation() return hdl.instances() - - @hdl.block - def process(self, glbl, smpi, smpo): - """FIR filter model. - - Args: - glbl: - smpi: sample stream input - smpo: sample stream output - - Not convertible - """ - assert isinstance(smpi, Samples) - assert isinstance(smpo, Samples) - - clock, reset = glbl.clock, glbl.reset - x, y = smpi, smpo - ntaps = len(self.b) - h = tuple(self.b) - - xd = [0 for _ in range(len(h))] - - @hdl.instance - def beh_proc(): - while True: - yield clock.posedge - - if x.valid: - xd.insert(0, int(smpi.data)) - xd.pop(-1) - - sop = 0 - for ii in range(ntaps): - sop = sop + (h[ii] * xd[ii]) - - y.data.next = int(round(sop)) - y.valid.next = True - else: - y.valid.next = False - - return hdl.instances() diff --git a/filter_blocks/fda/iir.py b/filter_blocks/fda/iir.py index 59b3c62..40617cd 100644 --- a/filter_blocks/fda/iir.py +++ b/filter_blocks/fda/iir.py @@ -4,12 +4,12 @@ from myhdl import Signal, intbv, StopSimulation from .filter_hw import FilterHardware -from ..iir import iir_df1 +from ..iir import iir_df1, iir_sos from filter_blocks.support import Clock, Reset, Global, Samples class FilterIIR(FilterHardware): - def __init__(self, b=None, a=None): + def __init__(self, b = None, a = None): """Contains IIR filter parameters. Parent Class : FilterHardware Args: b (list of int): list of numerator coefficients. @@ -37,9 +37,22 @@ def run_sim(self): """Run filter simulation""" testfil = self.filter_block() - # testfil.config_sim(trace=True) + #testfil.config_sim(trace=True) testfil.run_sim() + def info(self): + """Print filter info""" + print("Filter type :", self.filter_type, "\n" + "Filter order :", len(self.b), "\n" + "Arithmatic :", "fixed", "\n" + "Coefficient format :", self.coef_word_format ,"\n" + "Input format :", self.input_word_format ,"\n" + "Accumulator size :", "\n" + "Output format :", self.output_word_format ,"\n" + "Round mode :", "no rounding", "\n" + "Overflow mode :" "saturate" + ) + def convert(self, **kwargs): """Convert the HDL description to Verilog and VHDL. """ @@ -60,6 +73,7 @@ def filter_iir_top(hdl , clock, reset, x, xdv, y, ydv): # choose appropriate filter iir_hdl = iir_df1.filter_iir + iir = iir_hdl( glbl, sigin, sigout, self.b, self.a, self.coef_word_format, shared_multiplier=self._shared_multiplier @@ -71,6 +85,7 @@ def filter_iir_top(hdl , clock, reset, x, xdv, y, ydv): x = Signal(intbv(0, min=-imax, max=imax)) y = Signal(intbv(0, min=-omax, max=omax)) xdv, ydv = Signal(bool(0)), Signal(bool(0)) + if self.hdl_target.lower() == 'verilog': filter_iir_top(hdl, clock, reset, x, xdv, y, ydv) @@ -87,7 +102,7 @@ def filter_block(self): w = self.input_word_format w_out = self.output_word_format - ymax = 2**(w[0]-1) + ymax = 2**(2*w[0]-1) vmax = 2**(2*w[0]) omax = 2**(w_out[0]-1) xt = Samples(min=-ymax, max=ymax, word_format=self.input_word_format) @@ -111,10 +126,11 @@ def filter_block(self): if self.n_cascades > 0: # TODO: port the SOS iir into the latest set of interfaces - # filter_insts = iir_sos.filter_iir_sos( - # glbl, xt, yt, self.b, self.a, self.coef_word_format - # ) + #filter_insts = iir_sos.filter_iir_sos( + # glbl, xt, yt, self.sos, self.coef_word_format + #) pass + else: filter_insts = dfilter( glbl, xt, yt, self.b, self.a, self.coef_word_format diff --git a/filter_blocks/fir/fir_df1.py b/filter_blocks/fir/fir_df1.py index 74f792d..9019636 100644 --- a/filter_blocks/fir/fir_df1.py +++ b/filter_blocks/fir/fir_df1.py @@ -6,6 +6,7 @@ @hdl.block + def filter_fir(glbl, sigin, sigout, b, coef_w, shared_multiplier=False): """Basic FIR direct-form I filter. diff --git a/filter_blocks/iir/iir_df1.py b/filter_blocks/iir/iir_df1.py index d1af635..6c5f9c9 100644 --- a/filter_blocks/iir/iir_df1.py +++ b/filter_blocks/iir/iir_df1.py @@ -4,6 +4,7 @@ from myhdl import Signal, intbv, always_seq from filter_blocks.support import Samples, Signals +import math @hdl.block @@ -37,13 +38,6 @@ def filter_iir(glbl, sigin, sigout, b, a, coef_w, shared_multiplier=False): ymax = 2 ** (w[0] - 1) vmax = 2 ** (2 * w[0]) # top bit is guard bit # max without guard bit. Value at which output will saturate - amax = 2 ** (2 * w[0] - 1) - - q, qd = w[0], 2 * w[0] # guard bit not fed back - - # guard bit not passed to output - o, od = 2 * w[0] - w_out[0], 2 * w[0] - o = 0 if o < 0 else o N = len(b) - 1 clock, reset = glbl.clock, glbl.reset @@ -51,19 +45,53 @@ def filter_iir(glbl, sigin, sigout, b, a, coef_w, shared_multiplier=False): y, ydv = sigout.data, sigout.valid x, xdv = sigin.data, sigin.valid + + + ######### method 1 for calculating accumulator + + # amax = 2 ** (2 * w[0] - 1) + + # q, qd = w[0], 2 * w[0] # guard bit not fed back + # q = 0 if q < 0 else q + + # # guard bit not passed to output + # o, od = 2 * w[0] - w_out[0], 2 * w[0] + # o = 0 if o < 0 else o + # yacc = Signal(intbv(0, min=-vmax, max=vmax)) # verify the length of this + + ######### + + + ########## method 2 of calculating accumulator size based on fir filter implementation + + acc_bits = w[0] + coef_w[0] + math.floor(math.log(N, 2)) + #print(acc_bits) + amax = 2**(acc_bits-1) + od = acc_bits - 1 + o = acc_bits-w_out[0] - 1 + o = 0 if o < 0 else o + q, qd = acc_bits - w[0] -1 , acc_bits -1 + q = 0 if q < 0 else q + + yacc = Signal(intbv(0, min=-amax, max=amax)) + + ########## + # Delay elements, list-of-signals ffd = Signals(intbv(0, min=-ymax, max=ymax), N) fbd = Signals(intbv(0, min=-ymax, max=ymax), N) - yacc = Signal(intbv(0, min=-vmax, max=vmax)) # verify the length of this + dvd = Signal(bool(0)) + overflow = Signal(bool(0)) + underflow = Signal(bool(0)) + #print(len(yacc)) # print(len(yacc)) @hdl.always(clock.posedge) def beh_direct_form_one(): if sigin.valid: - # print(x) for i in range(N - 1): ffd[i + 1].next = ffd[i] @@ -77,12 +105,21 @@ def beh_acc(): c = b[0] sop = x * c + for ii in range(N): c = b[ii + 1] # first element in list in b0 d = a[ii + 1] # first element in list is a0 =1 sop = sop + (c * ffd[ii]) - (d * fbd[ii]) - yacc.next = sop + if overflow: + yacc.next = amax-1 + + if underflow: + yacc.next = -amax + + else : + yacc.next = sop + @always_seq(clock.posedge, reset=reset) def beh_output(): @@ -92,13 +129,20 @@ def beh_output(): if (yacc[qd] == 1 and yacc[qd - 1] == 1) or (yacc[qd] == 0 and yacc[qd - 1] == 0): ydv.next = dvd y.next = yacc[od:o].signed() + overflow = 0 + underflow = 0 - elif yacc[qd + 1] == 1 and yacc[qd] == 0: + elif yacc[qd] == 1 and yacc[qd-1] == 0: y.next = -amax ydv.next = dvd + underflow = 1 + print('underflow') + elif yacc[qd] == 0 and yacc[qd - 1] == 1: y.next = amax - 1 ydv.next = dvd + overflow = 1 + print('overflow') return hdl.instances() diff --git a/filter_blocks/iir/iir_sos.py b/filter_blocks/iir/iir_sos.py index e69de29..501dff1 100644 --- a/filter_blocks/iir/iir_sos.py +++ b/filter_blocks/iir/iir_sos.py @@ -0,0 +1,119 @@ + +""" +Simple IIR Filter +================= +The following is a straight forward HDL description of a +direct-form-I IIR filter. This module can be used to +generate synthesizable Verilg/VHDL. + + + +:Author: Christopher Felton +""" + +import myhdl as hdl +from myhdl import Signal, intbv, always, always_comb, always_seq +from filter_blocks.support import Samples + + +@hdl.block +def filter_iir(glbl, sigin, sigout, sos, shared_multiplier=False): + """Basic IIR direct-form I filter. + + Ports: + glbl (Global): global signals. + sigin (SignalBus): input digital signal. + sigout (SignalBus): output digitla signal. + + Args: + b (tuple, list): numerator coefficents. + a (tuple, list): denominator coefficents. + + Returns: + inst (myhdl.Block, list): + """ + assert isinstance(sigin, Samples) + assert isinstance(sigout, Samples) + assert isinstance(sos, tuple) + #assert isinstance(b, tuple) + + # All the coefficients need to be an `int`, the + # class (`???`) handles all the float to fixed-poit + # conversions. + ra = [isinstance(aa, int) for aa in sos] + #rb = [isinstance(bb, int) for bb in b] + assert all(ra) + #assert all(rb) + + w = sigin.word_format + ymax = 2**(w[0]-1) + vmax = 2**(100*w[0]) # double width max and min + vmin = -vmax + + # Quantized IIR coefficients + b0, b1, b2, a0, a1, a2 = sos + print(sos) + print(b0,b1,b2,a0,a1,a2) + q, qd = w[0]-1, 2*w[0] + + # Locally reference the interface signals + clock, reset = glbl.clock, glbl.reset + x, xdv = sigin.data, sigin.valid + y, ydv = sigout.data, sigout.valid + + # Delay elements, list-of-signals (double length for all) + ffd = [Signal(intbv(0, min=vmin, max=vmax)) for _ in range(2)] + fbd = [Signal(intbv(0, min=vmin, max=vmax)) for _ in range(2)] + yacc = Signal(intbv(0, min=vmin, max=vmax)) + dvd = Signal(bool(0)) + + @always(clock.posedge) + def beh_direct_form_one(): + if sigin.valid: + ffd[1].next = ffd[0] + ffd[0].next = x + + fbd[1].next = fbd[0] + #fbd[0].next = yacc[qd:q].signed() + fbd[0].next = yacc.signed() + + @always_comb + def beh_acc(): + # double precision accumulator + yacc.next = (b0 * x) + (b1 * ffd[0]) + (b2 * ffd[1]) \ + - (a1 * fbd[0]) - (a2 * fbd[1]) + + @always_seq(clock.posedge, reset=reset) + def beh_output(): + dvd.next = xdv + y.next = yacc[qd:q].signed() + ydv.next = dvd + + # @todo add shared multiplier version ... + + return hdl.instances() + + +@hdl.block +def filter_iir_sos(glbl, x, y, sos, w): + """IIR sum of sections ... + """ + #assert len(b) == len(a) + number_of_sections = len(sos) + list_of_insts = [None for _ in range(number_of_sections)] + + xmax = x.imax + xmin = x.imin + + xb = [Samples(min = xmin, max = xmax) for _ in range(number_of_sections+1)] + xb[0] = x + xb[number_of_sections] = y + + for ii in range(len(sos)): + list_of_insts[ii] = filter_iir( + glbl, xb[ii], xb[ii+1], + sos=tuple(map(int, sos[ii])) + ) + + return list_of_insts + diff --git a/notebooks/pyfda_api_example.ipynb b/notebooks/pyfda_api_example.ipynb index d6dda87..296b9e2 100644 --- a/notebooks/pyfda_api_example.ipynb +++ b/notebooks/pyfda_api_example.ipynb @@ -7,8 +7,6 @@ "## Introduction\n", "Filter-blocks repository contains a collection of hardware digital filter implementations coded with myhdl.\n", "\n", - "### Installation\n", - "insert installation steps here\n", "\n", "### object oriented API\n", "An API has been built for the filter-blocks package which allows easy access to the filter implementations. It also helps set parameters required by serving as a wrapper around MyHDL methods. \n", @@ -18,17 +16,12 @@ "The filter implementations that presently exist in the filter blocks package:\n", "\n", "- fir filters\n", - " - direct form I fir filter\n", + " - direct form I \n", " - cascaded second order sections \n", "- iir filters\n", - " - direct form I iir filter \n", - " - cascaded \n", - " - parallel \n", - "\n", - "### class hierarchy\n", - "insert class hierarchy figure here\n", - "\n", - "A visual representation of all the methods and attributes which can be set/modified according to requirements. " + " - direct form I\n", + " - cascaded second order sections\n", + " - parallel implementation\n" ] }, { @@ -116,13 +109,118 @@ "hdlfilter.convert(hdl = 'VHDL')\n", "```\n", "\n", - "\n" + "hdl: 'VHDL' or 'Verilog'. Defaults to Verilog.\n", + "\n", + "Args:
\n", + "path: Destination folder. Defaults to current working dir.
\n", + "name: Module and output file name. Defaults to self.mod.name.
\n", + "trace: Whether the testbench should dump all signal waveforms. Defaults to False.
\n", + "testbench: Verilog only. Specifies whether a testbench should be created. Defaults to True.
\n", + "timescale: timescale parameter. Defaults to '1ns/10ps'. Verilog only.
" ] }, { "cell_type": "markdown", - "metadata": {}, - "source": [] + "metadata": { + "collapsed": true + }, + "source": [ + "### Create IIR filter with integer coefficients\n", + "\n", + "The steps to be followed are the same as that of FIR filter. Hence only an example should suffice with minimal explanation. \n", + "\n", + "#### Create filter coefficients\n", + "\n", + "In this example we will be using the scipy library to create iir coefficients for a low pass elliptical filter. We design a 3rd-order lowpass elliptic filter with 5 dB of passband ripple, 40 dB of stopband attenuation, and a passband edge frequency of 0.6$\\pi$ rad/sample\n", + "\n", + "```python \n", + "import scipy.signal as signal\n", + "\n", + "b, a = signal.ellip(3, 5, 40, 0.6, output='ba') # 3 taps \n", + "```\n", + "Next, the coefficients are scaled up.\n", + "\n", + "```python\n", + "B1 = 18 # Number of bits for coefficients\n", + "bsc = b*(2**B1) #scaled coefficients\n", + "asc = a*(2**B1) \n", + "bsc_int = [int(x) for x in bsc] #convert coefficient to type int\n", + "asc_int = [int(x) for x in bsc] \n", + "```\n", + "#### Build the IIR Filter\n", + "``` python \n", + "from filter_blocks.fda import FilterIIR\n", + "hdlfilter = FilterIIR()\n", + "```\n", + "#### Set the Filter Parameters to Work with Integers\n", + "Next, we set the input parameters of the filter to appropriate values.\n", + "\n", + "```python \n", + "hdlfilter.set_word_format(coeff_w = (18, 17, 0), input_w = (12,11,0), output_w = (24,23,0))\n", + "```\n", + "Args:
\n", + "coeff_w (tuple of int): word format (W,WI,WF)
\n", + "input_w (tuple of int): word format (W,WI,WF)
\n", + "output_w (tuple of int): word format (W,WI,WF)
\n", + "Where WI is the number of integer bits, WF is the number of fractional bit and W = WI+WF+1 (extra 1 for the sign bit). It is important to set the coefficient bits to 18, as this was used to calculate the scaling factor. In this example we are assuming that the input signal is of 12 bits and the output is set to 24 bits. \n", + "\n", + "Next, we pass the filter coefficients as determined earlier.\n", + "```python\n", + "hdlfilter.set_coefficients(coeff_b = bsc_int, coeff_a = asc_int)\n", + "\n", + "```\n", + "\n", + "the info method prints details about the filter implementation \n", + "\n", + "```python \n", + "hdlfilter.info()\n", + "```\n", + "\n", + "\n", + "#### Create a Stimulus for the Filter\n", + "We generate an input signal (stimulus) exactly the same way as for the fir filter. \n", + "\n", + "```python \n", + "import numpy as np\n", + "\n", + "N=20 #length of input signal\n", + "sig = [np.sin(0.1*np.pi*i) for i in np.arange(0,N,1)]\n", + "\n", + "B2 = 12 # Number of bits\n", + "L2 = math.floor(math.log((2**(B2-1)-1)/max(sig), 2)) # calculate scaling factor\n", + "\n", + "sig = np.multiply(sig, 2**L2) \n", + "sig = sig.round()\n", + "sig = sig.astype(int)\n", + "\n", + "hdlfilter.set_stimulus(sig) #pass the stimulus to the filter object\n", + "\n", + "```\n", + "\n", + "#### Run the simulation\n", + "\n", + "To filter the input signal generated above, we need to run a simulation and obtain the output signal. Enter the following:\n", + "\n", + "```python \n", + "hdlfilter.run_sim()\n", + "y = hdlfilter.get_response()\n", + "```\n", + "Filter-blocks returns a numpy array of the output which is stored in 'y'. Here y is a 24 bits as was set earlier, meaning that some bits might have possibly been discarded to fit the output. The most significant 24 bits from the accumulator are assigned to y.\n", + "\n", + "\n", + "#### Converting design to Verilog or VHDL\n", + "```python \n", + "hdlfilter.convert(hdl = 'VHDL')\n", + "```\n", + "hdl: 'VHDL' or 'Verilog'. Defaults to Verilog.\n", + "\n", + "Args:
\n", + "path: Destination folder. Defaults to current working dir.
\n", + "name: Module and output file name. Defaults to self.mod.name.
\n", + "trace: Whether the testbench should dump all signal waveforms. Defaults to False.
\n", + "testbench: Verilog only. Specifies whether a testbench should be created. Defaults to True.
\n", + "timescale: timescale parameter. Defaults to '1ns/10ps'. Verilog only.
" + ] }, { "cell_type": "code", @@ -155,4 +253,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/tests/test_fir/test_df1_sine.py b/tests/test_fir/test_df1_sine.py index da1610b..8e2b33a 100644 --- a/tests/test_fir/test_df1_sine.py +++ b/tests/test_fir/test_df1_sine.py @@ -121,4 +121,4 @@ def test_df1_sine(): if __name__ == '__main__': - test_df1_sine() \ No newline at end of file + test_df1_sine() diff --git a/tests/test_fir/test_firs.py b/tests/test_fir/test_firs.py index 5d3933d..cecede2 100644 --- a/tests/test_fir/test_firs.py +++ b/tests/test_fir/test_firs.py @@ -9,7 +9,6 @@ from filter_blocks.fda.fir import FilterFIR - def test_filters(args=None): if args is None: ntaps, nbands, fs, imax = 86, 3, 1e5, 2**7 @@ -80,6 +79,7 @@ def tbstim(): for sr in rlist: sr.record = False + yield delay(1100) raise hdl.StopSimulation diff --git a/tests/test_iir/test_iir_df1.py b/tests/test_iir/test_iir_df1.py index f130c98..dc7d5c6 100644 --- a/tests/test_iir/test_iir_df1.py +++ b/tests/test_iir/test_iir_df1.py @@ -1,30 +1,58 @@ -from filter_blocks.fda import FilterIIR + +import math import numpy as np +import scipy.signal as signal import matplotlib.pyplot as plt +from filter_blocks.fda import FilterIIR + + def test_iir_df1(): """Meant to emulate how pyfda will pass parameters to filters""" + + sig = signal.unit_impulse(10) + # print(sig) + + B2 = 17 # Number of bits + L2 = math.floor(math.log((2 ** (B2 - 1) - 1) / max(sig), 2)) # Round towards zero to avoid overflow + # print(L) + sig = np.multiply(sig, 2 ** L2) + sig = sig.round() + sig = sig.astype(int) + + + + + stim = np.empty(15) stim.fill(32767) hdlfilter = FilterIIR() - b = [1287, 5148, 7722, 5148, 1287] + #b = [1287, 5148, 7722, 5148, 1287] + #a = [1, -22954, 14021, -3702, 459] + b = [32767, 32767, 32767, 32767, 32767] a = [1, -22954, 14021, -3702, 459] + hdlfilter.set_coefficients(coeff_b=b, coeff_a=a) # TODO: increase the test coverage by adding contraint random # - hdlfilter.set_word_format((16,23,0), (16, 23, 0), (26, 53, 0)) + hdlfilter.set_word_format((16,23,0), (16, 23, 0), (100, 53, 0)) hdlfilter.set_stimulus(stim) hdlfilter.run_sim() - hdlfilter.convert(hdl = 'verilog') + #hdlfilter.convert(hdl = 'verilog') + #hdlfilter.info() y = hdlfilter.get_response() + + y_tf = signal.lfilter(b, a, stim) + print(y) + #print(y_tf) hdlfilter.convert(hdl = 'verilog') # TODO: plotting should not be included in the tests, # create simple scripts in filter-blocks/scripts # for plotting ... - # plt.plot(y, 'b') - # plt.show() + #plt.plot(y, 'b') + #plt.show() if __name__ == '__main__': diff --git a/tests/test_iir/test_iir_df1_sine.py b/tests/test_iir/test_iir_df1_sine.py index 4400262..b6613a4 100644 --- a/tests/test_iir/test_iir_df1_sine.py +++ b/tests/test_iir/test_iir_df1_sine.py @@ -12,19 +12,19 @@ def fixp_sine(bsc_int, asc_int, B1, L1): # N=20 # sig = [np.sin(0.1*np.pi*i) for i in np.arange(0,N,1)] - sig = signal.unit_impulse(20) + sig = signal.unit_impulse(10) - B2 = 12 # Number of bits + B2 = 17 # Number of bits L2 = math.floor(math.log((2 ** (B2 - 1) - 1) / max(sig), 2)) # Round towards zero to avoid overflow sig = np.multiply(sig, 2 ** L2) sig = sig.round() sig = sig.astype(int) - print(sig) + #print(sig) hdlfilter = FilterIIR() hdlfilter.set_coefficients(coeff_b=bsc_int, coeff_a=asc_int) - hdlfilter.set_word_format((B1, B1 - 1, 0), (B2, B2 - 1, 0), (1000, 39, 0)) + hdlfilter.set_word_format(coeff_w = (B1, B1 - 1, 0), input_w = (B2, B2 - 1, 0), output_w = (35, 25, 0)) hdlfilter.set_stimulus(sig) hdlfilter.run_sim() y = hdlfilter.get_response() @@ -46,7 +46,7 @@ def floatp_sine(b, a, B1, L1): sig = signal.unit_impulse(10) # print(sig) - B2 = 12 # Number of bits + B2 = 17 # Number of bits L2 = math.floor(math.log((2 ** (B2 - 1) - 1) / max(sig), 2)) # Round towards zero to avoid overflow # print(L) sig = np.multiply(sig, 2 ** L2) @@ -63,43 +63,27 @@ def floatp_sine(b, a, B1, L1): def test_iir_df1_sine(): """Meant to emulate how pyfda will pass parameters to filters""" - # fs = 1000. - # f1 = 45. - # f2 = 95. - # b = signal.firwin(3,[f1/fs*2,f2/fs*2]) #3 taps - # b, a = signal.iirfilter(3, [0.4, 0.7], rs=60, btype='band', ftype='cheby2') - # print(len(b)) - # print(len(a)) - # print(b) - # print(a) - - # print(max([max(b),max(a)])) - # convert floating point to fixed point - b, a = signal.ellip(3, 0.009, 80, 0.05, output='ba') + #b, a = signal.ellip(3, 5, 40, 0.6, output='ba') + b, a = signal.bessel(4, 0.09, 'low') print(b) print(a) - # y_sos = signal.sosfilt(sos, x) - # plt.plot(y_tf, 'r', label='TF') - # plt.plot(y_sos, 'k', label='SOS') - # plt.legend(loc='best') - # plt.show() - - B1 = 12 # Number of bits + B1 = 17 # Number of bits L1 = math.floor(math.log((2 ** (B1 - 1) - 1) / max([max(b), max(a)]), 2)) # Round towards zero to avoid overflow + bsc = b * (2 ** B1) asc = a * (2 ** B1) bsc_int = [int(x) for x in bsc] asc_int = [int(x) for x in asc] - print(bsc_int) - print(asc_int) + #print(bsc_int) + #print(asc_int) y1 = fixp_sine(bsc_int, asc_int, B1, L1) # print(y1/2**B1) y2 = floatp_sine(b, a, B1, L1) - # y = edge(B1, L1) + # print(y1) # print(y2) diff --git a/tests/test_iir/test_iir_parallel.py b/tests/test_iir/test_iir_parallel.py index 076c8d4..2b3a8fe 100644 --- a/tests/test_iir/test_iir_parallel.py +++ b/tests/test_iir/test_iir_parallel.py @@ -9,7 +9,6 @@ # TODO: fix these, they should not be failing! -@pytest.mark.xfail @hdl.block def test_iir_api(): @@ -18,25 +17,26 @@ def test_iir_api(): glbl = Global(clock, reset) tbclk = clock.process() - x = Signal(intbv(0)[8:]) - y = Signal(intbv(0)[20:]) + w = (24,0,23) + w_out = (24,0,23) + + ymax = 2**(2*w[0]-1) + vmax = 2**(2*w[0]) + omax = 2**(w_out[0]-1) + + xt = Samples(min=-ymax, max=ymax, word_format = w) + yt = Samples(min=-omax, max=omax, word_format = w_out) + + b = [[101, 0, 132], [23324, 0, 232]] + a = [[24223, 1], [233, 0]] + w = (24, 23, 0) - b = [[1, 0, 1], [0, 0, 1]] - a = [[0, 1], [0, 0]] - w = (24, 0) + #iir_test = FilterIIR(b, a) - iir_test = FilterIIR(b, a) - iir = iir_test.filter_block(glbl, x, y, b, a, w) + iir = iir_parallel.filter_iir_parallel(glbl, xt, yt, b, a, w) - @hdl.instance - def stimulus(): - """Input for test bench taken from text file test.txt""" - for line in open('test.txt'): - x.next = int(line) - yield clock.posedge - raise StopSimulation - return iir, stimulus, tbclk + return hdl.instances() @pytest.mark.xfail diff --git a/tests/test_iir/test_iir_sos.py b/tests/test_iir/test_iir_sos.py new file mode 100644 index 0000000..97ccea7 --- /dev/null +++ b/tests/test_iir/test_iir_sos.py @@ -0,0 +1,107 @@ + +import numpy as np +import scipy.signal as signal +import math +import matplotlib.pyplot as plt + +from filter_blocks.fda import FilterIIR + + +def fixp_sine(sos_asc_int, B1, L1): + + sig = signal.unit_impulse(10) + + B2 = 12 # Number of bits + L2 = math.floor(math.log((2 ** (B2 - 1) - 1) / max(sig), 2)) # Round towards zero to avoid overflow + + sig = np.multiply(sig, 2 ** L2) + sig = sig.round() + sig = sig.astype(int) + print(sig) + + hdlfilter = FilterIIR() + hdlfilter.set_coefficients(sos = sos_asc_int) + hdlfilter.set_cascade(3) + hdlfilter.set_word_format((B1, B1 - 1, 0), (B2, B2 - 1, 0), (1000, 39, 0)) + hdlfilter.set_stimulus(sig) + hdlfilter.run_sim() + y = hdlfilter.get_response() + + #yout = np.divide(y, 2 ** B1) + print(y) + + return y + + +def floatp_sine(sos, B1, L1): + + # sig = [np.sin(0.1*np.pi*i) for i in np.arange(0,x,1)] + sig = signal.unit_impulse(10) + # print(sig) + + B2 = 12 # Number of bits + L2 = math.floor(math.log((2 ** (B2 - 1) - 1) / max(sig), 2)) # Round towards zero to avoid overflow + # print(L) + sig = np.multiply(sig, 2 ** L2) + sig = sig.round() + sig = sig.astype(int) + + y_sos = signal.sosfilt(sos, sig) + + print(y_sos) + + return y_sos + + + + +def main(): + """Meant to emulate how pyfda will pass parameters to filters""" + + # fs = 1000. + # f1 = 45. + # f2 = 95. + # b = signal.firwin(3,[f1/fs*2,f2/fs*2]) #3 taps + #b, a = signal.iirfilter(3, [0.4, 0.7], rs=60, btype='band', ftype='cheby2') + # print(len(b)) + # print(len(a)) + # print(b) + # print(a) + + #print(max([max(b),max(a)])) + #convert floating point to fixed point + + + sos = signal.ellip(3, 0.009, 80, 0.05, output='sos') + x = signal.unit_impulse(10) + y_sos = signal.sosfilt(sos, x) + print(sos) + + #print(y_sos) + + + B1 = 12 # Number of bits + L1 = 2**(B1-1) # Round towards zero to avoid overflow + + sos_sc = sos*(2**(B1-1)) + sos_sc_int = sos_sc.astype(int) + + print(sos_sc_int) + + y1 = fixp_sine(sos_sc_int, B1, L1) + # #print(y1/2**B1) + y2 = floatp_sine(sos, B1, L1) + + # #print(y1) + # #print(y2) + # y1 = y1[6:19] #hardcoded presently. Needs to be + # y2 = y2[:13] + + # #print(y1) + # #print(y2) + # #print( ((y1 - y2) ** 2).mean(axis=None)) + + + +if __name__ == '__main__': + main() \ No newline at end of file