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

9 خرداد, 1396

تبدیل mfile متلب به کد VHDL

آیا تا به حال از شما خواسته شده است که یک الگوریتم پردازشی توصیف شده با زبان C یا m. فایل متلب را در FPGA پیاده‌سازی کنید؟

اگر پاسخ شما به این سوال مثبت است، تماشای این برنامه ویدئویی را که در مورد تبدیل mfile متلب به کد VHDL است از دست ندهید.

در این برنامه، به شما نشان خواهم داد برای پیاده‌سازی و نوشتن کد VHDL یک الگوریتم پردازشی از روی یک برنامه به زبان C یا m. فایل متلب، چه نکاتی را باید در نظر بگیرید.

More...

به این نکته تاکید می‌کنم که این برنامه در مورد روش‌های خودکار تبدیل mfile متلب به کد VHDL نیست و در آن به شما بینشی جدید ارائه خواهم کرد تا خودتان بتوانید کد VHDL معادل برنامه m. فایل متلب را به راحتی بنویسید.

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

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

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

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

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

ویدئو یا متن؟

محتوای این برنامه آموزشی، به دو صورت ویدئو و متن آماده شده است. اگر علاقمند به یادگیری این مطلب به صورت ویدئویی هستید، ویدئوی زیر را ببینید و اگر ترجیح می‌دهید آن را به صورت متن مطالعه کنید، ادامه این مطلب را بخوانید.

برای دانلود نسخه با کیفیت این ویدئو، روی دکمه زیر کلیک کنید:

در این مقاله قصد دارم درباره‌ی نحوه‌ی تبدیل یک الگوریتم، در قالب m-file متلب، به کد VHDL صحبت کنم.

باید به این نکته تاکید کنم که منظور من از تبدیل m-file متلب به کد VHDL، روش‌هایی که به صورت اتوماتیک توسط ‌نرم‌افزار انجام می‌‌شود، نیست؛ بلکه منظور من این است که شما بتوانید با بررسی یک m-file، کد VHDL‌ای که پیاده کننده‌‌ی محتوای آن باشد را بنویسید.

مراحل پیاده‌‌سازی الگوریتم‌های پردازش سیگنال با FPGA

ابتدا مراحل پیاده‌‌سازی الگوریتم‌های پردازش سیگنال با FPGA را به صورت خیلی خلاصه شده برای شما بیان می‌‌کنم.

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

گاهی اوقات شما خودتان مجبورید این مدل را درست کنید. به طور کلی برای ایجاد آن، دو روش وجود دارد.

روش اول این است که این مدل را به کمک زبانی مانند زبان C یا m-file متلب ایجاد کنید؛ در واقع به کمک دستورات برنامه‌‌نویسی این مدل را ایجاد کنید.

در روش دوم، مدل را به کمک ‌نرم‌افزاری مانند Simulink متلب ایجاد می‌‌کنید. محاسبات این مرحله، محاسباتی با دقت اعشاری بسیار زیاد و در قالب Floating-Point است. بنابراین می‌‌توانیم این مدل را، مدل Floating-Point بنامیم.

مرحله‌‌ی دوم، تبدیل مدل Floating-Point به مدل Fixed-Point است.

مدل Fixed-Point، مدلی است که عرض بیت نمایش مقادیر ثابت و محاسبات (یعنی خروجی ضرب‌‌کننده‌ها و جمع‌‌کننده‌ها) را تا حدی که هنوز عملکرد مدار Fixed-Point با عملکرد مدار Floating-Point تقریباً برابر باشد، کاهش می‌دهیم.

البته، تبدیل مدل Floating-Point به مدل Fixed-Point، باعث ایجاد مقداری خطا در محاسبات خواهد شد؛ اما هدف این است که عرض بیت سیگنال‌ها و محاسبات را تا جایی که خطا قابل قبول باشد، به حداقل ممکن برسانیم. روند انجام این تبدیل، بحث بسیار مفصلی دارد که در این مقاله وارد جزئیات آن نخواهیم شد.

