I needed to add the voice output to my FPGA based system.
I have decided to use a very simple Sigma-Delta DAC with dual loop.
This archive contains VHDL sources for two equivalent implementations.
The first one (dac_dsm2v.vhd) uses the variables in the process (which maybe
not acceptable for some synthesis tools).
The second one (dac_dsm2.vhd) is slightly less readable, but should be easier
to synthesize.
The top entity (dac_dsm2_top.vhd) instantiates the 16-bit DAC for synthesis
(however the number of bits is adjustable with "nbits" generic).
For Xilinx ISE 9.2 i've got the following results:
Speed oriented synthesis for device xc3s200, package ft256, speed -5 resulted with
100MHz clock at 4% slice utilization (so you can get even 1000 oversampling ratio
for 100kHz sampling frequency).
Synthesis of 24-bit version of the DAC resulted for the same chip in 5% slice
utilization and also 100MHz fclk max.
The archive contains also the testbench (dac_dsm2_tb.vhd), allowing you to verify
the response of the DAQ for input signal consisting of three sinusoids.
The show_dac.py program shows the spectrum of the output signal.
I hope, that this simple VHDL code may be useful for someone.
Please consider it a public domain code.
Wojciech M. Zabolotny
wz...@ise.pw.edu.pl
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.6.3).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `#!/bin/sh' line above, then type `sh FILE'.
#
lock_dir=_sh08792
# Made on 2009-04-29 20:59 CEST by <wzab@wzab>.
# Source directory was `/tmp/DAC/files'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 1668 -rw-r--r-- dac_dsm2_top.vhd
# 2112 -rw-r--r-- dac_dsm2.vhd
# 2196 -rw-r--r-- dac_dsm2v.vhd
# 4855 -rw-r--r-- dac_tb.vhd
# 486 -rw-rw-r-- makefile
# 340 -rw-r--r-- show_dac.py
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums. Consider installing GNU coreutils.'
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
if test "$gettext_dir" = FAILED && test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir ;;
esac
fi
if test "$locale_dir" = FAILED && test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
echo=echo
else
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901
if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps. Consider getting and'
${echo} 'installing GNU `touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir}
then : ; else ${echo} 'lock directory '${lock_dir}' exists'
exit 1
fi
if mkdir ${lock_dir}
then ${echo} 'x - created lock directory `'${lock_dir}\''.'
else ${echo} 'x - failed to create lock directory `'${lock_dir}\''.'
exit 1
fi
# ============= dac_dsm2_top.vhd ==============
if test -f 'dac_dsm2_top.vhd' && test "$first_param" != -c; then
${echo} 'x -SKIPPING dac_dsm2_top.vhd (file already exists)'
else
${echo} 'x - extracting dac_dsm2_top.vhd (text)'
sed 's/^X//' << 'SHAR_EOF' > 'dac_dsm2_top.vhd' &&
-------------------------------------------------------------------------------
-- Title : DAC_DSM2 - sigma-delta DAC converter with double loop
-- Project :
-------------------------------------------------------------------------------
-- File : dac_dsm2.vhd
-- Author : Wojciech M. Zabolotny ( wzab[at]ise.pw.edu.pl )
-- Company :
-- Created : 2009-04-28
-- Last update: 2009-04-29
-- Platform :
-- Standard : VHDL'93c
-------------------------------------------------------------------------------
-- Description: Top entity for synthesis of 16-bit version
-------------------------------------------------------------------------------
-- Copyright (c) 2009 - THIS IS PUBLIC DOMAIN CODE!!!
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2009-04-28 1.0 wzab Created
-------------------------------------------------------------------------------
X
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
X
entity dac_dsm2_top is
X
X port (
X din : in signed(15 downto 0);
X dout : out std_logic;
X clk : in std_logic;
X n_rst : in std_logic);
X
end dac_dsm2_top;
X
architecture beh1 of dac_dsm2_top is
X
X component dac_dsm2
X generic (
X nbits : integer);
X port (
X din : in signed((nbits-1) downto 0);
X dout : out std_logic;
X clk : in std_logic;
X n_rst : in std_logic);
X end component;
X
begin
X dac_dsm2_1 : dac_dsm2
X generic map (
X nbits => 16)
X port map (
X din => din,
X dout => dout,
X clk => clk,
X n_rst => n_rst);
end beh1;
SHAR_EOF
(set 20 09 04 29 20 13 59 'dac_dsm2_top.vhd'; eval "$shar_touch") &&
chmod 0644 'dac_dsm2_top.vhd'
if test $? -ne 0
then ${echo} 'restore of dac_dsm2_top.vhd failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'dac_dsm2_top.vhd: MD5 check failed'
) << SHAR_EOF
23bcebbdb7098491d7a676d59866d331 dac_dsm2_top.vhd
SHAR_EOF
else
test `LC_ALL=C wc -c < 'dac_dsm2_top.vhd'` -ne 1668 && \
${echo} 'restoration warning: size of dac_dsm2_top.vhd is not 1668'
fi
fi
# ============= dac_dsm2.vhd ==============
if test -f 'dac_dsm2.vhd' && test "$first_param" != -c; then
${echo} 'x -SKIPPING dac_dsm2.vhd (file already exists)'
else
${echo} 'x - extracting dac_dsm2.vhd (text)'
sed 's/^X//' << 'SHAR_EOF' > 'dac_dsm2.vhd' &&
-------------------------------------------------------------------------------
-- Title : DAC_DSM2 - sigma-delta DAC converter with double loop
-- Project :
-------------------------------------------------------------------------------
-- File : dac_dsm2.vhd
-- Author : Wojciech M. Zabolotny ( wzab[at]ise.pw.edu.pl )
-- Company :
-- Created : 2009-04-28
-- Last update: 2009-04-29
-- Platform :
-- Standard : VHDL'93c
-------------------------------------------------------------------------------
-- Description: Implementation without variables
-------------------------------------------------------------------------------
-- Copyright (c) 2009 - THIS IS PUBLIC DOMAIN CODE!!!
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2009-04-28 1.0 wzab Created
-------------------------------------------------------------------------------
X
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
X
entity dac_dsm2 is
X
X generic (
X nbits : integer := 16);
X
X port (
X din : in signed((nbits-1) downto 0);
X dout : out std_logic;
X clk : in std_logic;
X n_rst : in std_logic);
X
end dac_dsm2;
X
architecture beh1 of dac_dsm2 is
X
X signal del1, del2, d_q : signed(nbits+2 downto 0) := (others => '0');
X constant c1 : signed(nbits+2 downto 0) := to_signed(1, nbits+3);
X constant c_1 : signed(nbits+2 downto 0) := to_signed(-1, nbits+3);
begin -- beh1
X
X process (clk, n_rst)
X begin -- process
X if n_rst = '0' then -- asynchronous reset (active low)
X del1 <= (others => '0');
X del2 <= (others => '0');
X dout <= '0';
X elsif clk'event and clk = '1' then -- rising clock edge
X del1 <= din - d_q + del1;
X del2 <= din - d_q + del1 - d_q + del2;
X if din - d_q + del1 - d_q + del2 > 0 then
X d_q <= shift_left(c1, nbits);
X dout <= '1';
X else
X d_q <= shift_left(c_1, nbits);
X dout <= '0';
X end if;
X end if;
X end process;
X
X
end beh1;
SHAR_EOF
(set 20 09 04 29 20 09 45 'dac_dsm2.vhd'; eval "$shar_touch") &&
chmod 0644 'dac_dsm2.vhd'
if test $? -ne 0
then ${echo} 'restore of dac_dsm2.vhd failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'dac_dsm2.vhd: MD5 check failed'
) << SHAR_EOF
86acf39552bf6854526ee83f09f9f586 dac_dsm2.vhd
SHAR_EOF
else
test `LC_ALL=C wc -c < 'dac_dsm2.vhd'` -ne 2112 && \
${echo} 'restoration warning: size of dac_dsm2.vhd is not 2112'
fi
fi
# ============= dac_dsm2v.vhd ==============
if test -f 'dac_dsm2v.vhd' && test "$first_param" != -c; then
${echo} 'x -SKIPPING dac_dsm2v.vhd (file already exists)'
else
${echo} 'x - extracting dac_dsm2v.vhd (text)'
sed 's/^X//' << 'SHAR_EOF' > 'dac_dsm2v.vhd' &&
-------------------------------------------------------------------------------
-- Title : DAC_DSM2 - sigma-delta DAC converter with double loop
-- Project :
-------------------------------------------------------------------------------
-- File : dac_dsm2.vhd
-- Author : Wojciech M. Zabolotny ( wzab[at]ise.pw.edu.pl )
-- Company :
-- Created : 2009-04-28
-- Last update: 2009-04-29
-- Platform :
-- Standard : VHDL'93c
-------------------------------------------------------------------------------
-- Description: Implementation with use of variables inside of process
-------------------------------------------------------------------------------
-- Copyright (c) 2009 - THIS IS PUBLIC DOMAIN CODE!!!
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2009-04-28 1.0 wzab Created
-------------------------------------------------------------------------------
X
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
X
entity dac_dsm2v is
X
X generic (
X nbits : integer := 16);
X
X port (
X din : in signed((nbits-1) downto 0);
X dout : out std_logic;
X clk : in std_logic;
X n_rst : in std_logic);
X
end dac_dsm2v;
X
architecture beh1 of dac_dsm2v is
X
X signal del1, del2, d_q : signed(nbits+2 downto 0) := (others => '0');
X constant c1 : signed(nbits+2 downto 0) := to_signed(1, nbits+3);
X constant c_1 : signed(nbits+2 downto 0) := to_signed(-1, nbits+3);
begin -- beh1
X
X process (clk, n_rst)
X variable v1, v2 : signed(nbits+2 downto 0) := (others => '0');
X begin -- process
X if n_rst = '0' then -- asynchronous reset (active low)
X del1 <= (others => '0');
X del2 <= (others => '0');
X dout <= '0';
X elsif clk'event and clk = '1' then -- rising clock edge
X v1 := din - d_q + del1;
X v2 := v1 - d_q + del2;
X if v2 > 0 then
X d_q <= shift_left(c1, nbits);
X dout <= '1';
X else
X d_q <= shift_left(c_1, nbits);
X dout <= '0';
X end if;
X del1 <= v1;
X del2 <= v2;
X end if;
X end process;
X
X
end beh1;
SHAR_EOF
(set 20 09 04 29 20 09 52 'dac_dsm2v.vhd'; eval "$shar_touch") &&
chmod 0644 'dac_dsm2v.vhd'
if test $? -ne 0
then ${echo} 'restore of dac_dsm2v.vhd failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'dac_dsm2v.vhd: MD5 check failed'
) << SHAR_EOF
34768cc51a47d97d6edae6113dd15654 dac_dsm2v.vhd
SHAR_EOF
else
test `LC_ALL=C wc -c < 'dac_dsm2v.vhd'` -ne 2196 && \
${echo} 'restoration warning: size of dac_dsm2v.vhd is not 2196'
fi
fi
# ============= dac_tb.vhd ==============
if test -f 'dac_tb.vhd' && test "$first_param" != -c; then
${echo} 'x -SKIPPING dac_tb.vhd (file already exists)'
else
${echo} 'x - extracting dac_tb.vhd (text)'
sed 's/^X//' << 'SHAR_EOF' > 'dac_tb.vhd' &&
-------------------------------------------------------------------------------
-- Title : Testbench for design "dac_dsm2"
-- Project :
-------------------------------------------------------------------------------
-- File : dac_dsm2_tb.vhd
-- Author : Wojciech M. Zabolotny ( wzab[at]ise.pw.edu.pl )
-- Company :
-- Created : 2009-04-28
-- Last update: 2009-04-29
-- Platform :
-- Standard : VHDL'93c
-------------------------------------------------------------------------------
-- Description: Testbench for S-D DAC converters
-------------------------------------------------------------------------------
-- Copyright (c) 2009 - THIS IS PUBLIC DOMAIN CODE!!!
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2009-04-28 1.0 wzab Created
-------------------------------------------------------------------------------
X
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use std.textio.all;
X
-------------------------------------------------------------------------------
X
entity dac_tb is
X
end dac_tb;
X
-------------------------------------------------------------------------------
X
architecture beh1 of dac_tb is
X
X -- Configuration of the testbench
X -- Clock period [ns]
X constant TCLK : time := 10 ns;
X constant OSR : real := 256.0; -- Oversampling ratio:
X
X constant FREQ1 : real := 0.35; -- Frequency of the first sinusoid (relative to the sampling frequency)
X constant AMP1 : real := 0.2; -- Amplitude of the first sinusoid
X constant PHASE1 : real := 0.35; -- Phase of the first sinusoid
X constant FREQ2 : real := 0.3; -- Frequency of the second sinusoid (relative to the sampling frequency)
X constant AMP2 : real := 0.4; -- Amplitude of the second sinusoid
X constant PHASE2 : real := 0.35; -- Phase of the second sinusoid
X constant FREQ3 : real := 0.25; -- Frequency of the third sinusoid (relative to the sampling frequency)
X constant AMP3 : real := 0.3; -- Amplitude of the third sinusoid
X constant PHASE3 : real := 0.35; -- Phase of the third sinusoid
X constant TSTEP : real := 3.1415926 / OSR; -- Phase/time step of the sinusoid generation (considering the OSR)
X
X file OUTFILE : text is out "dac_tb.dat";
X
X component dac_dsm2
X generic (
X nbits : integer);
X port (
X din : in signed((nbits-1) downto 0);
X dout : out std_logic;
X clk : in std_logic;
X n_rst : in std_logic);
X end component;
X
X component dac_dsm2v
X generic (
X nbits : integer);
X port (
X din : in signed((nbits-1) downto 0);
X dout : out std_logic;
X clk : in std_logic;
X n_rst : in std_logic);
X end component;
X
X -- component generics
X constant nbits : integer := 16;
X
X -- component ports
X signal din : signed((nbits-1) downto 0) := (others => '0');
X signal dout : std_logic := '0';
X signal dout_v : std_logic := '0';
X signal n_rst : std_logic := '0';
X
X -- input signal
X signal s_inp : real := 0.0;
X signal s_time : real := 0.0;
X -- clock
X signal Clk : std_logic := '1';
X
begin -- beh1
X
X -- component instantiation
X DUT1 : dac_dsm2
X generic map (
X nbits => nbits)
X port map (
X din => din,
X dout => dout,
X clk => clk,
X n_rst => n_rst);
X
X DUT2 : dac_dsm2v
X generic map (
X nbits => nbits)
X port map (
X din => din,
X dout => dout_v,
X clk => clk,
X n_rst => n_rst);
X
X -- clock generation
X Clk <= not Clk after TCLK/2.0;
X
X -- Generation of input signal and simulation of DACs
X din <= to_signed(integer(s_inp), nbits);
X process (clk, n_rst)
X variable s : line;
X variable c : character := ' ';
X variable c1 : character := '1';
X variable c0 : character := '0';
X begin -- process
X if n_rst = '0' then -- asynchronous reset (active low)
X s_time <= 0.0;
X elsif clk'event and clk = '1' then -- rising clock edge
X s_time <= s_time+TSTEP;
X s_inp <= (2.0**(nbits-1))*(
X AMP1 * sin(s_time*FREQ1+PHASE1) +
X AMP2 * sin(s_time*FREQ2+PHASE2) +
X AMP3 * sin(s_time*FREQ3+PHASE3)
X );
X -- Write results to file
X write(s, s_inp);
X write(s, c);
X
X if dout = '1' then
X write(s, c1);
X else
X write(s, c0);
X end if;
X write(s, c);
X
X if dout_v = '1' then
X write(s, c1);
X else
X write(s, c0);
X end if;
X writeline(OUTFILE, s);
X end if;
X end process;
X
X -- waveform generation
X WaveGen_Proc : process
X begin
X -- insert signal assignments here
X wait until Clk = '1';
X wait for 25 ns;
X n_rst <= '1';
X end process WaveGen_Proc;
X
end beh1;
X
X
SHAR_EOF
(set 20 09 04 29 20 08 58 'dac_tb.vhd'; eval "$shar_touch") &&
chmod 0644 'dac_tb.vhd'
if test $? -ne 0
then ${echo} 'restore of dac_tb.vhd failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'dac_tb.vhd: MD5 check failed'
) << SHAR_EOF
b4cd43244592c51bbfb2ad9201b1b23e dac_tb.vhd
SHAR_EOF
else
test `LC_ALL=C wc -c < 'dac_tb.vhd'` -ne 4855 && \
${echo} 'restoration warning: size of dac_tb.vhd is not 4855'
fi
fi
# ============= makefile ==============
if test -f 'makefile' && test "$first_param" != -c; then
${echo} 'x -SKIPPING makefile (file already exists)'
else
${echo} 'x - extracting makefile (text)'
sed 's/^X//' << 'SHAR_EOF' > 'makefile' &&
VHDLS = \
X dac_dsm2.vhd \
X dac_dsm2v.vhd \
X dac_tb.vhd \
X
X
STD=standard
#STD=synopsys
show_spectra: dac_tb.dat
X python show_dac.py
show_ghw: dac_tb dac_tb.ghw
X gtkwave dac_tb.ghw wzrmb.sav
dac_tb: ${VHDLS}
X ghdl -a --std=93c --ieee=${STD} ${VHDLS}
X ghdl -e --std=93c -fexplicit --ieee=${STD} dac_tb
dac_tb.dat: dac_tb
X ./dac_tb --wave=dac_tb.ghw --stop-time=500000ns
dac_tb.ghw: dac_tb
X ./dac_tb --wave=dac_tb.ghw --stop-time=500000ns
clean:
X rm *.o *.vcd *.ghw *.dat *.cf dac_tb
SHAR_EOF
(set 20 09 04 29 20 59 06 'makefile'; eval "$shar_touch") &&
chmod 0664 'makefile'
if test $? -ne 0
then ${echo} 'restore of makefile failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'makefile: MD5 check failed'
) << SHAR_EOF
d25e0fac548d1aacea5bda439d7ab17d makefile
SHAR_EOF
else
test `LC_ALL=C wc -c < 'makefile'` -ne 486 && \
${echo} 'restoration warning: size of makefile is not 486'
fi
fi
# ============= show_dac.py ==============
if test -f 'show_dac.py' && test "$first_param" != -c; then
${echo} 'x -SKIPPING show_dac.py (file already exists)'
else
${echo} 'x - extracting show_dac.py (text)'
sed 's/^X//' << 'SHAR_EOF' > 'show_dac.py' &&
from pylab import *
w=load("dac_tb.dat")
t1=[i[2] for i in w]; t1=t1-mean(t1)
t2=[i[2] for i in w]; t2=t2-mean(t2)
f1=20.0*log(0.0001+abs(fft(t1)))
f2=20.0*log(0.0001+abs(fft(t2)))
plot(f1)
plot(f2)
title("Whole spectra of output signal")
grid()
show()
plot(f1[0:1000])
plot(f2[0:1000])
title("First 1001 samples of spectra")
grid()
show()
SHAR_EOF
(set 20 09 04 29 19 56 11 'show_dac.py'; eval "$shar_touch") &&
chmod 0644 'show_dac.py'
if test $? -ne 0
then ${echo} 'restore of show_dac.py failed'
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'show_dac.py: MD5 check failed'
) << SHAR_EOF
1a54e6e16c761347eea085dc432a4e09 show_dac.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'show_dac.py'` -ne 340 && \
${echo} 'restoration warning: size of show_dac.py is not 340'
fi
fi
if rm -fr ${lock_dir}
then ${echo} 'x - removed lock directory `'${lock_dir}\''.'
else ${echo} 'x - failed to remove lock directory `'${lock_dir}\''.'
exit 1
fi
exit 0