1 آزمایش اول: آشنایی با نرم افزار ISE و مراحل پیادهسازی کد VHDL
روی FPGA
هدف از این آزمایش، آشنایی با نرم افزار ISE میباشد. نرم
افزار ISE از شرکت Xilinx است که برای شبیهسازی، سنتز و پیادهسازی برنامههای نوشته شده به
زبانهای توصیف سخت افزار قابل استفاده است. اصولا هر شرکت تولید کننده FPGA باید نرم
افزاری جهت پیادهسازی کدهای توصیف سخت افزار داشته باشد.
برای پیادهسازی یک مدار
روی FPGA
مراحل زیر مورد نیاز است:
الف) طراحی مدار
ب) توصیف مدار با زبانهای
توصیف سخت افزار
ج) شبیهسازی
د)سنتز
ه)پیادهسازی
در مرحله طراحی، با توجه به صورت مساله و ورودی/خروجیهای سیستم، روشی
برای تولید خروجیها بر اساس ورودیها ارائه میشود(روشهای مختلف طراحی در درس
طراحی خودکار سیستمهای دیجیتال ارائه میشود) سپس با به طراحی انجام شده، برنامه VHDL نوشته میشود
و در مرحله سنتز، برنامه نوشته شده به زبان VHDL به المانهای
دیجیتال نظیر فلیپ فلاپ، حافظه، دیکدر، مالتی پلکسر و ... تبدیل میشود و نهایتا
در مرحله پیادهسازی، کدهای سنتز شده با المانهای موجود در FPGA جایگزین و
فایل نهایی برای پراگرام کردن FPGA آماده میشود.
برای سنتز و پیادهسازی برنامه VHDL، نرم افزارهای مختلفی وجود دارد. با
توجه به اینکه در طراحی بوردهای آزمایشگاه، از FPGAهای شرکت Xilinx استفاده شده است بنابراین از نرم افزار تولید شده شرکت زایلینکس
برای سنتز و پیادهسازی کدهای VHDL نوشته شده استفاده میکنیم. در شکل 1-1، شکل ظاهر این نرم افزار
نمایش داده شده است. در این شکل، قسمتهای مختلف این نرم افزار مشخص شدهاند.
هنگام ایجاد یک پروژه جدید در این نرم افزار، باید نام خانواده FPGA، نام قطعه،
نوع بسته بندی انتخاب گردد. مثلا FPGAهای استفاده شده در
بوردهای آزمایشگاه از خانواده Spartan6 هستند و نام قطعه 6SLX9
ونوع بسته بندی تراشه TQG144 میباشد(تراشه از نوع چهارطرفه و دارای 208 پایه).
بعد از اینکه در این نرم افزار پروژه خود را ایجاد کردید نام تراشه به صورت اختصار
در قسمت فوقانی پنجره مینویسد. تراشه ای که در شکل 1-1 استفاده شده است xc6slx9-3tqg144 میباشد که حاوی تمام اطلاعات تراشه مورد نظر میباشد.

