Lập mô hình hành vi & thời gian trong Verilog

Các mô hình hành vi trong Verilog chứa các câu lệnh thủ tục, điều khiển mô phỏng và thao tác các biến của kiểu dữ liệu. Tất cả các câu lệnh này được chứa trong các thủ tục. Mỗi thủ tục có một luồng hoạt động được liên kết với nó.

Trong quá trình mô phỏng mô hình hành vi, tất cả các luồng được xác định bởi câu lệnh "always" và "initial" bắt đầu cùng nhau tại thời điểm mô phỏng "zero". Các câu lệnh ban đầu được thực thi một lần và các câu lệnh always được thực thi lặp đi lặp lại. Trong mô hình này, các biến thanh ghi a và b được khởi tạo thành nhị phân 1 và 0 tương ứng tại thời điểm mô phỏng 'không'. Câu lệnh ban đầu sau đó được hoàn thành và không được thực thi lại trong quá trình chạy mô phỏng đó. Câu lệnh ban đầu này chứa một khối đầu cuối (còn được gọi là khối tuần tự) các câu lệnh. Trong khối loại begin-end này, a được khởi tạo đầu tiên, sau đó là b.

Ví dụ về Mô hình Hành vi

module behave; 
reg [1:0]a,b; 

initial 
begin 
   a = ’b1; 
   b = ’b0; 
end 

always 
begin 
   #50 a = ~a; 
end 

always 
begin 
   #100 b = ~b; 
end 
End module

Nhiệm vụ thủ tục

Các phép gán thủ tục là để cập nhật các biến reg, số nguyên, thời gian và bộ nhớ. Có sự khác biệt đáng kể giữa chuyển nhượng theo thủ tục và chuyển nhượng liên tục như được mô tả dưới đây:

Các phép gán liên tục thúc đẩy các biến ròng và được đánh giá và cập nhật bất cứ khi nào toán hạng đầu vào thay đổi giá trị.

Các phép gán thủ tục cập nhật giá trị của các biến thanh ghi dưới sự điều khiển của các cấu trúc luồng thủ tục bao quanh chúng.

Phía bên phải của phép gán thủ tục có thể là bất kỳ biểu thức nào đánh giá một giá trị. Tuy nhiên, phần chọn ở phía bên phải phải có chỉ số không đổi. Phía bên tay trái chỉ ra biến nhận nhiệm vụ từ phía bên tay phải. Phía bên trái của việc phân công thủ tục có thể có một trong các dạng sau:

  • biến đăng ký, số nguyên, thực hoặc thời gian - Một phép gán cho tham chiếu tên của một trong các kiểu dữ liệu này.

  • chọn bit của một thanh ghi, số nguyên, thực hoặc biến thời gian - Một phép gán cho một bit duy nhất để các bit khác không bị ảnh hưởng.

  • lựa chọn một phần của một biến thanh ghi, số nguyên, thực hoặc thời gian - Lựa chọn một phần của hai hoặc nhiều bit liền kề để không tác động đến phần còn lại của các bit. Đối với hình thức chọn từng phần, chỉ có các biểu thức hằng là hợp pháp.

  • phần tử bộ nhớ - Một từ duy nhất của bộ nhớ. Lưu ý rằng lựa chọn bit và chọn một phần là bất hợp pháp trên các tham chiếu phần tử bộ nhớ.

  • nối của bất kỳ dạng nào ở trên - Có thể chỉ định một phép ghép của bất kỳ dạng nào trong bốn dạng trước đó, điều này có hiệu quả phân vùng kết quả của biểu thức bên phải và gán các phần phân vùng, theo thứ tự, cho các phần khác nhau của phép nối.

Sự chậm trễ trong việc phân công (không phải để tổng hợp)

Trong một phép gán bị trễ, đơn vị thời gian Δt sẽ trôi qua trước khi câu lệnh được thực thi và việc gán tay trái được thực hiện. Với độ trễ trong quá trình gán, vế phải được đánh giá ngay lập tức nhưng có độ trễ là Δt trước khi kết quả được đặt vào nhiệm vụ bên trái. Nếu một quy trình khác thay đổi tín hiệu bên phải trong thời gian Δt, thì nó không ảnh hưởng đến đầu ra. Sự chậm trễ không được hỗ trợ bởi các công cụ tổng hợp.

