การใช้งานคำสั่ง assign ใน Verilog HDL|คู่มือพื้นฐานสำหรับผู้เริ่มต้น

目次

1. Verilog assign คืออะไร?【อธิบายสำหรับผู้เริ่มต้น】

Verilog HDL คืออะไร?

Verilog HDL (Hardware Description Language) คือภาษาบรรยายฮาร์ดแวร์ที่ใช้ในการอธิบายวงจรดิจิทัล แตกต่างจากภาษาการเขียนโปรแกรมสำหรับซอฟต์แวร์ เพราะ Verilog ใช้ในการอธิบายโครงสร้างและการทำงานของฮาร์ดแวร์ (วงจรลอจิก) และสามารถแปลงเป็นวงจรจริง เช่น FPGA หรือ ASIC ได้ผ่านการจำลองและการสังเคราะห์

หนึ่งในคำสั่งที่ถูกใช้บ่อยใน Verilog คือคำสั่ง assign โดยเฉพาะเมื่อเขียน วงจรคอมบิเนชัน (Combinational Circuit) ซึ่งเป็นโครงสร้างพื้นฐานสำคัญ

หน้าที่ของ assign คืออะไร?

assign คือคำสั่งที่ใช้สำหรับการกำหนดค่าแบบต่อเนื่อง (continuous assignment) ให้กับสัญญาณชนิด wire การกำหนดค่าแบบต่อเนื่องหมายความว่า เมื่ออินพุตเปลี่ยนแปลง เอาต์พุตก็จะเปลี่ยนทันที

ตัวอย่างเช่น หากต้องการคำนวณ AND ระหว่างสัญญาณสองตัวแล้วส่งออก สามารถเขียนได้ดังนี้:

assign out = in1 & in2;

เพียงบรรทัดเดียวก็สามารถทำให้ “out แสดงผลเป็น in1 AND in2 ตลอดเวลา” ได้ assign จึงทำหน้าที่เหมือนการกำหนดการเชื่อมต่อของวงจรฮาร์ดแวร์โดยตรง

assign ในวงจรคอมบิเนชัน

วงจรดิจิทัลแบ่งได้เป็น วงจรคอมบิเนชัน และ วงจรซีเควนเชียล

  • วงจรคอมบิเนชัน: เอาต์พุตเปลี่ยนทันทีตามอินพุต (เช่น ตัวบวก, เกตลอจิก)
  • วงจรซีเควนเชียล: ใช้นาฬิกาหรือองค์ประกอบหน่วยความจำเพื่อเก็บสถานะ (เช่น ฟลิปฟลอป, เคาน์เตอร์)

assign ถูกใช้ในวงจรประเภทแรกคือ คอมบิเนชัน เพราะต้องอัปเดตเอาต์พุตทันทีตามอินพุต

ทำไม assign ถึงสำคัญสำหรับผู้เริ่มต้น?

ในขั้นเริ่มต้นของการเรียนรู้ Verilog สิ่งสำคัญที่สุดคือการเข้าใจวงจรคอมบิเนชัน และ assign เป็นเครื่องมือหลักที่ใช้ในการอธิบาย ตั้งแต่วงจรลอจิกง่าย ๆ เงื่อนไข การบวก ไปจนถึงตัวเปรียบเทียบ ล้วนสามารถเขียนได้ด้วย assign อย่างกระชับ

นอกจากนี้ assign ยังช่วยให้ผู้เรียนเข้าใจการไหลของสัญญาณในระดับฮาร์ดแวร์ ซึ่งเป็นพื้นฐานสำคัญสำหรับการเขียนวงจรซีเควนเชียลที่ซับซ้อนในอนาคต

สรุป: เข้าใจพื้นฐานของ assign

assign ใน Verilog คือคำสั่งพื้นฐานที่สุดในการเขียนวงจรคอมบิเนชัน เพราะช่วยอธิบายการเชื่อมต่อและการดำเนินการลอจิกได้อย่างง่ายดาย ดังนั้นผู้เริ่มต้นควรทำความเข้าใจและใช้งานได้อย่างคล่องแคล่วตั้งแต่แรกเริ่ม

2. พื้นฐานการเขียน assign ใน Verilog

โครงสร้างพื้นฐานของ assign

โครงสร้างของ assign ใน Verilog มีความเรียบง่าย โดยใช้สำหรับกำหนดค่าให้กับสัญญาณชนิด wire ด้วยนิพจน์ทางลอจิกหรือการคำนวณ

assign สัญญาณออก = นิพจน์;

นิพจน์สามารถเป็นการอ้างอิงสัญญาณอื่น ๆ การดำเนินการลอจิก หรือการดำเนินการบิต ทั้งนี้ assign ใช้ได้เฉพาะกับwire ไม่สามารถใช้กับ reg