شکل 1-1: قسمتهای مختلف
نرم افزار ISE
بعد از ایجاد پروژه و اضافه کردن فایلها به آن، با دابل کلیک کردن روی
گزینه Synthesize، عملیات سنتز شروع میگردد. گزارش مربوط به سنتز کردن را در پنجره
console
(قسمت پایینی پنجره) نمایش میدهد. در صورت موفقیت آمیز بودن عملیات سنتز، میتوان
گام بعدی که پیادهسازی طرح باشد را شروع کرد. برای شروع عملیات پیادهسازی باید
روی گزینه Implement Design دابل کلیک کنید. همانطور که در شکل 1-1 مشاهده میشود عملیات پیادهسازی
از سه گام به نامهای Translate، Map و Place &
Route تشکیل شده است. برای ایجاد فایل
نهایی باید روی گزینه Generate
Programming… دابل کلیک کنید.
برای پراگرام کردن FPGA میتوان مستقیما روی گزینه Configure Target Device
دابل کلیک کرد یا اینکه از نرم افزار Impact استفاده کرد. این نرم افزار جزء مجموعه ISE است و به
صورت اتوماتیک هنگام نصب نرم افزار ISE، نصب میگردد.
اصولا در سیستمهای دیجتال برای اینکه نشان داده شود سیستم زنده و در
حال کار میباشد، یک LED روی بورد مربوطه تعبیه میشود و با روشن شدن سیستم، این LED با روشن و
خاموش شدن متوالیش، زنده بودن سیستم را نشان میدهد. در این قسمت قصد داریم به
عنوان اولین آزمایشی که با بوردهای آزمایشگاه انجام میدهیم یک مدار چشمک زن را پیادهسازی
کنیم.
برنامه VHDL مورد نیاز برای یک LED چشمک زن به صورت زیر است
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
USE ieee.std_logic_unsigned.all;
entity BlinkingLED is
Port (
Clk : in STD_LOGIC;
Led : out STD_LOGIC
);
end BlinkingLED;
architecture Behavioral of BlinkingLED is
signal delay_counter: std_logic_vector(31 downto 0);
begin
Led <= delay_counter(24);
process(Clk)
begin
if(Clk='1' and Clk'event)then
delay_counter<=delay_counter+1;
end if;
end process;
end Behavioral;
1- نرم
افزار ISE را
طبق صحبتهای انجام شده در آزمایشگاه، نصب کنید. سپس با انتخاب گزینه New Project … از
منوی File، یک
پروژه جدید به نام BlinkingLED ایجاد کنید( تنظیمات مورد نیاز در شکل 1-2 نشان داده شده است)
2-
یک فایل جدید ایجاد کنید و برنامه نوشته شده در قسمت پیش
گزارش را در آن کپی کنید. برای ایجاد یک فایل جدید باید روی اسم تراشه(در پنجره
سمت چپ)، کلیک راست نموده و گزینه New
Source… را انتخاب کنید سپس در پنجره ظاهر
شده، گزینه VHDL Module را انتخاب کنید و نام فایل را نیز در قسمت مربوطه بنویسید.(مراحل انجام
آن در شکلهای 1-3 و 1-4 نشان داده شده است)
3-
برنامه را سنتز کنید و مشاهدات خود از خروجی سنتز (پنجره
کنسول که در شکل 1-1 نشان داده شده است) را بنویسید.(تعداد فلیپ فلاپها، بلوکهای
تشخیص داده شده در کد VHDL و Warningها) (جواب این سوال را
روی کاغذ بنویسید و به صورت حضوری در آزمایشگاه تحویل دهید)
.
شکل 1-2: تنظیمات مربوط
به تراشه مورد استفاده در بوردهای آزمایشگاه

شکل 1-3: نحوه ایجاد یک
فایل جدید

شکل 1-4: انتخاب فایل VHDL و نامگذاری
آن
1- در
نرم افزار ISE یک پروژه جدید به نام BlinkingLED ایجاد کنید
تراشه را XC6LXو پکیج آن را TQG144 انتخاب کنید.
2- برنامه
VHDLی بنویسید که فرکانس کلاک ورودی
را که 50مگاهرتز است را به 1 هرتز تبدیل کند.
3-
برنامه را سنتز کنید و مشاهدات خود از خروجی سنتز (پنجره
کنسول که در شکل 1-1 نشان داده شده است) را بنویسید.(تعداد فلیپ فلاپها، بلوکهای
تشخیص داده شده در کد VHDL و Warningها)
4-
برای برنامه فوق یک فایل UCF بنویسید به
نحوی که پایه کلاک را به اسیلاتور روی بورد متصل و سیگنال 1 هرتز را به LED روی بورد
متصل کند.
5-
بیت فایل را ایجاد کنید و با کمک نرم افزار impact آن
را روی بورد پراگرام کنید و نتیجه را به مسئول آزمایشگاه نشان دهید.
2 آزمایش دوم: راه اندازی دکمههای فشاری و دیودهای نورانی روی
بورد FPGA
هدف از این آزمایش، کار با دکمههای فشاری روی بورد آموزشی میباشد.
بعد از اینکه برنامه VHDL نوشته شد و شبیهسازی آن به اتمام رسید، برای پیادهسازی نهایی آن
روی سخت افزار، باید فایلی را آماده کرد که پورتهای Entity نوشته شده
را به پایههای تراشه FPGA مپ کند. به این فایل اصطلاحا ucf (User Constraint File)
گفته میشود. فرمت این فایل به صورت زیر است:
NET "CLK25M" LOC = "AA14" |
IOSTANDARD = LVTTL ;
سطر فوق به این مفهوم است که سیگنال CLK25M که در
برنامه VHDL
استفاده شده است (یکی از پورتهای Entity است) به پایهای به نام AA14(نام پایهها را باید از کاتالوگ
تراشه FPGA
استخراج کنید) متصل شود و استاندارد ولتاژ آن پایه نیز LVTTL است.
1- با
استفاده از شماتیک بورد آزمایشگاه، به سوالات زیر پاسخ دهید:
الف) چند عدد دکمه فشاری روی بورد
وجود دارند؟ نام آنها را یادداشت کنید؟
ب)کدام یک از دکمههای فشاری
مستقیما به پایههای FPGA متصل هستند؟ شمارههای پایه FPGA را نیز
مشخص کنید.
2-
به برنامه جلسه گذشته یک پورت جدید به نام Enable
اضافه کنید. عملکرد این پایه به این صورت است که اگر مقدار آن "1" باشد
آنگاه، LED
چشمک زن باشد و اگر مقدار آن "0" بود آنگاه LED خاموش شود.
3- برای
برنامه فوق، یک فایل ucf ایجاد کنید و با توجه به شماتیک بورد، پایههای مناسب را به
پورتهای Entity خود اضافه کنید.
4-
بیت فایل را ایجاد کنید و با کمک نرم افزار impact آن
را روی بورد پراگرام کنید و نتیجه را به مسئول آزمایشگاه نشان دهید
5-
برنامهای به زبان VHDL بنویسید که
یک عدد BCD
دریافت کند و معادل 7seg آن را
تولید کند.
6-
با استفاده از دستور for generate، یک جمع
کننده 512 بیتی را با استفاده از Full
Adder پیادهسازی کنید.
هدف از این آزمایش، آشنایی با مباحث تاخیر و فرکانس کلاک در FPGA میباشد. در این آزمایش به عنوان نمونه، نحوه محاسبه تاخیر یک جمع کننده
32 بیتی آزمایش میگردد.
یکی از سوالهای مهم در هنگام پیادهسازی یک مدار دیجیتال با استفاده از
FPGA، این
است که مدار پیادهسازی شده حداکثر با چه فرکانسی کار خواهد کرد. یا اگر مدار به
صورت ترکیبی باشد آنگاه تاخیر این مدار ترکیبی چقدر خواهد بود.
برای محاسبه تاخیر و حداکثر فرکانس کلاک در مدارهای ترتیبی کافی است در
فایل ucf، یک
محدودیت برای سیگنال کلاک تعریف شود. اگر فرض کنیم CLK نام سیگنال
کلاک باشد آنگاه با اضافه کردن دو دستور زیر در فایل ucf، به نرم
افزار اعلام میکنیم که قصد داریم به پریود کلاک برابر 3 نانوثانیه برسیم.
NET "CLK" TNM_NET = "SYS_CLK";
TIMESPEC "TS_SYS_CLK" = PERIOD
"SYS_CLK" 3 ns HIGH 50 %;
نرم افزار ISE سعی میکند در مراحل map و place and route، زمان ذکر شده در فایل ucf را برآورده کند. در نهایت تاخیرها و
فرکانس کلاک دست یافتنی را در گزارش place
and route ارائه میدهد. این گزارش در فایلی
با پسوند par
ذخیره میشود.
(گزارش مربوط به مرحله سنتز در فایلی با پسوند syr و گزارش
مربوط به مرحله MAP در فایلی با پسوند map ذخیره میشود)
برای دیدن پارامترهای زمانی طرح ایمپلیمنت شده میتوان از طریق دابل
کلیک روی & Route Static Timing Analyze Post-Place از
زیر مجموعه Place & Route اقدام کرد. در شکل 3-1، محل این گزینه نمایش داده
شده است.

