راستش خیلی وقت بود دنبال یه آموزش مناسب بودم که با دیدنش بتونم بفهمم دقیقاً چرا ما از زبون C داریم برای برنامه نویسی میکروکنترلر ها استفاده میکنیم و اگه زبون دیگه ای هم به کار میره خیلی از جاها به مشکلاتی میخوره که برای زبون C و ساختار کامپایلر اون اصلاً مشکل به حساب نمیاد؟! خیلی هامون شنیدیم زبون C یه زبون intermediate هستش یا به عبارتی زبونیه که نه خیلی سطح بالاس (مثه پایتون) و نه خیلی سطح پایین (مثه اسمبلی) و همین دلیل هم باعث شده بین برنامه نویس های سخت افزار (به اصطلاح مهندسای امبدد) خیلی محبوب باشه و کلی ازش استفاده شه.
ولی خب سوالی که پیش میاد اینه که این مطلب دقیقاً چه کمکی به ما تو توسعه اپلیکیشن های امبدد می کنه؟ ساختار کامپایلر به چه صورت هست؟
و اینکه چطور میشه از این قابلیتها برای توسعه اپلیکیشنهایی با کارایی بالاتر استفاده کنیم؟
پروسه تبدیل برنامه
احتمالاً می دونید برنامهای که ما مینویسیم به صورت مستقیم قابلیت اجرا روی سخت افزار رو نداره و باید بعد طی کردن مراحلی به زبان ماشین تبدیل شه و اون موقع هستش که میتونیم این برنامه رو روی سخت افزار پیاده سازی کنیم.
کاری که خیلی از محیطهای توسعه میکروکنترلر ها انجام میدن در واقع همینه که میان این فرایند رو برامون انجام میدن تا ما به صورت مستقیم با این فرایند درگیر نشیم ولی مسئلهای که هست اینه که خیلی از بهبودهایی که توی کد ما انجام میشه در واقع تو همین مراحل صورت میگیره و اگه ما اطلاعات دقیقی از این پروسه داشته باشیم میتونیم بعضی قسمتهای رو بهتر کنیم.
بالاخره هرچی باشه کاری رو که ما میتونیم انجام بدیم شاید کامپایلر نتونه به اون خوبی انجام بده!
خب بریم سراغ توضیح ساختار کامپایلر !
اولین قدم برا برنامه نویسی سخت افزار نوشتن کد C در قالب یه فایل با پسوند *.C هستش.
حالا اگه بخواید فایل های دیگه یا کتابخونه هایی رو هم به پروژه تون اضافه کنید یه سری فایل با پسوند *.h هم خواهید داشت.
تا اینجا کاریه که شما به عنوان برنامه نویس انجام میدین. باقی مراحل رو کامپایلر نصب شده روی سیستم شما براتون انجام میده!
پردازش اولیه فایل ها
کامپایلر با گرفتن این فایل ها در قدم اول فرآیند Preprocessing رو انجام میده.
تو این فرایند یه فایل با پسوند *.i تولید میشه که در واقع همون کدی هستش که شما نوشتید ولی یه سری تغییرات توش داده میشه.
مثلا اینکه شما یه ثابت رو در ابتدای کد با نام TEST تعریف کردید و تو باقی کد هرجا خواستید ازش استفاده کنید به جای نوشتن مقدار اون ثابت صرفا کلمه TEST رو نوشتید.
تو فرایند پیس پردازش تمام جاهایی که شما TEST رو نوشتید مقدار اصلیش رو قرار میده.
البته این مرحله و مراحل بعدی رو ایشالا در ادامه به صورت کاملتر توضیح خواهم داد.
تبدیل تک تک فایل ها به اسمبلی
بعد تولید این فایل کامپایلر وارد عمل میشه و کدهای موجود رو تبدیل میکنه به زبون اسمبلی (هنوز کد برای قرارگیری روی سخت افزار آماده نشده!) و یه فایل با پسوند *.s تولید میکنه.
طبیعتاً برای ترجمه کدهای C به اسمبلی لازمه کامپایلر با ISA مربوط به سخت افزار مورد نظر ما کاملاً آشنا باشه تا بتونه از instruction هایی استفاده کنه که معماری اون پردازنده پشتیبانی میکنه!
خب تو این مرحله اسمبلر کارشو شروع میکنه و فایل اسمبلی ورودی رو تبدیل میکنه به یه object file با پسوند *.o که تقریبا میشه گفت مشابه چیزی هستش که روی سخت افزار پیاده میشه..
داخل این فایل هم یه سری کد به فرمت هگز هستش که در واقع همون کد اسمبلی تولیدی ماست که به این روز دراومده!
نهایی شدن فایل هگز
تو قسمت بعدی این سریال، کار Linker شروع میشه. در واقع هدف از وجود لینکر اینه که ما بتونیم تمام فایلهایی رو که داریم و این فایلها بعضاً به هم ارتباط هم دارن (مثه include هایی که توی کد انجام میدیم و یه فایل دیگه رو فراخوانی میکنیم) تجمیع کنه و به صورت یه فایل شسته رفته بهمون تحویل بده. از اسمش هم تقریباً مشخصه که چی کارس! خروجی این مرحله یه فایل با پسوند *.elf هستش.
آخرین قسمت این اپیزود هم متعلق به ابزاریه به اسم Locator که این فایل شسته رفته تحویل گرفته شده از Linker رو تبدیل به یه فایل اجرایی میکنه که قابل پیاده سازی روی سخت افزار باشه.
به عبارت بهتر میاد و امکانات سخت افزار رو در اختیار کد قرار میده و فضاهای حافظه (address space) رو به بخشهای مختلف کد اختصاص میده. خروجی این مرحله هم یه فایل اجرایی هستش که برای معماریهای مختلف پسوندهاش متفاوت میتونه باشه (برای میکروهای AVR پسوند معروفش *.hex هستش)
خب تا اینجا با مراحل مختلف روند تولید کد سخت افزار و ساختار کامپایلر از ابتدا تا انتها آشنا شدیم. تو قسمتهای بعدی ایشالا این مراحل رو به صورت کامل با تمام جزییات لازم بررسی میکنیم.
با ما همراه باشید تا در قسمت دوم انواع کامپایلر را بررسی کنیم.
منبع : سیسوگ