توسط احمد ثقفی 

5 شهریور, 1397

متغیر در زبان VHDL

آیا تا به حال از variable یا متغیر در زبان VHDL استفاده کرده‌اید؟

تفاوت استفاده از signal و متغیر در زبان VHDL چیست؟

وقتی کد شما در FPGA پیاده‌سازی می‎‌شود، تحقق دیجیتالی سیگنال و متغیر چیست؟

در چه مواردی باید از سیگنال و چه زمانی از متغیر استفاده کنیم؟

اینها سوالاتی است که در این برنامه ویدئویی به آنها پاسخ می‌دهم.

More...

در زبان VHDL مفهومی به نام object وجود دارد که در حقیقت کمک می‌کند بتوانید سیم‌ها و رجیسترها را در FPGA پیاده‌سازی کنید. سه object در زبان VHDL وجود دارد:

  • Signal
  • Constant
  • Variable

در مورد signal که اصلی‌ترین object مورد استفاده در زبان VHDL است در برنامه‌های قبلی به طور مفصل صحبت کرده‌ام.

برای آشنایی با زبان VHDL این برنامه ویدئویی را ببینید…

در این برنامه، ضمن یادآوری مفهوم signal و constant به طور مفصل در مورد مفهوم variable و نحوه استفاده از آن توضیح می‌دهم.

آنچه خواهید دید به شما نشان می‌دهد که استفاده از متغیر در زبان VHDL با ذات پیاده‌سازی دیجیتال در FPGA خیلی سازگار نیست و مشکلاتی ایجاد می‌کند که باعث می‌شود از به کار گیری آن صرف نظر کنیم.

یکی از مشکلات مهمی که استفاده از متغیر در زبان VHDL ایجاد می‌کند، خصوصا اگر این کار را بدون بینش لازم در مورد نحوه تفسیر و پیاده‌سازی متغیر در زبان VHDL انجام دهیم، کاهش سرعت مدار است.

برای آشنایی با روش‌های افزایش سرعت مدار در FPGA این مقاله را مطالعه کنید…

برای درک بهتر تفاوت‌های متغیر و سیگنال در پیاده‌سازی روی FPGA شماتیک مدار پیاده‌سازی شده در FPGA را در هر دو حالت به کمک یکی از قابلیت‌های جالب نرم‌افزار ISE به شما نشان می‌دهم.

برای آشنایی با نرم‌افزار ISE این برنامه ویدئویی را ببینید…

اگر زمان کافی برای مشاهده این ویدئو ندارید، اجازه دهید خلاصه بحث را در یک جمله به شما بگویم: هیچوقت از متغیر در زبان VHDL استفاده نکنید. همین.

اما پیشنهاد می‌کنم حتما این برنامه را به طور کامل مشاهده کنید، چون در آن در مورد موضوعات و مفاهیم بسیار مهمی در ارتباط با پیاده‌سازی دیجیتال در FPGA به کمک زبان VHDL صحبت می‌کنم.

ویدئو یا متن؟

محتوای این برنامه آموزشی، به دو صورت ویدئو و متن آماده شده است. اگر علاقمند به یادگیری این مطلب به صورت ویدئویی هستید، ویدئوی زیر را ببینید و اگر ترجیح می‌دهید آن را به صورت متن مطالعه کنید، ادامه این مطلب را بخوانید.

برای دانلود نسخه با کیفیت این ویدئو، روی دکمه زیر کلیک کنید:

معمولاً افراد تازه وارد به حوزه FPGA، دوست دارند بدانند که چگونه می‌توان از متغیر، در کد توصیف سخت‌افزاری استفاده کرد؛ کدی که قرار است در FPGA پیاده‌سازی شود.

من در تمام کدها و پیاده‌سازی‌های سال‌های اخیر، تا به حال از متغیر استفاده نکرده‌ام و هیچ مشکلی برای پیاده‌سازی هر نوع مدار دیجیتال نداشته‌ام.

اما منظور از متغیر در زبان VHDL چیست و چرا استفاده از آن را در پیاده‌سازی‌ها توصیه نمی‌کنم؟

قبل از ورود به مبحث متغیر، یک نگاه کلی به مفهومی به‌نام object در زبان VHDL خواهیم داشت.

انواع Object در زبان VHDL

همان‌طور که گفتم، به‌طور کلی در زبان VHDL سه نوع object داریم:

  • signal
  • constant
  • variable

شاید بتوان گفت که مهم‌ترین objectای که در زبان VHDL استفاده می‌شود signal است. در واقع، بخش عمده‌ای از کد شما را سیگنال‌ها تشکیل می‌دهند.

