واجهة Module
قائمة منافذ module هي واجهته - كل ما يستطيع العالم الخارجي رؤيته ولمسه. داخل module، يحسب الجسم المخارج من المداخل. المنافذ هي الأسلاك التي تعبر الحدود.
إعلان ANSI الحديث يضع الاتجاه والنوع والعرض كلها في القائمة:
module my_module(
input wire clk,
input wire reset,
input wire [7:0] data_in,
output reg [7:0] data_out,
output wire valid
);
// الجسم
endmodule
هذا هو الشكل كاملًا. لكل منفذ:
- اتجاه:
inputأوoutputأوinout. - نوع:
wireأوreg(أوlogicفي SystemVerilog). - عرض: نطاق مثل
[7:0]، أو بت واحدة إن حُذف. - اسم: المُعرّف الذي ستستخدمه داخل الجسم.
الاتجاهات الثلاثة
input - يُقاد من الخارج
المداخل دائمًا wire. القائد خارج module - إما إشارة من module أعلى، أو reg من testbench. داخل module، يمكنك قراءة المداخل في تعابير لكن لا يمكنك أبدًا الإسناد إليها:
module reader(input wire [7:0] data);
initial $display("data = %h", data);
endmodule
كتابة data = ... داخل module ستكون خطأ.
output - يُقاد من الداخل
المخارج تُقاد بشيء داخل هذا module. يمكن أن تكون إما wire (مَقُودة بـ assign أو خرج sub-module) أو reg (مَقُودة من always/initial).
module driver(
input wire a,
input wire b,
input wire clk,
output wire y, // wire - مَقُودة بـ assign
output reg q // reg - مَقُودة بـ always
);
assign y = a & b; // ok لأن y هو wire
always @(posedge clk)
q <= a; // ok لأن q هو reg
endmodule
محاولة الإسناد إلى y داخل كتلة always، أو قيادة q بـ assign، ستكون خطأ ترجمة. اختيار الكلمة المفتاحية يجب أن يطابق نوع القائد.
inout - ثنائي الاتجاه
منافذ inout أسلاك يمكن أن يقودها module أو ما يتصل خارجيًا، بالتناوب. تظهر عند حدود الشريحة - خطوط SDA في I²C، وأرجل GPIO ثنائية الاتجاه، وباصات بيانات مشتركة. داخل module، تتحكم بالاتجاه عبر نمط tri-state:
module bidir_pin(
input wire data_out,
input wire output_enable,
output wire data_in,
inout wire pin
);
assign pin = output_enable ? data_out : 1'bz;
assign data_in = pin;
endmodule
pin هو السلك المشترك. عندما يكون output_enable مرتفعًا، يقود module pin إلى data_out. وعندما يكون منخفضًا، يُحرّر pin إلى المعاوقة العالية (z)، فيدع قائدًا خارجيًا يستخدمه. data_in يراقب دائمًا ما هو موجود على pin حاليًا.
inout نادر في المنطق الداخلي البحت. إن كنت تبني متحكم ذاكرة أو نواة CPU أو خط معالجة صور، فقد لا تحتاجها أبدًا. إنها ميزة لحلقة I/O عند حدود الشريحة.
منافذ بت واحدة مقابل متعددة البت
نطاق العرض اختياري - احذفه لمنفذ بت واحدة:
input wire valid, // 1 bit
input wire [7:0] data, // 8 bits
input wire [31:0] addr, // 32 bits
يمكنك استخدام parameters للعرض، وهكذا تعمل modules ذات parameters:
module bus #(
parameter WIDTH = 32
)(
input wire [WIDTH-1:0] in,
output wire [WIDTH-1:0] out
);
assign out = in;
endmodule
أسلوب ANSI مقابل Verilog-1995
سترى أحيانًا شيفرة قديمة تُقسّم قائمة المنافذ والإعلانات:
// أسلوب Verilog-1995 القديم - لا تفعل هذا في شيفرة جديدة
module foo(clk, data_in, data_out);
input clk;
input [7:0] data_in;
output reg [7:0] data_out;
// ...
endmodule
قائمة المنافذ تحوي الأسماء فقط؛ ثم يُعلن عن كل منفذ منفصلًا داخل الجسم. مُسهب، وعرضة لأخطاء "مذكور في قائمة المنافذ لكن غير مُعلن"، ويتطلب ضعف الكتابة. أسلوب ANSI-2001 (المُعتمد في هذه المستندات) يُغني عن الاثنين:
module foo(
input wire clk,
input wire [7:0] data_in,
output reg [7:0] data_out
);
// ...
endmodule
استخدم أسلوب ANSI. الأدوات تدعمه منذ 2001.
مثال كامل
هذا module واحد يحوي:
- parameter (
WIDTH). - مداخل للساعة وreset وتفعيل التحميل والبيانات وتفعيل الإزاحة.
- خرج
reg(data_out) مَقُود داخل كتلةalways. - خرج
wire(msb_out) مَقُود بـ continuous assignment. - إعلان كامل بأسلوب ANSI يُدرج اتجاه ونوع وعرض كل منفذ في القائمة.
هذا تقريبًا شكل كل module قابل للتركيب ستكتبه.
ماذا بعد
المستند التالي - Module Instantiation - يُظهر كيف تأخذ هذا module وتستخدمه داخل تصميم أكبر. الـ shifter الذي كتبته للتو ليس مفيدًا بمفرده؛ يكتسب قيمته عندما يأخذ شيء نسخة منه ويصله بنظام.
الأسئلة الشائعة
ما اتجاهات المنافذ الثلاثة في Verilog؟
input وoutput وinout. input يُقاد من الخارج. output يُقاد من داخل module. inout ثنائي الاتجاه - مفيد لباصات tri-state حيث يقود module ويقرأ نفس الـ pin. الغالبية العظمى من المنافذ الداخلية هي input أو output؛ ولا تظهر inout إلا عند pins الشريحة.
ما الفرق بين output wire وoutput reg؟
output wire يعني أن الخرج مَقُود بـ continuous assignment أو بمنفذ خرج sub-module - أي شيء خارج كتلة always. output reg يعني أنه مَقُود من كتلة إجرائية (initial أو always). الكلمة المفتاحية تتحكم بإمكانية استهداف الإشارة من داخل always، لا بما إذا كان العتاد المُركّب يحوي flip-flop.
ما هو أسلوب ANSI لمنافذ Verilog؟
أسلوب ANSI يُعلن عن اتجاه كل منفذ ونوعه في قائمة المنافذ مباشرةً: module foo(input wire [7:0] data, output reg [7:0] result);. الأسلوب الأقدم Verilog-1995 يُدرج الأسماء فقط في قائمة المنافذ ثم يُعيد إعلانها داخل module. استخدم دائمًا أسلوب ANSI في الشيفرة الجديدة - أقصر وأقل عرضة للأخطاء وقياسي في كل الأدوات الحديثة.
هل يمكن أن يكون لـ Verilog module عدة inputs وoutputs؟
نعم - يمكن أن يكون لـ module أي عدد من المنافذ في أي مزيج من الاتجاهات. افصل بينها بفواصل في قائمة المنافذ. ترتيب قائمة المنافذ يُحدّد ترتيب الوصل الموضعي عند الـ instantiation، لكنك في الغالب تستخدم وصلات مُسماة (.port(signal)) لتفادي الاعتماد على الترتيب.