\title{myHDL Sawtooth Wave Generator based on the Phase Accumulation method} \author{Steven K Armour} \maketitle

This is a simple SawTooth wave generator based on the phase accumulation method inspired by George Pantazopoulos implementation SawWaveGen in http://old.myhdl.org/doku.php/projects:dsx1000

Libraries used


In [1]:
from myhdl import *
import pandas as pd
from myhdlpeek import Peeker
import  numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from sympy import *
init_printing()

Helper functions


In [2]:
#helper  functions to read in the .v and .vhd generated files into python
def VerilogTextReader(loc, printresult=True):
    with open(f'{loc}.v', 'r') as vText:
        VerilogText=vText.read()
    if printresult:
        print(f'***Verilog modual from {loc}.v***\n\n', VerilogText)
    return VerilogText

def VHDLTextReader(loc, printresult=True):
    with open(f'{loc}.vhd', 'r') as vText:
        VerilogText=vText.read()
    if printresult:
        print(f'***VHDL modual from {loc}.vhd***\n\n', VerilogText)
    return VerilogText

Architecture Setup


In [ ]:
BitWidth=16
#the max in excluded in intbv 
MaxV=int(2**(BitWidth-1)); MinV=-int(2**(BitWidth-1))
a=intbv(0)[BitWidth:]; a=a.signed()
len(a), a.min, MinV, a.max, MaxV

Symbolic Derivation


In [5]:
t, T=symbols('t, T', real=True)
y=Function('y')(t)

In [6]:
yEq=Eq(y, (t/T)-floor(t/T)); yEq


Out[6]:
$$y{\left (t \right )} = - \lfloor{\frac{t}{T}}\rfloor + \frac{t}{T}$$

In [7]:
ft, fc, W=symbols('f_t, f_c, W')
PhaseMax=(ft*2**W)//fc; PhaseMax


Out[7]:
$$\lfloor{\frac{2^{W} f_{t}}{f_{c}}}\rfloor$$

In [8]:
Targets={ft:440, W:BitWidth}; Targets[fc]=100e3
Targets


Out[8]:
$$\left \{ W : 16, \quad f_{c} : 100000.0, \quad f_{t} : 440\right \}$$

In [9]:
PM=PhaseMax.subs(Targets)
f'PhaseMax={PM}'


Out[9]:
'PhaseMax=288'

