مقدمه
به صورت پیش فرض Vivado-HLS به عنوان ابزاری برای توسعه سیستمها و الگوریتمهای پردازشی در FPGA شناخته میشود. ممکن است این سوال برای شما هم پیش آمده باشد که آیا میتوان از HLS برای طراحی یک مسیر دیتای پرسرعت بین بخشهای مختلف سیستم استفاده کرد. پاسخ به این سوال مثبت است. در این آموزش از پایگاه دانش هگزالینکس قصد داریم شما را با شیوه خواندن و نوشتن در حافظه خارجی DDR با HLS آشنا کنیم.
کار با حافظه های خارجی
ما علاوه بر توسعه الگوریتمهای پردازشی میتوانیم برای ساخت مسیرهای تبادل داده نیز از HLS استفاده کنیم. زمانهایی وجود دارد که در هنگام استفاده از HLS ما نیاز داریم تا با حافظههای خارجی همچون حافظههای DDR ارتباط بر قرار کنیم و به تبادل اطلاعات بپردازیم. این کار ممکن است برای ذخیره دیتا، بازیابی دیتا و یا خواندن دادههایی که توسط ماژول یا فانکشن دیگر روی حافظه نوشته شده باشد، صورت بپذیرد.
در ابتدا ممکن است ایده تبادل اطلاعات با یک حافظه DDR و خواندن و نوشتن در حافظه خارجی با استفاده از HLS کمی پیچیده به نظر برسد. اما هیچ چیز نمیتواند جذاب تر از حقیقتی باشد که در ادامه تصمیم دارم برای شما به اثبات برسانم.
برای این کار تصمیم دارم از بورد Arty که دارای یک تراشه FPGA از نوع Artix35T با 256MB حافظه DDR3L است، استفاده کنم. البته شما میتوانید از هر بوردی که در اختیار دارید استفاده کنید. آموزشی که در ادامه خدمتتان ارائه میکنم یک HLS IP است که میتواند درون طراحی Vivado ما قرار بگیرد و با حافظه DDR به تبادل اطلاعات بپردازد. عملکرد این بلوک HLS بسیار ساده خواهد بود. من تنها یک الگوی مشخص از اعداد را در حافظه DDR مینویسم.
پروژه Vivado
برای انجام این پروژه خواندن و نوشتن در حافظه DDR، به یک طراحی در Vivado با مشخصات زیر نیاز داریم.
- استفاده از Memory Interface Generator و پیکره بندی آن برای کار با DDR3L
- استفاده از JTAG to AXI IP Core برای خطایابی و بازبینی دسترسی به DDR3L
- استفاده از AXI Interconnect برای ایجاد امکان دسترسی همزمان بلوک طراحی شده در HLS و JTAG to AXI IP Core به MIG.
- استفاده از VIO برای ارسال فرمان شروع به کار به بلوک HLS
- استفاده از ILA برای بازبینی و دیباگ ماژولهای داخلی طرح
- بلوک تست ساخته شده در HLS
ایده اصلی پشت این پروژه به این صورت است که ابتدا بلوک HLS عملیات نوشتن در DDR را آغاز میکند و بعد ما با استفاده از JTAG to AXI IP Core شروع به خواندن از حافظه DDR میکنیم.
ماژول تست HLS
برای ساخت بلوک HLS قاعدتاً باید از ابزار Vivado HLS استفاده کنیم و یک پروژه جدید ایجاد کنیم. پس پروژه جدید را بر اساس تراشه Artix روی بورد Arty ایجاد میکنیم.
پیش از هر کاری بهتر است سورس فایلی که برای این پروژه استفاده میکنیم را با هم بررسی کنیم. وقتی با زبان C برنامه نویسی میکنیم برای کپی کردن دیتا از فانکشن memcopy استفاده میکنیم. با استفاده از این فانکشن ما میتوانیم دادهها را از مبدا به مقصد کپی کنیم. وقتی در HLS هستیم باز هم از این تابع استفاده میکنیم. رفتار این فانکشن همچنان به همان صورت قبل است با این تفاوت که در اینجا انتقال و جابجایی دادهها با استفاده از اینترفیس AXI4 Memory Mapped انجام میشود.
به طور کلی اینترفیس AXI4 برای استفاده از MIG و سایر اینترفیسهای حافظه ایدهال است. از این رو کدهایی که ما در این پروژه برای ساخت یک بلوک تستی HLS استفاده میکنیم، مثال بسیار ساده هستند.
// test.h void memcpy_test( int *ddr);
// test.c #include "test.h" #include "string.h" void memcpy_test(int *ddr) { int i; int data[256]; #pragma HLS INTERFACE m_axi port=ddr depth=256 offset=direct for (i =0;i<256;i++){ data[i] = i; } memcpy(ddr,data,256*sizeof(int));
کد testbench نوشته برای این کد نیز بسیار ساده مشابه کدهای زیر است.
// test_tb.c #include <stdio.h> #include "test.h" int main() { int ddr[256]; memcpy_test(&ddr); }
با نگاه دقیقتر به جزئیات کد، ملاحظه میکنید که فانکشن main درای یک اشارهگر از نوع int به سمت حافظه DDR است، در واقع این همان اینترفیسی است که تصمیم داریم از آن برای نوشتن دیتا در حافظه DDR استفاده کنیم. در صورت نیاز، ما میتوانیم از این اینترفیس برای خواندن از DDR نیز استفاده کنیم.
پیکرهبندی اینترفیس
ما این اینترفیس را به صورت یک اینترفیس AXI Master تعریف میکنیم. برای این کار از پراگمای HLS Interface با پارامترهایی به صورت زیر استفاده میکنیم.
interface type = m_axi port = ddr depth = 256 offset = direct
به این ترتیب یک اینترفیس AXI Master به پورت ddr نسبت داده میشود. پارامتر عمق (depth) در این پراگما برای co-simulation، هنگامی که اشارهگرها به جای آرایهها استفاده میشوند، بکار برده میشود. در واقع این پارامتر تعداد دفعات نوشتن و خواندن را تعیین میکند.
مقدار offset نیز به عنوان یک پارامتر دیگر برای کنترل فضای آدرس استفاده میشود. ما از آن برای آدرس دهی و دسترسی به اینترفیس AXI استفاده میکنیم. در اینجا سه انتخاب داریم که هر کدام متد خاصی را فعال میکنند.
- گزینه اول Off : گزینه Off گزینه پیش فرض است، با انتخاب این گزینه آدرس دهی از آدرس 0x00000000 آغاز میشود.
- گزینه دوم Direct : با انتخاب این گزینه یک پورت روی ماژول HLS ایجاد میشود، به این ترتیب، تعریف و کنترل مقدار offset از درون Block Design در زمان فراخوانی HLS IP در Vivado امکان پذیر میشود.
- گزینه Slave : با انتخاب این گزینه یک اینترفیس AXI-4 Lite ایجاد میشود و با استفاده از یک رجیستر، مقدار offset تعیین میشود.
برای این مثال، ما از گزینه direct استفاده میکنیم و کار را ساده می کنیم. با این کار طرح ما بر پایه منابع منطقی و به صورت ثابت پیاده سازی میشود. در حالی که اگر گزینه AXI4-Lite انتخاب میکردیم طراحی ما بیشتر شبیه یک سیستم نهفته با کنترل پذیری بالاتر میشد.
شبیه سازی ماژول HLS
اما هنگامی که سخن از ارزیابی و صحه گذاری طرح به میان میآید، طراحی و استفاده از test bench مناسب اجتناب ناپذیر است. با استفاده از test bench ما میتوانیم c-based simulation و صد البته C/RTL co-simulation انجام دهیم. در این پروژه test bench طراحی شده بسیار ابتدایی است. در واقع test bench من دارای یک آرایه ۲۵۶ بیتی است که به سمت فانکشن یا ماژول HLS ارسال میشود و آن را پر میکند.
شبیه سازی
در Vivado-HLS تست و صحه گذاری کدهای C با استفاده از test bench نوشته شده به زبان C انجام میشود. این شبیه سازی تحت عنوان c-based simulation یا به بیان فارسی شبیه سازی C نام گذاری شده است.
عبارت C/RTL co-simulation معرف شبیه سازی توامان سخت افزاری و نرم افزاری است. در واقع برنامه test bench نوشته شده به زبان C به صورت اتوماتیک به RTL test bench تبدیل میشود و امکان فراخوانی و اجرای آن در Vivado امکان پذیر میگردد. این شبیه سازی که روی طرح RTL خروجی اجرا میشود، تحت عنوان co-simulation نام گذاری شده است. در طول این آموزش و به طور کلی کلیه آموزشهای HLS ما به اختصار عبارت شبیه سازی RTL را برای مخاطب قرار دادن این مفهوم استفاده میکنیم. تست و صحه گذاری کدهای C نیز با عنوان شبیه سازی C مخاطب قرار داده میشود.
در واقع شبیه سازی در محیط C امکان بررسی صحت عملکرد کد قبل از سنتز HLS و شبیه سازی RTL را فراهم میآورد. مشابه هر فرایند خطایابی و دیباگ دیگری در زبان C ، ما باید از break point برای متوقف کردن اجرا و بررسی محتوای متغیرها استفاده کنیم.
با اتمام کار طراحی و کدنویسی، نوبت به سنتز و شبیه سازی RTL میرسد، پیش از اجرای شبیه سازی RTL از فعال بودن تیک گزینه dump trace port اطمینان حاصل کنید. با این کار شما یک کپی از سیگنالها و پورتهای ماژول سنتز شده در محیط شبیه ساز Vivado ایجاد میکنید. به این ترتیب ما میتوانیم شکل موج روی ورودیها و خروجیهای ماژول HLS پیادهسازی شده را مشاهده کنیم.
در زمان شبیه سازی یک testbench دقیقاً مشابه کد C اما به صورت RTL تولید میشود. سپس با اعمال همان ورودیهای درون کد C به مدار RTL موفقیت یا عدم موفقیت شبیه سازی با یک پیغام در کنسول خروجی Vivado HLS گزارش میشود.
ساخت HLS IP
در صورت رضایت نتایج شبیه سازی RTL گام آخر ساخت یک IP Core از ماژول HLS طراحی شده و اضافه کردن آن به لیست مخازن IP ها در Vivado است. به این ترتیب امکان فراخوانی و استفاده از IP در محیط Vivado-IPI فراهم میآید.
بعد از اضافه کردن ماژول HLS به طراحی صورت گرفته در Vivado ، زمان آن فرا میرسد که آدرسهای ماژول HLS را تعیین کنیم. قرار است IP Core جدیدمان از این آدرسها برای تبادل اطلاعات استفاده کند. برای این کار لازم است از یک constant block استفاده کنیم.
با پایان این بخش باید طرح را سنتز و پیاده سازی کنیم و در نهایت بیت فایل خروجی را تولید کنیم. بعد از پایان تمامی این مراحل، بیت فایل تولید شده را با استفاده از Hardware Manger روی تراشه برنامهریزی میکنیم.
تست روی سخت افزار
نوبتی هم که باشد نوبت VIO است. به لطف VIO ما میتوانیم سیگنال ap_start در ورودی ماژول HLS را کنترل کنیم. این بدان معناست که قبل از شروع کار ما میتوانیم با استفاده JTAG to AXIS درون حاقظه DDR را بخوانیم. به این ترتیب میتوانیم مطمئن شویم که مقادیر تستی ما از قبل روی حافظه DDR نوشته نشدهاند.
بعد از اینکه از مقدار دهی اولیه و البته تصادفی حافظه مطمئن شدیم، میتوانیم فرمان آغاز بکار HLS IP را با استفاده از VIO صادر کنیم و بررسی کنیم که آیا عملیات نوشتن در حافظه DDR به درستی صورت پذیرفته است یا خیر؟
برای بازبینی و بررسی موفقیت آمیز بودن عملیات نوشتن دیتا، دو گزینه پیش رو داریم:
گزینه اول: استفاده از ILA و پیکره بندی آن به نحوی که تریگر آن روی AXI Write قرار داده شود و در نهایت بررسی مقادیری که در حال نوشته شدن درون حافظه است.
گزینه دوم: استفاده از JTAG to AXI bridge برای خواندن آدرسهای نوشته شده و تایید صحت مقادیری که در حافظه نوشته شده است.
با این نتایج موفقیت آمیز بودن عملیات نوشتن در حافظه تایید میشود، این یعنی طراحی HLS ما بسیار انعطاف پذیر و البته قابل اطمینان است.
در انتها در لازم است به چند نکته مهم توجه کنید:
- مطمئن شوید که بلوک HLS به درستی ریست میشود.
- مطمئن شوید که سیگنال ap_start تا زمانی که سیگنال ap_ready فعال نشده است در وضعیت ‘1’ منطقی باقی میماند.
- در نظر داشته باشید که نگه داشتن سیگنال ap_start در وضعیت
‘1’
منطقی منجربه به اجرای چندباره ماژول HLS میشود. - و در نهایت مطمئن شوید که رنجِ آدرس برای دسترسی به حافظه از درون سخت افزار به درستی تنظیم شده است.
جمع بندی
امیدوارم از مطالعه این آموزش بهره کافی را برده باشید. در این آموزش من در قالب یک پروژه ساده شیوه خواندن و نوشتن در حافظه خارجی با Vivado HLS را به شما توضیح دادم. همانطور که در ابتدای آموزش نیز اشاره شد، شما میتوانید از هر بوردی که در اختیار دارید برای انجام این پروژه استفاده کنید، فقط کافی است این بورد دارای یک FPGA از خانواده آموزشی سری ۷ یا ZYNQ باشد و حافظه DDR روی بورد وجود داشته باشد.
منبع: برگرفته از adiuvoengineering.com نوشته Adam Taylor
7 در مورد “خواندن و نوشتن در حافظه DDR با HLS”
سلام و درود، سپاسگزارم از گروهتون بابت محتوای ارزشمندی که درست میکنید.
سوالی داشتم از خدمتتون، ایا امکان این هست که ماژول حافظه خارجی مثلا DRAM رو هم در کنار پردازنده Fpga که به اون متصل است رو باهم شبیه سازی کنیم تا بتونیم حداقل صحت عملکرد مدار رو نرم افزاری تست کنیم (چون بنده بردی ندارم تا کل سیستم طراحی شده رو بتونم تست کنم و فقط میخوام ارزیابی کنم سیستم طراحی شده حداقل نرم افزاری درست کار میکنه یا نه).و اگر امکان پذیره علاوه بر شبیه سازی رفتاری، شبیه سازی زمانی هم امکان پذیره تا بتونیم با دقت خوبی تاخیر های خواندن و نوشتن از DRAM رو هم مدل کرد.
سپاسگزارم از وقتی که گذاشتید.
سلام سجاد عزیز
از اینکه وقت با ارزش خودتون را به خواندن این مقاله و نوشتن این پیام اختصاص دادید بی نهایت سپاسگزاریم.
در حالت کلی شبیه سازی فقط برای مداراتی امکان پذیر هست که داخل تراشه FPGA هستند و هر تراشه یا اینترفیس خارجی باید به صورت دستی توسط طراح در فایل تست بنچ مدل بشه که برای DDR کار سختی هست. علاوه بر این در چنین سیستمهایی به جز تأخیرهای ذاتی طرح تأخیرهای ناشی از مسیرهای روی PCB هم تأثیر گذارند. (در واقع MIG این موارد رو هم هنگام برقراری ارتباطات در نظر میگیره.)
اما پیشنهادی که میتونم بهتون بکنم اینکه از مثال آماده MIG که امکان شبیه سازی هم داره استفاده کنید. شاید ایده خوبی بهتون بده و بتوانید ازش برای ادامه کارتون استفاده کنید.
با سلام.ممنون از این آموزش سور س فایل های این طراحی را از کجا می شه پیدا کرد ممنون می شم از کمککتون.
سلام و درود فراوان بر شما دوست عزیز،
از اظهار لطف شما نسبت به مطالب منتشر شده سپاسگزارم.
برای دسترسی به فایلهای پروژه میتوانید به گیت هاب آدام تیلور که لینکش در قسمت منبع این مقاله هست، مراجعه کنید. اما توجه داشته باشید که پروژه های Vivado معمولاً حجیم هستند و فایل پروژه به صورت مستقیم در فضای ابری (آنلاین) قرار داده نمیشود و در عوض فایلهای Tcl برای ساخت مجدد پروژه مورد استفاده قرار میگیرد.
شما میتوانید از پروژه شماره ۲۳۹ در این گیت هاب و به شکل دقیق تر فایل part_239.tcl برای ساخت مجدد این پروژه استفاده کنید. البته کار یک مقدار سخت تر هستش چون حتماً باید پروژه Vivado HLS را هم قبلش بسازید و به صورت IP به Repository های Vivado اضافه کنید و بعد این فایل را اجرا کنید.
نسخه Vivado مورد استفاده 2017.4 است. موفق باشید.
از لطف شما متشکرم.
خیلی خیلی اموزش خوبی بود ممنون میشم اگر باز هم اموزش هایی از این دست چه با پروژه های ساده چه پیشرفته تر منتشر کنید.
درود بر شما دوست عزیز و گرامی
امیدوارم سرحال و پیروز باشید و روز به روز در مسیر موفقیت و پیشرفت گام بردارید. حتماً از نظر ارزشمند شما در تهیه مطالب آموزشی استفاده خواهیم کرد.