Device Tree چیست؟ آشنایی با Device Tree و کاربرد آن

0 44
۵/۵ - (۱ امتیاز)

فهرست

Device Tree چیست؟

در دنیای کامپیوتر، درخت دستگاه (Device Tree) یک ساختار داده است که اجزای سخت افزاری یک کامپیوتر خاص را توصیف می کند. سپس هسته سیستم عامل به کمک Device Tree می تواند از آن دستگاه ها استفاده کند و آن ها را مدیریت کند. این دستگاه ها شامل CPU، حافظه، باس ها (Bus) و دستگاه‌های جانبی هستند. Device Treee از کامپیوترهای مبتنی بر SPARC از طریق پروژه Open Firmware برگرفته شده اند. توجه کنید که کامپیوترهای شخصی با معماری x86 عموما از Device Tree استفاده نمی کنند. سیستم های شخصی مبتنی با معماری x86 بر مبنای پروتکل های Auto Configuration یا همان ACPI هستند و به کمک ACPI سخت افزار را شناسایی می کنند.

برای درک بهتر Device Tree به یک مثال دقت کنید. فرض کنید یک بورد الکترونیکی داریم که در یک خودرو مورد استفاده قرار می گیرد. این بورد شامل چند دستگاه جانبی است. برای مثال، شتاب سنج، بلوتوث، چند باس سخت افزاری و … . سیستم عامل برای شناختن این دستگاه های جانبی و دیگر قطعات (همانند CPU) از Device Tree کمک می گیرد. یعنی درون Device Tree خصوصیات و مشخصات دستگاه ها وارد می شود تا وقتی سیستم عامل به سراغ آن رفت، بداند که چطور با آن دستگاه ارتباط برقرار کند.

Device Tree در لینوکس

همان کرنل کامپایل شده لینوکس می تواند از سخت افزارهای مختلف پشتیبانی کند. هسته لینوکس برای معماری های ARC، ARM، C6x، H8/300، MicroBlaze، MIPS، NDS32، Nios II، OpenRISC، PowerPC، RISC-V، SuperH و Xtensa اطلاعات Device Tree را می خواند. در ARM به این صورت است که Device Tree برای همه SoCهای جدید از سال ۲۰۱۲ اجباری شده است. این به این دلیل است که تعداد زیادی فورک (لینوکس و Das U-Boot) که در طول تاریخ برای پشتیبانی از بردهای مختلف ARM ایجاد شده اند، در نظر گرفت. هدف این است که بخش قابل توجهی از توضیحات سخت‌افزار را به خارج از کرنل منتقل کنیم. چرا که لزومی ندارد برنامه های توسعه داده شده برای یک بورد بخصوص، در همه بورد ها حضور داشته باشد. بسیاری از این بوردها به عنوان یک سیستم Embedded مورد استفاده قرار می گیرند. بنابراین عدم اشغال بی دلیل حافظه بسیار مهم است.

نگاه سطح بالا به Device Tree

مهمترین چیزی که باید درک کنید این است که DT به سادگی یک ساختار داده ای است که سخت افزار را توصیف می کند. هیچ چیز جادویی در مورد آن وجود ندارد، و به طور جادویی همه مشکلات پیکربندی سخت افزار را از بین نمی برد. کاری که انجام می دهد ارائه زبانی برای جدا کردن پیکربندی سخت افزار از پشتیبانی برد و درایور دستگاه در هسته لینوکس (یا هر سیستم عامل دیگری در این زمینه) است. استفاده از آن به پشتیبانی از برد و دستگاه اجازه می دهد تا داده محور شود. تصمیم گیری در مورد راه اندازی بر اساس داده های ارسال شده به هسته به جای انتخاب های سخت کدگذاری شده برای هر ماشین.

در حالت ایده‌آل، راه‌اندازی پلتفرم مبتنی بر داده باید منجر به تکرار کد کمتری شود و پشتیبانی از طیف گسترده‌ای از سخت‌افزار با یک تصویر هسته را آسان‌تر کند.

لینوکس از داده های DT برای سه هدف عمده استفاده می کند:

  • شناسایی پلتفرم
  • پیکربندی Run-time
  • شناسایی دستگاه ها

شناسایی پلتفرم – Platform Identification

