VHDL ve Verilog ile PWM Sinyali Üretimi

VHDL ve Verilog ile PWM Sinyali Üretimi

  • 8 min read

VHDL ve Verilog ile PWM Sinyali Üretimi

PWM (Pulse Width Modulation), dijital sinyal çıktısının aktif olduğu süreyi değiştirerek çıktının değerinin kontrol edildiği, elektronik uygulamalarda yaygın kullanılan bir çıkış formatıdır. Bu çıkış formatıyla analog çıkışa denk bir sinyalin etkisi dijital bir sinyal ile oluşturulabilmektedir. Ayrıca PWM sinyalinin zaman parametreleri kullanılarak başka bir dijital sistemler arası veri aktarmak da mümkündür.

PWM, dijital sistemlerin hızlı anahtarlanması prensibine dayanır. Bir periyottaki açık kalınan süre (Ton) ve kapalı kalınan sürenin (Toff) değiştirilmesiyle çıkışa aktarılan gücün değiştirilmesi prensibiyle çalışır.PWM sinyalinin yapısı Şekil 1’de verilmiştir.

T: Periyot Ton: Çıkışın aktif ‘1’ olarak tutulduğu süre Duty: Ton/T oranı olarak ifade edilmektedir.


Şekil 1 - PWM sinyalinin yapısı

Şekil 1’de görülen PWM sinyal yapısında çıkışa aktarılan güç bir periyot için Pmax*(Ton/T)‘a eşittir. Bu matematiksel olarak aşağıdaki ifade ile ifade edilebilir.

Eşitliğin sağdaki 0 ile çarpılan kısmı sıfır olacağından eşitlik aşağıdaki hale sadeleşir.

PWM sinyal formatı, bir periyot içerisindeki Ton/T oranı değiştirilerek çıkışa aktarılan gücün değiştirilmesi esasına dayanmaktadır. Örneğin %100 gücünde çalıştırılmak istenen bir sistemde Ton/T oranı = 1 olacak şekilde ayarlanması gerekirken %10 gücünde çalışması gereken bir sistemde Ton/T oranı 0,1 olacak şekilde ayarlanmalıdır.

Duty (doluluk) oranının değiştirilmesiyle çıkışta kontrol edilmek istenen sistemin çıkış gücü, voltajı veya konumu gibi çıktı değerlerinin değiştirilmesi mümkün olmaktadır. Duty değerinin değiştirilmesiyle çıktının ortalama aktif kaldığı süre değiştirilerek çıkış değerinin efektif olarak ayarlanması sağlanabilmektedir. Özellikle hızlı açılıp kapatılarak çalıştırılabilen sistemlerde (ısıtıcılar, aydınlatma ürünleri, motorlar, smps gibi) çıktının gücü, hızı gibi parametreleri bu yöntem ile kontrol edilebilmektedir. Çok hızlı şekilde (insan veya ölçüm yapan sistemin algılama frekansından yüksek frekansta) yapılan bu anahtarlama sayesinde gerçek bir analog kontrol ediliyorcasına çıktı elde edilebilmektedir.

Aşağıda VHDL ve Verilog donanım tasarım dillerinde PWM uygulaması yer almaktadır. Bu tasarımlarda generic olarak clock frekansı, PWM sinyalinin frekansı ve bu modülü kontroledecen PWM girdi değerinin çözünürlüğü generic olarak belirlenebilir şekilde bir tasarım yapılmıştır. Böylece girdi ve çıktı parametreleri isteğe göre ayarlanabilmektedir.

