{"book":"adl0111","page":47,"text":"٤٤\n\nامان اللہ کی حکومت کے چھوٹے چھوٹے جرم جیسے وعدہ خلافیاں فریب کاریاں\nحتیکہ فراشوں اور خانسامانوں کے ساتھ بھی چالیں چلنا اسقدر زیادہ ہیں کہ انکا شمار\nحد و حساب سے باہر ہے۔ ہم ان معمولی مسائل سے قطع نظر کرتے ہیں۔\nمگر جن بڑے بڑے گناہوں کے ارتکاب سے ملک ویران ہو جاتے ہیں\nاور مانکی آزادی و استقلال محو ہوجاتا ہے اور ہزاروں بندگان خدا کا خون\nبہہ جاتا ہے انکی گنتی بھی اتنی ہے کہ ایک لمبی فہرست بن جاتی ہے ۔\nمثلاً اسکے تمام کاموں میں سے جو ایک کام سرزد ہوا ہے اور جسکو فی الجملہ\nکارخیر کہہ سکتے ہیں اور اس کے دوسرے کاموں کی نسبت ظاہراً اس میں شرافت\nاور اسلامیت کی کچھ کچھ بو آتی ہے وہ یہ ہے کہ اُسنے بیچارے ہندوستانیوں کو ہجرت کی\nترغیب دی ۔ گویا ان قسمت کے مارے غریب الوطنوں نے دار الحرب سے دار الاسلام\nمیں ہجرت کی ۔ خواہ اس میں کتنے ہی پالیٹار و دلسوزی کو دخل ہو صرف اس ایک ہی\nمسئلے میں بھی خامی رہی وہ یہ ہے کہ وطن سے دور ہزاروں مہاجر قطغن کے دشتوں\nاور میمنہ اور ترکستان کے درّوں میں مر کھپ گئے ۔\nخیوہ اور بخارا نے روسیوں کی سیاسی نگرانی کے ماتحت خارجہ ممالک کی\nنظروں میں ایک دولت کی حیثیت پیدا کر لی تھی اور روسیوں نے انکو برائے نام\nآزادی بھی دے رکھی تھی ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔ ۔\n\nThe 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$:\n$$ y[n] = \\sum_{k=0}^{n}w[k]x[n-k] $$\nIf 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. \n\nAs 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`\n\n\n\n````c\nvoid FirFilter(din_t din, coeff_t w[N], dout_t *dout) {\n\tstatic acc_t acc = 0;\n\tstatic din_t shift_reg[N];\n\t#pragma HLS pipeline II=1\n\n\tacc = 0;\n\tfor (int i = N - 1; i > 0; i--) {\n\t\tshift_reg[i] = shift_reg[i - 1];\n\t\tacc += shift_reg[i] * w[i];\n\t}\n\n\tshift_reg[0] = din;\n\tacc += shift_reg[0] * w[0];\n\n\t*dout = acc;\n}\n````\n\nUsing `#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.\n\n<img src=\"./fig/sr2.png\" width=\"80%\">\n\nThis figure represents how HLS maps a C array defined as static to hardware primitives. However, there are some restrictions on that:\n1. The shift registers' data has to be stored in registers, not block RAMs. \n2. Fully unrolling of shift registers into individual registers can result in a complex routing problem.\n\n\n### 5. Loop pipelining\n\nPipeline 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. \nNow, 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.\n\n````c\nvoid FirFilter(din_t din, coeff_t w[N], dout_t *dout) {\n\n#pragma HLS pipeline II=1\n#pragma HLS array_partition variable=w type=complete\n\tstatic acc_t acc = 0;\n\tstatic din_t shift_reg[N];\n#pragma HLS array_partition variable=shift_reg type=complete\n\n\tacc = 0;\n\tfor (int i = N - 1; i > 0; i--) {\n\t\tshift_reg[i] = shift_reg[i - 1];\n\t\tacc += shift_reg[i] * w[i];\n\t}\n\n\tshift_reg[0] = din;\n\tacc += shift_reg[0] * w[0];\n\n\t*dout = acc;\n}\n\n````\n\n\nNow, synthesise the code and evaluate the required resources in terms of DSP block. \nAs shown in the following figure, now HLS can find that `shift_reg` elements' indices can be evaluated independent of the time instance index.\n\n<img src=\"./fig/sr3.png\" width=\"80%\">\n\n> - **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`).\n> - **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```.\n\n### 6. MAC FIR design with single DSP slice\n\nBy 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. \n\n````c\nvoid FirFilter(din_t din, coeff_t w[N], dout_t *dout) {\n    #pragma HLS ALLOCATION operation instances=mul limit=1\n    \n    // FIR filter logic here\n}\n````\n\nBy doing that, the synthesis cannot achieve `II=1`, therefore it is better to modify the pragmas as follow. \n\n````c\nvoid FirFilter(din_t din, coeff_t w[N], dout_t *dout) {\n\tstatic acc_t acc = 0;\n\tstatic din_t shift_reg[N];\n\n    #pragma HLS ALLOCATION operation instances=mul limit=1\n\n\tacc = 0;\n\tfor (int i = N - 1; i > 0; i--) {\n        #pragma HLS PIPELINE\n\t\tshift_reg[i] = shift_reg[i - 1];\n\t\tacc += shift_reg[i] * w[i];\n\t}\n\n\tshift_reg[0] = din;\n\tacc += shift_reg[0] * w[0];\n\n\t*dout = acc;\n}\n\n````\n\nThis 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.\n\n## Second part: Using AXI Stream interface for FIR\nInstead 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.\n\n````c\n#include \"FirFilter.h\"\n#include <hls_stream.h>\n\nvoid FirFilter(hls::stream<din_t>& din, hls::stream<dout_t>& dout, coeff_t w[N]) {\n#pragma HLS INTERFACE mode=axis port=din\n#pragma HLS INTERFACE mode=axis port=dout\n#pragma HLS INTERFACE mode=s_axilite port=w\n#pragma HLS INTERFACE mode=s_axilite port=return\n\n    static din_t shift_reg[N];\n#pragma HLS array_partition variable=shift_reg type=complete\n#pragma HLS array_partition variable=w type=complete\n\n    // Read new input data from the stream\n    din_t x_in = din.read();\n\n    acc_t acc = 0;\n\n    // Shift register and Multiply-Accumulate logic\n    // Add pipelining to allow processing one input per clock cycle\n#pragma HLS PIPELINE II=1\n\n    // Shift data and compute the FIR output\n    for (int i = N - 1; i > 0; i--) {\n        shift_reg[i] = shift_reg[i - 1];\n        acc += shift_reg[i] * w[i];\n    }\n\n    shift_reg[0] = x_in;\n    acc += shift_reg[0] * w[0];\n\n    // Write the output result to the stream\n    dout.write(acc);\n}\n````\n1. Explain how AXI stream is implemented using C codes.\n2. What are the limitation of `hls::stream<>` types? Explain. Check the official AMD user guide to learn more about `hls::stream<>` variable type.\n3. Replace the FIR codes with the codes presented above. Run C simulation and explain why the simulation gives segmentation fault error.\n4. Correct the testbench. In the case you couldn't here is the corrected one.\n````c\n#include \"FirFilter.h\"\n#include <iostream>\n\nusing namespace std;\n\n\n// Testbench main function\nint main() {\n    // Array of FIR coefficients\n    coeff_t w[N] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};\n\n    // Input data array to stimulate the filter\n    din_t input_data[15] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0};\n\n    // Array to hold the expected output (for checking)\n    dout_t expected_output[15];\n\n    hls::stream<din_t> din_stream;\n    hls::stream<dout_t> dout_stream;\n\n    // Run the FIR filter software model (for expected output calculation)\n    for (int n = 0; n < 15; n++) {\n        acc_t acc = 0;\n        for (int k = 0; k < N; k++) {\n            if (n - k >= 0) {\n                acc += input_data[n - k] * w[k];\n            }\n        }\n        expected_output[n] = acc;\n    }\n\n    // Call the hardware FIR filter model\n    int pass = 1;\n    for (int n = 0; n < 15; n++) {\n    \tdout_t dout_val;\n\n    \tdin_stream.write(input_data[n]);\n\n    \tFirFilter(din_stream, dout_stream, w);\n\n    \tdout_val = dout_stream.read();\n\n        // Print input, output, and expected value\n        cout << \"Input: \" << input_data[n] << \" \\t\";\n        cout << \"Output: \" << dout_val << \" \\t\";\n        cout << \"Expected: \" << expected_output[n] << endl;\n\n        // Check if output matches expected output\n        if (dout_val != expected_output[n]) {\n            pass = 0;\n            cout << \"Mismatch at index \" << n << endl;\n        }\n    }\n\n    // Final result message\n    if (pass) {\n        cout << \"SUCCESS: All outputs match expected values.\" << endl;\n    } else {\n        cout << \"FAILED: Outputs do not match expected values.\" << endl;\n    }\n\n    return (pass == 1) ? 0 : 1;\n}\n````\n5. 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`.\n6. Run RTL cosimulation and compare the expected behavior with outputs from simulation.\n\n\n\nIn this task, we will improve the performance using block processing scheme rather than sample processing scheme.\nWrite a top module program using following code skeleton. Synthesize the code, check the results, check that if II=1 constraint met.  \n\n````C\nvoid FirFilter(hls::stream<din_t>& din, hls::stream<dout_t>& dout, coeff_t w[N]) {\n#pragma HLS INTERFACE mode=axis port=din\n#pragma HLS INTERFACE mode=axis port=dout\n#pragma HLS INTERFACE mode=s_axilite port=w\n#pragma HLS INTERFACE mode=s_axilite port=return\n\n    static din_t shift_reg[N];\n#pragma HLS array_partition variable=shift_reg type=complete\n#pragma HLS array_partition variable=w type=complete\n\n\t//TODO\n\t// write a loop that will read all the available data from din \n\t// stream process them using the same MAC algorithm  presented earlier\n\t// write back the result into the output stream. \n\t\n}\n````\n1. 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. \n\n<details>\n<summary> 💡 Solution for C code. Please check it after you've tried. </summary>\n\n```c\nvoid FirFilter(hls::stream<din_t>& din, hls::stream<dout_t>& dout, coeff_t w[N]) {\n#pragma HLS INTERFACE mode=axis port=din\n#pragma HLS INTERFACE mode=axis port=dout\n#pragma HLS INTERFACE mode=s_axilite port=w\n#pragma HLS INTERFACE mode=s_axilite port=return\n\n    static din_t shift_reg[N];\n#pragma HLS array_partition variable=shift_reg type=complete\n#pragma HLS array_partition variable=w type=complete\n    \n    acc_t acc;\n    while (!din.empty()) {\n#pragma HLS PIPELINE II=1\n    \tacc = 0;\n        din_t x_in = din.read();\n\n        // Shift data and compute the FIR output\n        for (int i = N - 1; i > 0; i--) {\n            shift_reg[i] = shift_reg[i - 1];\n            acc += shift_reg[i] * w[i];\n        }\n\n        shift_reg[0] = x_in;\n        acc += shift_reg[0] * w[0];\n\n        // Write the output result to the stream\n        dout.write(acc);\n    }\n}\n```\n</details>\n\n<details>\n<summary> 💡 Solution for Testbench. Please check it after you've tried. </summary>\n\n```c\n\n#include \"FirFilter.h\"\n#include <iostream>\n\nusing namespace std;\n\n\n// Testbench main function\nint main() {\n    // Array of FIR coefficients\n    coeff_t w[N] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};\n\n    // Input data array to stimulate the filter\n    din_t input_data[15] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0};\n\n    // Array to hold the expected output (for checking)\n    dout_t expected_output[15];\n\n    hls::stream<din_t> din_stream;\n    hls::stream<dout_t> dout_stream;\n\n    // Run the FIR filter software model (for expected output calculation)\n    for (int n = 0; n < 15; n++) {\n        acc_t acc = 0;\n        for (int k = 0; k < N; k++) {\n            if (n - k >= 0) {\n                acc += input_data[n - k] * w[k];\n            }\n        }\n        expected_output[n] = acc;\n    }\n\n    // Call the hardware FIR filter model\n    int pass = 1;\n\n    for (int n = 0; n < 15; n++) {\n        din_stream.write(input_data[n]);\n    }\n    \n    FirFilter(din_stream, dout_stream, w);\n\n    for (int n = 0; n < 15; n++) {\n        dout_t dout_val = dout_stream.read();\n\n        // Print input, output, and expected value\n        cout << \"Input: \" << input_data[n] << \" \\t\";\n        cout << \"Output: \" << dout_val << \" \\t\";\n        cout << \"Expected: \" << expected_output[n] << endl;\n\n        // Check if output matches expected output\n        if (dout_val != expected_output[n]) {\n            pass = 0;\n            cout << \"Mismatch at index \" << n << endl;\n        }\n    }\n\n    // Final result message\n    if (pass) {\n        cout << \"SUCCESS: All outputs match expected values.\" << endl;\n    } else {\n        cout << \"FAILED: Outputs do not match expected values.\" << endl;\n    }\n\n    return (pass == 1) ? 0 : 1;\n}\n\n```\n</details>\n\nNow 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).\n\n<img src=\"./fig/waveform.png\" width=\"80%\">\n\n**Fig. 8: Waveform in Cosimulation viewer.**\n\n## Third Part: Implementing and Testing Using PYNQ\n\nNow, the HLS IP from the previous part will be packaged and evaluated physically on PyNQ-Z2. \n\n1. Export the HLS IP.\n\n2. 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`.\n\n<img src=\"./fig/DMA.png\" width=\"80%\">\n\n**Fig. 9: DMA Configuration.**\n\n\n3. 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`. \n\n<img src=\"./fig/IPBD.png\" width=\"80%\">\n\n**Fig. 10: Vivado IP Block Design.**\n\n4. 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.\n\n5. 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.\n\n\n```python\nimport numpy as np\nimport pynq\nfrom pynq import Overlay, allocate\n\n# Load the overlay\noverlay = Overlay('./hw_FIR.bit')  # Ensure the name matches your block design\n\n# Instantiate DMA and the HLS IP blocks\ndma = overlay.axi_dma_0  # Replace with actual name if different\nfir_ip = overlay.FirFilter_0  # Replace with actual name of HLS IP block\n\n# Configure filter coefficients (assuming 11 coefficients as per your C++ code)\n# Example weights used in the C++ testbench\nweights = [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]\n\n# Write weights to the IP block's memory\n# Assumes AXI-Lite registers for `w` are mapped sequentially\n# The base address for `w` should be checked in Vitis HLS Synthesis Report\nweight_base_address = 0x10  # Replace with actual offset for `w` from HLS\nfor i, weight in enumerate(weights):\n    fir_ip.write(weight_base_address + i * 4, weight)  # Write each 32-bit (4-byte) integer\n\n# The amount of samples you are willing to send through FIR filter\ndata_size = 15\n\n# Allocate memory buffers for DMA transfer\n# Using `allocate` to get physically contiguous memory for the PL\nin_buffer = allocate(shape=(data_size,), dtype=np.int32)\nout_buffer = allocate(shape=(data_size,), dtype=np.int32)\n\n# Initialize input buffer with test data\ninput_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0]\nfor i in range(data_size):\n    in_buffer[i] = input_data[i]\n\n# Start the FIR filter by writing to the control register\n# Usually, bit 0 of offset 0x00 is the AP_START bit\nfir_ip.write(0x00, 0x01)  # Set AP_START\n\n# Perform DMA Transfers\n# 1. Send data from PS to PL (MM2S: Memory-Mapped to Stream)\ndma.sendchannel.transfer(in_buffer)\n\n# 2. Receive data from PL to PS (S2MM: Stream to Memory-Mapped)\ndma.recvchannel.transfer(out_buffer)\n\n# Wait for transfers to complete\ndma.sendchannel.wait()\ndma.recvchannel.wait()\n\n# Display results\nprint(\"Input Data:     \", in_buffer)\nprint(\"Output Data:    \", out_buffer)\n\n# Verification against expected results from C++ testbench\nexpected_output = [1, 4, 10, 20, 35, 56, 80, 104, 125, 140, 146, 140, 125, 104, 80]\nsuccess = True\nfor i in range(data_size):\n    if out_buffer[i] != expected_output[i]:\n        success = False\n        print(f\"Mismatch at index {i}: Expected {expected_output[i]}, Got {out_buffer[i]}\")\n\nif success:\n    print(\"SUCCESS: All hardware outputs match expected values.\")\nelse:\n    print(\"FAILED: Output mismatch detected.\")\n\n# Free buffers to avoid memory leaks\nin_buffer.close()\nout_buffer.close()\n\n```\n6. Report the result obtained on Pynq Z2 Evaluation board to tutor and include in your report.\n\n\n### Appendix:\nBelow are instructions on how to use array partition, unroll, and pipeline pragmas along with related C code snippets.\n\n### Array Partition Pragma\n\nThe `#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.\n\n#### Example: Array Partition\n\n```c\n#define N 16\n\nvoid array_partition_example(int in[N], int out[N]) {\n    // Partition the input array into 4 smaller arrays\n    #pragma HLS ARRAY_PARTITION variable=in block factor=4\n    // Partition the output array entirely into individual registers\n    #pragma HLS ARRAY_PARTITION variable=out complete\n\n    for (int i = 0; i < N; i++) {\n        // Multiply by 2\n        out[i] = in[i] * 2;\n    }\n}\n```\n\n### Unroll Pragma\n\nThe `#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.\n\n#### Example: Loop Unroll\n\n```c\n#define M 8\n\nvoid unroll_example(int in[M], int out[M]) {\n    for (int i = 0; i < M; i++) {\n        // Fully unroll the loop to execute all iterations in parallel\n        #pragma HLS UNROLL\n        out[i] = in[i] + 5;\n    }\n}\n```\n\n### Pipeline Pragma\n\nThe `#pragma HLS PIPELINE` pragma reduces the initiation interval for a function or loop by allowing concurrent execution of operations. Pipelining increases throughput.\n\n#### Example: Loop Pipelining\n\n```c\n#define K 10\n\nvoid pipeline_example(int in1[K], int in2[K], int out[K]) {\n    for (int i = 0; i < K; i++) {\n        // Pipeline the loop with an initiation interval of 1\n        #pragma HLS PIPELINE II=1\n        out[i] = in1[i] + in2[i];\n    }\n}\n```\n\n### Combined Example\n\nYou can combine these pragmas to optimize hardware generation further. Here's an example of a dot product using partition, unroll, and pipeline.\n\n#### Example: Dot Product with Combined Pragmas\n\n```c\n#define LEN 32\n\nint dot_product(int A[LEN], int B[LEN]) {\n    // Partition arrays completely into individual registers for maximum bandwidth\n    #pragma HLS ARRAY_PARTITION variable=A complete\n    #pragma HLS ARRAY_PARTITION variable=B complete\n\n    int acc = 0;\n    \n    for (int i = 0; i < LEN; i++) {\n        // Pipeline the loop to initiate a new multiply-accumulate every clock cycle\n        #pragma HLS PIPELINE II=1\n        // Optionally unroll if further parallelism is needed (redundant if pipelining perfectly)\n        // #pragma HLS UNROLL\n        acc += A[i] * B[i];\n    }\n    \n    return acc;\n}\n```\n# L4: Shift Registers and DSP blocks within High Level Synthesis \n\n\nThe following sections will guide you through this lab:\n* [First Part](#first-part-shifting-the-data)\n* [Second Part](#second-part-using-axi-stream-interface-for-fir)\n* [Third Part](#third-part-implementing-and-testing-using-pynq)\n\n## Overview \nThis 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.\n\n\n## First part: Shifting the data\n\n\n### 1. FIR C/C++ Design\nWhen 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. \n\n**Mathematical background**\nA 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:\n\n$$ y[n] = \\sum_{k=0}^{N-1}w[k]x[n-k] $$\n\nWhere:\n- $y[n]$ is the output signal at discrete time $n$.\n- $x[n-k]$ are the current and past input samples (delayed by $k$ samples).\n- $w[k]$ are the filter coefficients (weights or taps).\n- $N$ is the filter order (number of coefficients).\n\n**Task**\nWrite 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.\n\n```c\n#ifndef FIR_H\n#define FIR_H\n\n#define N 11 // Number of taps\n\ntypedef int din_t;\ntypedef int coeff_t;\ntypedef int dout_t;\ntypedef int acc_t;\n\nvoid FirFilter(din_t din, coeff_t w[N], dout_t *dout);\n\n#endif // FIR_H\n```\n```c\n#include \"FirFilter.h\"\n\n// Define function with exact types as presented in header\nvoid FirFilter(din_t din, coeff_t w[N], dout_t *dout) {\n\t//TODO: Create shift register\n    //TODO: Implement the MAC algorithm here\n}\n```\n\n```c\n#include \"FirFilter.h\"\n#include <iostream>\n\nusing namespace std;\n\n// Testbench main function\nint main() {\n    // Array of FIR coefficients\n    coeff_t w[N] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};\n\n    // Input data array to stimulate the filter\n    din_t input_data[15] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0};\n\n    // Array to hold the expected output (for checking)\n    dout_t expected_output[15];\n\tdout_t dout_val;\n\n    // Run the FIR filter software model (for expected output calculation)\n    for (int n = 0; n < 15; n++) {\n        acc_t acc = 0;\n        for (int k = 0; k < N; k++) {\n            if (n - k >= 0) {\n                acc += input_data[n - k] * w[k];\n            }\n        }\n        expected_output[n] = acc;\n    }\n\n    // Call the hardware FIR filter model\n    int pass = 1;\n    for (int n = 0; n < 15; n++) {\n    \tFirFilter(input_data[n], w, &dout_val);\n\n        // Print input, output, and expected value\n        cout << \"Input: \" << input_data[n] << \" \\t\";\n        cout << \"Output: \" << dout_val << \" \\t\";\n        cout << \"Expected: \" << expected_output[n] << endl;\n\n        // Check if output matches expected output\n        if (dout_val != expected_output[n]) {\n            pass = 0;\n            cout << \"Mismatch at index \" << n << endl;\n        }\n    }\n\n    // Final result message\n    if (pass) {\n        cout << \"SUCCESS: All outputs match expected values.\" << endl;\n    } else {\n        cout << \"FAILED: Outputs do not match expected values.\" << endl;\n    }\n\n    return (pass == 1) ? 0 : 1;\n}\n\n```\n<details>\n<summary> 💡 Solution. Please check it after you've tried. </summary>\n\n```c\nvoid FirFilter(din_t din, coeff_t w[N], dout_t *dout) {\n\tacc_t acc = 0;\n\tdin_t shift_reg[N];\n\tacc = 0;\n\tfor (int i = N - 1; i > 0; i--) {\n\t\tshift_reg[i] = shift_reg[i - 1];\n\t\tacc += shift_reg[i] * w[i];\n\t}\n\tshift_reg[0] = din;\n\tacc += shift_reg[0] * w[0];\n\t*dout = acc;\n}\n```\n</details>\n\nIf you check the schedule viewer you can identify the structure of algorithm implemented through synthesis process. \n\n![alt text](./fig/sch1.png)\n\n### 2. State memory variables (static keyword)\n\nAs you can notice the result from `Simulation` will not give the expected answer. \nYou 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! \n\n1. Write the code again using `static` keyword for `shift_reg` internal array variable. \n2. Check again the result obtained from `simulation`. Can you tell us why expected answer and output answer matched after editing the code? \n3. Run the Synthesis. Are you expecting any changes on BRAM or Flip-flops requirements?\n4. 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? \n\n\n<details>\n<summary> 💡 Solution. Please check it after you've tried. </summary>\n\n```c\nvoid FirFilter(din_t din, coeff_t w[N], dout_t *dout) {\n\tstatic acc_t acc = 0;\n\tstatic din_t shift_reg[N];\n\n\tacc = 0;\n\tfor (int i = N - 1; i > 0; i--) {\n\t\tshift_reg[i] = shift_reg[i - 1];\n\t\tacc += shift_reg[i] * w[i];\n\t}\n\n\tshift_reg[0] = din;\n\tacc += shift_reg[0] * w[0];\n\n\t*dout = acc;\n}\n```\n</details>\n\n\nLab2/Makefile\n *.o .Xil\n# Compiler\nCXX = g++\n\n# Compiler flags\nCXXFLAGS = -O3 -Wall -Wextra -std=c++11 -I. -I/tools/Xilinx/Vivado/2023.2/include\n\n# Executable name\nEXEC = tb_matrixmul\n\n# Source files\nSRCS = matrixmul.cpp matrixmul_test.cpp\n\n# Object files\nOBJS = $(SRCS:.cpp=.o)\n\n# Default target\nall: $(EXEC)\n\n# Link the executable\n$(EXEC): $(OBJS)\n\t$(CXX) $(CXXFLAGS) -o $@ $^\n\n# Compile source files into object files\n%.o: %.cpp\n\t$(CXX) $(CXXFLAGS) -c $< -o $@\n\n# Run the program\nrun: $(EXEC)\n\t./$(EXEC)\n\n# Clean up build files\nclean:\n\trm -rf $(EXEC)Project/Project.md\n# Machine Learning Edge Deployment Case Study\n\nThis 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.\n\n## Overview\n\nThe case study focuses on:\n- Designing a quantized neural network tailored for spatial data (MNIST dataset).\n- Exporting the trained model to ONNX format.\n- Compiling the model using Brevitas and FINN for FPGA deployment.\n- Generating the bitstream and deploying it on the Pynq-Z2 board.\n\nThe model is based on Multi-Layer Perceptron (MLP) components, known as Spatial MLPs, capable of handling variable data shapes suitable for edge deployment.\n\n---\n\n## 1. Introduction\n\nWith 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.\n\n### 1.1 Objectives\n\nThe primary objectives of this project are:\n1. **Model Development**: Design a quantized neural network using Brevitas, utilizing Spatial MLPs that avoid traditional Convolutional Neural Networks (CNNs).\n2. **Model Training**: Train the model on the MNIST dataset to achieve optimal accuracy.\n3. **Export & Compilation**: Export the trained model to ONNX and use the FINN compiler to generate a customized hardware accelerator.\n4. **Hardware Deployment**: Deploy the generated bitstream to the Pynq-Z2 FPGA and run inference using Python.\n5. **Performance Evaluation**: Assess the model’s accuracy, hardware resource utilization, and inference latency.\n\n---\n\n## 2. Tools and Frameworks\n\nTo complete this case study, we will utilize the following tools and frameworks:\n- **PyTorch**: For defining and training the neural network.\n- **Brevitas**: A PyTorch library for Quantization-Aware Training (QAT), essential for converting models into low-precision formats suitable for hardware.\n- **FINN**: An experimental framework from Xilinx Research that creates custom hardware architectures for quantized neural networks (QNNs).\n- **TVM**: An open deep learning compiler stack for CPUs, GPUs, and specialized accelerators (FPGAs).\n- **Vivado HLS**: High-Level Synthesis tool used by FINN to generate RTL from C/C++.\n- **Pynq-Z2**: The target hardware platform for the final deployment.\n\n---\n\n## 3. Project Workflow\n\nThe project is structured into several phases:\n1. **Network Architecture Design**: Designing the Spatial MLP.\n2. **Quantization-Aware Training**: Training the model using Brevitas.\n3. **Model Export**: Converting the model to ONNX format.\n4. **Compilation with FINN**: Transforming the ONNX model into hardware via FINN and TVM.\n5. **Deployment on Pynq-Z2**: Running the compiled model on the FPGA.\n\n---\n\n## 4. Phase 1: Network Architecture Design\n\n### 4.1 Spatial MLP Concept\n\nTraditional 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.\n\n### 4.2 Model Specifications\n\n- **Input Size**: $28 \\times 28$ grayscale images (MNIST dataset).\n- **Quantization**: 4-bit weights and activations.\n- **Architecture**:\n  - Input quantization layer.\n  - Spatial MLP blocks containing linear layers tailored for local receptive fields.\n  - Non-linear activations (e.g., ReLU or QuantReLU).\n  - Fully connected layer for classification into 10 classes.\n\n### 4.3 Implementing the Spatial MLP\n\nHere is the implementation of the `SpatialMLP` class using Brevitas:\n\n```python\nimport torch\nimport torch.nn as nn\nimport brevitas.nn as qnn\nfrom brevitas.core.quant import QuantType\n\nclass SpatialMLPBlock(nn.Module):\n    def __init__(self, in_channels, out_channels, bit_width=4):\n        super(SpatialMLPBlock, self).__init__()\n        self.fc1 = qnn.QuantLinear(in_channels, out_channels, weight_bit_width=bit_width, bias=False)\n        self.act1 = qnn.QuantReLU(bit_width=bit_width)\n        self.fc2 = qnn.QuantLinear(out_channels, out_channels, weight_bit_width=bit_width, bias=False)\n        self.act2 = qnn.QuantReLU(bit_width=bit_width)\n        \n    def forward(self, x):\n        x = self.act1(self.fc1(x))\n        x = self.act2(self.fc2(x))\n        return x\n\nclass SpatialMLPNet(nn.Module):\n    def __init__(self, bit_width=4):\n        super(SpatialMLPNet, self).__init__()\n        # Flatten the 28x28 image to a 784-dimensional vector\n        self.flatten = nn.Flatten()\n        # Input quantization\n        self.quant_inp = qnn.QuantIdentity(bit_width=8, return_quant_tensor=True)\n        \n        # Define MLP blocks\n        self.block1 = SpatialMLPBlock(784, 128, bit_width)\n        self.block2 = SpatialMLPBlock(128, 64, bit_width)\n        \n        # Classification layer\n        self.classifier = qnn.QuantLinear(64, 10, weight_bit_width=bit_width, bias=True)\n\n    def forward(self, x):\n        x = self.flatten(x)\n        x = self.quant_inp(x)\n        x = self.block1(x)\n        x = self.block2(x)\n        x = self.classifier(x)\n        return x\n\n# Instantiate the model\nmodel = SpatialMLPNet(bit_width=4)\n```\n\n> **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.\n\n---\n\n## 5. Phase 2: Quantization-Aware Training (QAT)\n\nQuantization-Aware Training simulates lower precision during the training process, allowing the model to adapt and minimize accuracy loss due to quantization.\n\n### 5.1 Training Setup\n\nWe will use the standard PyTorch training loop on the MNIST dataset.\n\n```python\nimport torch.optim as optim\nfrom torchvision import datasets, transforms\nfrom torch.utils.data import DataLoader\n\n# Device configuration\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel.to(device)\n\n# Dataset and DataLoader\ntransform = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.1307,), (0.3081,))\n])\n\ntrain_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)\ntest_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)\ntest_loader = DataLoader(test_dataset, batch_size=1000, shuffle=False)\n\n# Loss and optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.001)\n```\n\n### 5.2 Training Loop\n\n```python\ndef train(model, device, train_loader, optimizer, criterion, epochs=5):\n    model.train()\n    for epoch in range(epochs):\n        for batch_idx, (data, target) in enumerate(train_loader):\n            data, target = data.to(device), target.to(device)\n            \n            optimizer.zero_grad()\n            output = model(data)\n            loss = criterion(output, target)\n            loss.backward()\n            optimizer.step()\n            \n            if batch_idx % 100 == 0:\n                print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} '\n                      f'({100. * batch_idx / len(train_loader):.0f}%)]\\tLoss: {loss.item():.6f}')\n\ntrain(model, device, train_loader, optimizer, criterion, epochs=5)\n```\n\n### 5.3 Evaluation\n\n```python\ndef test(model, device, test_loader, criterion):\n    model.eval()\n    test_loss = 0\n    correct = 0\n    with torch.no_grad():\n        for data, target in test_loader:\n            data, target = data.to(device), target.to(device)\n            output = model(data)\n            test_loss += criterion(","chars":47755,"image":"https://storage.googleapis.com/adl-page-images/adl0111/00047.jpg","first_page":1,"last_page":125,"title":"Tardīd-i shāyiʻāt-i bāṭilah-ʼi shāh-i makhlūʻ تردىد شاىعات باطلۀ شاه مخلوع :تردىد شاىعات باطلۀ شاه مخلوع","permalink":"https://afghanpress.org/book/adl0111/47","canvas":"https://afghanpress.org/iiif/adl0111/canvas/47","source":"https://afghanistandl.nyu.edu/books/adl0111/","source_pdf":"https://afghanistandl.nyu.edu/pdf/adl0111_download.pdf","rights":{"image":{"statement":"Public domain. NYU states: \"All works presented on this website are, unless otherwise indicated, in the public domain. The images available on this website may be freely reproduced, distributed and transmitted by anyone for any purpose, commercial or non-commercial.\"","uri":"http://rightsstatements.org/vocab/NoC-US/1.0/","holder":"New York University Libraries","source":"https://afghanistandl.nyu.edu/about.html","delivery":"Images are served from a copy mirrored by this project, not from NYU's servers, under the reproduction terms above.","endorsement":"This is an independent project. It is not affiliated with, endorsed by, or reviewed by New York University."},"text":{"statement":"Machine-generated transcription, dedicated to the public domain under CC0 1.0. No warranty of accuracy: see the provenance block on every page.","uri":"https://creativecommons.org/publicdomain/zero/1.0/","holder":"Steps Ventures"}},"provenance":{"text_produced_by":"gemini-3.1-pro-preview","thinking_level":"low","read_completed":"2026-08-09","per_page_confidence":null,"per_page_confidence_note":"Not recorded during the corpus run. No calibrated per-page score exists, and none is estimated here.","human_reviewed":false,"accuracy_context":"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.","primary_source":"The page image. The text is an index into it."}}