مقدمه
در قسمت اول این سری آموزشی به شکل خلاصه مبانی اینترفیس AXI را با هم مرور کردیم و مهمترین مفاهیم و اصطلاحات کلیدی در AXI3/AXI4 آشنا شدیم. در قسمت دوم به یک سؤال مهم پاسخ دادیم و در رابطه با متدهای استاندارد شبیه سازی اینترفیس AXI که توسط Xilinx در محیط توسعه Vivado ارائه شده است، توضیحاتی ارائه کردیم. در قسمت سوم این سری آموزشی از پایگاه دانش هگزالینکس قصد داریم با اضافه کردن AXI VIP به یک پروژه در Vivado فرایند شبیه سازی اینترفیس AXI4-Lite با AXI VIP را به طور کامل بررسی کنیم. در انتهای کار نیز نگاهی دقیقتر به سیگنالهایی که در تراکنشهای AXI4-Lite شرکت دارند میاندازیم و شکل موجهای قابل نمایش در پنجره Waveform را به دقت بررسی میکنیم. اگر علاقمند به این چالش نسبتاً پیچیده هستید، تا انتهای این مطلب با ما همراه باشید.
استفاده از AXI-VIP به عنوان AXI4-Lite Master
برای شروع کار لازم است فایلهای پروژه را از اینجا دانلود کنید. در ادامه میتوانید از یکی از دو گزینه زیر استفاده کنید و یک پروژه ساده در Vivado بسازید. انتخاب با شماست، با هر روشی که برای شما مناسب تر است کار را شروع کنید. توجه داشته باشید که در صورت انتخاب گزینه دوم باید از نسخه 2018.2 یا 2019.2 محیط توسعه Vivado استفاده کنید.
- گام به گام از مرحله اول با ما همراه شوید و شروع به ساخت پروژه از ابتدا بکنید.
- با استفاده از فایلهای اسکریپت آماده و کنسول Tcl در محیط توسعه Vivado به صورت اتوماتیک پروژه مورد استفاده در این آموزش را بسازید.
ما در این پروژه به بورد وابسته نیستیم و قصد داریم اتصاللات واقعی روی بورد را شبیه سازی کنیم این دقیقاً همان چیزی است که از آن به عنوان محیط تست مجازی نام برده میشود و در قسمت قبلی به آن اشاره کردیم.
ساخت پروژه (گام به گام)
در صورتی که تمایل دارید به صورت اتوماتیک پروژه شما ساخته شود، نیازی به مطالعه این بخش ندارید. کافیست کمی به پایین اسکرول کنید و به بخش بعدی بروید.
۱- از منوی استارت روی گزینه Vivado Design Suite کلیک کنید و Vivado را باز کنید. به این ترتیب ویزارد ساخت پروژه به شما نمایش داده میشود. یک پروژه جدید بسازید و نام آن AXI_Basics_3 قرار دهید. در صفحه Project Type گزینه RTL Project را انتخاب کنید و تیک گزینه Do not specify sources at This Time را غیرفعال کنید.
فایل تست بِنچ اولیهای را که برای این پروژه آماده کردیم، از اینجا دانلود کنید. در صفحه Add Sources روی گزینه Add Files کلیک کنید و فایل AXI_GPIO_tb.sv را به پروژه اضافه کنید. دقت کنید گزینه Copy Source فعال باشد و در قسمت HDL Source for نیز مقدار Simulation Only تنظیم شده باشد. در آخرین صفحه ویزارد نیز برای تراشه هدف از قطعه شماره xc7z020clg484-1 استفاده کنید.
۲- بعد از باز شدن پروژه از صفحه Flow Navigator روی گزینه Create Block Design کلیک کنید. نام بلوک دیاگرام را AXI_GPIO_Sim قرار دهید.
۳- با کلیک روی علامت + در وسط صفحه یک AXI GPIO به صفحه خالی بلوک دیاگرام اضافه کنید و تنظیمات زیر را روی آن اعمال کنید.
GPIO:
All Outputs (selected)
GPIO Width = 1
Default Output Value = 0x00000000
Enable Dual Channel (selected)
GPIO2:
All Inputs (selected)
GPIO Width = 1
۴- بعد از اعمال تنظیمات روی بلوک AXI GPIO مطابق شکل (۳) پورتهای آن را تعریف کنید. روی پورت s_axi_aclk راست کلیک کنید و گزینه Make External را انتخاب کنید. یک پورت ورودی برای شما ساخته میشود. از پنجره Block Interface Properties در سمت چپ نام این پورت را به aclk تغییر دهید. همین کار را برای سایر پورتها هم انجام دهید و تنظیمات زیر را روی پورتها اعمال کنید.
Inputs:
s_axi_aclk : aclk
s_axi_areset : areset
gpio2_io_i : switch_1
Outputs:
gpio1_io_o : led_1
برای نهایی سازی پروژه در پنجره Source روی نام بلوک دیاگرام راست کلیک کنید و گزینه Create HDL Wrapper را انتخاب کنید.، در پنجره دیالوگی که باز میشود، گزینه پیش فرض را بپذیرید و روی OK کلیک کنید.
کار ساخت بخش اول پروژه همینجا به پایان میرسد. طرح نهایی شما در نهایت مشابه شکل (۴) خواهد بود. برای ادامه این آموزش میتوانید از مرحله پنجم کار را دنبال کنید و پروژه خودتان را تکمیل کنید.
ساخت پروژه (اتوماتیک)
برای ساخت پروژه به صورت اتوماتیک نیاز داریم فایلهای پروژه را دانلود کنیم و با استفاده از فایلهای Tcl دستورات ساخت پروژه را اجرا کنیم.
۱- فایلهای پروژه را از اینجا دانلود کنید و آنها را در یک پوشه مناسب از حالت فشرده خارج کنید.
۲- از منوی استارت روی Vivado Design Suite کلیک کنید و Vivado را باز کنید.
۳- از کنسول Tcl در پایین صفحه خوشامد گویی Vivado وارد پوشهای که دانلود و Extract کردید، بشوید. برای این کار باید از دستوری به صورت زیر استفاده کنید. توجه کنید که برای آدرس دهی باید از / به جای \ استفاده کنید.
cd AXI_Basics_3
۴- در کنسول Tcl فایل create_proj.tcl را اجرا کنید. برای این کار باید از دستور Tcl به صورت زیر استفاده کنید.
source ./create_proj.tcl
این دستورات در نهایت یک پروژه Vivado همراه با یک Block Design میسازد که در آن یک AXI GPIO فراخوانی و پیکره بندی شده است. این AXI GPIO فقط یک خروجی دارد که به کانال یک این IP متصل شده است و کانال یک در ادامه به یک LED متصل شده است. کانال دو نیز به عنوان ورودی تعریف شده است. ما در هر لحظه وضعیت این سوئیچ را میخوانیم و مقدار آن را در کنسول نمایش میدهیم. خاموش یا روشن بودن LED نیز در کنسول گزارش میشود.
در واقع با این کار اتصالات واقعی روی بورد را شبیه سازی میکنیم. علاوه بر بلوک دیاگرام با اجرای این اسکریپت Tcl ، تست بنچ مورد نیاز برای شبیه سازی طرح که یک فایل RTL با نام AXI_GPIO_tb.sv است به صورت اتوماتیک به پروژه اضافه میشود.
یک کار جالب تر که میتوانستیم انجام دهیم این بود که با استفاده از تراکنشهای AXI ، سوئیچ ورودی که به کانال دو این IP متصل شده است را مستقیماً برای خاموش یا روشن کردن LED استفاده کنیم. این کار را به عنوان تمرین به شما واگذار میکنیم.
تکمیل پروژه
بعد از ساخته شدن پروژه نوبت به ساخت یک محیط تست مجازی با استفاده از AXI VIP میرسد. اگر با این IP آشنایی ندارید پیشنهاد میکنم ویدئویی که در قسمت دوم این آموزش منتشر شده است را مشاهده بفرمایید.
۵- کار را با هم ادامه میدهیم و یک AXI VIP به پروژه اضافه میکنیم.
۶- روی این AXI VIP کلیک میکنیم و تا پنجره تنظیمات آن برای ما باز بشود، در پنجره تنظیمات چند پارامتر را به صورت زیر تغییر میدهیم. این تغییرات در شکل (۵) نشان داده شده است.
Basic Settings:
Interface mode : MASTER
Protocol (MANUAL) : AXI4LITE
۷- خب حالا وقت آن رسیده است که اینترفیس AXI4-Lite را به AXI VIP که در مد Master پیکره بندی شده است، متصل کنیم. پس پورت Salve از AXI GPIO را به پورت Master از AXI-VIP در بلوک دیاگرامی که با هم طراحی کردیم، متصل میکنیم. پورتهای ریست و کلاک AXI VIP را هم به عنوان پورتهای ورودی تعریف میکنیم، کافی است این دو پورت را به پورتهای areset و aclk متصل کنیم. دیاگرام نهایی در شکل (۶) قابل مشاهده است.
۸- حالا نوبت آن رسیده است که آدرسها را ویرایش کنیم پس در بالای صفحه Block Design، تب Address Editor را انتخاب میکنیم. برای تنظیم آدرسها دو گزینه پیش رو داریم، میتوانیم این کار را به صورت دستی یا اتوماتیک انجام دهیم. ما اینجا کار ساده تر را انجام میدهیم و به صورت اتوماتیک آدرسها رو تنظیم میکنیم. پس فقط کافیست روی گزینه Auto Assign کلیک کنیم.
۹- در انتهای کار هم بهتر است از تنظیم صحیح آدرس روی مقدار 0x4000_0000 اطمینان حاصل کنیم.
نکته: قسمت بالایی آدرس در این مثال خیلی مهم نیست. چون اگر اینترفیس Slave از AXI_GPIO را بررسی کنیم، میبینیم که فقط ۹ بیت کم ارزش آدرس به AXI VIP متصل شده است.
۱۰- با اتمام کار طراحی بلوک دیاگرام، روی گزینه Validate Design کلیک میکنیم تا صحت بلوک طراحی تأیید شود. طبیعتاً در این مرحله هیچ خطا یا پیام اخطاری نباید وجود داشته باشد. در نهایت بلوک دیاگرام را ذخیره میکنیم.
خب حالا نیاز داریم که فایل تست بنچ را بروز رسانی کنیم و AXI VIP را به صورت مناسبی ویرایش کنیم. برای این کار براساس مستندات Xilinx و دستورالعملهای کدنویسی که در سند PG267 توضیح داده شده عمل میکنیم. باهم مثالی را که در صفحه ۴۶ این سند ارائه شده است، بررسی میکنیم.
دستورالعمل ویرایش تست بنچ
۱۱- فایل تست بنچ را باز میکنیم، نام این فایل AXI_GPIO_tb.sv است. برای این کار به پنجره Source مراجعه میکنیم. این مسأله در شکل (۹) نشان داده شده است.
با توجه به اینکه فایل تست بنچ، از قبل برای این آموزش تهیه شده است. یکسری از سیگنالها و کنترلهای اولیه درون آن قرار داده شده است. مثلاً تعاریف مربوط به سیگنالهای کلاک و ریست را در این فایل در خطوط ۸۲ و ۸۸ قرار داده شده است. علاوه بر این برای خروجی کردن وضعیت LED ها در کنسول از پروسسی که در انتهای تست بنچ نوشته شده است، استفاده میکنیم. در بخشهای مختلفی از کد تست بنچ کامنت نویسی انجام شده است. قصد داریم با هم گام به گام این فایل تست بنچ را ویرایش کنیم و فایل نهایی را بسازیم. یکسری الزامات برای این فایل تست بنچ وجود دارد که باید رعایت شوند. پس ابتدا این الزامات را در ۵ گام بررسی میکنیم.
always @(posedge led_1) begin $display("led 1 ON"); end always @(negedge led_1) begin $display("led 1 OFF"); end
گام اول
اولین گامی که در سند PG267 و در بخش Useful Coding Guidelines and Examples به آن اشاره شده است، ساخت یک module درون تست بنچ System Verilog است. با توجه به اینکه ما یک فایل تست بنچ نیمه آماده داریم. نیازی به اجرای این گام نداریم. پس به گام دوم میرویم.
گام دوم
درگام دوم دو پکیج به نامهای axi_vip_pkg و <component_name<_pkg را فراخوانی میکنیم و از آنها استفاده میکنیم. این دو پکیج به صورت اتوماتیک تولید میشوند و در برگیرنده کلاسها و دیتا تایپهای مورد استفاده در تست بنچ هستند.
نکته: برای پیدا کردن نام کامپوننت <component_name> (که در اینحا منظور نام کامپوننت VIP است) باید از دستور Tcl زیر استفاده کنیم. خروجی حاصل از اجرای این دستور نام کامپوننت AXI VIP مورد استفاده ما خواهد بود.
get_ips *vip*
ما در فایل تست بنچ فرض کردیم که نام کامپوننت AXI_GPIO_Sim_axi_vip_0_0 هست. این نام، به نوعی نام پیش فرض AXI VIP که ما در Block Design اضافه کردیم نیز هست و ما میتوانیم آن را تغییر بدهیم.
۱۲- این چند خط کد را بعد از کامنت Step 2// در خط ۵۸ اضافه میکنیم.
//Step 2 - Import two required packages: axi_vip_pkg and <component_name>_pkg. import axi_vip_pkg::*; import AXI_GPIO_Sim_axi_vip_0_0_pkg::*;
گام سوم
در گام سوم یک agent از نوع Master VIP تعریف میکنیم.
۱۳- این چند خط کد را جایی حدود خط ۱۰۲ بعد از کامنت Step 3// در کد اضافه میکنیم.
// Step 3 - Declare the agent for the master VIP AXI_GPIO_Sim_axi_vip_0_0_mst_t master_agent;
گام چهارم و پنجم
گام چهارم مربوط به تعریف agent است. گام پنجم نیز مربوط به صدور فرمان آغاز به کار این agent هست.
۱۴- این چند خط کد را جایی حدود خط ۱۰۷ در کد بعد از کامنتهای Step 4// و Step 5// اضافه میکنیم.
// Step 4 - Create a new agent master_agent = new("master vip agent",UUT.AXI_GPIO_Sim_i.axi_vip_0.inst.IF); // Step 5 - Start the agent master_agent.start_master();
خب کار بررسی الزامات به پایان رسید و همه چیز آماده شده است. میتونیم با ارسال تراکنشهای مناسب کار شبیه سازی را آغاز کنیم.
ارسال تراکنشها
ارسال تراکنشهای AXI4-Lite کار بسیار سادهای هست. برای این کار فقط نیاز داریم از API ها استفاده کنیم. به این صورت:
Write transaction:
AXI4LITE_WRITE_BURST(addr,prot,data,resp)
Read transaction:
AXI4LITE_READ_BURST(addr,prot,data,resp)
نکته: تمام API های مربوط به AXI VIP به خوبی توسط Xilinx مستند شده و در یک فایل فشرده Zip قابل دانلود است، که میتوانید از این اینجا دانلود کنید.
در این آموزش ما قصد داریم LED_1 را خاموش و روشن کنیم، این LED به کانال اول AXI GPIO وصل شده است، همینطور قصد داریم وضعیت SWITCH_1 را که به کانال دوم AXI GPIO متصل است، بخوانیم و هر دو مقدار را در کنسول نمایش دهیم.
اگر نگاهی به register map های AXI GPIO بیاندازیم، به سادگی متوجه میشویم که ما باید در آدرس 0x0 بنویسیم و از آدرس 0x8 بخوانیم. اطلاعات مربوط به این register map ها در PG144 قرار دارند و مطالعه تکمیلی در این رابطه را به خودتان واگذار میکنیم.
ما عملیات نوشتن را آغاز میکنیم، و وضعیت LED_1 را تغییر میدهیم.
۱۵- با استفاده از کد زیر میتوانیم مقدار 0x1 رو در رجیستر یک در آدرس 0x0 از AXI GPIO بنویسیم. این کار به معنی روشن کردن LED میباشد. پس برای روشن کردن LED این چند خط را به فایل تست بنچ اضافه میکنیم.
// 1 Send 0x1 to the AXI GPIO Data register #500ns addr = 0; data = 1; master_agent.AXI4LITE_WRITE_BURST(base_addr + addr,0,data,resp);
۱۶- با استفاده از کد زیر میتوانیم مقدار 0x0 را در رجیستر یک که در آدرس 0x0 از AXI GPIO قرار دارد، بنویسیم. این کار به معنی خاموش کردن LED میباشد. پس برای خاموش کردن LED این چند خط را به فایل تست بنچ اضافه میکنیم.
// 1 Send 0x0 to the AXI GPIO Data register #200ns addr = 0; data = 0; master_agent.AXI4LITE_WRITE_BURST(base_addr + addr,0,data,resp);
گام بعدی ما خواندن از مقدار سوئیچ است. پس بعد از ایجاد هر تغییر در وضعیت سوئیچ مقدار آن را میخوانیم و وضعیت سوئیچ را در کنسول نمایش میدهیم.
۱۷- برای انجام تراکنش خواندن، قطعه کد زیر را به تست بنچ اضافه میکنیم. در این قطعه کد کوتاه یکبار مقدار سوئیچ را برابر با ‘0’ قرار میدهیم و مقدار آن را میخوانیم و سپس مقدار آن را به ‘1’ تغییر میدهیم و مجدداً وضعیت آن را بررسی میکنیم. آدرس مورد استفاده برای خواندن وضعیت سوئیچ برابر با 0x8 است.
// Switch in OFF position switch_1 = 0; // Read the AXI GPIO Data register 2 #200ns addr = 8; master_agent.AXI4LITE_READ_BURST(base_addr + addr,0,data,resp); switch_state = data&1'h1; if(switch_state == 0) $display("switch 1 OFF"); else $display("switch 1 ON"); // Switch in ON position switch_1 = 1; // Read the AXI GPIO Data register 2 #200ns addr = 8; master_agent.AXI4LITE_READ_BURST(base_addr + addr,0,data,resp); switch_state = data&1'h1; if(switch_state == 0) $display("switch 1 OFF"); else $display("switch 1 ON");
این آخرین قطعه کدی بود که نیاز داشتیم. به این ترتیب فایل تست بنچ ما تکمیل شد و کم کم باید آماده اجرای شبیه سازی شویم.
اجرای شبیه سازی
با انتخاب گزینه Run Simulation از صفحه Flow Navigator میتوانیم شبیه سازی را اجرا کنیم.
۱۸- شبیه سازی را برای مدت 3us اجرا میکنیم. در کنسول Tcl، شما باید روشن و خاموش شدن LED و همینطور گزارشی از تغییر وضعیت سوئیچ را در کنسول ببینید.
حالا میتوانیم تراکنشهای AXI4-Lite را در پنجره Waveform آنالیز کنیم.
۱۹- در صفحه Scope زیر ماژول axi_vip_0 را از زیر شاخه AXI_GPIO_tb > UUT > AXI_GPIO_Sim_i انتخاب میکنیم.
۲۰- در صفحه Object ، روی پورت اینترفیس M_AXI راست کلیک میکنیم و گزینه Add to Wave Window را انتخاب میکنیم.
قابلیت Protocol Instance که امکان مشاهده مجموعهای از سیگنالهای AXI در سطح پروتکل را به طراح میدهد، از نسخه 2019.1 به بعد در Vivado معرفی شده است و در صورتی که شما از نسخه 2018.2 یا سایر نسخههای قدیمی تر Vivado استفاده میکنید. همچنان باید از شکل موجهای مرسوم در شبیه سازی استفاده کنید.
Vivado 2019.1 Release note
۲۱- حالا شبیه سازی را ریست میکنیم و مجدداً برای 3us آن را اجرا میکنیم.
همانطور که در شکل (۱۴) نشان داده شده است،۴ تراکنش روی اینترفیس AXI4-Lite شامل دو تراکنش نوشتن که به دنبال آن دو تراکنش خواندن در پنجره Waveform قابل مشاهده میباشد.
۲۲- پورت اینترفیس M_AXI را باز میکنیم تا کانالهای متفاوت را ببینیم. با هم گامهایی را که در زمان اجرای تراکنش نوشتن قابل مشاهده است، مرور میکنیم.
اول آدرس ارسال میشود. این آدرس وقتی که هر دو سیگنال READY و VALID روی کانال آدرس نوشتن به صورت همزمان یک باشند از Master به slave ارسال میشود. یعنی سیگنالهای (AWREADY و AWVALID).
بعد دیتا از Master به Slave ارسال میشود. ارسال دیتا زمانی که هر دو سیگنال READY و VALID روی کانال نوشتن، به صورت همزمان یک باشند، انجام میشود. یعنی سیگنالهای (WREADY و WVALID).
نکته: فقط یک بخش از دیتا به ازای هر آدرس ارسال میشود، و این کاملاً طبیعی است. چون مد burst در اینترفیس AXI4-Lite پشتیبانی نمیشود و ما مجبوریم به ازای هر آدرس یک دیتا ارسال کنیم.
در نهایت تراکنش نوشتن زمانی به پایان میرسد که Slave پاسخ نوشتن را به سمت Master ارسال کند. این پاسخ در تأیید موفقیت آمیز بودن عملیات نوشتن در کانال نوشتن پاسخ ارسال میشود. ارسال پاسخ از Slave به Master زمانی که هر دو سیگنال READY و VALID روی کانال نوشتن پاسخ همزمان یک باشند، اجرا میشود. یعنی سیگنالهای (BREADY و BVALID).
تراکنشهای خواندن هم به شکل مشابهی قابل آنالیز هستند.
ابتدا آدرس از Master به Slave ارسال میشود. ارسال زمانی که هر دو سیگنال READY و VALID روی کانال خواندن آدرس به صورت همزمان یک باشند، انجام میشود. یعنی سیگنالهای (ARREADY و ARVALID).
در ادامه دیتا از Master به Slave ارسال میشود. ارسال زمانی که هر دو سیگنال READY و VALID روی کانال نوشتن به صورت همزمان یک باشند، صورت میپذیرد. یعنی سیگنالهای (RREADY و RVALID).
در طول اجرای تراکنش، Slave هم پاسخ خواندن را برای گزارش موفقیت آمیز بودن تراکنش خواندن، به سمت Master ارسال میکند.
ارسال این پاسخ همزمان با ارسال دیتا درون کانال خواندن دیتا انجام میشود.
جمع بندی
مطلبی که مطالعه کردید قسمت سوم از سری مقالات آموزشی اینترفیس AXI بود. در این آموزش یک پروژه نسبتاً ساده برای شبیه سازی اینترفیس AXI4-Lite ساختیم و با ویرایش فایل تست بنچ اولیه، تراکنشهای خواندن و نوشتن تولید کردیم و در نهایت با استفاده از آن رفتار یک AXI GPIO را شبیه سازی کردیم. امیدوارم از این آموزش بهره کافی را برده باشید. شما میتوانید با کمی حوصله و دقت پروژههای به مراتب پیچیده تری را شبیه سازی کنید. منتظر آموزشهای بعدی ما باشید.
منبع: با اقتباس از وبلاگ Xilinx