سیگنال؛ به‌عنوان یک Object در زبان VHDL

محل تعریف سیگنال‌ها در کد VHDL، محیط decleration یا معرفی است. این محیط، قبل از begin در بخش architecture واقع شده است.

برای مقداردهی به یک سیگنال در کد VHDL، از علامت کوچکتر-مساوی (=>) یا signal assignment استفاده می‌کنیم.

سیگنالی که در بخش معرفی تعریف می‌کنید در تمام کد قابل دسترسی است؛ در واقع، این سیگنال در محیط کانکارنت و در تمامی پراسس‌های کد قابل استفاده است.

اما سوال بسیار بسیار مهمی وجود دارد که می‌توانیم در ارتباط با سیگنال بپرسیم. من این سوال را در ابتدای مباحث دوره طراحی دیجیتال از شرکت کنندگان می‌پرسم.

سوال این است که وقتی یک سیگنال را قبل از begin در بخش architecture تعریف می‌کنیم، در هنگام پیاده‌سازی کد در FPGA، این سیگنال تبدیل به چه سخت‌افزاری می‌شود.

در واقع، تحقق دیجیتالی یا فیزیکی سیگنالی که در کد VHDL تعریف می‌شود، در FPGA چیست؟

پاسخ این است که سیگنال تعریف شده تبدیل به سیم یا رجیستر می‌شود.

اما در چه صورت سیگنال تبدیل به سیم و در چه صورت تبدیل به رجیستر می‌شود؟

اگر شما به یک سیگنال در محیط کانکارنت ارجاع دهید، آن سیگنال تبدیل به یک سیم می‌شود.

اما اگر به یک سیگنال در محیط پراسس و درون شرط لبه بالارونده کلاک ارجاع دهید، آن سیگنال تبدیل به رجیستر می‌شود.

اکنون به‌سراغ object بعدی، یعنی constant برویم.

مقدار ثابت؛ به‌عنوان یک Object در زبان VHDL

در زبان VHDL، شما می‌توانید constant را نیز در محیط معرفی (قبل از begin در بخش architecture) تعریف کنید.

در هنگام تعریف constant، شما یک مقدار اولیه به آن اعمال می‌کنید که البته مقدار نهایی آن constant نیز است.

در واقع، نمی‌توانید در خلال کد، مقدار جدیدی به آن اعمال کنید. مقدار constant در هنگام تعریفش مشخص می‌شود و همان مقدار باقی می‌ماند.

constant را نیز می‌توان مانند سیگنال، در تمام کد (شامل محیط کانکارنت و پراسس‌ها) به‌کار برد.

اما تحقق فیزیکی و دیجیتالی constant در FPGA به چه صورت است؟

با توجه به اینکه constant یک مقدار ثابت است، در پیاده‌سازی، به‌صورت سطوح منطقی یا همان Vcc و GND‌هایی که درون FPGA وجود دارد استفاده می‌شود.

مثلاً فرض کنید یک constant دو‌بیتی دارید که مقدار اولیه آن را 01 قرار داده‌اید. حال فرض کنید در بخشی از کد این constant را به یک سیگنال ارجاع داده‌اید؛ در عمل چه چیزی به آن سیگنال ارجاع داده می‌شود؟

در عمل، مقدار ثابت 01 به آن سیگنال اعمال می‌شود که تحقق فیزیکی آن Vcc و GND‌هایی است که درون FPGA وجود دارند.

با این مقدمه، به‌سراغ variable برویم و ببینیم که در زبان VHDL، variable دقیقاً چیست و نحوه پیاده‌سازی آن به چه صورت است؟

متغیر؛ به‌عنوان یک Object در زبان VHDL

برخلاف signal و constant که در محیط معرفی (قبل از begin در بخش architecture) تعریف می‌شوند، variable درون پراسس و قبل از begin این بخش تعریف می‌شود.

signal و constant را می‌توان در تمام کد (شامل محیط کانکارنت و پراسس‌ها) به‌کار برد؛ اما variable را تنها می‌توان در پراسسی که در آن تعریف شده است استفاده کرد.

برای ارجاع signal و constant از علامت (=>) استفاده می‌کردیم اما برای ارجاع variable باید از علامت (=:) استفاده کنیم.

این موارد، تفاوت‌های ظاهری و نحوه استفاده signal با variable هستند.

اما تفاوت اصلی variable و signal چیست؟

برخلاف سیگنال، مقدار ارجاع شده به یک متغیر در یک پراسس، در همان کلاک تغییر می‌کند.

