در قسمت هجدهم و قسمت نوزدهم ازمجموعه آموزشی FPGA با شیفت رجیستر آشنا شدیم و شیفت رجیستر را با روشهای متفاوتی پیادهسازی کردیم. در این قسمت قصد داریم که ابتدا با عملگرها آشنا شویم و سپس به عملگرها و توابع در زبان VHDL بپردازیم.
عملگرها
خارج از دنیای دیجیتال ما در ریاضیات با عملگرها آشنا هستیم و بدون استفاده از عملگرها، یک سری عدد داریم که هیچ کاری با آنها نمیتوانیم انجام بدهیم. چهار عمل اصلی در ریاضیات شامل: جمع، تفریق، ضرب و تقسیم میباشد و ما با استفاده از همین عملگرها به ریاضیات معنا میدهیم و از ریاضیات در دنیای واقعی و کارهای خودمان استفاده میکنیم. در دنیای دیجیتال و کدنویسی که البته خود تا حدود زیادی برگرفته از ریاضیات است نیز ما همین کار را انجام میدهیم. در دنیای دیجیتال و کدنویسی هم بدون استفاده از عملگرها، تقریبا هیچ کاری قابل انجام نیست. و اگر عملگرها وجود نداشته باشند یک سری دادهی دیجیتال به صورت صفر و یک یا همان باینری داریم که هیچ کاری با آنها نمیتوانیم انجام بدهیم.
توابع
با تابع هم در دنیای ریاضیات به خوبی آشنا هستید و مفهوم آن را میدانید. در دیجیتال و کدنویسی هم تقریبا تعریف همان است و در جزئیات کمی تفاوت دارد. مثلا تابع میتواند یک یا چند پارامتر را به عنوان ورودی بگیرد و یک یا چند پارامتر دیگر را به عنوان خروجی به ما بدهد. کاری که توابع انجام میدهند کمی پیچیدهتر از عملگرهاست و در یک تابع ممکن است به تعداد دفعات زیادی از عملگرها استفاده کنیم. پس با توجه به همین پیچیدگی اگر یک عمگر بر روی یک سری داده اعمال شود، خروجیِ حاصل، نتیجه محاسبات سادهای بر روی ورودیهاست. اما اگر یک تابع بر روی یک سری داده اعمال شود، خروجیِ حاصل، نتیجه محاسبات بیشتری بر روی ورودیهاست. فرض کنید میخواهیم تابع سینوس (Sin) را در دیجیتال پیادهسازی کنیم، راههای مختلفی برای این پیادهسازی وجود دارد یکی از این راهها استفاده از بسط تیلور است. در بسط تیلور به تعداد دفعات زیادی از عمگرهای جمع و تفریق استفاده میشود (البته از عملگرهای دیگری نیز استفاده میشود). پس وقتی میخواهیم تابع سینوس را پیادهسازی کنیم به تعداد دفعات زیادی از عملگرها استفاده خواهیم کرد و به نسبت محاسبات زیادتری را نیز انجام میدهیم. اگر میبینید که تعاریف را کمی متاوتتر از آنچه که به عنوان معمول برای تعریف این مفاهیم وجود دارد، ارائه میدهیم برای این است که شما دقیقا با کاری که قرار است انجام شود به خوبی آشنا شوید نه صرفا آشنایی با یک تعریف کلیشهای. اجازه بدهید کم کم با عملگرها و توابع در زبان VHDL آشتا شویم.
عملگرها و توابع در زبان VHDL
همانطور که میدانید زبان VHDL یک زبان توصیف سختافزار و سطح پایین میباشد. سطح پایین به این معنا که نیاز است شما به خوبی با سختافزار، نحوهی پیادهسازی عملیات، تحلیل بیت به بیت دیتا، جبر بول و بسیاری از موارد دیگر به خوبی آشنا باشید. با همهی این تفاسیر بعضی از عملگرها و توابع در زبان VHDL از قبل برای ما وجود دارد و ما میتوانیم از آنها استفاده کنیم. مثلا عملگر جمع و ضرب از جمله عملگرهایی هستند که در زبان VHDL تعریف شده و شناخته شده هستند و نیازی نیست که بخواهیم آنها را از ابتدا خودمان تعریف کنیم. به عنوان مثال دیگر، عملیات تقسیم در زبان VHDL تعریف شده نیست و باید با استفاده از الگوریتمهایی مختلف و همچنین دانش مدار منطقی و جبر بول خودمان این عملیات را پیادهسازی کنیم. ناگفته نماند که در زبان VHDL برای تقسیم، IP وجود دارد و ما میتوانیم از این IP بدون درگیر شدن با عملیات پیادهسازی، استفاده کنیم. در ادامه با یک عملگر و یک تابع بسیار پرکاربرد از جمله عملگرها و توابع در زبان VHDL آشنا خواهیم شد.
عملگر Concatenation در زبان VHDL
همانطور که از معنای این عملگر پیداست، کار این عملگر چسباندن یا کنار هم قرار دادن دادهها یا سیگنالها است. ما با استفاده از علامت & میتوانیم دو سیگنال را در کنار هم قرار دهیم. فرض کنید که دو سیگنال هشت بیتی داریم و میخواهیم به نحوی آنها را کنار هم قرار دهیم. مثلا بیت سوم تا ششم یکی از سیگنالها را کنار بیت پنجم تا هفتم یکی دیگر از سیگنالها قرار بدهیم. ابتدا به کد زیر توجه کنید تا در ادامه توضیحات لازم را بیان کنیم.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use ieee.numeric_std_all; entity Concatenation is port ( Output_R : out unsigned(6 downto 0) ); end Concatenation; architecture Behavioral of configuration is signal A : unsigned (6 downto 0); signal B : unsigned (7 downto 0); signal C : unsigned (7 downto 0); begin A <= B(5 downto 2) & C(6 downto 4); Output_R <= A; end Behavioral;
همانطور که گفتیم دو سیگنال هشت بیتی داریم که میخواهیم در مجموع به نحوی این دو سیگنال را کنار هم قرار بدهیم که یک سیگنال هفت بیتی ساخته شود. پس ما در ابتدا دو سیگنال هشت بیتی و یک سیگنال هفت بیتی تعریف میکنیم. در ادامهی کد، چهار بیت از سیگنال B و سه بیت از سیگنال C را با استفاده از عملگر & در کنار هم قرار دادیم و به سیگنال A که هفت بیتی است ارجاع دادیم.
نکتهای که بسیار مهم است و باید حتما به آن توجه کرد این است که اولا باید نوع سیگنالها در هر دو طرف ارجاع یکسان باشد، دوما مجموع عرض بیت سیگنالهای سمت راست برابر با عرض بیت سیگنالهای سمت چپ باشد.
شما در یک ارجاع میتوانید چندین بار از عملگر & استفاده کنید و بیتهای مختلف چندین سیگنال را به هم بچسبانید. در ادامه بحث عملگرها و توابع در زبان VHDL به بحث جذاب توابع میپردازیم.
تابع resize در زبان VHDL
از کاربرد این تابع همین کافی که قوانین سختگیرانه زبان VHDL هرگز به شما اجازه نمیدهند که بخواهید از این تابع استفاده نکنید. جبر و دیکتاتوریهای قبل از این تابع سوتفاهم بود. حقیقتا زبان VHDL یک زبان بسیار حساس و دقیق میباشد و در انواع ارجاعات آن باید بیت به بیت دیتا و پارامترهای دیگر را بررسی کرد و ملاحظات لازم را در نظر گرفت تا کار به درستی صورت بگیرد. شاید بتوانید در یک پروژه کوچک از این تابع استفاده نکنید اما در یک پروژه بزرگ، مخصوصا اگر بخواهید پردازش سیگنال انجام بدهید، تابع resize جز لاینفک پروژه خواهد بود.
عملکرد تابع resize
بسته به اینکه سیگنال موردنظر باعلامت باشد یا بیعلامت، عملکرد این تابع میتواند متفاوت باشد. در ادامه به تشریح این دو حالت خواهیم پرداخت. اگر سیگنال بیعلامت باشد:
- اگر سیگنال موردنظر به عرض بیت بزرگتری resize شود، به سمت چپ آن به تعداد لازم، تا رسیدن عرض بیت به تعداد بیتهایی که ما در ورودی این تابع در نظر گرفتیم، صفر افزوده میشود.
- اگر سیگنال موردنظر به عرض بیت کوچکتری resize شود، از سمت چپ آن به تعداد لازم، تا رسیدن عرض بیت به تعداد بیتهایی که ما در ورودی این تابع در نظر گرفتیم، بیت برداشته میشود.
اگر سیگنال باعلامت باشد:
- اگر سیگنال موردنظر به عرض بیت بزرگتری resize شود، به سمت چپ آن به تعداد لازم، تا رسیدن عرض بیت به تعداد بیتهایی که ما در ورودی این تابع در نظر گرفتیم، بیت علامت افزوده میشود.
- اگر سیگنال موردنظر به عرض بیت کوچکتری resize شود، بیت علامت حفظ میشود و سپس از سمت چپ آن به تعداد لازم، تا رسیدن عرض بیت به تعداد بیتهایی که ما در ورودی این تابع در نظر گرفتیم، بیت برداشته میشود.
ابتدا به کد زیر توجه کنید:
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use ieee.numeric_std_all; entity Resize_F is port ( A_1 : out signed(11 downto 0); A_2 : out signed(3 downto 0); B_1 : out unsigned(11 downto 0); B_2 : out unsigned(3 downto 0) ); end Resize_F; architecture Behavioral of configuration is signal A : signed (7 downto 0) := "11110000"; signal B : unsigned (7 downto 0); := "11110000"; begin A_1 <= resize(A, 12); A_2 <= resize(A, 4); B_1 <= resize(B, 12); B_2 <= resize(B, 4); end Behavioral;
در کد بالا ما دو سیگنال باعلامت و بیعلامت با مقدار اولیه یکسان تعریف کردیم و سپس با استفاده از تابع resize، این سیگنالها را هم به عرض بیت بزرگتر و هم به عرض بیت کوچکتر resize کردیم و به خروجی ارجاع دادیم. حال میخواهیم ببینم که پس از resize کردن سیگنال، چه تغییراتی روی این سیگنال و به چه صورت رخ میدهد. برای اینکه به خوبی متوجه بشوید پس از resize شدن سیگنال چه اتفاقی روی آن رخ میدهد به تصویر زیر توجه کنید:
تصویر بالا به خوبی مطابقت دارد با توضیحاتی که در رابطه با resize شدن به عرض بیت بزرگتر بیان کردیم و نیاز به هیچ گونه توضیح اضافی ندارد. resize شدن به عرض بیت کوچکتر را به عنوان تمرین به عهده خودتان میگذاریم. تصویر بالا عملیات zero/sign extension را انجام میدهد، که میتوانید در این رابطه بیشتر تحقیق کنید. امیدوارم که مقالهی عملگرها و توابع در زبان VHDL نیز مانند سایر مقالات برای شما مفید واقع شود و از آن در پروژههای خو بهره ببرید. در قسمت بعدی کار بر روی برد و بحث عملی را شروع خواهیم کرد.
منبع : سیسوگ