مرحله‌ی آخر این است که با مرجع قرار دادن مدل Fixed-Point، شروع به نوشتن یک کد به زبان HDL (VHDL یا Verilog) می‌کنید و به کمک این کد، الگوریتم‌تان را پیاده‌‌سازی می‌کنید.

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

در دوره‌ی پردازش سیگنال با FPGA، به صورت عمده در مورد مدل‌هایی صحبت می‌‌کنیم که به صورت Simulink پیاده‌‌سازی شده‌‌اند. در واقع، خودمان یا شخصی که سیستم را طراحی کرده است، در قسمت Simulink نرم‌افزار متلب، یک مدل Floating-Point از الگوریتم را ایجاد کرده است و می‌‌خواهیم بدانیم، چگونه می‌‌توانیم این مدل را در FPGA پیاده‌‌سازی کنیم.

تفاوت مدل Simulink و مدل m-file

ابتدا خیلی مختصر در مورد تفاوت مدل Simulink و مدل m-file صحبت می‌کنم؛ زیرا ممکن است به جای اینکه مدل ما، در ‌نرم‌افزار Simulink طراحی شده باشد، به کمک زبان C یا m-file متلب مدل‌‌سازی شده باشد.

ویژگی‌های مدل Simulink

مدل Simulink، مدلی است که به کمک آن می‌توانیم دو خاصیت مهم را در مدارات دیجیتال، شبیه‌‌سازی کنیم.

خاصیت اول، همان خاصیت Fixed-Point است که به آن، bit-true نیز گفته می‌شود. با داشتن این خاصیت، شما می‌‌توانید اثر کاهش عرض بیت سیگنال‌ها و مقادیر را در این مدل مشاهده کنید؛

مثلاً می‌توان دید که اگر یک مقدار ثابت را به جای نمایش به صورتFloating-Point  (مثلاً با 64 یا 32 بیت)، با 10 بیت نمایش دهیم، چه اثری در خروجی مدار ایجاد می‌‌کند؟

مدلی که در آن عرض بیت سیگنال‌ها و مقادیر به حداقل رسیده باشند و ما بتوانیم مقدار خطای کاهش عرض بیت را، در آن بررسی کنیم، مدل Fixed-Point یا bit-true می‌نامیم.

ویژگی دوم این است که مدل‌های ساخته شده با Simulink، اصطلاحا Cycle-Accurate هستند.

مدل Cycle-Accurate، مدلی است که شبیه‌‌سازی را به صورت گام به گام(step by step) انجام می‌دهد؛

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

در عمل نیز، در یک مدار همین اتفاق می‌افتد. یعنی شما وقتی که یک مدار دیجیتال پردازش سیگنال (مثلاً یک فیلتر) را پیاده‌‌سازی ‌می‌کنید، در هر کلاک (در متلب به جای کلاک می‌گوییم step زمانی)، یک نمونه وارد ‌می‌شود و این نمونه، وارد مسیر پردازشی می‌‌شود.

ویژگی‌های مدل m-file

اما مدل m-file یا مدلی که مثلاً به زبان C نوشته شده باشد، چه خاصیتی دارد؟

مدل m-file، فقط خاصیت Fixed-Point دارد. یعنی شما در این مدل (که شامل مجموعه‌‌ای از دستورات است)، فقط می‌‌توانید اثر کاهش عرض بیت سیگنال‌ها و مقادیر را شبیه‌‌سازی کنید.

همچنین، این مدل، مدل Cycle-Accurate نیست و روش عملکرد آن متفاوت است؛

معمولاً در مدل‌های پردازشی که به کمک یک زبان برنامه‌‌نویسی ایجاد ‌می‌کنید، ابتدا شما یک مجموعه‌ی بزرگ از ورودی (یا به اصطلاح، یک بردار ورودی) را ایجاد ‌می‌کنید. بعد از اینکه این بردار آماده شد، نمونه به نمونه از این بردار می‌‌خوانید و نمونه‌ها را به الگوریتم‌‌تان وارد ‌می‌کنید؛

