من الخوارزمية للدواير الإلكترونية
كل اللي اتكلّمنا عنه في الكورس ده — الـ filters والتحويلات والـ correlation والـ multirate signal processing — بيتنفّذ في الآخر على hardware حقيقي. وفي المنتجات الفعلية، الـ hardware ده عادةً embedded processor صغير طاقته محدودة، مش محطة شغل على مكتب. وإنك تفهم خوارزميات الـ DSP بتتحط إزاي على الأجهزة الـ embedded هو الجسر بين النظرية والملايين من الأجهزة اللي بتعالج signals حوالينا كل يوم: السماعات والموبايلات وأجهزة المراقبة الطبية وcontrollers الموتورات وreceivers الأقمار الصناعية.
الـ embedded DSP بييجي بقيود صارمة نادرًا ما مهندسين الـ software بيقابلوها على الكمبيوترات العادية: memory محدودة (غالبًا كيلوبايتات مش جيجابايتات)، وميزانيات طاقة صارمة، وdeadlines في الـ real time مفيهاش فصال. لو فوّت frame sample واحد، النتيجة صوت بايظ أو packet اتصالات ضايعة أو غلطة في التحكّم في الموتور. والتعامل مع القيود دي محتاج تفهم صيغ الحساب ومعماريات الـ processors وتقنيات تحسين الـ code.
الـ embedded DSP موازنة تلاتية بين سرعة الحساب واستهلاك الطاقة والدقة العددية. وكل قرار تصميم بيغيّر الموازنة دي. مثلًا اختيار الـ fixed-point بدل الـ floating-point بينزّل الطاقة لحد 10 أضعاف بس محتاج ظبط دقيق عشان تتجنّب الـ overflow والـ quantization noise.
الحساب بالـ fixed-point قصاد الـ floating-point
الاختيار الأساسي في الـ embedded DSP هو إزاي تمثّل الأرقام. أرقام الـ floating-point (الفاصلة العائمة) (IEEE 754، single precision 32 bit) بتشفّر dynamic range واسع أوتوماتيك — الفاصلة بتتحرّك للمكان اللي محتاجاه. وده بيخلّي تطوير الخوارزميات مباشر: نادرًا ما بتقلق من الـ overflow أو الـ scaling. والتمن هو التكلفة في الـ hardware: الـ floating-point unit (FPU) بتاخد مساحة كبيرة على الـ chip وطاقة كبيرة.
أرقام الـ fixed-point (الفاصلة الثابتة) بتمثّل القيم كأعداد صحيحة متعمّلها scale. مثلًا صيغة Q15 بتخزّن عدد صحيح 16 bit والـ binary point الضمنية واقعة بعد الـ sign bit وقبل 15 bit كسرية، فبتدّي نطاق من −1 لأقل شوية من +1 بدقة 2−15 ≈ 30.5 µV لو الـ full scale 1 فولت. وحساب الـ fixed-point مش محتاج FPU وبيشتغل على hardware أعداد صحيحة بسيط، فبيستهلك طاقة وموارد chip أقل بكتير.
الخطر الأساسي في حساب الـ fixed-point هو الـ overflow: لو عملية ضرب أو جمع طلّعت نتيجة برا النطاق اللي ممكن يتمثّل، القيمة بتعمل wraparound بشكل كارثي. والـ scaling الدقيق — اختيار مكان الـ binary point وقد إيه headroom تسيبه — هو المهارة الأساسية في برمجة الـ DSP بالـ fixed-point. والـ saturation arithmetic (التثبيت عند أكبر قيمة ممكن تتمثّل بدل الـ wraparound) ميزة hardware في أغلب أنوية الـ DSP بتخفّف الـ overflow بشياكة.
معماريات الـ DSP processors
الـ processors العامة (ARM Cortex-A، x86) متحسّنة لأنماط الـ branching والوصول للـ memory المعتادة في أنظمة التشغيل والتطبيقات. أما الـ DSP processors فمتحسّنة للـ loops المتكرّرة الكثيفة البيانات اللي في قلب الـ signal processing: عمليات الـ multiply-accumulate، والـ circular buffering، والـ bit-reversed addressing في الـ FFT.
عيلة TI C6000 (زي C6748 وC66x) هي DSP processors عالية الأداء من نوع very long instruction word (VLIW) فيها execution units كتير بتشتغل بالتوازي. ونواة C6748 واحدة تقدر تعمل 3000 مليون عملية multiply-accumulate في الثانية (3000 MMACS) وهي بتستهلك أقل من 1 واط. وموجودة في كل حتة في معالجة الصوت والتصوير الطبي والـ modems.
الـ microcontrollers ARM Cortex-M (M4 وM7 وM33 وM55) بتجيب قدرة DSP للـ microcontrollers الموفّرة جدًا في الطاقة. الـ Cortex-M4 ضاف FPU بـ single precision وتعليمات SIMD — عمليتين ضرب 16 bit بتعليمة واحدة. والـ Cortex-M55 بيضيف الـ Helium vector extension، فبيسمح بتمن عمليات MAC 16 bit في نفس الوقت. والأجهزة دي بتشتغل بملّي أمبيرات من التيار، وده بيخلّيها مثالية لـ sensors الـ wearables والـ IoT.
كل الـ DSP processors فيها multiply-accumulate unit (MAC) مخصوصة بتحسب y += a × b في clock cycle واحدة. والـ convolution والـ filtering والـ correlation كلهم بيتلخّصوا في عمليات MAC متكرّرة. والـ MAC unit 32 bit مع accumulator 64 bit (منتشر في الـ DSPs الـ fixed-point 16 bit) بيمنع الـ overflow وهو بيجمّع مئات الضربات قبل ما يعمل scaling للنتيجة النهائية ويخزّنها.
قيود الـ memory والحساب
الـ memory في الـ embedded DSP قليلة وغالية (من ناحية الطاقة ومساحة الـ chip) ومهمة معماريًا. والـ DSP processors عادةً بتدّي memories منفصلة للتعليمات والبيانات (معمارية Harvard) عشان الـ processor يقدر يجيب التعليمة الجاية ويحمّل قيمة بيانات في نفس الوقت، فبيتجنّب الـ bottleneck بتاع von Neumann.
جداول معاملات الـ filters والـ circular sample buffers لازم تساعها الـ SRAM اللي على الـ chip أو الـ tightly coupled memory (TCM) عشان تتجنّب غرامة الـ latency اللي بتوصل من 10 لـ 100 ضعف لما توصل للـ DRAM الخارجية. ولـ FIR filter فيه 256 tap بصيغة Q15، جدول المعاملات بياخد 512 بايت — الـ chip بتساعه بسهولة. أما الـ working memory لـ FFT بـ 1024 نقطة بصيغة complex 32 bit فمحتاجة 8 كيلوبايت — لسه على الـ chip في أغلب أجزاء Cortex-M7 بس تستاهل تاخد بالك منها في الميزانية.
الـ circular buffers (ring buffers) هي بنية البيانات المعيارية لتنفيذ الـ delay lines في الـ filters والـ echo cancellers. الـ pointer بيعمل wraparound بالـ modulo على طول الـ buffer، فبيتجنّب تكلفة إنك تزحزح الـ array كله مع كل sample جديدة. وأغلب معماريات الـ DSP processors بتدعم الـ circular addressing في الـ hardware اللي بينفّذ الـ circular buffering من غير أي overhead.
متطلبات المعالجة في الـ real time
نظام DSP في الـ real time لازم يعالج كل sample دخل (أو block من الـ samples) جوه deadline صارم. وللصوت بـ 48 كيلوهرتز، الـ processor معاه 20.83 ميكروثانية بالظبط لكل sample. وفي baseband processor بـ symbol rate 100 ميجاهرتز لشبكة 5G، الـ deadline بيبقى 10 نانوثانية. وإنك تفوّت الـ deadline بيوصّل لـ buffer overrun: الـ sample الجديدة بتكتب فوق sample لسه متعالجتش، فبيطلع click مسموع أو symbol ضايع أو غلطة في التحكّم.
الـ block processing بيوزّع الـ overhead بتاع الـ interrupts بإنه بيجمّع الـ samples في frames (مثلًا 64 أو 256 sample) ويعالج الـ frame كله مرة واحدة. والـ latency (التأخير من الدخل للخرج) بتزيد لمدة frame واحد — وده مقبول لأغلب تطبيقات الصوت والاتصالات — بس كفاءة الـ throughput بتتحسّن بشكل ملحوظ. والـ FFT خوارزمية blocks بطبيعتها وبتركب على النموذج ده بشكل طبيعي.
الـ real time مش معناه إنك تبقى سريع — معناه إنك تبقى سريع بشكل متوقّع. نظام بيعالج أغلب الـ frames في 5 ميكروثانية بس ساعات بياخد 25 ميكروثانية هيفوّت الـ deadline. والـ interrupt latencies والـ cache misses ونقل الـ DMA والـ branch mispredictions كلهم بيساهموا في الـ worst-case execution time (WCET). والـ code الـ embedded DSP بيتكتب عشان يقلّل التذبذبات دي ويحطّلها حد أقصى، مش بس يحسّن المتوسط.
تحسين الـ code للـ DSP
إنك تكتب code DSP بيلحق الـ deadlines بتاعة الـ real time على hardware طاقته محدودة محتاج عملية تحسين منضبطة. وأكتر التقنيات تأثيرًا، بترتيب الأهمية تقريبًا:
استخدم الـ libraries المتحسّنة بتاعة الـ vendor. الـ ARM CMSIS-DSP والـ TI DSPLIB بيدّوا تنفيذات assembly متظبّطة باليد للـ FIR filters والـ FFTs وعمليات الـ matrices وغيرها. والـ FFT اللي من الـ library عادةً أسرع من 5 لـ 10 أضعاف من تنفيذ C مباشر لأنها بتستغل الـ SIMD والـ loop unrolling اليدوي وأنماط وصول للـ memory مناسبة للـ cache. متعيدش كتابة اللي الـ library عملته أحسن.
استغل الـ SIMD والـ intrinsics. أنوية الـ DSP الحديثة تقدر تعالج مسارات بيانات كتير بالتوازي. الـ ARM Cortex-M4 SIMD بيعمل عمليتين MAC بـ Q15 في كل cycle باستخدام التعليمة SMLAD. وإنك تكتب ده في C عن طريق الـ intrinsics (زي __SMLAD()) بيتجنّب الـ assembly وبيطلّع code مثالي. والـ inner loop المكتوبة باليد بالـ SIMD ممكن تضاعف الـ throughput من 2 لـ 4 مرات مقارنة بالـ code العادي.
اتجنّب الـ branching المعتمد على البيانات في الـ loops الحرجة. الـ conditional jumps في الـ inner loops بتعمل pipeline stalls. والـ saturation arithmetic والـ predicated instructions والـ loop unrolling بيشيلوا الـ branching من المسار الحرج. وخيار الـ compiler -funroll-loops بيساعد، بس الـ unrolling اليدوي للـ loops اللي عدد لفّاتها ثابت (زي FIR filter فيه 32 tap) غالبًا أفعل.
إدارة شرسة لتسلسل الـ memory hierarchy. معاملات الـ filter والـ sample buffers لازم تعيش في أسرع memory متاحة (L1 SRAM أو TCM). ونقل الـ DMA من الـ Flash الخارجية الأبطأ أو الـ SDRAM لازم يشتغل في الخلفية، بالـ ping-pong بين bufferين عشان الـ processor يحسب دايمًا على buffer مليان والـ DMA بيملا الـ buffer اللي بعده.
- حساب الـ fixed-point (Q15 وQ31) بيبدّل مرونة الـ dynamic range بتكلفة طاقة وhardware أقل بكتير؛ والـ scaling الدقيق والـ saturation arithmetic بيمنعوا الـ overflow.
- الـ DSP processors المخصوصة (TI C6000) والـ microcontrollers المدعّمة بـ DSP (ARM Cortex-M4/M7/M55) بيدّوا MAC units في cycle واحدة وتعليمات SIMD وcircular buffer addressing ضروريين للأداء في الـ real time.
- معمارية Harvard للـ memory والـ SRAM/TCM اللي على الـ chip بيشيلوا الـ bottleneck بتاع الـ memory؛ والمعاملات والـ working buffers لازم يتحطّوا في أسرع طبقة memory متاحة.
- المعالجة في الـ real time محتاجة worst-case execution time متوقّع، مش بس أداء متوسط سريع؛ والـ block processing والـ DMA double-buffering وإزالة الـ branching هي التقنيات الأساسية.
- الـ DSP libraries المتحسّنة من الـ vendors (CMSIS-DSP وDSPLIB) بتدّي أداء SIMD متظبّط باليد؛ واستخدامها نقطة البداية قبل ما تكتب assembly مخصوص.
- لازم الـ CPU load يفضل أقل بكتير من 100% عشان يسيب هامش للـ interrupts وoverhead نظام التشغيل والـ features الجاية من غير ما يعمل buffer overruns.