آیا میدانید برخلاف عمل جمع و ضرب، عملگری که بتواند تقسیم در FPGA را پیادهسازی کند وجود ندارد؟!
در عین حال، عمل تقسیم یکی از ملزومات مهم پیادهسازی الگوریتمهای پردازش سیگنال است!
More...
در این برنامه ویدئویی، شما را با نحوه پیادهسازی عملیات تقسیم در FPGA به کمک IPی تقسیم کننده آشنا میکنم.
برای آشنایی با نحوه استفاده از IPها در نرمافزار ISE این برنامه ویدئویی را ببینید…
به کمک IPی تقسیم کننده در نرمافزار ISE میتوانید عملیات تقسیم در FPGA را با توجه به نیازمندیهای پروژهتان پیادهسازی کنید.
مثلا میتوانید تقسیم کننده را با عرض بیتهای مختلف یا به صورت علامتدار پیادهسازی کنید.
همچنین میتوانید مشخص کنید که آیا نیاز به باقیمانده صحیح تقسیم دارید یا نیاز به بخش کسری خارج قسمت دارید.
در این برنامه، یک مثال کامل از نحوه استفاده از IPی تقسیم کننده هم مطرح کردم و آن را به کمک نرمافزار ISim شبیهسازی کردم.
برای آشنایی با نحوه شبیهسازی مدارات دیجیتال به کمک نرمافزار ISim این برنامه ویدئویی را ببینید…
ویدئو یا متن؟
محتوای این برنامه آموزشی، به دو صورت ویدئو و متن آماده شده است. اگر علاقمند به یادگیری این مطلب به صورت ویدئویی هستید، ویدئوی زیر را ببینید و اگر ترجیح میدهید آن را به صورت متن مطالعه کنید، ادامه این مطلب را بخوانید.
برای دانلود نسخه با کیفیت این ویدئو، روی دکمه زیر کلیک کنید:
در بحث پیادهسازی الگوریتمهای پردازش سیگنال با FPGA، یکی از موارد مهمی که نیاز است به آن تسلط کامل داشته باشید، آشنایی با نحوهی پیادهسازی چهار عمل اصلی ریاضی، یعنی جمع، تفریق، ضرب و تقسیم است.
از لحاظ پیادهسازی، عمل تقسیم نسبت به سه عمل دیگر، تفاوتهایی دارد که در این مقاله قصد دارم در مورد آن و نحوهی عملی پیادهسازی عملیات تقسیم در FPGA صحبت کنم.
تفاوت عملگر تقسیم با سایر عملگرهای اصلی
در زبان VHDL عملگر '+' برای جمع، '-' برای تفریق، '*' برای ضرب و '/' برای تقسیم قابل استفاده است.
اما عملگر تقسیم نسبت به عملگرهای دیگر کمی محدودتر است؛ وقتی شما در کد VHDL از عملگر مثبت، منفی یا ستاره استفاده میکنید، نرمافزار سنتز، مدار مناسبی برای عملیات جمع، تفریق و یا ضرب ایجاد میکند.
اما در زبان VHDL عملگر '/'، فقط برای پیادهسازی عملیات تقسیم یک رجیستر بر یک عدد ثابت قابل استفاده است که عدد ثابت حتما باید توانی از دو باشد. مثلاً شما میتوانید به کمک عملگر تقسیم، ریجستر A را بر یک عدد ثابت مانند 128یا 256 تقسیم کنید.
اما نمیتوانید به کمک این عملگر یک رجیستر را بر رجیستر دیگر تقسیم کنید؛ مثلاً نمیتوانید عبارت A=B/C (که در آن B و C هر دو رجیستر هستند) را در کدتان بنویسید. برای تقسیم یک رجیستر بر رجیستر دیگر، باید مدار ویژهای طراحی و پیادهسازی کنید.
روشهای پیادهسازی مدار تقسیمکننده
برای طراحی این مدار، یک روش این است که خودتان یک الگوریتم را شناسایی و سپس آن را به کمک زبان VHDL پیادهسازی کنید.
روش بهتر، استفاده از IP تقسیمکننده است. در نرمافزار ISE، یک IPی آماده برای پیادهسازی ماجول تقسیمکننده وجود دارد. این IP، دو ورودی و دو خروجی اصلی دارد؛ پورتهای ورودی Dividend و Divisor، برای مقسوم و مقسومعلیه، و پورتهای خروجی Quotient و Remainder، برای خارجقسمت و باقیمانده هستند. در شکل زیر میتوانید آنها را ملاحظه کنید:
برای این که دقیقاً متوجه شوید که این اسامی چگونه در تقسیم به کار میروند، من شکل زیر را آماده کردهام. در این شکل،میتوانید جایگاه مقسوم، مقسومعلیه، خارجقسمت و باقیمانده را ببینید.
اکنون اجازه دهید به کمک یک مثال عملی، انجام تنظیمات و نحوهی استفاده از IPی تقسیمکننده را بررسی کنیم.
استفاده از IPی تقسیمکننده
برای استفاده از IPی تقسیمکننده، ابتدا باید وارد نرم افزار ISE شوید و در بخش IPها، IPی تقسیمکننده را انتخاب کنید. سپس تنظیمات آن را بر مبنای نیازتان انجام دهید.
به شما پیشنهاد میکنم اگر با موضوع IPها آشنا نیستید، برنامه معرفی IPها در نرمافزار ISE را در سایت ملاحظه کنید و بعد از آن، مجددا به این برنامه مراجعه کنید.
برای اضافه کردن IP به پروژه، همانند شکل زیر در قسمت Hierarchy کلیکراست کنید. سپس گزینه New Source را انتخاب کنید.
اکنون گزینهی IP را انتخاب و در کادر نامگذاری، نامی برای IPتان تایپ کنید؛ برای مثال من نام آن را Div قرار دادهام. سپس روی دکمهی Next کلیک کنید.
از میان IPهایی که در پنچرهی باز شده وجود دارد، فولدر Math Function را باز کنید. سپس وارد فولدر Divider شوید و مطابق شکل زیر، روی IP Divider Generator کلیک کنید.
حال گزینهی Next را انتخاب و در آخر گزینهی Finish را کلیک کنید تا پنچرهی تنظیمات IP ظاهر شود.
تنظیمات IPی تقسیمکننده
در این پنجره شما میتوانید مطابق شکل زیر، IP را بر مبنای نیازتان تنظیم کنید.
مورد اول مربوط به انتخاب الگوریتمی است که برای این Divider یا تقسیمکننده به کار میرود. بر مبنای توصیهی دیتاشیت این IP، اگر عرض بیت ورودیهای تقسیمکننده کمتر از ۱۶ بیت باشد، باید از الگوریتم Radix2 استفاده کنیم و اگر عرض بیت ورودیهای آن، بزرگتر از ۱۶ بیت باشد، الگوریتم High Radix را انتخاب میکنیم.
در قسمت بعدی، عبارت Dividend & Quotient Width نوشته شده است که در آن میتوانید عرض بیت مقسوم و خارجقسمت را مشخص کنید. برای مثال من عدد هشت را انتخاب کردهام. در بخش Divisor Width میتوانید عرض بیت مقسومعلیه را مشخص کنید.
همچنین برای قسمت Remainder Type مطابق شکل زیر، دو انتخاب دارید؛
مورد اول، Remainder است که در صورت انتخاب، در پورت خروجی Remainder، حاصل باقیماندهی صحیح این تقسیم مشاهده خواهد شد؛ مثلاً در تقسیم عدد ۱۰ بر ۳، باقیمانده صحیح برابر با یک است؛ بنابراین در خروجی Remainder عدد یک دیده میشود.
در صورتی که گزینهی دوم، یعنی Fractional را انتخاب کنید، خروجی کاملاً متفاوت با حالت قبل و برابر با بخش کسری خارجقسمت خواهد بود؛ مثلاً در تقسیم ۱۰ بر ۳، همانطور که میدانید میتوان با اعشاری کردن خارجقسمت، تقسیم را ادامه داد؛ بنابراین خارجقسمت برابر با 3/3 میشود. (البته میتوان آن را تا ارقام اعشاری بیشتری نیز ادامه داد.) در حالت Fractional، در پورت خروجی Remainder، عدد 3/0 را خواهیم داشت. ما برای این مثال، گزینه Remainder را انتخاب میکنیم.
گزینهی بعدی Operand Sign است. در این قسمت میتوانید از بین دو گزینهی Signed و Unsigned یکی را انتخاب کنید. من گزینهی Signedرا انتخاب میکنم، چون میخواهم دو عدد علامتدار را بر هم تقسیم کنم.
گزینهی دیگر، Clocks per Division است که میتواند یکی از مقادیر یک، دو، چهار و هشت را داشته باشد.
اگر این گزینه برابر یک باشد، ماجول میتواند یک عملیات تقسیم را در هر کلاک انجام دهد.
اگر شما آن را برابر چهار قرار دهید، هر چهار کلاک یکبار، یک تقسیم انجام میشود؛ یعنی در کلاک اول یک تقسیم انجام میشود، سپس سه کلاک میگذرد و در این مدت نمیتوانید ورودی جدید به ماجول بدهید. پس از آن و در کلاک پنجم، دوباره یک تقسیم میتواند انجام شود. اگر شما این گزینه را روی مقادیر بالاتر تنظیم کنید، از نظر پیادهسازی در این تقسیمکننده تفاوت ایجاد میشود.
ما معمولا مقدار یک را برای این گزینه انتخاب میکنیم.
در پنچرهی تنظیمات، گزینههای کنترلی نیز وجود دارد که بر مبنای نیازتان میتوانید از آنها استفاده کنید.
برای مثال، گزینههای Clock Enable و Clear که به ترتیب با تیک زدن CE و SCLR فعال میشوند. در بخش شماتیک یا IP Symbol (همان قسمتی که بلوک تقسیمکننده را نشان میدهد) با فعال کردن هر ورودی کنترلی، پایهی متناظر با آن پررنگ میشود؛ این بدین معنی است که این پایه پیادهسازی خواهد شد. البته من معمولا این پایهها را فعال نمیکنم.
همانطور که گفته شد، میتوان پایههای فعال را در شماتیک مشاهده کرد؛ مثلا پایه RFD (مخفف Ready For Data)، یک پایهی خروجی تکبیتی است؛ هرگاه خروجی آن منطق ۱ باشد، شما اجازه اعمال ورودی جدید برای انجام عملیات تقسیم را دارید.
اگرClocks per Division را برابر یک قرار داده باشید، در هر کلاک اجازه دارید به ماجول تقسیمکننده ورودی جدید اعمال کنید؛ بنابراین پایهی RFD همیشه برابر 1 خواهد بود.
اما اگر Clocks per Division را برابر ۴ قرار دهید، پایه RFD، هر ۴ کلاک یکبار و بهمدت یک کلاک، ۱ است و شما دقیقاً در همان کلاک اجازه دارید که به این ماجول ورودی جدید اعمال کنید.
نکته آخر درمورد تنظیمات این است که همانند تمامی IPهای دیگر، یک گزینهی دیتاشیت در پایین پنجره وجود دارد؛ با کلیک بر آن، میتوانید دیتاشیت این ماجول را در قالب یک فایل PDF ببینید.
در این فایل، دربارهی تمامی تنظیمات ماجول، از جمله الگوریتمهای به کار رفته در آن و پایههای ورودی-خروجی توضیح داده شده است. شما میتوانید در صورت داشتن سوال یا ابهام دربارهی تنظیمات IP، دیتاشیت آن را مطالعه کنید.
پس از انجام تنظیمات، با فشردن دکمه Generate، این IP ساخته شده و به پروژهی شما اضافه میشود.
شبیهسازی IPی تقسیمکننده
برای تست و شبیهسازی IP، باید یک تاپ ماجول بسازیم و IP را در آن Instant کنیم.
من یک کد بسیار ساده به عنوان تاپ ماجول آماده کردهام که در واقع در آن چیزی به جز خود IP وجود ندارد.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity Example_02_IP_Core_Divider is Port ( Dividend_Top : in signed (7 downto 0); Divisor_Top : in signed (7 downto 0); Quotient_Top : out signed (7 downto 0); Remainder_Top : out signed (7 downto 0); Clock : in STD_LOGIC ); end Example_02_IP_Core_Divider; architecture Behavioral of Example_02_IP_Core_Divider is signal Quotient_Int : std_logic_vector(7 downto 0) := (others=>'0'); signal Fractional_Int : std_logic_vector(7 downto 0) := (others=>'0'); component Example_02_IP_Divider port ( clk: in std_logic; rfd: out std_logic; dividend: in std_logic_vector(7 downto 0); divisor: in std_logic_vector(7 downto 0); quotient: out std_logic_vector(7 downto 0); fractional: out std_logic_vector(7 downto 0)); end component; begin Quotient_Top <= signed(Quotient_Int); Remainder_Top <= signed(Fractional_Int); IP_Core_Divider_Inst : Example_02_IP_Divider port map ( clk => Clock, rfd => open, dividend => std_logic_vector(Dividend_Top), divisor => std_logic_vector(Divisor_Top), quotient => Quotient_Int, fractional => Fractional_Int ); end Behavioral;
در Entityی این تاپ ماجول، همان پورتهای ورودی-خروجی IP را تعریف میکنیم؛ بنابراین پورتهای Quotient ،Remainder ،Divisor ،Dividend و Clock را تعریف میکنیم.
اکنون باید IP تقسیمکننده را در تاپ ماجول Instant کنیم.
کافی است مطابق شکل زیر، روی IP کلیک کنید و گزینه Core Generator را از منوی پایین صفحه باز کنید.
حال روی گزینه View HDL Instantiation Template، دبلکلیک کنید.
در نتیجه، نمونه کد Instant کردن IP ساخته میشود. اکنون باید مطابق شکل زیر، بخش Component Declaration را از کد ساخته شده انتخاب کنید و آن را در تاپ ماجول و قبل از Begin Architecture، کپی کنیم.
حالا مطابق شکل زیر، از کد نمونه قسمت Instant کردن این IP را انتخاب و آن را در تاپ ماجول و بعد از Begin مربوط به بخش Architecture، کپی میکنیم.
تنها کار باقی مانده، عملیات Port Map کردن است.
نکتهای که باید در Port Map کردن به آن دقت کنید این است که ما برای شفافیت بیشتر کد و راحتی کار، تمام پورتهای برداری را به صورت signed یا unsigned تعریف میکنیم.
اما پورتهای برداری IPها به صورت پیشفرض، STD_Logic_Vector تعریف شدهاند.
تمامی پورتهایی که من برای پروژه تعریف کردهام، از نوع signed هستند (خط 6 تا 11)، اما پورتهای IPی تقسیمکننده همگی STD_Logic_Vector هستند.
بنابراین زمانی که میخواهیم IP را در کد Port Map کنیم، باید تبدیل تایپ انجام دهیم.
پورتهای Dividend و Divisor که ورودیهای IP هستند را در لحظهی Port Map کردن و به کمک عبارت ()STD_Logic_Vector تبدیل تایپ میکنیم (خط 39 و 40 از کد)؛
در واقع پورت Dividend (متعلق به تاپ ماجول)، از نوع signed است که به STD_Logic_Vector تبدیل میکنیم. سپس آن را به پورت ورودی Dividend (متعلق به IP)، اعمال میکنیم.
اما برای دو پورت خروجی quotient و fractional نمیتوانیم مانند پورتهای ورودی عمل کنیم.
برای پورتهای خروجی، روش کار به این صورت است که دو سیگنال میانی بهنامهای Quotient_Int و Fractional_Int را از نوع STD_Logic_Vector تعریف میکنیم و برای Port Map کردن به IP تقسیمکننده، از آنها (بهجای پورتهای خروجی Quotient و Fractional که تایپ آنها signed است) استفاده میکنیم.
در واقع، مقدار دو خروجی quotient و fractional (متعلق به IP) را به سیگنالهای Quotient_Int و Fractional_Int منتقل میکنیم.
همچنین همانطور که در کد میبینید، باید در محیط Concurrent، نوع دو سیگنال Quotient_Int و Fractional_Int را به signed تبدیل کرده و به خروجیهای تاپ ماجول که Quotient و Remainder هستند اعمال کنیم.
این روش را باید در کدهایی با نوع دادههای برداری signed وunsigned استفاده کنید تا بتوانید آنها راPort Map کنید.
به این ترتیب، کد ما کامل شده است و میتوانیم آن را شبیهسازی کنیم.
من فایل تستبنچ را قبلاً آماده کردهام. اگر با این موضوع آشنا نیستید، به شما پیشنهاد میکنم برنامه ویدئویی "نحوه شبیهسازی با نرمافزار ISim" را در سایت ما مشاهده و دوباره به این برنامه مراجعه کنید.
همانطور که در کد بالا میبینید، ورودیهایی را برای شبیهسازی به ماجول تقسیمکننده اعمال کردهام.
به Dividend و Divisor (یعنی مقسوم و مقسومعلیه)، ورودیهای ۴۶ و ۹ اعمال شده است؛ در واقع میخواهیم عدد ۴۶ را بر ۹ تقسیم کنیم.
اکنون مطابق شکل زیر، برای انجام شبیهسازی از منوی سمت چپ، گزینهی Simulation را انتخاب و سپس روی تستبنچ کلیک کنید.
حال از منوی پایین صفحه، روی گزینه Simulate Behavioral Model دبلکلیک کنید تا نرمافزار ISim از دل نرافزار ISE اجرا و عمل شبیهسازی انجام شود.
نرمافزار ISim به صورت پیشفرض به مدت یک میکروثانیه شبیهسازی را انجام میدهد.
در سمت چپ تصویر، ورودیها و خروجیها نوشتهشده است؛ ولی نمایش آنها به صورت باینری است که خواندن آنها را کمی سخت میکند.
برای تبدیل آنها به عددهای دسیمال signed، میتوانید پس از انتخاب سیگنالهای موردنظر، روی آنها کلیکراست کرده و سپس با انتخاب گزینهی Radix، انواع داده را مشاهده کنید. من نوع signed decimal را انتخاب میکنم.
همانطور که میبینید، ۴۶ بر ۹ تقسیم شده است و عدد ۵ را در خروجی خارج قسمت، و عدد ۱ را در خروجی باقیماندهی صحیح داریم. بنابراین عملیات تقسیم به درستی انجام شده است.
امیدوارم از خواندن این مقاله هم لذت برده باشید و بتوانید از نکات یاد گرفته شده، در انجام پروژههایتان استفاده کنید.
آیا برنامه ویدئویی پیادهسازی عملیات تقسیم در FPGA برای شما مفید بود؟
لطفا نظرتان را در مورد این برنامه در پایین همین پست با دیگران به اشتراک بگذارید. همچنین با کلیک روی هر کدام از دکمههای اشتراک گذاری ابتدای این مطلب و به اشتراکگذاری آن در شبکههای اجتماعی میتوانید افراد بیشتری را در یادگیری این مطالب سهیم کنید.
سلام و عرض خسته نباشید
استاد من بعد از انجام مراحل ابتدایی برای generate کردن و ساختن این ip این خطا را میگیرم.
Errore found during generation
Failed to generate divide. An error occurred. Please see the
log for details
من نتونستم این مشکل رو حل کنم، میخواستم ازتون کمک بگیرم.
سلام،
متاسفانه در پیام خطایی که کپی کردهاید، توضیحی در مورد نوع خطا داده نشده است.
موفق باشید.
سلام
بنده تقسیم کننده را تا انتها درست میرم ولی دو مقدار خارج قسمت و باقیمانده را نمیدهد . چکار کنم؟
سلام،
با توجه به اینکه این ماجول دارای تاخیر نسبتا طولانی در تولید اولین دیتا است، احتمالا لازم است شبیهسازی را برای چند کلاک دیگر ادامه دهید تا خروجی ظاهر شود.
موفق باشید.
سلام
عالی ترین آموزش بود
سلام،
ممنون از نظر مثبت شما.
موفق باشید.