Birim çözünürlük için kaç clock gerektiği bilgisi pwm_tick_counter_limit_c adlı sabit değere generic parametreler kullanılarak sentezleme aşamasında hesaplanır ve yazılır. PWM_i adlı girdi her PWM periyodunun başında örneklenerek pwm_value isimli bir sinyale yazılır. pwm_tick_counter, pwm_tick_counter_limit_c-1 değerine ulaştığında yani 1 birim çözünürlük için gereken zaman tamamlandığında pwm_tick adlı sinyal 1 clock süresince ‘1’ yapılır. pwm_tick ‘1’ olduğunda PWM sinyalinin periyodunu birim çözünürlük biriminden saklayan pwm_duty_counter adlı bir sinyalin değeri bir arttırılır. PWM_i isimli girişten alınan değer ile pwm_duty_counter kıyaslanarak PWM sinyalinin Ton veya Toff durumlarından hangisinde olması gerektiği belirlenir. pwm_tick_counter değeri 0 ile pwm_value-1 değeri arasında ise çıktı ‘1’ yapılarak Ton durumu gerçekleştirilirken pwm_tick_counter değerinin bu değerden büyük olması durumunda çıkış ‘0’ yapılır.

VHDL’de yazılan PWM üretici kod aşağıda verilmiştir.

--VHDLVerilog.com (VerilogVHDL.com) - 2025 
--PWM Kontrolcusu

library ieee;
use ieee.std_logic_1164.all;
use IEEE.numeric_std.all;

entity pwm_generator is
	generic
	(
		CLK_FREQ_c		: integer := 100_000_000;--CLK frekansi(Hz)
		PWM_FREQ_c		: integer := 100;--PWM frekansi(Hz)
		PWM_RESOLUTION_c: integer := 10--PWM cozunurlugu bit degeri
	);
	port
	(
		CLK_i			: in std_logic;--CLK input
		RESET_n_i		: in std_logic;--active low reset
		
		EN_i			: in std_logic;
		PWM_VALUE_i		: in std_logic_vector(PWM_RESOLUTION_c-1 downto 0);
		
		PWM_o			: out std_logic
	);
end entity;

architecture pwm_generator_beh of pwm_generator is

	--pwm_tick_counter signalinin bit deringligini hesaplamak icin kullanilacak olan logaritma fonksiyonu
	--Xilinx Language Templates'ten alinmistir.
	function clogb2( depth : natural) return integer is
		variable temp    : integer := depth;
		variable ret_val : integer := 0;
	begin
		while temp > 0 loop
			ret_val := ret_val + 1;
			temp    := temp / 2;
		end loop;

		return ret_val;
	end function;
	
	--1 birim duty icin gereken clock vuruş sayisinin hesaplanip saklandigi sabit 
	constant pwm_tick_counter_limit_c : integer := (CLK_FREQ_c + ((PWM_FREQ_c * (2**PWM_RESOLUTION_c))/2) ) / (PWM_FREQ_c * (2**PWM_RESOLUTION_c));
	signal pwm_tick_counter 		  : unsigned(clogb2(pwm_tick_counter_limit_c)-1 downto 0) := (others=>'0');--zaman sayaci icin signal tanimlamasi
	signal pwm_tick 				  : std_logic := '0';--1 birim duty gectiginde tetiklenecek olan signalin tanimlamasi 
	signal pwm_duty_counter 		  : unsigned(PWM_RESOLUTION_c-1 downto 0) := (others=>'0');--duty counter sayaci icin signal tanimlamasi
	signal pwm_value 			  	  : unsigned(PWM_RESOLUTION_c-1 downto 0) := (others=>'0');--PWM girdisinin periyot basinda orneklenip saklandigi signal
	
