24 آذر, 1399

حلقه for در زبان VHDL

حتماً تاکنون به دفعات از حلقه for در برنامه‌نویسی استفاده کرده‌اید.

احتمالاً، کمتر برنامه‌ای را سراغ دارید که آن را بدون استفاده از حلقه for نوشته باشید.

اما حلقه for در زبان‌های توصیف سخت‌افزاری، همچون زبان VHDL، به چه صورت عمل می‌کند؟

آیا نحوه عملکرد حلقه for در زبان VHDL همانند زبان‌های برنامه‌نویسی است؟

در این مقاله، با نحوه عملکرد حلقه for در زبان VHDL آشنا می‌شوید؛ همچنین، به‌کمک حلقه for، یک شیفت‌رجیستر و یک خط تاخیر (delay line) که هر دو از پرکاربردترین بلوک‌ها در مدارات دیجیتال هستند را پیاده‌سازی خواهیم کرد.

More...


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

نحوه عملکرد حلقه for در زبان‌های برنامه‌نویسی

نمونه‌ای از ساختار حلقه for در زبان‌های برنامه‌نویسی را در زیر مشاهده می‌کنید؛ این کد به زبان C نوشته شده است:

for (i = 0; i < 10; i++) { 
    A = A + B*i; 
}

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

دستور یا فرمولی که درون حلقه for کد بالا می‌بینید، یک ضرب‌کننده به‌همراه یک جمع‌کننده است. به‌عبارت دیگر، ما یک Multiplier & Accumulator یا یک بلوک MAC داریم.

در واقع، ما در این فرمول، یک Accumulator به نام A تولید کرده‌ایم که در هر تکرار حلقه for، مقدار قبلی A را با حاصل‌ضرب رجیستر B در مقدار i جمع می‌کند و دوباره به رجیستر A منتقل می‌کند؛ در هر تکرار حلقه، مقدار A مدام بزرگ و بزرگ‌تر می‌شود.

نحوه عملکرد حلقه for در زبان توصیف سخت‌افزاری VHDL

در زبان VHDL نیز ساختاری به‌نام حلقه for وجود دارد؛ اما کاربرد آن با آنچه در کد بالا دیدیم کاملاً متفاوت است.

حلقه for در زبان VHDL، یک قابلیت برای کاهش حجم کد است.

برای مثال، در کد زیر می‌توانید نمونه‌ای از ساختار حلقه for در زبان VHDL را ببینید:

for I in 0 to 9 loop 
     B(I) <= A(I); 
end loop;

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

در واقع، این کد معادل این است که ارجاع A به B به تعداد ۱۰ بار کپی و در کد جاگذاری‌ شده باشد. در هر بار نیز مقدار اندیس‌ها، یک واحد اضافه شده باشد.

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

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


اکنون، مثال دیگری در این زمینه مطرح می‌کنم؛ این مثال را با دقت دنبال کنید تا کاربرد حلقه for در زبان VHDL برای شما شفاف‌تر شود.

فرض کنید در بخشی از کدتان ارجاع‌هایی مانند ارجاع‌های کد زیر داشته باشید:

B(2) <= A(0); 
B(3) <= A(1); 
B(4) <= A(2); 
B(5) <= A(3);

در واقع، هر بیت از سیگنال A، به بیتی با دو اندیس بزرگ‌تر در سیگنال B منتقل شده است.

آیا می‌توانید در کد بالا، یک الگوی تکراری مشخص پیدا کنید؟

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

اندیس‌ها نیز با یک الگوی مشخص تغییر می‌کنند؛ در هر خط، اندیس سیگنال B، دو واحد کوچکتر از اندیس سیگنال A است.

بنابراین، توانستیم یک الگوی تکراری در این کد پیدا کنیم.

پس، می‌توانیم این کد را به‌صورت ساده شده زیر و به‌کمک حلقه for بازنویسی کنیم:

for I in 0 to 3 loop 
     B(I+2) <= A(I); 
end loop;

اکنون اگر مقادیر I را از صفر تا سه جاگذاری کنیم، دقیقاً کدی معادل با کد قبلی خواهیم داشت.

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

