การสร้างแบบจำลองและกำหนดเวลาพฤติกรรมใน Verilog
แบบจำลองพฤติกรรมใน Verilog ประกอบด้วยคำสั่งขั้นตอนซึ่งควบคุมการจำลองและจัดการตัวแปรของชนิดข้อมูล ข้อความทั้งหมดเหล่านี้มีอยู่ในขั้นตอน แต่ละโพรซีเดอร์มีโฟลว์กิจกรรมที่เกี่ยวข้อง
ในระหว่างการจำลองแบบจำลองพฤติกรรมโฟลว์ทั้งหมดที่กำหนดโดยคำสั่ง "always" และ "initial" จะเริ่มต้นพร้อมกันที่เวลาจำลอง "ศูนย์" คำสั่งเริ่มต้นจะดำเนินการเพียงครั้งเดียวและคำสั่ง always จะถูกดำเนินการซ้ำ ๆ ในโมเดลนี้ตัวแปรรีจิสเตอร์ a และ b จะเริ่มต้นเป็นไบนารี 1 และ 0 ตามลำดับที่เวลาจำลอง 'ศูนย์' จากนั้นคำสั่งเริ่มต้นจะเสร็จสมบูรณ์และจะไม่ดำเนินการอีกในระหว่างการดำเนินการจำลองนั้น คำสั่งเริ่มต้นนี้ประกอบด้วยบล็อกเริ่มต้น (หรือเรียกว่าบล็อกตามลำดับ) ของคำสั่ง ในบล็อกประเภทเริ่มต้นนี้ a จะเริ่มต้นก่อนตามด้วย b
ตัวอย่างการสร้างแบบจำลองพฤติกรรม
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
การกำหนดขั้นตอน
การกำหนดขั้นตอนมีไว้สำหรับการอัพเดตตัวแปร reg, integer, time และ memory มีความแตกต่างอย่างมีนัยสำคัญระหว่างการมอบหมายขั้นตอนและการมอบหมายงานต่อเนื่องดังที่อธิบายไว้ด้านล่าง -
การกำหนดอย่างต่อเนื่องขับเคลื่อนตัวแปรสุทธิและได้รับการประเมินและอัปเดตเมื่อใดก็ตามที่ตัวถูกดำเนินการอินพุตเปลี่ยนค่า
การกำหนดขั้นตอนจะอัพเดตค่าของตัวแปรรีจิสเตอร์ภายใต้การควบคุมของโครงสร้างโฟลว์โพรซีเดอร์ที่ล้อมรอบ
ด้านขวามือของการกำหนดขั้นตอนอาจเป็นนิพจน์ใด ๆ ที่ประเมินเป็นค่า อย่างไรก็ตามส่วนที่เลือกทางด้านขวามือจะต้องมีดัชนีคงที่ ด้านซ้ายแสดงถึงตัวแปรที่รับงานจากด้านขวามือ ด้านซ้ายมือของการกำหนดขั้นตอนสามารถใช้รูปแบบใดรูปแบบหนึ่งต่อไปนี้ -
register, integer, real หรือ time variable - การกำหนดชื่ออ้างอิงของข้อมูลประเภทใดประเภทหนึ่งเหล่านี้
การเลือกบิตของตัวแปร register, จำนวนเต็ม, จริงหรือเวลา - การกำหนดให้กับบิตเดียวที่ทำให้บิตอื่น ๆ ไม่ถูกแตะต้อง
part-select of a register, integer, real หรือ time variable - การเลือกบางส่วนของบิตที่ต่อเนื่องกันตั้งแต่สองบิตขึ้นไปซึ่งจะทำให้ส่วนที่เหลือไม่ถูกแตะต้อง สำหรับรูปแบบการเลือกส่วนหนึ่งนิพจน์คงที่เท่านั้นที่ถูกกฎหมาย
องค์ประกอบหน่วยความจำ - คำเดียวของหน่วยความจำ โปรดทราบว่าการเลือกบิตและการเลือกบางส่วนไม่ถูกต้องในการอ้างอิงองค์ประกอบหน่วยความจำ
การเรียงต่อกันของข้อใดข้อหนึ่งข้างต้น - สามารถระบุการเรียงต่อกันของรูปแบบใดก็ได้จากสี่รูปแบบก่อนหน้าซึ่งแบ่งพาร์ติชันผลลัพธ์ของนิพจน์ด้านขวามือได้อย่างมีประสิทธิภาพและกำหนดส่วนพาร์ติชันตามลำดับไปยังส่วนต่างๆของการเรียงต่อกัน
ความล่าช้าในการมอบหมายงาน (ไม่ใช่สำหรับการสังเคราะห์)
ในการมอบหมายงานล่าช้าหน่วยเวลาจะผ่านไปก่อนที่คำสั่งจะถูกดำเนินการและทำการกำหนดทางซ้าย ด้วยความล่าช้าภายในการมอบหมายงานด้านขวาจะได้รับการประเมินทันที แต่มีความล่าช้าก่อนที่ผลลัพธ์จะถูกวางไว้ในการมอบหมายด้านซ้ายมือ หากขั้นตอนอื่นเปลี่ยนสัญญาณด้านขวามือระหว่างΔtจะไม่มีผลกับเอาต์พุต เครื่องมือสังเคราะห์ไม่รองรับความล่าช้า
ไวยากรณ์
Procedural Assignmentตัวแปร = นิพจน์
Delayed assignment# Δtตัวแปร = นิพจน์;
Intra-assignment delayตัวแปร = # Δtนิพจน์;
ตัวอย่าง
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. */
การบล็อกงาน
คำสั่งกำหนดขั้นตอนการบล็อกจะต้องดำเนินการก่อนการดำเนินการของคำสั่งที่ตามมาในบล็อกตามลำดับ คำสั่งกำหนดขั้นตอนการบล็อกไม่ได้ป้องกันการดำเนินการของคำสั่งที่ตามมาในบล็อกคู่ขนาน
ไวยากรณ์
ไวยากรณ์สำหรับการกำหนดขั้นตอนการบล็อกมีดังนี้ -
<lvalue> = <timing_control> <expression>
โดยที่ lvalue เป็นชนิดข้อมูลที่ถูกต้องสำหรับคำสั่งการกำหนดขั้นตอน = คือตัวดำเนินการกำหนดและการควบคุมเวลาคือความล่าช้าภายในที่เป็นทางเลือก ความล่าช้าในการควบคุมเวลาอาจเป็นได้ทั้งการควบคุมการหน่วงเวลา (เช่น # 6) หรือการควบคุมเหตุการณ์ (ตัวอย่างเช่น @ (posedge clk)) นิพจน์คือค่าด้านขวามือที่เครื่องมือจำลองกำหนดให้ทางด้านซ้ายมือ ตัวดำเนินการ = ที่ใช้โดยการบล็อกการกำหนดขั้นตอนยังใช้โดยการกำหนดขั้นตอนต่อเนื่องและการมอบหมายงานต่อเนื่อง
ตัวอย่าง
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
การมอบหมายการไม่ปิดกั้น (RTL)
การกำหนดขั้นตอนที่ไม่ปิดกั้นช่วยให้คุณสามารถกำหนดเวลาการมอบหมายงานได้โดยไม่ปิดกั้นขั้นตอน คุณสามารถใช้คำสั่งขั้นตอนการไม่ปิดกั้นเมื่อใดก็ตามที่คุณต้องการทำการมอบหมายการลงทะเบียนหลายรายการภายในขั้นตอนเวลาเดียวกันโดยไม่คำนึงถึงลำดับหรือการพึ่งพาซึ่งกันและกัน
ไวยากรณ์
ไวยากรณ์สำหรับการกำหนดขั้นตอนที่ไม่ปิดกั้นมีดังนี้ -
<lvalue> <= <timing_control> <expression>
โดยที่ lvalue เป็นชนิดข้อมูลที่ถูกต้องสำหรับคำสั่งการกำหนดขั้นตอน <= คือตัวดำเนินการกำหนดที่ไม่ปิดกั้นและการควบคุมเวลาคือการควบคุมเวลาภายในที่กำหนดซึ่งเป็นทางเลือก ความล่าช้าในการควบคุมเวลาอาจเป็นได้ทั้งการควบคุมการหน่วงเวลาหรือการควบคุมเหตุการณ์ (ตัวอย่างเช่น @ (posedge clk)) นิพจน์คือค่าด้านขวามือที่เครื่องมือจำลองกำหนดให้ทางด้านซ้ายมือ ตัวดำเนินการกำหนดแบบไม่ปิดกั้นเป็นตัวดำเนินการเดียวกับที่ตัวจำลองใช้สำหรับตัวดำเนินการเชิงสัมพันธ์ที่มีคุณภาพน้อยกว่า ตัวจำลองตีความตัวดำเนินการ <= เป็นตัวดำเนินการเชิงสัมพันธ์เมื่อคุณใช้ในนิพจน์และตีความว่าตัวดำเนินการ <= เป็นตัวดำเนินการกำหนดเมื่อคุณใช้ในโครงสร้างการกำหนดขั้นตอนที่ไม่ปิดกั้น
วิธีที่เครื่องจำลองประเมินการกำหนดขั้นตอนที่ไม่ปิดกั้นเมื่อเครื่องจำลองพบการกำหนดขั้นตอนที่ไม่ปิดกั้นเครื่องจำลองจะประเมินและดำเนินการการกำหนดขั้นตอนที่ไม่ปิดกั้นในสองขั้นตอนดังนี้ -
เครื่องจำลองจะประเมินทางด้านขวามือและกำหนดเวลาการกำหนดค่าใหม่ที่จะเกิดขึ้นในเวลาที่กำหนดโดยการควบคุมเวลาตามขั้นตอน เครื่องจำลองจะประเมินทางด้านขวามือและกำหนดเวลาการกำหนดค่าใหม่ที่จะเกิดขึ้นในเวลาที่กำหนดโดยการควบคุมเวลาตามขั้นตอน
ในตอนท้ายของขั้นตอนเวลาซึ่งความล่าช้าที่กำหนดได้หมดลงหรือเหตุการณ์ที่เหมาะสมเกิดขึ้นตัวจำลองจะดำเนินการมอบหมายโดยกำหนดค่าให้ทางด้านซ้ายมือ
ตัวอย่าง
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
เงื่อนไข
คำสั่งเงื่อนไข (หรือคำสั่ง if-else) ใช้ในการตัดสินใจว่าจะดำเนินการคำสั่งหรือไม่
อย่างเป็นทางการไวยากรณ์มีดังนี้ -
<statement>
::= if ( <expression> ) <statement_or_null>
||= if ( <expression> ) <statement_or_null>
else <statement_or_null>
<statement_or_null>
::= <statement>
||= ;
<expression> ได้รับการประเมิน; ถ้าเป็นจริง (นั่นคือมีค่าที่ไม่รู้จักเป็นศูนย์) คำสั่งแรกจะดำเนินการ ถ้าเป็นเท็จ (มีค่าเป็นศูนย์หรือค่า x หรือ z) คำสั่งแรกจะไม่ดำเนินการ หากมีคำสั่ง else และ <expression> เป็นเท็จคำสั่ง else จะดำเนินการ เนื่องจากค่าตัวเลขของนิพจน์ if ถูกทดสอบว่าเป็นศูนย์จึงสามารถใช้ทางลัดบางอย่างได้
ตัวอย่างเช่นสองคำสั่งต่อไปนี้แสดงตรรกะเดียวกัน -
if (expression)
if (expression != 0)
เนื่องจากส่วนอื่นของ if-else เป็นทางเลือกอาจมีความสับสนเมื่อส่วนอื่นถูกละไว้จากลำดับ if ที่ซ้อนกัน สิ่งนี้แก้ไขได้โดยการเชื่อมโยงสิ่งอื่นกับสิ่งที่ใกล้เคียงที่สุดก่อนหน้านี้เสมอหากไม่มีสิ่งอื่น
ตัวอย่าง
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;
โครงสร้างของ: if- else- if
การก่อสร้างต่อไปนี้เกิดขึ้นบ่อยครั้งมากจนควรมีการอภิปรายแยกกันสั้น ๆ
Example
if (<expression>)
<statement>
else if (<expression>)
<statement>
else if (<expression>)
<statement>
else
<statement>
ลำดับของ if นี้ (เรียกว่าโครงสร้าง if-else-if) เป็นวิธีทั่วไปที่สุดในการเขียนการตัดสินใจหลายทาง นิพจน์ได้รับการประเมินตามลำดับ หากนิพจน์ใด ๆ เป็นจริงคำสั่งที่เกี่ยวข้องจะถูกดำเนินการและสิ่งนี้จะยุติห่วงโซ่ทั้งหมด แต่ละคำสั่งเป็นทั้งคำสั่งเดียวหรือบล็อกของคำสั่ง
ส่วนอื่นสุดท้ายของโครงสร้าง if-else-if จัดการกับกรณีที่ "ไม่มีข้อใดข้อหนึ่งข้างต้น" หรือกรณีเริ่มต้นโดยที่เงื่อนไขอื่นไม่เป็นไปตาม บางครั้งไม่มีการดำเนินการที่ชัดเจนสำหรับค่าเริ่มต้น ในกรณีนั้นสามารถละเว้นอื่น ๆ ต่อท้ายหรือสามารถใช้สำหรับการตรวจสอบข้อผิดพลาดเพื่อตรวจจับเงื่อนไขที่เป็นไปไม่ได้
คำชี้แจงกรณี
คำชี้แจงกรณีเป็นคำสั่งการตัดสินใจหลายทางแบบพิเศษที่ทดสอบว่านิพจน์ตรงกับนิพจน์อื่น ๆ หรือไม่และแตกแขนง คำสั่ง case มีประโยชน์สำหรับการอธิบายตัวอย่างเช่นการถอดรหัสคำสั่งของไมโครโปรเซสเซอร์ คำสั่ง case มีไวยากรณ์ต่อไปนี้ -
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>
สำนวนคดีจะได้รับการประเมินและเปรียบเทียบตามลำดับที่ได้รับ ในระหว่างการค้นหาเชิงเส้นหากหนึ่งในนิพจน์ไอเท็มเคสตรงกับนิพจน์ในวงเล็บคำสั่งที่เกี่ยวข้องกับไอเท็มเคสนั้นจะถูกดำเนินการ หากการเปรียบเทียบทั้งหมดล้มเหลวและมีการกำหนดรายการเริ่มต้นระบบจะดำเนินการคำสั่งรายการเริ่มต้น หากไม่ได้กำหนดคำสั่งเริ่มต้นและการเปรียบเทียบทั้งหมดล้มเหลวจะไม่มีการเรียกใช้งานคำสั่ง case item
นอกเหนือจากไวยากรณ์คำสั่ง case แตกต่างจากการสร้าง if-else-if แบบหลายทางในสองวิธีที่สำคัญ -
นิพจน์เงื่อนไขในโครงสร้าง if-else-if มีลักษณะทั่วไปมากกว่าการเปรียบเทียบนิพจน์หนึ่งกับนิพจน์อื่น ๆ เช่นในคำสั่ง case
คำสั่ง case ให้ผลลัพธ์ที่ชัดเจนเมื่อมีค่า x และ z ในนิพจน์
คำสั่งวนซ้ำ
คำสั่งวนซ้ำมีสี่ประเภท พวกเขาให้วิธีการควบคุมการดำเนินการของคำสั่งศูนย์หนึ่งครั้งหรือมากกว่านั้น
ดำเนินการคำสั่งอย่างต่อเนื่องตลอดไป
ทำซ้ำดำเนินการคำสั่งตามจำนวนครั้งที่กำหนด
ในขณะที่ดำเนินการคำสั่งจนกว่านิพจน์จะกลายเป็นเท็จ ถ้านิพจน์เริ่มต้นเป็นเท็จจะไม่มีการดำเนินการคำสั่งเลย
สำหรับการควบคุมการดำเนินการของคำสั่งที่เกี่ยวข้องโดยกระบวนการสามขั้นตอนดังต่อไปนี้ -
ดำเนินการมอบหมายตามปกติที่ใช้เพื่อเริ่มต้นตัวแปรที่ควบคุมจำนวนลูปที่ดำเนินการ
ประเมินนิพจน์ - ถ้าผลลัพธ์เป็นศูนย์สำหรับลูปจะออกและถ้าไม่ใช่ศูนย์ for loop จะรันคำสั่งที่เกี่ยวข้องจากนั้นดำเนินการขั้นตอนที่ 3
ดำเนินการมอบหมายตามปกติที่ใช้เพื่อแก้ไขค่าของตัวแปร loopcontrol จากนั้นทำซ้ำขั้นตอนที่ 2
ต่อไปนี้เป็นกฎไวยากรณ์สำหรับคำสั่งวนซ้ำ -
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
การควบคุมความล่าช้า
การควบคุมความล่าช้า
การดำเนินการของคำสั่งขั้นตอนสามารถควบคุมความล่าช้าได้โดยใช้ไวยากรณ์ต่อไปนี้ -
<statement>
::= <delay_control> <statement_or_null>
<delay_control>
::= # <NUMBER>
||= # <identifier>
||= # ( <mintypmax_expression> )
ตัวอย่างต่อไปนี้ทำให้การดำเนินการมอบหมายล่าช้าไป 10 หน่วยเวลา -
# 10 rega = regb;
สามตัวอย่างถัดไปแสดงนิพจน์ตามหลังเครื่องหมายตัวเลข (#) การดำเนินการของความล่าช้าในการมอบหมายตามจำนวนเวลาจำลองที่ระบุโดยค่าของนิพจน์
การควบคุมเหตุการณ์
การดำเนินการของคำสั่งขั้นตอนสามารถซิงโครไนซ์กับการเปลี่ยนแปลงค่าบนเน็ตหรือรีจิสเตอร์หรือการเกิดเหตุการณ์ที่ประกาศโดยใช้ไวยากรณ์การควบคุมเหตุการณ์ต่อไปนี้ -
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> คือนิพจน์ที่แก้ไขเป็นค่าหนึ่งบิต
การเปลี่ยนแปลงมูลค่าบนมุ้งและรีจิสเตอร์สามารถใช้เป็นเหตุการณ์เพื่อทริกเกอร์การดำเนินการของคำสั่ง สิ่งนี้เรียกว่าการตรวจจับเหตุการณ์โดยปริยาย ไวยากรณ์ Verilog ยังช่วยให้คุณตรวจจับการเปลี่ยนแปลงตามทิศทางของการเปลี่ยนแปลงนั่นคือไปยังค่า 1 (posedge) หรือไปที่ค่า 0 (negedge) พฤติกรรมของ posedge และ negedge สำหรับค่านิพจน์ที่ไม่รู้จักมีดังนี้ -
- ตรวจพบ negedge ในการเปลี่ยนจาก 1 เป็นไม่ทราบและจากไม่ทราบเป็น 0
- มีการตรวจพบ posedge ในการเปลี่ยนจาก 0 เป็นไม่ทราบและจากไม่ทราบเป็น 1
ขั้นตอน: เสมอและบล็อกเริ่มต้น
ขั้นตอนทั้งหมดใน Verilog ถูกระบุไว้ในหนึ่งในสี่บล็อกต่อไปนี้ 1) บล็อกเริ่มต้น 2) บล็อกเสมอ 3) ภารกิจ 4) ฟังก์ชัน
เปิดใช้งานคำสั่งเริ่มต้นและเสมอเมื่อเริ่มต้นการจำลอง บล็อกเริ่มต้นดำเนินการเพียงครั้งเดียวและกิจกรรมจะตายเมื่อคำสั่งเสร็จสิ้น ในทางตรงกันข้ามบล็อกเสมอจะดำเนินการซ้ำ ๆ กิจกรรมจะตายก็ต่อเมื่อการจำลองสิ้นสุดลง ไม่มีการ จำกัด จำนวนบล็อกเริ่มต้นและบล็อกเสมอที่สามารถกำหนดได้ในโมดูล งานและฟังก์ชันคือโพรซีเดอร์ที่เปิดใช้งานจากที่หนึ่งหรือหลายแห่งในโพรซีเดอร์อื่น
บล็อกเริ่มต้น
ไวยากรณ์สำหรับคำสั่งเริ่มต้นมีดังนี้ -
<initial_statement>
::= initial <statement>
ตัวอย่างต่อไปนี้แสดงให้เห็นถึงการใช้คำสั่งเริ่มต้นสำหรับการกำหนดค่าเริ่มต้นของตัวแปรเมื่อเริ่มต้นการจำลอง
Initial
Begin
Areg = 0; // initialize a register
For (index = 0; index < size; index = index + 1)
Memory [index] = 0; //initialize a memory
Word
End
การใช้งานทั่วไปอีกประการหนึ่งของบล็อกเริ่มต้นคือข้อกำหนดของคำอธิบายรูปคลื่นที่ดำเนินการครั้งเดียวเพื่อกระตุ้นให้เกิดส่วนหลักของวงจรที่กำลังจำลอง
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
บล็อกเสมอ
คำสั่ง 'เสมอ' จะทำซ้ำอย่างต่อเนื่องตลอดการดำเนินการจำลองทั้งหมด ไวยากรณ์สำหรับคำสั่ง always แสดงไว้ด้านล่าง
<always_statement>
::= always <statement>
คำสั่ง 'เสมอ' เนื่องจากลักษณะการวนซ้ำจึงมีประโยชน์เมื่อใช้ร่วมกับการควบคุมเวลาบางรูปแบบเท่านั้น หากคำสั่ง 'always' ไม่ให้เวลาล่วงหน้าคำสั่ง 'always' จะสร้างเงื่อนไขการหยุดชะงักจำลอง ตัวอย่างเช่นรหัสต่อไปนี้สร้างลูปดีเลย์เป็นศูนย์ที่ไม่มีที่สิ้นสุด -
Always areg = ~areg;
การให้การควบคุมเวลาให้กับโค้ดด้านบนจะสร้างคำอธิบายที่เป็นประโยชน์ดังตัวอย่างต่อไปนี้ -
Always #half_period areg = ~areg;