بنابراین، ابتدا کل نمونه‌های شبیه‌‌سازی تولید می‌شوند و سپس نمونه به نمونه وارد مسیر شبیه‌سازی خواهند شد. در حالی که در مدل Simulink که به صورت Cycle-Accurate است، وقتی یک نمونه تولید شد، هم‌‌زمان وارد مسیر پردازشی می‌شود.

در مدل پیاده‌سازی شده به کمک یک زبان برنامه‌‌نویسی، مراحل شبیه‌‌سازی نیز مطابق با حالت واقعی پیش نمی‌روند؛

روند شبیه‌سازی به این شکل است که ابتدا تمام نمونه‌ها، وارد step اول الگوریتم می‌شوند. سپس، شبیه‌‌سازی در آن step انجام می‌شود و یک بردار به عنوان خروجی آن مرحله، ایجاد می‌شود. پس از آن، بردار خروجی مرحله‌ی اول، وارد مرحله‌ی بعدی شبیه‌‌سازی می‌‌شود و محاسبات مرحله‌ی دوم انجام می‌شود.

بدین ترتیب، شبیه‌سازی، مرحله به مرحله پیش می‌رود تا نتیجه‌ی نهایی به دست آید.

بررسی روند تبدیل m-file به کد VHDL به کمک یک مثال

برای درک بهتر فرآیند نوشتن کد VHDL معادل با یک m-file (یا یک مدل به زبان C)، در ادامه‌ی مقاله، مثالی را با هم بررسی می‌کنیم.

در زیر، یک m-file متلب را می‌بینید که به کمک آن، یک الگوریتم بسیار بسیار ساده را پیاده‌‌سازی کرده‌ام.

Input_Vector = rand(1,200);
Output_Vector = zeros(1,20);

for i = 1:length(Input_Vector)/10
    
    B = Input_Vector(10*i-9 : 10*i);
    A = 0;
    
    for j = 1:10
    
        if B(j) < 0
            B(j) = 0;
        end;
        A = A + B(j)*j;

    end;
    
    Output_Vector(i) = A / 8;

end;

تفسیر کد متلب

الگوریتم پیاده‌‌سازی شده، به این صورت عمل می‌‌کند که هر ۱۰ نمونه‌ی پشت سر هم از ورودی را در فرمول زیر (فرمول واقع در خط ۱۵اُم کد)، قرار می‌‌دهد و یک نمونه در خروجی تولید می‌شود.

ACC=ACC+B(j)*j

​سیگنال B، سیگنال ورودی است. روی این سیگنال، یک قید قرار داده‌ایم که اگر کوچک‌تر از صفر بود، برابر با صفر قرار داده شود (خط ۱۱اُم کد). یعنی اگر نمونه‌ای از سیگنال ورودی، عددی منفی بود، به جای آن، عدد صفر را به فرمول ‌اعمال می‌کنیم.

مقدار اندیس j  از یک تا ۱۰ تغییر می‌کند؛‌ بنابراین ما ۱۰ نمونه‌ی اول را به کمک یک حلقه‌ی for، وارد فرمول می‌کنیم. بعد از اینکه برای ۱۰ نمونه، این فرمول را محاسبه کردیم، مقدار Accumulator را بر هشت تقسیم ‌می‌کنیم و آن را به عنوان اولین خروجی، به پورت خروجی مدار ارسال ‌می‌کنیم.

همان‌‌طور که قبلا گفته شد، مدل m-file، یک مدل Cycle-Accurate نیست؛ بنابراین شما مجبور هستید در ابتدا، کل ورودی را در قالب یک بردار ایجاد کنید؛ بنابراین من در ابتدای این m-file، به کمک تابع rand (که یک بردار تصادفی تولید ‌می‌کند)، یک بردار ۲۰۰ سمپلی تولید کرده‌ام. سپس این بردار را به متغیر Input_Vector منتقل کرده‌ام (خط اول کد).

همچنین، به کمک تابع zeros، یک متغیر ۲۰ سمپلی با مقادیر صفر تولید کرده و آن را به متغیر Output_Vector منتقل کرده‌ام (خط دوم کد). این بردار خروجی، بیست خروجی را در خود ذخیره خواهد کرد.

سپس یک حلقه‌ی for ایجاد کرده‌ام (خط چهارم کد)، که این حلقه از اندیس یک تا اندیس طول متغیر Input_Vector تقسیم بر ۱۰، می‌‌شمارد. طول این متغیر، ۲۰۰ است که با تقسیم آن بر ۱۰ حاصل ۲۰ خواهد شد. بنابراین این حلقه‌ی ​for، به تعداد ۲۰ بار، تکرار می‌شود. در هر تکرار این حلقه‌، ۱۰ سمپل از Input_Vector را جدا کرده و در متغیر B ریخته‌ام (خط ششم کد). سپس مقدار Accumulator را ریست کرده‌ام (خط هفتم).

همان طور که قبلاً توضیح داده شد، در خط نهم کد، به کمک تکرار حلقه‌ی for به تعداد ۱۰ بار، ۱۰ نمونه‌ی متوالی از مقادیر غیر منفی ورودی را در فرمول اصلی قرار می‌دهیم و مقدار Accumulator را محاسبه ‌می‌کنیم. پس از تکرار حلقه به تعداد ۱۰ بار، مقدار Accumulator  را بر هشت تقسیم می‌کنیم و به عنوان یکی از خروجی‌ها در Output_Vector قرار می‌دهیم.

پیاده‌‌سازی الگوریتم m-file به کمک کد VHDL

مهم‌ترین نکته برای پیاده‌‌سازی این الگوریتم به کمک کد VHDL، تفکیک m-file به دو بخش است؛ بخش‌هایی که به خاطر مدل‌‌سازی ایجاد شده‌اند و بخش‌هایی که قرار است پیاده‌‌سازی شوند.

تفکیک m-file به دو بخش؛ مدل‌‌سازی و پیاده‌سازی

ما در ابتدای کد، به عنوان نمونه، یک بردار ۲۰۰ تایی تولید کردیم تا این شبیه‌‌سازی را انجام دهیم. اما در عمل، ورودی‌های ما ۲۰۰ تا نیستند، بلکه ورودی‌های ما به صورت دائمی و با هرکلاک وارد سیستم می‌شوند. بنابراین عدد ۲۰۰، برای طول ورودی، مقدار انتخابی ما برای مدل‌‌سازی است و ارتباطی به پیاده‌سازی ندارد.

در بخش بعدی کد، یک حلقه‌ی for را داریم که در مثال ما از یک تا ۲۰ می‌شمارد. این حلقه، برای این است که من یک بردار ۲۰۰ نقطه‌ای یا ۲۰۰ نمونه‌ای درست کرده‌ام و می‌‌خواهم روی هر ده نمونه‌ی متوالی ورودی، محاسباتی انجام دهم؛ بنابراین این قسمت از کد، مربوط به مدل‌‌سازی است و ارتباطی به پیاده‌‌سازی نهایی ندارد؛ زیرا همان‌‌طور که گفته شد، تعداد نمونه‌ها در پیاده‌‌سازی نهایی بی‌‌نهایت است و همیشه در حال وارد شدن به سیستم هستند.

در خط ششم کد، ۱۰ نمونه از ۲۰۰ نمونه را جدا کرده و به متغیر B منتقل کرده‌ام. به نظر شما این بخش جزء پیاده‌‌سازی است یا جزء ملزومات مدل‌‌سازی ماست؟ اگر دقت کنید، این بخش هم به مدل‌‌سازی مربوط می‌شود؛ زیرا در عمل و در پیاده‌‌سازی، نیازی نیست که از ورودی، به صورت ۱۰ نمونه- ۱۰ نمونه جدا کنیم.

اما در خط ​هفتم، Accumulator را صفر کرده‌ام که می‌تواند جزء بخش پیاده‌‌سازی باشد؛ چون برای محاسبه‌ی مقادیر Accumulator، ابتدا باید آن را صفر کنیم.

در خط نهم، یک حلقه‌ی for دیگر داریم؛ در این حلقه(که ۱۰ بار تکرار می‌شود)، ابتدا نمونه‌ی ورودی چک می‌شود که در صورت منفی بودن، برابر صفر قرار گیرند. سپس، نمونه‌ها، در فرمول اصلی قرار می‌گیرند. پس از انجام محاسبات روی هر ۱۰ نمونه، مقدار Accumulator بر هشت تقسیم شده و به خروجی منتقل می‌شود. به نظر می‌رسد که این محاسبات جزء اصل موضوع الگوریتم ما هستند که باید پیاده‌‌سازی شوند.

بنابراین الگوریتم ما به طور ساده، الگوریتمی است که در عمل، ۱۰ نمونه ورودی را دریافت می‌کند و محاسبات درون حلقه‌ی for داخلی را روی نمونه‌ها انجام می‌دهد و سپس یک نمونه، به خروجی ارسال می‌کند. مجدداً روی ده نمونه‌ی بعدی همین محاسبات را انجام می‌دهد و یک نمونه‌ی دیگر به خروجی ارسال می‌کند.

در واقع، الگوریتم ما به ازای هر ۱۰ نمونه‌ی ورودی، یک نمونه در خروجی ایجاد می‌کند. حالا باید این مفهوم را به کمک زبان VHDL پیاده‌‌سازی کنیم.

نوشتن کد VHDL

ابتدا باید به نکاتی دقت کنیم؛ یکی از مهم‌‌ترین نکاتی که وجود دارد این است که ما در این کد، یک حلقه‌ی for داریم که گفتیم جزء پیاده‌‌سازی ما است. اما آیا ما در زبان VHDL می‌توانیم از مفهومی به نام حلقه‌ی for، به این معنا که در زبان‌های برنامه‌‌نویسی وجود دارد، استفاده کنیم؟ جواب این سوال منفی است. در زبان VHDL برای پیاده‌‌سازی یک حلقه‌ی for باید ترفند دیگری به کار ببریم.

در زیر کدی که من برای پیاده‌‌سازی m-file آماده کرده‌ام را می‌بینید. نکات بسیار مهمی در نوشتن این کد وجود دارد.

برای مشاهده کد، اینجا را کلیک کنید...

در این مقاله، در مورد موضوع Fixed-Point کردن (تبدیل مدل Floating-Point به مدل Fixed-Point) صحبت نخواهیم کرد. بلکه درباره‌ی موضوع مهم درک صحیح از m-file و نحوه‌ی پیاده‌‌سازی آن به کمک زبان VHDL صحبت خواهم کرد.

اکنون، از ابتدای کد، شروع به بررسی می‌کنیم. در بخش entity، این پورت‌ها را داریم؛ یک کلاک، یک پورت ورودی به نام Data_In و یک پورت خروجی به نام Data_Out.

پورت ورودی، همان پورتی است که در هر کلاک، نمونه‌ها را می‌‌گیرد و به ازای هر ۱۰ کلاک، یک نمونه (بر طبق محاسباتی که قبلا دیدیم)، در پورت خروجی خواهیم داشت.

اکنون، بخش process ‌کد را بررسی می‌کنیم. طبق الگوی استاندارد کدنویسی، این بخش فقط حساس به لبه‌‌ی بالا رونده‌ی کلاک است. همچنین، طبق این الگوی استاندارد، سیگنال ورودی را در یک سیگنال داخلی به نام Data_In_Int قرار داده‌ایم.

 من می‌خواهم اگر ورودی‌هایم منفی هستند، آن‌ها را برابر با صفر کنم؛ بنابراین در خط ۳۴ از کد، یک سیگنال میانی به نام Saturate_On_Negative  تعریف می‌کنم. سیگنال ورودی را به سیگنال Saturate_On_Negative اعمال می‌کنیم.

