From Python to silicon

```import sys, os

import numpy as np
from numpy import pi, sin, cos, log10, log2, ceil
from numpy.random import uniform
from numpy.fft import fft, fftshift
from scipy.signal import freqz, step, lti

from myhdl import *

import pylab as plt

Nloops  = 100
Nfft    = 4096
MaxGain = 1

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def pass_thru(x, dvi, y, dvo):
"""
Place holder
"""

@always_comb
def rtl():
y.next   = x
dvo.next = dvi

return rtl

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def cmb_dly(clk, x, dvi, y):
"""
Simple delay used with the comb filter
"""

@always(clk.posedge)
def rtl():
if dvi:
y.next   = x

return rtl

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def accum(clk, rst, x, dvi, y, dvo):
"""
Simple Integrator
"""

@always(clk.posedge)
def rtl():
if rst:
y.next = 0
else:
if dvi:
y.next = y + x
dvo.next = True
else:
dvo.next = False

return rtl

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def combd(clk, rst, x, dvi, y, dvo, D=5):
"""
Simple Comb (difference) filter with parameterizable delay
"""
Q = len(x)-1
L = 2**Q

dlyi = [None for i in range(D)]
dlyx = [Signal(intbv(0, min=-L, max=L)) for i in range(D)]

subx = dlyx[D-1]

for ii in range(D):
if ii == 0:
dlyi[ii] = cmb_dly(clk, x, dvi, dlyx[ii])
else:
dlyi[ii] = cmb_dly(clk, dlyx[ii-1], dvi, dlyx[ii])

@always(clk.posedge)
def rtl():
if rst:
y.next = 0
else:
if dvi:
y.next   = x - subx
dvo.next = True
else:
dvo.next = False

return rtl, dlyi

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def decimate(clk, rst, x, dvi, y, dvo, R=8):
"""
"""
Qc  = ceil(log2(R))
cnt = Signal(intbv(0, max=2**Qc))

@always(clk.posedge)
def rtl1():
if rst:
y.next = 0
else:
if cnt == 0:
y.next  = x
dv.next = True
else:
y.next  = 0
dv.next = False

@always(clk.posedge)
def rtl2():
if rst:
cnt.next = 0
else:
if cnt == R-1:
cnt.next = 0
else:
cnt.next = cnt + 1

return rtl1, rtl2

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def interpolate(clk, rst, x, dvi, y, dvo, R=8):
"""
"""
Qc  = ceil(log2(R))
cnt = Signal(intbv(0, max=2**Qc))

@always(clk.posedge)
def rtl1():
if rst:
y.next = 0
else:
if dvi:
y.next  = x
dv.next = True
elif cnt > 0:
y.next  = 0
dv.next = True
else:
y.next  = 0
dv.next = False

@always(clk.posedge)
def rtl2():
if rst:
cnt.next = 0
else:
if dvi:
cnt.next = R-1
elif cnt > 0:
cnt.next = cnt + 1

return rtl1, rtl2

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def cic(clk, rst, x, dvi, y, dvo, M=3, D=8, R=8, Q=7, Type=None):

This module is an fixed-point implementation of CIC filter with configurable
parameters.

Ports:
------
clk:  System clock, must be equal to or greater than the max
sample rate after interpolation or before decimation
rst:  System reset
x:    2's compliment input
dvi:  data valid input, flow control signal for the different rates
y:    2's compliment output
dvo:  data valid output, flow control signal for the different rates

Parameters:
-----------
M:      CIC order
D:      Comb delay
R:      Decimation/interpolation value
Q:      Input word size (7,15,31...)
Type:   'Dec', 'Int', or None
"""
Qi = len(x)-1
Qo = len(y)-1
Li = 2**Qi
Lo = 2**Qo

Cmbi = [None for i in range(M)]
Acci = [None for i in range(M)]
xcmb = [Signal(intbv(0, min=-Lo, max=Lo)) for i in range(M)]
xacc = [Signal(intbv(0, min=-Lo, max=Lo)) for i in range(M)]
xi   = Signal(intbv(0, min=-Lo, max=Lo))

dvoi = Signal(False)
dvoc = [Signal(False) for i in range(M)]
dvoa = [Signal(False) for i in range(M)]

# Add Interpolation, Up sample then filter
if Type == 'Int' :
Int = interploate(clk, rst, x, dvi, xi, dvoi, R)
else:
Int = pass_thru(x, dvi, xi, dvoi)

# Create the Comb Stages
for ii in range(M):
if ii == 0:
Cmbi[ii] = combd(clk, rst, xi, dvoi, xcmb[ii], dvoc[ii], D)
else:
Cmbi[ii] = combd(clk, rst, xcmb[ii-1], dvoc[ii-1], xcmb[ii], dvoc[ii], D)

# Create the Integrator Stages
for ii in range(M):
if ii == 0:
Acci[ii] = accum(clk, rst, xcmb[M-1], dvoc[M-1], xacc[ii], dvoa[ii])
else:
Acci[ii] = accum(clk, rst, xacc[ii-1], dvoa[ii-1], xacc[ii], dvoa[ii])

# Final Decimation Stage, filter then down sample
if Type == 'Dec':
Dec = decimate(clk, rst, xacc[M-1], dvoa[M-1], y, dvo, R)
else:
Dec = pass_thru(xacc[M-1], dvoa[M-1], y, dvo)

return Cmbi, Acci, Dec, Int

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def cmdLineTestBench(run='trace', M=1, D=5, R=50, Q=7, Type=None):
"""
This is the simulation and conversion function for the CIC filter.

Inputs
run  --  Simulation or coversion type
M    --  Order of the CIC filter
D    --  Delay of the Comb Filter
R    --  Decimation/Interpolation size
Q    --  Quantization size of input word
"""
global MaxGain

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Parameters / Globals
MaxGain = D**M        # Max gain of the CIC
L = 2**(Q)            # Max value for input
maxV = L * 2*MaxGain  # Max Value for output
minV = -maxV          # Min Value for output

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Signals
clk = Signal(False)
rst = Signal(False)

x = Signal(intbv(0, min=-L, max=L))
y = Signal(intbv(0, min=minV, max=maxV))

dvi = Signal(True)
dvo = Signal(False)

xcnt  = Signal(0)
N_CLK = 0

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Instantiate MyHDL simulation and coversion functions
if run == 'trace':
dut = traceSignals(cic, clk, rst, x, dvi, y, dvo, M, D, R)
elif run == 'ver':
toVerilog(cic, clk, rst, x, dvi, y, dvo, M, D, R)
return None
elif run == 'vhd':
toVHDL(cic, clk, rst, x, dvi, y, dvo, M, D, R)
return None
else:
dut = cic(clk, rst, x, dvi, y, dvo, M, D, R)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Stimulus
@always(delay(1))
def clkgen():
clk.next = not clk

@always(clk.posedge)
def ist():

# Generate Input samples
if xcnt >= N_CLK:
x.next = int(L*uniform(-1,1))
dvi.next = True
xcnt.next = 0
else:
x.next = 0
dvi.next = False
xcnt.next = xcnt + 1

@instance
def stimulus():
"""
The following is a basic testbench to stimulate the CIC filter
and create some plots.
"""

ysave = [0.0] * Nfft
favg  = np.zeros(Nfft)
xfavg = np.zeros(Nfft)
xs    = np.zeros(Nfft)

yield clk.posedge
rst.next = True
yield delay(10)
rst.next = False
yield delay(10)

for ii in range(Nloops):
for jj in range(Nfft):
xs[jj] = float(x)/L
if dvo:
if y == 0:
#Prevent any log(0) errors, startup zeros
ysave[jj] = 10.0**-10
else:
ysave[jj] = float(y._val)/L  # Undo fix-point
yield clk.negedge

favg = favg + abs(fft(ysave, Nfft)) / Nfft
xfavg = xfavg + abs(fft(xs))/Nfft

favg = favg / Nloops
xfavg = xfavg / Nloops

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Plot responses
xa = 2*pi * np.arange(Nfft)/Nfft - pi
plt.ioff()
plt.plot(xa, fftshift(20*log10(favg/MaxGain)) )
plt.plot(xa, fftshift(20*log10(xfavg)) )
plt.title('CIC Filter Frequency Response')
plt.savefig('plots/myhdl_cic_freq_response_M%d_D%d_R%d.png' % (M,D,R) )
plt.close('all')

print MaxGain
xrsp = 20*log10(favg/MaxGain) - 20*log10(xfavg)
plt.plot(xa, fftshift(xrsp) )
#plt.plot(xa, fftshift(20*log10(xfavg)) )
plt.title('CIC Filter Frequency Response Gain Removed, Output * 1/D')
plt.ylabel('Amplitude dB')
plt.axis([ -pi, pi, -60, 0])
plt.savefig('plots/myhdl_cic_freq_response2_M%d_D%d_R%d.png' % (M,D,R) )
plt.close('all')

# Plot the expected response
num = np.zeros(D+1)
num[0] = 1
num[D] = -1
den = [1, -1]
print num, den

w,H = freqz(num, den)
Hplt = 20*log10(abs(H[1:]))
plt.plot(w[1:],  Hplt)
plt.title('CIC Filter Frequency Response freqz')
plt.axis([ 0, pi, -60, max(Hplt) + 2])
plt.ylabel('Amplitude dB')
plt.savefig('plots/freqz_cic_freq_response_M%d_D%d.png' % (M,D) )
plt.close('all')

Hplt = 20*log10(abs(H[1:]/D))
plt.plot(w[1:],  Hplt)
plt.title('CIC Filter Frequency Response freqz')
plt.axis([ 0, pi, -60, max(Hplt) + 2])
plt.ylabel('Amplitude dB')
plt.savefig('plots/freqz_cic_freq_response2_M%d_D%d.png' % (M,D) )
plt.close('all')

raise StopSimulation

return clkgen, stimulus, ist, dut

#-------------------------------------------------------------------------------
# Script init
#-------------------------------------------------------------------------------
if __name__ == "__main__":

if sys.argv[1] == 'ver':
cmdLineTestBench(sys.argv[1], M=1, D=5, R=5, Q=7, Type='Dec')
os.system('cp cic.v cic_M1_D5_R5_Q7_DEC.v')
cmdLineTestBench(sys.argv[1], M=3, D=8, R=8, Q=15, Type='Dec')
os.system('cp cic.v cic_M3_D8_R8_Q15_DEC.v')
cmdLineTestBench(sys.argv[1], M=5, D=50, R=50, Q=31, Type='Dec')
os.system('cp cic.v cic_M5_D50_R50_Q31_DEC.v')
else:
tb = cmdLineTestBench(sys.argv[1], M=5, D=5, R=0, Q=7)
sim = Simulation(tb)
sim.run()

tb = cmdLineTestBench(sys.argv[1], M=1, D=5, R=5, Q=7, Type='Dec')
sim = Simulation(tb)
sim.run()

tb = cmdLineTestBench(sys.argv[1], M=3, D=8, R=0, Q=15, Type='Dec')
sim = Simulation(tb)
sim.run()

tb = cmdLineTestBench(sys.argv[1], M=3, D=8, R=8, Q=15, Type='Dec')
sim = Simulation(tb)
sim.run()

tb = cmdLineTestBench(sys.argv[1], M=5, D=50, R=50, Q=31)
sim = Simulation(tb)
sim.run()

tb = cmdLineTestBench(sys.argv[1], M=5, D=50, R=50, Q=31)
sim = Simulation(tb)
sim.run()```