This is a sinewave generator using a SigmaDelta DAC, i.e. it uses only one ouput bit and does not need external hardware beside the low pass filter. The Delta-Sigma DAC core was created by George Pantazopoulos. For speed reasons we use a modified version. The sine calculation is a modified version of the MyHDL Cookbook Example.
#!/usr/bin/env python """ Sinewave generator using George Pantazopoulos' Sigma Delta DAC. """ from myhdl import * from math import log, ceil from dac_dsx1000_hd import dac_dsx1000 from cordic import SinCos # ---------------------------------------------------------------------------- def top(clk, pdm_o, CLK_HZ): """ Top-level module. Sine wave generator """ __author__ = "Thomas Traber (orgin: George Pantazopoulos http://www.gammaburst.net)" OUTPUT_FREQ_HZ = 10000.0 PHASE_RESOLUTION = 16 PHASE_INC = int( OUTPUT_FREQ_HZ / CLK_HZ * 2**PHASE_RESOLUTION) print "CLK_HZ = ", CLK_HZ print "OUTPUT_FREQ_HZ = ", OUTPUT_FREQ_HZ # Reset signal rst = Signal(bool(0)) # PCM Audio signal. # Note that we control the (theoretical) resolution of the converter # by setting the bit width of this signal. DAC_RESOLUTION = 16 pcmi = Sigint(min=-2**DAC_RESOLUTION/2,max=2**DAC_RESOLUTION/2-1) pcmq = Sigint(min=-2**DAC_RESOLUTION/2,max=2**DAC_RESOLUTION/2-1) pcm_data = Sigint(N=DAC_RESOLUTION) ampl = Sig(3,min=-2**15,max=2**15-1) done = Sigbool(0) samplingclk = Sigbool(0) start = Sigbool(0) # Dummy reset driver @always_comb def rstDrv(): rst.next = False phase_accu = Sigint(min=-2**(PHASE_RESOLUTION-1),max=2**(PHASE_RESOLUTION-1)-1) sinegen = SinCos(pcmi,pcmq,done,ampl,phase_accu,clk,clk,rst) @always(done.posedge) def sampler(): if done: pcm_data.next = pcmi + 2**(DAC_RESOLUTION-1) @always(clk.posedge) def PhaseAccumulator(): if phase_accu >= phase_accu.max - PHASE_INC: phase_accu.next = phase_accu.min else: phase_accu.next = phase_accu + PHASE_INC # Instantiation of our Delta-Sigma DAC DAC = dac_dsx1000(clk_i=clk, rst_i=rst, pcm_i=pcm_data, pdm_o=pdm_o) return instances() # ---------------------------------------------------------------------------- def Datasaver(clk,pdm): global outfile outfile = file("sinewave.dat","w") @always(clk.posedge) def save_data(): outfile.write("%s\n"%(int(pdm))) return instances() # ---------------------------------------------------------------------------- if __name__=="__main__": # Set this to your FPGA's clock frequency CLK_HZ = 81000000 if __name__=="__main__": import sys # Signals connected to the FPGA/CPLD clk = Signal(bool(0)) pdm_o = Signal(bool(0)) if sys.argv[1]=="v": # MyHDL to Verilog conversion toVerilog.name = "dsx1000" toVerilog(top, clk, pdm_o, CLK_HZ) else: from testenv import * clkgen = Clkgen(clk,2) datasaver = Datasaver(clk,pdm_o) simulate(2**18,top(clk,pdm_o,CLK_HZ),clkgen,datasaver)
Datasaver in the code above saves the simulation data in a file which is examined afterwords using matplotlib.
The low frequency spectrum looks pretty good:
At higher frequencies the Sigma Delta Noise can be seen. This is filtered out by the analog low pass filter in hardware.
The noise step at very high frequencies is supposed to be caused by the non constant sampling rate of the sinewave. The function sampler in the code above samples the sine wave, whenever the cordic calculator signals done.