همان‌طور که می‌دانید، وقتی به یک سیگنال در محیط پراسس و درون شرط لبه بالارونده کلاک مقداری را ارجاع می‌دهید، مقدار آن سیگنال تغییری نمی‌کند، تا زمانی که پراسس خاتمه یابد و تمامی ارجاعات انجام شوند.

به‌عبارت دیگر، تمامی ارجاعات درون پراسس، با مقادیر قبل از فعال شدن پراسس انجام می‌شود. اما در مورد متغیر این داستان متفاوت است.

برای روشن شدن بهتر موضوع، از یک مثال استفاده می‌کنیم.

فرض کنید که شما کدی دارید که دارای یک پراسس است. درون شرط لبه بالارونده کلاک درون پراسس، چند ارجاع وجود دارد (کد زیر):

If rising_edge(Clock) then

	B <= A;
	C <= B;

End;

فرض کنید A، B و C سیگنال هستند.

اگر قبل از فعال شدن پراسس، مقادیر B ،A و C به‌ترتیب 1، 2 و 3 باشد، پس از اعمال لبه بالارونده کلاک و انجام ارجاعات پراسس، این مقادیر به چه صورت خواهد بود؟

همان‌طور که گفته شد، تمام ارجاعات، با مقادیر قبل از فعال شدن پراسس انجام می‌شود.

در واقع، در ارجاع اول، مقدار A برابر با 1 خواهد بود و در ارجاع دوم، مقدار B برابر با 2 است.

بنابراین، پس از فعالیت پراسس، مقادیر B ،A و C به‌ترتیب برابر 1، 1 و 2 می‌شوند.

زیرا به سیگنال A، ارجاعی صورت نگرفته است. پس مقدار آن بدون تغییر می‌ماند. در سطر اول، A به B ارجاع شده است؛ مقدار A برابر 1 است. پس مقدار B نیز 1 خواهد بود.

و اما برای مقدار C، چون سیگنال B به C ارجاع شده است و مقدار B، قبل از فعال شدن پراسس برابر با 2 بوده است، بنابراین، مقدار C نیز 2 خواهد بود.

این روندی است که برای ارجاعات درون پراسس اتفاق می‌افتد.

برای آشنایی بیشتر با بخش پراسس در کد VHDL، این برنامه را ببینید...

این خصوصیات ظاهراً عجیب و غریب باعث می‌شود که بتوانیم مدارات دیجیتالی را به‌کمک زبان VHDL توصیف کنیم.

همان‌طور که می‌دانید، در عمل سیگنال B و C درون FPGA تبدیل به رجیستر می‌شوند.

اکنون کد قبل را به‌صورت زیر تغییر می‌دهیم:

If rising_edge(Clock) then

	B := A;
	C <= B;

End;

فرض کنید A و C سیگنال و B یک متغیر باشد؛ در واقع، B متغیری است که درون همین پراسس تعریف شده است.

همان‌طور که مشاهده می‌کنید، برای ارجاع به متغیر B، از علامت =: استفاده کرده‌ایم.

فرض کنید مقادیر B ،A و C قبل از فعال شدن پراسس 1، 2 و 3 باشند.

حال فرض کنید که پراسس فعال شود، ارجاعات درون آن انجام شوند و پراسس خاتمه یابد.

به نظر شما پس از اتمام فعالیت پراسس، مقادیر B ،A و C به چه صورت خواهد بود؟

هر سه مقدار برابر با 1 خواهند بود؛ اما چرا؟

به‌دلیل تفاوتی که متغیرها با سیگنال‌ها دارند این اتفاق می‌افتد.

همان‌طور که پیش از این نیز گفتم، قبل از اتمام پراسس، مقدار سیگنال‌ها تغییر نمی‌کند. بنابراین، تمام ارجاعات با مقادیر سیگنال‌ها، قبل از فعال شدن پراسس اتفاق می‌افتد.

اما در مورد متغیر‌ها به این صورت نیست؛ وقتی درون یک پراسس به یک متغیر مقداری را ارجاع می‌دهید، مقدار متغیر در همان لحظه تغییر می‌کند. بنابراین، در ارجاعات سطرهای بعدی، مقدار جدید متغیر لحاظ می‌شود.

در اولین ارجاع این کد، مقدار متغیر B در لحظه ارجاع، برابر با 1 می‌شود. در ارجاع دوم، B به C ارجاع داده شده است؛ مقدار B در سطر قبل برابر با 1 شده بود؛ چون B یک متغیر است، مقدار 1 به سیگنال C ارجاع می‌شود.

بنابراین، پس از اتمام فعالیت پراسس، مقادیر B ،A و C هر سه برابر با 1 هستند.

همان‌طور که می‌دانید، با آغاز به‌کار پراسس، تمامی ارجاعات به‌صورت هم‌زمان شروع می‌شوند.