شکل 3-1: نحوه بررسی
نتایج زمانی مربوط به ایمپلیمنت طرح
برای محاسبه تاخیر در مدارهای ترکیبی، میتوان ورودیها و خروجیهای
طرح را رجیستر و مدار را به یک مدار ترتیبی تبدیل کرد. سپس مدار ترتیبی بدست آمده
را مشابه روش ذکر شده در بخش 3-1-1 تحلیل زمانی کرد.
به عنوان مثال فرض کنید یک بلوک جمع کننده به صورت نمایش داده شده در
شکل 3-1 را در اختیار داریم.

|

|
شکل 3-1: یک جمع کننده
که به صورت ترکیبی پیادهسازی شده است.
|
شکل 3-3: اضافه کردن
رجیستر به ورودیها و خروجیهای جمع کننده
|
برای بدست آوردن تاخیر این جمع کننده باید یک فایل جدید ایجاد شود و
مدار نمایش داده شده در شکل 3-3 در آن پیادهسازی گردد. همانطور که در شکل ملاحظه میگردد،
با اضافه کردن سه رجیستر به نامهای a_r، b_r و c_r مدار نمایش داده شده در شکل 3-1 به یک مدار ترتیبی تبدیل شده است.
در شکل 3-4، برنامه مربوط به مدار شکل 3-3 نمایش داده شده است.

شکل 3-4: برنامه VHDL مربوط به
مدار نمایش داده شده در شکل 3-3
بعد از اتمام مرحله Place
& Route، دوره تناوب بدست آمده برای کلاک را
میتوان در فایل با پسوند par مشاهده نمود. همانطور که در شکل 3-5 نشان داده شده است، دوره
تناوب کلاک میتواند حداقل 2.539ns
باشد. این نکته به این مفهوم است که تاخیر جمع کننده برابر با 2.539ns است.

شکل 3-5: قسمتی از نتیجه Place & Route که
حاوی اطلاعاتی در مورد Constraint قرار داده شده برای کلاک میباشد.
7-
تاخیر یک جمع کننده 32 بیتی را برای تراشههای مختلف XC6SLX4 و XC6SLX9 را
بدست آورید
8-
با تغییر تعداد بیتهای جمع کننده رابطه بین تعداد بیتها و
میزان تاخیر را در تراشه XC6SLX9 را بدست آورید.
9-
تاخیر دو مدار زیر را برای محاسبه جمع چهار عدد 32 بیتی
بدست آورید


هدف از این آزمایش، آشنایی با Coreهای
سخت افزاری و نحوه تولید آنها توسط نرم افزار ISE میباشد.
COREها، برنامههای آماده و
سنتز شده ای هستند که میتوان از آنها در برنامههای VHDL استفاده
نمود. نکته ی اصلی اینجاست که امکان دسترسی به source code این برنامهها
وجود ندارد و تنها میتوان از آنها در برنامههای گوناگون استفاده نمود.
COREها به دو دسته تقسیم میشوند:
یک دسته از COREها، از قبل و به صورت
انحصاری توسط شرکتهای سازنده ی FPGA با توجه به ساختار داخلی آن، طراحی و عرضه میشود. مثلا برای
استفاده از حافظه ی داخلی یا میکروکنترلر تعبیه شده در FPGA میتوان از COREهای مخصوص آن استفاده
کرد. در اینجا کد VHDL واقعی وجود ندارد و در واقع Core به عنوان
یک wrapper
برای آن بلوک سخت افزاری میباشد.
دسته ی دوم، برنامههای عمومیای هستند که به زبان توصیف سخت افزار
نوشته و سنتز شدهاند و فایل سنتز شده ی آن عرضه میشود. مثلا برای ارتباط وسایل
جانبی با FPGA میتوان از این دسته COREها استفاده نمود. در اینجا کد VHDL، واقعا
وجود دارد ولی در دسترس نمیباشد. ( ممکن است شرکتهای سازنده ی FPGA، این دسته
از COREها را هم تولید نمایند.)
حداقل به دو فایل برای استفاده از COREها در برنامه ی VHDL نیاز است: فایل سنتز شده و Component
مورد استفاده (یعنی اطلاع از نام پورتها و مشخصات آنها)
استفاده از COREها در نرم افزار ISE را با یک
مثال پی میگیریم:
ابتدا یک پروژه ی جدید با نام Multiply ایجاد میکنیم:

حالا در پروژه، روی گزینه ی New Source... کلیک میکنیم:

حال در صفحه ی باز شده، گزینه ی IP (CORE Generator & Architecture Wizard) را انتخاب میکنیم:


و Next را میزنیم؛ حال پنجره ی New Source Wizard باز میشود:

در اینجا فهرست کاملی از COREهای تعبیه شده در نرم
افزار وجود دارد که میتوان برای برنامههای گوناگون از آن استفاده نمود. در قسمت Search IP Catalog هم
میتوان CORE
مورد نظر را جستجو کرد.
در اینجا ما برای پیادهسازی یک ضرب کننده ی 8بیتی، از شاخه ی Math Functions، گزینه
ی Multiplier را
انتخاب میکنیم.


Next را میزنیم. حالا پنجره ی دیگری باز میشود که در
آن میتوان خصوصیات CORE انتخابی را مشاهده و مدیریت نمود:

