مقدمه
برای شروع ابتدا لازم است یک یادآوری بسیار کوتاه از درس مدار منطقی داشته باشیم و نحوه نمایش اعداد باینری منفی را باهم مرور کنیم. در ریاضیات برای نمایش اعداد منفی کافی است از علامت پیشوندی -
پیش از نمایش عدد استفاده کنیم. اما در سیستمهای دیجیتال اعداد تنها با استفاده از دنبالهای از بیتها قابل نمایش هستند. سه روش کلی برای نمایش اعداد منفی در این سیستمها وجود دارد. که روش علامت اندازه، روش مکمل یک و روش مکمل دو نام دارند. در این آموزش از پایگاه دانش هگزالینکس قصد داریم شما را با شیوه نمایش اعداد علامت دار در FPGA آشنا کنیم. اگر علاقمند به این موضوع هستید تا انتها با ما همراه شوید.
روش علامت اندازه
در این روش برای نمایش اعداد از یک بیت اضافه استفاده میشود و این بیت اضافه به علامت عدد، اختصاص داده میشود. این بیت علامت بعد از با ارزشترین بیت در جایگاه MSB قرار داده میشود و مقدار آن برای اعداد مثبت صفر و برای اعداد منفی یک است. به عنوان مثال برای نمایش عدد مثبت و منفی ۹ داریم:
"01001" = +9, "11001" = -9
رِنج اعداد قابل نمایش در این روش به صورت زیر است و برای یک عدد ۵ بیتی مقادیر از منفی تا مثبت ۱۵ قابل نمایش هستند و دو مقدار برای عدد صفر داریم. در رابطه زیر N معرف تعداد بیت است.
-(2N-1-1) : 2N-1-1
همانطور که حتما حدس زدید یکی از معایب اصلی که در این شیوه نمایش، وجود دو مقدار "00000"
و "10000"
برای نمایش عدد صفر است. این شیوه نمایش به جز موارد بسیار خاص در FPGA مورد استفاده قرار نمیگیرد.
روش مکمل یک
نمایش مقدار مثبت در این روش کاملا مشابه روش اول است، برای نمایش مقدار یک عدد به صورت منفی کافی است تمامی بیتها را معکوس کنیم. در این نمایش بازهم با ارزشترین بیت علامت عدد را مشخص میکند. نمایش عدد ۹ به صورت منفی و مثبت در این روش به صورت زیر است.
"01001" = +9, "10110" = -9
رِنج اعداد قابل نمایش در این روش مشابه روش اول است و برای یک عدد ۵ بیتی مقادیر از منفی تا مثبت ۱۵ قابل نمایش هستند.
-(2N-1-1) : 2N-1-1
با توجه به اینکه با استفاده از N بیت می توان 2N عدد مثبت را نمایش داد، واضح است که با علامت دار شدن اعداد دامنه تغییرات اعداد نصف میشود. اما نکته اینحاست که بازهم برای نمایش عدد صفر مشکل داریم و دو مقدار "00000"
و "11111"
هر دو نمایانگر عدد صفر هستند. عملا این روش نیز در FPGA مورد استفاده قرار نمیگیرد.
روش مکمل دو
مشابه دو روش قبل برای نمایش اعداد مثبت بازهم به صورت کاملا مستقیم عمل میکنیم و از معادل باینری اعداد استفاده میکنیم. برای نمایش اعداد منفی راحترین کار کمک گرفتن از نمایش مکمل یک است. به این ترتیب که ابتدا همه بیتها را معکوس میکنیم و سپس یک واحد به آن اضافه میکنیم. البته روش ذهنی دیگری نیز وجود دارد که در آن از سمت راست عدد باینری شروع میکنیم و تمام اعداد بعد از اولین یک را معکوس میکنیم. نمایش عدد منفی و مثبت ۹ در این روش به صورت زیر است.
"01001" = +9, "10111" = -9
برخلاف دو شیوه نمایش قبلی دامنه اعداد قابل نمایش با استفاده از ۵ بیت از ۱۶- تا ۱۵ میباشد و برای نمایش عدد صفر تنها مقدار "00000"
قابل استفاده است.
-2N-1 : 2N-1-1
با استفاده از نمایش مکمل دو پیاده سازیِ عملگر تفریق بسیار ساده میشود و مدار جمع کننده میتواند برای محاسبه حاصل جمع دو عدد منفی یا یک عدد مثبت و یک عدد منفی مشابه جمع اعداد مثبت به کار گرفته شود. در این روش برای نمایش تمامی اعداد فقط یک مقدار یکتا وجود دارد. از این رو به صورت گسترده در پیادهسازیِ مدارات محاسباتی روی FPGA بکارگرفته میشود.
جایگزینی تفریق با جمع
نمایش مکمل دو شیوهای برای نمایش اعداد علامت دار در سیستمهای دیجیتال است. هدف اصلی آن جایگزین کردن عمل تفریق با جمع است. به این ترتیب امکان محاسبه تفریق و جمع با استفاده از یک مدار مشترک امکانپذیر میشود. این مسأله باعث کاهش تعداد گیتها، ابعاد مدار، توان مصرفی و در نهایت هزینه سیستم میشود. در حقیقت اگر مفهوم مکمل وجود نداشت، مجبور بودیم، از الگویی مشابه با آنچه به هنگام کار با اعداد دسیمال بکار میبریم، استفاده کنیم. در این حالت برای انجام عملیات جمع و تفریق به دو بلوک محاسباتی متفاوت نیاز داشتیم. علاوه بر این مجبور بودیم قبل از اعمال ورودیها به بلوک جمعکننده/تفریقکننده چند معیار مهم را نیز چک کنیم.
پیادهسازی عملیات جمع
در زبان VHDL برای استفاده از سیستم مکمل دو پیشنهاد میشود از کتابخانه ieee
و از پکیج numeric_std
استفاده شود. در این کتابخانه دو نوع signed
و unsigned
بر اساس نوع std_logic_vector
تعریف شدهاند و برای تعریف سیگنالهای علامت دار و بدون علامت بکار گرفته میشوند.
library ieee; use ieee.numeric_std.all;
کد HDL زیر یک جمع کننده ۸ بیتی علامت دار را نشان میدهد. به نحوه اختصاص مقادیر پورتها به سیگنالهای درونی ماژول توجه کنید. در این کد فرض بر این است که حاصل جمع دو ورودی ai
و bi
در یک رجیستر ۸ بیتی ذخیره میشود. عبارت signed
در خطوط ۱۷ و ۱۸ برای تغییر نوع std_logic_vector
به نوع signed
استفاده شده است. این تبدیل نوع در خط ۲۰ به شکل دیگری تکرار شده است.
-- Copyright 2019 Hexalinx.com library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity adder is generic (M : integer := 8); port (ai,bi : in std_logic_vector(M-1 downto 0); xo : out std_logic_vector(M-1 downto 0)); end; architecture arch of adder is signal a,b,x : signed(M-1 downto 0); begin a <= signed(ai); b <= signed(bi); x <= a + b; xo <= std_logic_vector(x); end architecture;
نکتهای که باید به آن دقت کرد، محاسبه رشد بیت در عملگر جمع است. حاصل جمع دو عدد ۸ بیتی در بدترین حالت یک بیت رشد خواهد داشت. این بیت اضافه در واقع همان بیت نقلی حاصل از عملیات جمع است. از این رو برای اطمینان از دستیابی به پاسخ صحیح در خروجی باید تاثیر این بیت اضافه در نظر گرفته شود. در کد فوق این بیت اضافه در نظر گرفته نشده است. برای اصلاح این مساله باید به صورت زیر عمل کنیم.
-- Copyright 2019 Hexalinx.com library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity adder is generic (M : integer := 8); port (ai,bi : in std_logic_vector(M-1 downto 0); xo : out std_logic_vector(M-1 downto 0)); end; architecture arch of adder is signal a,b : signed(M-1 downto 0); signal x : signed(M downto 0); begin a <= signed(ai); b <= signed(bi); x <= resize(a + b, M+1); xo <= std_logic_vector(x(M downto 1)); end architecture;
برای اینکه خروجی همچنان در ۸ بیت قابل نمایش باشد کمی از دقت حاصل جمع کاسته شده است و بیت کم ارزش آن کنار گذاشته شده است. تابع resize
در پکیج numeric_std
معرفی شده است و برای تغییر سایز سیگنال در زمان ارجاع استفاده میشود.
جمع بندی
برای محاسبات علامت دار در FPGA از شیوه نمایش مکمل دو استفاده میشود. پیشنهاد اکید میشود برای پیادهسازی الگوریتمهای ریاضی از دو نوع signed
و unsigned
در کدنویسیهای HDL استفاده شود. و در نهایت تاثیر بیت نقلی روی پاسخ خروجی فراموش نشود.
در صورتی که علاقمند هستید با شیوه نمایش اعداد اعشاری ممیز ثابت آشنا شوید. پیشنهاد میکنم آموزشهای تکمیلی هگزالینکس را مطالعه بفرمایید.
4 در مورد “نمایش اعداد علامت دار در FPGA”
در مکمل دو عدد 11110 مشخص کننئه ی چه عددیست مگه جواب باز صفر نیست؟
مجتبی عزیز عدد مد نظر شما ۱۸- است. (10010}
با سلام و تشکر بابت آموزش هاتون .سوال من اینه که چرا از resize استفاده کردین در حالی که سیگنال x یک بیت بیشتر از a وb هست .یعنی با اضافه کردن یک بیت به طول xآیا باز هم نیازه از resize استفاده بشه ؟
با سلام و احترام
ممنون از دقتی که به خرج دادید. به صورت کلی تعداد بیتهای متغیرهای سمت راست ارجاع (=>) و سمت چپ اون باید باهم برابر باشند. در غیر این صورت دو احتمال وجود داره. یا ابزار سنتز خطا میده و یا مقدار خروجی اشتباه میشه. البته در مواردی ممکنه پاسخ درست هم حاصل بشه ولی روش کار درست نیست.
با استفاده از تابع resize در (x <= resize(a + b, M+1 تعدا بیتهای سمت راست رو ۹ در نظر گرفتیم. یعنی پاسخ a+b رو ۹ بیتی کردیم و بعد به x ارجاع دادیم.