اما وقتی در یک پراسس متغیر داشته باشید، با شروع پراسس، ارجاعاتی که در سمت راست آن‌ها متغیر حضور دارد، نمی‌توانند انجام شوند.

زیرا ابتدا باید به آن متغیر ارجاع انجام شود و مقدار آن تغییر کند و سپس مقدار جدید آن در ارجاعات دیگر لحاظ شود.

همان‌طور که احتمالاً حدس می‌زنید، این موضوع موجب کند شدن مدار شما می‎شود.

مثلاً، در کدهای این مقاله، ارجاعات کد اول به محض فعال شدن پراسس، انجام می‌شوند. اما در کد دوم، ابتدا باید سطر اول انجام شده و مقدار متغیر تعیین شود و سپس ارجاع سطر دوم می‌تواند انجام شود.

به این ترتیب، با حضور متغیر در یک پراسس، مدت زمان انجام ارجاعات افزایش پیدا می‌کند و مدار کُند می‌شود که این مسئله خوشایند ما نیست.

زیرا ما همواره به‌دنبال این هستیم که ارجاعات پراسس در کمترین زمان ممکن اتفاق بیفتند تا بتوانیم سیگنال کلاکی با فرکانس بیشتر به مدار اعمال کنیم.

از طرف دیگر، این نوع از ارجاع، با ذات و ساختاری که برای پیاده‌سازی مدارات و توصیف مدارات دیجیتال مدنظر ما است هم‌خوانی ندارد.

بنابراین، در پروژه‌هایمان از متغیر استفاده نمی‌کنیم؛ همان‌طور که گفتم، من در هیچ‌کدام از پروژه‌هایی که در حدود ۱۲ سال اخیر انجام داده‌ام، از متغیر استفاده نکرده‌ام و این موضوع، محدودیتی در پیاده‌سازی‌ها ایجاد نکرده است.

برای شفاف شدن بهتر موضوع، اکنون مثالی را بررسی می‌کنم که در آن، شماتیک دو مداری که یکی از آن‌ها دارای متغیر و دیگری بدون متغیر است را در نرم‌افزار ISE با‌هم مقایسه خواهیم کرد.

به این ترتیب، تفاوت پیاده‌سازی یک سیگنال و متغیر در FPGA را مشاهده خواهید کرد.

مثالی از کد VHDL بدون متغیر

به کد VHDL زیر توجه کنید:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity vari is
    Port ( 
				 Clock 		: in  	STD_LOGIC;
         A 				: in  	unsigned (7 downto 0);
         B 				: in  	unsigned (7 downto 0);
         C 				: in  	unsigned (7 downto 0);
         D 				: in  	unsigned (7 downto 0);
         Output 	: out 	unsigned (15 downto 0)
			);
end vari;

architecture Behavioral of vari is

signal A_Int 			:	unsigned(7 downto 0)		:= (others => '0');
signal B_Int 			:	unsigned(7 downto 0)		:= (others => '0');
signal C_Int 			:	unsigned(7 downto 0)		:= (others => '0');
signal D_Int 			:	unsigned(7 downto 0)		:= (others => '0');
signal Product1 	:	unsigned(15 downto 0)		:= (others => '0');
signal Product2 	:	unsigned(15 downto 0)		:= (others => '0');
signal Sum 				:	unsigned(15 downto 0)		:= (others => '0');


begin

Output <= Sum;

process(Clock)
begin

	if rising_edge(Clock) then
		
		A_Int 	 <= A;
		B_Int 	 <= B;
		C_Int 	 <= C;
		D_Int 	 <= D;
		
		Product1 <=	A_Int	*	B_Int;
		Product2 <= 	C_Int	*	D_Int;
		
		Sum 		 <=  Product1	+	Product2;		
	
	end if;
	
 end process;

end Behavioral;

در بخش entity این کد (خط هفت تا ۱۱)، یک سیگنال Clock و سیگنال‌های ورودی هشت‌بیتی C ،B ،A و D را داریم. همچنین، یک خروجی ۱۶‌بیتی به‌نام سیگنال Output داریم. همه سیگنال‌های برداری از نوع unsigned هستند.

در خط ۱۸ تا ۲۱ (بخش معرفی)، سیگنال‌هایی تعریف کرده‌ام که به‌کمک آن‌ها ورودی‌ها را رجیستر کنم. این موضوع برای رعایت یکی از قواعد الگوی استاندارد کدنویسی برای FPGA است.

بر طبق این قاعده، حتماً باید ورودی‌ها را رجیستر کنیم.