Cú pháp

  • Procedural Assignmentbiến = biểu thức

  • Delayed assignment# Biến Δt = biểu thức;

  • Intra-assignment delaybiểu thức biến = # Δt;

Thí dụ

reg [6:0] sum; reg h, ziltch; 
sum[7] = b[7] ^ c[7]; // execute now. 
ziltch = #15 ckz&h; /* ckz&a evaluated now; ziltch changed 
after 15 time units. */ 

#10 hat = b&c; /* 10 units after ziltch changes, b&c is
evaluated and hat changes. */

Chặn bài tập

Một câu lệnh gán thủ tục chặn phải được thực hiện trước khi thực hiện các câu lệnh theo sau nó trong một khối tuần tự. Câu lệnh gán thủ tục chặn không ngăn cản việc thực hiện các câu lệnh theo sau nó trong một khối song song.

Cú pháp

Cú pháp để gán thủ tục chặn như sau:

<lvalue> = <timing_control> <expression>

Trong đó, lvalue là kiểu dữ liệu hợp lệ cho một câu lệnh gán thủ tục, = là toán tử gán và điều khiển thời gian là độ trễ gán trong tùy chọn. Độ trễ điều khiển thời gian có thể là điều khiển độ trễ (ví dụ: # 6) hoặc điều khiển sự kiện (ví dụ: @ (posedge clk)). Biểu thức là giá trị bên phải mà trình mô phỏng chỉ định cho bên trái. Toán tử gán = được sử dụng bằng cách chặn các phép gán thủ tục cũng được sử dụng bởi phép gán liên tục theo thủ tục và phép gán liên tục.

Thí dụ

rega = 0; 
rega[3] = 1;            // a bit-select 
rega[3:5] = 7;          // a part-select 
mema[address] = 8’hff;  // assignment to a memory element 
{carry, acc} = rega + regb;  // a concatenation

Nhiệm vụ không chặn (RTL)

Việc gán thủ tục không chặn cho phép bạn lên lịch cho việc gán mà không chặn luồng thủ tục. Bạn có thể sử dụng câu lệnh thủ tục không chặn bất cứ khi nào bạn muốn thực hiện một số chỉ định đăng ký trong cùng một bước thời gian mà không cần quan tâm đến thứ tự hoặc sự phụ thuộc vào nhau.

Cú pháp

Cú pháp để gán thủ tục không chặn như sau:

<lvalue> <= <timing_control> <expression>

Trong đó lvalue là kiểu dữ liệu hợp lệ cho một câu lệnh gán thủ tục, <= là toán tử gán không chặn và điều khiển thời gian là điều khiển thời gian gán trong tùy chọn. Độ trễ điều khiển thời gian có thể là điều khiển trễ hoặc điều khiển sự kiện (ví dụ: @ (posedge clk)). Biểu thức là giá trị phía bên phải mà trình mô phỏng gán cho phía bên trái. Toán tử gán không chặn là toán tử giống như toán tử mà trình mô phỏng sử dụng cho toán tử quan hệ nhỏ hơn bậc. Trình mô phỏng diễn giải toán tử <= là một toán tử quan hệ khi bạn sử dụng nó trong một biểu thức và giải thích toán tử <= thành một toán tử gán khi bạn sử dụng nó trong một cấu trúc gán thủ tục không chặn.

Cách trình mô phỏng đánh giá phép gán thủ tục không chặn Khi trình mô phỏng gặp một phép gán thủ tục không chặn, trình mô phỏng sẽ đánh giá và thực hiện phép gán thủ tục không chặn theo hai bước như sau:

  • Trình mô phỏng đánh giá phía bên phải và lên lịch việc gán giá trị mới diễn ra tại thời điểm được chỉ định bởi điều khiển thời gian thủ tục. Trình mô phỏng đánh giá phía bên phải và lên lịch việc gán giá trị mới diễn ra tại thời điểm được chỉ định bởi điều khiển thời gian thủ tục.

  • Vào cuối bước thời gian, trong đó độ trễ nhất định đã hết hoặc sự kiện thích hợp đã diễn ra, trình mô phỏng thực hiện phép gán bằng cách gán giá trị cho phía bên trái.

Thí dụ

module evaluates2(out); 
output out; 
reg a, b, c; 
initial 

begin 
   a = 0; 
   b = 1; 
   c = 0; 
end 
always c = #5 ~c; 
always @(posedge c) 

begin 
   a <= b; 
   b <= a; 
end 
endmodule

Điều kiện

Câu lệnh điều kiện (hoặc câu lệnh if-else) được sử dụng để đưa ra quyết định liệu một câu lệnh có được thực thi hay không.

Về mặt hình thức, cú pháp như sau:

<statement> 
::= if ( <expression> ) <statement_or_null> 
||= if ( <expression> ) <statement_or_null> 
   else <statement_or_null> 
<statement_or_null> 

::= <statement> 
||= ;

<expression> được đánh giá; nếu nó đúng (nghĩa là có một giá trị đã biết khác 0), câu lệnh đầu tiên sẽ thực thi. Nếu nó sai (có giá trị bằng 0 hoặc giá trị là x hoặc z), câu lệnh đầu tiên không thực hiện. Nếu có một câu lệnh else và <biểu thức> sai, câu lệnh else sẽ thực thi. Vì giá trị số của biểu thức if được kiểm tra bằng 0 nên có thể sử dụng một số phím tắt nhất định.

Ví dụ, hai câu lệnh sau đây thể hiện cùng một logic:

if (expression) 
if (expression != 0)

Vì phần khác của if-else là tùy chọn, có thể có sự nhầm lẫn khi một phần khác bị bỏ qua khỏi chuỗi if lồng nhau. Điều này được giải quyết bằng cách luôn liên kết cái khác với cái gần nhất trước đó nếu thiếu cái khác.

Thí dụ

if (index > 0) 
if (rega > regb) 
   result = rega; 
   else // else applies to preceding if 
   result = regb; 

If that association is not what you want, use a begin-end block statement 
to force the proper association 

if (index > 0) 
begin 

if (rega > regb) 
result = rega; 
end 
   else 
   result = regb;

Cấu tạo của: if- else- if

Việc xây dựng sau đây xảy ra thường xuyên đến mức đáng để thảo luận riêng.

Example

if (<expression>) 
   <statement> 
   else if (<expression>) 
   <statement> 
   else if (<expression>) 
   <statement> 
   else  
   <statement>

Chuỗi if (được gọi là cấu trúc if-else-if) này là cách tổng quát nhất để viết một quyết định nhiều chiều. Các biểu thức được đánh giá theo thứ tự; nếu bất kỳ biểu thức nào là đúng, câu lệnh liên kết với nó sẽ được thực thi và điều này kết thúc toàn bộ chuỗi. Mỗi câu lệnh là một câu lệnh đơn hoặc một khối câu lệnh.

Phần khác cuối cùng của cấu trúc if-else-if xử lý 'không có trường hợp nào ở trên' hoặc trường hợp mặc định mà không có điều kiện nào khác được thỏa mãn. Đôi khi không có hành động rõ ràng nào cho mặc định; trong trường hợp đó, dấu khác có thể được bỏ qua hoặc nó có thể được sử dụng để kiểm tra lỗi nhằm bắt một điều kiện bất khả thi.

Báo cáo tình huống

Câu lệnh case là một câu lệnh quyết định nhiều chiều đặc biệt để kiểm tra xem một biểu thức có khớp với một trong số các biểu thức khác và các nhánh tương ứng hay không. Câu lệnh case hữu ích để mô tả, ví dụ, giải mã lệnh của bộ vi xử lý. Câu lệnh case có cú pháp sau:

Example

<statement> 
::= case ( <expression> ) <case_item>+ endcase 
||= casez ( <expression> ) <case_item>+ endcase 
||= casex ( <expression> ) <case_item>+ endcase 
<case_item> 
::= <expression> <,<expression>>* : <statement_or_null> 
||= default : <statement_or_null> 
||= default <statement_or_null>

Các biểu thức trường hợp được đánh giá và so sánh theo thứ tự chính xác mà chúng được đưa ra. Trong quá trình tìm kiếm tuyến tính, nếu một trong các biểu thức mục trường hợp khớp với biểu thức trong dấu ngoặc đơn, thì câu lệnh liên kết với mục trường hợp đó được thực thi. Nếu tất cả các phép so sánh không thành công và mục mặc định được đưa ra, thì câu lệnh mục mặc định sẽ được thực thi. Nếu câu lệnh mặc định không được đưa ra và tất cả các phép so sánh đều thất bại, thì không câu lệnh nào trong mục trường hợp được thực thi.

Ngoài cú pháp, câu lệnh trường hợp khác với cấu trúc if-else-if đa chiều theo hai cách quan trọng:

  • Các biểu thức điều kiện trong cấu trúc if-else-if tổng quát hơn là so sánh một biểu thức với một số biểu thức khác, như trong câu lệnh trường hợp.

  • Câu lệnh case cung cấp một kết quả xác định khi có các giá trị x và z trong một biểu thức.

Tuyên bố vòng lặp

Có bốn loại câu lệnh lặp. Chúng cung cấp một phương tiện để kiểm soát việc thực hiện một câu lệnh không, một hoặc nhiều lần.

  • mãi mãi liên tục thực hiện một câu lệnh.

  • lặp lại thực hiện một câu lệnh một số lần cố định.

  • trong khi thực hiện một câu lệnh cho đến khi một biểu thức trở thành sai. Nếu biểu thức bắt đầu bằng sai, câu lệnh không được thực thi.

  • để kiểm soát việc thực thi (các) câu lệnh liên quan của nó theo quy trình ba bước, như sau:

    • Thực thi một phép gán thường được sử dụng để khởi tạo một biến kiểm soát số vòng lặp được thực thi

    • Đánh giá một biểu thức — nếu kết quả là 0, vòng lặp for sẽ thoát ra và nếu nó không phải là 0, vòng lặp for thực hiện (các) câu lệnh liên quan và sau đó thực hiện bước 3

    • Thực thi một phép gán thường được sử dụng để sửa đổi giá trị của biến loopcontrol, sau đó lặp lại bước 2

Sau đây là các quy tắc cú pháp cho các câu lệnh lặp:

Example

<statement> 
::= forever <statement> 
||=forever 
begin 
   <statement>+ 
end  

<Statement> 
::= repeat ( <expression> ) <statement> 
||=repeat ( <expression> ) 
begin
   <statement>+ 
end  

<statement> 
::= while ( <expression> ) <statement> 
||=while ( <expression> ) 
begin 
   <statement>+ 
end  
<statement> 
::= for ( <assignment> ; <expression> ; <assignment> ) 
<statement> 
||=for ( <assignment> ; <expression> ; <assignment> ) 
begin 
   <statement>+ 
end

Kiểm soát độ trễ

Kiểm soát độ trễ

Việc thực thi một câu lệnh thủ tục có thể được kiểm soát độ trễ bằng cách sử dụng cú pháp sau:

<statement> 
::= <delay_control> <statement_or_null> 
<delay_control> 
::= # <NUMBER> 
||= # <identifier> 
||= # ( <mintypmax_expression> )

Ví dụ sau đây trì hoãn việc thực hiện nhiệm vụ đi 10 đơn vị thời gian -

# 10 rega = regb;

Ba ví dụ tiếp theo cung cấp một biểu thức theo sau dấu số (#). Việc thực hiện nhiệm vụ bị trì hoãn theo khoảng thời gian mô phỏng được chỉ định bởi giá trị của biểu thức.

Kiểm soát sự kiện

Việc thực hiện một câu lệnh thủ tục có thể được đồng bộ hóa với sự thay đổi giá trị trên mạng hoặc thanh ghi, hoặc sự xuất hiện của một sự kiện được khai báo, bằng cách sử dụng cú pháp điều khiển sự kiện sau:

Example

<statement> 
::= <event_control> <statement_or_null> 

<event_control> 
::= @ <identifier> 
||= @ ( <event_expression> ) 

<event_expression> 
::= <expression> 
||= posedge <SCALAR_EVENT_EXPRESSION> 
||= negedge <SCALAR_EVENT_EXPRESSION> 
||= <event_expression> <or <event_expression>>

* <SCALAR_EVENT_EXPRESSION> là một biểu thức phân giải thành giá trị một bit.

Các thay đổi giá trị trên lưới và thanh ghi có thể được sử dụng như các sự kiện để kích hoạt việc thực thi một câu lệnh. Điều này được gọi là phát hiện một sự kiện ngầm. Cú pháp Verilog cũng cho phép bạn phát hiện thay đổi dựa trên hướng thay đổi — nghĩa là về phía giá trị 1 (posedge) hoặc về phía giá trị 0 (negedge). Hành vi của posedge và negedge đối với các giá trị biểu thức không xác định như sau:

  • một negedge được phát hiện khi chuyển đổi từ 1 sang không xác định và từ không xác định thành 0
  • một posedge được phát hiện khi chuyển đổi từ 0 sang không xác định và từ không xác định thành 1

Thủ tục: Luôn luôn và Khối ban đầu

Tất cả các thủ tục trong Verilog được chỉ định trong một trong bốn Khối sau. 1) Các khối ban đầu 2) Luôn luôn các khối 3) Nhiệm vụ 4) Chức năng