ตัวอย่างการใช้งานที่พบบ่อย①: การดำเนินการลอจิกพื้นฐาน

รูปแบบที่ใช้บ่อยที่สุดคือการใช้เกตลอจิก ตัวอย่างเช่น AND, OR, XOR ซึ่งสามารถเขียนด้วย assign ได้ดังนี้:

assign and_out = a & b;   // AND gate
assign or_out  = a | b;   // OR gate
assign xor_out = a ^ b;   // XOR gate

ด้วยการใช้โอเปอเรเตอร์ สามารถรวมสัญญาณหลายตัวแล้วกำหนดค่าเอาต์พุตอย่างต่อเนื่องได้

ตัวอย่างการใช้งานที่พบบ่อย②: การจัดการบิต (Bitwise)

assign รองรับการดำเนินการในระดับบิต เช่น การดึงบางบิตออกมา หรือรวมหลายบิตเข้าด้วยกัน

assign upper_4bits = data[7:4];          // ดึง 4 บิตบนจาก 8 บิต
assign lower_4bits = data[3:0];          // ดึง 4 บิตล่าง
assign combined = {data1[3:0], data2[3:0]};  // รวม 4 บิตจาก 2 ตัว ให้เป็น 8 บิต

ดังนั้น assign จึงเหมาะสำหรับการดึงและรวมข้อมูลบางส่วนใหม่อย่างมีประสิทธิภาพ

ความหมายของ “Continuous Assignment” ใน assign

ใน Verilog การใช้ assign เรียกว่า continuous assignment (การกำหนดค่าแบบต่อเนื่อง) หมายความว่าเมื่ออินพุตเปลี่ยน เอาต์พุตจะอัปเดตทันที

แตกต่างจาก “การกำหนดค่า” ในซอฟต์แวร์ เพราะในฮาร์ดแวร์การเชื่อมต่อของสัญญาณจะคงอยู่เสมอ ทำให้ assign สามารถจำลองพฤติกรรมการส่งสัญญาณจริงของฮาร์ดแวร์ได้

การใช้ assign แบบกำหนดเวลาหน่วง (Delay)

ใน Verilog สามารถเพิ่มdelay (เวลาหน่วง) ใน assign ได้ โดยใช้สำหรับการจำลอง (simulation) เป็นหลัก ไม่ค่อยมีผลต่อการสังเคราะห์จริง

assign #5 out = a & b;  // ส่งผลลัพธ์ AND ของ a และ b ไปที่ out โดยหน่วง 5 time units

“#5” หมายถึงเวลาหน่วงตามหน่วยเวลา (time unit) ซึ่งมีประโยชน์ในการตรวจสอบการทำงานเชิงจำลอง แต่จะถูกละเว้นในการสังเคราะห์เป็นวงจรจริง

ตัวอย่าง assign ที่ใช้เงื่อนไข (Conditional)

assign สามารถใช้ร่วมกับตัวดำเนินการแบบมีเงื่อนไข (ternary operator) เพื่อให้ทำงานคล้าย if ได้

assign out = sel ? data1 : data2;

ความหมายคือ “ถ้า sel = 1 ให้ใช้ data1 ไม่เช่นนั้นใช้ data2” เหมาะสำหรับการเขียน MUX หรือการเลือกสัญญาณ

สรุป: เรียนรู้วิธีเขียน assign

assign ใน Verilog เป็นคำสั่งที่เรียบง่ายแต่ทรงพลัง รองรับการดำเนินการหลากหลาย เช่น ลอจิก, การจัดการบิต, เงื่อนไข และการหน่วงเวลา

สำหรับผู้เริ่มต้น ควรทำความเข้าใจโครงสร้างพื้นฐานของ assign เพื่อเขียนวงจรคอมบิเนชันได้อย่างอิสระ ถือเป็นก้าวแรกที่สำคัญในการเรียนรู้ Verilog

3. ความสัมพันธ์ระหว่าง assign และ wire|ตั้งแต่การประกาศจนถึงการใช้งาน

ความสัมพันธ์พื้นฐานระหว่าง assign และ wire

หนึ่งในกฎสำคัญของ Verilog คือ assign ใช้ได้เฉพาะกับสัญญาณชนิด wire เท่านั้น หากไม่เข้าใจกฎนี้ เวลาเขียนโค้ดมักจะเกิดข้อผิดพลาดทางไวยากรณ์

การกำหนดค่าด้วย assign เรียกว่า continuous assignment (การกำหนดค่าแบบต่อเนื่อง) และสามารถใช้ได้เฉพาะกับสัญญาณที่ถูกประกาศเป็น wire เท่านั้น

wire คืออะไร? ── ภาพจำลองของ “สายไฟ”