اول و مهمتر از همه، هسته از داده ها در DT برای شناسایی ماشین خاص استفاده می کند. در یک دنیای عالی، پلتفرم خاص نباید برای هسته مهم باشد زیرا تمام جزئیات پلت فرم به طور کامل توسط درخت دستگاه به شیوه ای سازگار و قابل اعتماد توصیف می شود. با این حال، سخت‌افزار کامل نیست، و بنابراین هسته باید دستگاه را در ابتدای راه‌اندازی شناسایی کند تا فرصتی برای اجرای تعمیرات خاص دستگاه داشته باشد.

در اکثر موارد، هویت ماشین نامربوط است و در عوض، هسته کد راه‌اندازی را بر اساس CPU یا SoC هسته دستگاه انتخاب می‌کند. برای مثال، در ARM، setup_arch() در arch/arm/kernel/setup.c، setup_machine_fdt() را در arch/arm/kernel/devtree.c فراخوانی می‌کند که در جدول machine_desc جستجو می‌کند و machine_desc را انتخاب می‌کند که با داده‌های درخت دستگاه مطابقت دارد. . بهترین تطابق را با نگاه کردن به ویژگی “compatible” در گره درختی دستگاه ریشه و مقایسه آن با لیست dt_compat در struct machine_desc (که در arch/arm/include/asm/mach/arch.h تعریف شده است) تعیین می کند. کنجکاو هستند).

ویژگی «سازگار» شامل فهرست مرتب‌شده‌ای از رشته‌ها است که با نام دقیق ماشین شروع می‌شود، و به دنبال آن فهرستی اختیاری از تابلوهایی که با آن‌ها سازگار است مرتب‌سازی شده‌اند از سازگارترین به حداقل.

پیکربندی در لحظه – Run-time Configuration

در بیشتر موارد، یک DT تنها روش برقراری ارتباط داده ها از سیستم عامل به هسته خواهد بود، بنابراین برای ارسال در زمان اجرا و داده های پیکربندی مانند رشته پارامترهای هسته و مکان یک تصویر initrd نیز استفاده می شود.

بیشتر این داده ها در گره /chosen موجود است و هنگام بوت کردن لینوکس چیزی شبیه به این خواهد بود:

chosen {
        bootargs = "console=ttyS0,115200 loglevel=8";
        initrd-start = <0xc8000000>;
        initrd-end = <0xc8200000>;
};

ویژگی bootargs حاوی آرگومان های هسته است و ویژگی های initrd-* آدرس و اندازه یک حباب initrd را مشخص می کند. توجه داشته باشید که initrd-end اولین آدرس بعد از تصویر initrd است، بنابراین با معنایی معمول منبع ساختار مطابقت ندارد. گره انتخاب شده همچنین ممکن است به صورت اختیاری دارای تعداد دلخواه خصوصیات اضافی برای داده های پیکربندی پلت فرم خاص باشد.

در طول راه‌اندازی اولیه، کد راه‌اندازی معماری، of_scan_flat_dt() را چندین بار با تماس‌های مختلف کمکی فراخوانی می‌کند تا داده‌های درخت دستگاه را قبل از تنظیم صفحه‌بندی تجزیه کند. کد ()of_scan_flat_dt از طریق درخت دستگاه اسکن می شود و از کمک کننده ها برای استخراج اطلاعات مورد نیاز در هنگام بوت اولیه استفاده می کند. معمولاً از helper() early_init_dt_scan_chosen برای تجزیه گره انتخابی از جمله پارامترهای هسته، () early_init_dt_scan_root برای مقداردهی اولیه مدل فضای آدرس DT و () early_init_dt_scan_memory برای تعیین اندازه و مکان رم قابل استفاده استفاده می شود.

در ARM، تابع setup_machine_fdt() مسئول اسکن اولیه درخت دستگاه پس از انتخاب صحیح machine_desc است که از برد پشتیبانی می کند.

شناسایی دستگاه ها

پس از شناسایی برد، و پس از تجزیه داده های پیکربندی اولیه، مقداردهی اولیه هسته می تواند به روش عادی ادامه یابد. در نقطه ای از این فرآیند، ()unflatten_device_tree فراخوانی می شود تا داده ها را به یک نمایش زمان اجرا کارآمدتر تبدیل کند. همچنین زمانی است که قلاب‌های راه‌اندازی مخصوص ماشین فراخوانی می‌شوند، مانند قلاب‌های machine_desc .init_early()، .init_irq() و .init_machine() در ARM. بقیه این بخش از نمونه هایی از پیاده سازی ARM استفاده می کند، اما همه معماری ها در هنگام استفاده از DT تقریباً همین کار را انجام می دهند.