برای آشنایی با الگوی استاندارد کدنویسی برای FPGA، این برنامه را ببینید...

کاری که در این مدار قصد انجام آن را داریم بسیار ساده است؛ همان‌طور که در بخش پراسس مشاهده می‌کنید، ابتدا سیگنال‌های A تا D را در سیگنال‌های A_Int تا D_Int رجیستر کرده‌ام (خط ۳۶ تا ۳۹).

سپس، در خط ۴۱، سیگنال A_Int و B_Int را در هم ضرب کرده و به سیگنال Product1 ارجاع داده‌ام. سیگنال Product1 را در بخش معرفی (خط ۲۲) تعریف کرده‌ام؛ Product1 یک سیگنال ۱۶‌بیتی از نوع unsigned است.

پس از آن، در خط ۴۲، سیگنال C_Int و D_Int را در هم ضرب کرده و به سیگنال Product2 ارجاع داده‌ام.

در خط ۴۴، سیگنال‌های Product1 و Product2 را با‌هم جمع کرده و به سیگنال Sum ارجاع داده‌ام؛ سیگنال Sum،‌ یک سیگنال ۱۶‌بیتی و از نوع unsigned است.

در بخش کانکارنت،‌ سیگنال Sum را به سیگنال Output ارجاع داده‌ام (خط ۲۹). سیگنال Output نیز ۱۶‌بیتی و از نوع unsigned است.

کد بالا، یک کد استاندارد و روش مرسومی است که ما برای پیاده‌سازی‌ها استفاده می‌کنیم.

همان‌طور که در ابتدای مقاله گفتم، اگر شما در شرط لبه بالارونده کلاک به یک سیگنال ارجاع انجام دهید، آن سیگنال در پیاده‌سازی درون FPGA تبدیل به یک رجیستر می‌شود.

بنابراین،‌ تمام سیگنال‌هایی که در سمت چپ ارجاعات درون پراسس قرار دارند (سیگنال‌های A_Int تا D_Int، سیگنال Product1، سیگنال Product2 و سیگنال Sum)، در پیاده‌سازی تبدیل به رجیستر می‌شوند.

برای دیدن یک شماتیک از مداری که درون FPGA پیاده‌سازی می‌شود، باید همانند شکل زیر از زیر مجموعه Synthesize-XST،‌ گزینه View RTL Schematic را انتخاب کنید:

نمایش مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

در پنجره‌ای که مطابق شکل زیر باز می‌شود،‌ گزینه OK را انتخاب کنید:

نمایش مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

در پنجره بعدی،‌ ابتدا گزینه Add و سپس گزینه Create Schematic را انتخاب کنید:

نمایش مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

به این ترتیب، شماتیک مدار به‌صورت شکل زیر نمایش داده می‌شود:

شماتیک مدار در نرم‌افزار ISE

شماتیک کلی مدار

البته این شکل یک تاپ‌لِوِلی از شماتیک را نشان می‌دهد؛‌ با دبل‌کلیک بر روی آن می‌توانیم شماتیک کامل مدار را مشاهده کنیم.

اگر مطابق شکل زیر، روی گزینه Zoom to full view کلیک کنید، می‌توانید تمام اجزای مدار را در یک قاب ببینید:

شماتیک مداری که به‌کمک کد توصیف سخت‌افزاری دوم در FPGA پیاده‌سازی خواهد شد

شماتیک مداری که به‌کمک کد توصیف سخت‌افزاری بدون متغیر در FPGA پیاده‌سازی خواهد شد

در قسمت شماره 1، چهار رجیستر A_Int تا D_Int را ملاحظه می‌کنید؛ در واقع، ورودی‌ها را به این طریق رجیستر کرده‌ایم.

سپس، در قسمت شماره 2 ملاحظه می‌کنید که این رجیسترها وارد دو بلوک ضرب‌کننده شده‌اند.

همان‌طور که به‌خاطر دارید، حاصل‌ضرب‌ها را به دو سیگنال Product1 و Product2 ارجاع دادیم؛ در قسمت شماره 3 مشاهده می‌کنید که این سیگنال‌ها به رجیستر تبدیل شده‌اند.

در قسمت شماره 4، یک جمع‌کننده می‌بینید که دو سیگنال Product1 و Product2 وارد آن می‌شوند.

در قسمت شماره 5، رجیستر Sum را ملاحظه می‌کنید که حاصل‌جمع Product1 و Product2 به آن وارد می‌شود.

چون در محیط کانکارنت، سیگنال Sum را به خروجی Output ارجاع دادیم، رجیستر Sum به‌کمک یک سیم به خروجی متصل می‌شود.