ชนิดข้อมูล wire ใน Verilog เปรียบเสมือน การจำลองสายไฟจริงในวงจรลอจิก ทำหน้าที่เป็นตัวกลางในการส่งค่าที่ได้รับจากแหล่งอื่น (เช่น assign หรือเอาต์พุตของโมดูล)

ดังนั้น wire จะไม่เก็บค่าเอง แต่จะส่งต่อค่าที่ได้รับจากแหล่งกำเนิด

wire a, b, out;

assign out = a & b;  // เอาต์พุต out แสดงผล a AND b ตลอดเวลา

ในตัวอย่างนี้ out ต้องเป็นชนิด wire หากกำหนดเป็น reg จะเกิดข้อผิดพลาดทันที

ความแตกต่างกับ reg: ทำไม assign ใช้ไม่ได้?

reg ถูกใช้ในวงจรซีเควนเชียล เพื่อเก็บค่าในช่วงเวลาหนึ่ง และจะถูกกำหนดค่าภายใน always block เท่านั้น ดังนั้น assign ไม่สามารถใช้กับ reg ได้

ตัวอย่างโค้ดที่ผิด:

reg out;
assign out = a & b;  // ❌ ผิด! assign ใช้กับ reg ไม่ได้

สรุปคือ assign ใช้กับ wire ส่วน always ใช้กับ reg

การประกาศ wire แบบหลายบิต (bus)

wire รองรับทั้งการประกาศแบบ 1 บิตและหลายบิต (bus)

wire [3:0] a, b;
wire [3:0] out;

assign out = a & b;  // การ AND ทีละบิต (bitwise AND)

หากเป็นหลายบิต ต้องกำหนดขนาดบิตอย่างชัดเจนตอนประกาศ

wire ในการเชื่อมต่อระหว่างโมดูล

นอกจากใช้กับ assign แล้ว wire ยังใช้เป็นตัวกลางเชื่อมต่อสัญญาณระหว่างโมดูล

wire result;

module1 u1 (.a(a), .b(b), .out(result));
module2 u2 (.in(result), .y(y));

ดังนั้น wire จึงเป็นองค์ประกอบพื้นฐานที่ใช้ทั่วไปใน Verilog

สรุป: เข้าใจ wire เพื่อใช้ assign อย่างถูกต้อง

เพื่อใช้ assign อย่างถูกต้อง จำเป็นต้องเข้าใจ wire เนื่องจากเป็น “สายไฟ” ที่รับค่ามาจากสัญญาณอื่น ๆ และ assign ทำหน้าที่กำหนดการเชื่อมต่อ

ในทางตรงกันข้าม reg จะถูกกำหนดค่าภายใน always เท่านั้น การเข้าใจการใช้งานทั้งสองแบบจะช่วยให้เขียนโค้ด Verilog ได้ถูกต้องและมีประสิทธิภาพ

4. ความแตกต่างระหว่าง assign และ always【จุดที่ผู้เริ่มต้นมักสับสน】

ทำไม assign และ always ถึงทำให้ผู้เริ่มต้นสับสน?

สำหรับผู้ที่เริ่มเรียน Verilog ใหม่ ๆ มักสับสนระหว่าง assign และ always เพราะทั้งคู่ใช้ในการกำหนดค่าให้กับสัญญาณ แต่มีจุดประสงค์และชนิดข้อมูลที่รองรับแตกต่างกัน

ในส่วนนี้ เราจะอธิบายความแตกต่างหลัก และแนวทางในการเลือกใช้อย่างถูกต้อง

คุณสมบัติและการใช้งานของ assign

ก่อนอื่น มาทบทวนลักษณะของ assign กันก่อน

  • การใช้งาน: ใช้สำหรับวงจรคอมบิเนชัน
  • ชนิดสัญญาณ: ใช้กับ wire เท่านั้น
  • วิธีการกำหนดค่า: continuous assignment (กำหนดค่าอย่างต่อเนื่อง)
  • คีย์เวิร์ด: assign

ตัวอย่าง: AND gate แบบ 2 อินพุต (assign)

wire a, b;
wire out;

assign out = a & b;

อินพุตเปลี่ยนเมื่อใด เอาต์พุตจะเปลี่ยนทันที ── นี่คือลักษณะของวงจรคอมบิเนชัน

คุณสมบัติและการใช้งานของ always

always มีความยืดหยุ่นกว่า และใช้สำหรับวงจรซีเควนเชียล หรือการควบคุมแบบมีเงื่อนไข โดยมักจะสัมพันธ์กับสัญญาณนาฬิกา (clock)

  • การใช้งาน: วงจรซีเควนเชียลหรือการควบคุมที่ซับซ้อน
  • ชนิดสัญญาณ: ใช้กับ reg
  • วิธีการกำหนดค่า: กำหนดตามเงื่อนไข (triggered assignment)
  • คีย์เวิร์ด: always