فرض کنید به‌جای داشتن چهار ارجاع، چهل ارجاع‌ در کد داشتیم. در این صورت، حتماً می‌توانید حدس بزنید که استفاده از حلقه for چقدر کد ما را کوتاه‌تر و خواناتر می‌‌کرد.

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

پیاده‌سازی یک شیفت‌رجیستر به‌کمک زبان VHDL

حال به‌سراغ نوشتن کد VHDL توصیف‌کننده یک شیفت‌رجیستر با استفاده از حلقه for برویم.

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

ابتدا نحوه عملکرد شیفت‌رجیستر را یادآوری می‌کنم و سپس به پیاده‌سازی یک شیفت‌رجیستر به‌کمک ساختار حلقه for در زبان VHDL می‌پردازم.

انواع شیفت‌رجیستر

به‌طور کلی، شیفت‌رجیستر‌ها دو نوع هستند:

  • شیفت‌رجیستر خطی
  • شیفت‌رجیستر چرخشی
نمایش نحوه‌ی عملکرد شیفت‌رجیستر خطی و شیفت‌رجیستر چرخشی

نحوه عملکرد شیفت‌رجیستر خطی و شیفت‌رجیستر چرخشی


در این شکل، یک شیفت‌رجیستر هفت‌بیتی را مشاهده می‌کنید که عمل شیفت به سمت راست را انجام می‌دهد. من خانه‌های این شیفت‌رجیستر را از ۰ تا ۶ نام‌گذاری کرده‌­ام.

نحوه عملکرد شیفت‌رجیستر خطی یا Linear Shift Register به این ترتیب است که در هر کلاک، یک ورودی تک‌بیتی به شیفت‌رجیستر وارد می‌شود.

این ورودی تک‌بیتی به پرارزش‌ترین خانه شیفت‌رجیستر (در این مثال، خانه­ شماره ۶) وارد می‌شود. هم‌زمان، مقدار موجود در هر خانه، به خانه­ سمت راست آن منتقل می‌شود و نهایتاً مقدار موجود در خانه­ ۰ از بین می‌رود.

اما نحوه عملکرد شیفت‌رجیستر چرخشی یا Circular Shift Register به چه صورت است؟

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

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

معمولاً یک ماجول شیفت‌رجیستر دارای امکانات متنوعی است؛ در شکل زیر، یک ماجول شیفت‌رجیستر را مشاهده می‌کنید:

نمایش بلوک دیاگرام یک ماجول شیفت‌رجیستر که قرار است آن را به‌کمک حلقه for در زبان vhdl توصیف کنیم

بلوک دیاگرام یک ماجول شیفت‌رجیستر

پورت‌های ورودی و خروجی این شیفت‌رجیستر به‌صورت زیر است:

  • ورودی سریال: با هر کلاک، یک بیت از طریق این ورودی سریال به شیفت‌رجیستر وارد می‌شود.
  • ورودی موازی: کار این ورودی این است که می‌توانید در یک کلاک، کل محتوای شیفت‌رجیستر را با مقدار دلخواه، جایگزین کنید.
  • خروجی موازی: به‌کمک این خروجی موازی می‌توانیم محتوای کامل شیفت‌رجیستر را ببینیم و از آن استفاده کنیم.
  • سیگنال Reset: هرگاه سیگنال Reset فعال باشد، محتوای شیفت‌رجیستر پاک می‌شود (صفر می‌شود).

معمولاً ورودی موازی به‌همراه یک ورودی کنترلی به نام «Load» عمل می‌کند. ورودی کنترلی Load، دستور جایگزینی را می‌دهد.

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

توصیف کاملاً generic یک شیفت‌رجیستر به‌کمک حلقه for در زبان VHDL

کد زیر، توصیف یک شیفت‌‌رجیستر است که با استفاده از ساختار حلقه for، کاملاً generic شده است:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;

entity Example_08_Shift_Register_For_Loop is
	
	generic	(
					Reg_Width	: integer	:=	16
				);

	port 
		(
			Input		:	in	std_logic;
			Clock		:	in	std_logic;
			Parallel_Input	:	in	unsigned	(Reg_Width-1 downto 0);
			Load		:	in	std_logic;
			Reset		:	in		std_logic;
			Output		:	out	unsigned	(Reg_Width-1 downto 0)
		);
end Example_08_Shift_Register_For_Loop;

