ddfs

extras/ddfs.vhdl

Dependencies

sizing arithmetic

Description

This package provides a set of functions and a component used for implementing a Direct Digital Frequency Synthesizer (DDFS). The DDFS component is a simple accumulator that increments by a pre computed value each cycle. The MSB of the accumulator switches at the requested frequency established by the ddfs_increment() function. The provided functions perform computations with real values and, as such, are only synthesizable when used to define constants.

There are two sets of functions for generating the increment values needed by the DDFS accumulator. One set is used to compute static increments that are assigned to constants. The other functions work in conjunction with a procedure to dynamically generate the increment value using a single inferred multiplier.

It is possible to generate multiple frequencies by computing more than one increment constant and multiplexing between them. The ddfs_size() function should be called with the smallest target frequency to be used to guarantee the requested tolerance is met. You can alternately set a fixed size and compute the effective tolerance with ddfs_tolerance().

For simulation reporting, you can compute the effective output frequency with the ddfs_frequency() function and the error ratio with ddfs_error().

The utility function resize_fractional() will truncate unwanted LSBs when downsizing the generated DDFS accumulator value.

Example usage

The ddfs_size() and ddfs_increment() functions are used to compute static increment values:

constant SYS_FREQ  : real    := 50.0e6; -- 50 MHz
constant TGT_FREQ  : real    := 2600.0; -- 2600 Hz
constant DDFS_TOL  : real    := 0.001;  -- 0.1%
constant SIZE      : natural := ddfs_size(SYS_FREQ, TGT_FREQ, DDFS_TOL);
constant INCREMENT : unsigned(SIZE-1 downto 0) :=
                             ddfs_increment(SYS_FREQ, TGT_FREQ, SIZE);
...
whistle: ddfs
  port map (
    Clock => clock,
    Reset => reset,

    Enable     => '1',
    Load_phase => '0',
    New_phase  => unsigned'"",

    Increment   => INCREMENT,
    Accumulator => accum,
    Synth_clock => synth_tone, -- Signal with ~2600 Hz clock
    Synth_pulse => open
  );
...
-- Report the DDFS precision (simulation only)
report "True synthesized frequency: "
  & real'image(ddfs_frequency(SYS_FREQ, TGT_FREQ, SIZE)
report "DDFS error: " & real'image(ddfs_error(SYS_FREQ, TGT_FREQ, SIZE)

The alternate set of functions min_fraction_bits(), ddfs_dynamic_factor(), and ddfs_dynamic_inc() are used to precompute a multiplier factor that is used to dynamically generate an increment value in synthesizable logic:

constant MIN_TGT_FREQ : natural := 27;
constant MAX_TGT_FREQ : natural := 4200;
constant FRAC_BITS    : natural := min_fraction_bits(SYS_FREQ,
                                      MIN_TGT_FREQ, SIZE, DDFS_TOL);
constant DDFS_FACTOR  : natural := ddfs_dynamic_factor(SYS_FREQ, SIZE,
                                                       FRAC_BITS);