اکنون در کد تغییراتی ایجاد می‌کنیم و در آن از متغیر استفاده می‌کنیم تا ببینیم چه تفاوتی در مدار پیاده‌سازی شده خواهیم داشت.

مثالی از کد VHDL که در آن از متغیر استفاده شده است

در کد جدید، Product1 و Product2 را به‌صورت متغیر تعریف خواهیم کرد.

برای این کار، باید خط ۲۲ و ۲۳ کد اول (محلی که این دو سیگنال تعریف شده‌اند) را به قبل از begin مربوط به پراسس جابه‌جا کنیم؛ زیرا محل تعریف متغیرها قبل از begin در بخش پراسس است.

البته در این دو سطر باید به‌جای کلمه signal، از کلمه variable استفاده کنیم تا Product1 و Product2 به‌عنوان متغیر تعریف شوند (خط ۳۱ و ۳۲ کد زیر).

به‌علاوه، در محل ارجاع به این دو متغیر در درون پراسس،‌ از علامت =: استفاده می‌کنیم (خط ۴۳ و ۴۴ کد زیر).

کد ویرایش شده را در زیر مشاهده می‌کنید:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity vari is
    Port ( 
				 Clock 		: in  	STD_LOGIC;
         A 				: in  	unsigned (7 downto 0);
         B 				: in  	unsigned (7 downto 0);
         C 				: in  	unsigned (7 downto 0);
         D 				: in  	unsigned (7 downto 0);
         Output 	: out  	unsigned (15 downto 0)
			);
end vari;

architecture Behavioral of vari is

signal A_Int 		:	unsigned(7 downto 0)		:= (others => '0');
signal B_Int 		:	unsigned(7 downto 0)		:= (others => '0');
signal C_Int		:	unsigned(7 downto 0)		:= (others => '0');
signal D_Int 		:	unsigned(7 downto 0)		:= (others => '0');
signal Sum 			:	unsigned(15 downto 0)		:= (others => '0');


begin

Output <= Sum;

process(Clock)

variable Product1 	:	unsigned(15 downto 0)	:= (others => '0');
variable Product2 	:	unsigned(15 downto 0)	:= (others => '0');

begin

	if rising_edge(Clock) then
		
		A_Int 	<= A;
		B_Int 	<= B;
		C_Int 	<= C;
		D_Int 	<= D;
		
		Product1 :=	A_Int	*	B_Int;
		Product2 := C_Int	*	D_Int;
		
		Sum 		<= Product1	+	Product2;		
	
	end if;
	
 end process;

end Behavioral;

قبل از سنتز کردن کد جدید و مشاهده شماتیک آن، ابتدا حداکثر فرکانس قابل اعمال به مدار کد قبل را چک می‌کنیم.

برای این کار، مطابق شکل زیر عمل می‌کنیم؛‌ به‌سراغ گزارش سنتز می‌رویم و از بخش Timing Summary که در اواخر این گزارش واقع شده است،‌ حداکثر فرکانس را مشاهده می‌کنیم. این مقدار فرکانس مربوط به مرحله سنتز و به‌صورت تخمینی است.

بررسی حداکثر فرکانس قابل اعمال به مدار در گزارش سنتز

بررسی حداکثر فرکانس قابل اعمال به مدار در گزارش سنتز

اکنون می‌خواهیم کد ویرایش شده را با کد قبل مقایسه کنیم.

چون در کد دوم، Product1 و Product2 متغیر هستند، ابتدا باید مقادیر آن‌ها در پراسس تعیین شود. پس از آن، ارجاع سطر ۴۶ که در آن مجموع Product1 و Product2 به سیگنال Sum ارجاع می‌شود، انجام خواهد شد.

دوباره گزینه View RTL Schematic را انتخاب می‌کنیم:

مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

دکمه OK را انتخاب می‌کنیم:

مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

سپس، گزینه Add و View Schematic را انتخاب می‌کنیم:

مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

مراحل دسترسی به شماتیک مداری که در FPGA پیاده‌سازی خواهد شد

بلوک دیاگرام زیر نمایش داده می‌شود که روی آن دبل کلیک می‌کنیم:

نمایش شماتیک مدار در نرم‌افزار ISE

شماتیک کلی مدار

به این ترتیب، مدار زیر نمایش داده می‌شود:

شماتیک مداری که به‌کمک کد توصیف سخت‌افزاری دوم در FPGA پیاده‌سازی خواهد شد

شماتیک مداری که به‌کمک کد توصیف سخت‌افزاری دارای متغیر در FPGA پیاده‌سازی خواهد شد

همان‌طور که مشاهده می‌کنید، در این مدار خبری از رجیسترهای Product1 و Product2 نیست؛ زیرا آن‌ها را به‌عنوان یک متغیر تعریف کردیم.