In [10]:
yN=lambdify((t, T), yEq.rhs, dummify=False)
TN=1/100e3; TN
tN=np.linspace(0, .1, PM//4)

In [11]:
fig, axBot=plt.subplots(ncols=1, nrows=1)
axTop=axBot.twiny()
axBot.plot(tN, yN(tN, TN))
axBot.set_xlabel('Time [s]')
axTop.plot(yN(tN, TN))
axTop.set_xlabel('n');


myHDL Implementation


In [12]:
@block
def SawToothGen(y, clk, rst, Freq, ClkFreq):
    """
    Inputs:
        clk (bool): system clock 
        rst (bool): reset signal
    
    Ouputs:
        y(2's): SawWave Ouput
    
    Parmeters:
        Freq(float): Target Freq
        ClkFreq(float): System Clock Freq
    """
    
    #Registor to store the phase; aka a counter
    Phase=Signal(intbv(0)[BitWidth:])
    
    # Make phase (Counter) limit
    PhaseCeil=int((Freq*2**BitWidth)//ClkFreq)
    
    @always(clk.posedge)
    def logic():
        if rst:
            Phase.next=0
            y.next=0
        else:
            if Phase==PhaseCeil-1:
                y.next=0
                Phase.next=0
            else:
                y.next=y+1
                Phase.next=Phase+1
        
    return instances()

myHDL Testing


In [13]:
Peeker.clear()
y=Signal(intbv(0)[BitWidth:]); Peeker(y, 'y')
#Phase=Signal(modbv(0, max=5)); Peeker(Phase, 'P')
clk, rst=[Signal(bool(0)) for _ in range(2)]
Peeker(clk, 'clk'); Peeker(rst, 'rst')

DUT=SawToothGen(y, clk, rst, 440, 100e3)

def SawToothGen_TB():
    
    @always(delay(1))  ## delay in nano seconds
    def clkGen():
        clk.next = not clk
    
    @instance
    def Stimules():
        for i in range(8*PM):
            yield clk.posedge

        for i in range(4):
            if i <2:
                rst.next=True
            else:
                rst.next=False
            yield clk.posedge


        raise StopSimulation
    return instances()

sim=Simulation(DUT, SawToothGen_TB(), *Peeker.instances()).run()
#Peeker.to_wavedrom()

In [14]:
Simdata=Peeker.to_dataframe()
Simdata=Simdata[Simdata.clk!=0]
Simdata.reset_index(drop=True, inplace=True)
Simdata


Out[14]:
clk rst y
0 1 0 1
1 1 0 2
2 1 0 3
3 1 0 4
4 1 0 5
5 1 0 6
6 1 0 7
7 1 0 8
8 1 0 9
9 1 0 10
10 1 0 11
11 1 0 12
12 1 0 13
13 1 0 14
14 1 0 15
15 1 0 16
16 1 0 17
17 1 0 18
18 1 0 19
19 1 0 20
20 1 0 21
21 1 0 22
22 1 0 23
23 1 0 24
24 1 0 25
25 1 0 26
26 1 0 27
27 1 0 28
28 1 0 29
29 1 0 30
... ... ... ...
2277 1 0 262
2278 1 0 263
2279 1 0 264
2280 1 0 265
2281 1 0 266
2282 1 0 267
2283 1 0 268
2284 1 0 269
2285 1 0 270
2286 1 0 271
2287 1 0 272
2288 1 0 273
2289 1 0 274
2290 1 0 275
2291 1 0 276
2292 1 0 277
2293 1 0 278
2294 1 0 279
2295 1 0 280
2296 1 0 281
2297 1 0 282
2298 1 0 283
2299 1 0 284
2300 1 0 285
2301 1 0 286
2302 1 0 287
2303 1 1 0
2304 1 1 0
2305 1 0 0
2306 1 0 1

2307 rows × 3 columns


In [15]:
Simdata.plot(y='y')


Out[15]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f14cfa6f9b0>

In [16]:
y=Simdata[Simdata.rst!=1]['y']
fy=np.fft.fftshift(np.fft.fft(y, len(y)))
fs=np.fft.fftfreq(len(y))
n=np.where(fs>=0)

plt.plot(fs[n], np.abs(fy[n]))
plt.twinx()
plt.plot(fs[n], np.angle(fy[n], deg=True), color='g', alpha=.3)


Out[16]:
[<matplotlib.lines.Line2D at 0x7f14cf9f54e0>]

In [17]:
f=fs.max()*100e3; f


Out[17]:
$$49978.308026$$

myHDL to Verilog


In [18]:
DUT.convert()
VerilogTextReader('SawTooth');


***Verilog modual from SawTooth.v***

 // File: SawTooth.v
// Generated by MyHDL 0.10
// Date: Wed Apr 11 20:11:25 2018


`timescale 1ns/10ps

module SawTooth (
    y,
    clk,
    rst
);


output [15:0] y;
reg [15:0] y;
input clk;
input rst;

reg [15:0] Phase;



always @(posedge clk) begin: SAWTOOTH_LOGIC
    if (rst) begin
        Phase <= 0;
        y <= 0;
    end
    else begin
        if (($signed({1'b0, Phase}) == (288 - 1))) begin
            y <= 0;
            Phase <= 0;
        end
        else begin
            y <= (y + 1);
            Phase <= (Phase + 1);
        end
    end
end

endmodule

/home/iridium/anaconda3/lib/python3.6/site-packages/myhdl/conversion/_toVerilog.py:296: ToVerilogWarning: Output port is read internally: y
  category=ToVerilogWarning

In [ ]: