1 - سطح گیت
جلسه اول (ماژول ها) جلسه دوم (گیت ها) جلسه سوم (نوع سیگنال) جلسه چهارم (پایان فصل)2 - مقداردهی مداوم
جلسه پنجم (اُپراتور ها) جلسه ششم (آرایه ها) جلسه هفتم (شرط ها) جلسه هشتم (فراخوانی ماژول) جلسه نهم (تست بنچ) جلسه دهم (نگاشت حافظه)3 - سطح رفتاری (ترکیبی)
جلسه یازدهم (بلاک Always) جلسه دوازدهم (ابزار ها) جلسه سیزدهم (نوع پردازش) جلسه چهاردهم (نکات تکمیلی) جلسه پانزدهم (پایان فصل)4 - سطح رفتاری (ترتیبی)
جلسه شانزدهم (لبه ها) جلسه هفدهم (تست بنچ 2) جلسه هجدهم (شمارنده ها) جلسه نوزدهم (ضرب و شیفت) جلسه بیستم (استیت ماشین) جلسه بیست و یکم (کشف رشته) جلسه بیست و دوم (فرکانس) جلسه بیست و سوم (نان بلاک) جلسه بیست و چهارم (پایان)5 - جلسات تمرینی
جلسه تمرین اول (تاخیر ها) جلسه تمرین دوم (الحاق-منطق) جلسه تمرین سوم (شیفت ها) جلسه تمرین چهارم (استیت)6 - مثال های پروژه محور
پروژه طراحی پردازنده RTL پروژه پردازنده Maano پروژه پردازنده MIPS7 - ارتباط و گفتگو
ارتباط با نویسنده گروه تلگرام تبلیغات و آگهیآموزش وریلاگ آموزش Verilog
این جلسه به معرفی ابزار هایی خواهم پرداخت که در بلاک always قابل استفاده هستند .
شکل بالا یک دیکودر 2 به 4 رو نشون میده که قراره به روش توصیف رفتاری کُد شو بنویسیم :
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- 33- 34- 35- 36- 37-
module DEC2_4( input en, input [1:0] X, output reg [3:0] Y ); always @(*) begin if (en==0) begin Y=4'b0000; end else if (X==0) begin Y=4'b0001; end else if (X==1) begin Y=4'b0010; end else if (X==2) begin Y=4'b0100; end else if (X==3) begin Y=4'b1000; end else begin Y=4'bzzzz; end end endmodule
کد را با استفاده از ابزار if نوشتیم ، توجه کنید هر بخش از if یا else if یا else که استفاده می کنید ، حتما باید کد های اون بخش رو درون یک begin و end قرار بدید . طبق تبصره جلسه قبل ، اگه مثل کد بالا ، خطوط درون if یک خط باشه الزامی به گذاشتن begin و end نداریم اما من گذاشتم چون این کار بسیار پسندیده است و از بروز برخی خطاهای محاسباتی جلوگیری میکنه . کد بالا توضیح چندانی نداره اگه en صفر باشه باعث غیرفعال شدن خروجی میشه و طبق جدول خروجی ما صفر میشه در غیر اینصورت با توجه به مقادیر X خروجی ما مقداردهی میشه ، اگه سوالی دارید توی کامنت بپرسید. در ضمن توی شکل سیگنال ها تک بیتی بودن ولی من بُرداری در نظر گرفتم تا ببینید همه چی دست خودمونه . چند نکته بسیار مهم در مورد if ها وجود داره که توضیحش توی نوشتار واقعا سخته و من توی ویدئو بهتون این توضیحات رو دادم ، برخی تصورات غلط در مورد if ها وجود داره که سعی کردم برای شما شفاف سازی کنم و به بررسی شرط های تودرتو هم پرداختم .
در شکل بالا یک انکودر 4 به 2 رو میبینیم که عملکردش معکوس دیکودر هست ، حالا با عملکردش زیاد کار نداریم مهم رفتار ماژول هست که توی جدول صحت ذکر شده و باید طبق اون کد وریلاگ رو بنویسیم ، توجه کنید این انکودر ، سیگنال en نداره ، چرا ؟ چون طراح سوال دلش خواسته نذاره.
1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12- 13- 14- 15- 16- 17- 18- 19-
module Encoder( input [3:0] X, output reg [1:0] Y ); always @(*) begin case (X) 4'b0001 : begin Y=0; end 4'b0010 : begin Y=1; end 4'b0100 : begin Y=2; end 4'b1000 : begin Y=3; end default : begin Y=2'bzz; end endcase end endmodule
میتونستیم با if این مدار رو توصیف کنیم اما برای اینکه حجم کُد مون کمتر بشه از case استفاده کردیم . حالا بیاین ببینیم نحوه نوشتن یک case چجوریه . جلوی هر case باید بنویسید که مقدار کدوم سیگنال برای شما شرایط متفاوتی رو رقم میزنه . به جدول صحت نگاه کنید ، این سیگنال X هست که مقدارش تعیین میکنه خروجی چی باشه . پس ما case رو روی X اعمال میکنیم . حالا در هر خط باید بگیم به ازای چه مقداری از X چه دستوری اجرا بشه ، در خط اول گفتیم اگه X مقدار 0001 رو داشت Y=0 بشه و اگه X مقدار 0010 رو داشت Y=1 بشه . در خط آخر گفتیم بصورت پیشفرض یا همون دیفالت ، اگه X هیچکدوم از مقدار های بالا رو نداشت و یه مقدار دیگه ای داشت مثلا مقدار 0111 رو داشت ، دستور Y=2’bz انجام بشه و خروجی "های امپدانس" بشه و در نهایت با یک case به کار endcase خاتمه میدیم . باز هم طبق قوانین begin و end باید برای هر خط از دستورات case این begin و end رو گذاشت ، ولی اگه مشابه مثال بالا ، دستورات یک خط بودن میشه از گذاشتن اش صرف نظر کرد (اما بهتره بذارید) . نکته بعدی اینکه گذاشتن default در case ها کاملا اختیاری هست و می تونیم نذاریم . یه نکته هم بگم ، وقتی من میگم دستورات یک خط هست یا چند خط ، حواس تون باشه یچیزی مثل این Y=4;G=5; درسته که توی یک خط نوشته شده اما 2 دستور محسوب میشه و وریلاگ 2 خط در نظرش میگیره پس حتما باید بین begin و end باشه .
شکل بالا رو ببینید . باز هم یک انکودر داریم ولی این بار سیگنال فعالساز en هم بهش اضافه شده که روی مقدار خروجی ما تاثیر داره . طبق جدول صحت اگه en مقدارش یک باشه ، مقادیر مثال قبلی معتبره ولی اگه en صفر باشه ، مقدار خروجی "های امپدانس" خواهد شد ، چرا ؟ چون طراح سوال اینطور از ما خواسته .
1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12- 13- 14- 15- 16- 17- 18- 19- 20-
module EncoderB( input en, input [3:0] X, output reg [1:0] Y ); always @(*) begin case (X) 4'b0001 : begin if (en) Y=0; else Y=2'bz; end 4'b0010 : begin if (en) Y=1; else Y=2'bz; end 4'b0100 : begin if (en) Y=2; else Y=2'bz; end 4'b1000 : begin if (en) Y=3; else Y=2'bz; end default : begin Y=2'bz; end endcase end endmodule
همونطور که میبینید ما اجازه داریم ابزار هارو درون یکدیگر استفاده کنیم . مثلا توی case از if استفاده کنیم یا بالعکس . کد ما مشابه مثال قبلی خواهد بود با این تفاوت که در هر حالت از case یک شرط گذاشتیم و گفتیم en بررسی شود ، اگر en یک بود مقدار Y داده شود در غیر اینصورت اگر en صفر بود مقدار Y برابر های امپدانس شود . نکته اول اینکه ، در این مثال فقط برای case ما begin و end گذاشتیم ولی برای if و else چون دستور درونِ شان یک خطی بود ، نذاشتیم . البته میتونستیم بذاریم و بهتر بود بذاریم اما چون توی عکس جا نمیشد نذاشتم . نکته دوم ، چون en تک بیتی است ، if(en) همان if(en==1) خواهد بود و if(~en) معادل if(en==0) است . برای کوتاه تر شدن برنامه ، میشه این مدلی هم کد نوشت که ما نوشتیم (توضیح بیشتر در ویدئو) نکته سوم ، Y=2’bzz با Y=2’bz یکیه ، وقتی میخای سیگنال رو مساوی HighZ قرار بدی ، لزومی نداره به تعداد بیت ها z بذاری ، ولی خب اگه بذاری هم ایرادی نداره . پس Y=8’bzzzzzzzz با Y=8’bz یکیه .
1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12- 13- 14- 15- 16- 17- 18- 19- 20-
module EncoderB( input en, input [3:0] X, output reg [1:0] Y ); always @(*) begin case ({en,X}) {1'b1,4'b0001} : begin Y=0; end {1'b1,4'b0010} : begin Y=1; end {1'b1,4'b0100} : begin Y=2; end {1'b1,4'b1000} : begin Y=3; end default : begin Y=2'bz; end endcase end endmodule
به کد بالا نگاه کنید ، مسئله همون قبلی است اما به روش دیگه ای حل کردیم . به جای اینکه روی X عمل case را انجام دهیم ، روی الحاقی از X و en این کار را کردیم . میبینیم که کد بالا دقیقا عملکردی مشابه مثال قبلی داره اما با این تکنیک حجم کُد بسیار کمتر شد .
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-
module EncoderB( input en, input [3:0] X, output reg [1:0] Y ); always @(*) begin if (en) begin case (X) 4'b0001 : begin Y=0; end 4'b0010 : begin Y=1; end 4'b0100 : begin Y=2; end 4'b1000 : begin Y=3; end default : begin Y=2'bz; end endcase end else if (~en) begin Y=2'bz; end end endmodule
کد بالا هم یک روش دیگه برای حل مسئله قبلی ماست ، اومدیم توی if از case استفاده کردیم . به این صورت که گفتیم اگه en یک بود چه کاری انجام بشه و اگه en صفر بود چه کاری انجام بشه . طبق نحوه begin و end ای که برای if و else if گذاشتیم ، خطوط 11 تا 21 برای if و خطوط 23 تا 25 برای else if می باشد. بنابراین برای حل یک مثال ، هزاران راه حل و ترکیب ابزار وجود داره که انتخابش دست خود ماست .
خب حالا این مسئله رو به کلی فراموش کنید ، میخوام یه مبحث جدید مطرح کنم ، به شکل زیر نگاه کنید یک انکودر اولویت دار رو میبینید :
فرقش با انکودر قبلی چیه ؟ به جدول صحت نگاه کنید ، در این انکودر برخی مقادیر ورودی (X) ، نه صفر هست نه یک ، بلکه به جای مقادیر شون ضربدر گذاشتیم که اصطلاحا بهشون میگیم dont care ، امیدوارم ضربدر هارو با ایکس اشتباه نگیرید . اگه توی درک این انکودر مشکل دارید به گوگل مراجعه کنید و بعد از اینکه فهمیدید قضیه چیه بیاین سراغ کد زیر :
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-
module Encoderx( input en, input [3:0] X, output reg [1:0] Y ); always @(*) begin if (en) begin casex (X) 4'b0001 : begin Y=0; end 4'b001X : begin Y=1; end 4'b01XX : begin Y=2; end 4'b1XXX : begin Y=3; end default : begin Y=2'bz; end endcase end else if (~en) begin Y=2'bz; end end endmodule
قسمت if مثل مسئله قبلی هست ، اما بخش case متفاوت شده . ما یه ابزار داریم به اسم casex که کجا استفاده میشه ؟ وقتی یک ورودی دارید که مقادیر اون بجای مثلا 0110 باید 0X11 باشه یا بجای 1111 قراره 1XXX باشه ، اینجا نمیشه از case استفاده کرد بلکه باید از casex استفاده کرد . در ادامه مقادیر رو بصورت کاملا دقیق طبق جدول صحت قرار دادیم . ابزار casex کاربرد چندانی نداره و فقط برخی جاهای بسیار خاص استفاده میشه و من تمایلی نداشتم که بگم ، منتها یکی از دوستان درخواست کردن که این بخش رو مثال بزنم و زدم . دانلود فیلم جلسه
رفتن به جلسه بعد ...محمد
سلام دوست عزیز ممنونم از اموزش هایی که گذاشتی ، خیلی لطف کردی انقدر وقت گذاشتی و اینقدر روان توضیح دادی بهت پیام دادم که هم تشکر کنم از بابت زحمتی که کشیدی هم یه سوالی ذهنم را مشغول کرده الان داشتم جلسه دوازدهم میخوندم متوجه شدم خروجی Y را از نوع reg تعریف کردی در صورتی که گفتی برای ورودی ها و خروجی ها باید از نوع wire تعریف کنید ممنون میشم اگه توضیح بدی که من کجا رو درست متوجه نشدم یا دارم اشتباه میکنم
سید محسن
سلام ، ممنون از توجه شما ، به جلسه سوم مراجعه کنید ، احتمالا از خاطرتون رفته ، اونجا کاملا شفاف گفتیم هرگاه یک خروجی یا یک سیگنال در بلاک always مقدار دهی بشه ، نوع اون باید از نوع reg باشه .
احسان
سلام ممنون از آموزشتون واقعا حرفی باقی نمی مونه فقط میتونم بگم عالی بود
سید محسن
خواهش میکنم ، سایت رو به دوستان تون معرفی کنید .