Source code for caf_verilog.sig_gen
from . quantizer import quantize
import os
from jinja2 import Environment, FileSystemLoader, Template
import numpy as np
from .caf_verilog_base import CafVerilogBase
from . quantizer import bin_num
filedir = os.path.dirname(os.path.realpath(__file__))
sig_gen_tb_module_path = os.path.join(filedir, '..', 'src')
sig_gen_module_path = os.path.join(filedir, '..', 'src', 'sig_gen.v')
[docs]class SigGen(CafVerilogBase):
def __init__(self, freq_res, fs, n_bits,
output_dir='.'):
self.f = freq_res
self.fs = fs
self.n_bits = n_bits
self.phase_bits = self.calc_smallest_phase_size()
self.output_dir = output_dir
self.tb_filename = 'sig_gen_tb.v'
self.sig_gen_name = "sig_gen_%s_%s_%s" % (str(fs).replace('.', '')[:3], self.phase_bits, self.n_bits)
self.lut_filename = "%s.txt" % (self.sig_gen_name)
self.test_output_filename = "sig_gen_output_values.txt"
self.write_module()
[docs] def calc_smallest_phase_size(self):
pb = phase_bits(self.fs, self.f)
if pb <= self.n_bits:
diff = self.n_bits - pb + 1
pb += diff
return pb
[docs] def template_dict(self, inst_name=None):
t_dict = {'phase_bits': self.phase_bits, 'n_bits': self.n_bits}
t_dict['lut_filename'] = os.path.abspath(os.path.join(self.output_dir, self.lut_filename))
t_dict['sig_gen_inst_name'] = inst_name
t_dict['sig_gen_output'] = os.path.abspath(os.path.join(self.output_dir, self.test_output_filename))
t_dict['lut_length'] = 2 ** (self.n_bits + 1) - 1
t_dict['sig_gen_name'] = self.sig_gen_name
return t_dict
[docs] def gen_tb(self, freq=None):
"""
Generate a testbench for the specified frequency.
:param freq:
:return:
"""
self.write_sig_gen_tb_module(freq)
[docs] def write_sig_gen_tb_module(self, freq=None):
"""
Write out a testbench file to test the sig_gen module.
:param freq:
:return:
"""
des_freq = freq if freq else self.fs / 4
increment = phase_increment(des_freq, self.phase_bits, self.fs)
t_dict = self.template_dict('sig_gen_tb')
t_dict['freq_step_str'] = freq_step_str(self.phase_bits, increment)
template_loader = FileSystemLoader(searchpath=sig_gen_tb_module_path)
env = Environment(loader=template_loader)
template = env.get_template('sig_gen_tb.v')
sig_gen_tb = template.render(**t_dict)
with open(os.path.join(self.output_dir, self.tb_filename), 'w+') as tb_file:
tb_file.write(sig_gen_tb)
[docs] def write_lut_values(self):
"""
:return:
:rtype: None
"""
values = lut_values(self.n_bits)
with open(os.path.join(self.output_dir, self.lut_filename), 'w+') as lut_file:
for val in values:
lut_file.write(bin_num(val, self.n_bits) + "\n")
[docs] def write_module(self):
self.write_lut_values()
module_template = None
t_dict = self.template_dict()
with open(sig_gen_module_path) as module_file:
module_template = Template(module_file.read())
with open(os.path.join(self.output_dir, self.sig_gen_name+".v"), "w+") as module_file:
module_file.write(module_template.render(**t_dict))
[docs]def freq_step_str(phase_bits, increment):
fss = "%d'%s" % (phase_bits - 1, str(bin(increment))[1:])
return fss
[docs]def lut_values(n_bits):
"""
Create and return an array of values quantized to the number of bits requested.
The list is always 2 ** (n_bits + 1) in length for n_bits.
:param n_bits:
:return:
:rtype: list
"""
step = 2 * np.pi / 2 ** (n_bits + 1)
n = np.arange(0, 2 * np.pi, step)
values = np.sin(n)
values = quantize(values, n_bits)
return values
[docs]def phase_bits(f_clk, freq_res):
"""
Calculate the number of bits the phase accumulator will need.
:param f_clk: Sampling rate/clock frequency
:param freq_res: Frequency resolution required
:return:
"""
return int(np.ceil(np.log2(f_clk / freq_res)))
[docs]def phase_increment(f_out, phase_bits, f_clk):
"""
Calculate the phase increment required to produce the desired frequency.
:param f_out:
:param phase_bits:
:param f_clk:
:return:
"""
return int(f_out * 2**phase_bits / f_clk)