 So first of all, thank you for joining my presentation. My name is Alejandro Hernandez. My presentation's name is Developing, Building, and Testing Your Bare Metal Applications using the OctaProject and Open Embedded, the infrastructure basically. And yeah, I'm just gonna get started. So the outline of this presentation is the following. I'll go through some multi-architecture embedded devices and why I'll go through the runtime testing infrastructure coming from Open Embedded, how it works already and what we're trying to do. And then I'm gonna go and explain a little bit about how can you use the OctaProject for bare metal applications? And then at the very end, we're gonna go to doing an actual application example and a test. So first of all, as time goes by, we see more and more devices that are multi-architecture or heterogeneous devices. This is because it has a reason, right? You can have several processors on a single embedded product and it's because certain processors are better fitted to do some tasks than others. So you usually have, for example, in this device, which is the one that I work with, it's the actual sphere device and it has two processors. It actually has a Cortex-A7, for example, for Linux and then a Cortex-M4 for bare metal. So this is just an example, there's several of these and you can see it how the trend is to create these kind of devices. So yeah, the Open Embedded Runtime Testing Infrastructure. So I wanna point out that this happens every day on the auto builder for the OctaProject. Every single day, several architectures are tested on Linux on the pocket distribution. So this is something that happens every single day and it's how we in the OctaProject get our test results and get how our system, the quality of our system basically. So how does this work? If you wanted to test a Linux OS, then the way it works right now is that you can either do QMU, you can do hardware as well. In this example that I'm showing here, this is on my local.com and I'm inheriting the test image class. I'm setting the target as QMU and I'm setting the test suites as auto which is a good number of tests that will give you a good idea of how your system is looking on a certain day. You can specify, for example, a simple remote on which you can specify an IP address for your device and then the test will be done through SSH if that's what you want. On test suites, for example, one important thing is that you can specify per case basis. So for example, you could put ping, SSH, SCP, or parse loads. Those are four tests that I can use as an example. Those are located on the meta directory, leave, OEQA, runtime, and then cases directory. So if you go into that directory, you can see a file per test usually and you can find the ping test access so that I can do and test that now. It's important to point out as well that the order matters because they have dependencies between them. So for example, in the example that I'm putting there, it's I put SCP, ping, and SSH and what's gonna happen in that case is that SCP is gonna be skipped and then ping is gonna be run and SSH is gonna be run. And the reason is because the SCP test itself depends on the ping test. So in this case, it wouldn't work. The correct order would be to put ping and then SSH and then SCP. So that's how it works on Linux. Normally, this is right here, for example, I'm gonna put an example of how that works. If I build the core image minimal image and then I pass this dash C test image, I'm gonna go back and replay that. So if I bid big core image minimal and then pass dash C to specify a task and then put test image as my task, assuming I have already built the image, what's gonna happen is that the system is gonna, bid big is gonna run the test image task. As you can see there, it's running on two seconds, seven seconds, and at some point, bear with me a little bit, I'm gonna get a result from that test, from those tests, those tests. And then for example there, it's complaining that I didn't put an SSH server on my image. So I put ping and SSH and it says the ping test pass and then the SSH test was kept. So the important thing here is that you can see the report of what was passed, what was kept, and what failed, right? So that's how it works right now on Linux. And that's the sort of context that you need for this. So I've implemented the test image boot patterns in OpenEmbedded and this is because tests need a synchronous communication between the host and the target. And in the test image class, you can see the examples on how to use those. In the example there, I think I'm, if I have a web server running on an embedded device or something and my user is a web server, I'm changing those specific variables. They are prepended with send and search, depending on if it's something that the host will send or search on the output of the target. And then on the QMU runner.py script, which is what we use to run QMU automatically, we can see the defaults are set. And as an example, well, not example, but the defaults are, for example, that you will, to search for a reach prompt, you will search for the login colon string. Your login user would be root and then your login succeeded will be a regex like that. And then a command finished regex would look like that as well. So to make it, I know it doesn't make a lot of sense right now, but I'll make it more clear in this example. So whatever you see on red is coming on the, it's on the host, whatever you see on purple, it's on the target. And so the first thing that happens is that the host tells the target to start booting, again, whether it is QMU or something, or if the target is a hardware device, then it just starts booting and the host is reading the output of the boot lock. On the target, what's gonna happen is that it's gonna start booting and at some point it's gonna get to a login prompt, which is the string that we declared here in these variables. So great, we got a login prompt. At that point, the host is gonna send the root newline string and then what's gonna happen on the target is it's gonna log in and we're gonna get a root prompt, which is gonna be something like root at something and then pound sign, right? So what that means is that we successfully logged in. So now we're the root user on the system and the host knows that because it's gonna search for that regex, right? And then at that point, it's gonna send commands to the target. The target, it's gonna execute the command and then it's gonna get another prompt because it are the command already finished. Command finished and then the host will continually just read output. So for example, and then if we're testing ping, we would, at this point, we would send ping. We will, the target will perform the ping command and then at some point it'll exit and then it'll finish and we're gonna read the output of that and then if we're gonna assert, it looks the way that we expect it to be and that means the ping command succeeded. So that's how a test worked. That's all great, but that's all in Linux. So I'm gonna switch a little bit and I'm gonna talk about the variable tool chains and applications on the Octave project. So this is, if you wanted to run an example of a bare metal application using the Octave project, these are the following steps that you have to follow. First you clone the Pocky repository. You see the into Pocky. This is all normal, this is what you usually do to build the Linux. You source the environment and then you add the meta skeleton layer which comes from the Pocky repository as well. You can either use a BitBitLayers command or you can just add it to the urbblayers.conf. Either way would work. And then after that, in this case I'm using the QMORM64 machine. So I'm passing that to my local.conf and very importantly, I'm passing the tc.leafc variable, bare metal. The clarion tc.leafc has bare metal and I'm telling the build system that I'm not gonna use the C library, it's just gonna be a bare metal device. And then after that you can use the BitPick and then bare metal hello world which is what the recipe is called. It's on the meta skeleton layer. You can look at it, it's quite simple. After that, after you're done building you can just run QMU just as you would with the Linux system. And at the very end you'll get a, now once QMU runs you'll get a string, a hello world application in this case is hello open embedded. So that's great. That's all you need to do to run a sample bare metal application using the Yocto project. Obviously all dependencies, all the tool chain, everything is built there. It just works out of the box. Some interesting targets you can do if you're not doing the bare metal hello world is you can use the bare metal tool chain, target sorry, which will get you an SDK. If you do have an application that has dependencies and you do bare metal hello world which is my application, that's the populate SDK. You'll get an SDK along with the dependency for your application. So that way people can develop on top of that if you want to. And one very important thing is that if you, instead of doing TC leapsy equals bare metal, if you do TC leapsy equals new live, you'll get the new live C library, which without going to a lot of details, it's a C library that's for embedded devices. Again, at a very high level, it contains a lot of weak symbols for the main functions that you would need. And then the libgloss part of the new live source code will have the BSP part of it. So registers and everything would be declared there. So the weak symbols will allow a new live to link against your binary. And then libgloss would override those things. And then you'll get the actual result on your bare metal application. So this is how it looks like. Again, you clone the repository, you see the into the repository. And then you do bid-bake bare metal hello world. The build system just works the same as it does with Linux. And I'm gonna go back, this goes too fast. So yeah, it parses the recipes and metadata. At this point, I've already built it. So this is building from estate cache. And that's why it's so fast. You can see that it's executing the different tasks for my specific application. It actually generates an RPM. And if I poke around the deploy directory, I can see in my deployed images in QMU64, I get a binary, an L file, a manifest, and a QMU boot file, boot.com file. The QMU boot.com file is quite important because it's what allows us to just run QMU afterwards. Even if you were building a normal Linux distribution with this, you'll still get that QMU boot.com. And it just tells bid-bake how to run QMU with arguments to pass, for example. So for example there, I'm running run QMU. And the system already knows what to pass because of that file, which is generated automatically. You don't have to take care of that. There's a class in an open embedded that allows you to the bare metal applications to get this. You can see, for example there, you can see the full command. You can see I'm passing, I think 128 megabytes of RAM, 256 actually. You can see the CPU that I'm using. And well, at the very end, you can see that I do get the string, hello, open embedded. So that means my application is running properly. It was built and run properly. Plus, if you wanted to get started, that's how you get your bare metal application running on QMU RX64. So let's say you wanted to do something a bit more interesting with a bit more features. And you want to integrate that with the testing infrastructure that I mentioned before from Linux. So what I did is I went to the QMU source code and I got the addresses for the UART, for the RAM, for the kernel. And then I went to the developers manual for ARM. I can see that my device is using a UART PL011 device. And from there, the important part is what I'm putting there on that table, which is the data register, the flag register, receive status, and then the error clear register. So I need those addresses because I'm gonna create my application. Some of these are already using the bare metal one. I'm gonna show you in a little bit. And you also need the startup code and the linker script, which a lot of people are not familiar with those things because you usually get those things from the C runtime. So on Linux, you don't have to worry about those things. You get them for free. On here, you actually have to create those and let the system know how it's supposed to run your code, basically. This is actually the code for the application that you saw. So it just declares where the UART address is and it has a very simple print UART0 function, which basically just passes that character to the driver until it hits the end of the string. And then I have a C entry function, which calls that print UART function and then the argument is hello, open, embedded. Nothing too fancy in there. Do note that there's no main function on my C program, in this case, and that's on purpose. In this case, I'm using the C entry function as my main function. This is the, you can see the linker script on your left and you can see the startup code on your right. These are coming already from, if you do the bare metal hello world, you get these as an example where you can add on top. So first, without like, at a high level, I got the RAM address from the QM source code and then I know that if I'm passing the dash kernel argument to the QMU, it adds 0x100 to it. And then the linker script will make sure that I get my binary organized correctly. The startup, I'm gonna get the text section from my startup.o file, which is generated from the one on your right and all the other sections there, text, read only data, data BSS, and then I get 4k of stack at the end. And then the startup code basically just tells it that if you can, I think you can see my cursor. If you look here, it's where I'm jumping to the C entry function. So that's how it's gonna know it's gonna have to jump into this function instead of the main one. You could name it main, it's not a problem, but in this case, on purpose, I name it differently. So you could see that you have to change that. So that's a linker script, that's the startup code and that's the actual code. And again, if you wanted to do that, if you wanted to run that, you'll get the hello open embedded string at the end. And this doesn't have to be on QMU. It is on QMU because it's simple, but it just as easily can be on a hardware device. So I am gonna stop sharing for a second and I'm gonna switch my share screen to a terminal. Give me a second. Great, my application is not showing up. Oh, there you go. Okay, I hope you can see my screen. I think you can. All right, so this is my terminal. I'm on my Pocky build directory. And again, if I do bid, beg, bear, metal, bear, metal, hello world. In this case, it's not gonna build anything because I already built, but you can see that that's how you do it. If I go and poke around in the source code, if I wanted to modify this and do a, again, a more interesting program, which uses a bit more things than just printing a string. What I want you to do is make it play well or play nice with the infrastructure that I showed you before, which is expecting a login prompt. It's expecting to get a user. It's expecting to get a command. So what I did is that I modified the C code. I haven't modified the startup code. I just wanna show you that. That stays the same. I haven't modified the linker script either. Still the same as well. But what I did is I modified the C code. And one thing to note here, very important, is that I'm using standard in string.h. And you're saying, well, this is a berminal device. How are you using it? Well, that's coming from NewLiv. So this time I'm gonna have to use NewLiv as my C library when I'm building. I created this set of macros just to explain what I'm getting from the ARM developer manual. Basically, there are just addresses and a couple of masks that I'm using to read and write properly from the UART. I created a couple of functions. Quite simple. The puture UART function, it's basically got the same thing as the print one that I showed you before. In this case, it's just using properly the flag register TX. But it's printing the characters on the UART. The print UART function is actually a wrapper for the puture UART function. And lastly, I have a read UART function because I also wanna read what's coming for to me from the target. And that's just basically reading. It's not too fancy. And then I have a check command function and my C entry function, I modified a little bit as well. As you can see, it still starts with print UART and then in this case, I switched. No, I'm not using open embedded. I'm using hello open source sum in Japan 2020. And then I get into a while loop in which I'm reading the UART and then checking for a command and stuff like that. So those are the modifications that I have made to the example code. And if I can share this again. Sorry. Okay. So we now have the bare metal application which is very simple. Again, coming from MetaSkeleton, you should know that that's meant to be as an example. Now I have gotten that example and I've modified it to my own needs. And the last thing that you need is to create a test case or reuse one that already existed. In this case, what I did is I reuse one. Well, it's open source after all, so we should reuse things. In this case, I'm borrowing the one from coming from the MetaFreeR2 layer. If you wanted to do what I'm doing, you would clone the pocket distribution, the pocket repo, sorry, you would clone the MetaFreeR2 layer. You add the MetaFreeR2 layer to your bblayers.com just as you did with the MetaSkeleton. And one important thing is to, so on the MetaFreeR2 layer, there's a test called freeR2's echo. And it's very simple. I made some modifications, but what I wanted to do is that my command is gonna be a question mark in a carriage return. And then I am expecting to see success coming from the other end, right? So that way I know, I can assert and I know that my test gate ran properly. So this is all the test for, this is all the code, sorry, on the test. It's quite simple, but it can be used as an example if you wanted to test something. At least it tells me if the system is running properly, it was built and run properly every night. So this is where everything gets wired in together. Remember when I showed you at the beginning, how the test runs on Linux and how you can override the patterns either for when you get a login prompt or the user that you're using and the command succeeded or the command finished. Well, you put these on your local.com or one of your com files and then that's the way you can override it. So in this case, I'm using, again, the machine is my QMARM64. I switched my TC LeapSeat to NewLiv in Server Metal. I still inherit the test image class and I'm declaring the test suites. When it was on Linux, I put it auto and then I put ping in SSH. In this case, I'm using the free articles underscore echo test case and I'm overriding the following things. I'm expecting, so the host will know the target has booted when it sees the open source summit Japan 2020 string. At that point, the target will send the Yocto project user to it. Sorry, the host will send that. The target will say welcome because it knows that user and then every command will be a new line. That's how we will recognize that, right? If I override those things, I modified my test case and I modified my code already. So I got all those things together. So what's gonna happen now on the bare metal case? Again, the red things are on the host, the purple things are on the target. The host will tell the target to start booting. The target will start booting and then we'll print the OSSJ 2020 string. At that point, the host will send Yocto project or YP carriage return and then the target will say, I know you, welcome. So that means we have logged in, at least the system things that, and then at that point, it's when the command will be executed, right? In this case, I'm substituting the command with what's coming from the test case here. I put a question mark in there on CMV. So that's what's gonna happen. It could have been something else, but I just switched, I just chose the question mark. And then at that point, I'm gonna see success because the target will know that and then it's gonna print success. The command will be finished and then the host will be reading the target output and will know everything's fine. So if you wanted to run that, this is how it would look like. So I'm rebuilding the Burmese application because I modified the code, right? So it doesn't take very long, it's quite fast. So I first rebuild it and then I pass the test image task to it using dash C and what's gonna happen in a couple of seconds is that it's running the test image task, as you can see here and there, you can see the results. It ran the free RTOES test and it passed, it ran one test. I got success as one, keep zero, failure zero, error zero. So that's how you test a Burmese, that's how you make your Burmese application interact properly with the open and better infrastructure for testing. Yeah, that's it. I'm gonna be here for a while. If you have questions, please let me know. I don't, thank you guys for attending.