architecture Behavioral of Example_08_Shift_Register_For_Loop is
	
	signal	Shift_Content	:unsigned	(Reg_Width-1 downto 0)	:=(others=>'0');

begin

	Output		<=		Shift_Content;
	
	process(Clock)
	begin
	
		if rising_edge(Clock) then
		
			Shift_Content(Reg_Width-1)	<=	input;
			
			for i in 0 to Reg_Width-2 loop
				Shift_Content(i)	<=	Shift_Content(i+1);
			end loop;
			
			if (Load = '1') then
				Shift_Content	<=	Parallel_Input;
			end if;

			if (Reset = '1') then
				Shift_Content		<=		(others=>'0');
			end if;
		
		end if;
	
	end process;

end Behavioral;

ابتدا در بخش Entity، یک generic تعریف کرده‌ام؛ نام این generic را Reg_Width و نوع آن را integer قرار داده‌ام.

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

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

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

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

سپس، پورت Clock و Parallel Input را داریم؛ همان‌طور که پیش از این توضیح دادم، این پورت به ما کمک می‌کند که در یک کلاک، محتوای شیفت‌رجیستر را به مقدار دلخواه تغییر دهیم.

سپس، پورت‌های Reset و Output را داریم؛ همان‌طور که قبلاً اشاره کردم، می‌توانیم در پورت Output تمام محتوای شیفت‌رجیستر را ببینیم که این محتوا، کلاک به کلاک جابه‌جا می‌شود.

همان طور که در خط‌های ۱۵ و ۱۸ از قسمت entity مشاهده می‌کنید، عرض بیت پورت‌های Parallel_Input و Output که باید برابر با عرض شیفت‌رجیستر باشند را به‌صورت generic تعریف کرده‌ام.

سیگنال داخلی Shift_Content نیز باید هم‌عرض شیفت‌رجیستر باشد؛ بنابراین، همان‌طور که در خط ۲۴ از کد مشاهده می‌کنید، آن را به‌صورت generic تعریف کرده‌ام.

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

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

در خط ۳۵، بیت ورودی را به سنگین‌ترین بیت سیگنال Shift_Content ارجاع داده‌ام.

سپس، در حلقه for خط ۳۷، محتوای درون سیگنال Shift_Content را به راست شیفت داده‌ام.

بازه تغییرات اندیس حلقه از صفر تا Reg_Width-2 تعیین شده است.

تغییر بازه اندیس حلقه، به‌معنای تغییر تعداد سطرهایی است که ساختار حلقه for تولید می‌کند.

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

پیاده‌سازی شیفت‌رجیستر به کمک عملگر Concatenation

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

اما در عمل، برای توصیف یک شیفت‌رجیستر صرفاً نیاز به یک خط کدنویسی دارید.

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

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

فرض کنید نام سیگنالی که قرار است عمل شیفت روی آن انجام شود، Sh و ورودی سریال مدار ما سیگنال Input باشد، می‌توانید یک شیفت‌رجیستر خطی به سمت راست را به‌صورت زیر توصیف و پیاده‌سازی کنید:

Sh <= Input & Sh (7 downto 1);

احتمالاً می‌توانید حدس بزنید که برای پیاده‌سازی شیفت‌رجیستر چرخشی به سمت راست، کد VHDL باید به چه صورت باشد؛ کد زیر، توصیف کننده این شیفت‌رجیستر است:

Sh <= Sh (0) & Sh (7 downto 1);

کد شیفت‌رجیستر چرخشی به سمت راست همانند کد شیفت خطی به سمت راست است، با این تفاوت که به‌جای ورودی Input، بیت Sh (0) قرار می‌گیرد.

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

پیاده‌سازی خط تاخیر یا delay line به‌کمک حلقه for در زبان VHDL

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

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

نمایش شمایی از یک خط تاخیر که آن را با حلقه for در زبان VHDL توصیف خواهیم کرد.

شمایی از یک خط تاخیر

در پیاده‌سازی دیجیتال، خط تاخیر به این صورت پیاده‌سازی می‌شود که مطابق شکل زیر چند رجیستر را پشت سر هم قرار می‌دهیم.

نمایش پیاده‌سازی دیجیتال یک خط تاخیر، به‌کمک چند رجیستر

پیاده‌سازی دیجیتال یک خط تاخیر، به‌کمک چند رجیستر

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

