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)” เพื่อเพิ่มความเข้าใจที่ลึกขึ้น。