آموزش وریلاگ | پروژه وریلاگ
 گروه تلگرام 
VerilogCode.ir
آموزش وریلاگ | پروژه وریلاگ


آموزش وریلاگ - جلسه 14 :

آموزش وریلاگ آموزش Verilog

دانلود فیلم مربوط به (جلسه چهاردهم)

این جلسه قرار نیست موضوع خاصی رو مطرح کنم ، فقط یه سری نکات تکمیلی هست که باید بدونید . گاهی توی برنامه نویسی یه عددی خیلی مورد استفاده قرار میگیره ، برای اینکه هردفعه نیایم این عدد رو تایپ کنیم ، یه پارامتر تعریف میکنیم و اون رو مساوی با عدد مورد نظر قرار میدیم حالا هردفعه بخوایم از اون عدد استفاده کنیم کافیه اسم پارامتر رو بنویسیم .
یا فرض کنید ما یه جمع کننده 4 بیتی ساختیم یهو بهمون میگن اشتباه شد 8 بیتی میخوایم ، حالا باید چیکار کنیم ؟ باید بشینیم کل جمع کننده رو ویرایش کنیم تا تبدیل بشه به جمع کننده 8 بیتی ، ولی اگه از همون ابتدا جمع کننده رو بر حسب پارامتر مثلا n میساختیم فقط کافی بود مقدار n رو 4 یا 8 بذاریم تا جمع کننده 4 یا 8 بیتی یا هرچند بیت داشته باشیم . به مثال زیر دقت کنید :

ISE Project - VerilogCode.ir : \ Box14-1.v
 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 رو عوض کنید.

ISE Project - VerilogCode.ir : \ Box14-2.v
 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
ISE Project - VerilogCode.ir : \ Box14-3.v
 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 مقداردهی اولیه کنیم با خطا مواجه میشیم .

ISE Project - VerilogCode.ir : \ Box14-4.v
 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 بشه .

ISE Project - VerilogCode.ir : \ Box14-5.v
 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 ایرادی نداره و هر کدوم توی یک بلاک تعریف شدن .
اگه یه جایی توی یه مسئله حساس و پیچیده ای دیدید که کار تون گره خورده و یه سیگنال رو باید توی دو تا بلاک مجزا مقداردهی کنید ، هیچ راهی ندارید جز اینکه ساختار مدار تون رو بهم بزنین و یجور دیگه مدار رو پیاده سازی کنین که نیاز نباشه یک سیگنال در دو جا مقداردهی بشه .

ISE Project - VerilogCode.ir : \ Box14-6.v
 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 مقداردهی کنیم ، مجبوریم یک ایگرگ تعریف کنیم و کاراشو روی ایگرگ انجام بدیم .