حال چگونه این خط تاخیر را به‌کمک زبان توصیف سخت‌افزاری VHDL پیاده‌سازی کنیم؟

برای این کار، باید پراسسی همانند زیر بنویسیم:

process(Clock)
begin
if rising_edge(Clock) then
B <= A;
C <= B;
D <= C;
...

پیاده‌سازی خط تاخیر به‌کمک آرایه

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

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

فرض کنید که بخواهیم یک خط تاخیر ۲۰تایی پیاده‌سازی کنیم. در این خط تاخیر، خروجی به‌اندازه ۲۰ کلاک از ورودی عقب‌تر است؛ در نتیجه برای پیاده‌سازی آن، به ۲۰ رجیستر نیاز داریم.

این خط تاخیر، نسبتاً طولانی است؛ بنابراین، از آرایه استفاده می‌کنیم.

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

type my_type  is array (0 to 19) of  unsigned(7 downto 0);

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

ابعاد این آرایه به این صورت است که تعداد ۲۰ خانه با اندیس‌‌های ۰ تا ۱۹ دارد؛ در هر خانه می‌توان یک داده هشت‌بیتی از نوع بدون‌ علامت قرار داد.

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

signal X : my_type;

بنابراین، سیگنال X، یک سیگنال آرایه‌‌ای است که دارای ۲۰ خانه هشت‌بیتی از نوع بدون‌ علامت است.

برای دسترسی به یکی از خانه‌‌های این آرایه، برای مثال، خانه شماره سه، باید عبارت زیر را بنویسید:

X(3)

در واقع، X(3) به سطر شماره سه آرایه اشاره می‌کند که خود، یک داده هشت‌بیتی از نوع بدون علامت است.

اکنون فرض کنید که در این آرایه، به بیت شماره پنج از خانه یا سطر شماره سه نیاز دارید. در زبان VHDL برای دسترسی به این بیت، باید عبارتی مانند زیر بنویسید:

X(3)(5)

نوع‌ داده آرایه، تحقق فیزیکی در دیجیتال و در FPGA ندارد.

در حقیقت، نوع آرایه یا array، صرفاً به ما کمک می‌کند که تعداد زیادی رجیستر را هم‌‌زمان با هم تعریف کنیم.

از طرف دیگر، این نوع‌ داده برای generic کردن و ساده‌‌سازی کد نیز مفید است.

نوع آرایه، یک سخت‌‌افزار جدید یا تحقق ویژه‌‌ای را در سخت‌‌افزار ایجاد نمی‌‌کند، بلکه فقط به generic کردن کد و ساده‌‌سازی آن کمک می‌کند.

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

کد زیر، توصیف کننده یک خط تاخیر به طول ۲۰ است:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;

entity Delay_Line is

	generic
			(
				Array_Width	:	integer	:=	8;
				Array_Length	:	integer	:=	20
			);

	port (
				Clock				:	in		std_logic;
				Data_In			:	in		unsigned	(Array_Width-1 downto 0);
				Data_Out		:	out		unsigned	(Array_Width-1 downto 0)
			);	

end Delay_Line;

architecture Behavioral of Delay_Line is
	
	signal	Data_In_Int		: unsigned(Array_Width-1 downto 0):= (others =>'0');
	signal	Data_Out_Int	: unsigned(Array_Width-1 downto 0):= (others =>'0');
	
	type Array_Type is array (0 to Array_Length-1) of unsigned (Array_Width-1 downto 0);
	signal	Array_Register : Array_Type :=(others =>(others => '0'));

begin

	Data_Out		<=		Data_Out_Int;
 
	process(Clock)
	begin
	
		if rising_edge(Clock) then
		
			Data_In_Int 			<= Data_In;
			Array_Register(0) <= Data_In_Int;
		
			for i in 0 to Array_Length-2 loop
        Array_Register(i+1)  <=  Array_Register(i);  
      end loop;
			
			Data_Out_Int		<= Array_Register(Array_Length-1);
			
		end if;
																											 
	end process;
																											 
end Behavioral;

همان‌طور که مشاهده می‌کنید، در خط نه و ۱۰ از کد، دو generic به‌نام‌های Array_Width و Array_Length تعریف کرده‌ام.