Các câu lệnh ban đầu và luôn luôn được kích hoạt khi bắt đầu mô phỏng. Các khối ban đầu chỉ thực thi một lần và hoạt động của nó sẽ chết khi câu lệnh kết thúc. Ngược lại, các khối luôn thực thi lặp đi lặp lại. Hoạt động của nó chỉ chết khi kết thúc mô phỏng. Không có giới hạn về số lượng khối ban đầu và luôn luôn có thể được xác định trong một mô-đun. Nhiệm vụ và chức năng là các thủ tục được kích hoạt từ một hoặc nhiều vị trí trong các thủ tục khác.

Khối ban đầu

Cú pháp cho câu lệnh ban đầu như sau:

<initial_statement> 
::= initial <statement>

Ví dụ sau minh họa việc sử dụng câu lệnh ban đầu để khởi tạo các biến khi bắt đầu mô phỏng.

Initial 
Begin 
   Areg = 0; // initialize a register 
   For (index = 0; index < size; index = index + 1) 
   Memory [index] = 0; //initialize a memory 
   Word 
End

Một cách sử dụng điển hình khác của các Khối ban đầu là đặc tả mô tả dạng sóng thực thi một lần để cung cấp kích thích cho phần chính của mạch được mô phỏng.

Initial 
Begin 
   Inputs = ’b000000; 
   // initialize at time zero 
   #10 inputs = ’b011001; // first pattern 
   #10 inputs = ’b011011; // second pattern 
   #10 inputs = ’b011000; // third pattern 
   #10 inputs = ’b001000; // last pattern 
End

Luôn chặn

Câu lệnh 'always' lặp lại liên tục trong toàn bộ quá trình chạy mô phỏng. Cú pháp cho câu lệnh always được đưa ra bên dưới

<always_statement> 
::= always <statement>

Câu lệnh 'always', vì bản chất lặp của nó, chỉ hữu ích khi được sử dụng kết hợp với một số dạng điều khiển thời gian. Nếu câu lệnh 'always' không cung cấp phương tiện cho thời gian trôi qua, câu lệnh 'always' tạo ra một điều kiện deadlock mô phỏng. Ví dụ, đoạn mã sau tạo ra một vòng lặp vô hạn độ trễ bằng 0:

Always areg = ~areg;

Việc cung cấp kiểm soát thời gian cho đoạn mã trên sẽ tạo ra một mô tả hữu ích tiềm năng — như trong ví dụ sau -

Always #half_period areg = ~areg;