مرحبا بالجميع! ستركز هذه المقالة على جزء واحد مهم من معالجة الإشارات الرقمية - تصفية إشارة النافذة ، ولا سيما على FPGAs. ستوضح المقالة كيفية تصميم النوافذ الكلاسيكية ذات الطول القياسي والنوافذ "الطويلة" من نماذج 64K إلى 16M +. لغة التطوير الرئيسية هي VHDL ، وقاعدة العنصر هي أحدث بلورات Xilinx FPGA من أحدث العائلات: هذه هي Ultrascale ، Ultrascale + ، 7-series. ستوضح المقالة تنفيذ CORDIC - النواة الأساسية لتكوين وظائف النافذة لأي مدة ، بالإضافة إلى وظائف النافذة الأساسية. تصف المقالة طريقة التصميم باستخدام اللغات عالية المستوى C / C ++ في Vivado HLS. كالعادة ، ستجد في نهاية المقال رابطًا لرموز المصدر للمشروع.
KDPV: مخطط نموذجي لإرسال الإشارات عبر عقد DSP لمهام تحليل الطيف.

مقدمة
من دورة "معالجة الإشارات الرقمية" ، يعرف الكثير من الناس أنه بالنسبة للموجة الجيبية اللانهائية في الوقت المناسب ، فإن طيفها هو وظيفة دلتا عند تردد الإشارة. من الناحية العملية ، فإن طيف الإشارة التوافقية المحدودة بالزمن الحقيقي يعادل الوظيفة
~ sin (x) / x ، ويعتمد عرض الفص الرئيسي على مدة الفاصل الزمني لتحليل الإشارة
T. الحد الزمني ليس أكثر من ضرب الإشارة في مظروف مستطيل. من المعروف من دورة DSP أن تكاثر الإشارات في المجال الزمني هو التفاف أطيافها في مجال التردد (والعكس بالعكس) ، وبالتالي فإن طيف الغلاف المستطيل المحدود للإشارة التوافقية يعادل ~ ~ (x). ويرجع ذلك أيضًا إلى حقيقة أنه لا يمكننا دمج الإشارة عبر فاصل زمني لانهائي ، وتحويل فورييه في شكل منفصل ، المعبر عنه من خلال مجموع محدود ، محدود بعدد العينات. كقاعدة ، يأخذ طول FFT في أجهزة المعالجة الرقمية FPGA الحديثة قيم
NFFT من 8 إلى عدة ملايين من النقاط. وبعبارة أخرى ، يتم حساب طيف إشارة الدخل على الفاصل
T ، الذي يساوي في كثير من الحالات
NFFT . من خلال قصر الإشارة على الفاصل الزمني
T ، فإننا نفرض "نافذة" مستطيلة بمدة عينات
T. لذلك ، فإن الطيف الناتج هو طيف الإشارة التوافقية المضاعفة والغلاف المستطيل. في مهام DSP ، تم اختراع النوافذ ذات الأشكال المختلفة لفترة طويلة ، والتي ، عند فرضها على إشارة في المجال الزمني ، يمكن أن تحسن خصائصها الطيفية. يرجع عدد كبير من النوافذ المختلفة في المقام الأول إلى واحدة من الميزات الرئيسية لأي تراكب نافذة. يتم التعبير عن هذه الميزة في العلاقة بين مستوى الفصوص الجانبية وعرض الفص المركزي. نمط معروف: كلما كان قمع الفصوص الجانبية أقوى ، كلما كان الفص الرئيسي أوسع والعكس صحيح.
أحد تطبيقات وظائف النوافذ: كشف الإشارات الضعيفة على خلفية الإشارات الأقوى عن طريق منع مستوى الفصوص الجانبية. وظائف النافذة الرئيسية في مهام DSP هي ثلاثية ، جيبية ، Lanczos ، Hann ، Hamming ، Blackman ، Harris ، Blackman-Harris window ، نافذة مسطحة أعلى ، Natall ، Gauss ، Kaiser window وغيرها الكثير. يتم التعبير عن معظمها من خلال سلسلة محدودة من خلال جمع الإشارات التوافقية بأوزان محددة. يتم حساب النوافذ ذات الشكل المعقد من خلال أخذ الأس (النافذة Gaussian) أو وظيفة Bessel المعدلة (نافذة Kaiser) ، ولن يتم النظر فيها في هذه المقالة. يمكنك قراءة المزيد عن وظائف النافذة في الأدبيات ، والتي سأقدمها عادةً في نهاية المقالة.
يوضح الشكل التالي وظائف النافذة النموذجية وخصائصها الطيفية التي تم إنشاؤها باستخدام أدوات Matlab CAD.

