توسط احمد ثقفی 

28 خرداد, 1397

کدنویسی آسان به کمک قابلیت Generic

آیا این جملات برای شما هم آشنا هستند؟

این کد‍ را دو ماه پیش نوشتم. بعضی از مقادیر و پارامترها تغییر کرده‌اند و حالا باید آنها را در کد اعمال کنم. اما تغییر کد فوق‌العاده مشکل است. نمی‌دانم دقیقا باید کجای کد را تصحیح کنم! در واقع همه چیز را فراموش کردم.

یا

کدی را که همکار سابق من در یک سال پیش نوشته است برای انجام چند تغییر به من داده‌اند. اما هر چه آن را بررسی می‌کنم، چیزی از آن سر در نمی‌آورم!

بررسی و انجام تغییرات در کدی که چند ماه یا حتی چند سال پیش نوشته است، امری معمول در فرآیند انجام پروژه‌های حرفه‌ای است. این کار می‌تواند فوق‌العاده مشکل و دردسرساز باشد اگر هنگام نوشتن کد، به روزی که مجبور به ایجاد تغییرات در آن هستید فکر نکرده باشید.

More...

برای نوشتن یک کد خوانا و قابل تصحیح، نکات و روش‌های مختلفی وجود دارند که باید به نحو مناسب به کار روند. در زبان VHDL هم امکانات مختلفی برای این کار وجود دارد و من در این مقاله قصد دارم یکی از پرکاربردترین آنها را برای شما تشریح کنم.

برای دانلود فایل PDF این مقاله، روی دکمه زیر کلیک کنید:

قابلیت Generic در زبان VHDL

قابلیتی در زبان VHDL وجود دارد به نام generic که به کمک یک مثال، آن رابرای شما توضیح خواهم داد.

نکته‌ای که در ابتدا باید به آن توجه داشته باشید، این است که امکانات مختلفی که در زبان VHDL وجود دارند، بعضی در حقیقت به شما یک قابلیت جدید و یک قدرت جدید در پیاده‌سازی می‌دهند، اما بعضی دیگر به شما قابلیت جدید پیاده‌سازی نمی‌دهد، بلکه کد‌نویسی شما را ساده‌تر می‌کنند. یعنی در واقع اگر شما این امکانات نوع دوم را ندانید هم می‌توانید هر نوع مداری را پیاده‌سازی کنید، اما با دانستن این امکانات می‌توانید کار کد‌نویسی را برای خودتان ساده‌تر کنید. موضوع مورد بحث این مقاله یعنی generic، یکی از این امکانات است.

همانطور که گفتم، امکان generic به شما قابلیت جدید پیاده‌سازی نمی‌دهد و فقط کد‌نویسی شما را ساده‌تر می‌کند. قابلیت generic مشابه قابلیت پارامتری کردن در زبان‌های برنامه‌نویسی است.

قابلیت Generic برای سهولت در کدنویسی:

  • این قابلیت امکان جدید پیاده‌سازی به شما نمی‌دهد
  • استفاده از generic کدنویسی شما را ساده‌تر می‌کند
  • قابلیت generic مشابه قابلیت پارامتری کردن در برنامه‌نویسی است

قابلیت پارامتری کردن در زبان‌های برنامه‌نویسی دقیقا یعنی چه؟

اجازه دهید که زبان C را به عنوان نمونه برای توضیح این موضوع، انتخاب کنیم. فرض کنید که کد زیر، یک نمونه کد نوشته شده به زبان C است.

A = B + 25; 
D = 25/C - 2; 
F = 50 + G;

در این کد، شما سه سطر از کدی را که به زبان C نوشته شده است می‌بینید. نقطه‌چین‌هایی که بین سطرها می‌بینید، به این معنی هستند که سطرهای دیگری هم وجود دارند و من این سه سطر را از یک کد بزرگ انتخاب کردم.

فرض کنید در بخشی از محاسبات این کد، نیاز به دمای اتاق باشد. دمای اتاق را ۲۵ درجه سانتی‌گراد فرض می‌کنیم. همانطور که می‌بینید، در یکی از سطر‌های این کد، ۲۵ درجه به اضافه رجیستری به اسم B شده است. در سطر دیگری، ۲۵ درجه تقسیم بر رجیستر C شده است و بعد منهای دو شده است. در سطر دیگری، ۲۵ درجه ضرب در دو شده است که می‌شود ۵۰ و بعد به اضافه G شده است و ممکن در یک بخش‌های دیگری از این کد هم از دمای اتاق استفاده شده باشد.

حالا فرض کنید این یک کد بزرگ و پیچیده است و در جاهای مختلف آن، از دمای اتاق در روابط مختلف استفاده شده است و در بعضی بخش‌ها حتی دمای اتاق یعنی ۲۵ درجه، با مقادیر دیگری جمع یا ضرب شده است و بنابراین دیگر حتی عدد ۲۵ درجه قابل مشاهده نیست.

