In [1]:
import math
import numpy as np
def sine(x):
return np.sin(2 * math.pi * x)
x = np.linspace(0., 1., num=256, endpoint=False)
In [2]:
import magma as m
m.set_mantle_target('ice40')
In [3]:
import mantle
def DefineDDS(n, has_ce=False):
class _DDS(m.Circuit):
name = f'DDS{n}'
IO = ['I', m.In(m.UInt(n)), "O", m.Out(m.UInt(n))] + m.ClockInterface(has_ce=has_ce)
@classmethod
def definition(io):
reg = mantle.Register(n, has_ce=has_ce)
m.wire(reg(m.uint(reg.O) + io.I, CE=io.CE), io.O)
return _DDS
def DDS(n, has_ce=False):
return DefineDDS(n, has_ce)()
In [4]:
from loam.boards.icestick import IceStick
icestick = IceStick()
icestick.Clock.on()
for i in range(8):
icestick.J1[i].input().on()
icestick.J3[i].output().on()
In [5]:
main = icestick.main()
dds = DDS(16, True)
wavetable = 128 + 127 * sine(x)
wavetable = [int(x) for x in wavetable]
rom = mantle.Memory(height=256, width=16, rom=list(wavetable), readonly=True)
phase = m.concat(main.J1, m.bits(0,8))
# You can also hardcode a constant as the phase
# phase = m.concat(m.bits(32, 8), m.bits(0,8))
# Use counter COUT hooked up to CE of registers to slow everything down so we can see it on the LEDs
c = mantle.Counter(10)
addr = dds( phase, CE=c.COUT)
O = rom( addr[8:] )
m.wire( c.COUT, rom.RE )
m.wire( O[0:8], main.J3 )
m.EndCircuit()
Compile and build.
In [6]:
m.compile('build/dds', main)
In [7]:
%%bash
cd build
cat sin.pcf
yosys -q -p 'synth_ice40 -top main -blif dds.blif' dds.v
arachne-pnr -q -d 1k -o dds.txt -p dds.pcf dds.blif
icepack dds.txt dds.bin
iceprog dds.bin
We can wire up the GPIO pins to a logic analyzer to verify that our circuit produces the correct sine waveform.
We can also use Saleae's export data feature to output a csv file. We'll load this data into Python and plot the results.
In [8]:
import csv
import magma as m
with open("data/dds-capture.csv") as sine_capture_csv:
csv_reader = csv.reader(sine_capture_csv)
next(csv_reader, None) # skip the headers
rows = [row for row in csv_reader]
timestamps = [float(row[0]) for row in rows]
values = [m.bitutils.seq2int(tuple(int(x) for x in row[1:])) for row in rows]
TODO: Why do we have this little bit of jitter? Logic analyzer is running at 25 MS/s, 3.3+ Volts for 1s
In [9]:
%matplotlib inline
import matplotlib.pyplot as plt
plt.plot(timestamps[:100], values[:100], "b.")
Out[9]:
In [ ]: