آموزش FPGA : قسمت هفدهم
ما در قسمت دوازدهم تا قسمت شانزدهم از مجموعه آموزشی FPGA حافظههای تکبیتی یا همان فلیپفلاپها را به طور کامل مورد بررسی قرار دادیم و هر آنچه نیاز بود، یا بهتر است بگوییم هر آنچه در رابطه با فلیپفلاپها وجود داشت را به طور کامل بررسی کردیم و همهی پارامترهای یک حافظه تک بیتی را با همدیگر شناختیم. اکنون در این قسمت قصد داریم که با استفاده از همان فلیپفلاپهایی که با همدیگر شناختیم، حافظههای چند بیتی که اصطلاحا به آنها رجیستر یا ثبات میگویند را توصیف کنیم. شاید دقیقا ندانید که رجیستر چیست یا به چه منظور از آنها استفاده میشود، پس بهتر است توضیحی کوتاه و مختصر در این رابطه داشته باشیم، سپس با استفاده از زبان VHDL یک رجیستر را توصیف کنیم.
Register (ثبات)
معمولا از رجیسترها به عنوان حافظههای چند بیتی یاد میشود و در بعضی از منابع فارسی با نام ثبات نیز شناخته میشوند. رجیسترها میتوانند مقادیر منطقی را در خود ذخیره کنند، این مقادیر منطقی میتوانند شامل داده یا اطلاعات، آدرس، شمارنده و … باشند. به احتمال زیاد اسم رجیسترها را بیشتر در پردازندهها شنیده باشید، در پردازندهها رجیسترها از قبل ساخته شدند و ما فقط با توجه به عملکرد مورد نطرمان این رجیسترها را مقدار دهی یا تنظیم میکنیم. یکی از پارامترهایی که باعث تمایز پردازندهها میشود، چند بیتی بودن رجیسترهای آنهاست، به عنوان مثال پردازندههای AVR دارای رجیسترهای 8 بیتی و پردازندههای ARM دارای رجیسترهای 32 بیتی هستند. اما در FPGAها رجیسترها از قبل وجود ندارند (اگرچه در FPGAها، شیفترجیسترها میتوانند جز منابع اختصاصی باشند و از قبل به صورت آماده وجود داشته باشند. در قسمتهای بعدی در رابطه با این موضوع صحبت خواهیم کرد). در ادامه ما با استفاده از فلیپفلاپها و در کنار هم گذاشتن آنها رجیسترهای 8 بیتی را توصیف خواهیم کرد. برای اینکه بهتر درک کنید رجیسترها چگونه با استفاده از فلیپفلاپها ساخته میشوند، ابتدا به تصویر زیر دقت کنید تا در ادامه کد VHDL آن را بنویسیم.
همانطور که در تصویر بالا مشاهده میکنید برای ساختن رجیستر، چندین فلیپفلاپ را در کنار هم قرار میدهیم به نحوی که کلاک آنها مشترک است و با تغییرات کلاک مقادیر این فلیپفلاپها همزمان تغییر میکنند، اما ورودی هر فلیپفلاپ به صورت جداگانه با تغییرات کلاک به هر فلیپفلاپ اعمال میشود. بدین نحو با استفاده از فلیپفلاپها میتوانیم رجیسترهای موردنظر خود را بسازیم. در ادامه چون میخواهیم یک رجیستر 8 بیتی را توصیف کنیم، پس باید 8 تا از این فلیپفلاپها را در کنار هم قرار دهیم و سپس به نحوی که گفته شد سیمکشی آنها را انجام دهیم.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity Register_8bit is Port ( D : in unsigned (7 downto 0); Clock : in STD_LOGIC; Reset : in STD_LOGIC; Q : out unsigned (7 downto 0) ); end Register_8bit; architecture Behavioral of Register_8bit is begin process(clock) begin if rising_edge (clock) then Q <= D; if (Reset = '1') then Q <= (others => '0'); end if; end if; end process; end Behavioral;
حال شاید از خود بپرسید چرا تنها با یک ارجاع ساده توانستیم یک رجیستر 8 بیتی را توصیف کنیم؟ اگر به خاطر داشته باشید قبلا گفته بودیم که اگر به سیگنالی ارجاع داده شود بسته به اینکه آن ارجاع کجا باشد، آن سیگنال میتواند تبدیل به سیم یا رجیستر شود. اگر به سیگنالی در محیط Concurrent ارجاع داده شود آن سیگنال تبدیل به سیم ولی اگر درون process به آن ارجاع داده شود آن سیگنال تبدیل به رجیستر میشود. چون در کد بالا ما درون process و در زیر شرط بالاروندهی کلاک به سیگنال Q ارجاع دادیم، این سیگنال تبدیل به رجیستر شده است (توجه کنید که در اینجا ما بین سیگنال و پورت تفاوتی قائل نمیشویم). بهتر است برای درک هرچه بهتر موضوع شماتیک مدار پیادهسازی شده در FPGA را نیز مشاهده کنیم. تصویر بالا کمی واضح نیست برای بهتر دیدن مدار پیادهسازی شده کمی روی آن زوم میکنیم تا به تصویر زیر برسیم.
مدار پیادهسازی شده علاوه بر فلیپفلاپها دارای بافرهای ورودی-خروجی، بافر کلاک و بخش ریست نیز میباشد، در مورد هر کدام از این بافرها بعدا صحبت خواهیم کرد. در مدار بالا همانطور که مشاهده میکنید کلاک فلیپفلاپها همزمان یا سنکرون میباشد و این موضوع باعث میشود که مقادیر فلیپفلاپها همزمان با یکدیگر تغییر کنند. از سمتی دیگر ورودیها پس از گذر از بافرهای ورودی به طور جداگانه به ورودی هر فلیپفلاپ اعمال میشوند. شما تنها با تغییر دادن عرض بیت D و Q میتوانید تعداد بیتهای رجیستر پیادهسازی خود را تغییر دهید، اما راهی بهتر برای این کار وجود دارد که ما از این به بعد همیشه از این تکنیک استفاده میکنیم. در ادامه با این روش آشنا خواهیم شد. خاصیتی وجود دارد به اسم Generic که ما با استفاده از آن میتوانیم یک پارامتر تعریف کنیم و به آن عددی را نسبت بدهیم و در قسمتهای مختلف کد از آن استفاده کنیم و هرگاه قرار بود قسمتهای مختلف کد تغییر کند به جای اینکه قسمتهای مختلف کد را تغییر بدهیم فقط همان پارامتر را تغییر میدهیم. در ادامه میخواهیم یک رجیستر 32 بیتی را با استفاده از خاصیت Generic توصیف کنیم برای این منظور باید تکه کد زیر را به کد قبلی اضافه کنیم و عرض بیت ورودی و خروجی را با استفاده از این پارامتر تعریف کنیم.
generic ( Number_of_bits: integer := 32 );
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity Register_32bit is generic ( Number_of_bits: integer := 32 ); Port ( D : in unsigned (Number_of_bits - 1 downto 0); Clock : in STD_LOGIC; Reset : in STD_LOGIC; Q : out unsigned (Number_of_bits - 1 downto 0) ); end Register_32bit; architecture Behavioral of Register_32bit is begin process(clock) begin if rising_edge (clock) then Q <= D; if (Reset = '1') then Q <= (others => '0'); end if; end if; end process; end Behavioral;