BrowseTardīd-i shāyiʻāt-i bāṭilah-ʼi shāh-i makhlūʻ → page 47

Page 47

Tardīd-i shāyiʻāt-i bāṭilah-ʼi shāh-i makhlūʻ · pages 1–125


Page image — the source of record

Scanned page 47 of Tardīd-i shāyiʻāt-i bāṭilah-ʼi shāh-i makhlūʻ

Machine transcription

٤٤ امان اللہ کی حکومت کے چھوٹے چھوٹے جرم جیسے وعدہ خلافیاں فریب کاریاں حتیکہ فراشوں اور خانسامانوں کے ساتھ بھی چالیں چلنا اسقدر زیادہ ہیں کہ انکا شمار حد و حساب سے باہر ہے۔ ہم ان معمولی مسائل سے قطع نظر کرتے ہیں۔ مگر جن بڑے بڑے گناہوں کے ارتکاب سے ملک ویران ہو جاتے ہیں اور مانکی آزادی و استقلال محو ہوجاتا ہے اور ہزاروں بندگان خدا کا خون بہہ جاتا ہے انکی گنتی بھی اتنی ہے کہ ایک لمبی فہرست بن جاتی ہے ۔ مثلاً اسکے تمام کاموں میں سے جو ایک کام سرزد ہوا ہے اور جسکو فی الجملہ کارخیر کہہ سکتے ہیں اور اس کے دوسرے کاموں کی نسبت ظاہراً اس میں شرافت اور اسلامیت کی کچھ کچھ بو آتی ہے وہ یہ ہے کہ اُسنے بیچارے ہندوستانیوں کو ہجرت کی ترغیب دی ۔ گویا ان قسمت کے مارے غریب الوطنوں نے دار الحرب سے دار الاسلام میں ہجرت کی ۔ خواہ اس میں کتنے ہی پالیٹار و دلسوزی کو دخل ہو صرف اس ایک ہی مسئلے میں بھی خامی رہی وہ یہ ہے کہ وطن سے دور ہزاروں مہاجر قطغن کے دشتوں اور میمنہ اور ترکستان کے درّوں میں مر کھپ گئے ۔ خیوہ اور بخارا نے روسیوں کی سیاسی نگرانی کے ماتحت خارجہ ممالک کی نظروں میں ایک دولت کی حیثیت پیدا کر لی تھی اور روسیوں نے انکو برائے نام آزادی بھی دے رکھی تھی ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ The main difference between this function block and functions studied above block resides on how input arrays parameter changes on time index $n$. This corresponds closely mathematically discrete convolution equation that assumes constant weights $W$: $$ y[n] = \sum_{k=0}^{n}w[k]x[n-k] $$ If filter weights were constant over time, all parameters except ```din``` should be invariant over time. Consequently they can be implemented dynamically in Vivado HLS. When inputs are defined as static, synthesis process considers them to be invariant. C simulation handles statics variable correctly, but RTL cosimulation may ignore those variables to match them with synthesis. Let see the output of the cosimulation. As you can notice, there is a mismatch between original algorithm result and RTL behavior. As it was discussed before this is due to how Vivado HLS manage the C `static` variables in simulation process. To solve this issue, HLS provides a pragma to remove this limitation. To make the HLS know that the function input parameters are used to carry the internal states of the function between its calls we can use `#pragma HLS pipeline II=1` ````c void FirFilter(din_t din, coeff_t w[N], dout_t *dout) { static acc_t acc = 0; static din_t shift_reg[N]; #pragma HLS pipeline II=1 acc = 0; for (int i = N - 1; i > 0; i--) { shift_reg[i] = shift_reg[i - 1]; acc += shift_reg[i] * w[i]; } shift_reg[0] = din; acc += shift_reg[0] * w[0]; *dout = acc; } ```` Using `#pragma HLS pipeline II=1` allows concurrent execution of operations, to process a new input every clock cycle and achieve highest performance. Then Vivado understand that `shift_reg` is a shift register and maps it onto RTL `Shift Register` as depicted below. <img src="./fig/sr2.png" width="80%"> This figure represents how HLS maps a C array defined as static to hardware primitives. However, there are some restrictions on that: 1. The shift registers' data has to be stored in registers, not block RAMs. 2. Fully unrolling of shift registers into individual registers can result in a complex routing problem. ### 5. Loop pipelining Pipeline pragma in HLS instructs the tool to attempt to pipeline the loop, which can significantly improve throughput by allowing successive iterations of the loop to overlap their execution, thus reducing the total time taken for the loop to complete. Now, it's time to evaluate how `#pragma HLS PIPELINE` can improve the throughput of FIR filter IP by parallelizing the multiplications and accumulation operations. Note that to do that, you should completely unroll the `for-loop` and the elements of `shift_reg` and `w` parameters should become fully accessible at the same time. The first step can be done by using `#pragma HLS unroll` pragma or putting the loop inside a pipeline. The `PIPELINE` pragma automatically unroll loops inside the corresponding pipeline. Therefore, in this example, it is not needed to unroll the `for-loop` explicitly. Second, we should use `#pragma HLS array_partition` to partition the elements of `shift_reg` and `w` parameters. ````c void FirFilter(din_t din, coeff_t w[N], dout_t *dout) { #pragma HLS pipeline II=1 #pragma HLS array_partition variable=w type=complete static acc_t acc = 0; static din_t shift_reg[N]; #pragma HLS array_partition variable=shift_reg type=complete acc = 0; for (int i = N - 1; i > 0; i--) { shift_reg[i] = shift_reg[i - 1]; acc += shift_reg[i] * w[i]; } shift_reg[0] = din; acc += shift_reg[0] * w[0]; *dout = acc; } ```` Now, synthesise the code and evaluate the required resources in terms of DSP block. As shown in the following figure, now HLS can find that `shift_reg` elements' indices can be evaluated independent of the time instance index. <img src="./fig/sr3.png" width="80%"> > - **Note**: If you want to use the output of the IP core immediately when the result is ready, you must use ```ap_none``` protocol for `dout` parameter using `#pragma HLS INTERFACE mode=ap_none port=dout`. Using the `ap_none` protocol tells the Vitis HLS compiler not to generate any control signals for a given port, meaning the port behaves purely as a data connection without handshaking signals (like `valid`, `ready`, or `acknowledge`). > - **Note**: To prevent the Vitis compiler to wait for the handshaking signals from AXI interface when it enters to the algorithm, we should use ```#pragma HLS INTERFACE ap_ctrl_none port=return```. ### 6. MAC FIR design with single DSP slice By looking at synthesis report of previous design, you may find that our design instantiated 11 DSP slices. You can use `#pragma HLS ALLOCATION` to manage the amount of resource allocated for mathematical operations (like multipliers and adders). You can limit the number of multipliers (or MAC units) that Vitis HLS will use for the FIR filter to reduce hardware area, at the cost of increased latency (time to compute one output). The `ALLOCATION` pragma enforces resource constraints on operations, functions, or cores. ````c void FirFilter(din_t din, coeff_t w[N], dout_t *dout) { #pragma HLS ALLOCATION operation instances=mul limit=1 // FIR filter logic here } ```` By doing that, the synthesis cannot achieve `II=1`, therefore it is better to modify the pragmas as follow. ````c void FirFilter(din_t din, coeff_t w[N], dout_t *dout) { static acc_t acc = 0; static din_t shift_reg[N]; #pragma HLS ALLOCATION operation instances=mul limit=1 acc = 0; for (int i = N - 1; i > 0; i--) { #pragma HLS PIPELINE shift_reg[i] = shift_reg[i - 1]; acc += shift_reg[i] * w[i]; } shift_reg[0] = din; acc += shift_reg[0] * w[0]; *dout = acc; } ```` This will give the compiler the freedom to pipeline the loop, and try to process each inner iteration per clock cycle. You may check the HLS result and compare it with the results obtained from previous sections. ## Second part: Using AXI Stream interface for FIR Instead of reading one value at a time from a specific address using `ap_none` protocol in FIR function arguments, passing an array to the function gives HLS the opportunity to read the input from a FIFO using AXI stream interfaces. Using AXI Stream in a FIR filter design allows for continuous, high-throughput, and pipelined data processing. ````c #include "FirFilter.h" #include <hls_stream.h> void FirFilter(hls::stream<din_t>& din, hls::stream<dout_t>& dout, coeff_t w[N]) { #pragma HLS INTERFACE mode=axis port=din #pragma HLS INTERFACE mode=axis port=dout #pragma HLS INTERFACE mode=s_axilite port=w #pragma HLS INTERFACE mode=s_axilite port=return static din_t shift_reg[N]; #pragma HLS array_partition variable=shift_reg type=complete #pragma HLS array_partition variable=w type=complete // Read new input data from the stream din_t x_in = din.read(); acc_t acc = 0; // Shift register and Multiply-Accumulate logic // Add pipelining to allow processing one input per clock cycle #pragma HLS PIPELINE II=1 // Shift data and compute the FIR output for (int i = N - 1; i > 0; i--) { shift_reg[i] = shift_reg[i - 1]; acc += shift_reg[i] * w[i]; } shift_reg[0] = x_in; acc += shift_reg[0] * w[0]; // Write the output result to the stream dout.write(acc); } ```` 1. Explain how AXI stream is implemented using C codes. 2. What are the limitation of `hls::stream<>` types? Explain. Check the official AMD user guide to learn more about `hls::stream<>` variable type. 3. Replace the FIR codes with the codes presented above. Run C simulation and explain why the simulation gives segmentation fault error. 4. Correct the testbench. In the case you couldn't here is the corrected one. ````c #include "FirFilter.h" #include <iostream> using namespace std; // Testbench main function int main() { // Array of FIR coefficients coeff_t w[N] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; // Input data array to stimulate the filter din_t input_data[15] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0}; // Array to hold the expected output (for checking) dout_t expected_output[15]; hls::stream<din_t> din_stream; hls::stream<dout_t> dout_stream; // Run the FIR filter software model (for expected output calculation) for (int n = 0; n < 15; n++) { acc_t acc = 0; for (int k = 0; k < N; k++) { if (n - k >= 0) { acc += input_data[n - k] * w[k]; } } expected_output[n] = acc; } // Call the hardware FIR filter model int pass = 1; for (int n = 0; n < 15; n++) { dout_t dout_val; din_stream.write(input_data[n]); FirFilter(din_stream, dout_stream, w); dout_val = dout_stream.read(); // Print input, output, and expected value cout << "Input: " << input_data[n] << " \t"; cout << "Output: " << dout_val << " \t"; cout << "Expected: " << expected_output[n] << endl; // Check if output matches expected output if (dout_val != expected_output[n]) { pass = 0; cout << "Mismatch at index " << n << endl; } } // Final result message if (pass) { cout << "SUCCESS: All outputs match expected values." << endl; } else { cout << "FAILED: Outputs do not match expected values." << endl; } return (pass == 1) ? 0 : 1; } ```` 5. Run the synthesis. How resources and synthesis estimated delay compared to the version discussed in Sec. 5? Check if it is able to synthesize an IP with `II=1`. 6. Run RTL cosimulation and compare the expected behavior with outputs from simulation. In this task, we will improve the performance using block processing scheme rather than sample processing scheme. Write a top module program using following code skeleton. Synthesize the code, check the results, check that if II=1 constraint met. ````C void FirFilter(hls::stream<din_t>& din, hls::stream<dout_t>& dout, coeff_t w[N]) { #pragma HLS INTERFACE mode=axis port=din #pragma HLS INTERFACE mode=axis port=dout #pragma HLS INTERFACE mode=s_axilite port=w #pragma HLS INTERFACE mode=s_axilite port=return static din_t shift_reg[N]; #pragma HLS array_partition variable=shift_reg type=complete #pragma HLS array_partition variable=w type=complete //TODO // write a loop that will read all the available data from din // stream process them using the same MAC algorithm presented earlier // write back the result into the output stream. } ```` 1. Correct the testbench to make it able to supply arrays/streams to the top module. Make sure to read all the streams in testbench loop too. <details> <summary> 💡 Solution for C code. Please check it after you've tried. </summary> ```c void FirFilter(hls::stream<din_t>& din, hls::stream<dout_t>& dout, coeff_t w[N]) { #pragma HLS INTERFACE mode=axis port=din #pragma HLS INTERFACE mode=axis port=dout #pragma HLS INTERFACE mode=s_axilite port=w #pragma HLS INTERFACE mode=s_axilite port=return static din_t shift_reg[N]; #pragma HLS array_partition variable=shift_reg type=complete #pragma HLS array_partition variable=w type=complete acc_t acc; while (!din.empty()) { #pragma HLS PIPELINE II=1 acc = 0; din_t x_in = din.read(); // Shift data and compute the FIR output for (int i = N - 1; i > 0; i--) { shift_reg[i] = shift_reg[i - 1]; acc += shift_reg[i] * w[i]; } shift_reg[0] = x_in; acc += shift_reg[0] * w[0]; // Write the output result to the stream dout.write(acc); } } ``` </details> <details> <summary> 💡 Solution for Testbench. Please check it after you've tried. </summary> ```c #include "FirFilter.h" #include <iostream> using namespace std; // Testbench main function int main() { // Array of FIR coefficients coeff_t w[N] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; // Input data array to stimulate the filter din_t input_data[15] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0}; // Array to hold the expected output (for checking) dout_t expected_output[15]; hls::stream<din_t> din_stream; hls::stream<dout_t> dout_stream; // Run the FIR filter software model (for expected output calculation) for (int n = 0; n < 15; n++) { acc_t acc = 0; for (int k = 0; k < N; k++) { if (n - k >= 0) { acc += input_data[n - k] * w[k]; } } expected_output[n] = acc; } // Call the hardware FIR filter model int pass = 1; for (int n = 0; n < 15; n++) { din_stream.write(input_data[n]); } FirFilter(din_stream, dout_stream, w); for (int n = 0; n < 15; n++) { dout_t dout_val = dout_stream.read(); // Print input, output, and expected value cout << "Input: " << input_data[n] << " \t"; cout << "Output: " << dout_val << " \t"; cout << "Expected: " << expected_output[n] << endl; // Check if output matches expected output if (dout_val != expected_output[n]) { pass = 0; cout << "Mismatch at index " << n << endl; } } // Final result message if (pass) { cout << "SUCCESS: All outputs match expected values." << endl; } else { cout << "FAILED: Outputs do not match expected values." << endl; } return (pass == 1) ? 0 : 1; } ``` </details> Now by synthesizing the codes above, we can notice that HLS could met II=1. You can verify it from RTL cosimulation waveform viewer (Fig. 8). <img src="./fig/waveform.png" width="80%"> **Fig. 8: Waveform in Cosimulation viewer.** ## Third Part: Implementing and Testing Using PYNQ Now, the HLS IP from the previous part will be packaged and evaluated physically on PyNQ-Z2. 1. Export the HLS IP. 2. Open Vivado tool, create a new IP. Insert `Zynq processing unit`. Add your packaged HLS IP to the IP block design, add one DMA ip `AXI Direct Memory Access`. Since, HLS IP uses streams, configure DMA for `Read/Write` channels. Disable `Scatter Gather Engine`. <img src="./fig/DMA.png" width="80%"> **Fig. 9: DMA Configuration.** 3. Connect the IP correctly like Figure 10. `TREADY` and `TVALID` from `M_AXIS_MM2S` on DMA connects to HLS IP `din`, and `S_AXIS_S2MM` connects to `dout`. <img src="./fig/IPBD.png" width="80%"> **Fig. 10: Vivado IP Block Design.** 4. You can use `<component.xml>` file inside the exported zip file to find out at which addresses register files in HLS IP corresponds to (or from synthesis report generated by vitis HLS IDE). As an example `<spirit:addressOffset>0x10</spirit:addressOffset>` determines that base address `0x10` gives access to first 4 bytes of weights. 5. After generating bitsream `hw_FIR.bit` and `hw_FIR.hwh`, move them alongside the jupyter notebook code attached to this document `test_FIR.ipynb`. Now you can use following code as a basis for your notebook python project for evaluation. ```python import numpy as np import pynq from pynq import Overlay, allocate # Load the overlay overlay = Overlay('./hw_FIR.bit') # Ensure the name matches your block design # Instantiate DMA and the HLS IP blocks dma = overlay.axi_dma_0 # Replace with actual name if different fir_ip = overlay.FirFilter_0 # Replace with actual name of HLS IP block # Configure filter coefficients (assuming 11 coefficients as per your C++ code) # Example weights used in the C++ testbench weights = [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1] # Write weights to the IP block's memory # Assumes AXI-Lite registers for `w` are mapped sequentially # The base address for `w` should be checked in Vitis HLS Synthesis Report weight_base_address = 0x10 # Replace with actual offset for `w` from HLS for i, weight in enumerate(weights): fir_ip.write(weight_base_address + i * 4, weight) # Write each 32-bit (4-byte) integer # The amount of samples you are willing to send through FIR filter data_size = 15 # Allocate memory buffers for DMA transfer # Using `allocate` to get physically contiguous memory for the PL in_buffer = allocate(shape=(data_size,), dtype=np.int32) out_buffer = allocate(shape=(data_size,), dtype=np.int32) # Initialize input buffer with test data input_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0] for i in range(data_size): in_buffer[i] = input_data[i] # Start the FIR filter by writing to the control register # Usually, bit 0 of offset 0x00 is the AP_START bit fir_ip.write(0x00, 0x01) # Set AP_START # Perform DMA Transfers # 1. Send data from PS to PL (MM2S: Memory-Mapped to Stream) dma.sendchannel.transfer(in_buffer) # 2. Receive data from PL to PS (S2MM: Stream to Memory-Mapped) dma.recvchannel.transfer(out_buffer) # Wait for transfers to complete dma.sendchannel.wait() dma.recvchannel.wait() # Display results print("Input Data: ", in_buffer) print("Output Data: ", out_buffer) # Verification against expected results from C++ testbench expected_output = [1, 4, 10, 20, 35, 56, 80, 104, 125, 140, 146, 140, 125, 104, 80] success = True for i in range(data_size): if out_buffer[i] != expected_output[i]: success = False print(f"Mismatch at index {i}: Expected {expected_output[i]}, Got {out_buffer[i]}") if success: print("SUCCESS: All hardware outputs match expected values.") else: print("FAILED: Output mismatch detected.") # Free buffers to avoid memory leaks in_buffer.close() out_buffer.close() ``` 6. Report the result obtained on Pynq Z2 Evaluation board to tutor and include in your report. ### Appendix: Below are instructions on how to use array partition, unroll, and pipeline pragmas along with related C code snippets. ### Array Partition Pragma The `#pragma HLS ARRAY_PARTITION` pragma enables the breaking of arrays into smaller arrays or individual elements. This increases memory bandwidth and enables parallel access to array elements. #### Example: Array Partition ```c #define N 16 void array_partition_example(int in[N], int out[N]) { // Partition the input array into 4 smaller arrays #pragma HLS ARRAY_PARTITION variable=in block factor=4 // Partition the output array entirely into individual registers #pragma HLS ARRAY_PARTITION variable=out complete for (int i = 0; i < N; i++) { // Multiply by 2 out[i] = in[i] * 2; } } ``` ### Unroll Pragma The `#pragma HLS UNROLL` pragma expands a loop, effectively replacing it with multiple copies of its loop body. This increases parallelism by allowing multiple iterations to be executed concurrently. #### Example: Loop Unroll ```c #define M 8 void unroll_example(int in[M], int out[M]) { for (int i = 0; i < M; i++) { // Fully unroll the loop to execute all iterations in parallel #pragma HLS UNROLL out[i] = in[i] + 5; } } ``` ### Pipeline Pragma The `#pragma HLS PIPELINE` pragma reduces the initiation interval for a function or loop by allowing concurrent execution of operations. Pipelining increases throughput. #### Example: Loop Pipelining ```c #define K 10 void pipeline_example(int in1[K], int in2[K], int out[K]) { for (int i = 0; i < K; i++) { // Pipeline the loop with an initiation interval of 1 #pragma HLS PIPELINE II=1 out[i] = in1[i] + in2[i]; } } ``` ### Combined Example You can combine these pragmas to optimize hardware generation further. Here's an example of a dot product using partition, unroll, and pipeline. #### Example: Dot Product with Combined Pragmas ```c #define LEN 32 int dot_product(int A[LEN], int B[LEN]) { // Partition arrays completely into individual registers for maximum bandwidth #pragma HLS ARRAY_PARTITION variable=A complete #pragma HLS ARRAY_PARTITION variable=B complete int acc = 0; for (int i = 0; i < LEN; i++) { // Pipeline the loop to initiate a new multiply-accumulate every clock cycle #pragma HLS PIPELINE II=1 // Optionally unroll if further parallelism is needed (redundant if pipelining perfectly) // #pragma HLS UNROLL acc += A[i] * B[i]; } return acc; } ``` # L4: Shift Registers and DSP blocks within High Level Synthesis The following sections will guide you through this lab: * [First Part](#first-part-shifting-the-data) * [Second Part](#second-part-using-axi-stream-interface-for-fir) * [Third Part](#third-part-implementing-and-testing-using-pynq) ## Overview This lab exercise explores the design and implementation of Finite Impulse Response (FIR) filters using AMD Vitis HLS. You will learn to write the algorithm in C/C++, apply various hardware optimization pragmas—such as loop unrolling, pipelining, and array partitioning—and analyze their effects on resource utilization and throughput. Additionally, you will integrate the generated IP into a larger Vivado block design, utilizing AXI Streams for efficient data transfer, and test the resulting hardware on a PYNQ-Z2 board using Python. Through this hands-on process, you will gain practical experience in bridging high-level software models with optimized hardware implementations for real-time digital signal processing. ## First part: Shifting the data ### 1. FIR C/C++ Design When using Vivado HLS, the very first step in implementing a digital filter is writing C/C++ code. Before adding any optimization pragmas, we will observe how Vitis HLS synthesizes plain C/C++ code. **Mathematical background** A Finite Impulse Response (FIR) filter calculates its output as a weighted sum of the current and past input samples. The mathematical operation performed is a discrete convolution, defined by the following difference equation: $$ y[n] = \sum_{k=0}^{N-1}w[k]x[n-k] $$ Where: - $y[n]$ is the output signal at discrete time $n$. - $x[n-k]$ are the current and past input samples (delayed by $k$ samples). - $w[k]$ are the filter coefficients (weights or taps). - $N$ is the filter order (number of coefficients). **Task** Write a simple module and testbench code for an $N$-Tap FIR (For this lab $N=11$). The coefficients $w[k]$ are assumed to be constants over time. Synthesize the code using Vitis HLS. Examine the resulting RTL block diagram in HLS and the required resources for the implementation, specifically tracking the BRAM elements and Flip-Flops. Run the Simulation with Vitis HLS simulation IDE interface and compare the values against Expected values. In your report, please provide screenshots of the Vivado synthesis report and code implementations. Below is a base template you can use to structure your implementation. ```c #ifndef FIR_H #define FIR_H #define N 11 // Number of taps typedef int din_t; typedef int coeff_t; typedef int dout_t; typedef int acc_t; void FirFilter(din_t din, coeff_t w[N], dout_t *dout); #endif // FIR_H ``` ```c #include "FirFilter.h" // Define function with exact types as presented in header void FirFilter(din_t din, coeff_t w[N], dout_t *dout) { //TODO: Create shift register //TODO: Implement the MAC algorithm here } ``` ```c #include "FirFilter.h" #include <iostream> using namespace std; // Testbench main function int main() { // Array of FIR coefficients coeff_t w[N] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; // Input data array to stimulate the filter din_t input_data[15] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0}; // Array to hold the expected output (for checking) dout_t expected_output[15]; dout_t dout_val; // Run the FIR filter software model (for expected output calculation) for (int n = 0; n < 15; n++) { acc_t acc = 0; for (int k = 0; k < N; k++) { if (n - k >= 0) { acc += input_data[n - k] * w[k]; } } expected_output[n] = acc; } // Call the hardware FIR filter model int pass = 1; for (int n = 0; n < 15; n++) { FirFilter(input_data[n], w, &dout_val); // Print input, output, and expected value cout << "Input: " << input_data[n] << " \t"; cout << "Output: " << dout_val << " \t"; cout << "Expected: " << expected_output[n] << endl; // Check if output matches expected output if (dout_val != expected_output[n]) { pass = 0; cout << "Mismatch at index " << n << endl; } } // Final result message if (pass) { cout << "SUCCESS: All outputs match expected values." << endl; } else { cout << "FAILED: Outputs do not match expected values." << endl; } return (pass == 1) ? 0 : 1; } ``` <details> <summary> 💡 Solution. Please check it after you've tried. </summary> ```c void FirFilter(din_t din, coeff_t w[N], dout_t *dout) { acc_t acc = 0; din_t shift_reg[N]; acc = 0; for (int i = N - 1; i > 0; i--) { shift_reg[i] = shift_reg[i - 1]; acc += shift_reg[i] * w[i]; } shift_reg[0] = din; acc += shift_reg[0] * w[0]; *dout = acc; } ``` </details> If you check the schedule viewer you can identify the structure of algorithm implemented through synthesis process. ![alt text](./fig/sch1.png) ### 2. State memory variables (static keyword) As you can notice the result from `Simulation` will not give the expected answer. You can use debug environment within `vitis hls` by putting breakpoint at `FirFilter` top function, checking how the values are updated in `shift_reg`. You may understand that since `shift_reg` in defined inside function without passing as argument and it is not static, so during each execution process, the internal values of `shift_reg` will be cleaned up! 1. Write the code again using `static` keyword for `shift_reg` internal array variable. 2. Check again the result obtained from `simulation`. Can you tell us why expected answer and output answer matched after editing the code? 3. Run the Synthesis. Are you expecting any changes on BRAM or Flip-flops requirements? 4. Run Cosimulation. Make sure the option `wave debug` is `true`. By default `cosimulation` viewer pops up. Try to identify state transition and `ap_return` signal. You will notice that the `cosimulation` fails after synthesis! But we already passed `c-simulation` phase. How the difference exists over these simulations? <details> <summary> 💡 Solution. Please check it after you've tried. </summary> ```c void FirFilter(din_t din, coeff_t w[N], dout_t *dout) { static acc_t acc = 0; static din_t shift_reg[N]; acc = 0; for (int i = N - 1; i > 0; i--) { shift_reg[i] = shift_reg[i - 1]; acc += shift_reg[i] * w[i]; } shift_reg[0] = din; acc += shift_reg[0] * w[0]; *dout = acc; } ``` </details> Lab2/Makefile *.o .Xil # Compiler CXX = g++ # Compiler flags CXXFLAGS = -O3 -Wall -Wextra -std=c++11 -I. -I/tools/Xilinx/Vivado/2023.2/include # Executable name EXEC = tb_matrixmul # Source files SRCS = matrixmul.cpp matrixmul_test.cpp # Object files OBJS = $(SRCS:.cpp=.o) # Default target all: $(EXEC) # Link the executable $(EXEC): $(OBJS) $(CXX) $(CXXFLAGS) -o $@ $^ # Compile source files into object files %.o: %.cpp $(CXX) $(CXXFLAGS) -c $< -o $@ # Run the program run: $(EXEC) ./$(EXEC) # Clean up build files clean: rm -rf $(EXEC)Project/Project.md # Machine Learning Edge Deployment Case Study This document details case study aimed to guide you from high-level PyTorch design of neural networks to hardware/software deployment on edge devices like the Pynq-Z2. The process is fully integrated with Xilinx FINN and TVM, utilizing an ONNX intermediate representation for model acceleration on FPGAs. ## Overview The case study focuses on: - Designing a quantized neural network tailored for spatial data (MNIST dataset). - Exporting the trained model to ONNX format. - Compiling the model using Brevitas and FINN for FPGA deployment. - Generating the bitstream and deploying it on the Pynq-Z2 board. The model is based on Multi-Layer Perceptron (MLP) components, known as Spatial MLPs, capable of handling variable data shapes suitable for edge deployment. --- ## 1. Introduction With the rising demand for deploying deep learning models on edge devices with constrained resources, developing highly optimized, quantized neural networks has become critical. This project demonstrates how to build and deploy a lightweight **Spatial MLP** on the **Pynq-Z2 FPGA** board using **Brevitas** for Quantization-Aware Training (QAT) and **FINN/TVM** for hardware synthesis and deployment. ### 1.1 Objectives The primary objectives of this project are: 1. **Model Development**: Design a quantized neural network using Brevitas, utilizing Spatial MLPs that avoid traditional Convolutional Neural Networks (CNNs). 2. **Model Training**: Train the model on the MNIST dataset to achieve optimal accuracy. 3. **Export & Compilation**: Export the trained model to ONNX and use the FINN compiler to generate a customized hardware accelerator. 4. **Hardware Deployment**: Deploy the generated bitstream to the Pynq-Z2 FPGA and run inference using Python. 5. **Performance Evaluation**: Assess the model’s accuracy, hardware resource utilization, and inference latency. --- ## 2. Tools and Frameworks To complete this case study, we will utilize the following tools and frameworks: - **PyTorch**: For defining and training the neural network. - **Brevitas**: A PyTorch library for Quantization-Aware Training (QAT), essential for converting models into low-precision formats suitable for hardware. - **FINN**: An experimental framework from Xilinx Research that creates custom hardware architectures for quantized neural networks (QNNs). - **TVM**: An open deep learning compiler stack for CPUs, GPUs, and specialized accelerators (FPGAs). - **Vivado HLS**: High-Level Synthesis tool used by FINN to generate RTL from C/C++. - **Pynq-Z2**: The target hardware platform for the final deployment. --- ## 3. Project Workflow The project is structured into several phases: 1. **Network Architecture Design**: Designing the Spatial MLP. 2. **Quantization-Aware Training**: Training the model using Brevitas. 3. **Model Export**: Converting the model to ONNX format. 4. **Compilation with FINN**: Transforming the ONNX model into hardware via FINN and TVM. 5. **Deployment on Pynq-Z2**: Running the compiled model on the FPGA. --- ## 4. Phase 1: Network Architecture Design ### 4.1 Spatial MLP Concept Traditional MLPs operate on 1D vectors, requiring flattening of 2D images. A **Spatial MLP** operates directly on 2D images, similar to CNNs but using localized multi-layer perceptron blocks. This approach captures local spatial features while reducing the computational complexity associated with standard convolutions. ### 4.2 Model Specifications - **Input Size**: $28 \times 28$ grayscale images (MNIST dataset). - **Quantization**: 4-bit weights and activations. - **Architecture**: - Input quantization layer. - Spatial MLP blocks containing linear layers tailored for local receptive fields. - Non-linear activations (e.g., ReLU or QuantReLU). - Fully connected layer for classification into 10 classes. ### 4.3 Implementing the Spatial MLP Here is the implementation of the `SpatialMLP` class using Brevitas: ```python import torch import torch.nn as nn import brevitas.nn as qnn from brevitas.core.quant import QuantType class SpatialMLPBlock(nn.Module): def __init__(self, in_channels, out_channels, bit_width=4): super(SpatialMLPBlock, self).__init__() self.fc1 = qnn.QuantLinear(in_channels, out_channels, weight_bit_width=bit_width, bias=False) self.act1 = qnn.QuantReLU(bit_width=bit_width) self.fc2 = qnn.QuantLinear(out_channels, out_channels, weight_bit_width=bit_width, bias=False) self.act2 = qnn.QuantReLU(bit_width=bit_width) def forward(self, x): x = self.act1(self.fc1(x)) x = self.act2(self.fc2(x)) return x class SpatialMLPNet(nn.Module): def __init__(self, bit_width=4): super(SpatialMLPNet, self).__init__() # Flatten the 28x28 image to a 784-dimensional vector self.flatten = nn.Flatten() # Input quantization self.quant_inp = qnn.QuantIdentity(bit_width=8, return_quant_tensor=True) # Define MLP blocks self.block1 = SpatialMLPBlock(784, 128, bit_width) self.block2 = SpatialMLPBlock(128, 64, bit_width) # Classification layer self.classifier = qnn.QuantLinear(64, 10, weight_bit_width=bit_width, bias=True) def forward(self, x): x = self.flatten(x) x = self.quant_inp(x) x = self.block1(x) x = self.block2(x) x = self.classifier(x) return x # Instantiate the model model = SpatialMLPNet(bit_width=4) ``` > **Note**: This model is a simple MLP where the spatial structure is flattened. For a true *Spatial* MLP, the linear layers would be applied across spatial patches, analogous to a pointwise convolution or patching mechanism in Vision Transformers. --- ## 5. Phase 2: Quantization-Aware Training (QAT) Quantization-Aware Training simulates lower precision during the training process, allowing the model to adapt and minimize accuracy loss due to quantization. ### 5.1 Training Setup We will use the standard PyTorch training loop on the MNIST dataset. ```python import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader # Device configuration device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model.to(device) # Dataset and DataLoader transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform) test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform) train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False) # Loss and optimizer criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) ``` ### 5.2 Training Loop ```python def train(model, device, train_loader, optimizer, criterion, epochs=5): model.train() for epoch in range(epochs): for batch_idx, (data, target) in enumerate(train_loader): data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() optimizer.step() if batch_idx % 100 == 0: print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} ' f'({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.6f}') train(model, device, train_loader, optimizer, criterion, epochs=5) ``` ### 5.3 Evaluation ```python def test(model, device, test_loader, criterion): model.eval() test_loss = 0 correct = 0 with torch.no_grad(): for data, target in test_loader: data, target = data.to(device), target.to(device) output = model(data) test_loss += criterion(

Roughly two thirds of characters agree with a modern printed edition of the same text. Much of the disagreement is editorial, not misreading. Treat the transcription as a finding aid and read the page image before quoting. How this was measured.


Cite this page
Tardīd-i shāyiʻāt-i bāṭilah-ʼi shāh-i makhlūʻ, page 47. Afghanistan Digital Library, New York University Libraries. Machine-transcribed text: Afghan Press Archive, https://afghanpress.org/book/adl0111/47 (accessed [date]).
Permalink
https://afghanpress.org/book/adl0111/47
Machine-readable
JSON · IIIF manifest · Canvas
Source
Afghanistan Digital Library record