Learn FPGA Season 2

کاربرگرامی
آخرین بروز رسانی: 21 شهریور 1403
بدون دیدگاه
3 دقیقه زمان مطالعه
fpga

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

برای این کار، توابع مدار تمام جمع کننده‌ی چهار بیتی را استخراج و سپس آن‌ها را به کد VHDL تبدیل می‌کنیم.

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

قبل از اینکه بخواهیم مدار تمام جمع کننده چند بیتی را طراحی کنیم باید مقدماتی را بیان کرد.

انواع پورت‌ها

پورت‌ها از دو جهت مورد بررسی قرار می‌گیرند:

  • مسیر یا جهت
  • نوع

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

  • In
  • Out
  • Inout

وقتی قرار باشد دیتا وارد FPGA بشود از حالت In و هنگام خروج دیتا از FPGA از حالت Out استفاده می‌کنیم. احتمالا حدس خواهید زد وقتی که بخواهیم به نحوی مجموعی از این دو حالت را داشته باشیم، از حالت Inout استفاده می‌کنیم. بحث مربوط به Inout کمی پیچیدگی دارد، و در قسمتی جداگانه به این موضوع خواهیم پرداخت.

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

  • std_logic
  • std_logic_vector
  • signed
  • unsigned
  • integer

در طول این مجموعه آموزشی با هر کدام از موارد بالا آشنا خواهیم شد.

سیگنال‌ها

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

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

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

تمام جمع‌کننده‌ی چهار بیتی

Figure5Parallel-Adder-4-bit-Ripple-Carry-Adder-Block-Diagram copy

یک تمام جمع‌کننده‌ی چهار بیتی همانند تصویر بالا، از چهار، تمام جمع‌کننده‌ی تک بیتی تشکیل می‌شود.

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

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

برای اینکه یک ماژول را به ماژول دیگر اضافه کنیم باید اقداماتی صورت گیرد، که در حین انجام پروژه به توضیح آن خواهیم پرداخت.

 

برای اینکه یک ماژول را به ماژول دیگر اضافه کنیم باید اقداماتی صورت گیرد، که در حین انجام پروژه به توضیح آن خواهیم پرداخت.

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

S = x ⊕ y ⊕ cin