ตัวอย่าง: การประมวลผลแบบซิงโครไนซ์กับ clock (always)

reg out;

always @(posedge clk) begin
  out <= a & b;
end

ในกรณีนี้ ค่า a & b จะถูกเก็บไว้ใน out ทุกครั้งที่ขอบขาขึ้นของสัญญาณ clock มาถึง ── ใช้เมื่อมีการพิจารณาเรื่องเวลา

การเปรียบเทียบระหว่าง wire และ reg

คุณสมบัติwirereg
พื้นที่ใช้งานassignalways block
เก็บค่าได้หรือไม่ไม่ได้ (เพียงส่งต่อ)ได้ (เก็บชั่วคราว)
กำหนดค่าเริ่มต้นไม่ได้ทำได้ (เฉพาะ simulation)
รูปแบบการกำหนดค่าcontinuous assignmentblocking หรือ non-blocking assignment

ดังนั้น assign และ always มีความสัมพันธ์กับชนิดสัญญาณโดยตรง และควรเรียนรู้เป็น “คู่” เพื่อไม่สับสน

เลือกใช้ assign หรือ always อย่างไร?

วัตถุประสงค์ควรใช้ชนิดสัญญาณ
การดำเนินการลอจิก (คอมบิเนชัน)assignwire
การเก็บค่าซิงโครไนซ์ด้วย clock (ซีเควนเชียล)alwaysreg
การเปลี่ยนค่าแบบมีเงื่อนไขalwaysreg
เพียงการเชื่อมต่อสัญญาณassignwire

ตัวอย่าง: ต้องการใช้ if ⇒ ควรใช้ always

reg y;
always @(a or b) begin
  if (a == 1) y = b;
  else        y = 0;
end

โค้ดลักษณะนี้ไม่สามารถทำด้วย assign ได้ หากมีเงื่อนไขหรือการไหลของโปรแกรม ควรใช้ always

assign และ always ใช้พร้อมกันได้หรือไม่?

ไม่สามารถใช้ assign และ always กำหนดค่าสัญญาณเดียวกันได้ หากทำเช่นนั้นจะเกิด “การขับเคลื่อนหลายแหล่ง” (multiple drivers) ทำให้เกิดข้อผิดพลาด

โค้ดผิดตัวอย่าง:

assign y = a & b;

always @(posedge clk)
  y <= a | b;  // ❌ y ถูกกำหนดทั้งใน assign และ always

สรุป: แยกความแตกต่างของ assign และ always ให้ชัดเจน

สรุปการเลือกใช้:

  • อินพุตและเอาต์พุตเชื่อมตรงกันเสมอ → ใช้ assign (wire)
  • ต้องมีการเก็บค่า หรือขึ้นกับเวลา → ใช้ always (reg)

หากเข้าใจกฎนี้ จะช่วยให้ผู้เริ่มต้นไม่สับสนระหว่าง assign และ always

5. ตัวอย่างวงจรคอมบิเนชันด้วย assign【พร้อมภาพประกอบ】

วงจรคอมบิเนชันคืออะไร?

เริ่มจากพื้นฐานก่อน วงจรคอมบิเนชัน (Combinational Circuit) หมายถึง เอาต์พุตเปลี่ยนแปลงทันทีตามค่าของอินพุตปัจจุบัน โดยไม่มีการเก็บสถานะในอดีต ดังนั้น ผลลัพธ์ขึ้นอยู่กับอินพุต ณ เวลานั้นเท่านั้น

ใน Verilog คำสั่งที่เหมาะที่สุดสำหรับการเขียนวงจรประเภทนี้คือ assign

การสร้างเกตลอจิกพื้นฐาน (AND, OR, XOR)

ตัวอย่างโมดูลที่ใช้ assign เขียนเกตลอจิกพื้นฐาน:

module logic_gates(
  input  wire a,
  input  wire b,
  output wire and_out,
  output wire or_out,
  output wire xor_out
);

  assign and_out = a & b;
  assign or_out  = a | b;
  assign xor_out = a ^ b;

endmodule

โมดูลนี้จะรับอินพุต a และ b แล้วส่งออกค่า AND, OR, XOR โดยใช้เพียง assign ── ไม่จำเป็นต้องมี clock หรือเงื่อนไขใด ๆ

การสร้าง Half Adder (ตัวบวกครึ่ง)

Half Adder เป็นวงจรพื้นฐานที่ใช้ในการสอนบ่อยที่สุด ทำหน้าที่บวกเลขฐานสอง 2 บิต แล้วส่งออกผลรวมและบิตพกพา