التنفيذ
في بداية المقال ، أدرجت KDPV ، الذي يعرض بشكل عام مخططًا هيكليًا لمضاعفة بيانات الإدخال بواسطة وظيفة النافذة. من الواضح أن أسهل طريقة لتنفيذ تخزين وظيفة النافذة في FPGA هي كتابتها في الذاكرة (حظر
RAMB أو الموزع
الموزع - لا يهم كثيرًا) ، ثم استرداد البيانات دوريًا عند وصول عينات الإدخال للإشارة. كقاعدة ، في FPGAs الحديثة ، يسمح مقدار الذاكرة الداخلية بتخزين وظائف النافذة ذات الأحجام الصغيرة نسبيًا ، والتي يتم ضربها بعد ذلك في إشارات الإدخال الواردة. أعني بالوظائف الصغيرة حتى 64K عينات طويلة.
ولكن ماذا لو كانت وظيفة النافذة طويلة جدًا؟ على سبيل المثال ، قراءات 1M. من السهل حساب أنه بالنسبة لوظيفة النافذة هذه المقدمة في شبكة 32 بت ، NRAMB = 1024 * 1024 * 32/32768 = 1024 خلايا ذاكرة كتلة من نوع RAMB36K FPGA Xilinx مطلوبة. وعينات 16M؟ 16 ألف خلية ذاكرة! لا يوجد FPGA حديث واحد لديه الكثير من الذاكرة. بالنسبة للعديد من FPGA ، يعد هذا كثيرًا ، وفي حالات أخرى هو استخدام مسرف لموارد FPGA (وبالطبع ، أموال العميل).
في هذا الصدد ، تحتاج إلى التوصل إلى طريقة لتوليد عينات وظيفة النافذة مباشرة إلى FPGA على الطاير ، دون كتابة المعاملات من الجهاز البعيد إلى ذاكرة الكتلة. لحسن الحظ ، تم اختراع الأشياء الأساسية منذ فترة طويلة بالنسبة لنا. باستخدام خوارزمية مثل
CORDIC (طريقة
رقم برقم ) ، من الممكن تصميم العديد من وظائف النافذة التي يتم التعبير عن صيغها من حيث الإشارات التوافقية (Blackman-Harris ، Hann ، Hamming ، Nattal ، إلخ.)
كوردي
CORDIC هي طريقة تكرارية بسيطة ومريحة لحساب دوران نظام الإحداثيات ، والتي تسمح لك بحساب الوظائف المعقدة من خلال إجراء عمليات الإضافة البدائية وعمليات النقل. باستخدام خوارزمية CORDIC ، يمكن للمرء حساب قيم الإشارات التوافقية sin (x) ، cos (x) ، والعثور على المرحلة - atan (x) و atan2 (x ، y) ، والدوال المثلثية الزائدية ، وتدوير المتجه ، واستخراج جذر الرقم ، إلخ.
في البداية كنت أرغب في أخذ نواة CORDIC النهائية وتقليل كمية العمل ، لكن لدي كره طويل لحبات Xilinx. بعد دراسة المستودعات على جيثوب ، أدركت أن جميع النوى المقدمة ليست مناسبة لعدد من الأسباب (موثقة بشكل سيئ وغير قابلة للقراءة ، وليست عالمية ، مصنوعة لمهمة معينة أو قاعدة عنصر ،
مكتوبة في فيريلوج ، إلخ). ثم طلبت من الرفيق
lazifo القيام بهذا العمل من أجلي. بالطبع ، تعامل معها ، لأن تنفيذ CORDIC هو واحد من أبسط المهام في مجال DSP. ولكن بما أنني غير صبور ، وبالتوازي مع عمله ، كتبت
دراجتي مع نواة
خاصة بي. وتتمثل الميزات الرئيسية في عمق البت
القابل للتكوين لإشارات خرج
DATA_WIDTH ومرحلة الإدخال
المقيسة PHASE_WIDTH من -1 إلى 1 ، ودقة حسابات الدقة. يتم تنفيذ CORDIC الأساسية وفقًا للدائرة المتوازية لخط الأنابيب - في كل دورة ساعة يكون القلب جاهزًا لإجراء العمليات الحسابية واستلام عينات الإدخال. تنفق النواة دورات N لحساب عينة الإخراج ، التي يعتمد عددها على سعة عينات الإخراج (كلما زادت السعة ، زادت التكرار لحساب قيمة الإخراج). تحدث جميع الحسابات بالتوازي. وبالتالي ، فإن CORDIC هو النواة الأساسية لإنشاء وظائف النافذة.
وظائف النافذة
في إطار هذه المقالة ، أدرك فقط وظائف النافذة التي يتم التعبير عنها من خلال الإشارات التوافقية (Hann ، Hamming ، Blackman-Harris من أوامر مختلفة ، إلخ). ما هو المطلوب لهذا؟ بشكل عام ، تبدو صيغة إنشاء نافذة وكأنها سلسلة من الطول المحدود.