با انتخاب دکمه Datasheet میتوان به اطلاعات اصلی CORE دسترسی
یافت. در این مثال خاص میتوان مثلا اندازه ی هر یک از عناصر ضرب شونده (PORTA و PORTB) را تعیین نمود که در اینجا ما هر دو را 8بیتی در نظر میگیریم و
اعداد را علامتدار انتخاب میکنیم. و Next را میزنیم.

در این صفحه جدید میتوان خصوصیات دیگری را تنظیم نمود. مثلا در قمست Multiplier Construction میتوان تعیین کرد که پیادهسازی ضرب کننده چگونه باشد. ما در اینجا Use Mults را
انتخاب میکنیم. در این حالت پیادهسازی ضرب کننده به وسیله ی ضرب کنندههای داخلی
FPGA
انجام میپذیرد. در این پنجره، همچنین گزینههایی راجع به بهینه سازی وجود دارد.
دکمه ی Next را
میزنیم.

در پنجره آخر هم اطلاعاتی وجود دارد. مثلا میتوان اندازه ی خروجی ضرب
کننده را تعیین نمود که در اینجا با توجه به اندازه ی عناصر ورودی (8 بیتی)، اندازه
ی خروجی به طور پیش فرض 16 بیت در نظر گرفته شده که البته میتوان آن را تغییر
داد.
نهایتا دکمه ی Generate را میزنیم. همه ی فایلهای مورد نیاز در پوشه ای به نام ipcore_dir
قرار دارد. از مهمترین فایلهایی که در این پوشه ساخته میشود عبارتند از فایلی با
پسوند vho و
دیگری با پسوند veo. این دو فایل به ترتیب حاوی تعریف component و نحوه Port Map
کردن Core
ایجاد شده به زبانهای VHDL و Verilog میباشد.



همچنین در پنجره ی پایینی، گزینه ی CORE Generator
ایجاد میشود که قسمتهای مختلفی را برای مدیریت و استفاده از CORE به دست میدهند.
هم اکنون فایل سنتز شده ی موردنیاز نیز به طور خودکار ایجاد شده است. همان طور که
گفته شد غیر از فایل سنتز شده به مشخصات پورتهای CORE نیز
نیازمندیم. این اطلاعات در قسمت View
HDL Instantiation Template وجود دارد.
با دوبار کلیک کردن روی این گزینه، یک فایل متنی باز میشود که حاوی اطلاعات پورتها
برای افزودن Component و سپس Port Map کردن است:

حالا میتوان یک برنامه به زبان VHDL به صورت top module
نوشت و از CORE به عنوان یک Component استفاده کرد. به همین منظور یک فایل VHDL به نام top میسازیم و کد آن را به این صورت دستکاری میکنیم:

حالا میتوان با استفاده از یک برنامه Test Bench، برنامه ی
بالا را شبیهسازی نمود. اگر در برنامه TestBench دستورات زیر را قرار دهیم:

آنگاه نتیجه شبیهسازی به صورت زیر خواهد شد:

همان طور که در نتیجه شبیهسازی مشاهده میشود تاخیر ضرب کننده 5
نانوثانیه بوده و ضرب دو عدد 3 و 4 و همچنین ضرب اعداد 12- و 83 (در مبنای 10) را
به درستی انجام داده است.
10-با استفاده از Core ضرب کننده و
جمع کننده در نرم افزار ISE، مدار زیر را طراحی و شبیهسازی کنید.

11-با اضافه کردن Constraint
برای کلاک، و اضافه کردن رجیستر در ورودی و خروجیهای مدار فوق، حداکثر فرکانس کلاک
برای مدار فوق را بدست آورید.
12-فایل ucf برای طرح
بالا ایجاد کنید و سپس اعداد را از طریق DIP switchهای روی بورد به سیستم اعمال کنید و نتیجه را روی 16 LED مشاهده
کنید و نتیجه را به مسئول آزمایشگاه نشان دهید.
5 آزمایش پنجم:
آشنایی با Block RAMها و
زمانبندی خواندن و نوشتن در آنها
5-1
پیش آگاهی
هدف از این آزمایش، آشنایی با Block Ramها
و نحوه
تولید آنها توسط نرم افزار ISE میباشد و زمانبندی خواندن و نوشتن در آن و شبیهسازی
آن میباشد. Block Ramها قطعات سخت افزاری هستند کهRam را پیادهسازی میکنند و در اکثر طرحهای
سخت افزاری به کار میروند که معمولا در اندازههای 2کیلو بایتی(18کیلو بیت) میباشند.
Block Ram یکی از اجزای اصلی FPGA است. در مشخصات FPGAها دو نوع Ram وجود دارد: Distributed Ram و Block Ram.
Distributed Ram حافظه
ی RAMاست که از کنار هم قرار دادن LUTهای FPGA ایجاد
میشود. در مواردی که بخواهیم از حافظههای کوچک استفاده کنیم به کار میرود.
ابتدا یک پروژه جدید با نام blkram_exe1 ایجاد میکنیم.

برای ایجاد یک block ram، باید یک core جدید ایجاد
کنیم. همانند آزمایش 4 مراحل زیر را تکرار میکنیم.
بر روی پروژه کلیک راست کرده و گزینه new source را انتخاب میکنیم.
در پنجره باز شده IP(CORE
Generator & Architecture Wizard) را انتخاب میکنیم
و نام core را
در قسمت file name
مینویسیم. در اینجا حافظه مورد نظر ما یک حافظه 1k*8bit است.
بنابراین نام حافظه را blkram1kx8bits قرار میدهیم.

در قسمت New Source Wizard، با
جستجوی عبارت memory، Block Memory
Generator را انتخاب میکنیم.