สมการลอจิก

  • ผลรวม (sum) = A ⊕ B (XOR)
  • ตัวพกพา (carry) = A・B (AND)

ตัวอย่างการเขียน Verilog

module half_adder(
  input  wire a,
  input  wire b,
  output wire sum,
  output wire carry
);

  assign sum   = a ^ b;
  assign carry = a & b;

endmodule

Half Adder สามารถเขียนได้ง่าย ๆ ด้วย เพียง 2 บรรทัด assign เหมาะกับผู้เริ่มต้นฝึกฝน

การสร้าง Full Adder (ตัวบวกเต็ม)

Full Adder ทำการบวกเลขฐานสอง 3 บิต (A, B, Cin) แล้วส่งออกผลรวม (Sum) และตัวพกพา (Carry)

สมการลอจิก

  • ผลรวม (sum) = A ⊕ B ⊕ Cin
  • ตัวพกพา (carry) = (A・B) + (Cin・(A ⊕ B))

ตัวอย่างการเขียน Verilog

module full_adder(
  input  wire a,
  input  wire b,
  input  wire cin,
  output wire sum,
  output wire cout
);

  wire ab_xor;

  assign ab_xor = a ^ b;
  assign sum    = ab_xor ^ cin;
  assign cout   = (a & b) | (cin & ab_xor);

endmodule

ตัวอย่างนี้ใช้สัญญาณกลาง ab_xor เพื่อเพิ่มความอ่านง่าย ── แสดงให้เห็นว่า assign สามารถจัดการหลายขั้นตอนของการคำนวณได้อย่างยืดหยุ่น

การสร้างวงจรเลือกสัญญาณ (MUX)

อีกตัวอย่างที่พบบ่อยคือ Multiplexer (MUX) ซึ่งทำหน้าที่เลือกเอาต์พุตตามเงื่อนไข

module mux2to1(
  input  wire a,
  input  wire b,
  input  wire sel,
  output wire y
);

  assign y = sel ? b : a;

endmodule

ถ้า sel = 1 จะเลือก b ถ้า sel = 0 จะเลือก a ── เขียนได้ง่าย ๆ ด้วย ternary operator

ข้อควรระวังในการใช้งาน

  • สัญญาณต้องประกาศเป็น wire: assign ใช้กับ reg ไม่ได้
  • เอาต์พุตหลายตัวควรเขียน assign แยกกัน: เพื่อความอ่านง่าย
  • ใช้สัญญาณกลาง (wire) เมื่อสมการซับซ้อน: เพื่อเพิ่มความชัดเจน

สรุป: วงจรพื้นฐานเขียนได้ด้วย assign

ดังที่เห็น วงจรคอมบิเนชันพื้นฐานทั้งหมดสามารถเขียนได้ด้วย assign ไม่ว่าจะเป็นเกตลอจิก, ตัวบวก หรือ MUX

สำหรับผู้เริ่มต้น แนะนำให้ฝึกเขียนวงจรเล็ก ๆ ด้วย assign เพื่อให้เข้าใจการไหลของสัญญาณและโครงสร้างวงจรได้อย่างเป็นธรรมชาติ

6. ข้อควรระวังและข้อผิดพลาดที่พบบ่อยเมื่อใช้ assign

หลุมพรางที่ผู้เริ่มต้นมักเจอเมื่อใช้ assign

assign เป็นหนึ่งในคำสั่งที่เรียบง่ายที่สุดใน Verilog แต่ความง่ายนี้เองที่ทำให้หลายคนใช้โดยไม่เข้าใจอย่างถูกต้อง และนำไปสู่ข้อผิดพลาดหรือพฤติกรรมที่ไม่คาดคิด

ในส่วนนี้ เราจะอธิบายข้อผิดพลาดที่พบบ่อย และแนวทางแก้ไข

1. ใช้ assign กับ reg แล้วเกิด error

✅ ตัวอย่างโค้ดผิดบ่อย:

reg out;
assign out = a & b;  // ❌ Error! reg ไม่สามารถใช้ assign

💡 สาเหตุและวิธีแก้:

assign ใช้ได้เฉพาะกับ wire เท่านั้น ส่วน reg ต้องใช้ใน always block

วิธีแก้: เปลี่ยน out ให้เป็น wire หรือย้ายไปเขียนใน always

2. เขียน assign ซ้ำกับสัญญาณเดียวกัน

✅ ตัวอย่างที่ผิด:

assign y = a & b;
assign y = a | b;  // ❌ ขัดแย้งกัน

💡 สาเหตุและวิธีแก้:

สัญญาณหนึ่งตัวสามารถมีแหล่งขับ (driver) ได้เพียง 1 แหล่งเท่านั้น หากมีหลาย assign จะเกิด error หรือ undefined behavior