begin
	
	process(CLK_i)
	begin
	
		if rising_edge(CLK_i) then
		
			if RESET_n_i = '0' then--sync reset
			
				--Cikis sifirlamasi
				PWM_o <= '0';
			
				--reset durumundaki signal sifirlama
				pwm_value		 <= (others=>'0');--PWM degerini saklayan registeri sifirla
				pwm_tick_counter <= (others=>'0');--zaman sayacini sifirla
				pwm_tick		 <= '0';--periyot sayacini tetikleyen sinyali sifirla
				pwm_duty_counter <= (others=>'0');--periyot sayacini sifirla.
				
			else
				
				if EN_i = '1'  then--ENABLE girisi aktif ise
				
					if pwm_duty_counter = 0 then--Periyot tamamlanmis ise yeni deger girdisi kabul et
						pwm_value <= unsigned(PWM_VALUE_i);--PWM girdisini örnekle
					end if;
					
					--birim duty periyodunu olcmek icin kullanilan sayac
					if pwm_tick_counter = to_unsigned(pwm_tick_counter_limit_c-1,clogb2(pwm_tick_counter_limit_c)) then--birim duty periyodunun sonuna gelindiyse
						pwm_tick_counter <= (others=>'0');--birim duty counterini sifirla
						pwm_tick <= '1';
					else
						pwm_tick_counter <= pwm_tick_counter + 1;--birim duty counterini arttir.
						pwm_tick <= '0';
					end if;
					
					--PWM periyodunu olcmek icin kullanilan sayac
					if pwm_tick = '1' then--PWM periyodunun sonuna gelindiyse
						if pwm_duty_counter = (pwm_duty_counter'range => '1') then--PWM periyodunun sonuna gelindiyse(tum bitlerin 1 olmasi durumu)
							pwm_duty_counter <= (others=>'0');--duty counteri sifirla
						else
							pwm_duty_counter <= pwm_duty_counter + 1;--duty counteri arttir.
						end if;
					end if;
					
					if pwm_duty_counter < pwm_value then--PWM Ton aninda ise
						PWM_o <= '1';
					else--PWM Toff aninda ise
						PWM_o <= '0';
					end if;					
					
				else--ENABLE girisi pasif ise
					
					--Cikis sifirlamasi
					PWM_o <= '0';
				
					pwm_value		 <= (others=>'0');--PWM degerini saklayan registeri sifirla
					pwm_tick_counter <= (others=>'0');--zaman sayacini sifirla
					pwm_tick		 <= '0';--periyot sayacini tetikleyen sinyali sifirla
					pwm_duty_counter <= (others=>'0');--periyot sayacini sifirla.
					
				end if;
				
			end if;--RESET_n_i

		end if;--rising_edge
	
	end process;

end architecture;

Verilog’da yazılan, yukarıdaki kodun tamamen aynısı olan PWM üretici kod aşağıda verilmiştir.

//VHDLVerilog.com (VerilogVHDL.com) - 2025 
//PWM Kontrolcusu

module pwm_generator
#(
	parameter CLK_FREQ_c		= 100_000_000,//CLK frekansi(Hz)
	parameter PWM_FREQ_c		= 100,//PWM frekansi(Hz)periyodu(T)
	parameter PWM_RESOLUTION_c	= 10//PWM cozunurlugu bit degeri
)
(
	input CLK_i,//CLK input
	input RESET_n_i,//active low reset
	
	input EN_i,		
	input [PWM_RESOLUTION_c-1:0] PWM_VALUE_i,
	
	output reg PWM_o		
);


    //pwm_tick_counter signalinin bit deringligini hesaplamak icin kullanilacak olan logaritma fonksiyonu
    //Xilinx Language Templates'ten alinmistir (***).
    function integer clogb2;
    input integer depth;
      for (clogb2=0; depth>0; clogb2=clogb2+1)
        depth = depth >> 1;
    endfunction

	//1 birim duty icin gereken clock vuruş sayisinin hesaplanip saklandigi sabit 
	localparam pwm_tick_counter_limit_c = (CLK_FREQ_c + ((PWM_FREQ_c * (2**PWM_RESOLUTION_c))/2) ) / (PWM_FREQ_c * (2**PWM_RESOLUTION_c));
	reg [clogb2(pwm_tick_counter_limit_c)-1:0] pwm_tick_counter = {clogb2(pwm_tick_counter_limit_c){1'b0}};//zaman sayaci icin signal tanimlamasi
	reg pwm_tick = 1'b0;//birim duty gectiginde tetiklenecek olan signalin tanimlamasi 
	reg [PWM_RESOLUTION_c-1:0] pwm_duty_counter = 0;//duty counter sayaci icin signal tanimlamasi
	reg [PWM_RESOLUTION_c-1:0] pwm_value = 0;//PWM girdisinin periyot basinda orneklenip saklandigi signal
	
	always@(posedge CLK_i)
	begin
		if(~RESET_n_i) begin //sync reset
		
				//Cikis sifirlamasi
				PWM_o <= 1'b0;
			
				//reset durumundaki signal sifirlama
				pwm_value		 <= 0;//PWM degerini saklayan registeri sifirla
				pwm_tick_counter <= 0;//zaman sayacini sifirla
				pwm_tick		 <= 1'b0;//periyot sayacini tetikleyen sinyali sifirla
				pwm_duty_counter <= 0;//periyot sayacini sifirla.
				
		end else begin
				
			if(EN_i) begin//ENABLE girisi aktif ise
			
				if(pwm_duty_counter == 0)//Periyot tamamlanmis ise yeni deger girdisi kabul et
					pwm_value <= PWM_VALUE_i;//PWM girdisini örnekle
				
				//birim duty periyodunu olcmek icin kullanilan sayac
                    if(pwm_tick_counter == (pwm_tick_counter_limit_c-1)) begin//birim duty periyodunun sonuna gelindiyse
                        pwm_tick_counter <= 0;//birim duty counterini sifirla
                        pwm_tick <= 1'b1;
                    end else begin
                        pwm_tick_counter <= pwm_tick_counter + 1;//birim duty counterini arttir.
                        pwm_tick <= 1'b0;
                    end
                    
				//PWM periyodunu olcmek icin kullanilan sayac
				if(pwm_tick == 1)//PWM periyodunun sonuna gelindiyse
					if(pwm_duty_counter == {PWM_RESOLUTION_c{1'b1}}) //PWM periyodunun sonuna gelindiyse(-1 tum bitlerin 1 olmasi durumudur)
						pwm_duty_counter <= 0;//duty counteri sifirla
					else
						pwm_duty_counter <= pwm_duty_counter + 1;//duty counteri arttir.
				
				if(pwm_duty_counter < pwm_value)//PWM Ton aninda ise
					PWM_o <= 1'b1;
				else//PWM Toff aninda ise
					PWM_o <= 1'b0;
				
			end else begin//ENABLE girisi pasif ise
				
				//Cikis sifirlamasi
                PWM_o <= 1'b0;
            
                //reset durumundaki signal sifirlama
                pwm_value         <= 0;//PWM degerini saklayan registeri sifirla
                pwm_tick_counter <= 0;//zaman sayacini sifirla
                pwm_tick         <= 1'b0;//periyot sayacini tetikleyen sinyali sifirla
                pwm_duty_counter <= 0;//periyot sayacini sifirla.
                
			end
		
		end
	end

endmodule

VHDL’de yazılan testbench kodu aşağıda verilmiştir.

--VHDLVerilog.com (VerilogVHDL.com) - 2025 
--PWM kontrolcusu testbench
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity pwm_generator_tb is
	
	generic
	(
		CLK_FREQ_c		: integer := 100_000_000;--CLK frekansi(Hz)
		PWM_FREQ_c		: integer := 100;--PWM frekansi(Hz)
		PWM_RESOLUTION_c: integer := 10--PWM cozunurlugu bit degeri
	);

end entity;

architecture DUT of pwm_generator_tb is

---------------------------------------------------------------------------------------------------	
--components
---------------------------------------------------------------------------------------------------
	component pwm_generator is
		generic
		(
			CLK_FREQ_c		: integer := 100_000_000;--CLK frekansi(Hz)
			PWM_FREQ_c		: integer := 100;--PWM frekansi(Hz)
			PWM_RESOLUTION_c: integer := 10--PWM cozunurlugu bit degeri
		);
		port
		(
			CLK_i			: in std_logic;--CLK input
			RESET_n_i		: in std_logic;--active low reset
			
			EN_i			: in std_logic;
			PWM_VALUE_i		: in std_logic_vector(PWM_RESOLUTION_c-1 downto 0);
			
			PWM_o			: out std_logic
		);
	end component;

---------------------------------------------------------------------------------------------------
--signals
---------------------------------------------------------------------------------------------------
	constant clock_period_c	: time := 10ns;--100MHz
	
	signal clk				: std_logic := '0';--clk
	signal reset_n			: std_logic := '1';--active low reset
	signal en				: std_logic := '0';--enable
	
	signal pwm_value		: std_logic_vector(PWM_RESOLUTION_c-1 downto 0) := (others=>'0');

begin
	
	--clock generator
	clock_process : 
	process
	begin
		clk <= '1';
		wait for clock_period_c/2;
		clk <= '0';
		wait for clock_period_c/2;
	end process;
	
	PWM_GENERATOR_TEST :
	process
	begin
		
		reset_n <= '0';--reset aktif
		en <= '0';
		wait for clock_period_c;
		reset_n <= '1';--reset pasif
		pwm_value <= "0000000000";--0
		wait for 1ms;
		en <= '1';
		wait for 15ms;
		pwm_value <= "0010000000";
		wait for 20ms;
		pwm_value <= "0001000000";
		wait for 20ms;
		pwm_value <= "1000000000";
		wait for 3.5ms;
		pwm_value <= "1111111111";
		en <= '0';
		wait for 15ms;
		pwm_value <= "1111111111";
		en <= '1';
		wait for 15ms;
		en <= '0';
		pwm_value <= "0000000000";
		
		wait;

	end process;

---------------------------------------------------------------------------------------------------
--instantiations
---------------------------------------------------------------------------------------------------
	DUT : pwm_generator
	generic map
	(
		CLK_FREQ_c		=> CLK_FREQ_c,	
		PWM_FREQ_c		=> PWM_FREQ_c,
		PWM_RESOLUTION_c=> PWM_RESOLUTION_c
	)
	port map
	(
		CLK_i			=> clk,
		RESET_n_i		=> reset_n,
		
		EN_i			=> en,
		PWM_VALUE_i		=> pwm_value,
		
		PWM_o			=> open
	);
	
end architecture;

Verilog’da yazılan, yukarıdaki testbench kodunun tamamen aynısı olan testbench kodu aşağıda verilmiştir.

//VHDLVerilog.com (VerilogVHDL.com) - 2025 
//PWM kontrolcusu testbench
`timescale 1ns / 1ns
module pwm_generator_tb
	#
	(
		parameter CLK_FREQ_c = 100_000_000,//CLK frekansi(Hz)
		parameter PWM_FREQ_c = 100,//PWM frekansi(Hz)
		parameter PWM_RESOLUTION_c = 10//PWM cozunurlugu bit degeri
	);
	
	localparam clock_period_c = 10;
	
	reg clk		= 1'b0;//clk
	reg reset_n	= 1'b0;//active low reset
	reg en		= 1'b0;//enable
	
	reg [PWM_RESOLUTION_c-1:0] pwm_value = 0;
	
	always #(clock_period_c/2) clk=~clk; //100MHz
	
	initial begin
		reset_n <= 1'b0;//reset aktif
		en <= 1'b0;
		#clock_period_c
		reset_n <= 1'b1;//reset pasif
		pwm_value <= 10'b0000000000;//0
		#1000000//1ms
		en <= 1;
		#15000000//15ms
		pwm_value <= 10'b0010000000;
		#20000000//20ms
		pwm_value <= 10'b0001000000;
		#20000000//20ms
		pwm_value <= 10'b1000000000;
		#20000000//20ms
		pwm_value <= 10'b1111111111;
		en <= 0;
		#3500000//3.5ms
		pwm_value <= 10'b1111111111;
		en <= 1;
		#15000000//15ms
		en <= 0;
		pwm_value <= 10'b0000000000;
		
		$stop;
		
		end

	pwm_generator
	#(
		.CLK_FREQ_c		  (CLK_FREQ_c),	
		.PWM_FREQ_c		  (PWM_FREQ_c),
		.PWM_RESOLUTION_c (PWM_RESOLUTION_c)
	)
	DUT
	(
		.CLK_i		 (clk),
		.RESET_n_i	 (reset_n),

		.EN_i		 (en),
		.PWM_VALUE_i (pwm_value),

		.PWM_o		 ()
	);
		
endmodule

Yukarıdaki testbench kodlarının waveform çıktısı Şekil 2’de verilmiştir.


Şekil 2 - Waveform çıktısı

Bu içerikte kullanılan Şekil 1 Learn Electronics India adresinden alınmıştır.