بلاک دیاگرام Ram در سمت چپ تصویر نشان داده شده که پایههای فعال و غیرفعال آن در
شکل مشخص شده است. در این حافظه پورت خواندن و نوشتن از یکدیگر مجزا هستند و به
همین دلیل به سیگنال کنترلی read نیازی ندارد. خواندن همیشه فعال است. آدرس را که قرار دهیم، Data پس از یک CLK برابر
محتویات آن خانه از حافظه میشود. به این صورت که Data همیشه یک CLK شیفت دارد.
اگر در یک CLK داده را قرار دهیم، در CLK بعدی میتوانیم داده را استفاده
کنیم.
در اینجا ما دو نوع Interfaceداریم: AXI وNative. اگر AXI را
انتخاب کنیم، Interface مستقیما بهAXI Busمتصل میشود که ما در
اینجا از آن استفاده نمیکنیم و Native را انتخاب میکنیم.

Memory Type:
با انتخاب هر یک از انواع حافظه زیر، پورتهای بلاک دیاگرام سمت چپ
تغییر میکند. Single Port Ram: فقط یک Busآدرس دارد. همزمان نمیتوان به خانههای
مختلف حافظه
دسترسی داشت
ولی میتوانیم در یک خانه از حافظه بنویسیم و بخوانیم.
True Dual
port: یک حافظه با دو پورت کاملا مجزا
است. ( پورت A و B)
Simple dual
port: پورت A امکان
خواندن و نوشتن را دارد ولی پورت B فقط امکان خواندن دارد.
Single port
Rom: یک مقدار اولیه به آن داده میشود
و همه از همان مقدار اولیه استفاده میکنند و دیگرنمیتوان در آن نوشت.
Dual port Rom: دو پورت آدرس دارد و امکان نوشتن ندارد.
در این مرحله Single
Port Ram را انتخاب میکنیم.
ECC(Error Correction
Code):
چون در حافظهها امکان خطا وجود دارد، به ازای هر 8 بیت، یک بیت اضافه میکند و parity را
در آن ذخیره میکند. گفتیم حافظهها معمولا 2کیلوبایتی هستند که برابر 16 کیلو بیت
است که با درنظرگرفتن دو بیت برای parity، 18کیلوبیت خواهیم داشت.
Byte Write Enable:
برای حافظههایی که بیشتر از 8 بیت عرض دارند، مشخص میکند که در یک
سطر کدام بایتها نوشته شوند.
در قسمت Algorithm نیز با توجه به هدف پروژه، یکی از سه مورد آن را انتخاب میکنیم.

در اینجا میخواهیم یک حافظه 1k x 8bit را پیادهسازی کنیم.
بنابراین عرض داده را 8 بیتی و تعداد کلمات را 1024=
(10 خط آدرس)قرار میدهیم.
Operating Mode اولویتهای ReadوWrite را
مشخص میکند. ما در اینجا write first را
انتخاب میکنیم.
در قسمت Enable نیز always enable را
انتخاب میکنیم.

در مرحله بعد، قسمتOptional output Register
مشخص میکند خروجی که از حافظه میگیریم یک طبقه رجیستر شود یا خیر.
معایب رجیستر کردن: تعدادی فلیپ فلاپ مصرف میشود و یک فاز تاخیر بیشتر
داریم.
حسن رجیستر کردن:
(Pipelining) باعث میشود که تاخیر خارج شدن داده از حافظه، از تاخیرهای بعدی
جداشود.
Memory Initialization: میتوان
به حافظه مقدار اولیه داد. اگر تیک Load init file را فعال
کنیم، میتوانیم
محتویات خانههای حافظه را از یک فایل متنی با پسوند.COE بگیریم.
در قسمت fill remaining
memory locations نیز میتوان
بقیه خانههای حافظه که در فایل متنی مقدار دهی نشدهاند را مقداردهی کرد. تیکها
را فعال نمیکنیم تا مقدار اولیه به صورت پیش فرض، صفر درنظر گرفته شود.

در این مرحله مشخص میکنیم که پایه reset داشته
باشیم یا خیر. معمولا reset برای حافظهها استفاده نمیشود. با فعال کردن reset، باید
نوع آن: سنکرون یا آسنکرون بودن را تعیین کرد. در اینجا reset را فعال نمیکنیم.

مرحله بعد گزینههایی برای شبیهسازی دارد. در هنگام ایجاد هر core یک functional model
نیز برای آن ایجاد میشود. در اینجا میتوان مشخص کرد که functional model روی چه چیزهایی warning
دهد. وقتی All فعال باشد زمان شبیهسازی بیشتر میشود ولی برای طرحهای کوچک این
زمان بسیار ناچیز است.


قسمت Information نیز گزارشی از ram که قرار است ایجاد شود را به ما میدهد. همان طور که در شکل
مشاهده میشود ما دو نوع block ram داریم: 9k و 18k. Read Latency مدت زمان
ظاهرشدن داده، بعد از قراردادن آدرس آن را مشخص میکند که در اینجا در یک سیکل این
کار انجام میشود. Address Width
نیز نشان دهنده این است که خطوط آدرس 10 بیتی هستند.
حال
core را
Generate میکنیم.
اکنون باید یک فایل top_blkram
بسازیم و blkram را در آن port map کنیم.
New -> new source -> VHDL Module
پورتهای ورودی و خروجی را مانند شکل زیر مینویسیم و next میزنیم.

حال باید blkram را در فایل top، port map کنیم. قسمتهای
مشخص شده در شکل زیر را به فایل top
اضافه میکنیم. به این صورت که بر روی blkram کلیک کرده، فایل view HDL Instantiation tempرا انتخاب کرده و محتویات آن را copy میکنیم.
در این مرحله پورتها نیز اصلاح میشوند. باید به تفاوت نوع پایههای write(wr) و write enable(wea)
توجه داشته باشیم. wr از نوع STD_LOGIC و wea از نوعSTD_LOGIC_VECTOR
است. در نتیجه یک سیگنال برداری را تعریف میکنیم.(سیگنال
wr_sig)
برای مقدار
دهی حتما باید index سیگنال گذاشته شود. فایل را save میکنیم.




شبیهسازی حافظه:
حال باید یک فایل testbench بسازیم و نام آن را TB_top_blkram انتخاب میکنیم.
خط زیر را به فایل TB اضافه میکنیم تا آدرس را برابر با صفر قرار دهیم.


