Create Pluse Width Modulation from VHDL

Create Pulse Width Modulation from VHDL

This topic is an example of how to create pulse width modulation work with the Zynq processing system.

block design

 

Example PWM_VHDL.VHD VHDL code

library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_arith.all;
    use ieee.std_logic_unsigned.all;
entity pwm is
    generic(
        g_width               : integer range 1 to 99 := 12;  -- the number of bits used to represent amplitude value     
        g_div_factor_freq     : integer := 2
        );
    port(
        i_clk           : in std_logic;                                 -- input clock signal
        i_reset         : in std_logic;
        i_sine_ampl     : in std_logic_vector(g_width-1 downto 0);      -- current amplitude value of the sine signal
        o_ena_freq_trig : out std_logic    ;                            --
        o_pwm_out       : out std_logic    ;                             -- pulse width modulated signal
        o_pwm_out2      : out std_logic                                 
        );
end;

architecture rtl of pwm is
    type state_type is (load_new_ampl, pwm_high, pwm_low); -- states s0, s1, s2
    signal state: state_type := load_new_ampl ;
    signal ena_freq_trig : std_logic := '0';    -- clock enable signal for the fsm
    signal threshold  : integer range 0 to ((2**g_width)-1) := 0;  -- integer range 0 to 4095 (in our case)
    signal count     : integer range 0 to ((2**g_width)-1) := 0;  -- integer range 0 to 4095 (in our case)
    signal r_pwm_out : std_logic;
attribute mark_debug: string ;
attribute mark_debug of ena_freq_trig : signal is "True";
attribute mark_debug of threshold : signal is "True";
attribute mark_debug of count : signal is "True";  
attribute mark_debug of r_pwm_out : signal is "True";  

begin
   
    process1: process (i_clk,i_reset)
    begin
       if i_reset= '0' then                        
         r_pwm_out <= '0'; 
       elsif rising_edge(i_clk) then
            if (ena_freq_trig = '1') then       
                case state is
                    when load_new_ampl => 
                        threshold <= conv_integer (i_sine_ampl);
                        count <= 0;
                        if (i_sine_ampl > 0) then
                            state <= pwm_high;
                            r_pwm_out <='1'; 
                        elsif (i_sine_ampl = 0) then
                            state <= pwm_low;
                            r_pwm_out <='0'; 
                        end if;
 
                    when pwm_high => 
                        count <= count + 1;
                        if (count < ((2**g_width)-1) and count < i_sine_ampl) then
                            state <= pwm_high;
                            r_pwm_out <='1'; 
                        elsif (count = ((2**g_width)-1)) then
                            state <= load_new_ampl;
                        elsif (count < ((2**g_width)-1) and count = i_sine_ampl) then
                            state <= pwm_low;
                            r_pwm_out <='0'; 
                        end if;

                    when pwm_low => 
                        count <= count + 1;
                        if (count < ((2**g_width)-1)) then
                            state <= pwm_low;
                            r_pwm_out <='0'; 
                        elsif (count = ((2**g_width)-1)) then
                            state <= load_new_ampl;
                        end if;
                end case;              
            end if;
        end if;
    end process process1;
    
       o_ena_freq_trig <=ena_freq_trig ;
       o_pwm_out <=r_pwm_out ;
       o_pwm_out2 <=r_pwm_out ;
     
    fsm_ce: entity work.frequency_trigger(rtl)    -- instance of the frequency trigger

        generic map (
            g_div_factor_freq => g_div_factor_freq
        )
        port map (
            i_clk     => i_clk,
            i_reset   => i_reset,
            o_freq_trig    => ena_freq_trig
        );
end;

Write test bench then simulation and check result

library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_arith.all;
    use ieee.std_logic_unsigned.all;
entity pwm_tb is
    generic(
        width_g                : integer range 1 to 99 := 12         -- the number of bits used to represent amplitude value
        );
end;
architecture Behavioral_tb of pwm_tb is
    signal i_clk_tb        : std_logic := '0';     -- input clock signal
    signal i_reset_tb     : std_logic := '1';
    signal i_sine_out_tb   : std_logic_vector(width_g-1 downto 0) := "001101011100";         -- current amplitude value of the sine signal
    signal o_pwm_out_tb        : std_logic ;                  -- pwm signal


begin

    dut1 : entity work.pwm                               -- pwm instance
        generic map(
            g_width                       =>  width_g,
            g_div_factor_freq             => 2
            )
        port map(
            i_clk        => i_clk_tb,
            i_reset      => i_reset_tb,
            i_sine_ampl     => i_sine_out_tb,
            o_pwm_out       => o_pwm_out_tb
            );

    i_clk_tb <=  not (i_clk_tb) after 5ns;
    

end Behavioral_tb;

 

Here is the simulation result

simulation

 

 

Debug by using ILA( Integrated Logic Analyzer (ILA)

The ILA can be used to monitor the internal signal in design. We can verify results from the signal waveform which is created by Vivado tools.

The method of how to set up ILA in RTL code is shown in the step below.

1.  After completing the simulation of the PWM_VHDL.VHD file, we have to add the attribute mark (mark_debug) for creating debug circuit as shown in the below picture.

mark_debug

 

 

2. Click Set up Debug signal in the synthesis then select the signals that we want to debug.

set up debug

 

ประเภทเนื้อหาของ article
FPGA Development
Rating
Average: 3 (2 votes)