OpenCores

Project maintainers

Details

Name: triangle_wave_generator
Created: Jun 20, 2022
Updated: Aug 18, 2022
SVN: No files checked in
Bugs: 1 reported / 0 solved
Star0you like it: star it!

Other project properties

Category:DSP core
Language:VHDL
Development status:Stable
Additional info:
WishBone compliant: No
WishBone version: n/a
License: LGPL

# modelsim wave picture

Tags: vhdl triangle wave generator compact code

How to add files? Just like adding an image. But an 'image' with extension .vhd or .txt is not accepted: "opencores.org - Failed to upload image"

# error message

The url of the svn repository is: https://opencores.org/websvn/listing/triangle_wave_generator/triangle_wave_generator

OK, no download of documents possible and no one has given any hint so far how to make that work, so here the vhdl code as plain text:

Had to add newlines after every line else this text mode contatenes everything, no 'code view' mode found

---------------------------START OF FILE triangle_generator.vhd

--! @filename triangle_generator.vhd

--! @brief File holds the triangle_generator VHDL code

--!

--! This is the triangle_generator VHDL code

--! Description: This generates triangle and square waves with 90 degree phase shifts

--!

--! @author Benjamin Perdeck

--! $Date: 2022-06-13 16:32:03 +0200 (Mon, 13 Jun 2022) $

--!

--! The use of this file, whole or a part, is subjected to licensing terms.


-- Description of the entity:

-- The triangle generator generates two triangle signals with 90 degree phase offset using only one adder

-- The frequency of the triangle is configurable using a rate multiplier

-- Also, a square wave and 90 degrees shifted square wave signal is made.

-- The user can select to use signed or unsigned or both signals at the outputs

library IEEE;

use IEEE.STD_LOGIC_1164.ALL;

use IEEE.NUMERIC_STD.ALL;

use ieee.std_logic_misc.all; -- e.g and_reduce or_reduce


-- Entity

entity triangle_generator is generic (

TRIANGLE_WIDTH         : integer := 16; -- the width of the unsigned triangle
	
RATE_GENERATOR_WIDTH   : integer := 20  -- bits in the rate generator; The top bits are used for the triangles
	
  -- NOTE: RATE_GENERATOR_WIDTH > TRIANGLE_WIDTH !!
		

);

port ( CLK : in std_logic; --! module clock

-- inputs:
	
ENABLE                 : in  std_logic; -- a step is made at each ENABLE
	
-- config:
	
TRIANGLE_RATE          : in  unsigned(RATE_GENERATOR_WIDTH-1 downto 0);
	
-- outputs:
	
TRIANGLE00_U           : out unsigned(TRIANGLE_WIDTH-1 downto 0); --  'sine' like
	
TRIANGLE90_U           : out unsigned(TRIANGLE_WIDTH-1 downto 0); --  +90 degree pre-running triangle 'cosine' like
	
TRIANGLE00_S           : out signed(TRIANGLE_WIDTH-1 downto 0);   --  'sine' like
	
TRIANGLE90_S           : out signed(TRIANGLE_WIDTH-1 downto 0);   --  +90 degree pre-running triangle 'cosine' like
	
--
SQUARE00_U             : out unsigned(TRIANGLE_WIDTH-1 downto 0); --  1/16 to 15/16 range
	
SQUARE90_U             : out unsigned(TRIANGLE_WIDTH-1 downto 0); --  1/16 to 15/16 range
	
SQUARE00_S             : out signed(TRIANGLE_WIDTH-1 downto 0);   --   7/8 to  7/8  range
	
SQUARE90_S             : out signed(TRIANGLE_WIDTH-1 downto 0)    --  -7/8 to  7/8  range
	

);

end triangle_generator;


architecture arch_imp of triangle_generator is


-- Constants

constant TWIDTH1 : natural := TRIANGLE_WIDTH+1;


-- Type definitions


-- Component declarations


-- Signal declarations

signal rate_gen00 : unsigned(RATE_GENERATOR_WIDTH-1 downto 0) := (others => '0');

signal rate_gen00_nofrac : unsigned(TWIDTH1-1 downto 0);

signal rate_gen00_N_ff : unsigned(TWIDTH1-1 downto 0);

