ما در قسمت دوازدهم تا قسمت شانزدهم از مجموعه آموزشی FPGA حافظههای تکبیتی یا همان فلیپفلاپها را بهطورکامل موردبررسی قرار دادیم و هر آنچه نیاز بود، یا بهتر است بگوییم هر آنچه در رابطه با فلیپفلاپها وجود داشت را بهطورکامل بررسیکردیم و همهی پارامترهای یک حافظه تک بیتی را با همدیگر شناختیم. اکنون در این قسمت قصد داریم که بااستفادهاز همان فلیپفلاپهایی که با همدیگر شناختیم، حافظههای چندبیتی که اصطلاحا به آنها رجیستر یا ثبات میگویند را توصیف کنیم. شاید دقیقا ندانید که رجیستر چیست یا به چه منظور از آنها استفاده میشود، پس بهتر است توضیحی کوتاه و مختصر در این رابطه داشتهباشیم، سپس بااستفادهاز زبان VHDL یک رجیستر را توصیفکنیم.
Register (ثبات)
معمولا از رجیسترها بهعنوان حافظههای چندبیتی یاد میشود و در بعضیاز منابع فارسی با نام ثبات نیز شناخته میشوند. رجیسترها میتوانند مقادیر منطقی را در خود ذخیره کنند، این مقادیر منطقی میتوانند شامل داده یا اطلاعات، آدرس، شمارنده و… باشند. به احتمال زیاد اسم رجیسترها را بیشتر در پردازندهها شنیده باشید، در پردازندهها رجیسترها از قبل ساختهشدند و ما فقط باتوجهبه عملکرد موردنطرمان این رجیسترها را مقداردهی یا تنظیم میکنیم. یکیاز پارامترهایی که باعث تمایز پردازندهها میشود، چندبیتی بودن رجیسترهای آنهاست، بهعنوانمثال پردازندههای AVR دارای رجیسترهای ۸بیتی و پردازندههای ARM دارای رجیسترهای ۳۲بیتی هستند. اما در FPGAها رجیسترها از قبل وجود ندارند (اگرچه در FPGAها، شیفترجیسترها میتوانند جز منابع اختصاصی باشند و از قبل بهصورت آماده وجود داشتهباشند. در قسمتهای بعدی در رابطه با این موضوع صحبتخواهیمکرد). در ادامه ما بااستفادهاز فلیپفلاپها و درکنارهم گذاشتن آنها رجیسترهای ۸بیتی را توصیفخواهیمکرد. برای اینکه بهتر درککنید رجیسترها چگونه بااستفادهاز فلیپفلاپها ساخته میشوند، ابتدا به تصویر زیر دقت کنید تا در ادامه کد VHDL آنرا بنویسیم.
همانطورکه در تصویربالا مشاهده میکنید برای ساختن رجیستر، چندین فلیپفلاپ را در کنارهم قرار میدهیم بهنحوی که کلاک آنها مشترک است و با تغییرات کلاک مقادیر این فلیپفلاپها همزمان تغییر میکنند، اما ورودی هر فلیپفلاپ بهصورت جداگانه با تغییرات کلاک به هر فلیپفلاپ اعمال میشود. بدیننحو بااستفادهاز فلیپفلاپها میتوانیم رجیسترهای موردنظر خود را بسازیم. در ادامه چون میخواهیم یک رجیستر ۸بیتی را توصیفکنیم، پس باید ۸تا از این فلیپفلاپها را در کنارهم قرار دهیم و سپس بهنحوی که گفتهشد سیمکشی آنها را انجامدهیم.
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;
حال شاید از خود بپرسید چرا تنها با یک ارجاع ساده توانستیم یک رجیستر ۸بیتی را توصیفکنیم؟ اگر بهخاطر داشتهباشید قبلا گفتهبودیم که اگر به سیگنالی ارجاعدادهشود بسته به اینکه آن ارجاع کجا باشد، آن سیگنال میتواند تبدیل به سیم یا رجیستر شود. اگر به سیگنالی در محیط Concurrent ارجاع داده شود آن سیگنال تبدیل به سیم ولی اگر درون process به آن ارجاع داده شود آن سیگنال تبدیل به رجیستر میشود. چون در کد بالا ما درون process و در زیر شرط بالاروندهی کلاک به سیگنال Q ارجاع دادیم، این سیگنال تبدیل به رجیسترشدهاست(توجهکنید که در اینجا ما بین سیگنال و پورت تفاوتی قائل نمیشویم). بهتر است برای درک هرچه بهتر موضوع شماتیک مدار پیادهسازی شده در FPGA را نیز مشاهده کنیم.
تصویربالا کمی واضح نیست برای بهتر دیدن مدار پیادهسازی شده کمی روی آن زوم میکنیم تا به تصویر زیر برسیم.
مدار پیادهسازی شده علاوهبر فلیپفلاپها دارای بافرهای ورودی-خروجی، بافر کلاک و بخش ریست نیز میباشد، درمورد هرکدام از این بافرها بعدا صحبتخواهیمکرد. در مداربالا همانطورکه مشاهده میکنید کلاک فلیپفلاپها همزمان یا سنکرون میباشد و این موضوع باعث میشود که مقادیر فلیپفلاپها همزمان با یکدیگر تغییر کنند. از سمتی دیگر ورودیها پساز گذر از بافرهای ورودی بهطورجداگانه به ورودی هر فلیپفلاپ اعمال میشوند. شما تنها با تغییردادن عرض بیت D و Q میتوانید تعداد بیتهای رجیستر پیادهسازی خود را تغییر دهید، اما راهی بهتر برای این کار وجود دارد که ما از این به بعد همیشه از این تکنیک استفاده میکنیم. در ادامه با این روش آشناخواهیمشد. خاصیتی وجود دارد به اسم Generic که ما بااستفادهاز آن میتوانیم یک پارامتر تعریفکنیم و به آن عددی را نسبتبدهیم و در قسمتهای مختلف کد از آن استفادهکنیم و هرگاه قرار بود قسمتهای مختلف کد تغییر کند بهجایاینکه قسمتهای مختلف کد را تغییر بدهیم فقط همان پارامتر را تغییر میدهیم. در ادامه میخواهیم یک رجیستر ۳۲بیتی را بااستفادهاز خاصیت 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;
حال هرموقع خواستید عرض بیت رجیسترتان را تغییر دهید، فقط کافیاست که تنها پارامتر Number_of_bits را تغییردهید تا به کل کد اعمال شود. در کدهای بزرگ این موضوع میتواند در توسعهی کد بسیار سودمند و مفید باشد. نکتهی دیگر اینکه چون ریست در انتهای کد نوشتهشدهاست اولویت با آن است و اگر شرط آن برقرار باشد مقدار رجیستر ۰ منطقی خواهد شد.
امیدوارم که بهخوبی این موضوع ساده را فراگرفتهباشید. در قسمت هجدهم به توصیف شیفت رجیستر خواهیم پرداخت.
منبع: سیسوگ