در خط ۲۶، به‌کمک این دو generic، یک نوع داده آرایه به‌نام Array_Type تعریف کرده‌ام. این آرایه دارای ۲۰ خانه هشت‌بیتی است.

در خط ۲۷، سیگنال Array_Register را از نوع Array_Type تعریف کردم.

طبق الگوی استاندارد کدنویسی، در خط‌های ۲۳ و ۲۴، سیگنال‌های Data_In_Int و Data_Out_Int را تعریف کرده‌ام تا ورودی و خروجی مدار را رجیستر کنم.

در خط‌های ۳۱ و ۳۸، به ترتیب، خروجی و ورودی مدار را رجیستر کرده‌ام.

در خط ۴۰، داده ورودی را به اولین بیت سیگنال Array_Register منتقل کرده‌ام.

سپس، به‌کمک حلقه for خط ۴۲، یک delay_line ساخته‌ام؛ به‌طوری که در هر کلاک، محتوای هر رجیستر، به رجیستر بعدی آن منتقل می‌شود.

در نهایت، در خط ۴۸، داده موجود در آخرین (۲۰ امین) رجیستر را به سیگنال داخلی Data_Out_Int منتقل کرده‌ام.

آنگاه، در خط ۳۱، محتوای این سیگنال را به خروجی مدار، یعنی، Data_Out منتقل کرده‌ام.

به این ترتیب، در این کد، به‌کمک نوع داده آرایه و ساختار حلقه for، به‌راحتی یک خط تاخیر ساختیم.

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

پیاده‌سازی خط تاخیر به‌کمک حافظه بلوکی

اگر یک خط تاخیر بسیار طولانی، مثلاً ۱۲۸تایی داشته باشید، می‌توانید آن را به‌کمک حافظه بلوکی در FPGA پیاده‌سازی کنید.

اما برای استفاده از این روش، یک شرط وجود دارد.

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

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

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

فرض کنید که بخواهیم یک خط تاخیر ۱۲۸تایی را پیاده‌سازی کنیم؛ همان‌طور که می‌دانید، در این خط تاخیر، خروجی به‌اندازه ۱۲۸ کلاک از ورودی عقب‌تر خواهد بود.

برای پیاده‌سازی این خط تاخیر به روش معمول، به ۱۲۸ رجیستر نیاز داریم. اما در این اینجا، با توجه به طولانی بودن خط تاخیر، می‌توانیم از حافظه بلوکی استفاده کنیم و در واقع در مصرف رجیسترها صرفه‌جویی کنیم.

برای این کار، نیاز به تعریف دو شمارنده داریم؛ یک شمارنده برای آدرس‌دهی خواندن از حافظه بلوکی و دیگری برای آدرس‌دهی نوشتن در آن؛ شمارنده‌هایی که دائماً از صفر تا ۱۲۸ را می‌شمارند.

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

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

نحوه پیاده‌سازی خط تاخیر به‌کمک یک حافظه بلوکی

مثلاً، مقدار اولیه آدرس خواندن را یک و مقدار اولیه آدرس نوشتن را صفر قرار می‌دهیم.

به این ترتیب، هر داده‌ای که در هر کلاک می‌خوانید همان داده‌ای است که ۱۲۸ کلاک قبل در حافظه بلوکی نوشته شده است و یک خط تاخیر ۱۲۸تایی ساخته می‌شود.

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

می‌توانید در پایین همین صفحه، عنوان مقالات مرتبط را ببینید و از آن‌ها نیز استفاده کنید.

در صورت داشتن هر گونه ابهام، سوال و یا نکته‌ای درباره حلقه for در زبان VHDL، در پایان همین پست و در قسمت نظرات، آن را با ما در میان بگذارید.

این مقاله، برگرفته از دوره طراحی دیجیتال با FPGA بود.

برای اطلاع از جزئیات این دوره، روی دکمه زیر کلیک کنید:

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

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

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

نویسنده: فاطمه مشاک

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

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

فایل قیود کاربر در نرم‌افزار ویوادو: تخصیص پین‌ها و اعمال قید زمانی
  • با عرض سلام و خدا قوت
    مطلب این مقاله نیز همچون سایر مقالات این سایت بسیار عاااالی و کاربردی بود.
    کمال تشکر را دارم

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

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

    >