signal rate_gen00_I_ff : unsigned(TWIDTH1-1 downto 0);

signal gen_Ntop_so : unsigned(2-1 downto 0);

signal gen_Itop_so : unsigned(2-1 downto 0);

signal gen_N_mintop_so : unsigned(TWIDTH1-2-1 downto 0);

signal triangle00 : unsigned(TRIANGLE_WIDTH-1 downto 0); -- 'sine' like

signal triangle90 : unsigned(TRIANGLE_WIDTH-1 downto 0); -- +90 degree pre-running triangle 'cosine' like

signal sqt00 : unsigned(4-1 downto 0); -- 'sine' like

signal sqt90 : unsigned(4-1 downto 0); -- +90 degree pre-running triangle 'cosine' like

signal sq00 : unsigned(TRIANGLE_WIDTH-1 downto 0); -- 'sine' like

signal sq90 : unsigned(TRIANGLE_WIDTH-1 downto 0); -- +90 degree pre-running triangle 'cosine' like


-- Attributes


begin

assert (RATE_GENERATOR_WIDTH > TRIANGLE_WIDTH) report "Triangle generator: not RATE_GENERATOR_WIDTH > TRIANGLE_WIDTH" severity failure;


--! the rate generator


rate_gen_proc: process(CLK)

begin

if rising_edge(CLK) then
	
  if ENABLE='1' then
		
    rate_gen00 <= rate_gen00 + TRIANGLE_RATE; -- intended wrap at ofl!
			
  end if;
		
end if;
	

end process;

rate_gen00_nofrac <= rate_gen00(rate_gen00'high downto rate_gen00'length-TWIDTH1);

-- RANGE ERROR WHEN RATE_GENERATOR_WIDTH <= TRIANGLE_WIDTH : >>> .. downto (negative number)

-- NEW method: use most bits of gen00_FF or gen00_inv_ff and only top bits determined by case

-- in this way, number of adders/subtractors is minimal

table_tri_gen: process(CLK)

variable gen_N_mintop : unsigned(TWIDTH1-2-1 downto 0);
	
variable gen_I_mintop : unsigned(TWIDTH1-2-1 downto 0);
	
variable gen_Ntop_var : unsigned(2-1 downto 0);
	
variable gen_Itop_var : unsigned(2-1 downto 0);
	

begin

if rising_edge(CLK) then
	
  --make 00 and 90 degree versions:
		
  rate_gen00_I_ff <= unsigned(not std_logic_vector(rate_gen00_nofrac)); -- will give twice the values at extremes!
		
  rate_gen00_N_ff <= rate_gen00_nofrac;
		

  gen_N_mintop := rate_gen00_N_ff(TWIDTH1-2-1 downto 0);
		
  gen_I_mintop := rate_gen00_I_ff(TWIDTH1-2-1 downto 0);
		
  gen_N_mintop_so <= gen_N_mintop;
		

  gen_Ntop_var := rate_gen00_N_ff(TWIDTH1-1 downto TWIDTH1-2);
		
  gen_Itop_var := rate_gen00_I_ff(TWIDTH1-1 downto TWIDTH1-2);
		
  gen_Ntop_so  <= gen_Ntop_var;
		
  gen_Itop_so  <= gen_Itop_var;
		

  case to_integer(gen_Ntop_var) is -- 0 to 3
    when      0 => triangle00 <= "0" & gen_N_mintop; triangle90 <= "1" & gen_N_mintop; sqt00<="0001"; sqt90<="1111"; --rising  rising
			
    when      1 => triangle00 <= "1" & gen_N_mintop; triangle90 <= "1" & gen_I_mintop; sqt00<="1111"; sqt90<="1111"; --rising  falling
			
    when      2 => triangle00 <= "1" & gen_I_mintop; triangle90 <= "0" & gen_I_mintop; sqt00<="1111"; sqt90<="0001"; --falling falling
			
    when others => triangle00 <= "0" & gen_I_mintop; triangle90 <= "0" & gen_N_mintop; sqt00<="0001"; sqt90<="0001"; --falling rising
			
  end case;
		
end if;
	

end process;