วิธีแก้: ใช้ always block หรือแทรกสัญญาณกลางแทน

3. พยายามตั้งค่าเริ่มต้นด้วย assign

✅ ตัวอย่างที่ผิดพลาด:

assign a = 1'b0;  // ❌ ไม่ใช่ค่าเริ่มต้น แต่หมายถึง a = 0 ตลอดเวลา

💡 สาเหตุและวิธีแก้:

assign หมายถึงการขับค่าอย่างต่อเนื่อง ไม่ใช่การกำหนดค่าเพียงครั้งเดียว

วิธีแก้: ใช้ initial block สำหรับ simulation หรือใช้วงจร reset ในการออกแบบจริง

4. ลืมประกาศสัญญาณก่อนใช้ assign

✅ ตัวอย่างผิด:

assign result = a & b;  // ❌ result ไม่ได้ประกาศ

💡 วิธีแก้:

ต้องประกาศสัญญาณทุกตัวเป็น wire หรือ reg อย่างชัดเจนก่อนใช้งาน

5. ใช้การดำเนินการที่ไม่เหมาะสม

บางการดำเนินการเช่น การหาร (/) หรือการหารเอาเศษ (%) อาจทำให้เครื่องมือสังเคราะห์ FPGA ไม่รองรับ

assign out = a / 3;  // ⚠️ บางกรณีอาจสังเคราะห์ไม่ได้

วิธีแก้: ตรวจสอบว่าโอเปอเรเตอร์รองรับการสังเคราะห์จริงหรือไม่ ถ้าไม่ ควรเขียนใหม่ด้วยลอจิก

6. ใช้ ternary operator ซ้อนกันมากเกินไป

assign out = sel1 ? a : (sel2 ? b : (sel3 ? c : d));  // ❌ อ่านยากมาก

วิธีแก้:

  • ใช้สัญญาณกลาง (wire) เพื่อแยกเป็นหลายบรรทัด
  • หรือใช้ always block แทน

เทคนิคการดีบัก assign

  • ตรวจสอบชนิดสัญญาณ (wire/reg) ให้ถูกต้อง
  • อ่าน warning ของเครื่องมือ simulation อย่างละเอียด
  • ตรวจสอบข้อจำกัดของ FPGA/ASIC tool แต่ละค่าย

สรุป: assign ใช้ง่าย แต่ต้องระวัง

assign เป็นคำสั่งที่เรียบง่ายและทรงพลัง แต่มีข้อจำกัด เช่น ใช้ได้เฉพาะกับ wire และห้ามกำหนดหลายครั้งกับสัญญาณเดียวกัน

การปฏิบัติตามกฎเหล่านี้ จะช่วยให้หลีกเลี่ยงบั๊กและทำให้โค้ดอ่านง่ายและดูแลรักษาได้ดีขึ้น

7. คำถามที่พบบ่อย (FAQ)

เกี่ยวกับคำสั่ง assign ใน Verilog ผู้เริ่มต้นจนถึงระดับกลางมักจะมีข้อสงสัย เราได้รวบรวมคำถามที่ถูกค้นหาบ่อยและอธิบายเป็นรูปแบบ Q&A

Q1: สำหรับผู้เริ่มต้น ควรใช้ assign หรือ always ก่อน?

A: ควรเริ่มจาก assign ก่อน

assign ใช้เขียนวงจรคอมบิเนชันได้ง่าย จึงเหมาะสำหรับผู้เริ่มเรียน Verilog ในขณะที่ always เหมาะกับวงจรซีเควนเชียลและซับซ้อนกว่า

  • ลอจิกง่าย ๆ → assign
  • ต้องมีเวลา/สถานะ → always

Q2: ถ้าอยากใช้ assign กับ reg ทำได้ไหม?

A: ไม่ได้ ต้องใช้ always

assign ใช้ได้เฉพาะกับ wire ส่วน reg ต้องกำหนดค่าใน always

// ✅ ถูกต้อง
reg out;
always @(a or b)
  out = a & b;

// ❌ ผิด
reg out;
assign out = a & b;

Q3: ใช้ assign หลายครั้งกับสัญญาณเดียวกันได้ไหม?

A: ไม่ได้เด็ดขาด

กฎของ Verilog คือ สัญญาณหนึ่งต้องมีแหล่งขับเพียง 1 แหล่ง หากมีหลาย assign จะเกิด error หรือ undefined behavior