در ادامه‌ی کد و در خط ۳۶اُم، یک if قرار داده‌ام که اگر سیگنال ورودی کوچکتر از صفر بود، Saturate_On_Negative، برابر با صفر شود.

بنابراین اگر سیگنال ورودی مثبت باشد، من در خط ۳۴اُم کد آن را به سیگنال Saturate_On_Negative منتقل کرده‌ام و اگر سیگنال ورودی، منفی باشد، در خط ۳۷اُم، مقدار صفر را به آن منتقل می‌کنم؛ بنابراین این بخش از کد باعث می‌شود که سیگنال‌های منفی ورودی، به صفر تبدیل شوند.

در ادامه، باید فرمول اصلی را پیاده‌‌سازی کنیم؛ فرمول اصلی، در کد متلب به صورت زیر بود:

ACC=ACC+B(j)*j

برای پیاده‌سازی این فرمول، دو سیگنال داخلی در خط ۱۹اُم و ۲۰اُم کد VHDL تعریف کرده‌ام. سیگنال Accumulate_Counter برای معادل‌سازی اندیس j تعریف شده است؛ این سیگنال از نوع unsigned تعریف شده و قرار است از اندیس یک تا ۱۰ را بشمارد، بنابراین چهار بیت برای طول آن کافی است. سیگنال Accumulator نیز همان ACC است.

در خط ۴۰اُم کد، می ‌بینیم که سیگنال ورودی Saturate_On_Negative (که غیر منفی است)، در Accumulate_Counter ضرب شده است. سپس حاصل ضرب، با مقدار Accumulator جمع شده است و در نهایت، حاصل این جمع، به سیگنال Accumulator منتقل شده است.

همان‌طور که در کد متلب دیدید، اندیس حلقه‌ی for، باید از یک تا ۱۰ بشمارد؛ بنابراین، من در خط ۴۲اُم کد، به مقدار اندیس Accumulate_Counter، یکی اضافه کرده‌ام و در خط ۴۴اُم نیز، شرطی قرار داده‌ام که هر گاه این شمارنده برابر ۱۰ شد، دوباره مقدار آن یک شود.

همچنین، درون همین شرط، سیگنال Accumulator را بر هشت تقسیم می‌کنیم؛ زیرا قرار بود، پس از ده بار انجام عمل Accumulator، سیگنال Accumulator بر هشت تقسیم شده و یک نمونه به خروجی منتقل شود. با حذف سه بیت کم ارزش از سیگنال Accumulator، آن را به اندازه‌ی سه شیفت، به راست منتقل می‌کنیم. هر شیفت به راست، معادل یک تقسیم بر دو است. پس سه شیفت به راست، برابر با تقسیم بر هشت خواهد بود. تقسیم بر هشت شده‌ی سیگنال Accumulator را، به سیگنال Data_Out_Int منتقل می‌کنیم و در قسمت Concurrent، سیگنال Data_Out_Int را به پورت خروجی Data_Out، اعمال می‌‌کنیم.

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

اگر خوب به این کد دقت کنید، می‌‌بینید که اثری از حلقه‌ی for یا موردی مشابه با آن نیست. پس من حلقه‌ی for را چطور در کد VHDL پیاده‌‌سازی کرده‌ام؟

پیاده‌سازی حلقه‌ی for موجود در m-file، به کمک کد VHDL

