1. บทนำ
parameter
ใน Verilog คืออะไร?
Verilog เป็นหนึ่งในภาษาบรรยายฮาร์ดแวร์ (HDL: Hardware Description Language) ที่ใช้สำหรับการออกแบบวงจรดิจิทัล ภายในนั้น parameter
(พารามิเตอร์) เป็นฟีเจอร์สำคัญที่ช่วยเพิ่มความยืดหยุ่นและการนำกลับมาใช้ซ้ำของการออกแบบ
parameter
เป็นการกำหนดค่าคงที่ (constant) โดยมีชื่อกำกับ ซึ่งมีประโยชน์อย่างมากเมื่อคุณต้องการใช้โมดูลเดียวกันซ้ำภายใต้การตั้งค่าที่ต่างกัน หรือเพื่อให้โค้ดอ่านและดูแลได้ง่ายขึ้น โดยปกติแล้วแทนที่จะระบุค่าแบบตายตัว (เช่น ความกว้างของบิต ขนาดบัส หรือการตั้งค่า timing) เราสามารถกำหนดเป็น parameter
เพื่อให้ปรับเปลี่ยนค่าได้ง่ายภายหลัง
ทำไม parameter
จึงสำคัญ?
การใช้ parameter
ในการออกแบบด้วย Verilog มีข้อดีดังนี้:
- เพิ่มความสามารถในการนำกลับมาใช้ซ้ำ
โมดูลที่สร้างขึ้นครั้งเดียวสามารถนำไปใช้ซ้ำได้หลายงาน ทำให้การพัฒนาโครงการขนาดใหญ่มีประสิทธิภาพมากขึ้น - เพิ่มความสะดวกในการบำรุงรักษา
ค่าคงที่จะถูกจัดการในที่เดียว หากมีการเปลี่ยนแปลงก็เพียงแก้ไขparameter
ที่เกี่ยวข้อง - ทำให้โค้ดอ่านง่ายขึ้น
ช่วยกำจัด “magic number” และทำให้ผู้อ่านเข้าใจได้ทันทีว่าค่าใดมีความหมายว่าอะไร
ตัวอย่างเช่น การเขียนค่าความกว้างบัสเป็นตัวเลข “8” หรือ “16” โดยตรง อาจไม่ชัดเจน แต่หากใช้ parameter DATA_WIDTH = 8;
และตามด้วย [DATA_WIDTH-1:0]
จะสื่อความหมายของการออกแบบได้ชัดเจนกว่า
สิ่งที่จะได้จากบทความนี้
บทความนี้จะอธิบายการใช้ parameter
ใน Verilog ตั้งแต่พื้นฐานไปจนถึงการประยุกต์ โดยเหมาะสำหรับ:
- ผู้เริ่มต้นที่กำลังเรียนรู้ Verilog
- ผู้ที่ต้องการออกแบบโมดูลให้มีความยืดหยุ่นมากขึ้น
- ผู้ออกแบบที่ต้องการให้โค้ดอ่านง่ายและดูแลรักษาง่าย
เมื่ออ่านจบ คุณจะเข้าใจทั้งวิธีการใช้ parameter
พื้นฐาน วิธีการนำไปใช้ในโมดูลจริง รวมถึงข้อควรระวังที่ควรทราบ
2. โครงสร้างพื้นฐานของ parameter
วิธีประกาศ parameter
ใน Verilog, parameter
ใช้สำหรับกำหนดค่าคงที่ที่ใช้ภายในโมดูล โครงสร้างพื้นฐานมีดังนี้:
parameter ชื่อพารามิเตอร์ = ค่า;
ตัวอย่างเช่น หากต้องการกำหนดความกว้างข้อมูลเป็น 8 บิต:
parameter DATA_WIDTH = 8;
parameter
ที่ประกาศไว้สามารถใช้งานได้เหมือนตัวแปรภายในโมดูล อย่างไรก็ตาม parameter
เป็นค่าคงที่ที่ถูกกำหนดตอนออกแบบ ไม่สามารถเปลี่ยนแปลงได้ในระหว่างการทำงานจริง
การกำหนด parameter
หลายค่าในครั้งเดียว
ถ้าโมดูลมีหลายพารามิเตอร์ สามารถกำหนดในบรรทัดเดียวโดยใช้เครื่องหมายคอมมา:
parameter WIDTH = 8, DEPTH = 256;
หรือเพื่อให้อ่านง่ายขึ้น อาจเขียนแยกบรรทัด:
parameter WIDTH = 8;
parameter DEPTH = 256;
การกำหนดความกว้างบิต (bit-width)
โดยค่าเริ่มต้น parameter
จะเป็นจำนวนเต็มแบบไม่เซ็น (unsigned) ขนาด 32 บิต แต่สามารถระบุความกว้างได้ชัดเจน เช่น:
parameter [7:0] INIT_VALUE = 8'hFF;
วิธีนี้ทำให้ INIT_VALUE
ถูกกำหนดอย่างชัดเจนว่าเป็นค่าขนาด 8 บิต ซึ่งสำคัญเมื่อมีการออกแบบที่เกี่ยวข้องกับการดำเนินการทางบิต
ขอบเขต (scope) และการเขียนทับ (override) ของ parameter
parameter
เป็นค่าคงที่ที่ใช้ได้เฉพาะภายในโมดูล ไม่สามารถเข้าถึงโดยตรงจากภายนอกได้ อย่างไรก็ตาม ขณะอินสแตนซ์โมดูลสามารถเขียนทับ (override) ค่าพารามิเตอร์จากโมดูลระดับบนได้ (รายละเอียดจะกล่าวในภายหลัง)
นอกจากนี้ Verilog ยังมี localparam
ซึ่งคล้ายกัน แต่ไม่สามารถเขียนทับจากภายนอกได้
3. การทำโมดูลให้เป็นแบบปรับได้ด้วย parameter
parameter
เพื่อเพิ่มความยืดหยุ่นของโมดูล
parameter
ช่วยให้โมดูลมีความยืดหยุ่นและสามารถนำโมดูลเดียวกันไปใช้ในเงื่อนไขที่ต่างกัน ได้อย่างมีประสิทธิภาพ โดยการกำหนดค่าต่าง ๆ (เช่น ความกว้างบิต ขนาดอาร์เรย์ รอบสัญญาณนาฬิกา) เป็น parameter
จะทำให้การออกแบบหนึ่งสามารถใช้ได้หลายวัตถุประสงค์
ตัวอย่าง: โมดูลตัวบวกที่ปรับได้ด้วยพารามิเตอร์
ด้านล่างคือตัวอย่างโมดูลตัวบวก (adder) ที่สามารถกำหนดความกว้างบิตได้ด้วย parameter
:
module adder #(parameter WIDTH = 8)(
input [WIDTH-1:0] a,
input [WIDTH-1:0] b,
output [WIDTH-1:0] sum
);
assign sum = a + b;
endmodule
โมดูลนี้มีค่าเริ่มต้นเป็นตัวบวกขนาด 8 บิต แต่เมื่อทำการอินสแตนซ์สามารถเปลี่ยนค่า WIDTH
ได้ เพื่อใช้เป็นตัวบวกความกว้างบิตอื่น
วิธีเขียนทับพารามิเตอร์จากโมดูลระดับบน
1. การใช้โครงสร้าง #()
ขณะอินสแตนซ์โมดูล สามารถใช้ #()
เพื่อกำหนดค่าใหม่ให้พารามิเตอร์ จากโมดูลระดับบน:
adder #(.WIDTH(16)) adder_inst (
.a(a_input),
.b(b_input),
.sum(sum_output)
);
โค้ดนี้จะทำให้ตัวบวกทำงานเป็นแบบ 16 บิต
2. การใช้ defparam
(ไม่แนะนำ)
อีกวิธีหนึ่งคือใช้คำสั่ง defparam
:
defparam adder_inst.WIDTH = 16;
อย่างไรก็ตาม defparam
มักทำให้โค้ดกระจัดกระจายและยากต่อการบำรุงรักษา ดังนั้นในแนวทางการออกแบบสมัยใหม่ถือว่าไม่แนะนำ จึงควรใช้วิธี #()
เพื่อความชัดเจน
การเขียนทับโมดูลที่มีหลายพารามิเตอร์
ถ้าโมดูลมีหลายพารามิเตอร์ ก็สามารถกำหนดค่าผ่าน #()
โดยใช้เครื่องหมายคอมมาคั่น:
module fifo #(parameter DATA_WIDTH = 8, DEPTH = 64)(/* ports */);
// การอินสแตนซ์ในโมดูลระดับบน
fifo #(
.DATA_WIDTH(16),
.DEPTH(128)
) fifo_inst (
/* การเชื่อมต่อ */
);
วิธีนี้ช่วยให้โมดูลมีการนำกลับมาใช้ซ้ำสูงและปรับการตั้งค่าได้ยืดหยุ่น
4. ตัวอย่างการประยุกต์ใช้ parameter
parameter
ไม่ได้จำกัดเพียงแค่การแทนค่าคงที่เท่านั้น แต่ยังสามารถนำไปประยุกต์ใช้ได้หลากหลายในการออกแบบ Verilog ส่วนนี้จะแสดงตัวอย่างจริงเพื่อให้เข้าใจวิธีการใช้ parameter
ในระดับที่สูงขึ้น
การปรับเปลี่ยนความกว้างบิตและขนาดบัส
ในการออกแบบวงจรดิจิทัล การปรับความกว้างบิตได้อย่างยืดหยุ่น เป็นสิ่งที่มีประโยชน์มาก โดยเฉพาะใน datapath หรือ bus design ที่มักมีการเปลี่ยนข้อกำหนดภายหลัง
module register #(parameter WIDTH = 8)(
input wire clk,
input wire [WIDTH-1:0] d,
output reg [WIDTH-1:0] q
);
always @(posedge clk)
q <= d;
endmodule
ตัวอย่างนี้ใช้ WIDTH
เพื่อปรับขนาดบิตได้อิสระ เช่น 8 บิต, 16 บิต หรือ 32 บิต
การจัดการค่าการออกแบบจากจุดเดียวเพื่อความอ่านง่ายและดูแลรักษา
แม้จะมีการใช้ค่าคงที่ร่วมกันหลายโมดูลหรือหลายไฟล์ การใช้ parameter
ทำให้กำหนดและแก้ไขได้ในที่เดียว
ตัวอย่าง:
parameter CLK_DIV = 100;
จากนั้นสามารถใช้ในวงจรแบ่งสัญญาณนาฬิกา (clock divider), ตัวจับเวลา (timer) หรือเคาน์เตอร์ (counter) ได้พร้อมกัน:
always @(posedge clk)
if (counter == CLK_DIV)
clk_out <= ~clk_out;
สิ่งนี้ช่วยกำจัด “magic number” และทำให้เจตนาของโค้ดชัดเจน
การใช้ร่วมกับ generate
เพื่อควบคุมโครงสร้างที่ซ้ำกัน
parameter
สามารถใช้ร่วมกับ generate
เพื่อสร้างโครงสร้างซ้ำได้ยืดหยุ่น ตัวอย่างเช่น การสร้าง shift register N ขั้น:
module shift_reg #(parameter STAGES = 4)(
input wire clk,
input wire in,
output wire out
);
reg [STAGES-1:0] shift;
always @(posedge clk)
shift <= {shift[STAGES-2:0], in};
assign out = shift[STAGES-1];
endmodule
เพียงเปลี่ยนค่า STAGES
ก็สามารถสร้าง shift register ได้หลายขนาดตามต้องการ ซึ่งทำให้การออกแบบฮาร์ดแวร์ยืดหยุ่นและประหยัดทรัพยากร
การใช้งานใน Testbench
ใน testbench ก็สามารถใช้ parameter
เพื่อกำหนดเงื่อนไขการทดสอบจากจุดเดียว และเปลี่ยนการตั้งค่าทั้งหมดได้รวดเร็ว:
module testbench;
parameter DATA_WIDTH = 16;
reg [DATA_WIDTH-1:0] a, b;
wire [DATA_WIDTH-1:0] result;
adder #(.WIDTH(DATA_WIDTH)) dut (
.a(a),
.b(b),
.sum(result)
);
// กระบวนการทดสอบ...
endmodule
เพียงเปลี่ยนค่าพารามิเตอร์ใน testbench ก็สามารถทดสอบโมดูลเดียวกันกับหลายความกว้างบิตได้สะดวก
5. ข้อควรระวังในการใช้ parameter
แม้ว่า parameter
จะเป็นฟีเจอร์ที่มีประโยชน์มาก แต่หากใช้งานไม่ถูกต้องอาจก่อให้เกิดพฤติกรรมที่ไม่คาดคิด หรือข้อผิดพลาดในการออกแบบ ได้ ส่วนนี้จะอธิบายจุดที่ควรระวัง
อย่าลืมระบุความกว้างบิตให้ชัดเจน
ใน Verilog, parameter
จะถูกตีความเป็นจำนวนเต็มไม่เซ็น (unsigned) ขนาด 32 บิต โดยค่าเริ่มต้น แม้จะไม่มีปัญหาเมื่อใช้กับค่าธรรมดา แต่เมื่อมีการคำนวณทางบิตหรือการเลือกบิต (slice) ควรระบุความกว้างบิตชัดเจน:
parameter [7:0] INIT_VAL = 8'hFF; // กำหนดให้เป็นค่า 8 บิตอย่างชัดเจน
สิ่งนี้ช่วยให้โค้ดทำงานตามที่ตั้งใจ ป้องกันคำเตือนระหว่างการจำลองและหลีกเลี่ยงบั๊กขณะสังเคราะห์
เข้าใจความแตกต่างระหว่าง parameter
และ localparam
ใน Verilog มี localparam
ซึ่งคล้ายกับ parameter
แต่ไม่สามารถแก้ไขค่าจากภายนอกโมดูลได้
ชนิด | เขียนทับจากโมดูลระดับบนได้หรือไม่ | การใช้งาน |
---|---|---|
parameter | ได้ | ค่าที่ต้องการให้กำหนดจากภายนอกโมดูล |
localparam | ไม่ได้ | ค่าคงที่ที่ใช้เฉพาะภายในโมดูล |
ตัวอย่าง:
module example #(parameter WIDTH = 8) ();
localparam HALF_WIDTH = WIDTH / 2;
endmodule
ในกรณีนี้ localparam
เหมาะสำหรับค่าช่วยคำนวณที่ไม่ควรถูกแก้ไขจากภายนอก
การนิยามซ้ำและปัญหาเกี่ยวกับลำดับชั้น (hierarchy)
เมื่อโมดูลซับซ้อนขึ้น อาจสับสนได้ว่าค่า parameter
ใดถูกใช้จริง โดยเฉพาะหากมีหลายอินสแตนซ์ที่ใช้ชื่อเดียวกันแต่กำหนดค่าต่างกัน ซึ่งอาจก่อให้เกิดพฤติกรรมที่ไม่คาดคิด
แนวทางแก้ไข:
- ตั้งชื่อนิพจน์ให้ชัดเจน เช่น
FIFO_DEPTH
,ALU_WIDTH
- เขียนโค้ดโดยคำนึงถึงขอบเขตของโมดูลแต่ละระดับ
ข้อจำกัดของเครื่องมือสังเคราะห์ (synthesis tools)
เครื่องมือสังเคราะห์และซิมูเลชันบางตัวอาจมีข้อจำกัดหรือพฤติกรรมต่างกันในการจัดการ parameter
เช่น:
- การคำนวณที่เกี่ยวข้องกับ
parameter
แบบกำหนดบิต อาจทำงานต่างกันไปขึ้นกับเครื่องมือ - การตีความ signed/unsigned อาจแตกต่างกัน
- บางกรณีไม่รองรับ
defparam
เพราะเป็นโครงสร้างที่ล้าสมัย
ดังนั้นจึงควรทดสอบกับ toolchain ที่ใช้งานจริง ก่อนนำไปใช้กับโครงการ
6. FAQ: คำถามที่พบบ่อยและคำตอบ
ส่วนนี้จะอธิบายคำถามที่มักเกิดขึ้นเกี่ยวกับ parameter
ใน Verilog ตั้งแต่ระดับผู้เริ่มต้นจนถึงระดับกลาง โดยอ้างอิงจากปัญหาที่มักพบในสถานการณ์จริง
Q1. ความแตกต่างระหว่าง parameter
และ localparam
คืออะไร?
A1.
parameter
เป็นค่าคงที่ที่สามารถแก้ไขได้จากโมดูลภายนอก ในขณะที่ localparam
เป็นค่าคงที่ที่ใช้ได้เฉพาะภายในโมดูล และไม่สามารถเขียนทับได้
parameter
: มีความยืดหยุ่น แต่ต้องระวังการถูกเขียนทับโดยไม่ตั้งใจlocalparam
: เหมาะกับค่าที่ต้องการให้คงที่และไม่แก้ไขจากภายนอก
แนวทางเลือกใช้:
- หากต้องการนำโมดูลไปใช้ซ้ำ → ใช้
parameter
- หากต้องการค่าคงที่ที่ไม่ควรถูกแก้ไข → ใช้
localparam
Q2. ถ้าไม่ระบุความกว้างบิตของ parameter
จะเป็นอย่างไร?
A2.
ใน Verilog, parameter
ที่ไม่ได้ระบุความกว้างบิตจะถูกตีความเป็นจำนวนเต็มไม่เซ็น (unsigned) 32 บิต โดยอัตโนมัติ
parameter WIDTH = 8; // จริง ๆ แล้วมีขนาด 32 บิต
หากไม่ได้ระบุความกว้างบิตอย่างชัดเจน อาจเกิดปัญหาในการขยายบิต (sign extension) หรือผลลัพธ์ไม่ตรงตามที่คาดไว้ โดยเฉพาะเมื่อมีการคำนวณหรือเลือกบิต (slice)
ดังนั้นควรระบุความกว้างบิต เช่น:
parameter [7:0] WIDTH = 8;
Q3. parameter
ต้องเป็นค่าคงที่เสมอหรือไม่?
A3.
ใช่, parameter
ต้องเป็นค่าคงที่ในขั้นตอนออกแบบ ไม่สามารถกำหนดค่าจากตัวแปรหรือสัญญาณที่เปลี่ยนแปลงได้
ตัวอย่างโค้ดที่ผิด:
input [7:0] a;
parameter WIDTH = a; // ❌ Error
ตัวอย่างที่ถูกต้อง:
parameter WIDTH = 8; // ✅ ค่าคงที่
Q4. ถ้าเปลี่ยนค่าของ parameter
จะส่งผลอย่างไรกับ FPGA?
A4.
เมื่อเปลี่ยนค่า parameter
จะส่งผลโดยตรงกับโครงสร้างวงจรที่สังเคราะห์ เช่น หากเปลี่ยนความกว้างบิตของตัวบวก (adder) วงจรก็จะใหญ่ขึ้นหรือลดลง ส่งผลต่อการใช้ทรัพยากรและความหน่วง (delay) ของวงจร
นี่คือข้อดีที่ทำให้ Verilog มีความยืดหยุ่น แต่หากไม่ตรวจสอบอย่างละเอียด อาจได้โครงสร้างวงจรที่ไม่ตรงกับความคาดหวัง
Q5. สามารถใช้การคำนวณทางคณิตศาสตร์หรือทางตรรกะกับ parameter
ได้หรือไม่?
A5.
ได้, parameter
ถูกประเมินค่าเป็นค่าคงที่ในขั้นตอนออกแบบ ดังนั้นสามารถใช้การคำนวณทางคณิตศาสตร์ (เช่น +, -, *) และตรรกะ (AND, OR, NOT) ได้
parameter WIDTH = 8;
parameter HALF_WIDTH = WIDTH / 2;
แต่ควรระวังขนาดบิตและ signed/unsigned เพื่อป้องกันผลลัพธ์ที่ไม่ตั้งใจ โดยควรระบุความกว้างบิตให้ชัดเจนหลังการคำนวณ
7. สรุป
parameter
ใน Verilog เป็นองค์ประกอบสำคัญที่ช่วยให้งานออกแบบฮาร์ดแวร์มีความยืดหยุ่นและสามารถนำกลับมาใช้ซ้ำได้สูง บทความนี้ได้อธิบายตั้งแต่โครงสร้างพื้นฐานไปจนถึงการประยุกต์ใช้งานจริง
สรุปประเด็นสำคัญ
parameter
คือฟังก์ชันการกำหนดค่าคงที่ภายในโมดูล ที่ช่วยเพิ่มความทั่วไปและความสะดวกในการดูแลรักษา- สามารถเปลี่ยนค่าพารามิเตอร์จากโมดูลภายนอกด้วยโครงสร้าง
#()
- การใช้ร่วมกับ
generate
ทำให้ควบคุมโครงสร้างซ้ำและการเลือกเงื่อนไข ได้อย่างยืดหยุ่น - ควรระวังเรื่องการกำหนดความกว้างบิต, ความแตกต่างกับ
localparam
และพฤติกรรมที่ขึ้นอยู่กับเครื่องมือสังเคราะห์ - FAQ ได้ครอบคลุมข้อเข้าใจผิดที่พบบ่อยและปัญหาที่ควรหลีกเลี่ยง
ข้อคิดส่งท้าย
ความสามารถในการใช้ parameter
อย่างมีประสิทธิภาพในการออกแบบโมดูล Verilog นั้นมีผลโดยตรงต่อคุณภาพและความสามารถในการขยายของโค้ด
สำหรับผู้เริ่มต้น ควรเริ่มจากการเข้าใจวิธีใช้พื้นฐาน และค่อย ๆ ขยายไปสู่การประยุกต์ในงานที่ซับซ้อนขึ้น เพื่อสร้างการออกแบบที่มีความชาญฉลาดและบำรุงรักษาง่าย
เมื่อโครงการมีความซับซ้อนมากขึ้น การนำ parameter
มาใช้จะช่วยให้คุณสามารถเปลี่ยนการตั้งค่าและนำโมดูลเดิมกลับมาใช้ใหม่ ได้อย่างมีประสิทธิภาพ แทนที่จะต้องสร้างใหม่ทั้งหมด