signal dyn_freq : unsigned(bit_size(MAX_TGT_FREQ)-1 downto 0);
signal dyn_inc  : unsigned(SIZE-1 downto 0);
...
dyn_freq <= to_unsigned(261, dyn_freq'length); -- Middle C
...
dyn_freq <= to_unsigned(440, dyn_freq'length); -- Change to A4
...
-- Wrap ddfs_dynamic_inc in a sequencial process to synthesize a
-- multiplier with registered product.
dyn: process(clock, reset) is
begin
  if reset = '1' then
    dyn_inc <= (others => '0');
  elsif rising_edge(clock) then
    ddfs_dynamic_inc(DDFS_FACTOR, FRAC_BITS, dyn_freq, dyn_inc);
  end if;
end process;

fsynth: ddfs
  port map (
    Clock => clock,
    Reset => reset,

    Enable     => '1',
    Load_phase => '0',
    New_phase  => unsigned'"",

    Increment   => dyn_inc,
    Accumulator => accum,
    Synth_clock => synth_tone,
    Synth_pulse => open
  );

Components

ddfs

component ddfs is generic ( RESET_ACTIVE_LEVEL : std_ulogic ); port ( --# {{clocks|}} Clock : in std_ulogic; Reset : in std_ulogic; --# {{control|}} Enable : in std_ulogic; Load_phase : in std_ulogic; New_phase : in unsigned; Increment : in unsigned; --# {{data|}} Accumulator : out unsigned; Synth_clock : out std_ulogic; Synth_pulse : out std_ulogic ); end component;


ddfs_pkg.ddfs

Synthesize a frequency using a DDFS.

Generics:
  • RESET_ACTIVE_LEVEL (std_ulogic) – Asynch. reset control level
Port:
  • Clock (in std_ulogic) – System clock
  • Reset (in std_ulogic) – Asynchronous reset
  • Enable (in std_ulogic) – Enable the DDFS counter
  • Load_phase (in std_ulogic) – Load a new phase angle
  • New_phase (in unsigned) – Phase angle to load
  • Increment (in unsigned) – Value controlling the synthesized frequency
  • Accumulator (out unsigned) – Internal accumulator value
  • Synth_clock (out std_ulogic) – Synthesized frequency
  • Synth_pulse (out std_ulogic) – Single cycle pulse for rising edge of synth_clock

ddfs_pipelined

component ddfs_pipelined is generic ( MAX_CARRY_LENGTH : positive; RESET_ACTIVE_LEVEL : std_ulogic ); port ( --# {{clocks|}} Clock : in std_ulogic; Reset : in std_ulogic; --# {{control|}} Enable : in std_ulogic; Load_phase : in std_ulogic; New_phase : in unsigned; Increment : in unsigned; --# {{data|}} Accumulator : out unsigned; Synth_clock : out std_ulogic; Synth_pulse : out std_ulogic ); end component;


ddfs_pkg.ddfs_pipelined

Synthesize a frequency using a DDFS.

Generics:
  • MAX_CARRY_LENGTH (positive) –
  • RESET_ACTIVE_LEVEL (std_ulogic) – Asynch. reset control level
Port:
  • Clock (in std_ulogic) – System clock
  • Reset (in std_ulogic) – Asynchronous reset
  • Enable (in std_ulogic) – Enable the DDFS counter
  • Load_phase (in std_ulogic) – Load a new phase angle
  • New_phase (in unsigned) – Phase angle to load
  • Increment (in unsigned) – Value controlling the synthesized frequency
  • Accumulator (out unsigned) – Internal accumulator value
  • Synth_clock (out std_ulogic) – Synthesized frequency
  • Synth_pulse (out std_ulogic) – Single cycle pulse for rising edge of synth_clock

Subprograms

ddfs_pkg.ddfs_size (Sys_freq : real; Target_freq : real; Tolerance : real) → natural
Compute the necessary size of a DDFS accumulator based on system and target frequencies with a specified tolerance. The DDFS accumulator must be at least as large as the result to achieve the requested tolerance.
Parameters:
  • Sys_freq (real) – Clock frequency of the system
  • Target_freq (real) – Desired frequency to generate
  • Tolerance (real) – Error tolerance
Returns:

Number of bits needed to generate the target frequency within the allowed tolerance.

ddfs_pkg.ddfs_tolerance (Sys_freq : real; Target_freq : real; Size : natural) → real
Compute the effective frequency tolerance for a specific size and target frequency.
Parameters:
  • Sys_freq (real) – Clock frequency of the system
  • Target_freq (real) – Desired frequency to generate
  • Size (natural) – Size of the DDFS counter
Returns:

Tolerance for the target frequency with a Size counter.

ddfs_pkg.ddfs_increment (Sys_freq : real; Target_freq : real; Size : natural) → natural
Compute the natural increment value needed to generate a target frequency.
Parameters:
  • Sys_freq (real) – Clock frequency of the system
  • Target_freq (real) – Desired frequency to generate
  • Size (natural) – Size of the DDFS counter
Returns:

Increment value needed to generate the target frequency.

ddfs_pkg.ddfs_increment (Sys_freq : real; Target_freq : real; Size : natural) → unsigned
Compute the unsigned increment value needed to generate a target frequency.
Parameters:
  • Sys_freq (real) – Clock frequency of the system
  • Target_freq (real) – Desired frequency to generate
  • Size (natural) – Size of the DDFS counter
Returns:

Increment value needed to generate the target frequency.

ddfs_pkg.min_fraction_bits (Sys_freq : real; Target_freq : real; Size : natural; Tolerance : real) → natural
Find the minimum number of fraction bits needed to meet the tolerance requirement for a dynamic DDFS. The target frequency should be the lowest frequency to ensure proper results.
Parameters:
  • Sys_freq (real) – Clock frequency of the system
  • Target_freq (real) – Lowest desired frequency to generate
  • Size (natural) – Size of the DDFS counter
  • Tolerance (real) – Error tolerance
Returns:

Increment value needed to generate the target frequency.

ddfs_pkg.ddfs_dynamic_factor (Sys_freq : real; Size : natural; Fraction_bits : natural) → natural
Compute the factor used to generate dynamic increment values.
Parameters:
  • Sys_freq (real) – Clock frequency of the system
  • Size (natural) – Size of the DDFS counter
  • Fraction_bits (natural) – Number of fraction bits
Returns:

Dynamic increment factor passed into ddfs_dynamic_inc().

ddfs_pkg.ddfs_dynamic_inc (Dynamic_factor : in natural; Fraction_bits : in natural; Target_freq : in unsigned; Increment : out unsigned)
This procedure computes dynamic increment values by multiplying the result of a previous call to ddfs_dynamic_factor by the integer target frequency. The result is an integer value with fractional bits removed. This can be synthesized by invocation within a synchronous process.
Parameters:
  • Dynamic_factor (in natural) – Dynamic factor constant
  • Fraction_bits (in natural) – Fraction bits for the dynamic DDFS
  • Target_freq (in unsigned) – Desired frequency to generate
  • Increment (out unsigned) – Increment value needed to generate the target frequency.
ddfs_pkg.ddfs_frequency (Sys_freq : real; Target_freq : real; Size : natural) → real
Compute the actual synthesized frequency for the specified accumulator size.
Parameters:
  • Sys_freq (real) – Clock frequency of the system
  • Target_freq (real) – Desired frequency to generate
  • Size (natural) – Size of the DDFS counter
Returns:

Frequency generated with the provided parameters.

ddfs_pkg.ddfs_error (Sys_freq : real; Target_freq : real; Size : natural) → real
Compute the error between the requested output frequency and the actual output frequency.
Parameters:
  • Sys_freq (real) – Clock frequency of the system
  • Target_freq (real) – Desired frequency to generate
  • Size (natural) – Size of the DDFS counter
Returns:

Ratio of generated frequency to target frequency.

ddfs_pkg.resize_fractional (Phase : unsigned; Size : positive) → unsigned
Resize a vector representing a fractional value with the binary point preceeding the MSB.
Parameters:
  • Phase (unsigned) – Phase angle in range 0.0 to 1.0.
  • Size (positive) – Number of bits in the result
Returns:

Resized vector containing phase fraction

ddfs_pkg.radians_to_phase (Radians : real; Size : positive) → unsigned
Convert angle in radians to a fractional phase value.
Parameters:
  • Radians (real) – Angle to convert
  • Size (positive) – Number of bits in the result
Returns:

Fraction phase in range 0.0 to 1.0.

ddfs_pkg.degrees_to_phase (Degrees : real; Size : positive) → unsigned
Convert angle in degrees to a fractional phase value.
Parameters:
  • Degrees (real) – Angle to convert
  • Size (positive) – Number of bits in the result
Returns:

Fraction phase in range 0.0 to 1.0.