Guest User

Untitled

a guest
Sep 17th, 2025
28
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
VHDL 7.73 KB | Source Code | 0 0
  1. library ieee;
  2.   use ieee.std_logic_1164.all;
  3.   use ieee.numeric_std.all;
  4.  
  5. entity generator is
  6.   port (
  7.     clk        : in    std_logic;
  8.     -- Reset? rst : in std_logic;
  9.     freq       : in    unsigned(15 downto 0);
  10.     ampl       : in    unsigned(7 downto 0); -- Why shorten the name? amplitude is descriptive and obvious, already not too short.
  11.     phs        : in    unsigned(7 downto 0);         -- degrees? what is phs? Phase? Just name it phase
  12.     sig        : out   std_logic; -- Stylistic choice, but I always would put the output at the bottom of the entity
  13.     sig_sel    : in    std_logic_vector(1 downto 0); -- 00:sin; 01:square; 10:saw; 11:triangle
  14.  
  15.     duty_cycle : in    integer range 0 to 100
  16.   );
  17. end entity generator;
  18.  
  19. architecture behavioral of generator is
  20.  
  21.   constant C_F_CLK          : integer               := 50000000;        -- 50 MHz
  22.   signal   phase_accu       : unsigned(31 downto 0) := (others => '0');
  23.   signal   phase_step       : unsigned(31 downto 0) := (others => '0');
  24.   signal   lut_index        : unsigned(4 downto 0)  := (others => '0');
  25.   signal   sig_tmp          : unsigned(15 downto 0) := (others => '0'); -- holds 8bitΓ—8bit product
  26.   signal   sig_int          : unsigned(7 downto 0)  := (others => '0');
  27.   signal   pwm_counter      : unsigned(7 downto 0)  := (others => '0');
  28.   signal   duty_thresh      : unsigned(31 downto 0) := (others => '0');
  29.   signal   lut_value        : std_logic_vector(7 downto 0);
  30.   signal   lut_value_mirror : std_logic_vector(7 downto 0);
  31.  
  32. begin
  33.  
  34.   lut_inst : entity work.lut(rtl)
  35.     port map (
  36.       index        => std_logic_vector(lut_index),
  37.       value        => lut_value,
  38.       value_mirror => lut_value_mirror
  39.     );
  40.  
  41.   main_proc : process (clk) is
  42.  
  43.     variable freq64    : unsigned(63 downto 0);
  44.     variable temp64    : unsigned(63 downto 0);
  45.     variable duty64    : unsigned(63 downto 0);
  46.     variable sum64     : unsigned(63 downto 0);
  47.     variable sig_tmp64 : unsigned(31 downto 0);
  48.     variable temp8     : unsigned(7 downto 0);
  49.  
  50.   begin
  51.  
  52.     sig <= '0'; -- It is very bad practice to assign anything outside of the rising_edge(clk) in a clocked process. This is definitely causing you trouble.
  53.  
  54.     if rising_edge(clk) then
  55.       -- Reset?
  56.       -- if rst = '1' then
  57.       --   Reset registers here
  58.       -- else
  59.       freq64 := resize(freq, 64);
  60.       temp64 := shift_left(freq64, 32) / to_unsigned(C_F_CLK, 64);
  61.  
  62.       -- phase accumulator with safe wrap, no overflow
  63.       sum64      := resize(phase_accu, 64) + resize(phase_step, 64);
  64.       phase_accu <= sum64(31 downto 0);
  65.       phase_step <= temp64(31 downto 0);
  66.  
  67.       case sig_sel is -- case statement based on input is fine, but if you want to run this faster, I would hold stateful information in local registers
  68.  
  69.         -- sine
  70.         when "00" =>
  71.  
  72.           lut_index <= phase_accu(31 downto 27);
  73.  
  74.           case phase_accu(31 downto 30) is
  75.  
  76.             when "00" => -- 1st quadrant
  77.  
  78.               sig_tmp <= unsigned(lut_value) * ampl;
  79.               sig_int <= sig_tmp(15 downto 8);
  80.  
  81.             when "01" => -- 2nd quadrant (mirror)
  82.  
  83.               sig_tmp <= unsigned(lut_value_mirror) * ampl;
  84.               sig_int <= sig_tmp(15 downto 8);
  85.  
  86.             when "10" => -- 3rd quadrant (negative)
  87.  
  88.               sig_tmp <= unsigned(lut_value) * ampl;
  89.               sig_int <= 255 - sig_tmp(15 downto 8);
  90.  
  91.             when "11" => -- 4th quadrant (negative mirror)
  92.  
  93.               sig_tmp <= unsigned(lut_value_mirror) * ampl;
  94.               sig_int <= 255 - sig_tmp(15 downto 8);
  95.  
  96.             when others =>
  97.  
  98.               sig_int <= (others => '0');
  99.  
  100.           end case;
  101.  
  102.           if (pwm_counter = 255) then -- numeric_std unsigned already does this :) wraps cleanly by default. This is great practice though if you want other pwm freq.
  103.             pwm_counter <= (others => '0');
  104.           else
  105.             pwm_counter <= pwm_counter + 1;
  106.           end if;
  107.  
  108.           if (pwm_counter < resize(sig_int, 8)) then
  109.             sig <= '1';
  110.           else
  111.             sig <= '0';
  112.           end if;
  113.  
  114.         -- square
  115.         when "01" =>
  116.  
  117.           duty64      := shift_left(resize(to_unsigned(duty_cycle, 64), 64), 32) / to_unsigned(100, 64);
  118.           duty_thresh <= duty64(31 downto 0);
  119.  
  120.           -- I see you are assigning sig differently depending on case... This is maybe fine but I would prefer to see
  121.           -- the pwm logic independant of this case statement, and the waveform control be done exclusively with sig_int.
  122.           if (phase_accu >= duty_thresh) then
  123.             sig <= '0';
  124.           else
  125.             sig <= '1';
  126.           end if;
  127.  
  128.         -- saw
  129.         when "10" =>
  130.  
  131.           sig_tmp64 := resize(phase_accu(31 downto 24), 16) * resize(ampl, 16);
  132.           temp8     := sig_tmp64(23 downto 16); -- take the slice
  133.           sig_int   <= temp8;
  134.  
  135.           if (pwm_counter < sig_int) then
  136.             sig <= '1';
  137.           else
  138.             sig <= '0';
  139.           end if;
  140.  
  141.         -- triangle
  142.         when "11" =>
  143.  
  144.           duty64      := shift_left(resize(to_unsigned(duty_cycle, 64), 64), 32) / to_unsigned(100, 64);
  145.           duty_thresh <= duty64(31 downto 0);
  146.  
  147.           if (phase_accu < duty_thresh) then
  148.             -- Rising slope
  149.             sig_tmp64 := resize(phase_accu(31 downto 24), 16) * resize(ampl, 16);
  150.             temp8     := sig_tmp64(15 downto 8);
  151.             sig_int   <= temp8;
  152.           else
  153.             -- Falling slope
  154.             sig_tmp64 := resize(not phase_accu(31 downto 24), 16) * resize(ampl, 16);
  155.             temp8     := sig_tmp64(15 downto 8);
  156.             sig_int   <= temp8;
  157.           end if;
  158.  
  159.           if (pwm_counter < sig_int) then
  160.             sig <= '1';
  161.           else
  162.             sig <= '0';
  163.           end if;
  164.  
  165.         when others =>
  166.  
  167.           sig <= '0';
  168.  
  169.       end case;
  170.  
  171.     end if;
  172.  
  173.   end process main_proc;
  174.  
  175. end architecture behavioral;
  176.  
  177. library ieee;
  178.   use ieee.std_logic_1164.all;
  179.   use ieee.numeric_std.all;
  180.  
  181. entity lut is
  182.   generic (
  183.     GC_LUT_SIZE   : integer := 32; -- Great use of generics! They got renamed with prefixes by my formatter.
  184.     GC_DATA_WIDTH : integer := 8
  185.   );
  186.   port (
  187.     index        : in    std_logic_vector(4 downto 0); -- HARDCODED
  188.     value        : out   std_logic_vector(GC_DATA_WIDTH - 1 downto 0);
  189.     value_mirror : out   std_logic_vector(GC_DATA_WIDTH - 1 downto 0)
  190.   );
  191. end entity lut;
  192.  
  193. architecture rtl of lut is
  194.  
  195.   type lut_array is array (0 to 31) of std_logic_vector(GC_DATA_WIDTH - 1 downto 0);
  196.  
  197.   constant SINE_TABLE : lut_array :=
  198.   (
  199.     0  => "00000000",
  200.     1  => "00000110",
  201.     2  => "00001100",
  202.     3  => "00010010",
  203.     4  => "00011000",
  204.     5  => "00011111",
  205.     6  => "00100101",
  206.     7  => "00101011",
  207.     8  => "00110000",
  208.     9  => "00110110",
  209.     10 => "00111100",
  210.     11 => "01000001",
  211.     12 => "01000111",
  212.     13 => "01001100",
  213.     14 => "01010001",
  214.     15 => "01010101",
  215.     16 => "01011010",
  216.     17 => "01011110",
  217.     18 => "01100010",
  218.     19 => "01100110",
  219.     20 => "01101010",
  220.     21 => "01101101",
  221.     22 => "01110000",
  222.     23 => "01110011",
  223.     24 => "01110110",
  224.     25 => "01111000",
  225.     26 => "01111010",
  226.     27 => "01111100",
  227.     28 => "01111101",
  228.     29 => "01111110",
  229.     30 => "01111111",
  230.     31 => "01111111"
  231.   );
  232.  
  233. begin
  234.  
  235.     -- always 0 unless index is all 1? This is certainly not what you intended?
  236.     -- Did you not just want value to always be equal to sine_table(index).
  237.   value        <= sine_table(to_integer(unsigned(index))) when index <= "11111" else
  238.                   (others => '0');
  239.  
  240.   value_mirror <= sine_table(GC_LUT_SIZE - 1 - to_integer(unsigned(index))) when index <= "11111" else
  241.                   (others => '0');
  242.  
  243. end architecture rtl;
  244.  
Advertisement
Add Comment
Please, Sign In to add comment