فرض کنید که این کد را شما نوشته‌اید و چند ماه گذشته و حالا متوجه شده‌اید که دمای اتاق را باید به جای ۲۵ درجه، ۲۶ درجه قرار می‌دادید. حتماً خودتان می‌توانید حدس بزنید که تصحیح این کد چقدر سخت است. شما باید کد را خط به خط دوباره مرور کنید و هر جا که از دمای اتاق در روابط استفاده کردید، آن را از ۲۵ به ۲۶ تغییر دهید و در بسیاری از موارد این می‌تواند بسیار سخت باشد.

چه روشی برای اینکه این فرآیند را ساده کنید، به ذهن شما می‌رسد؟

 

استفاده از قابلیت define در زبان برنامه‌نویسی C

روشی که می‌توانید استفاده کنید این است که در ابتدای کد، یک ثابت تعریف کنید که در زبان C شما می‌توانید آن را به کمک عبارت define انجام دهید. مثلاً من ثابتی به اسم Temp (مخفف Temperature) را به کمک define تعریف می‌کنم. همانطور که در کد زیر می‌بینید، به کمک define مقدار Temp را برابر با ۲۵ قرار می‌دهم:

#Define Temp 25 
A = B + Temp; 
D = Temp/C - 2; 
F = Temp*2 + G;

حالا می‌توانم در هر جای کد که نیاز به دمای اتاق یعنی ۲۵ درجه دارم، به جای اینکه خود عدد ۲۵ را بنویسم، ثابت Temp را بنویسم.

اما مزیت استفاده از چنین روشی چیست؟

مزیت این کار این است که هر زمان تصمیم بگیرم که مقدار دمای اتاق را عوض کنم، کافی است در ابتدای کد، عدد ۲۵ را تغییر بدهم و دیگر هیچ نیازی نیست که تمام کد را بررسی کنم و احیاناً با خطاهایی که در اثر این تغییرات رخ می‌دهد مواجه شوم.

 

ساده‌سازی کد VHDL به کمک قابلیت Generic

مشابه همین قابلیت در زبان VHDL هم وجود دارد، اما به آن قابلیت generic گفته می‌شود. اجازه دهید که با بررسی یک مثال، این امکان و قابلیت را با هم مرور کنیم.

کد زیر، توصیف کننده یک رجیستر nبیتی است. منظورم از nبیت این است که عرض این رجیستر را خودتان می‌توانید در این کد تعیین کنید و این کار به کمک قابلیت generic امکان‌پذیر است:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;

entity Example_Register_Generic is

Generic
(
    Reg_Width:integer:= 8);

Port 
( 
    D : in unsigned(Reg_Width-1 downto 0);
    Clock: in STD_LOGIC;
    Reset: in std_logic;
    Q : out unsigned(Reg_Width-1 downto 0));

end Example_Register_Generic;

architecture Behavioral of Example_Register_Generic is
begin

process(Clock) 
begin

    --FlipFlop definition with Synch reset.
    if rising_edge(Clock) then
        Q<=D;
        if (Reset = '1') then
            Q<= (others=>'0');
        end if;
    end if;
end process;
end Behavioral;

همانطور که می‌بینید، در بخش entity بخش دیگری را اضافه کردم در کنار بخش port به اسم generic. شما احتمالا تا پیش از این در بخش entity فقط portها را مشخص می‌کردید، اما در صورت نیاز شما یک مورد دیگر را هم می‌توانید در همین بخش entity مشخص کنید و آن هم generic است.

در این کد، من یک generic معرفی کرده‌ام به اسم Reg_Width و نوع آن را integer تعریف کرده‌ام. به سیگنالی که نوع آن integer باشد می‌توانید مقادیر integer را ارجاع دهید. همچنین یک مقدار اولیه هم به آن ارجاع داده‌ام که در واقع این مقدار نهایی آن هم است، چون این مقدار دیگر قابل تغییر نیست. برای مقداردهی اولیه از علامت =: استفاده می‌کنیم و در این مثال، مقدار آن را برابر با هشت قرار داده‌ام.

برای آشنایی بیشتر با زبان VHDL این برنامه ویدئویی را ببینید…

بعد از آن portها را تعریف می‌کنم، اما این بار آن‌ها را بر مبنای Reg_Width تعریف می‌کنم. هدف من این بود که یک رجیستر تعریف کنم. یک رجیستر مثل یک فلیپ‌فلاپ است با این تفاوت که به جای یک بیت، چند بیت دارد.

در این کد، پورت ورودی D را تعریف می‌کنم و می‌خواهم عرض آن را برابر با Reg_Width قرار دهم. بنابراین، برای تعیین عرض بیت آن، بجای اینکه مثلا بنویسم:

D : in unsigned(7 downto 0);

می‌نویسم:

D : in unsigned(Reg_Width-1 downto 0);

چون Reg_Width برابر با هشت است، در نتیجه این دو عبارت کاملا یکسان هستند. برای Q هم به همین ترتیب عرض بیت را (Reg_Width-1 downto 0) قرار می‌دهم.

