1. บทนำ
Verilog เป็นหนึ่งในภาษาบรรยายฮาร์ดแวร์ (HDL) ที่ถูกใช้อย่างแพร่หลายในการออกแบบวงจรดิจิทัล ภายในนั้น คำสั่ง case เป็นโครงสร้างที่สำคัญสำหรับ การเขียนเงื่อนไขแบบแยกแขนงอย่างมีประสิทธิภาพ โดยมักถูกใช้บ่อยใน การออกแบบการเปลี่ยนสถานะ (State Machine) และตัวเลือกหลายทาง (Multiplexer)。
บทความนี้จะอธิบายรายละเอียดตั้งแต่พื้นฐานจนถึงการประยุกต์ใช้ case ของ Verilog พร้อมทั้งข้อควรระวังในการใช้งาน โดยจะมี ตัวอย่างโค้ดที่เข้าใจง่ายสำหรับผู้เริ่มต้น เพื่อให้คุณสามารถเรียนรู้ได้อย่างมั่นใจจนจบเนื้อหา。
2. พื้นฐานของคำสั่ง case ใน Verilog
case คืออะไร?
คำสั่ง case ใน Verilog คือโครงสร้างที่ใช้ ดำเนินการต่าง ๆ ตามเงื่อนไข (selector) ที่กำหนด ซึ่งทำงานคล้ายกับคำสั่ง switch-case ในภาษา C แต่มีความแตกต่างบางประการ。
โครงสร้างพื้นฐานเป็นดังนี้:
case (expression)
เงื่อนไข1: คำสั่ง1;
เงื่อนไข2: คำสั่ง2;
เงื่อนไข3: คำสั่ง3;
default: คำสั่ง4; // กรณีที่ไม่ตรงกับเงื่อนไขใด ๆ
endcaseการใช้งานพื้นฐานของ case
ตัวอย่างนี้แสดง case แบบง่าย ที่กำหนดค่าสัญญาณ out ตามค่าของอินพุต 2 บิต sel。
module case_example(input [1:0] sel, output reg [3:0] out);
always @(*) begin
case (sel)
2'b00: out = 4'b0001;
2'b01: out = 4'b0010;
2'b10: out = 4'b0100;
2'b11: out = 4'b1000;
default: out = 4'b0000; // ค่าเริ่มต้นเพื่อความปลอดภัย
endcase
end
endmoduleในโค้ดนี้ out จะเปลี่ยนค่าตาม sel และเมื่อใส่ default จะช่วย ป้องกันปัญหาเมื่อได้รับค่าที่ไม่คาดคิด。
ความแตกต่างระหว่าง case, casex, casez
Verilog มีรูปแบบขยายของคำสั่ง case ได้แก่ casex และ casez ซึ่งทำงานคล้ายตัวแทน (wildcard) ที่สามารถละเว้นบิตบางตำแหน่งได้。
| รูปแบบ | คุณสมบัติ |
|---|---|
case | ต้องตรงกันทั้งหมด (ค่าเริ่มต้น) |
casex | ละเว้นค่า X (ไม่ทราบค่า) และ Z (high-impedance) |
casez | ละเว้นเฉพาะค่า Z |
ตัวอย่างการใช้ casez:
casez (sel)
2'b1?: out = 4'b1111; // ถ้าบิตบนสุดเป็น 1 จะตรงกัน
2'b01: out = 4'b0001;
default: out = 4'b0000;
endcaseที่นี่ 1? หมายถึง “ตรงกันเมื่อบิตบนสุดเป็น 1 โดยไม่สนใจบิตล่าง”。
3. ตัวอย่างการใช้งาน case
การแยกเงื่อนไขพื้นฐาน
โค้ดนี้เป็นดีโคเดอร์ของ CPU ขนาดเล็ก ที่ทำงานต่างกันตามค่าอินพุต opcode ขนาด 8 บิต。
module decoder(input [7:0] opcode, output reg [3:0] control_signal);
always @(*) begin
case (opcode)
8'h00: control_signal = 4'b0001; // NOP
8'h01: control_signal = 4'b0010; // ADD
8'h02: control_signal = 4'b0100; // SUB
default: control_signal = 4'b0000; // คำสั่งไม่ถูกต้อง
endcase
end
endmoduleการใช้ใน State Machine
คำสั่ง case มักถูกนำมาใช้ใน Finite State Machine (FSM)。
typedef enum reg [1:0] {IDLE, RUN, STOP} state_t;
state_t current_state, next_state;
always @(posedge clk) begin
if (reset)
current_state <= IDLE;
else
current_state <= next_state;
end
always @(*) begin
case (current_state)
IDLE: next_state = RUN;
RUN: next_state = STOP;
STOP: next_state = IDLE;
default: next_state = IDLE;
endcase
endโค้ดนี้เป็น FSM ที่มี 3 สถานะ การใช้ case จะช่วยให้โครงสร้างโค้ดอ่านง่ายและชัดเจน。
4. ข้อควรระวังในการใช้ case
1. ควรมี default เสมอ
การครอบคลุมทุกเงื่อนไขเป็นสิ่งสำคัญ หากไม่มี default อาจทำให้เกิด latch โดยไม่ตั้งใจ เมื่อทำการสังเคราะห์ลง FPGA/ASIC。
2. ระวังการใช้ casex และ casez
คำสั่งเหล่านี้อาจทำให้ สัญญาณที่ไม่ต้องการถูกจับคู่ ดังนั้นควร ทดสอบการทำงานด้วยการจำลอง ทุกครั้ง。
3. อย่าใช้ case มากเกินไป
ถ้าเป็นเงื่อนไขขนาดเล็ก if-else อาจ เข้าใจง่ายกว่า ดังนั้นควรใช้ case เฉพาะเมื่อมีหลายทางเลือก。
5. สรุป
บทความนี้ได้อธิบายเกี่ยวกับคำสั่ง case ของ Verilog ได้แก่:
✅ โครงสร้างพื้นฐานและการทำงาน
✅ ความแตกต่างระหว่าง case, casex, casez
✅ ตัวอย่างการใช้งานจริง (การแยกเงื่อนไขและ FSM)
✅ ข้อควรระวังในการใช้งาน
การใช้คำสั่ง case อย่างถูกต้อง จะช่วย เพิ่มความชัดเจนของโค้ดและลดข้อผิดพลาดในการออกแบบ แนะนำให้นำไปประยุกต์ใช้กับการออกแบบวงจรของคุณ!
สิ่งที่ควรเรียนรู้ต่อไปใน Verilog
หลังจากเข้าใจ case แล้ว ขั้นต่อไปควรศึกษา “always” และ “วงจรผสม (Combinational) กับวงจรลำดับ (Sequential)” เพื่อเพิ่มความเข้าใจที่ลึกขึ้น。