بنابراین، Product1 و Product2 به‌صورت سیم پیاده‌سازی شده‌اند.

وقتی لبه بالارونده سیگنال کلاک به رجیسترهای ورودی می‌رسد، مقادیر ورودی‌ها به بلوک‌های ضرب‌کننده منتقل می‌شود. سپس، باید عملیات جمع انجام شود و حاصل وارد رجیستر Sum می‌شود.

اگر پیش از انجام ضرب‌ها و جمع،‌ لبه دیگری از سیگنال کلاک به رجیسترها اعمال شود،‌ خروجی نامعتبر خواهد بود؛ چون هنوز عملیات قبلی در حال انجام است.

در مدار مربوط به کد قبل،‌ بین هر دو رجیستر تنها یک عملیات انجام می‌شد؛ اما در این مدار، بین رجیسترهای ورودی و رجیستر Sum، هم عمل ضرب و هم عمل جمع باید انجام شود.

پس از انجام هر دو عملیات اجازه داریم لبه دیگری از سیگنال کلاک را به مدار اعمال کنیم. بنابراین، فاصله بین دو لبه سیگنال کلاک بیشتر می‌شود؛ در نتیجه، فرکانس سیگنال کلاک، نسبت به حالت قبل کمتر خواهد بود. به این ترتیب، کارکرد مدار دوم کندتر است.

پس، انتظار داریم حداکثر فرکانس کلاک قابل اعمال به مدار کاهش پیدا کرده باشد.

اکنون با مراجعه به گزارش سنتز،‌ این مورد را بررسی می‌کنیم:

بررسی حداکثر فرکانس قابل اعمال به مدار در گزارش سنتز

بررسی حداکثر فرکانس قابل اعمال به مدار در گزارش سنتز

همان‌طور که مشاهده می‌کنید، حداکثر فرکانس کلاک قابل اعمال به مدار به مقدار 104MHz کاهش یافته است.

زیرا در مدار جدید از یک عنصر سنکرون تا یک عنصر سنکرون دیگر (یعنی، بین دو رجیستر)، هم عملیات ضرب و هم جمع را داریم.

مجموع تاخیرهای این دو عملیات موجب کند شدن مدار نسبت به مدار قبل می‌شود؛ زیرا در مدار قبل، بین دو رجیستر تنها یک عملیات باید انجام می‌شد.

بنابراین، با گسترش مدار ترکیبی بین دو رجیستر، شاهد افزایش تاخیر انتشار بودیم که این امر منجر به کاهش فرکانس سیگنال کلاک اعمالی به مدار (کاهش سرعت مدار) شد.

در نتیجه، استفاده از متغیر در پیاده‌سازی‌ها امکان‌پذیر است؛‌ اما خطر کاهش سرعت مدار وجود دارد.

به شما توصیه می‌کنم در ابتدای کار پیاده‌سازی از متغیر استفاده نکنید. پس از گذشت مدتی که تسلط لازم را به‌دست آوردید، می‌توانید آن را با احتیاط در کدهایتان به‌کار ببرید.

من پیش‌بینی می‌کنم که اگر در ابتدای راه پیاده‌سازی از متغیر استفاده نکنید، به تدریج به این نتیجه می‌رسید که می‌توانید هر ایده‌ای که داشته باشید را بدون نیاز به متغیر و فقط با استفاده از سیگنال‌ها پیاده‌سازی کنید.

امیدوارم از خواندن این مقاله هم لذت برده باشید و بتوانید از نکات یاد گرفته شده، در انجام پروژه‌‌هایتان استفاده کنید.

آیا برنامه ویدئویی چرا هرگز از متغیر در زبان VHDL استفاده نمی‌کنم برای شما مفید بود؟

لطفا نظرتان را در مورد این برنامه در پایین همین پست با دیگران به اشتراک بگذارید. همچنین با کلیک روی هر کدام از دکمه‌های اشتراک گذاری ابتدای این مطلب و به اشتراک‌گذاری آن در شبکه‌های اجتماعی می‌توانید افراد بیشتری را در یادگیری این مطالب سهیم کنید.

کانال تلگرام آموزش FPGA از صفر

برای عضویت در کانال تلگرام و دسترسی به آموزش‌های بیشتر و اطلاع سریع از زمان انتشار آموزش‌ها و تخفیف‌های ویژه، روی دکمه زیر کلیک کنید:

درباره نویسنده:

احمد ثقفی

