 Hello, everyone. I am M.Tamsil Shams working as I associate staff engineer for Samsung Semiconductor India. Today, I'll be talking about the topic Porting Zafire OS for IsoC and we'll look into specifically the examples of Cortex R5. So, let's get started. So, this is the agenda for today. So, first, we will see what is Zafire OS. And then we will see the different porting components which we need to actually add so as to make it compatible for IsoC. Then we will see to how to have those implementations which will support the hardware. Then moving forward, we will see the debugging tips. Then we will see the key tools and build commands. And then further, we will see the timer and the UR drivers. And then at the last, we'll see what other things we can add in the future. So, basically, Zafire OS is a small-time real-time operating system which is for connected resource-constrained embedded devices. And it's kind of a small-scale kernel which supports simple embedded devices. It has a lot of features similar to Linux kernel like memory management, multi-threading, scheduler, interrupt handling and etc. It supports wide varieties of CPU architectures and it has a build system called WEST which makes it easy to compile a lot of modules with the user applications. And then it provides SDK which contains tool chains for different architecture. So, regarding the porting components, basically we will see the key folder changes related to Zafire, how when we are adding a new project or an IsoC. So basically, these are four main things. So we will be adding a new IsoC. We need to add a new board details. We need to add new drivers if needed. Like there are some basic drivers already there. But if you want to have some drivers support is not there, then you need to add those drivers. And at the end, adding a device to give the resources we need to add device. So this is the configuration hierarchy of Zafire. First there is board, IsoC. So it starts from bottom architecture, CPU, IsoC, family, series and then the board. Yeah. So about the hardware support implementation. So if you see the same as in the previous slide, we can see there is architecture, then there is a CPU code, then there is IsoC family, then there is an IsoC series, IsoC, drivers and boards. So taking into example of Cortex-R5, if you see the architecture is ARM, the CPU code is Cortex-R5. IsoC family will be like company name slash social name, whatever you are trying to, the board which you are using that like if you have for Samsung, it's Samsung underscore XYZ if you name that IsoC. IsoC series is CR5 and IsoC is CR5. The drivers, basic drivers like serial, timer, interrupt, etc. And the board is Samsung board. So that can be like, these can be actually based on what one wants to name, it can be done actually. Yeah. So going into the details of each hardware configuration. So architecture, as we like whatever architecture we are using, like if it's an ARM, ARC, R6, RISC-V, X86, whatever be the thing. Then the CPU core. So basically the CPU core is what implements the early board sequence. It does all the handling related to interrupt and error, all the thread context switching and all the basic things which are done from the processor that is all done by the CPU core. The CPU cores are like R, V2, Cortex, M0 plus Cortex, R5, those things. Then there is IsoC family. It basically represents a single IsoC type and it can have many of its variations depending on the peripherals and the features we are using. So it can be like kinetics, IMX, NRF, or Exynos, etc. Then the IsoC series. So IsoC series represents a specific IsoC having a peripheral of features. It's a subset of IsoC family actually. It can be like different Exynos, which we have like Exynos 54, 33, or 50 at 50, or even Snapdragon, specific Snapdragon. Then the next thing is IsoC. So this is the actual IsoC that is soldered in the hardware platform and its configuration, which is used. So that is like very technical to all the IsoC companies. Then there is drivers. It's basically include device model responsible for configuring and initializing drivers. So whatever drivers we are using to make use of our device, that is what is driver like interrupt controller, timer, serial communication, etc. And the last thing is both, which includes IsoC plus its associated peripherals and features, including external components also, not just what is present on the IsoC and its peripheral, but also the external components also. Then the like, how do we configure, how do we give the details of configuration to the hardware, to the Zephyro S. So basically top level hardware configurations are defined by a key config similar to how we do in kernel. And once we add that and after the build, we can see the processing results at the following locations actually that build inside that board name will be there Zephyro.config. And in the include generated auto-confrotage. And similarly, the low level hardware specific configurations are passed on by the device tree and the final processing results located are in this files actually. So these are generated once the Zephyro S is built, compiled for that specific IsoC or board. So these are two different ways of configuring the top level hardware and low level hardware configurations. So now we will look into how, what all changes we need to make to add a new IsoC. So we need to define the IsoC family, the IsoC series, the IsoC and the IsoC part number. So if you see, we have already talked about architecture, Zephyro core. Now this is the main part where we are adding the IsoC basically. The IsoC related files are located at this location, IsoC, then architecture name, IsoC family name and the IsoC series. If you see for CR5 whatever we have discussed earlier, we will like SoC, ARM, Samsung, XYZ and then CR5 and then all the files related to it. So all the SoC folders actually have basic information like the processor informations, IRQ controller, linkers or any SoC related init configuration which will be, we will be doing. And it is called as part of the system and isolation with priority zero. So this is the first most thing which we will be doing while running our firmware code on the board. And it provides a SoC.h header which can be included by the board and driver files. And it can also contains lot of K config files, linker definitions and the device tree fix ups. So these are the details of while adding a new SoC. So if you see this is the structure of a new SoC. Inside this Samsung XYZ we can see there is a CMakeList.dxt, there is Kconfig, Kconfig, DevConfig, Kconfig, SoC. Similarly under CR5 we will have its own specific Kconfig, Cucamers, linkers, ksoc.c, SoC.h. So if you see those things are present here and these are included in the board files also in the config which can be found here. Similarly the DTSI things which are present for this SoC are located at this location. DTS ARM, Samsung XYZ, Samsung CR5. For CR5 for generic it will be like part DTS architecture, vendor, vendor SoC name and then dot DTSI whatever the drive ISG extension is. Yeah. So moving forward how to add the DTS file or what details to add in it. So basically device includes all the files which contain main SoC details and the nodes related to the different drivers or the peripherals like IRQ controller, CPU information or a PWM driver or a UR driver or MCT driver those things. And these DTS files are included in actual board DTS files. So there will be a generic board DTS file and we need to include these DTSI files so as to get that information. And also DTSI files are reusable. It's not necessary that you always need to create a new DTSI files. You can also reuse an older DTSI file by just changing few things. As mentioned in the last slide, the location is DTS architecture, vendor and the vendor SoC name dot DTSI. Another feature of DTSI is like as in kernel we need to store the DTB block at some additional memory and we pass it during runtime. But here we actually the DTSI information is taken care during the compile time itself. And then it is made like a flattened device tree and the details are sent to the driver files. And these compile DTSI files are actually cross checked with the YML bindings details present. And then the information is placed in a header file actually which is used by the driver files or any other code files in the project. So this is an sample DTSI file like it's very simple like how you mentioned the CPU, how you mentioned the associate details. Here the interrupt controller details are mentioned for a GIC similarly CPU Cortex R5. So inside this part DTSI, we have two different DTSI, we can have two different DTSI for CR5 and for M0. Since we'll be using CR5 so during compile time you need to mention this in the board file and accordingly it will be compiled and the header file will be generated from here. So the next thing is adding board. So this is like we have already seen this SOC series, the DTS. Now we are seeing the board and later we will see the drivers. So this actually, this board directory actually represents the actual application hardware. This is present at boards architecture board name. So for if you see for our example, it's boards on Samsung XYZ and all the board specific init information initialization information are present here. So anything which we need to pass related to board if external peripherals or internal peripherals will be present and board. Even this board file folder actually contains information from other driver and SOC folders even DTSI. And it contains a DevConfig file actually which we will use for building which will take use the other files to during the build time. So if you see this is the structure of boards. Inside boards on Samsung we have board.c, we have cmakelist.txt, we have kconfig.board, kconfig.dfconfig. Then we have samsung cr5.devconfig, we have samsung cr5.dts, simpson cr5.virml. So this DTS will include that DTSI file actually and this DevConfig will have all the details related to which modules to build during the compile time. And kconfig will also have that hardware configurations, what all we need to insert into the SOC details. And this YAML will be the one which will be using to cross check that details how the binding is happening where the details. So basically while compiling the fire image we always refer board name which includes all SOC DTSI related information. It also contains a .h files which can be used by the drivers and the applications. And in the board.cmake actually we instruct how to flash or debug the code when it's running on the board. And this includes that YAML file actually includes all the board details which will be verifying with the DTS files. So the next thing is adding new drivers. So these are located at drivers folder inside that there can be driver type or directly a folder. So if it's related to any specific protocol like serial, DMA or USB that can be added inside this sub folder. Or if it's any new driver which doesn't have any specific protocol then it can be added directly after drivers. And the selection and configuration of these drivers are done via the .kconfig and the device tree. If you present the nodes there in the device tree and then enable the same in the .kconfig and mention them in the .kconfig then during the compile time it will take this. And this initialization is performed during the drivers initialization performed during the boot only. The similar way in what we do in the kernel after the system initialization we go for different device driver initializations and then do the binding. YAML files are actually there which describe the device tree nodes and properties. So you need to define those properties or mention those properties in a driver according to those YAML description only so that you don't face any issue with the binding of device and driver. And then device tree file to define. So two steps to add driver for compilation is like first you need to add the driver files then you need to have .kconfig files for the details you need to either add in the main .kconfig files or you need to add your own .kconfig file which can be included in the other .kconfig file. So for a specific sub module driver the hierarchy looks like that that inside the drivers there is a serial there is file like cmake.txt.kconfig.crp. Here only the driver files will be actually there. So this is the steps to actually add driver to the init sequence. So we define a driver similar to whatever compatible name we are mentioning in the device tree. And then we pass this init function to this macro and it will do the add this driver to the init sequence actually. Basically this is the hooking of driver API to the Zephyr framework. So this was the part till where like what all porting changes we need to do or hardware support implementation we need to do. The next topic is debugging tips. Here we will see what are the basic debugging tips which we can use or I have used during my porting to do it smoothly. So first of all the thing is like we can check into the other source code reference to understand what exactly needs to be done to initialize. So see like already there are support for different associates there in the Zephyr. So you can look into those codes and see what all changes are required and then try to print in the UART actually while accessing registers or something. So the basic thing is like once the core is up the first thing you need to do is implement the UART driver. So that print case there because print case lies like a life for a firmware. So that can help to debug it faster. You can turn on the system logging or logger that can help you to get the logs from the system site. You can turn on the config asset to try and cache the errors. And you can also use an on chip digger like Ultrasoc or Jlink or Ulink so that you can debug it better while running or you don't need an external debug. You can use an on chip debugger actually. So now we will look into the key tools and build commands. So for that Zephyr build system use these important tools actually. There is one called CMake which contains all the source code build steps. So it contains all the basic build steps required for a source code. Then there is YAML. So these files provide information used mostly for documentation or health points. So basically all the codes or all the details which we are adding the information related to that as a document is present in this YAML files. Then there is a command called WEST which provides the build command which can be internally used by the CMake list inside each sub directory. And it will help to build each sub directory along with the main directory. So this is how we use the WEST commands to build. So if you see WEST in it does the init Zephyr build and WEST board will list all the available boards. And based on the available board you can use this command WEST build minus be board name and sample cell over whatever file you are adding minus P2 build this Zephyr OS for a specific board. Yeah, now we will look into the specific driver like the timer driver. So basically timer is a kernel object that measures the passage of time. So whenever you are running as you need to have a timer to which actually measures the passage of time. So this is one of the driver like there can be multiple different timers for ARM. It's an ARC timer then there is a MCT timer, WDT timer, PWM timer. And when a timer specific time limit is reached it can perform an application defined action or weird. So basically different timers have different features. Some timers like have features like if you reach a specific time limit it will do a reset. Or it will generate an interrupt which using which you can have an handler function to perform on any faction. So basically if you want any one feature which needs to be happened at certain point of time periodically then you can have this timer feature in that actually. And any number of timers can be defined. It's not like only one timer you can have. And the timer has following key properties like duration is there, period is there, expiry function is there, stop function is there. If you see the implementation of a timer, if you see there is a defining a timer how you define a timer. So basically it is defined using variable of type K, type K timer. It can be initialized by calling K timer in it. Then you can use the timer expiry function to call a handler or something like that call a callback function. Then there is another feature to read timer status. If you want to have some like polling or that you can read timer status and you can use a timer status synchronization also. So this was all about timers. The next thing is you are driver. So basically as said earlier you are driver is like it gives us print K which is the life for our SoC. So basically this will demonstrate how to use the URCL driver with a simple ECO. So basically it whatever data you give to the console it will read data and then it goes the same data after the end of line is actually received. That is once you press the enter key. So the for sending data polling API is used and for receiving the interrupt driven API is used. So UR can function in both polling and interrupt mode. So for receiving we are using interrupt and for sending we are using polling mode. By default the UR peripheral is normally used for Zephyr shell. Like the UR peripheral is used by the Zephyr only but which has support for all board. But if you want to have a specific shell for your you can implement that and use the UR peripheral. So this is how you build and run the UR. So this is where you are building the UR module along with the board and then we do West Flash. And once it comes you can say that the print comes that hello I am your ECO board tell me something and press enter. So now if you write there hide there and press enter the print will be like hide there. This is the way you can use UR if you want to access a Zephyr you can have that feature also added here. So the last part of the presentation is like what else we can do for the things in the future. So basically Zephyr is a framework for most of the generic drivers. But if you want to add any individual drivers that needs to be explored how to add if there's like most of the generic drivers will have their own framework. But if you want to add a new type of a generic driver then also you need to have a core framework then the associate specific drivers. That is one thing to look forward. Then device tree can hold most of the IP hardware details and these can be read from drivers. But the thing is these can actually not be needed and these can actually be given directly into the device driver. So that is one thing which we can look into site like there are many bare metal port devices which use this feature of having the details directly to the device driver. So we can look further into this so as to make putting more is he not much changes must be required in the device tree. Because a lot of things can be redundant in the device tree or it needs to be done according to some law. But if you don't have any information related to specific information then you can ignore it using the device driver. So for that we need to make the device driver itself compatible and all the predefined linker skips divide it is a file which are already defined can be modified and used. So normally nowadays when we try to put a new SOC we write a new linker skips or DTSI file. But it can be there like there can be a generic DTSI file or a linker skip which we can use with little modification so as to face less booting issues. So this was the presentation for putting so if you have any questions you can actually ask.