1 - سطح گیت
جلسه اول (ماژول ها) جلسه دوم (گیت ها) جلسه سوم (نوع سیگنال) جلسه چهارم (پایان فصل)2 - مقداردهی مداوم
جلسه پنجم (اُپراتور ها) جلسه ششم (آرایه ها) جلسه هفتم (شرط ها) جلسه هشتم (فراخوانی ماژول) جلسه نهم (تست بنچ) جلسه دهم (نگاشت حافظه)3 - سطح رفتاری (ترکیبی)
جلسه یازدهم (بلاک Always) جلسه دوازدهم (ابزار ها) جلسه سیزدهم (نوع پردازش) جلسه چهاردهم (نکات تکمیلی) جلسه پانزدهم (پایان فصل)4 - سطح رفتاری (ترتیبی)
جلسه شانزدهم (لبه ها) جلسه هفدهم (تست بنچ 2) جلسه هجدهم (شمارنده ها) جلسه نوزدهم (ضرب و شیفت) جلسه بیستم (استیت ماشین) جلسه بیست و یکم (کشف رشته) جلسه بیست و دوم (فرکانس) جلسه بیست و سوم (نان بلاک) جلسه بیست و چهارم (پایان)5 - جلسات تمرینی
جلسه تمرین اول (تاخیر ها) جلسه تمرین دوم (الحاق-منطق) جلسه تمرین سوم (شیفت ها) جلسه تمرین چهارم (استیت)6 - مثال های پروژه محور
پروژه طراحی پردازنده RTL پروژه پردازنده Maano پروژه پردازنده MIPS7 - ارتباط و گفتگو
ارتباط با نویسنده گروه تلگرام تبلیغات و آگهیآموزش وریلاگ آموزش Verilog
این جلسه قرار نیست موضوع خاصی رو مطرح کنم ، فقط یه سری نکات تکمیلی هست که باید بدونید . گاهی توی برنامه نویسی یه عددی خیلی مورد استفاده قرار میگیره ، برای اینکه هردفعه نیایم این عدد رو تایپ کنیم ، یه پارامتر تعریف میکنیم و اون رو مساوی با عدد مورد نظر قرار میدیم حالا هردفعه بخوایم از اون عدد استفاده کنیم کافیه اسم پارامتر رو بنویسیم . یا فرض کنید ما یه جمع کننده 4 بیتی ساختیم یهو بهمون میگن اشتباه شد 8 بیتی میخوایم ، حالا باید چیکار کنیم ؟ باید بشینیم کل جمع کننده رو ویرایش کنیم تا تبدیل بشه به جمع کننده 8 بیتی ، ولی اگه از همون ابتدا جمع کننده رو بر حسب پارامتر مثلا n میساختیم فقط کافی بود مقدار n رو 4 یا 8 بذاریم تا جمع کننده 4 یا 8 بیتی یا هرچند بیت داشته باشیم . به مثال زیر دقت کنید :
1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12- 13- 14- 15- 16- 17-
module Test( input [n:0] X, input [n:0] Y, output [n+1:0] F output reg [n+1:0] H ); parameter n=3; always @(*) begin H=X+Y; end assign F=X+n; endmodule
وقتی یه پارامتر با اسم دلخواه مثلا n تعریف میکنیم و برابر با 3 قرار میدیم ، کامپایلر توی کد برنامه هرجا n ببینه ، به جاش مقدار 3 رو میذاره ، بنابراین توی کد بالا ورودی های X و Y چهار بیتی تعریف میشن (صفر تا سه) و خروجی ها پنج بیتی تعریف میشن . حالا H میشه جمع دو عدد 4 بیتی X و Y ، در خط بعدی هم F میشه جمع ورودی X با عدد 3 . اگه توی خط 8 به جای 3 عدد 7 رو بذاریم ، ورودی های X و Y هشت بیتی و خروجی های ما 9 بیتی میشه و H برابر میشه با جمع دو عدد هشت بیتی X و Y و در نهایت F میشه جمع ورودی X با عدد 7 . پس خیلی ساده است هرجا n دیدید بجاش مقدار شو میذارید و اگه خواستید تغییری در کل ماژول ایجاد کنید فقط کافیه مقدار n رو عوض کنید.
1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12- 13- 14- 15- 16- 17- 18-
module Test( input Clk, output reg [7:0] F ); initial begin F=0; end always @(posedge Clk) begin F=F+1; end endmodule
1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12- 13- 14- 15- 16- 17- 18- 19- 20- 21-
module Test( input [1:0] Address, output reg [7:0] F ); reg [7:0] MEM [3:0]; initial begin MEM[0]=8'b0; MEM[1]=8'b0; MEM[2]=8'b0; MEM[3]=8'b0; end always @(*) begin F=MEM[Address]; end endmodule
ببینید این شیوه مقداردهی اولیه اصلا حرفه ای نیست ، توی طراحی های واقعی این روش اصلا مورد پذیرش نیست و باید یک سیگنال ریست تعریف کنیم و به کمک این سیگنال عمل مقداردهی اولیه رو پیاده سازی کنیم (توی فصل بعدی میگم چطور). اما مقداردهی اولیه با initial در تست بنچ نویسی بسیار استفاده میشه و کاملا هم مورد قبوله ، پس ما سعی میکنیم توی کد ماژول اصلی مون ازش استفاده نکنیم ولی توی تست بنچ مون ازش استفاده میکنیم . نکته آخر اینکه فقط سیگنال هایی که از نوع reg باشند قابلیت مقداردهی اولیه دارند و اگه wire ها رو توی initial مقداردهی اولیه کنیم با خطا مواجه میشیم .
1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12- 13- 14-
module Test( input [7:0] X, output reg Y ); always @(*) begin if(X<0) // if(X[7]==1) Y=1; else Y=0; end endmodule
به کد بالا نگاه کنید ، فرض کنید میخوایم بگیم هروقت X عدد منفی بود خروجی 1 بشه . این کد اشتباست چرا ؟ چون X از 00000000 تا 11111111 میتونه باشه و از نظر وریلاگ این عدد از 0 تا 255 هست . در واقع عدد منفی برای وریلاگ تعریف نشده است اما ما خودمون توی دنیای دیجیتال یه سری قرارداد داریم که اعداد منفی رو پوشش میدن مثلا گاهی با خودمون قرارداد میبندیم هرگاه بزرگترین بیت عدد مون صفر بود ، عدد ما مثبت و هرگاه بزرگترین بیت عدد مون یک بود ، عدد ما منفی خواهد بود (قرارداد مکمل 2) پس کافیه به جای X<0 بنویسیم X[7]==1 تا اگه بزرگترین بیت عدد مون یک بود ، خروجی ما 1 بشه .
1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12- 13- 14- 15- 16- 17- 18- 19- 20- 21- 22- 23-
module Test( input A, input B, output reg Y, output reg F, output reg H ); always @(*) begin Y=A&B; F=A|B; end always @(*) begin if(A==0) Y=B; H=A+B; end endmodule
حالا به کد بالا نگاه کنید ، اینکه دو تا بلاک always داریم هیچ ایرادی نداره ، میتونیم صد تا داشته بشیم اما ایراد اینجاست یک سیگنال در دوتا بلاک مجزا تعریف بشه . به Y نگاه کنید هم توی بلاک اولی مقداردهی شده و هم توی بلاک دومی و این خطاست . نحوه مقداردهی F و H ایرادی نداره و هر کدوم توی یک بلاک تعریف شدن . اگه یه جایی توی یه مسئله حساس و پیچیده ای دیدید که کار تون گره خورده و یه سیگنال رو باید توی دو تا بلاک مجزا مقداردهی کنید ، هیچ راهی ندارید جز اینکه ساختار مدار تون رو بهم بزنین و یجور دیگه مدار رو پیاده سازی کنین که نیاز نباشه یک سیگنال در دو جا مقداردهی بشه .
1- 2- 3- 4- 5- 6- 7- 8- 9- 10- 11- 12- 13- 14- 15- 16- 17- 18- 19- 20-
module Test( input WR, input [1:0] Address, inout [7:0] Data ); reg [7:0] Y; reg [7:0] MEM [3:0]; assign Data = (WR)? 8'bz : Y; always @(*) begin if(WR) MEM[Address]=Data; else Y=MEM[Address]; end endmodule
و اما نکته آخر ، ما علاوه بر سیگنال های ورودی و خروجی ، یک نوع سیگنال داریم که هم ورودی و هم خروجی باشه یعنی خط 4 سیگنال inout . البته به این معنی نیست که این سیگنال همزمان بتونه وظیفه ورودی و وظیفه خروجی رو انجام بده ، بلکه در برخی زمان ها نقش ورودی رو ایفا میکنه و در برخی زمان ها نقش خروجی . کد بالا یک حافظه است که با یک بودن WR دیتای ورودی رو در حافظه مینویسه و با صفر بودن WR محتوای حافظه رو بر دیتای خروجی قرار میده . بنابراین سیگنال Data باید هنگامی که WR یک هست ، نقش ورودی رو بازی کنه تا ما مقدار ورودی رو بگیریم و توی حافظه بنویسیم . برای اینکار وریلاگ برای ما قانون گذاشته که باید مقدار Data رو های-امپدانس بذاریم . زمانی هم که WR صفر هست و قراره مقدار حافظه رو روی خروجی قرار بدیم ، سیگنال Data ما برابر میشه با مقدار خروجی . پس توی خط 10 گفتیم اگه خواستیم ورودی بگیریم یعنی وقتی WR یک بود ، Data برابر با Hz بشه و اگه خواستیم یک خروجی داشته باشیم Data برابر با مقدار Y که همون مقدار حافظه است بشه . البته میشد همون ابتدا Data رو کلا ورودی در نظر بگیریم و Y رو هم خروجی مدار قرار بدیم ولی خواستم یه مثال ویژه بزنم تا ببینید با سیگنال های inout چطور باید کدنویسی کنیم . دانلود فیلم جلسه
رفتن به جلسه بعد ...سید ابوالفضل
سلام و خسته نباشید خدمت شما مدرس محترم ممنون از مطالب فوق العاده درجه یکتون سوالی بنده داشتم که ممنون میشم پاسخ بدید دلیل تعریف سیگنال y در مثال آخر چیه ؟ یعنی نمی توانستیم محتوا رو مستقیم از مموری روی data بذاریم و واسطه y رو حذف کنیم؟ تشکر
سید محسن
سلام ، دنباله ای از دلیل داره که به ترتیب گوش بدید تا بگم بهتون اولا ، چون سینگال دیتا ما ، هم ورودی و هم خروجی هست (این اوت) باید حتما از نوع وایر باشه . دوما ، چون سیگنال دیتای ما وایر شد ، حق نداریم توی always مقداردهیش کنیم بلکه فقط اجازه داریم توی assign بهش مقدار بدیم. سوما ، حالا شما اگه میتونید توی خط 10 ، به جای Y بنویسید MEM[Address] که کار تمومه و نیازی به تعریف ایگرگ نیست . ولی مشکل اینجاست وریلاگ خطا میده ، میگه عبارت MEM[Address] رو فقط می تونی توی always استفاده کنی . چهارما ، حالا که فقط میتونیم توی always استفاده کنیم و از طرفی هم حق نداریم دیتا که وایر هست رو توی always مقداردهی کنیم ، مجبوریم یک ایگرگ تعریف کنیم و کاراشو روی ایگرگ انجام بدیم .