فایل را ذخیره کرده و simulation را انجام میدهیم. نتیجه شبیهسازی در شکل صفحه بعد مشخص است:

در مرحله بعد میتوانیم مقادیر را خودمان وارد کنیم. مثلا بخواهیم در
یک خانه حافظه بنویسیم و محتویات آن خانه از حافظه را بخوانیم پس باید پایههای
حافظه را مقداردهی کنیم. در
اینجا میخواهیم در خانه صفر حافظه عدد AA را write
کنیم. پایه write را 1 میکنیم و بعد از 10ns
دوباره آن را صفر میکنیم.


اگر در کد تغییراتی دادیم و
خواستیم در شبیهسازی هم اعمال شود Re_launchرا میزنیم. در این حالت شبیهساز را مجددا run کرده ایم.
نتیجه شبیهسازی:


در مثالی دیگر بعد از اینکه wr را
صفر کردیم، آدرس را عوض میکنیم. به اندازه 100ns، wait انجام میدهیم و سپس مجددا آدرس را به خانه صفر برمیگردانیم.


دوباره re_launch میکنیم:

ابتدا عمل write انجام میشود.سپس از خانه صفر حافظه عمل خواندن صورت میگیرد و 100ns بعد از خانه یک حافظه عمل خواندن انجام میشود. داده در لبه
بالارونده روی باس قرار میگیرد. نتایج شبیهسازی در شکل بالا مشخص است.
13-یک حافظه BlockRAM با اندازه 512x8 ایجاد کنید. سپس یک برنامه VHDL با نام mem1024x16
ایجاد کنید و با Port Map کردن چهار عدد از حافظه بلاک رم ایجاد شده، یک حافظه 1024x16 ایجاد کنید و آن را شبیهسازی کنید(در شبیهسازی در 2 آدرس حافظه
بنویسید و سپس از همان 2 آدرس بخوانید، آدرسها را به گونه ای انتخاب کنید که در هر
یک از چهار حافظه عملیات خواندن و نوشتن انجام شده باشد)
14-با استفاده از دیتاشیت بلاک رم، فرمت
فایلهای coe را
مطالعه کنید سپس یک فایل coe ایجاد کنید و برای یک حافظه با مشخصه 8x40 یک
فایل coe
ایجاد کنید(محتویات آدرس صفر حافظه را شماره دانشجویی خود قرار دهید و برای آدرسهای
بعدی به ترتیب یک واحد به شماره دانشجویی خود اضافه کنید) سپس یک حافظه BlockRAM با
اندازه 8x40 ایجاد کنید و آن را با فایل coe ایجاد شده،
مقداردهی اولیه کنید. سپس یک TestBench بنویسید که با خواندن محتویات حافظه، از صحت مقادیر اولیه اطمینان
حاصل کنید.
15-با استفاده از نرم افزار ISE و ایجاد
انواع Coreهای حافظه از نوع distributed، و
سنتز و ایمپلیمنت آن، رابطه ای بین تعداد بیتهای حافظه از این نوع و تعداد LUTهای مصرفی بدست آورید. (راهنمایی: یک حافظه 64x16 ایجاد کنید سپس یک فایل top برای آن بنویسید و آنرا ایمپلیمنت
کنید و با باز کردن فایل با پسوند par، تعداد LUTهای مصرف شده را استخراج
کنید سپس این کار را برای چند حافظه با اندازههای دیگر تکرار کنید و رابطه را
بدست آورید)
6-1 پیش آگاهی
هدف از این آزمایش، آشنایی با FIFO و نحوه تولید آنها
توسط نرم افزار ISE میباشد و زمانبندی خواندن و نوشتن در آن و شبیهسازی
آن میباشد.
FIFO، سخت افزاری است که برای ذخیره و بازیابی دادهها به صورت صف مورد
استفاده قرار میگیرد. پیادهسازی آن به صورت BlockRAM دو پورته
است. در FIFO با هر بار خواندن شمارندهی آدرس خواندن جلو میرود و با هر بار نوشتن
آدرس یک واحد زیاد میشود.
برای پیادهسازی FIFO از BlockRAM یا حافظهی Distributed استفاده میشود. در سختافزار FIFO شرط پربودن و خالی بودن آن وجود دارد که میتوان آنها را با رجیسترها
تعیین کرد.
1-
صف: بافر کردن دادهها به صورت موقت؛ مثلاً اگر جایی سرعت
پردازش اطلاعات با سرعت ورودی یا خروجی اطلاعات یکی نیست، میتوان از این امکان FIFO
استفاده کرد. یا در بحث شبکه، وقتی بستههای دریافتی کامل شد آنها را برای پردازش
بفرستد. و برای این مورد اگر در FIFO بنویسیم بهتر از حافظه است چون نیازی نیست آدرس آن را خودمان
تولید کنیم.
2-
کاربرد مهمتر این است که وقتی در مدار دو کلاک داشته
باشیم و بخواهیم بین این دو قسمت از برنامه داده جابهجا کنیم، بین آنها یک FIFO
قرار میدهیم و قسمت اول با کلاک خودش در FIFO مینویسد و دومیبا کلاک خودش از FIFO
میخواند، بنابراین مشکل عدم پایداری به وجود نمیآید.
مورد دیگر: در پردازش اطلاعات شبکه کلاک پردازش شبکه و کلاک پردازش
داخلی متفاوت است، برای حل این مشکل هم از FIFOاستفاده
میکنیم.
ابتدا پروژه جدیدی ایجاد میکنیم و سپس راست کلیک روی پروژهی ساخته
شده و New Source را انتخاب میکنیم و گزینهی
) IP(CORE Generator & Architecture Wizard را انتخاب میکنیم. در اینجا میخواهیم یک FIFO
با اندازه ی 16K که هر کلمهی
آن 8 bit است
بسازیم.


پس از آن در قسمت search عبارت FIFO را جستجو
میکنیم و Fifo Generator را انتخاب کرده و Next را انتخاب میکنیم.

کمترین تعداد پورتهای Fifo
، 6 تا است که در شکل نشان داده شده
اگر RST را حذف کنیم بقیه پورتها 6 تا هستند. ( به غیر از clock
). در این مرحله Native را انتخاب کرده و سپس Next.

در این قسمت گزینههای مختلفی میبینید که چهار گزینهی اول مربوط به
ساختن Fifo با یک کلاک
هستند که Synchronous Fifo است. و سه گزینهی بعد مربوط به ساختن Fifo با دو کلاک است که ASynchronous
Fifo است.
همچنین در اینجا نوع حافظهای که میخواهید Fifo با آن
ساخته شود را انتخاب میکنید. برای مثال ساختن با
Shift register ، Block RAM ،Distributed RAM.
-
Built-in-FIFO، Fifoی است که خودش پیادهسازی
شده و مدارها را دارد.
بر اساس محدودیت سخت افزاری که داریم، اینکه FIFO با چه
ساخته میشود را انتخاب میکنیم. مثلا اگر BlockRAMها را میخواهیم در جای دیگر استفاده کنیم، در اینجا این گزینه را
انتخاب نمیکنیم. به طور کلی همهی اینها از نظر سرعت یکسانند، بسته به مدار یکی
را انتخاب میکنیم. که در اینجا همان گزینهی اول را انتخاب کنید و سپس Next.

در این قسمت ابتدا نوع خواندن (Read Mode) را انتخاب میکنیم.
-
Standard
FIFO: با انتخاب این گزینه تا زمانی که
سیگنال Read
را فعال نکنیم داده روی خروجی نمیآید و وقتی سیگنال Read را فعال کردیم، یک پالس بعد داده روی خروجی میآید.
-
First-Word
Fall- Through: با انتخاب این گزینه اولین
داده به محض انجام شدن Write روی خروجی میآید و نیازی به فعال کردن سیگنال Read
برای آن نیست ولی دادههای بعدی مانند گزینهی اول، پس از فعال شدن سیگنال Read روی خروجی ظاهر میشوند.
در اینجا ما گزینهی اول را انتخاب میکنیم چون فرقی بین کلمهها نمیگذارد!
Built-in FIFO
Options:
اگر در قسمت کلاک، کلاکها را متفاوت در نظر میگرفتیم در این قسمت
باید اندازهی آنها را مشخص میکردیم.
Data Port
Parameters:
در این قسمت طول کلمه و تعداد کلمات را برای Fifo انتخاب میکنیم.
و سپس Next.

کاربرد Flagهای مختلف:
-
Write
Acknowledge Flag:
اگر قبل از انجام عمل write
، پر بودن Fifo را چک
کردیم نیازی به این Flag نیست. اما اگر اینکار را انجام ندادیم، با چک
کردن این Flag متوجه میشویم که عمل نوشتن به درستی انجام شده
است یا خیر. چون ممکن است Fifo پر بوده باشد و ما عمل نوشتن را انجام دهیم و نوشتن انجام نگیرد.
-
Overflow
Flag:
وقتی Fifoپر باشد و ما عمل
نوشتن را انجام دهیم این پرچم فعال میشود.
-
Valid
Flag:
وقتی داده روی پورت قرار گرفت، یک کلاک بعد این Flag فعال میشود
و با فعال شدن آن میتوانیم داده را بخوانیم.
-
Underflow Flag:
وقتی Fifo خالی بوده و ما عمل خواندن انجام دادیم، این Flag
فعال میشود.
در این مرحله میتوانیم هر کدام از پرچمهایی که میخواهیم را فعال
کرده و سپسNext.

در این مرحله میتوانیم انتخاب کنیم که میخواهیم Reset Pin
داشته باشیم یا نه و Reset ما به صورت Synchronous باشد یا Asynchronous.
-
Full
Flags Reset Value: اگر مقدار یک را انتخاب
کنیم، یعنی وقتی Reset فعال شد، Full یک میشود
و اجازه ی نوشتن داده نمیشود و در واقع کسی که داده مینوشته دیگر نمینویسد. و
در قسمت پایین آن انتخاب میکنیم که وقتی Reset فعال شود، چه
مقداری روی خروجی نشان داده شود.
-
Programmable
Full Type: وقتی میخواهیم فعال شدن full
زودتر به ما اطلاع داده شود. و در واقع تعداد کلمات به حدی که میخواهیم رسید full
فعال شود.
-
Full
Threshold Assert Value: مقداری که
در این قسمت تعیین میکنیم، وقتی تعداد کلمات به این مقدار رسید به ما هشدار میدهد.
-
Full
Threshold Negate Value: وقتی
تعداد کلمات به این مقدار برسد، full فعال میشود.
همچنین با انتخاب گزینهی Single
Programmable Full Threshold Input Port
از قسمت Programmable Full Type میتوانیم، از طریق دادن ورودی میتوانیم این
محدوده را مشخص کنیم و از این طریق میتوان به صورت پویا محدوده را عوض کرد.
در مورد Empty هم به همین صورت است. مثلا تعیین میکنیم اگر دو
کلمه نوشته شد، Empty غیر فعال شود.

کاربرد Data Count به این صورت است که با دانستن ظرفیت و مقدار Data Count میتوانیم بفهمیم چقدر از Fifo پر شده است. در این قسمت همچنین میتوانیم تعیین کنیم طول Data Count چقدر باشد. برای مثال اگر طول داده در Fifo با 14 رسید
یکی بشمارد.

اگر در مرحلهی اول گزینهی AXIS را به جای Native انتخاب میکردیم تنظیمات این مرحله قابل تغییر بود.

در مرحله ی آخر یک سری اطلاعات از Fifo که قرار
است ساخته شود به ما میدهد. در این مرحله Generate را انتخاب میکنیم و Fifo با تنظیمات انتخاب شده توسط ما ساخته میشود.

پس از ساخته شدن Fifo یک فایل FIFO_TOP برای آن، با
مشخصات زیر میسازیم.

با انتخاب فایل Fifo ساخته شده و باز کردن فایل View HDL Instantation Template میتوانید قطعه کد Component و Port Map را در فایل FIFO_TOP کوپی کنید.

شکل زیر تغییرات انجام شده پس از کپی کردن PortMap و Component در فایل FIFO_TOP را هم نشان میدهد.

پس از آن از فایل TOP یک فایل TestBench میسازیم. با دادن مقادیر زیر میتوانیم هر یک از دستورات Reset،
Write
و Read را امتحان کنیم.

در شبیهسازی که طبق مقادیر داده شده در شکل قبل انجام میشود، مشاهده میکنید
که هم زمان با فعال شدن Reset،Full هم فعال شده و تا 3 کلاک پس از غیر فعال شدن Reset هم فعال باقی مانده است. و این نشان دهندهی این است که بلافاصه بعد از
Reset کردن نمیتوانیم در Fifo عمل نوشتن را انجام دهیم.

در فایل TestBench اگر لیست حساسیت در Process بنویسیم، دیگر نیازی به نوشتن Wait نیست و مدار Syncron با کلاک عمل میکند و مشکلی به وجود نمیآید.
مانند شکل زیر اگر در فایل Test Bench لیست
حساسیت بنویسیم دیگر نیازی به استفاده از wait نیست. توجه شود که
در شکل زیر از counter برای شمردن استفاده شده است. روش دیگر برای
نوشتن لیست حساسیت استفاده از state
Machine است که counter خود یک state Machine است. توجه کنید در اینجا چون از عملگر + استفاده شده باید USE ieee.std_logic_unsigned.ALLبه عنوان کتابخانه
اضافه شود. در این قسمت مقدار wr صفر داده شده و در زمآنهایی که میخواهیم فقط
مقدارش یک میشود. در مورد rd این طور نیست و وقتی که مقدارش را یک کنیم تا وقتی که دوباره با
شرط آن را تغییر نداده ایم، یک باقی میماند.
در شبیهسازی میتوانید متوجه این موضوع شوید.

1-
برنامهای بنویسید که مدار شکل روبرو را پیادهسازی کند.
ساختار داخلی این مدار به این صورت است که یک فیفو با عمق 128 و عرض 16 بیت است که
امکان نوشتن دیتا از بیرون را دارد و هر وقت لبه بالارونده روی سیگنال Start
مشاهده شد، خواندن از فیفو شروع میشود و تا موقعی که فیفو خالی شود مقادیر داخل
آن را با همدیگر جمع میزند.

برای مدار فوق یک TestBench
بنویسید که مقادیر 1 تا 10 را در فیفو بنویسد سپس یک لبه بالارونده روی Start
ایجاد کند.
2-
برنامهای بنویسید که مدار شکل روبرو را پیادهسازی کند.
در این مدار از دو بلاک رم از نوع Simple Dual Port با
اندازه 128x8 استفاده شده است. پورت A از بلاک رم
BRAM1 از
بیرون قابل دسترس است و میتوان در آن عمل نوشتن را انجام داد. عملکرد مدار به این
گونه باشد که با دیدن لیه بالا رونده روی پورت Copy، محتویات
آدرسهای 0 تا 10 بلاک رم 1 خوانده شده و با عدد 55 جمع زده میشود و در بلاک رم 2
نوشته میشود. با دیدن لیه پایین رونده روی پورت Show، محتویات
آدرسهای 0 تا 10 از بلاک رم2 خوانده شده و روی پورت Dout گذاشته میشود.
برای مدار فوق یک TestBench
بنویسید که مقادیر 100 تا 110 را در آدرسهای صفر تا 10 بلاک رم 1 بنویسد و سپس یک
لبه بالارونده روی Copy ایجاد کند و نهایتا یک لیه پایین رونده روی سیگنال Show ایجاد کند.

7 آزمایش هفتم:
آشنایی با ردههای مختلف شبیهسازی:
فانکشنال و Post Route Simulation
7-1 پیش آگاهی
هدف از این آزمایش، آشنایی با ردههای مختلف شبیهسازی با استفاده از
نرم افزار ISE میباشد.
برای اینکه از صحت عملکرد یک برنامه VHDL اطمینان
حاصل شود باید آن را در مراحل مختلف شبیهسازی کرد. علت این امر را با یک مثال
روشن میکنیم. ممکن است شما برنامه VHDL خود را شبیهسازی کنید و خروجیهای مدار دقیقا همان نتایج مورد انتظار
شما باشد ولی وقتی که برنامه سنتز میشود، قسمتهایی از برنامه حذف گردند (مثلا از
دستورات تاخیر در برنامه استفاده کرده باشید که قابل سنتز نیستند) بنابراین وقتی
که مدار خود را روی FPGA پیادهسازی میکنید، نتیجه مورد انتظار تولید
نخواهد شد. برای اینکه مشکلاتی از قبیل فوق رخ ندهد و شما بتوانید از صحت عملکرد
مدار خود بعد از انجام مراحل سنتز، MAP و Place and Route
اطمینان حاصل کنید، در نرم افزار ISE این قابلیت فراهم شده است تا برنامه خودر را در ردههای زیر شبیهسازی
کنید:
الف) شبیهسازی فانکشنال یا رفتاری (Behavioral simulation)
ب) شبیهسازی بعد از مرحله Translate (Post Translate simulation)
ج) شبیهسازی بعد از انجام مرحله MAP (Post MAP simulation)
د) شبیهسازی بعد از مرحله Place and Route (Post Route simulation)
در ادامه با ذکر یک مثال، روش انجام شبیهسازی در ردههای مختلف بیان میشود.
ابتدا به عنوان نمونه یک جمع کننده 8 بیتی ایجاد کرده و سپس تست بنچ
مربوط به آن را مینویسیم.