در قسمت چهارم از آموزش FPGA با مدارات ترکیبی و ترتیبی آشنا شدیم. در نهایت توابع یک، نیم جمع کننده را استخراج و آنها را به کد VHDL تبدیل کردیم. شاید سوالی که ذهنتان را درگیر کرده باشد، این است که آیا قرار است ما تا ابد، اگر خواستیم مداری را در FPGA پیادهسازی کنیم، باید مشقت استخراج توابع منطقی، جسممان را عذاب بدهد و روحمان را مثل خوره بخورد و بتراشد و تازه پس از این همه عذاب، توابع منطقی استخراج شده را تبدیل به کد VHDL کنیم؟ اگر حافظهی خوبی شما را یاری میکند و سیسوگ را نیز به خوبی دنبال کرده باشید، پس حتما به یاد دارید که در اواخر قسمت اول یادآوری کردیم که برای پیادهسازی مدار یا عملکرد موردنظرتان در FPGA نیازی به اشراف کامل بر مدارات دیجیتال نیست، و فقط درک منطق مدار دیجیتال کافیست، بقیه کارها را خود نرمافزار با الگوریتمهایی که دارد انجام میدهد. البته منکر این موضوع نیستیم که اشراف هرچه بیشتر بر مسئلهای، آن مسئله را قابل فهمتر میکند. حقیقت این است که در مدارات ساده همچون جمعکننده و امثالهم این کار راحت خواهد بود ولی در مدارات پیچیده، استخراج تک تک توابع و بررسی سطح پایین مدار، کاری بسیار دشوار و زمانبر است. حتی افراد حرفهای نیز چنین کاری نخواهند کرد، مگر در موارد خیلی خاصی که الگو یا رمزنگاری خاصی مدنظر باشد.
اگر موافق باشید اجالتا اجازه بدهید یک بار دیگه، فقط یک بار دیگه، مدار دیگری را به صورت سطح پایین و بیت به بیت بررسی کنیم. برای این کار، توابع مدار تمام جمع کنندهی چهار بیتی را استخراج و سپس آنها را به کد VHDL تبدیل میکنیم. در این حین نیز نکات زیادی را به شما آموزش خواهیم داد. پس از اینکه مدار مورد نظر را به صورت سطح پایین و بیت به بیت تحلیل و پیادهسازی کردیم، این کار را به عنوان هنر میبوسیم و میزاریم کنار و با خیال راحت از روشی که در انتهای همین مقاله به شما معرفی خواهیم کرد استفاده میکنیم.
قبل از اینکه بخواهیم مدار تمام جمع کننده چند بیتی را طراحی کنیم باید مقدماتی را بیان کرد.
انواع پورتها
پورتها از دو جهت مورد بررسی قرار میگیرند:
- مسیر یا جهت
- نوع
- In
- Out
- Inout
وقتی قرار باشد دیتا وارد FPGA بشود از حالت In و هنگام خروج دیتا از FPGA از حالت Out استفاده میکنیم. احتمالا حدس خواهید زد وقتی که بخواهیم به نحوی مجموعی از این دو حالت را داشته باشیم، از حالت Inout استفاده میکنیم. بحث مربوط به Inout کمی پیچیدگی دارد، و در قسمتی جداگانه به این موضوع خواهیم پرداخت.
نوع پورتها نیز با توجه به کاربردها، دارای انواع مختلفی است که ما در زیر به مهمترین آنها اشاره خواهیم کرد:
- std_logic
- std_logic_vector
- signed
- unsigned
- integer
در طول این مجموعه آموزشی با هر کدام از موارد بالا آشنا خواهیم شد.
سیگنالها
تمام جمعکنندهی چهار بیتی
یک تمام جمعکنندهی چهار بیتی همانند تصویر بالا، از چهار، تمام جمعکنندهی تک بیتی تشکیل میشود. یک روش مناسب برای پیادهسازی این است که توابع منطقی یک، تمام جمعکنندهی تک بیتی را استخراج کنیم و در یک ماژول بنویسیم و سپس از این ماژول، به نحوی، چهار بار استفاده کنیم. البته روشهای مناسبتر و بدتری نیز وجود دارند که ما روشهای بدتر را توضیح نخواهیم داد، اما در آخر همین مقاله به تشریح روش بهتری خواهیم پرداخت.
برای اینکه یک ماژول را به ماژول دیگر اضافه کنیم باید اقداماتی صورت گیرد، که در حین انجام پروژه به توضیح آن خواهیم پرداخت.
میتوان گفت توابع منطقی تمام جمعکننده، تعمیم یافته، همان توابع منطقی نیم جمعکننده میباشد. این توابع را میتوانید در زیر مشاهده کنید:
S = x ⊕ y ⊕ cin
(C = (x . y) + (x . cin) + (y . cin
ما توابع منطقی ذکر شده در بالا، که مربوط به یک، تمام جمعکنندهی تک بیتی میباشد را به کد VHDL تبدیل میکنیم و در یک زیرماژول قرار میدهیم. سپس این زیرماژول را، چهار بار به ماژول اصلی اضافه خواهیم کرد.
همانطور که در کد زیر مشاهده میکنید، زیرماژول ما سه ورودی و دو خروجی دارد، که همه از نوع std_logic هستند. یعنی همهی ورودی-خروجیها تک بیتی هستند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity Sub_Module is Port ( x : in STD_LOGIC; y : in STD_LOGIC; Cin : in STD_LOGIC; Sum : out STD_LOGIC; Cout : out STD_LOGIC ); end Sub_Module; architecture Behavioral of Sub_Module is begin Sum <= x xor y xor Cin; Cout <= (x and y) or (x and Cin) or (y and Cin); end Behavioral; |
حال ما باید به نحوی این زیرماژول را به ماژول اصلی اضافه کنیم. پس با دقت به گفتههای زیر توجه کنید که یک بار برای همیشه این موضوع را فرا بگیرید.
منطقا ماژول اصلی اونقدری عاقل نخواهد بود که بخواهد زیرماژول خود را تشخیص دهد. پس ما باید به طریقی به ماژول اصلی بفهمانیم که آقای ماژول اصلی، فلانی زیرماژول شماست. ما برای این کار، قبل از begin مربوط به architecture، زیرماژول را به ماژول اصلی، مانند کد زیر معرفی میکنیم (component port). پس از این کار، ماژول اصلی همهی اختیارات را بر عهده خواهد گرفت و میگوید که چگونه میخواهد از این زیر ماژول استفاده کند. و شما چگونگی استفاده از زیر ماژول را در کد زیر بعد از begin مربوط به architecture مشاهده میکنید (port map). در مورد چگونگی استفاده، باید یک سر برگردید به عقب و نیمنگاهی به دیاگرام مدار تمام جمعکنندهی چهار بیتی، بیندازید و همزمان با کد VHDL مقایسه کنید. باور کنید ما هم برای نوشتن این کد، کاری جز این نکردیم.
همانطور که در کد مشخص است، ما چهار بار، به منزلهی چهار بیتی بودن تمام جمعکننده، زیرماژول را به ماژول اصلی اضافه میکنیم.
برای اینکه بتوانیم بین زیرماژولها ارتباط برقرار کنیم یا بیت سرریز خروجی هر تمام جمعکننده را، به سرریز ورودی تمام جمعکنندهی بعدی انتقال بدهیم، یک سیگنال میانی 3 بیتی نیز تعریف میکنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity Top_Module is Port ( x : in STD_LOGIC_VECTOR (3 downto 0); y : in STD_LOGIC_VECTOR (3 downto 0); Cin : in STD_LOGIC; Sum : out STD_LOGIC_VECTOR (3 downto 0); Cout : out STD_LOGIC ); end Top_Module; architecture Behavioral of Top_Module is signal C_Int : std_logic_vector (2 downto 0) := (others=>'0'); component Sub_Module port ( x : in std_logic; y : in std_logic; Cin : in std_logic; Sum : out std_logic; Cout : out std_logic ); end component; begin Add1: Sub_Module port map ( x => x(0), y => y(0), Cin => Cin, Sum => Sum(0), Cout => C_Int(0) ); Add2: Sub_Module port map ( x => x(1), y => y(1), Cin => C_Int(0), Sum => Sum(1), Cout => C_Int(1) ); Add3: Sub_Module port map ( x => x(2), y => y(2), Cin => C_Int(1), Sum => Sum(2), Cout => C_Int(2) ); Add4: Sub_Module port map ( x => x(3), y => y(3), Cin => C_Int(2), Sum => Sum(3), Cout => Cout ); end Behavioral; |
خب همانطور که گفتیم، معمولا اول یک فایل برای زیرماژول ایجاد میکنیم و بعد از اینکه، توصیف جزئی از مدار را، در این زیرماژول نوشتیم، ماژول اصلی را ایجاد میکنیم. و طبق مراحلی که گفتیم زیر ماژول را هم به کد معرفی، و هم از آن استفاده میکنیم.
تا این مرحله هنوز زیرماژول، به ماژول اصلی اضافه نشده است ولی با اقداماتی که ما انجام دادیم، قرار است که این اتفاق تنها با یک دابل کلیک صورت گیرد. اما قبل از دابل کلیک کردن باید یک کار کوچک دیگر انجام بدهیم. چون ما اول زیرماژول، سپس ماژول اصلی را به پروژه اضافه کردیم، پروژه، زیرماژول را به عنوان ماژول اصلی میشناسد. برای رفع این مشکل باید روی ماژول اصلی راستکلیک کرده و گزینهی Set as Top Module را انتخاب کنیم تا علامت انحصاری تاپماژول مانند شکل زیر کنار همان ماژولی که به عنوان ماژول اصلی مدنظر ماست فعال شود.
اکنون اول ماژول اصلی را با ماوس انتخاب (Select) میکنیم، سپس با دابل کلیک کردن روی گزینه Check Syntax، زیرماژول، مانند تصویر زیر به ماژول اصلی اضافه خواهد شد. پس از طی این مراحل، زیرماژولها با کمی تورفتگی نسبت به ماژول اصلی قرار خواهند گرفت.
ما فقط یک زیرماژول داشتیم، پس چرا 4 زیرماژول به ماژول اصلی اضافه شده است؟ در جواب باید بگوییم که در واقع ما یک زیرماژول را چهار بار به ماژول اصلی اضافه کردیم. احتمالا مزیت زیرماژول نوشتن را نیز متوجه شده باشید. اگر ما در پروژهای نیاز داشته باشیم بخشی از کد را چندین بار تکرار کنیم، برای راحتی میتوانیم همان بخش کد را به صورت زیرماژول در بیاوریم و چندین بار از آن استفاده بکنیم. با این کار کد نیز خواناتر خواهد شد.
اگر کمی خسته شدید نفسی عمیق بکشید که قرار است همهی این کارهایی که در بالا انجام دادیم را فقط در یک خط خلاصه کنیم.
از این زمان به بعد ما در تمامی پروژهها، فقط عملکرد و منطق مدار را بررسی و آن را به کد VHDL تبدیل میکنیم. در کد زیر ما دو پورت ورودی و یک پورت خروجی چهار بیتی تعریف میکنیم و سپس در محیط Concurrent، ورودیها را باهم جمع کرده و به خروجی ارجاع میدهیم. به کد زیر توجه کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | library IEEE; use IEEE.STD_LOGIC_1164.ALL; use ieee.numeric_std.all; entity Full_Adder_4bit is Port ( x : in signed (3 downto 0); y : in signed (3 downto 0); Sum : out signed (3 downto 0) ); end Full_Adder_4bit; architecture Behavioral of Full_Adder_4bit is begin Sum <= x + y; end Behavioral; |
در کد بالا ورودی-خروجیها را از نوع علامتدار تعریف کردیم، و مطابق با این کار پکیج ieee.numeric_std.all را نیز اضافه کردیم. پس یادتان باشد هروقت خواستید از اعداد علامتدار استفاده کنید، پکیج مربوط به این اعداد را نیز اضافه کنید تا با خطا مواجه نشوید.
چرا وقتی با همین یک خط کد توانستیم مدار را توصیف کنیم، اون همه سختی را به جان خریدیم و یه عالمه کد نوشتیم؟ باور کنید ما نه بیمار هستیم و نه از اختلالات روانی خاصی رنج میبریم، هدف ما فقط و فقط آموزش FPGA به صورت کاملا دقیق و از پایه به شما عزیزان میباشد.
در آخر خدمتتان یادآور شویم که در آخرین کد ایراداتی همچون پوشش ندادن سرریز و بقیه موارد وجود دارد، اما چون حجم این مقاله کمی از حد معمول قسمتهای قبل بیشتر شده است و همچنین مقدمات مربوط به این موضوع را هنوز یاد نگرفتهایم، دیگر قصد پرداختن به این موضوع را نداریم و قرار است در سری مقالاتی با عنوان “اهمیت تئوری و ریاضیات در برنامهنویسی” که هماکنون در دست تالیف قرار دارد و قسمت اول آن نیز منتشر شده است، مفصل در باب این موضوع صحبت کنیم.
امیدواریم پس از چند قسمتی که از این مجموعه آموزشی میگذرد، توانسته باشیم در این مقاله، حس خوب شما نسبت به FPGA را ارضا کرده باشیم و شما نیز لذت و بهره کافی را برده باشید.
در قسمت ششم به شما آموزش خواهیم داد که چگونه مدار خود را در نرمافزار شبیهسازی کنید و نتایج را مشاهده کنید، پس با سیسوگ همراه باشید.
واقعا عالی بود. اینکه این آموزشهارو رایگان در اختیار ما میذارید قابل ستایشه. انشالله که خیرشو ببینید.
آموزش هاتون فوق العاده هستن. واقعا مممنونم.
سلام ممنون از به اشتراک گذاری نظرتون
خوشحالیم که تونستیم برای شما عزیزان فعال در حوزه الکترونیک مفید واقع بشیم.
با سلام و احترام
چرا در خط کد ذیل برای تعریف سیگنال میانی از others استفاده شده است.
signal C_Int : std_logic_vector (2 downto 0) := (others=>’0′);
باتشکر
سلام نازنینم.
این سینتکس برای این هستش که به راحتی مقادیر تمامی بیتها را فقط با (others=>’0′) مقداردهی کنیم.
در نظر بگیرید که یک سیگنال 32 بیتی داشته باشیم و بخواهیم 32 بار 0ها را پشت سرهم بنویسیم که احتمالا با اشتباه همراه است.
البته مزیت اصلی (others=>’0′) در generic تعریف کردن است.
ممنون از شما بایت آموزش خوب و پاسخ گویی
خواهش میکنم، سپاس از نظر مثبت شما.
سلام
من برای برنامه تون test bench نوشتم اما به نظر میرسه بیت کَری قبلی رو در نظر نمیگیره
سلام
منظورتون از بیت قبلی کدوم بیت؟
منظورم بیت کری که از جمع بیت های کم ارزش بدست میاد. من عدد 3 رو با عدد 5 جمع کردم اما نتیجه اون 2 شد
من همهی این کدا را تست کردم و نه تنها قابلیت شبیه سازی بلکه قابلیت پیاده سازی را نیز دارند. حتما جایی را اشتباه کرده اید لطفا تست بنچتان را بفرستید.
ببخشید اشتباه از من بود. یه جای برنامم ایراد داشت که امروز متوجه شدم.
گفتم که تست شده هستن. ولی خب هنوز یه اشتباه دیگه دارید که فکر میکنید این چیزی که دارید مینویسید یک برنامه است.
ممنون
خیلی خوب بود.
البته از generic هم میتونستید استفاده کنید تا 4 بار Add رو انجام ندید
نه به هر حال باید چهار بار اد شود حالا به هر روشی اما خب کاربرد generic جای دیگریست و به این موضوع مربوط نمیباشد
ممنون
خواهش میکنم
ولی من میگم بیا و اینمطالبو ننویس، بجاش طراحی گرافیک بزار، اینجوری خیلی بهتره، تا مطالبی که ازش سر در نمیاری بنویسی
دوست عزیز بهتر بود قبل از اینکه شروع به نقد کردن کنید، فرهنگ و اصول صحیح نقد کردن را مطالعه میکردید، سپس نقدتان در باب این موضوع را مینوشتید. چون در اینصورت به جای این حرف خیلی مودبانه میگفتید فلان قسمت از مقالتون از لحاظ علمی یا هر چیز دیگری مشکل دارد و ما هم از آنجا که معتقد هستیم همه چیز را همگان دانند، با کمال میل از شما تشکر میکردیم و مقاله خود را اصلاح میکردیم. در نهایت اگر شما فکر میکنید که در زمینهی FPGA به قدری سواد دارید که حرفی برای گفتن داشته باشید، خیلی خوشحال خواهیم شد که شما بیایید و وقت بگذارید مقالههای خود را بنویسید و در سیسوگ منتشر کنید.
موفق و پیروز باشید.
ولی من میگم این بحث رو ادامه بدین علاوه بر این شما هم طراحی گرافیک بزار تا بقیه هم استفاده کنند.
شاد و پیروز باشید.
سپاس از امیر عزیز. برای هرچه بهتر شدن و تکمیل این مجموعه آموزشی، حتما نیاز به نقدهای سازندهی شما خواهیم داشت.