شاید به این موضوعات نیز علاقه داشته باشید:

  • با عرض سلام و احترام،
    آموزش فوق العاده مفید و ظریف و کاربردی بود. بی نهایت از زمانی که برای تهیه این آموزش ها صرف میکنید و همچنین انتقال تجارب باارزشتون سپاسگزارم.
    سوالی که از خدمتتون داشتم این هست که در اولین کد مطرح شده، که شامل دو سیگنال A و C و یک متغیر B است، اگر جای خطوط ارجاع به متغیر B و سیگنال C تغییر کند، تفاوتی در کد ایجاد میشود؟
    متشکرم

    • سلام، ممنون. خوشحالم که این برنامه برای شما مفید بوده.

      بله، اگر جای خطوط را عوض کنید، مقداری که به C ارجاع داده می‌شود، مقدار B قبل از فعال شدن پراسس خواهد بود.

      موفق باشید.

  • سلام استاد ازتون بی نهایت تشکر می کنم بابت این آموزش های بسیار عالی

    یه سوال داشتم از خدمتتون

    چرا به signal و constant و variable عبارت object استفاده میشه ؟؟ منظور از این کلمه چیه؟

    • سلام، ممنون از شما.متاسفانه دلیل انتخاب این کلمه را نمی‌دانم اما منظور چیزی است که می‌تواند یک نوع و یک مقدار داشته باشد و همانطور که در ویدئو گفته شد دارای سه کلاس یا طبقه‌بندی است. بنابراین به هنگام تعریف، شما در واقع کلاس آن (سیگنال، ثایت یا متغیر)، نوع آن و در صورت نیاز مقدار آن را مشخص می‌کنید.

      موفق باشید.

  • بسیار عالی.
    فکر میکنم وبسایت شما تنها و فعلا بهترین مرجع فارسی در رابطه با VHDL هست.

    برای این فیلم های آموزشی هم اگر بتونید زیر نویس انگلیسی ش رو هم آماده کنید، از طریق یوتوب میتونید کسب درآمد کنید.

  • فرمودین که وقتی در پراسس هم متغیر داریم و هم سیگنال، ارجاعاتی که سمت راستشون از متغیر استفاده شده، دیر تر از ارجاعات دیگه (که سمت راستشون سیگنال هست) انجام میشه. این موضوع فقط برای وقتی هست ک متغیر به متغیر ارجاع میشه یا اینکه برای متغیر به سیگنال هم همینطوره؟

    • صحبت این ویدئو این نبود. صحبت این بود که وقتی سمت چپ یک ارجاع سیگنال باشد، آن سیگنال تبدیل به رجیستر می‌شود و وقتی یک متغیر باشد، تبدیل به سیم می‌شود. پس اگر متغیری را که سمت چپ ارجاع بوده است مجددا به سیگنال یا متغیر دیگری ارجاع دهید، شما در حقیقت در حال افزایش مسیر ترکیبی و در نتیجه، احتمالا کاهش سرعت مدار هستید.

  • خیلی ممنون از آموزش خیلی خوبتون. سوالم ین بود که تو حلقه for من مجبورم که حتما از یه variable استفاده کنم.چون یه اندیسی رو وقتی تو یه حلقه تعریف می کنم باید تو همون کلاک ازش استفاده کنم.تو قسمت کانکارنت هم امکان استفاده ازش نیست تو اینجا روشی جایگزین هس که ورییبل استفاده نکنیم؟

  • با سلام
    مطلب نوشته شده به غایت نادرست و گمراه کننده است.
    به طور کلی اگر پیاده سازی با variable و signal به گونه ای باشد که RTL مدار های طراحی شده کاملا یکسان باشد، تمام خروجی های سنتز، از جمله تعداد رجیستر ها، کریتیکال پس و در نهایت ماکزیمم فرکانس کاری مدار یکسان خواهد شد.

    • سلام،

      با تشکر از بیان نظرتان.

      به نظر می‌رسد شما مطالب این مقاله را دقیق مطالعه نکرده‌اید یا احتمالا فقط بخشی از آن را مطالعه کردید. پیام اصلی این مقاله و ویدئو این است که در استفاده از متغیر باید بسیار دقت کرد چون این کار عملا باعث افزایش مسیر بحرانی و در نتیجه کاهش حداکثر فرکانس قابل اعمال به کلاک می‌شود. در نتیجه بهتر است یا از آن اصلا استفاده نکنید یا زمانی که تسلط کافی به پیاده‌سازی پیدا کردید استفاده کنید. شما در کامنت‌تان مطلبی را بیان کردید که درست است اما ربطی به موضوع مطرح شده در این مقاله ندارد.

      موفق باشید.

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

    ۷ تکنیک پیشرفته کدنویسی برای FPGA

    >