در واقع من از ساختار process و کلاک، به عنوان همان حلقه‌ی for، استفاده کردم؛ یعنی در کد VHDL، هر تکرار حلقه‌ی for، در واقع یک کلاک است. هر گاه با هر کلاک، لبه‌‌ی بالارونده در کد من ظاهر می‌‌شود، process را فعال کرده و محاسبات درون آن انجام می‌‌شود. این فرآیند معادل با یک بار تکرار حلقه‌ی for در m-file متلب است. در کد VHDL، اندیس حلقه‌ی for در m-file متلب را، به کمک سیگنال Accumulate_Counter، درست کردم و به نحوی مناسب و با ایجاد یک شرط (خط ۴۴)، مقدار شمارنده را با عدد ۱۰ مقایسه کردم. هرگاه مقدارش به ۱۰ می‌رسد، آن را به مقدار یک ریست می‌کنم؛ بنابراین من اندیسی دارم که همواره از یک تا ۱۰ می‌‌شمارد و وقتی به ۱۰ می‌رسد، عمل تقسیم بر هشت و عمل ریست کردن Accumulator را نیز انجام می‌دهد؛ بنابراین کدی که نوشته‌ایم عملکردی برابر با m-file متلب دارد.

نکته‌‌ی تبدیل تایپ unsigned به signed در کد VHDL

یک نکته‌ی کدنویسی جالب در این کد وجود دارد که در ادامه، آن را توضیح می‌دهم؛ در زبان VHDL وقتی یک ارجاع را انجام می‌دهید، باید تمام سیگنال‌هایی که در آن سطر نوشته شده‌اند نوع (Type) یکسان داشته باشند. در خط ۴۰ از کد، سیگنال‌های Accumulator و Saturate_On_Negative، از نوع signed هستند؛ اما ​سیگنال Accumulate_Counter، از نوع unsigned است.

طبق قاعده‌ی زبان VHDL، برای ضرب یک سیگنال signed در یک سیگنال unsigned، باید تبدیل تایپ انجام دهید. همان‌‌طور که در خط ۴۰اُم کد می‌‌بینید، من با نوشتن کلمه‌ی signed و قرار دادن کلمه‌ی Accumulate_Counter، درون پرانتز، نوع سیگنال Accumulate_Counter را در لحظه‌ی ضرب کردن، تبدیل کرده‌ام.

اما نکته‌‌ای که در اینجا وجود دارد این است که وقتی شما تایپ یک سیگنال را از unsigned، به signed تغییر می‌دهید، اگر سنگین‌‌ترین بیت سیگنال unsigned، مقدار یک باشد، آنگاه سیگنال signed حاصل، یک عدد منفی خواهد شد؛ چون نمایش عدد signed، به صورت یک عدد two's complement است؛ بنابراین اگر بیت سنگینش برابر با یک باشد، آن عدد، منفی خواهد بود و به این ترتیب در محاسبات ما اشتباه رخ خواهد داد.

بنابراین، باید یک بیت صفر در سمت چپ سیگنال unsigned، به کمک عملگر & یا Concatenation اضافه کنید. به این ترتیب، چون سنگین‌ترین بیت سیگنال صفر است، وقتی نوع آن به signed تبدیل می‌شود، همیشه یک عدد مثبت است؛ بنابراین مشکلی به علت یک بودن احتمالی بیت پرارزش سیگنال (و در نتیجه منفی بودن آن) به وجود نمی‌آید و شما می‌‌توانید یک ضرب صحیح داشته باشید.

​امیدوارم​ از خواندن این مقاله هم لذت برده باشید و بتوانید از نکات یاد گرفته شده، در انجام پروژه‌‌هایتان استفاده کنید.

آیا برنامه ویدئویی تبدیل mfile متلب به کد VHDL برای شما مفید بود؟

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

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

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

درباره نویسنده:

احمد ثقفی

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

  • عالی بود. تشکر بابت تهیه و به اشتراک گذاری ویدیو . لطفا نحوه تهیه کد vhdl از بلوک های مطلب را نیزآموزش دهید.

  • سلام
    خیلی عالی بود
    راستش من تازه قراره شروع کنم
    و برام این چیزا خیلی جالبه….
    ممنون از شما

  • سلام
    واقعاااااااا ممنونننننننمم..عالی بود..من مدتها بود دنبال این میگشتم…کسی نبود بهم یاد بده…حتی یادمه تو یه شرکتی برای کارشون باید اینو بلد میبودم…اون موقع بلد نبودم…حالا الان میدونم چیه…ممنونم…بازم منتظر ویدیوهای خوبتون هستیم

  • سلام
    اول واقعاً تشکر میکنم از مطالب خوب و آموزنده و تلاشی که در نشر آموخته هاتون دارید، قابل تقدیره :)
    راستش من یک چیزی رو متوجه نمیشم :
    الان شما توی این مثال داده ورودی رو بافر می کنید، پس یه کلاک تأخیر دارید یعنی یک کلاک بعد از آمدن هر داده مشخص، اون رو میتونید پردازش کنید.
    پس برای صحت عملیات نباید یک کلاک تأخیر ایجاد کنید توی شمارنده؟
    و اینکه accumulator سیگناله و مقداری که بهش تخصیص داده میشه سر کلاک بعدی قابل مشاهده است، توی کد مثال وقتی counter=10 باشه مقدار فعلی accumulaor مقدار مجموع ۹ داده قبل هست که سر کلاک بعدی داده دهم هم مجموعش به accumulator تخصیص داده میشه اما همین جا ی شرط هست که میاد مقدار accumulator رو بر ۸ تقسیم میکنه و میده به خروجی و accumulator رو صفر میکنه. یک جورایی به این سیگنال دوبار مقدار داده میشه و محاسبات مربوط به داده دهم از بین میره.
    راستش من این کدو نوشتم و سیمولیت کردم ولی داده دهم رو در محاسبات دخیل نمیکنه.
    ممکنه من اشتباه نوشتم یا چیزی اضافه کردم که در صحت جواب تأثیر میذاره.
    با تشکر از صبوری شما :))

    • سلام، ممنون از شما.

      نکته‌ای که در ویدئو هم توضیح دادم این بود که در کد mfile متلب، مواردی وجود دارند که مربوط به پیاده‌سازی نیست و فقط برای نوشتن و مدلسازی کد متلب استفاده شده است.
      در عمل این سیستم با یک رشته بی‌نهایت دیتا مواجه است و اینکه ما در کد متلب آن را به دسته‌های ۱۰ تایی تقسیم کردیم، در عمل چنین دسته‌هایی وجود ندارد.
      بنابراین این مساله مقدار شمارنده در کدام مقدار ورودی ضرب می‌شود اهمیتی ندارد.
      در مورد accumulator هم مساله همین طور هست. ده مقدار متوالی با هم جمع می‌شوند اما اینکه از کجا شروع می‌شود مهم نیست.
      موفق باشید.

  • با عرض سلام خدمت استاد ثقفی
    در فایل ویدئویی “تبدیل m. فایل متلب به کد VHDL” مثالی ارائه نمودید که هر ۱۰ ورودی پیاپی را دریافت کرده و با استفاده از فرمولی یک خروجی به ازای آنها ارائه میدهد. من این مثال را در نرم افزار ISE وارد کردم. سپس یک فایل تست بنچ نیز برای آن آماده کردم. پس از شبیه سازی ملاحظه میشود به ازای هرگونه ورودی (چه اولین ورودی ۰ باشد و چه غیرصفر باشد) خروجی همواره مقدار صفر خواهد داشت. در این حالت اگر به جای شرط Data_in_int <= to_signed(0,14) در چک کردن منفی نبودن اعداد از شرط Data_in_int < to_signed(0,14) استفاده کنیم (یعنی مساوی را برداریم) پاسخ شبیه سازی درست میشود. هرچند نبود این مساوی از نظر محاسباتی و ریاضی تأثیری در نتیجه مطلوب ما ندارد، اما میخواستم بدانم علت این اتفاق از لحاظ زبان VHDL و پیاده سازی بر روی FPGA چیست؟ با تشکر و سپاس از حضرتعالی

  • در کد mfile متلب، ده مقدار متوالی با همدیگر جمع می‌شوند، سپس accumulator صفر می‌شود. اما در کد vhdl، نه (۹) مقدار متوالی جمع می‌شوند، سپس accumulator صفر می‌شود؟

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

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

    >