همانطور که از نام ها می توان حدس زد، .init_early() برای هر راه اندازی خاص ماشینی که باید در اوایل فرآیند بوت اجرا شود استفاده می شود و ()init_irq برای تنظیم مدیریت وقفه استفاده می شود. استفاده از DT رفتار هیچ یک از این عملکردها را تغییر نمی دهد. اگر یک DT ارائه شود، هر دو ()init_early و .init_irq() می توانند هر یک از توابع پرس و جوی DT (of_* در include/linux/of*.h) را فراخوانی کنند تا داده های اضافی در مورد پلتفرم به دست آورند.

جالب ترین قلاب در زمینه DT .init_machine() است که در درجه اول مسئول پر کردن مدل دستگاه لینوکس با داده های مربوط به پلتفرم است. از لحاظ تاریخی، با تعریف مجموعه‌ای از ساختارهای ساعت استاتیک، platform_devices، و سایر داده‌ها در فایل .c پشتیبانی می‌کند و آن را به‌صورت انبوه در .init_machine() ثبت می‌کند، این کار بر روی پلتفرم‌های تعبیه‌شده پیاده‌سازی شده است. هنگامی که از DT استفاده می شود، به جای کدگذاری سخت دستگاه های استاتیک برای هر پلت فرم، لیست دستگاه ها را می توان با تجزیه DT و تخصیص ساختارهای دستگاه به صورت پویا به دست آورد.

ساده ترین حالت زمانی است که .init_machine() فقط مسئول ثبت یک بلوک از platform_devices باشد. پلتفرم_دستگاه مفهومی است که توسط لینوکس برای دستگاه‌های نقشه‌برداری شده با حافظه یا ورودی/خروجی که توسط سخت‌افزار قابل شناسایی نیستند و برای دستگاه‌های «کامپوزیت» یا «مجازی» استفاده می‌شود. در حالی که اصطلاحات “دستگاه پلت فرم” برای DT وجود ندارد، دستگاه های پلت فرم تقریباً با گره های دستگاه در ریشه درخت و فرزندان گره های اتوبوس نقشه برداری شده با حافظه ساده مطابقت دارند.

ساختار Device Tree

Device Tree در فایل هایی با فرمت .dts ذخیره شده است. از طریق Device Tree Compiler یا همان DTC در یک Devicetree Blob یا فایل باینری درخت دستگاه (.dtb) کامپایل می شود. فایل‌های منبع Device Tree می‌توانند شامل فایل‌های دیگری باشند که به عنوان Device Tree Source Includes شناخته می شوند. در اصل همان Includeهای مورد نیاز هستند. در زیر یک نمونه از ساختار فایل DTS را مشاهده می کنید:

/dts-v1/;

/ {
    soc {
        flash_controller: [email protected] {
            reg = <0x4001e000 0x1000>;
            flash0: [email protected] {
                label = "SOC_FLASH";
                erase-block = <4096>;
            };
        };
    };
};

در مثال بالا، خط /dts-v1/; نشان دهنده نسخه ۱ سینتکس DTS است.

این درخت دارای چهار گره (Node) است:

  • گره ریشه که با علامت / نشان داده می شود. به این گره، گره ریشه یا Root Node هم گفته می شود.
  • گره soc (مخفف “System on Chip”)
  • گره [email protected] شامل کنترلرهای فلش است.
  • دستگاه [email protected] نمونه ای از فلاش که از کنترلر فلش استفاده می کند.
ساختار Device Tree

در تصویر بالا ساختار یک فایل DTS قابل مشاهده است.

قسمت هایی از نام گره ها بعد از علامت (@) وجود دارد، آدرس واحد (Unit Address) هستند. آدرس های واحد آدرس یک گره را در فضای آدرس گره والد آن مشخص می کند. درخت فوق را می توان توسط کامپایلر استاندارد DTC به فرمت یا اسمبلی DTB باینری کامپایل کرد. با این حال، در Zephyr RTOS، فایل‌های DTS در فایل‌های هدر C (.h) کامپایل می‌شوند، که سپس توسط سیستم ساخت برای کامپایل کد برای یک برد خاص استفاده می‌شود.

درباره ما

ترجنس | thregence.ir
آکادمی ترجنس | edu.thregence.ir
دوره‌های آکادمی ترجنس | courses.thregence.ir
اینستاگرام | instagram.com/thregence
تلگرام | t.me/thregence
یوتوب | https://bit.ly/30mGowo
آپارات | aparat.com/thregence

ارسال یک پاسخ