sq00 <= sqt00 & to_unsigned(0, sq00'length-4);

sq90 <= sqt90 & to_unsigned(0, sq00'length-4);

TRIANGLE00_U <= triangle00;

TRIANGLE90_U <= triangle90;

-- convert unsigned to signed by inversion of top bit:

TRIANGLE00_S <= signed( not triangle00(TRIANGLE_WIDTH-1) & std_logic_vector(triangle00(TRIANGLE_WIDTH-1-1 downto 0)));

TRIANGLE90_S <= signed( not triangle90(TRIANGLE_WIDTH-1) & std_logic_vector(triangle90(TRIANGLE_WIDTH-1-1 downto 0)));

SQUARE00_U <= sq00;

SQUARE90_U <= sq90;

-- convert unsigned to signed by inversion of top bit:

SQUARE00_S <= signed( not sq00(TRIANGLE_WIDTH-1) & std_logic_vector(sq00(TRIANGLE_WIDTH-1-1 downto 0)));

SQUARE90_S <= signed( not sq90(TRIANGLE_WIDTH-1) & std_logic_vector(sq90(TRIANGLE_WIDTH-1-1 downto 0)));

end arch_imp;

-----------------------------END OF FILE triangle_generator.vhd

-----------------------------START OF FILE triangle_generator_tb.vhd

--! @file triangle_generator_tb.vhd

--! @brief File holds the testbench for triangle_generators

--!

--! This is a testbench to test triangle_generators

--! --! @author Benjamin Perdeck

--! $Date: 2022-06-13 16:32:03 +0200 (Mon, 13 Jun 2022) $

--!

--! The use of this file, whole or a part, is subjected to licensing terms.


library ieee;

use ieee.std_logic_1164.all;

use ieee.std_logic_misc.all;

use ieee.numeric_std.all;

use ieee.std_logic_textio.all;

use std.textio.all;

use ieee.math_real.all;


--! @brief Testbench to test the triangle_generators

--!

--! Testbench is used to test triangle_generator signed/unsigned unit which is used to perform a

--! This is implemented as a separate testbench to ensure that the triangle_generators

--! are working as expected


entity triangle_generator_tb is

end triangle_generator_tb;


architecture testbench of triangle_generator_tb is


-- Constants

-- UUT configuration constants

-- Testbench constants

constant testbench_name : string := "triangle_generator_tb"; --! Testbench name

constant clk_100MHz_period : time := 10 ns; --! about 100MHz Clock

--for full range test:

constant TRIANGLE_WIDTH : integer := 8; -- the width of the signed triangle

constant RATE_GENERATOR_WIDTH : integer := 12; -- bits in the rate generator; The top bits are used for the triangles


-- Type definitions


-- Component declarations

component triangle_generator is

generic (

TRIANGLE_WIDTH         : integer := 16; -- the width of the signed triangle
	
RATE_GENERATOR_WIDTH   : integer := 20  -- bits in the rate generator; The top bits are used for the triangles
	

);

port (

CLK                    : in  std_logic; --! module clock
	
-- inputs:
	
ENABLE                 : in  std_logic; -- a step is made at each ENABLE
	
-- config:
	
TRIANGLE_RATE          : in  unsigned(RATE_GENERATOR_WIDTH-1 downto 0);
	
-- outputs:
	
TRIANGLE00_U           : out unsigned(TRIANGLE_WIDTH-1 downto 0); --  'sine' like
	
TRIANGLE90_U           : out unsigned(TRIANGLE_WIDTH-1 downto 0); --  +90 degree pre-running triangle 'cosine' like
	
TRIANGLE00_S           : out signed(TRIANGLE_WIDTH-1 downto 0);   --  'sine' like
	
TRIANGLE90_S           : out signed(TRIANGLE_WIDTH-1 downto 0);   --  +90 degree pre-running triangle 'cosine' like
	
	
--
	
SQUARE00_U             : out unsigned(TRIANGLE_WIDTH-1 downto 0); --  1/16 to 15/16 range
	
SQUARE90_U             : out unsigned(TRIANGLE_WIDTH-1 downto 0); --  1/16 to 15/16 range
	
SQUARE00_S             : out signed(TRIANGLE_WIDTH-1 downto 0);   --   7/8 to  7/8  range
	
SQUARE90_S             : out signed(TRIANGLE_WIDTH-1 downto 0)    --  -7/8 to  7/8  range
	

);

end component;


-- Signal declarations

-- UUT I/O signals:

signal clk : std_logic := '0'; --! expect to run at 100MHz

signal rst : std_logic := '1';

signal enable : std_logic;

signal triangle_rate : unsigned(RATE_GENERATOR_WIDTH-1 downto 0);

signal triangle00_s : signed(TRIANGLE_WIDTH-1 downto 0); -- 'sine' like

signal triangle90_s : signed(TRIANGLE_WIDTH-1 downto 0); -- +90 degree pre-running triangle 'cosine' like

signal triangle00_u : unsigned(TRIANGLE_WIDTH-1 downto 0); -- 'sine' like

signal triangle90_u : unsigned(TRIANGLE_WIDTH-1 downto 0); -- +90 degree pre-running triangle 'cosine' like

signal square00_u : unsigned(TRIANGLE_WIDTH-1 downto 0);

signal square90_u : unsigned(TRIANGLE_WIDTH-1 downto 0);

signal square00_s : signed(TRIANGLE_WIDTH-1 downto 0);

signal square90_s : signed(TRIANGLE_WIDTH-1 downto 0);


-- Functions

function stdlogic2int ( data: std_logic ) return integer is

begin

if data = '1' then return 1;

elsif data = '0' then return 0;

else return -1; -- cases 'x', 'z' ...

end if;

end stdlogic2int;


-- Procedures


-- Attributes


begin


--! @brief Unit under test: triangle_generator

--!

--! triangle_generator is used as unit under test to test the complete functionality

--! using this testbench


uut: triangle_generator

generic map (
	
  TRIANGLE_WIDTH         => TRIANGLE_WIDTH,
		
  RATE_GENERATOR_WIDTH   => RATE_GENERATOR_WIDTH
		
)
	
port map (
	
  CLK                    => clk,
		
  -- inputs:
		
  ENABLE                 => enable,
		
  -- config:
		
  TRIANGLE_RATE          => triangle_rate,
		
  -- outputs:
		
  TRIANGLE00_U           => triangle00_u,
		
  TRIANGLE90_U           => triangle90_u,
		
  TRIANGLE00_S           => triangle00_s,
		
  TRIANGLE90_S           => triangle90_s,
		
  SQUARE00_U             => square00_u,
		
  SQUARE90_U             => square90_u,
		
  SQUARE00_S             => square00_s,
		
  SQUARE90_S             => square90_s
		
);

-- Reset signals


rst <= '0' after clk_100MHz_period*20;



-- Clock signals


clk <= not(clk) after clk_100MHz_period/2;



--! @brief write_testbench_header_proc gives a message to the console output

--!

--! This process shows the user the simulation has started and what the name is


write_testbench_header_proc: process

variable s: line;
	

begin

-- Skip initial warnings
	
wait for 1 ps;
	
write(s, testbench_name);
	
writeline(output,s);
	
wait;
	

end process write_testbench_header_proc;


--triangle_rate <= to_unsigned((2**RATE_GENERATOR_WIDTH)/1000+1, RATE_GENERATOR_WIDTH); -- +1 to get different values out of it too

-- give an enable to teh generator every 10 clock cycles

enable_proc: process

begin

enable <= '0';
	
wait until rst='0';
	
while true loop
	
  for i in 1 to 10-1 loop
		
    wait until rising_edge(clk);
			
  end loop;
		
  enable <= '1';
		

  wait until rising_edge(clk);
		
  enable <= '0';
		
end loop;
	

end process;

-- vary the rate frequency between 1 and 30

rate_proc: process

begin

for rate in 1 to 30 loop
	
  triangle_rate <= to_unsigned(rate, RATE_GENERATOR_WIDTH);
		

  for i in 1 to 100000-rate*3000 loop -- wait shorter at higher frequency
		
    wait until rising_edge(clk);
			
  end loop;
		
end loop;
	
wait;
	

end process;


end testbench;

-----------------------------END OF FILE triangle_generator_tb.vhd