1 - سطح گیت
جلسه اول (ماژول ها) جلسه دوم (گیت ها) جلسه سوم (نوع سیگنال) جلسه چهارم (پایان فصل)2 - مقداردهی مداوم
جلسه پنجم (اُپراتور ها) جلسه ششم (آرایه ها) جلسه هفتم (شرط ها) جلسه هشتم (فراخوانی ماژول) جلسه نهم (تست بنچ) جلسه دهم (نگاشت حافظه)3 - سطح رفتاری (ترکیبی)
جلسه یازدهم (بلاک Always) جلسه دوازدهم (ابزار ها) جلسه سیزدهم (نوع پردازش) جلسه چهاردهم (نکات تکمیلی) جلسه پانزدهم (پایان فصل)4 - سطح رفتاری (ترتیبی)
جلسه شانزدهم (لبه ها) جلسه هفدهم (تست بنچ 2) جلسه هجدهم (شمارنده ها) جلسه نوزدهم (ضرب و شیفت) جلسه بیستم (استیت ماشین) جلسه بیست و یکم (کشف رشته) جلسه بیست و دوم (فرکانس) جلسه بیست و سوم (نان بلاک) جلسه بیست و چهارم (پایان)5 - جلسات تمرینی
جلسه تمرین اول (تاخیر ها) جلسه تمرین دوم (الحاق-منطق) جلسه تمرین سوم (شیفت ها) جلسه تمرین چهارم (استیت)6 - مثال های پروژه محور
پروژه طراحی پردازنده RTL پروژه پردازنده Maano پروژه پردازنده MIPS7 - ارتباط و گفتگو
ارتباط با نویسنده گروه تلگرام تبلیغات و آگهیآموزش وریلاگ آموزش Verilog
فرض کنید توی یه پروژه ای نیاز به نوشتن برنامه حافظه داریم . مثلا به ما گفتن یک ریز پردازنده رو به زبان وریلاگ طراحی کنید :
همونطور که میبینید ما باید بخش های مختلفی رو کُدنویسی کنیم که یکی از اون بخش ها ، حافظه است . ما چیزِ از پیش ساخته شده ای نداریم که به عنوان حافظه معرفی کنیم ، بلکه خودمون باید یجوری تعریفش کنیم . اصلا حافظه چیه ؟ بیاین یه بار مرور کنیم . حافظه یه تعداد خونه است که این خونه ها دو تا ویژگی دارند ، اولا میشه توشون مقدار گذاشت و دوما این خونه ها آدرس دارند . مثلا میشه گفت خونه 35 حافظه دارای مقدار 812 است. ما اگه یه سیگنال تک بیتی داشته باشیم انگاری یه خونه حافظه داریم که مقدارش فقط میتونه 0 یا 1 باشه ، حالا اگه این سیگنال بجای تک بیتی ، چند بیتی باشه ، ما یه خونه حافظه داریم که مقدارش میتونه یه عددی باشه مثلا همون 812 ، پس تا اینجا نصف راه رو رفتیم . اما مشکل اینجاست ما یدونه خونه حافظه نمیخوایم ، بلکه چندین خونه ی حافظه میخوایم مثلا هزار تا و هرکدوم هم آدرس خاص خودشو داشته باشه ، آیا حوصله مون میکشه هزار تا سیگنال تعریف کنیم ؟ خیلی سخته .
برای اینکار کافیه به ماژول بگیم که به تعداد n تا خونه ی مثلا 8 بیتی به من بده . در این صورت ما یه حافظه داریم که طول خونه های اون 8 بیت هست و مقدار هر خونه میتونه هشت بیت باشه و تعداد کل این خونه های حافظه هم n تا هست . آدرس هر خونه حافظه هم میشه از آدرس 0 تا n-1 که بصورت زیر نوشته میشه :
1- 2- 3- 4- 5- 6- 7- 8- 9-
module MyMemory( ); wire [7:0] MEM [1024:0]; endmodule
در واقع ما اومدیم سیگنال دو بُعدی تعریف کردیم یا همون بُردار دو بُعدی ، مهم نیست اسمش چیه ، اسم این سیگنال رو گذاشتیم MEM که اگه بخوایم یه خونه از حافظه رو صدا کنیم کافیه شماره یا آدرس شو جلوش بصورت MEM[n] بنویسیم که در اینجا MEM[3] یعنی خونه چهارم حافظه که محتواش 8 بیت داده است و یا MEM[0] یعنی خونه اول حافظه که محتواش 8 بیت داده است . در کُد بالا تعداد 1025 تا سیگنال واسطه ای 8 بیتی تعریف کردیم که نقش حافظه رو ایفا میکنه.
خب چرا این جلسه رو گفتم ؟ به دو دلیل ، اول اینکه اگه یجایی خواستید حافظه تعریف کنید ، اینجوری تعریف کنید . دلیل دوم اینکه من حقیقتا قصد داشتم یجوری سیگنال های دو بُعدی رو بهتون درس بدم . گفتم مثال بهتر از حافظه نمیشه زد ، برای همین حافظه رو بهونه کردم تا ازین طریق با سیگنال های دو بُعدی آشنا بشید. البته برای اینکه دست خالی هم نرید بیاین یه مثال باهم حل کنیم :
باتوجه به شکل بالا ، حافظه ای طراحی کنید که اگر سیگنال Read فعال شد، مقدار حافظه در آدرسِ موردنظر ، روی خروجی نمایش داده شود و اگر سیگنال Write فعال بود ، مقدار ورودی Din بر روی آدرس مورد نظر حافظه نوشته شود. توجه : در هنگام Write ، خروجی Dout مقدارش های-امپدانس شود.
1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12- 13- 14- 15- 16-
module MEMORY( input Read, input Write, input [7:0] Din, input [11:0] Address, output [7:0] Dout ); wire [7:0] MyMEM [4095:0]; assign Dout= (Read==1)? MyMEM[Address] : 8'bz; assign MyMEM[Address]= (Write==1)? Din; endmodule
طبق کُد بالا ، برای مواقعی که سیگنال Read فعال است ، خروجی Dout رو مشخص کردیم که برابر با مقدار حافظه در آدرس مورد نظر باشه و اگه سیگنال Read فعال نبود ، خروجی های-امپدانس باشه . برای مواقعی که سیگنال Write فعال است ، مشخص کردیم خونه موردنظر مقدارش عوض بشه و برابر با Din بشه . کد بالا دوتا ایراد داره ، ایراد اول اینکه ما اجازه نداریم یک wire رو با شماره ای نامشخص که هر لحظه ممکنه تغییر کنه ، مقداردهی کنیم . در خط 13 ، MyMEM یک wire با شماره [Address] هست که این آدرس ثابت نیست و هر لحظه میتونه یه مقداری داشته باشه ، بنابراین خطا داریم . پس باید MyMEM رو از نوع reg تعریف کنیم . ایراد دوم اینکه ، فرض کنید ما میخوایم به حافظه مون مقدار اولیه بدیم ، یعنی از ابتدای برنامه قبل از اینکه ماژول شروع به کار کنه خونه های حافظه ما مقدار داشته باشه . برای اینکار باید از ساختار initial استفاده کنیم . در این ساختار اجازه نداریم به wire ها مقدار اولیه بدیم و فقط میتونیم به reg ها مقدار بدیم . پس اینم یه دلیل دیگه که MyMEM باید reg تعریف بشه . خب ما MyMEM رو به عنوان سیگنال واسطه ای reg تعریف میکنیم و مشکلات حل میشه . اما یه مشکل جدید پیش میاد !! ما حق نداریم در ساختار assign یک reg رو مقداردهی کنیم ، بنابراین برای نوشتن خط 13 باید از ساختار always استفاده کنیم که در جلسات آینده بهش خواهیم رسید.
1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12- 13- 14- 15- 16- 17- 18- 19- 20- 21- 22- 23- 24- 25- 26- 27- 28- 29- 30- 31- 32-
module MEMORY( input Read, input Write, input [7:0] Din, input [11:0] Address, output [7:0] Dout ); reg [7:0] MyMEM [4095:0]; initial begin MyMEM[0]=54; MyMEM[1]=19; // ... // ... // ... MyMEM[4095]=28; end assign Dout= (Read==1)? MyMEM[Address] : 8'bz; always @(*) begin if(Write==1) MyMEM[Address]=Din; end endmodule
در کُد بالا به خونه های حافظه مقدار اولیه دادیم و در انتها به کمک ساختار always حالت Write برنامه رو پیاده سازی کردیم ، فعلا شما این خط رو نادیده بگیرید تا در جلسات بعدی بهش برسیم. اگه این حالت ابهام رو دوس ندارید و ذهن کنجکاوی دارید ، ویدئو این جلسه رو رایگان دانلود کنید توی فیلم بیشتر توضیح دادم . دانلود فیلم جلسه
رفتن به جلسه بعد ...
alireza
هعیی تورم
سید محسن
قشنگ نابود شدیم این مدت