In [1]:
from __future__ import division
from __future__ import print_function
from math import ceil
from math import log
from math import sin
from math import pi
from fractions import gcd
import myhdl
print(myhdl.__version__)
from myhdl import *
There was a question posted on reddit about a music box implementation. The following is an implementation of the simple example posted implemented in MyHDL. Experimenting and verifying an exmaple like this in MyHDL/Python is much easier than Verilog/VHDL (e.g. plot the outputs).
In [2]:
def m_musicbox(clock, reset, note, nv, sample_rate=48e3, clock_rate=50e6):
""" module to generate a "tone".
Port Map
--------
clock : circuit synchronous clock
reset : circult reset
note : digital signal for the note
nv : sample valid strobe
"""
# Build the ROM table to hold the "note".
# Replace the following with which ever "note"
# sequence (algorithm) one desires
Fs = sample_rate
nmax = note.val.max
f1,f2 = Fs/40, Fs/12
print("The note has a tone at %.3f and %.3f" % (f1,f2,))
nsmp = int((f1*f2)/gcd(f1,f2))
note_rom = [(sin(p1)+sin(p2))/2 for p1,p2 in
zip([((2*pi)/Fs)*ii*f1 for ii in range(nsmp) ],
[((2*pi)/Fs)*ii*f2 for ii in range(nsmp)]) ]
# convert the ROM from float to integer for the requested range
note_rom = tuple([int(round(nmax*nn)) for nn in note_rom])
# Signals and variable for the logic
ticks_per_fs = int(ceil(clock_rate/sample_rate))
sample_rate_cnt = Signal(intbv(1, min=0, max=ticks_per_fs+1))
# note ROM current index
Nsmp = len(note_rom)
noteidx = Signal(intbv(0, min=0, max=Nsmp))
@always_seq(clock.posedge, reset=reset)
def rtl():
if sample_rate_cnt == ticks_per_fs:
sample_rate_cnt.next = 1
note.next = note_rom[noteidx]
nv.next = True
noteidx.next = noteidx + 1 if noteidx < Nsmp-1 else 0
else:
sample_rate_cnt.next = sample_rate_cnt + 1
nv.next = False
return rtl
# quick check
m_musicbox(Signal(bool(0)), ResetSignal(0, 0, False),
Signal(intbv(0, min=-16, max=16)), Signal(bool(0)) )
Out[2]:
In [3]:
def test(Nsmp=10, Fs=48e3, Nmax=16, convert=False):
Fclk = 4*48e3
Ts = 1/Fs
xv,tv = [],[]
nmax = Nmax
clock = Signal(bool(0))
reset = ResetSignal(0, active=0, async=True)
note = Signal(intbv(0, min=-nmax, max=nmax))
nv = Signal(bool(0))
# clock driver
@always(delay(10))
def tbclk():
clock.next = not clock
# reset driver
def pulse_reset():
reset.next = reset.active
yield delay(100)
reset.next = not reset.active
yield clock.posedge
# stimulus driver
def _test():
tbdut = m_musicbox(clock, reset, note, nv, Fs, Fclk)
@instance
def tbstim():
t,scnt = 0,0
yield pulse_reset()
while scnt < Nsmp:
if nv:
xv.append(int(note.val))
tv.append(t)
t += Ts
scnt += 1
yield clock.posedge
raise StopSimulation
return tbclk, tbdut, tbstim
# run the simulation, using _test as the stimulus
Simulation(traceSignals(_test)).run()
if convert:
# convert the design to VHDL
toVHDL(m_musicbox, clock, reset, note, nv,
sample_rate=Fs, clock_rate=50e6)
toVerilog(m_musicbox, clock, reset, note, nv,
sample_rate=Fs, clock_rate=50e6)
return tv,xv
In [4]:
# run the simulation
Fs = 48e3
Nmax = 2**(15-1)
tv,xv = test(Nsmp=100, Fs=Fs, Nmax=Nmax, convert=False)
print("Simulation complete")
In [5]:
# run the simulation and collect 1000 samples and plot
import matplotlib.pyplot as plt
tv,xv = test(Nsmp=Fs//200, Fs=Fs, Nmax=Nmax, convert=False)
fig,ax = plt.subplots(1, figsize=(9,3,))
ax.plot(tv, xv)
ax.set_ylim(-Nmax, Nmax)
ax.set_xlabel("Time [sec]")
ax.set_ylabel("Raw Amplitude")
# plot the power spectral density (see the spikes of the "tones")
fig,ax = plt.subplots(1, figsize=(9,3,))
tv,xv = test(Nsmp=Fs, Fs=Fs, Nmax=Nmax, convert=False)
p,f = ax.psd(xv, Fs=Fs, NFFT=8192)
ax.set_ylim(-60, 80)
ax.set_xlim(0, 6000)
Out[5]:
In [6]:
# the following requires the myhdl_tools package
# (http://www.bitbucket.org/cfelton/myhdl_tools)
from myhdl_tools import vcd
# rerun the test with less samples
test(Nsmp=5, Fs=Fs, Nmax=Nmax, convert=False)
# not a great VCD plotter but enough to get an idea,
# use gtkwave for real debug
vcd.parse_and_plot("_test.vcd")
Out[6]:
In [7]:
# convert and run tools, this requires the gizflo
# package and can be found at http://www.github.com/cfelton/gizflo
tv,xv = test(Nsmp=2, Fs=Fs, Nmax=Nmax, convert=True)