تحدد مجموعة معينة من المعاملات
k وأعضاء السلسلة اسم النافذة. الأكثر شيوعًا والأكثر استخدامًا هو نافذة Blackman-Harris: بترتيب مختلف (من 3 إلى 11). فيما يلي جدول معاملات لنوافذ Blackman-Harris:

من حيث المبدأ ، فإن مجموعة نافذة Blackman-Harris قابلة للتطبيق في العديد من مشاكل التحليل الطيفي ، وليس هناك حاجة لمحاولة استخدام النوافذ المعقدة مثل Gauss أو Kaiser. النوافذ الوطنية أو المسطحة هي مجرد نوع من النوافذ ذات أوزان مختلفة ، ولكن نفس المبادئ الأساسية مثل Blackman-Harris. من المعروف أنه كلما زاد عدد أعضاء السلسلة ، كان قمع مستوى الفصوص الجانبية أقوى (رهنا باختيار معقول لعمق البت لوظيفة النافذة). بناءً على المهمة ، يجب على المطور فقط اختيار نوع النوافذ المستخدمة.
تنفيذ FPGA - النهج التقليدي
تم تصميم جميع نواة وظائف النافذة باستخدام النهج الكلاسيكي لوصف الدوائر الرقمية في FPGAs وهي مكتوبة بلغة VHDL. فيما يلي قائمة بالمكونات المصنوعة:
- bh_win_7term - طلب Blackman-Harris 7 ، نافذة بأقصى حد من قمع السقالات الجانبية.
- bh_win_5term - طلب Blackman-Harris 5 ، يشتمل على نافذة ذات سطح مستو.
- bh_win_4term - أوامر Blackman-Harris 4 ، تتضمن نافذة Nattal و Blackman-Nattal.
- bh_win_3term - بلاكمان هاريس 3 أوامر ،
- hamming_win - نوافذ هامينج وهان.
رمز المصدر لمكون نافذة Blackman-Harris هو 3 مرات من الحجم:
entity bh_win_3term is generic ( TD : time:=0.5ns;
في بعض الحالات ، استخدمت مكتبة
UNISIM لتضمين
عقدتي DSP48E1 و DSP48E2 في المشروع ، مما يسمح
لي في نهاية المطاف بزيادة سرعة العمليات الحسابية بسبب الأنابيب داخل هذه الكتل ، ولكن كما أظهرت الممارسة ، من الأسرع والأسهل إطلاق العنان لكتابة شيء مثل
P = أ * ب + ج وتحديد التوجيهات التالية في الكود:
attribute USE_DSP of <signal_name>: signal is "YES";
يعمل هذا بشكل جيد ويحدد بشكل صارم نوع العنصر الذي يتم تطبيق الوظيفة الرياضية عليه للمركب.
Vivado hls
بالإضافة إلى ذلك ، قمت بتطبيق جميع النوى باستخدام أدوات
Vivado HLS . سأذكر
المزايا الرئيسية
لـ Vivado HLS: السرعة العالية للتصميم (
الوقت إلى السوق ) باللغات عالية المستوى C أو C ++ ، والنمذجة السريعة للعقد المتطورة بسبب عدم وجود مفهوم لتردد الساعة ، والتكوين المرن للحلول (من حيث الموارد والأداء) من خلال تقديم البراغمات والتوجيهات في المشروع ، وكذلك عتبة دخول منخفضة للمطورين بلغات عالية المستوى. العيب الرئيسي هو التكلفة دون المستوى الأمثل لموارد FPGA بالمقارنة مع النهج الكلاسيكي. أيضًا ، لا يمكن تحقيق تلك السرعات التي توفرها طرق RTL الكلاسيكية القديمة (VHDL ، Verilog ، SV). حسنًا ، أكبر
عيب هو الرقص مع الدف ، ولكن هذا هو سمة جميع CAD من Xilinx. (ملاحظة: في مصحح أخطاء Vivado HLS وفي نموذج C ++ الفعلي ، غالبًا ما تم الحصول على نتائج مختلفة ، لأن Vivado HLS يعمل بشكل غريب باستخدام مزايا
الدقة التعسفية ).
تظهر الصورة التالية سجل نواة CORDIC المركبة في Vivado HLS. إنه مفيد للغاية ويعرض الكثير من المعلومات المفيدة: كمية الموارد المستخدمة ، واجهة مستخدم kernel ، الحلقات وخصائصها ، التأخير في الحوسبة ، الفاصل الزمني لحساب قيمة الإخراج (مهم عند تصميم الدوائر التسلسلية والمتوازية):

يمكنك أيضًا الاطلاع على طريقة حساب البيانات في المكونات (الوظائف) المختلفة. يمكن ملاحظة أنه في المرحلة صفر ، تتم قراءة بيانات المرحلة ، وفي الخطوتين 7 و 8 ، يتم عرض نتيجة العقدة CORDIC.

نتيجة Vivado HLS: نواة RTL مركبة تم إنشاؤها من كود C. يُظهر السجل أنه في تحليل الوقت ، تمر النواة بنجاح بجميع القيود:

ميزة أخرى كبيرة في Vivado HLS هي أنها تتحقق من كود RTL الذي تم توليفه على أساس النموذج المستخدم للتحقق من كود C للتحقق من النتيجة. قد يكون هذا اختبارًا بدائيًا ، لكني أعتقد أنه رائع للغاية ومريح بما يكفي لمقارنة تشغيل الخوارزمية في C وعلى HDL. فيما يلي لقطة شاشة من Vivado تُظهر محاكاة لنموذج وظيفة kernel لوظيفة النافذة التي تم الحصول عليها بواسطة Vivado HLS:

وبالتالي ، بالنسبة لجميع وظائف النافذة ، تم الحصول على نتائج مماثلة ، بغض النظر عن طريقة التصميم - في VHDL أو في C ++. ومع ذلك ، في الحالة الأولى ، يتم تحقيق تواتر أكبر للعمل وعدد أقل من الموارد ، وفي الحالة الثانية ، يتم تحقيق أقصى سرعة تصميم. كلا النهجين لهما الحق في الحياة.
لقد حسبت على وجه التحديد مقدار الوقت الذي سأقضيه في التطوير باستخدام طرق مختلفة. قمت بتنفيذ مشروع C ++ في Vivado HLS ~ 12 مرة أسرع من VHDL.
مقارنة المناهج
قارن كود المصدر لـ HDL و C ++ للنواة CORDIC. الخوارزمية ، كما قيل سابقًا ، مبنية على عمليات الجمع والطرح والتحول. على VHDL ، يبدو هذا: هناك ثلاثة ناقلات بيانات - أحدهما مسؤول عن دوران الزاوية ، والآخران يحددان طول المتجه على طول محوري X و Y ، وهو ما يعادل خطيئة و cos (انظر الصورة من ويكي):

من خلال حساب القيمة Z بشكل متكرر ، يتم حساب قيم X و Y بالتوازي. عملية البحث الدوري عن قيم الإخراج على HDL:
constant ROM_LUT : rom_array := ( x"400000000000", x"25C80A3B3BE6", x"13F670B6BDC7", x"0A2223A83BBB", x"05161A861CB1", x"028BAFC2B209", x"0145EC3CB850", x"00A2F8AA23A9", x"00517CA68DA2", x"0028BE5D7661", x"00145F300123", x"000A2F982950", x"000517CC19C0", x"00028BE60D83", x"000145F306D6", x"0000A2F9836D", x"0000517CC1B7", x"000028BE60DC", x"0000145F306E", x"00000A2F9837", x"00000517CC1B", x"0000028BE60E", x"00000145F307", x"000000A2F983", x"000000517CC2", x"00000028BE61", x"000000145F30", x"0000000A2F98", x"0000000517CC", x"000000028BE6", x"0000000145F3", x"00000000A2FA", x"00000000517D", x"0000000028BE", x"00000000145F", x"000000000A30", x"000000000518", x"00000000028C", x"000000000146", x"0000000000A3", x"000000000051", x"000000000029", x"000000000014", x"00000000000A", x"000000000005", x"000000000003", x"000000000001", x"000000000000" ); pr_crd: process(clk, reset) begin if (reset = '1') then
في C ++ ، في Vivado HLS ، يبدو الرمز هو نفسه تقريبًا ، ولكن السجل أقصر عدة مرات:
على ما يبدو ، يتم استخدام نفس الدورة مع التحول والإضافات. ومع ذلك ، بشكل افتراضي ، يتم "طي" جميع الحلقات في Vivado HLS وتنفيذها بالتتابع ، كما هو موضح للغة C ++. إدخال
HLS UNICOLL أو
HLS PIPELINE pragma يحول المسلسل إلى حسابات متوازية. يؤدي هذا إلى زيادة في موارد FPGA ، ومع ذلك ، فإنه يسمح لك بحساب قيم جديدة وإرسالها إلى القلب في كل دورة ساعة.
يتم عرض نتائج توليف المشروع في VHDL و C ++ في الشكل أدناه. كما ترى ، من الناحية المنطقية ، الفرق هو مرتين لصالح النهج التقليدي. بالنسبة لموارد FPGA الأخرى ، فإن التناقض ضئيل. لم أتعمق في تحسين المشروع في C ++ ، ولكن بشكل لا لبس فيه من خلال تحديد توجيهات مختلفة أو تغيير الشفرة جزئيًا ، يمكن تقليل عدد الموارد المستخدمة. في كلتا الحالتين ، تقاربت التوقيتات لتردد أساسي معين ~ 350 ميغا هرتز.

ميزات التنفيذ
نظرًا لأنه يتم إجراء العمليات الحسابية بتنسيق نقطة ثابتة ، فإن وظائف النافذة لها عدد من الميزات التي يجب أخذها في الاعتبار عند تصميم أنظمة DSP على FPGA. على سبيل المثال ، كلما زاد عمق البت لبيانات وظيفة النافذة ، زادت دقة تراكب النافذة. من ناحية أخرى ، مع عمق البتات غير الكافي لوظيفة النافذة ، سيتم إدخال التشوهات في الشكل الموجي الناتج ، مما سيؤثر على جودة الخصائص الطيفية. على سبيل المثال ، يجب أن تحتوي دالة النافذة على 20 بت على الأقل عند ضربها بإشارة مدتها 2 ^ 20 = 1M عينات.
الخلاصة
توضح هذه المقالة طريقة واحدة لتصميم وظائف النافذة دون استخدام ذاكرة خارجية أو ذاكرة كتلة FPGA. يتم إعطاء طريقة استخدام الموارد المنطقية الحصرية لـ FPGAs (وفي بعض الحالات كتل DSP). باستخدام خوارزمية CORDIC ، من الممكن الحصول على وظائف النافذة من أي عمق بت (ضمن المعقول) ، من أي طول وترتيب ، وبالتالي الحصول على مجموعة من أي خصائص طيفية للنافذة.
كجزء من إحدى الدراسات ، تمكنت من الحصول على نواة تعمل بشكل مستقر لوظيفة نافذة Blackman-Harris من 5 و 7 أوامر من الحجم على عينات 1M بتردد ~ 375 MHz ، وأيضًا لعمل مولد للمعاملات الدوارة لـ FFT استنادًا إلى CORDIC بتردد ~ 400 MHz. تستخدم FPGA Crystal: Kintex Ultrascale + (xcku11p-ffva1156-2-e).
رابط
لمشروع جيثب هنا . يحتوي المشروع على نموذج رياضي في Matlab ، ورموز مصدر لوظائف النافذة و CORDIC في VHDL ، بالإضافة إلى نماذج لوظائف النافذة المدرجة في C ++ لـ Vivado HLS.
مقالات مفيدة
أنصح أيضًا كتابًا شائعًا جدًا حول DSP -
Ayficher E. ، Jervis B. معالجة الإشارات الرقمية. نهج عمليشكرا لكم على اهتمامكم!