Q4: การใช้ delay (#) ใน assign มีประโยชน์ไหม?

A: มีประโยชน์เฉพาะในการ simulation เท่านั้น

assign #5 out = a & b;

delay (#5) จะทำงานใน simulation แต่จะถูกละเว้นเมื่อนำไปสังเคราะห์เป็น FPGA/ASIC

  • Simulation → ใช้ได้
  • Hardware synthesis → ถูกละเว้น

Q5: ถ้าอยากเขียนเงื่อนไขด้วย assign ต้องทำอย่างไร?

A: ใช้ ternary operator

assign out = sel ? a : b;

ถ้า sel=1 → ส่งออก a
ถ้า sel=0 → ส่งออก b
เหมาะกับเงื่อนไขง่าย ๆ ถ้าเงื่อนไขซับซ้อนควรใช้ always

Q6: ทำไม assign ไม่เปลี่ยนค่าเวลา test?

A: ตรวจสอบว่าอินพุตมีการเปลี่ยนจริงหรือไม่

assign จะเปลี่ยนค่าเฉพาะเมื่ออินพุตเปลี่ยน หากอินพุตคงที่ เอาต์พุตก็จะคงที่เช่นกัน

  • อินพุตมีการเปลี่ยนจริงหรือไม่?
  • มีการกำหนดค่าเริ่มต้นให้สัญญาณหรือไม่?
  • ใน simulation waveform มีการเปลี่ยนหรือเปล่า?

Q7: โค้ดที่เขียนด้วย assign สามารถสังเคราะห์ได้หรือไม่?

A: ส่วนใหญ่สังเคราะห์ได้ ยกเว้นบางโอเปอเรเตอร์

  • AND / OR / XOR → ✅ สังเคราะห์ได้
  • หาร (/) หรือทศนิยม → ⚠️ บางกรณีสังเคราะห์ไม่ได้

ดังนั้นก่อนใช้ควรตรวจสอบว่าเครื่องมือ FPGA/ASIC รองรับหรือไม่

8. พจนานุกรมคำศัพท์พื้นฐานสำหรับผู้เริ่มต้น Verilog

ต่อไปนี้คือคำศัพท์พื้นฐานที่ควรเข้าใจก่อนเริ่มเขียน Verilog โดยเฉพาะคำที่เกี่ยวข้องกับ assign และวงจรคอมบิเนชัน

wire (ไวร์)

ความหมาย:
สัญญาณที่จำลองการเป็น “สายไฟ” ในวงจรลอจิก ใช้สำหรับรับค่าจากสัญญาณหรือเอาต์พุตอื่น

คุณสมบัติ:

  • สามารถกำหนดค่าได้ด้วย assign
  • ไม่สามารถเก็บค่าด้วยตัวเอง
  • ใช้หลัก ๆ ในวงจรคอมบิเนชัน

ตัวอย่าง:

wire a, b, out;
assign out = a & b;

reg (เรจิสเตอร์)

ความหมาย:
สัญญาณที่เก็บค่าได้ชั่วคราว ใช้ใน always block เป็นหลัก

คุณสมบัติ:

  • ไม่สามารถใช้กับ assign
  • เก็บสถานะในวงจรซีเควนเชียล
  • มักใช้ร่วมกับ clock หรือเงื่อนไข

ตัวอย่าง:

reg out;
always @(posedge clk) out <= a;

assign (อะแซน์)

ความหมาย:
การกำหนดค่าแบบต่อเนื่อง (continuous assignment) ให้กับสัญญาณชนิด wire

คุณสมบัติ:

  • ใช้ในวงจรคอมบิเนชัน
  • เอาต์พุตเปลี่ยนทันทีตามอินพุต
  • รองรับการคำนวณ ลอจิก เงื่อนไข และค่าคงที่

ตัวอย่าง:

assign y = a & b;

always (ออลเวย์)

ความหมาย:
บล็อกที่ทำงานเมื่อเกิดเหตุการณ์ (event) เช่น ขอบสัญญาณ clock

คุณสมบัติ:

  • ใช้กับ reg
  • รองรับ if, case, และการควบคุมเชิงเงื่อนไข
  • ใช้สำหรับวงจรซีเควนเชียล

ตัวอย่าง:

always @(posedge clk) begin
  out <= a + b;
end

วงจรคอมบิเนชัน (Combinational Circuit)

ความหมาย:
วงจรที่เอาต์พุตขึ้นอยู่กับอินพุตปัจจุบันเท่านั้น

คุณสมบัติ:

  • ไม่มีองค์ประกอบเก็บค่า
  • เช่น ลอจิกเกต, ตัวบวก, MUX
  • ใน Verilog ใช้ assign หรือ always @(*)

วงจรซีเควนเชียล (Sequential Circuit)

ความหมาย:
วงจรที่เอาต์พุตขึ้นอยู่กับทั้งอินพุตปัจจุบันและสถานะในอดีต

คุณสมบัติ:

  • มีองค์ประกอบเก็บค่า เช่น flip-flop, register
  • ทำงานแบบซิงโครไนซ์กับ clock
  • เขียนด้วย always @(posedge clk)

ternary operator (ตัวดำเนินการเงื่อนไข)

ความหมาย:
โครงสร้างเงื่อนไขรูปแบบ เงื่อนไข ? ค่าเมื่อจริง : ค่าเมื่อเท็จ

คุณสมบัติ:

  • มักใช้ใน assign
  • เขียนได้กระชับกว่า if

ตัวอย่าง:

assign y = sel ? a : b;

module (โมดูล)

ความหมาย:
หน่วยการออกแบบพื้นฐานใน Verilog

คุณสมบัติ:

  • มีพอร์ต input/output
  • สามารถรวมโมดูลหลายตัวเป็นโครงสร้างแบบลำดับชั้น

ตัวอย่าง:

module adder(input a, input b, output sum);
  assign sum = a + b;
endmodule

initial (อินิเทียล)

ความหมาย:
บล็อกที่ทำงานเพียงครั้งเดียวเมื่อเริ่ม simulation

คุณสมบัติ:

  • ไม่ถูกสังเคราะห์เป็นวงจรจริง
  • ใช้ใน testbench เป็นหลัก

ตัวอย่าง:

initial begin
  a = 0;
  b = 1;
end

non-blocking assignment (<=)

ความหมาย:
รูปแบบการกำหนดค่าใน always block ที่ทำงานแบบขนาน

คุณสมบัติ:

  • ใช้บ่อยในการออกแบบที่ซิงโครไนซ์กับ clock
  • ช่วยหลีกเลี่ยงปัญหาลำดับการประมวลผล

ตัวอย่าง:

always @(posedge clk) begin
  out1 <= in1;
  out2 <= in2;
end

สรุป: เข้าใจคำศัพท์คือก้าวแรกในการเรียน Verilog

การเข้าใจคำเหล่านี้จะช่วยให้วิเคราะห์ error ได้ง่ายขึ้น และทำให้การเขียน Verilog ราบรื่นขึ้น

9. สรุป|การใช้งาน assign อย่างเชี่ยวชาญ

ในบทความนี้ เราได้อธิบายเกี่ยวกับ assign ใน Verilog ตั้งแต่พื้นฐานไปจนถึงการประยุกต์ใช้ สำหรับผู้เริ่มต้น assign ถือเป็นคำสั่งแรก ๆ ที่ควรเรียนรู้ เพราะเป็นเครื่องมือหลักในการเขียน วงจรคอมบิเนชัน

ทบทวนจุดสำคัญของ assign

✅ บทบาทของ assign

  • ใช้ assign เพื่อกำหนดค่าแบบต่อเนื่องให้กับสัญญาณชนิด wire
  • เอาต์พุตเปลี่ยนทันทีเมื่ออินพุตเปลี่ยน
  • เหมาะสำหรับการเขียนวงจรคอมบิเนชัน

✅ กฎการใช้งาน

  • assign ใช้กับ wire เท่านั้น ไม่สามารถใช้กับ reg
  • ห้ามเขียน assign ซ้ำกับสัญญาณเดียวกัน
  • ไม่สามารถใช้ assign เพื่อกำหนดค่าเริ่มต้น

✅ เคล็ดลับการใช้งาน

  • เข้าใจความแตกต่างระหว่าง assign และ always
  • ใช้ ternary operator เมื่อมีเงื่อนไขง่าย ๆ
  • เมื่อสมการซับซ้อน ควรแบ่งออกเป็นหลายบรรทัดด้วย wire กลาง เพื่อให้อ่านง่าย

ก้าวต่อไปที่ควรเรียนรู้

หลังจากเข้าใจ assign แล้ว ขั้นตอนถัดไปที่ควรศึกษา:

  • การเขียน วงจรซีเควนเชียล ด้วย always
  • การใช้ if และ case สำหรับเงื่อนไขที่ซับซ้อน
  • การเขียน testbench และการ simulation
  • การออกแบบแบบโมดูลาร์ (hierarchical design)

Verilog เป็นภาษาที่ต้องอาศัยทั้ง “การฝึกฝน” และ “ประสบการณ์จริง” การลองเขียนวงจรเล็ก ๆ ด้วย assign ซ้ำ ๆ จะช่วยให้คุณเข้าใจการทำงานของสัญญาณและโครงสร้างวงจรได้อย่างลึกซึ้ง

บทส่งท้าย

เมื่อคุณสามารถใช้ assign ได้อย่างถูกต้อง นั่นหมายความว่าคุณได้ก้าวข้ามอุปสรรคสำคัญของการเรียน Verilog ไปแล้ว

หวังว่าบทความนี้จะเป็นเสมือน “คู่มือ assign” ที่คุณสามารถกลับมาเปิดอ่านได้ทุกครั้งที่ต้องการทบทวน