(C = (x . y) + (x . cin) + (y . cin

 

ما توابع منطقی ذکر شده در بالا، که مربوط به یک، تمام جمع‌کننده‌ی تک بیتی می‌باشد را به کد VHDL تبدیل می‌کنیم و در یک زیرماژول قرار می‌دهیم.

سپس این زیرماژول را، چهار بار به ماژول اصلی اضافه خواهیم کرد.

 

همانطور که در کد زیر مشاهده می‌‎کنید، زیرماژول ما سه ورودی و دو خروجی دارد، که همه از نوع std_logic هستند. یعنی همه‌ی ورودی-خروجی‌ها تک‌ بیتی هستند.

				
					library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Sub_Module is

    Port ( 		
					x 		: in  	STD_LOGIC;
					y 		: in  	STD_LOGIC;
					Cin 	: in  	STD_LOGIC;
					Sum 	: out  	STD_LOGIC;
					Cout 	: out  	STD_LOGIC
			 );
			 
end Sub_Module;

architecture Behavioral of Sub_Module is

begin

Sum			<=		x xor y xor Cin;
Cout		<=		(x and y) or (x and Cin) or (y and Cin);



end Behavioral;
				
			

 

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

 

منطقا ماژول اصلی اونقدری عاقل نخواهد بود که بخواهد زیرماژول خود را تشخیص دهد.

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

ما برای این کار، قبل از  begin مربوط به architecture، زیرماژول را به ماژول اصلی، مانند کد زیر معرفی می‌کنیم (component port). پس از این کار، ماژول اصلی همه‌ی اختیارات را بر عهده خواهد گرفت و می‌گوید که چگونه می‌خواهد از این زیر ماژول استفاده کند.

و شما چگونگی استفاده از زیر ماژول را در کد زیر بعد از  begin مربوط به architecture مشاهده می‌کنید (port map). در مورد چگونگی استفاده، باید یک سر برگردید به عقب و نیم‌نگاهی به دیاگرام مدار تمام جمع‌کننده‌ی چهار بیتی، بیندازید و همزمان با کد VHDL مقایسه کنید. باور کنید ما هم برای نوشتن این کد، کاری جز این نکردیم.

 

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

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

				
					library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Top_Module is

    Port ( 
					x 		: in  	STD_LOGIC_VECTOR (3 downto 0);
					y 		: in  	STD_LOGIC_VECTOR (3 downto 0);
					Cin 	: in  	STD_LOGIC;
					Sum 	: out  	STD_LOGIC_VECTOR (3 downto 0);
					Cout 	: out  	STD_LOGIC
			 );
					
end Top_Module;

architecture Behavioral of Top_Module is



	signal	C_Int		:	std_logic_vector	(2 downto 0)			:= (others=>'0');

	component	Sub_Module
		port (
					x 		: 	in		std_logic;
					y	 	:	in 	std_logic;
					Cin    	:	in 	std_logic;
					Sum    	: 	out	std_logic;
					Cout	:	out	std_logic
				);
	end component;	

begin

	Add1:	Sub_Module
		port map 
					(
						x			=>		x(0),
						y			=>		y(0),
						Cin    		=>		Cin,
						Sum   		=>		Sum(0),
						Cout		=>		C_Int(0)		
					);

	Add2:	Sub_Module
		port map 
					(
						x			=>		x(1),
						y			=>		y(1),
						Cin   		=>		C_Int(0),
						Sum   		=>		Sum(1),
						Cout		=>		C_Int(1)
					);
				
	Add3:	Sub_Module
		port map 
					(
						x			=>		x(2),
						y			=>		y(2),
						Cin   		=>		C_Int(1),
						Sum   		=>		Sum(2),
						Cout		=>		C_Int(2)
					);

	Add4:	Sub_Module
		port map 
					(
						x			=>		x(3),
						y			=>		y(3),
						Cin   		=>		C_Int(2),
						Sum   		=>		Sum(3),
						Cout		=>		Cout						
					);	
					
end Behavioral;
				
			

 

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

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

 

تا این مرحله هنوز زیرماژول، به ماژول اصلی اضافه نشده است ولی با اقداماتی که ما انجام دادیم، قرار است که این اتفاق تنها با یک دابل کلیک صورت گیرد.

اما قبل از دابل کلیک کردن باید یک کار کوچک دیگر انجام بدهیم. چون ما اول زیرماژول، سپس ماژول اصلی را به پروژه اضافه کردیم، پروژه، زیرماژول را به عنوان ماژول اصلی می‌شناسد.

برای رفع این مشکل باید روی ماژول اصلی راست‌کلیک کرده و گزینه‌ی Set as Top Module را انتخاب کنیم تا علامت انحصاری تاپ‌ماژول مانند شکل زیر کنار همان ماژولی که به عنوان ماژول اصلی مدنظر ماست فعال شود.

befor copy

اکنون اول ماژول اصلی را با ماوس انتخاب (Select) می‌کنیم، سپس با دابل کلیک کردن روی گزینه Check Syntax، زیرماژول‌، مانند تصویر زیر به ماژول اصلی اضافه خواهد شد. پس از طی این مراحل، زیرماژول‌ها با کمی تورفتگی نسبت به ماژول اصلی قرار خواهند گرفت.

after copy

 

ما فقط یک زیرماژول داشتیم، پس چرا 4 زیرماژول به ماژول اصلی اضافه شده است؟

در جواب باید بگوییم که در واقع ما یک زیرماژول را چهار بار به ماژول اصلی اضافه کردیم.

احتمالا مزیت زیرماژول نوشتن را نیز متوجه شده باشید.

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

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

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

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

entity Full_Adder_4bit is
    Port ( 
					x 		: in  	signed (3 downto 0);
					y 		: in  	signed (3 downto 0);
					Sum 	: out  	signed (3 downto 0)
			  );
			  
end Full_Adder_4bit;

architecture Behavioral of Full_Adder_4bit is

begin

	Sum		<=		x + y;

end Behavioral;
				
			

 

در کد بالا ورودی-خروجی‌ها را از نوع علامتدار تعریف کردیم، و مطابق با این کار پکیج ieee.numeric_std.all را نیز اضافه کردیم.

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

چرا وقتی با همین یک خط کد توانستیم مدار را توصیف کنیم، اون همه سختی را به جان خریدیم و یه عالمه کد نوشتیم؟

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

 

بدون دیدگاه
اشتراک گذاری
اشتراک‌گذاری
با استفاده از روش‌های زیر می‌توانید این صفحه را با دوستان خود به اشتراک بگذارید.