سؤال
أنا جديد جدًا على لغة HDL. لدي سؤال حول كيفية برمجة سجل التحول. (أعلم أنني تحول إلى الاتجاه الآخر). لماذا يستخدم الكتاب wire[N-1:0] r_next
؟ ما هو عيب تنفيذي؟ شكرًا
محاولتي الأولى على النحو التالي
module lesson04#(parameter N=8)(
input wire clk, reset,
input wire data,
output wire out
);
reg [N-1: 0] r_reg;
always @(posedge clk or negedge reset)
begin
if(!reset)
r_reg =0;
else
r_reg[0]=data;
r_reg = r_reg<<1;
end
assign out =r_reg[N-1];
endmodule
لكن الكتاب يعطي:
module lesson04#(parameter N=8)(
input wire clk, reset,
input wire data,
output wire out
);
reg [N-1: 0] r_reg;
wire[N-1:0] r_next;
always @(posedge clk or negedge reset)
begin
if(!reset)
r_reg =0;
else
r_reg <= r_next;
end
assign r_next= {data, r_reg[N-1:1]};
assign out =r_reg[N-1];
endmodule
المحلول
بادئ ذي بدء ، لا تنسى begin
-end
S حول أقسام الكود:
else begin
r_reg[0]=data;
r_reg = r_reg<<1;
end
بدون هذا فقط r_reg[0]=data
سيكون في else
بند if
بيان. سيعمل هذا ، ولكن يعتبر أسلوبًا سيئًا بسبب عبارات الحظر في وصف منطقي متسلسل ...
ثانياً ، لنمذجة الكتل المتسلسلة ، استخدم المهام غير المحظورة (<=
) أو قد "تمر" حساباتك (Google Non Locklocking مقابل حظر مزيد من المعلومات). قد ينجح مثالك جيدًا (هل جربته في جهاز محاكاة؟) ولكن إذا أصبحت الأمور أكثر تعقيدًا وإضافة المزيد من المتغيرات التي يمكن أن تنكسرها الأشياء.
always @(posedge clk or negedge reset)
begin
if(!reset)
r_reg <= 0;
else begin // This is horrible! Don't write code like this!
r_reg[0] = data; // blocking
r_reg <= r_reg<<1; // non-blocking
end
end
للسبب أعلاه ، يوصى أحيانًا بالفصل بين منطق التحرير والسرد عن المنطق المتسلسل بحيث يمكنك كتابة مهام غير محظورة إلى السجلات في كتل متسلسلة ، وحظر كتل التحرير والسرد ولا تقلق أبدًا بشأن الجدولة.
للرمز بهذه الطريقة ، تحتاج إلى حساب ما يجب أن يستخدمه الإخراج التالي في الحالة الحالية ، وبالتالي r_next
حافلة في الجواب. أعتقد أنه يميل إلى مساعدة أداة التوليف أيضًا إذا تم فصل جميع التقلبات عن منطق التحرير والسرد المحيط بهذه الطريقة.
أيضًا ، إذا كانت إعادة تعيينك منخفضة (أي LOW
إعادة ضبط) يجب تسميتها على هذا النحو ، على سبيل المثال resetb
أو reset_n
.
نصائح أخرى
ينتج تنفيذك ناتجًا مختلفًا تمامًا عن الكتاب. يجب أن تثبت ذلك لنفسك عن طريق بناء مجموعة اختبار بسيطة لدفع مدخلاتك وتشغيل محاكاة. سترى أن إخراج الكتاب يغير بيانات الإدخال بواسطة دورة ساعة واحدة ، في حين أن الإخراج يغير بيانات الإدخال عن طريق ثمانية دورات الساعة.
بالمناسبة لديك بادئة always
كتلة ، لقد قادت إلى الاعتقاد بأنه ليس ما تريد. هذه هي الطريقة التي تتصرف بها كتلةك حقًا:
always @(posedge clk or negedge reset)
begin
if(!reset) begin
r_reg =0;
end else begin
r_reg[0]=data;
end
r_reg = r_reg<<1;
end
أنا دائما أستخدم صراحة begin/end
الكلمات الرئيسية في if/else
بيانات لتجنب هذا الالتباس.
الطريقة التي تحاكي بها r_reg
دائمًا ما يكون 0 لأنك تعبر عن المهمة الأولى (r_reg[0]=data;
) مع الثاني (r_reg = r_reg<<1;
). فرق آخر هو أن الكتاب يعين data
إلى MSB لسجل Shift ، لكنك تقوم بتعيينه إلى LSB.
إذا كنت تستخدم أدوات Licent Linting and Synthesis ، فمن المحتمل أن تحصل على مجموعة من التحذيرات لكودك. هذا من شأنه أن ينبهك لإجراء بعض التغييرات.