حال ببینیم بخش architecture این کد را بر مبنای generic چطور توصیف می‌کنیم.

 

نکات مربوط به بخش Architecture برای استفاده از قابلیت Generic

همانطور که در کد این مثال می‌بینید، یک پراسس داریم که طبق معمول در لیست حساسیت آن، فقط و فقط سیگنال clock وجود دارد. اولین خط بعد از begin پراسس، شرط لبه بالارونده کلاک است و بعد از آن D را به Q ارجاع داده‌ایم و این، عملکرد عادی این رجیستر است که همزمان با لبه بالارونده Clock باید ورودی را در Q ذخیره کند. البته D و Q در این جا nبیتی شده‌اند (در این مثال ۸ بیتی.)

سیگنال Reset هم در این کد وجود دارد و برای آن نوشته‌ایم اگر Reset برابر با یک شد، مقدار (’otheres=>’0) به آن ارجاع شود. معنی این ارجاع این است که تمام بیت‌های Q برابر با صفر شوند. روش دیگر برای این ارجاع این است که بنویسید:

Q <= “00000000”;

یک مزیت استفاده از مقداردهی به کمک others این است که روش راحت‌تری است. مثلا فرض کنید شما قصد داشتید یک سیگنال ۳۰ بیتی را مقداردهی کنید. در آن صورت باید ۳۰ بار عدد صفر را داخل دو گیومه تایپ می‌کردید تا مقداردهی به سیگنال Q انجام شود. اما به کمک others، شما به راحتی فقط می‌نویسید (’otheres=>’0) بدون توجه به عرض بیت سیگنال.

اما مزیت اصلی استفاده از others در واقع کمک به genericنویسی کد شما است. اگر شما در مثال این مقاله، مقداردهی به Q را به صورت مقداردهی داخل دو گیومه انجام دهید، یعنی مثلاً برای این مثال که عرض سیگنال Q برابر با ۸ بیت است بنویسید:

Q <= “00000000”;

این کد، یک کد generic کامل به حساب نمی‌آید؛ به این دلیل که گرچه در ابتدای کد، یک generic تعریف کردید و می‌توانید مقدار عرض بیت رجیستر را در آن تعریف کنید، اما بر مبنای مقدار آن generic، هر بار مجبور هستید در کدتان، بخش ریست کردن سیگنال Q را تصحیح کنید و تعداد صفرها را تنظیم کنید.

اما وقتی از others استفاده می‌کنید، چون در روش ارجاع به کمک others عرض بیت سیگنال مهم نیست، بنابراین فقط کافی است در ابتدای کدتان عرض بیت را مشخص کنید، و در بخش مقداردهی به Q، با هر عرض بیتی، تمام بیت‌ها به کمک others برابر با صفر می‌شوند.

بنابراین، تاکید می‌کنم که برای مقدار‌دهی به سیگنال‌ها (اگر می‌خواهید تمام بیت‌های یک سیگنال را به صفر یا تمام آنها را به یک مقداردهی کنید)، حتما از روش others استفاده کنید تا در صورت نیاز به generic نوشتن، کد شما قابلیت generic شدن کامل را داشته باشد.

 

دیگر نگران اینکه مدت زیادی از نوشتن کد شما گذشته نباشید

همانطور که دیدید، حالا این کد بصورت کامل generic شده است و شما در ابتدای کد می‌توانید بدون اینکه نگران هیچ تنظیم اضافه‌ای باشید، فقط مقدار Reg_Width را عوض کنید و تمام کد شما بدون اینکه نیاز به هیچ دست‌کاری اضافه‌ای داشته باشد می‌تواند با عرض بیت جدید پیاده‌سازی شود.

آیا روش‌های دیگری را برای خواناتر شدن کد و انجام راحت‌تر تغییرات احتمالی در آیند سراغ دارید؟

خوشحال خواهم شد که نظرتان را در مورد این روش‌ها و همچنین روش generic در پایین همین پست با من به اشتراک بگذارید.

 

آیا مقاله کدنویسی آسان به کمک قابلیت Generic برای شما مفید بود؟

لطفا نظرتان را در مورد این برنامه در پایین همین پست با دیگران به اشتراک بگذارید. همچنین با کلیک روی هر کدام از دکمه‌های اشتراک گذاری ابتدای این مطلب و به اشتراک‌گذاری آن در شبکه‌های اجتماعی می‌توانید افراد بیشتری را در یادگیری این مطالب سهیم کنید.

کانال تلگرام آموزش FPGA از صفر

برای عضویت در کانال تلگرام و دسترسی به آموزش‌های بیشتر و اطلاع سریع از زمان انتشار آموزش‌ها و تخفیف‌های ویژه، روی دکمه زیر کلیک کنید:

شاید به این موضوعات نیز علاقه داشته باشید:

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

۷ تکنیک پیشرفته کدنویسی برای FPGA

>