 Hello, folks. If you're watching this deep dive after the fact, please check out the description for time codes. These streams tend to be about two hours, so sit tight. Check out the links in the description for getting around the video if you're watching this after the fact, or just sit back and relax. So we'll take a few minutes here before we get started, and then we'll get the show on the road. I'll say hi to folks, too, as they pop in. Let me know how the audio is. I'm using the lapel that I failed to use last week. Hi, HamzLab. Hi, Tutu. 3-1 Puppy. Hello, Doctor. Hello, Minnesota Mentat. Hi, J.P. Constantinow. DCD. Hello, hello. Oh, Todd Bott was earlier. Hi, Bruce. Audio is great. Awesome. Hi, Piata and Pierre. I figured out what my audio problem was. It's really, really silly. When I had added it to, oh, did I get a little hot? When I had added it to, audio is good. Thank you, Jordan. Hi, Andy Roberts. When I had added it to OBS last time, I had added it just for a single scene, not globally, like the other settings were. So that's why when I clicked to a different scene, and the audio disappeared. So we could just do a quick check here and say, like, oh, here's my desktop. And you can still hear me, hopefully. And then we'll go back to the camera. That, that, that, that. Checking to see if Twitter Live works. Hopefully, PT. Yeah, silly problems are the best because you don't have to work hard to fix them. Oh yeah, 100%. Hi, Frostasia. I think I said that right. Hello, unexpected maker. I think it's going okay. The problem I wanted to talk about today is I have not solved it and I am not super confident I will, but we'll see. Hi, Dave. Mark Hart. Is that right? We're just getting settled in. Oh, we should reposition cat cam so that the cat is actually in the camera or into the shade. Happy Friday, Mark. Close enough. Sorry about that. Thanks, thanks, PT. I think it's time to get going to housekeeping. So hello, everyone. My name is Scott and I work for Adafruit on CircuitPython. Adafruit is an open source harbor and software company based out of New York City. I am in Seattle though. I work remotely for them and I work on CircuitPython which is a version of Python designed for microcontrollers. So microcontrollers are these little inexpensive computers. They come on boards kind of like this. This is how Adafruit makes money and how they pay me. If you want to support me in Adafruit, you can go to adafruit.com. Find some microcontroller stuff there that looks pretty interesting. So that's the high level. If you want to chat with me and a lot of other folks outside of this stream, we do have an Adafruit Discord server which is like a chat room, chat program thing that's really great and we've been doing that for years. So you can go to the URL adafru.it slash discord to join the discord. We'd love to see you there and we're there all week. Hi Keith, glad you could get home just in time. These deep dives happen every week, normally Fridays at 2 p.m. Pacific here on the Adafruit Discord, or the Adafruit Streams, not Discord Streams. It typically goes for two hours or more. Occasionally it'll be shifted to Thursday and questions are welcome. But I should also note that next week I am not straining because we're going for a long weekend and I want a week off. So next week no stream, so we'll be back in two weeks. Actually I should double check, but keep an eye out on the blog. It should be next two weeks from today that we'll stream again. Last up, the housekeeping, the cat that's in kind of the shade here that ooh, you can see my hand. He is epileptic, so occasionally he has seizures. He's been doing really good, so I don't expect it to happen but just a heads up if I'm not here or paying attention to something in the room that's probably what I'm doing. I am awaiting my iPhone delivery so I might run down and get that if it comes during this stream, we'll see. Just don't want to sit now back for hours. So I'm excited about that happy iPhone day. Hello Dave. Hi Skir, on stream unboxing, we can unbox it. If it comes, I'll take it out of the outer box first. I doubt it's gonna be that exciting, but you know. Whatever, we might wanna do it after diving deep into this Cortex A stuff. So I think that's all the housekeeping. If folks have questions, feel free to ask them. Now's a great time to do that before I get deep into something. Doctor says, oh now you can use your old iPhone as your stream device and that has no personal info on it. The problem is, is that I'm told them I would return it to get some money back for it. So I'm gonna send my old one back in. Oh and heads up, FOMI guy will be streaming this time next week, although it will start an hour later. So FOMI guy will step in and do a deep dive programming stream next week so that will be awesome. Yeah, Bruce S says, Apple thinks unlabeled boxes fool people, not really. Yeah, yeah I used to keep devices, but I have an iPhone 5 on my desk here that needs a new battery and I just like, I still haven't gotten around to it. So I decided with this one that it was like, you know, I think the best shot this phone has to be used more is to actually just return it to exchange it, to trade it in. And when they do the trade-ins and they sell them as refurb, they redo the outside shell and they replace the battery. So they're gonna be a lot more functional than a battery that's been in use for two or three years. So I think that's a better way to do it. 50 go-kart races sounds like fun. Yeah, the return box actually comes, I think, on Monday. So I've got a weekend to get it transferred over. I don't have a rush. Saul asks, first time watching your stream, what are we learning today? Oh, great intro or a great segue. Thank you, Saul. So today, last week we started working on Circuit Python on the Raspberry Pis. This is the like, Broadcom chips that are on the Raspberry Pis like zero through four. So these are different than a lot of, this is different from whatever Circuit Python runs on today. So microcontrollers are typically what Circuit Python runs on on and most of them are what we think of as Cortex-M CPUs and they're designed to just really run one thing. But I thought it would be really fun to bring Circuit Python up a tier into the Cortex-A series of CPUs. A stands for application. And so the main defining thing about the Cortex-As versus Cortex-Ms and basically microcontrollers versus application CPUs is that they have this unit called the memory mid mapping unit, MMU. I don't know what actually know what it stands for. But what the MMU does is it redirects addresses from what they call virtual addresses. So when you have a program, say you start Chrome up on your desktop, it gets its own virtual address space. But we know that Chrome memory is shared amongst all other applications. So there's this unit that is the MMU that takes individual virtual address spaces for different programs and maps them to what are called physical addresses, which are the addresses that I think I'm more used to because those are the ones that you use on like a Cortex-M. Like in Cortex-M, all you have is physical address space. You don't have virtual addresses. So the defining characteristic of an application processor, memory management unit, thanks HamzLab. One of the defining characteristics of application level processors is this memory management unit that allows the OS to segment different parts of memory off on each other. Ravza asks, if you learn original Python on a PC, is it then easy to go from there to CircuitPython? Yes, it is. It's easy to go either way. There are some more advanced things on CPython that you won't be able to use in CircuitPython, particularly standard library stuff, but the language itself, if statements, for loops, classes, functions, all that core stuff is gonna translate right over. You'll definitely get a boost from that. Okay, so going back to Saul's question and what I wanna poke at today is I think on the stream last time we got QMU running our code, but it was crashing. And I made progress on that. I figured out how to catch the exceptions and be able to see what the exceptions are. And yeah, so I got further and I can try to show where I'm at, but the problem was is that when I got it working, so I got, what I was trying to do is get the UR output working. So the serial debug output working and then once I got that working for tiny USB, I'd be able to start fixing tiny USB. I got that working in QMU. So QMU is an emulator that runs on my computer and pretends to be a Raspberry Pi 3. So it was a really good first step to get that working. The problem is that there's a particular check that the real hardware does that QMU does not do. So awesome. I enjoy anything involved with IoT devices. Yeah, yeah, Raspberry Pi is very common in IoT for sure. We're not gonna be talking networking it all the day, but we're gonna get really right down deep. Ah, 2231Puppy says QMU is pretty cool. I use it to run Cali Linux on my Mac. Yeah, I've been pretty impressed with it. Bjata says how dot dot dot. All right, so let's go to my desktop. And let's take a look at the code here. I don't even have it open. I just restarted my computer after updating everything. So I'm glad it's working so far. Okay, so primarily we're working in within tiny USB. So this is, how's the smoke today? The windows are open, so it's not too bad. I don't think we actually have smoke here, but maybe a bit. I don't know, it's supposed to get all rainy next week, so it should be nice and clean air because of all the rain next week. So this is, this family C is kind of where I do most of the work. And so bored in it here is what is called from main and it's like the first thing that main calls. So you can see what we're doing is we're just doing pin blinks, which I think I showed last week. What's new is I'm trying to set up the MMU, but ignore that for now. No, you're fine, Bjata. I know I've got a little bit of like sinus stuff going on, but I'm not sure what that's from. Something outside, I think, or laundry detergent, I don't know. Yeah, so we do some pin stuff. We're setting the UART up. Oops, here's a blink routine that I have and I think I talked about this last week. And then we have two prints. We have this UART print here and then we have this print call here. And I was trying to figure out that like, it was kind of like half working, but not kind of working. That was a little weird, but what I figured out and I think the conclusion I made is that this stir length, what I noticed is that it was the problem. So I had gotten it in some form where I was like, all I was calling stir length, I wasn't even calling the UART right and it still wasn't working. So I did some research and I stumbled on this thing that said, Arm V8 has support for what are called unaligned accesses. So if you're saying I want a 32 bit number and the address you give it doesn't start. The address you give it is not at the start of a 32 bit section or like a 32 section of memory so that the like however many bits at the end of the address are zero. That's known as like aligned versus unaligned. Unaligned is where like you get two, say two bytes from one kind of 32 bit bucket and then two bytes from another and they have to squish them together. So Arm V8 supports unaligned accesses. But the MMU has to be enabled in order for that to work. Otherwise, the default memory mapping that happens when the MMU is off doesn't allow for unaligned accesses because it's treating without the MMU on it's treating all memory kind of in the strictest sense. And so it's enforcing alignment which means that this stir length library that I'm using is compiled wrong. So I could recompile it without support for unaligned accesses, so forcing aligned accesses. But instead I thought what I would do is figure out how to set up the MMU so that the aligned accesses would work. The other nice thing about getting the MMU set up is that it means that it will be better about crashing, it'll crash sooner if we ever try to load memory outside of the area that we want. That's one advantage. And then another advantage of enabling the MMU is that then we could potentially enable the caches as well. So these processors go pretty quick but the RAM is on a separate chip and so going to RAM is actually quite slow, a lot slower than the processor itself. So if you don't use any caches between the RAM and the CPU itself, then you're gonna end up waiting a lot and not running nearly as fast as you want. 2231 Puppy asks, what model of Pi will this run on and will it be ARM32 or 64? So I'm focusing on ARM64 right now which means it's Pi three or four. I was just focusing on the four because that's the hardware. I'm using the CM4 IO board for on hardware testing but QMU can only do the Pi three. So I've been kind of switching back and forth and they're pretty close. So it shouldn't be that different between the two. My plan is that once I figure this out, I think I'll be like, this is the peak of the difficulty curve, I hope. So that once I can figure this stuff out and figure out how to get interrupts and stuff working, I'll be able to hopefully get the USB side working and then once I can get it going in ARM64, I don't expect it to be too difficult to get the ARM32s working as well. And so the difference between that is memory and pointer size. And Linux 203 points out ARM32 would open the door for the Pi zero. Yeah, totally. I just had to pick a place to start. I do kind of intend to do all of them. I think that once I get it working in one place, I don't think the USB peripheral, the thing that you actually like programming, I don't think it's different between any of the PIs. I think it's the same across all the PIs. I could be wrong, but I think that's the case. If you run CircuitPython on Raspberry Pi four, is there a need for 32 bits? No, but the 32 bit versions would go, would mean we can run on all versions of the Pi. Not just the Pi four in the three. I could have just done 32 bit everywhere because the 64 bit PIs can run 32 bit stuff as well, but I was wanting to do 64 bit. So that's where I'm trying to start. And so I'm trying to set this MMU up and just having a heck of a time getting it working. But yeah, I think, like I said, I think once I get one of them working, I don't think it's gonna be too hard to get the others working. It's just like, this is my first time in Cortex-A land, which is like, ARM V8A is the architecture. It's just a ton of learning. There's like a lot more complexity. So let's talk about that complexity. Although maybe, let me take a detour first. So I didn't set up the overhead, but let's see if I can't get the overhead going. Let me show you my setup here. And I am hooked to things, so I gotta be careful. But I did get my power supply. And the nice thing here is I can move and the audio should stay consistent. Let's see if that starts up and works okay. Overhead? Maybe not. I did it with Catcam last week. Oh and the cat moved. So you can't even see him because he's like blasted out. He's over here next to the window. Okay, get your sea legs on. Here's the setup here. So this is the hardware setup that I'm testing on when it's not QMU. So we have a CM4 IO module or CM4 board on an IO board. This is a power switch I've got so I can turn it on and off. I've got my USB or my micro SD card reader for setting it up. And then I do have a screen here that I got to be able to look at if it outputs anything. Oh, and then from here, I've got two things. I've got these wires here are for UART to this USB to UART adapter. So that's for debug output. And then these all wires are JTAG. So that goes into here and to the JTAG there. And then this is the new audio box. No Apple today. So that's the setup. The reason that I wanted to bring that up is I actually designed a board to do that. All of the wires that I just showed there. So let me just take a quick detour to Oshpark and look at my projects. So there's this Pi development board V1 and I will give you a warning. If you order this, I have not tested it. So it's on you to hope that it's correct. Oh yeah, share screen, thank you. So here's my Oshpark and here's what it looks like in Oshpark rendering. So this is a board that I made, it's a hat. Well, not an official hat. It doesn't have the identifier or whatever. Yeah, hope that it's correct or bodge away. So what we have here is kind of like on the right-hand side is the 40 pin header. Hopefully it's set up correctly. I didn't actually double check that, I should have. Oh well. I put a STEMI QT on it for testing iSPRT-C stuff. A big JTAG header, a small SWD header. I put it in quotes because it's actually JTAG over this header. So that's like most of the wires, that'll be really nice. And then this is the six wire pin out so that I can plug like my adapter directly into the headers. And then what I did is I added just four GPIO for just like LED debugging like I was talking about last week. So there's four through-hole resistors and four through-hole LEDs that you can put on here. If you don't want to put those on there, you could just hook to these and just read the signals yourself as well. So this is meant to just be like a get rid of all the wires that I just showed you and make it easier to get access and like debug mode into the Raspberry Pis. And this pin out I think should work across all of the Raspberry Pis. So any of the Raspberry Pis with that 40 pin header, I think this will work on that. I haven't tested it so I make no promises but that's the hope. And of course I haven't tested this board officially yet either. So again, order it at your own peril. It is available on GitHub as well. If you want to check my work, I put my board designs under Chickadee Tech, which is my company who Adafruit actually pays. So there's this pie developer board here and I'll link that as well. If you want to check it out, it's all key cad. So pretty straightforward. All the parts you can get from Adafruit except the JTAG, the JTAG header and I have it in an open tab somewhere because I need to order it. But yeah, reach out to me if you like that. But yeah, I like the name Chickadee Tech. I'm pretty happy with that. Okay, so that's the detour. So what have I learned? I think if I remember right, what we were doing last week is the QMU stuff and we were still faulting into some random memory address which is frustrating. Luckily, I figured out my problem. I figured out how to catch the exceptions. Catch the exceptions so I could actually not go off into the weeds. So that is done in this boot S. So I think we showed this before I think I did. So this is like very early startup code and then some macros for doing IRQ stuff and this is largely where things will end up. So they'll end up in a like push everything onto the registers, store the type of entry in X0, store the state of the, so there's ESR, which is the exception. I wanna say status register, but it's not. I think it's something else. And then ELR, this is actually the program counter from where the exception happened in case you actually wanna like return there because that's the thing with exceptions is like they're not nearly as fatal as, not nearly as fatal as interrupts in M0s and but also if they're gonna return like M0s will automatically kind of return back to where you started if you're done with an interrupt whereas you have to kind of more manually manage these. So this like IRQ entry thing is just like storing all the state of the registers and then when you exit it restores it all. So the thing that I figured out was that there's a place that it's set. So here, this is where the, so this loads the address of the vectors and then writes it into the V bar. And I think we tried to do all three last week, but it turns out this EL3 is two, so another thing that is on these Cortex As is that there's multiple privilege levels so that if you're an OS like Linux you run at one privilege level and then run Chrome or whatever like user application you have with fewer privileges. And then if anything, if it tries to do anything that requires privileges then the CPU can actually delegate to the higher privileged code through an exception. So I was trying to set the V bar. So V bar is the vector table address and it's done for every exception level. So there's like three, two, one, zero where zero is the least privilege. I was trying to do V bar EL3 and that wasn't working so I was only trying to EL1, but it turns out that there is actually a little bit of code that runs before us on the Raspberry Pi or on the ARM core that Raspberry Pi provides called an ARM stub and it switches from EL3 to EL2. So when we start running, we're actually in EL2 which means that when we're setting all these settings and checking our status, we need to just look at the EL2 level stuff. Once I got that, we can actually start catching catching these exceptions. So what does that look like? Let's just take a look here. Let's get this back going. So I've gone wild with splitting my terminals. So here we can do make and it's up to date because I haven't changed it. And then I can do a QMU. I'm just gonna rely on my here. And I got the command here so that serial PTY is provided twice. So this is how we can read the UART of this virtual Raspberry Pi. And then we can do TO, not that, TODEV, PTS. And we want the second one. So we'll see this connect and disconnect just like it normally would with circuit Python. And then one of these flags, this lowercase s or uppercase s, I can't remember, tells it to not actually run the code yet. Instead it starts GDP and waits for GDP to connect. So in this last one, what I can do is I can open GDP and I can do TAR, EXT, one, two, three, four. And now we're starting at zero. And I just hit continue. And then control C very quickly because it actually dies really fast and we don't know it. Break, air, hang. I think it's usually where I want to break. So I've got a mon system reset. And then continue again. I'm still not breaking. So I'm not sure why, but we're in vectors. So we could set another break point here and now we break when we hit it. So we look at a backtrace, it's not super great. And in fact, if we disassemble, it's invalid. And this is because the MMU is on, but it's incorrect. And so like once you switch the MMU on, if your settings are wrong, you just basically crash because it can't load the next code to run. DCD points out that I'm using like shortcuts. So TAR is actually short for like target extended remote, whereas I can just do TAR, EXT for that as well. So that's what I'm doing there is I'm doing shortcuts. So the other thing I did is I have been looking at the, so I made this file CortexA.py. This version of GDB has Python 2 built into it, which is not my favorite. I really wish it was Python 3, but I'll take some built-in Python over. No built-in Python any day. So I made this file CortexA.py. What it does is I was starting to read up in the architecture reference, which is like, so there's two really good resources. And maybe I showed these last week, I can't remember. First is this ARM CortexA series programmers guide for ARM V8A. So this is not specific to the particular CPU or a system on a chip, but it's for the architecture. And I think I talked about that last week. I actually ordered this printed out. It's like 300 pages. I was like, I could use a hard copy of this because it's a really good reference, I think. So I should get that maybe next week, hopefully. So this has really good overview of the MMU and kind of how it works. So you get the ARM core, it has an address, it's a virtual address and it requests the MMU to like figure out the right one. So the memory management unit converts virtual memory to real memory or physical memory addresses. And you can kind of see a diagram of that here. And it does this using a thing called page tables or translation tables, depending on what you're talking about. And it's, there are dynamic data structures that map address ranges in virtual space to address ranges in physical space, which is what I'm trying to figure out how to get it set up correctly. And I haven't figured it out yet. One thing that it's showing here is that there's this TTBR0 EL0 register. And this gives the base address for the starting translation table. So we are actually dealing with TTBR0 EL2 because we're just running an EL2. And having different levels of that allows you, when you do a program switch from Firefox to Chrome, you would change just that for the EL0 version. TLB is the mechanism that the CPU actually does the lookup or does a cache lookup on it. We don't need to go into detail of that. So this is a resource that's really quite good. RuseS is so much fun. This is actually not too bad. This is relatively well, like relatively readable. And they have these examples. The thing that is just a doozy is in here, I have this ARM architecture reference PDF. It is 8,696 pages. This has been really wild. RuseS is, I've seen where you have to be careful to have the cache turned off when this is first set up. And Mark says, oh, there's the long one. Yeah, exactly. And so this is super thorough. It's just like really dense. And so one thing I figured out was this ESR register. So if we're just back in GDB and we do info registers, we get a lot of stuff and we can see their values. And we actually get more registers here than we get from GDB over Jlink, which is interesting. But this ESR EL2 register is actually one of the more interesting ones. And let's just, ESR EL2. Let's pull up the documentation for this. Oh, it's just slow. Oh dear. It's a giant PDF. Okay, so look at this, d13.2.38 is the section for it. Oh, it's the syndrome register. Hi, Simon. Virtual memory to physical memory, oh joy. Yeah, trying to figure that out. The cats are doing good. They're relaxing since the cleaners were here this morning. So this is really handy. It doesn't show up here, but there's this ISS2, ECIL and ISS. But ESS normally, or no, EC is exception class. So this is really handy to tell you why you stopped. And the Robert says, another reason to have an underlying RTOS, it could manage the MMU for you. But like, I'm not gonna be changing the MMU dynamically even though I could. Like I just wanna get it set up so that the virtual addresses are equal to the physical addresses, but I can change the settings for the ranges so that I'm able to do unaligned accesses. Which I've seen is called a flat map. And then actually you're totally right. Like Zephyr has some code that sets this up too. It's complicated though, unfortunately. So here's a nice list of exception classes. One to one set up map the full memory, I guess. I do wanna do, it's not necessarily the full memory. Like it would be nice to not include invalid ranges just to like catch errors and problems sooner. But basically, yeah. It's basically gonna be like a gig for RAM and then whatever the peripheral range is. Yeah, so there's this big long list of EC codes. So what I did is I wrote this Cortex A.py file here. And it has this command that I'm hooking into GDB called, it's called arm v8 exception. So what I can do there is when it invokes, it gets the frame, it reads the ESR ELT register. Splits the bits apart into these ones that we saw at the top of the definition. And then it just goes through for all the different ECs and prints out what the reason is. If it's a data abort from the same level, it then goes into more detail. Which actually makes me think that I should take a look at, so yeah, let's do that before I get ahead of myself. So there's lots of registers here that I don't know. But we can see if we do this arm v8 exception, we can say instruction abort from same level. So that's currently why we're crashing. And what I haven't done, and maybe we should do is, so we're here, so we can just say print hello here, for example, and then if we source it again and then run it again, we'll get our hello. Folkology says, I wish Python had a switch statement. So this is Python 2, it's old and it should not even be being used. But Python just added this thing called pattern matching, which is like a super set that allows you to do something like switch. Structural pattern matching? I haven't used it before and it won't work in CircuitPython. Oh yeah, so it's match case. It's being added in 3.10. And we can see here that it's in Python 3.10 as well. Good job, two, two, three, one puppy. You're ahead of me. So it's coming, just not to CircuitPython. It's coming to regular Python. The other trick that I could have done in this file is you could actually use a dictionary. So one thing I could have done instead is instead of having this big long if-elf, I could have just had a dictionary with this being the key and this being the value. The reason that I didn't do that was because I wanted to be able to actually add more detail or more code for each individual one. So that's why I chose to do it this way. And that's kind of what I wanna do here is just like, actually let's see for this instruction abort from same level, let's take a look at and see what other detail we can get about the abort. So it's 1-0-0-0-1. Let's pull it up here. I don't want this, we don't want that, we don't want that, we want this. So let's find that entry. 1-0-0, instruction abort, taking without a change in exception level. So let's see what the ISS encoding for an exception from an instruction abort is. So it can tell us whether we're recoverable or not. It can tell us if the FAR is valid, which is nice. So I think FAR is probably fault address register. Would be my guess. And then, oh here, interesting. Instruction fault status code. So address size fault, translation fault, access flag fault, permission fault. So I kind of wanna print all these out. And I'm gonna, maybe I'll do it as a dictionary. I think that, yeah, they changed the new grammar complete. They changed the mechanism for implementing the grammar completely. Okay, so let's do, this is kind of fun. Yeah, Mark, I think the other reason that, the MMU is not that well documented because only so many things need to deal with it. I wish it was not that way. So let's do instruction abort, let's do that. And then we'll take a look here. So let's just split it by bits so far. So this is the ISS from an instruction abort. It's five bits for IFSC. So that's this IFSC and that's already five bits. And then let's go back. If I was on my big monitor, so the red zeros are like reserved so they don't mean anything. So bit seven here is S1PTW and that's still true. To get the seventh bit, we shift six out. And then, hi Johnny, let's see. Then we care about EA, FNV and set, which I think these are probably all the same, yeah. So these other top ones we don't use, this one we don't use. So I'm starting from the, from there, ISV, FNV, far not valid. Oh, that depends on the, that's right. So the equivalent of this is IFSC, awesome. It's only valid if the IFSC code is 0140s, which is probably the same again. So what we can do is go like that and then let's just print FSC. Okay, so that should give us more info. So let's do what we just did, source it and run it again. And it just did hello and that's because we didn't call it. I am curious. There's a reason that I'm, have you masked nine bits for ISS instead of five? Have I? I think I have, I think you're right. So you're talking about, or no, here's ISS. You're talking about ISFC there and that's the same problem here. So that's nine bits as Andrew points out in the Discord chat. So props to Andrew for finding that book. I bet I did it here too. SRT, I probably shouldn't mess with that. Or no, we can see that the next one's 20. So yeah, that one's wrong too. Buy 2231, can't wait to see the finished project. Nothing's ever finished, not for me at least. Print instruction abort is not defined. Why is that? Oh, I treated it as a global. Okay, so now we have an instruction abort from the same level and we know it's 1,001. So now let's do, I didn't wanna add all of them. So I just added a couple and then let's look it up. Access flag fault level one. Access flag fault. So what does that tell us? Let's confirm that. Huh, access flag fault level one. Okay, this could be the key. I didn't realize I could look at this detail, so that's good. Okay, so we're getting an access flag fault level one and the access flag, where did I see that? I think that's because, so the MMU here is two levels. So I just found this person who does a really good overview of it. So this person actually made a Python tool to generate the translation tables. So if I can't get it working based on this IFSC stuff, then I'll try doing this. There's a good view here that I won't go over but I will link to it. But there is, do they not have a diagram? This video's got some good diagrams. Let's just find it in the video here. I actually found this person answering some questions on the ARM forums too. So let's watch this. So actually our L2 table can be bytes because they are 64 bits of our virtual address. We're going in those different blocks. We therefore have two tables to index between them. Each mapping two megabytes, therefore L1 tables can directly point to one gigahertz. We've got 512 gigs. This one. Okay, oh, close that. I don't want to see it. So what this is showing is that there are different granularities that you can set these settings for the virtual address mapping and stuff. The smallest setting and the most common setting is four kilobytes. And so that's the last 12 bits of your address. That's what this is showing. And then the data structure that you have on top of that to map between the virtual address and the physical address is, you would need this level three thing. But these individual tables have to be the same size as the granular, is what they call it. So this L3 table here has 512 entries that can point to other stuff. And then what you do is you basically have bigger, you have higher level tables that administer larger chunks of memory. So like L3 administers 4K chunks, L2 does two megabyte chunks, and then L1 does a gigabyte chunk. So what I'm trying to do here is I'm trying to have one. So this is from the Discord from Bruce S. This is another way of looking at it. So a set, a table of tables of tables. Yes, but each entry in most of the tables can either say, I cover this entire block like this two megabyte chunk or one gigabyte chunk, or you can say, this chunk that I represent is administered by the sub table instead. And so what you end up getting is from this full address, you take individual nine bit chunks and look up each individual level of the table for each part of the address to get the resulting physical address out. So what I'm trying to set up, what I thought should work is that I'm setting it up to be 63, or not 63, setting it up to be 36 input bits. And so that means that I should only need an L1, I need an L1 table, level one table, and I'm gonna do a level two table as well just so I can get granular around where the registers for peripherals live. I don't think I need an L3 table at all because my entries in L2 are all blocks. So they're larger chunks than the smallest chunk size that I could do. So I'll show, that's enough, let's talk about the code. So here I have a volatile UN64 level one table. It's aligned to 4096, which is 4K, and you need to make sure you're aligned so that it can figure out the addresses. So I've got, hands labs, you're here, you can help me with this. My code is pushed to my tiny USB copy as well if people wanna poke at it. But the access flag is a lead. Hi, Dinkelberg. So we have two tables, we have this level one table, and then we have a level two table, and I put the address range in there. And the reason I picked these addresses is let's take a look at this BCM11 arm ranges. So in this full 35-bit address map, starting from zero at the bottom, we've got one gig, so this 4,000, or four, I don't know, it's a gig. So this first thing is a gig worth of RAM, and some of it's shared for the GPU. Let me zoom in. So this is the physical address, these are the physical addresses, oh dear, of the Raspberry Pi 4 chip. And these are not to scale, they warn you that these are not to scale. So the first gig here is shared RAM, it's RAM shared between the ARM and the GPU, and the GPU starts at a gig and works down. And this makes sense for the Raspberry Pi 4 because it's all of the versions have at least four gigs. Oh, awesome, yeah, thank you, Hams Labs. I won't be around this weekend, but yeah, you can ping me and I'll see you on Monday. And I think I'm actually in this low peripheral mode. So what I have here is I'm trying to do a one-to-one mapping of this first gig, and then this portion here, FC-0040's, up to 1-0-0-0-0-0, is the main peripherals in the ARM local peripherals. And so I wanna be able to say, these ones here are registers and they should be treated like registers, but the first gig I wanna treat like regular memory, and that's where I should be able to do like the unaligned accesses, which is where this all started, right? I want Sterling to work. I want the standard library to work for me. So that's my goal. So what we can see here is that I have the first entry of the level one table, which starts at access zero, and this level one table is a gigabyte of address range per, and then this is the third entry. This might actually need to be the fourth entry, but that doesn't matter. That's not working yet. So what I'm saying here is like for that first gig range, it's normal memory. The descriptor is a block descriptor, so it doesn't point to another table. It just says for this whole block, this is the setting, and then descriptor valid. And then for this entry here, which should be this range C blah, blah, blah to one blah, blah, blah, which covers the peripherals. Then we delegate to another table. That table is this level two table, which then has these entries that are block entries for smaller chunks that cover the peripheral range. Once we set that memory up, we set this MAIR register, which has like more attributes about the settings for each memory range. TCR is control register. TTBR, this is the base address, so here, and I don't think the example I saw didn't use this. And then SC system control register, this is what we actually switched. We set those three, we wait for everything to be changed, and then we read the system control register and flip the lowest bit in it, which turns the M of U on. And then this is where we crash, usually, because it's not configured right. So, trying to remember back, where did I see, so the fault that we're getting says it's a level one access flag fault. There is an access flag. Ooh, do I remember where it was? So, I think it might be on these descriptors. So, if we go, I know folks are cheering for mass transit in the U.S. Okay, so that was really useful, this address or access flag fault. One thing I was trying to do, and maybe I should just do it now, is let's take a look at what happens. I just wanted to comment this out and see if something changes. So, if this doesn't exist, what changes instead? What if it's all zeros and marked not valid? So, let's just try that. We'll kill it, we'll rebuild, we'll rerun QMU, connect back to it, continue, we're at our breakpoint and now let's run our arm exception. And now we have a different thing. So, that's interesting. So, we've confirmed that we're successfully reading that level one table, which is further than I got before. So, let's take a look at this one, zero, one. And if we look at the IFC here, we can say it's a translation fault level one. So, let's go translation fault. Let's add that to Cortex A. Ooh, I feel like I'm making progress. I'm excited about this translation fault. This says, oh, we didn't have an entry there. So, let's source again. And I need to make a repo for this Python script, because anybody doing this low level stuff, it should be really helpful for. Otherwise, you have to pay like thousands of dollars to arm to get a fancy Eclipse IDE thing that will give you information about this. Okay, so we got to the translation fault level. Now, if we enable it back, if we add the entry back, something's not right with that, right? Because if we do the dance that we just did, now we say we're an access flag fault. Can I use from future to make it more Python three, like in the GDB script? That's a good point. I probably could. It's probably not that different. The one thing I noticed is like the prints don't, they print as tuples, because it's print. But yeah, maybe I could import from future. That's a good point. It's really like pretty close. And I'm on the trail of this, so I don't want to get distracted. So, let's look at the entry docs again and figure out what this access thing is. So, why are we getting an access flag fault? This doc is gigantic. TTPR0EL2, this has got a link. Wait, what's in here? ACID address. Common, not private. It shouldn't matter. I lost my place in this doc. How do I get to the documentation for the block descriptor block descriptor, table descriptor, translation table. Here we go. The long descriptor level one and level two descriptor formats. There's upper block attributes, SBZ. So, it is a block entry, lower block attributes. Are you at the right level in configuring the MMU? You mean EL2? Is that what you mean by level? There's lots of levels here. I think I confirmed that I have set the right register to the first table entry because if I change that first entry, like enable and disable it, like my fault changes. So, I think that's, I think I've confirmed that it's, I think that it, I think that's right. Descriptor attribute fields. See, here's AP table, but I want a region attribute. Is the access flag set to writable for each page entry? Does it, why does it need to be writable to just load in an instruction? Attribute fields, I'm not in secure state. AP table zero is reserved SBZ in the non-secure EL2 stage one translation tables. I think it's where I'm at. Shouldn't all RAM be writable at that point? That's true. Were they talking about writable tables or what the tables are pointed to? Well, I'm writing the tables before I switch the MMU on, but I mean, I do want it to be writable. Like they should live in that first gig chunk, the tables themselves will. The other thing to look at actually is, let's look at this PG tool. I was gonna see what it generates, but let me see if I can't just spot how it does this. So this is the video I showed earlier. This guy wrote this library that generates bare metal tables. So I'm curious to see like just what settings they do, MMU, entries per table, generate TCR, inner shareable. So these are TCR settings and I'm not sure I set these correctly. Cause like this fourth value here is one, one, three. That could be the problem. Let's see what values I'm setting. They are in here. So PS, I'm trying to set it to outer shareable. Which should be fine. And then the other two, I think that might be red herring. Cause I imagine that would fault sooner. I imagine that would fault sooner. Exception level differences, PS, normal inner outer right back, FF00FF. What is our mirror value? Oh, register, error yield two. So we have it as 4400. So it's backwards. I think that's okay. We could switch the 44 to FF, but I don't think that's our problem. Oh, there's a shareable flag on the entry itself. And there is an AF. I thought that the access flag was just optional, but maybe it's not. We didn't find it in here, did we? Access flag maybe is just like, you can access it or you can't. Is that what we think? Oh, here is AF. Stage one block and page descriptors. So AF is bit 10. Does it say AF does? The access flag, it says, indicates when a page or section of memory is accessed for the first time since the access flag in the corresponding translation table descriptor was set to zero. In ARMv8, the access flag is managed by software as described. Access flag fault is generated whenever an attempt is made to read into the TLB a translation table descriptor entry for which the value of the access flag is zero. That sounds very suspicious. Okay, so let's add the access flag. I mean, that's the fault we're having. So let's do define MM descriptor access flag and then it's 0x1ULL to the 10. It's bit 10, right? Yes, it's bit 10. Okie dokie. Last up, we need to order that and it goes on these ones as well. That's good. Now we're still in there. Oh, but we hit my breakpoint instruction. Ooh, ooh, ooh, ooh, ooh, ooh, ooh. That's good, that's good. What if we comment out the breakpoint in the while true? Do the same thing again. Oh, we got lots further. Look, we got your output. One small step forward. I actually wonder though, I wanna try this on hardware now because I wonder if this means that we could get this far on the hardware. Making sure that those are the same thing. I'm excited, this is good. That's one step further. Although I wonder what, let's just while we're here, data abort. So I suspect this is because I have the MMU set up wrong. The MMU is currently set up for the pi four not the pi three. So let's try it on hardware here and see how far we get. Okay, this we can leave open. This we can close and we wanna open this to the Silicon Labs controller. This we want to quit. We wanna make again, take the SD card out, pop the SD card in, SD card pops up, copy the image over to boot. I don't know why it said it seemed identical. That should be not true, but let's give it a shot anyway. It's all crashing. Okay, so I flicked the power on. Up here I should be able to do open OCD. Nothing is happening. Address 44444, already in use. How do I switch it? That port is currently being used by OBS. Why? Why are you trying to do that? I can't stop OBS because that would stop the streaming. Oh, it might be in this config file. Tell that port, here we go. Now here, what we're gonna do is we're gonna connect to open OCD and we're in Air Hang. We can backtrace and we can do the R and V exception thing, which looks the same, but we did get any serial output, which I thought we should get. We're in Bordenet family 63, which is you are in it, which is quite early. I thought this is right, but I wonder, I think it would be printing the, let's see what this 101 from the data abort is. So, where's our data abort? Welcome to the deep dive, folks. If you're new here, this is how it goes. Here, you wanna find the DFSC. SC, encoding for data abort, DFSC, translation fault level one. That's helpful. Does it give us any more information? Is that right? 8,696 pages is a very deep dive, 100% David. I saw this and I was like, I can't pay to get that printed. It's too many pages. Just gonna have to search through it. Where information do we have? Like it should be F E. So, like Python, like a gig is 400000, right? That's a gig times two, eight. So, I think my index is right. Is there an access flag on that? Sounds like a lot of paper. It is a lot of paper, so I'm not doing it. Okay, so I think it is zero, starting at zero. And then two and then three starts where we want. Wait, yeah, zero, one, two. Is there an access flag on table entries? Could it be that simple? It must not be. Oh, you know I added the access flag to it. I wonder, oh no, that's right. Cause it's saying that this entry is wrong. So, zero is zero to four. One is four to eight. Eight, two would be eight to C. Zero to four, four to eight, eight to C. So, this is actually, should be a three. Trying to think of why that worked on the Raspberry Pi three, but let's try it. I don't know of a faster way to do this. This is all to get printf debugging working. Is that a weird way to say it? I don't know why it says they're identical. They're definitely not. It gets confused. Fundamental, what's fundamental, Andrew? We got your output. It works. It's alive. Yeah. Printf debugging. Oh, yes, 100%. Yes, yes. I did not know if I was gonna get this far today, so I'm stoked. Okay, so we got just as far. And let's see where we ended up. Tut and knit, data abort from same level. Can you summarize what you think you discovered? Sure. So there, the problem was that I was, there is a flag on each individual entry in the translation table. Four blocks, you have to set the access flag to one. Otherwise, you get the access flag fault, which is what we saw. So I was super close to getting it correct, except for the fact that, except for the fact that it was, I hadn't set the access flag, which apparently is required. And so we got that working, and then what we had to figure out is like the peripheral range was wrong. And then I think the reason that I'm faulting now is I suspect that the range that I have set in tiny USB for where the peripheral is is wrong. So that's the next thing for me to check. But stir length works, which is awesome and means that we can do some printf debugging now. So in DCD Synopsis, yeah, we have it set to this 7E, 7E980. David says 500 pages at a ream and a ream is 5.2 centimeters. So 8,696 pages would be 17.3 reams or a stack 90 centimeters tall. So 35.6 inches. I assume that's what freedom units are. It's like a yard. It's a very big book. Okay, so I think that this address is, it might be wrong or we just need to map it as well. But this is the point where you as tiny, so it's crashing when tiny USB is actually starting to initialize registers for the USB. This is clearly not the right address. So the question is what the right address for the USB is. 7E, how many bits is that? So let's see. So 7E, it's possible it's not in this range in peripherals. If it was 7E, but we're in this low peripheral mode, I bet that's 7E. So let's do, have a good walk, doctor. I feel you about getting dark earlier. Okay, so let's do Python and let's do this for the low peripheral address range. So let's take this, do hex of this number minus 0x7C234. Half if you print double-sided, true. And then it's FC001234, think right? If it's in this 7C range for main peripherals and then it starts with FC. Okay, so it would be FE98. All right, let's see what this does. I think I do have to, I don't think I can just load it, but I could be wrong. Oops, I didn't need to stop GDB. Oh well. I feel accomplished, I can call it a day after the stream is done. Gotta rebuild it. Don't wanna forget that up here. It's not identical, you're lying. You're just so confused. And this is not gonna connect and reconnect because it's going to the converter. Let's sort it back up and see what it prints. Didn't get any print further. Still went way too many print. I agree, too many pages to print. But that programmers manual seems pretty handy. Ah, okay. So we did get farther and we're not actually in a hard fault or we're not in a handler, we're actually in a spin loop waiting for things to reset. Oh synopsis 481. I would call it a weekend. So this is copied from the, are all the other cores stopped? They should be, there's this boot.s and it does a core check. It says if we're not on the main core just hang. That's what this is doing. Cat update, it moved. We're in tiny USB land. What do I do? So we're waiting for it to reset, enable internal fi. Bruce S says looks close. The rest is just details. This is just the beginning. So we're stuck here waiting for it to reset. I wonder if it's clocked. It wouldn't reset if it wasn't clocked. We're on line 481, right? I suppose I could wait. It's possible it advances further. Do I have any prints? I want more prints. Do I not have any debug statements in here? Da, da, da. How do I do debug prints? It's like to you log is how we do it. What's something fun I could do at the end? Somebody asked me a question. So I can stop doing this. Call it weekend. We need a net resetting. Wait a minute. Keep the EE asks what side projects are you looking forward to diving deeper into in the coming weeks? Ooh, that's a good question. It's hard because this has been one of those projects I've wanted to do for so long that it's definitely filling that void, I think. I would, I still would love to do the We Balance Board, UART. It's still sitting on my floor and I still would like to be able to use it. I do need to do the daily workflow stuff, but that's not really a side project. I mean, this weekend will probably be playing around with my new phone. I usually have so much side project stuff, but this is just like, this Raspberry Pi stuff is very much in my interest zone. At some point, I'd like to do an RP2040 Game Boy card again. Would you use the balance board as a head device? No, I want to use the balance board as a scale under the cat's litter box so that I can automatically record the weight of the cats every time they use it. It's like the perfect size to go into a litter box. Did you ever ask Adafruit to manufacture your old board? The old Game Boy board, I did not. I didn't get those new prints. I did not ask about the old Game Boy board because it wasn't working very well. If it worked solidly, then that would be good. But I saw somebody since then, somebody designed the PCB to stick in the back of a Game Boy on its own, so making the PCB thicker and slightly larger, and then you don't have to use the plastic shell at all. I saw that and I was like, that's a brilliant idea. That's something I would want to do as well. I never got the same D51 working as well as I would like to actually do it. Why didn't these logs work? Did I save? I rebuilt. I copied it over. I synced it. What? That is not useful. That's the sound of a UPS person. I know what I could do for the last 10 minutes of this. Oh, the flight controller. I was doing a flight controller too. I did not ask them to manufacture that. I also didn't want to support it. I will be right back. I'll go to Catcam while I run downstairs. You might be able to see me go by. All right, I'll be back. Okay, I'm back. I don't want to show my address, but I do want to show that it should be good. This is the... Are you watching the cat? Did he wonder where I was going? This is the box that it came in. I'm hiding it, but it's actually quite small, which is awesome. Inside that is... Oh, you know what? The overhead is still not working though. I assume I could try it again. I mean, it's just going to be fine. Overhead, are you working? No, you're not. I should have started it before the stream. This doesn't have any... Oh, this does have a serial number on it. Probably shouldn't show the serial number. There's stuff on the back of this box that I am not going to show. That's the front of the box. It's an iPhone 13 Pro, not the Max. So there's two tabs on the back, like this. And then I'll pull off, and then the case comes off. And the case has an indent for the camera. There's the back. I don't know. Is there exciting stuff in here? Interesting, there's this. It's like a screen protector, and it has... Can you see? It has icons to tell you what each button is. Which is interesting. I do have a case for this as well, so I like to put in a case. And then this is the manual things. And then they do give you a C to lightning adapter. But it does not have the power plug, which is totally fine by me because I wouldn't use it anyway. So this in here just has a SIM extractor, one like tiny regulatory thing, and an Apple sticker that I don't know anything with. I think that's about it. Any questions both about me getting an iPhone or any other things that we talked about? Yeah, so I'm upgrading from a 10S to a 13 Pro. Yeah, 10S to 13 Pro. I do have... So I ordered... I usually like the leather cases. So I actually got it earlier this week. So this is just the leather case box. And if you look at my current phone, it patinos a lot. So this is like... It's pretty wild, like it starts super bright like that, but then as you use it, it kind of gets this like dark... It's a leather, so it gets that leather look to it. So I'll just put that in there because I don't like iPhones without cases. So here's the case. And I'll just show that again. I also use a pop socket, but it's the one that I ordered for the new one is not here yet. So hopefully I won't drop it. And I purchased this. I should say... Since I'm streaming it, I purchased this on my own. It was not gifted to me or anything. I'm not going to do any sort of review. But okay, so I'm taking the screen protector off. And here. Weird. Like, gone are the rounded edges. Hopefully I won't get a text, but... Or there's like a size comparison. Just slightly bigger. But you can see the difference in color. I expect this one will be this color later. Cool. I think that's... I got to dig into the next steps. Let's recap, shall we? So we actually did kind of what I was hoping to do today, which is I didn't fully expect to happen. So we took a look at the MMU stuff, which is the memory management unit. We got the... We got the table lookups fixed. We added the access flag. I thought I didn't need the flag. We figured out how to get more information about the instruction fault, which is really handy. And now we have some prints from the actual Raspberry Pi 4, which is great. Although I tried to add some and they weren't working. So who knows how adding those will be. That's probably my next step, is just to figure out how to add more prints to that file. Because that DCD... DCD... What is it? Synopsis. This file. This is the file that we're going to have to change for the particular version of the Synopsis IP that is on the Raspberry Pi. And I think, like I said, I think that's pretty consistent across all of the devices. So that's exciting. So it'll be a mix of debugging. So printf debugging and Beagle debugging, probably to see once we get the peripheral going what transaction it's doing and kind of how far it gets. Ideally at some point it just works with the demo, but obviously we're not there yet. So yeah, those are the next steps. Yeah, my brain is gone. That MMU stuff is deep, but I'm happy we got through it. I have happy we're getting printf debugging going. And I have a shiny new phone to set up. So I think I'm going to call it a little early unless I see any final questions. Thank you all again for hanging out with me for this deep dive. It's been a pleasure. Thanks to the folks who have been advising me on this MMU stuff. It's all very new to me. And I hope that by doing this deep dive we'll be able to get some more information on YouTube about Cortex A-level stuff. Because it's actually quite scarce that I... quite scarce. So I've been looking, but I haven't found a whole lot. So that is that. And if you want to support me, you can do that by going to Adafruit.com and purchasing stuff there. They pay me to work on Circuit Python and this stream and this Raspberry Pi stuff. It's now sanctioned by them as well. It was a carrot for me to get through the Bealy workflow stuff, which I promise I will circle back to. I need to be testing some apps because that's the thing that we need to do next is getting the apps polished up for everything. So next week there's no stream. Remember that. Next week I am out of town, so we're just not going to stream. And I'm just checking my list for any other housekeeping stuff. Oh, if you want to join us on the chat, we have the Discord chat, which is the middle chat here. You can go to the URL adafru.it-slash-discord and that will get you on our Discord server that's available at any point. I also remembered I should go through the forums because I didn't do that today as well, so I'll probably do that as well. And with that, I think that's it. I'll pet the cat and we'll get out of here, and I'll see you in two weeks. Oh, and thanks again to DCD David for taking notes and to ask Patrick W for organizing those notes in a single repo. And the rest of you for hanging out. We'll see you in two.