 The story so far, I am porting Fusix to the ESP8266. So far I've got the kernel up and running. I have a file system working on the internal flash. I am loading binaries. I'm running user mode code. And I have enough fork in place that it's actually trying to schedule out the parent process. So if we actually run it, and I hit the reset button, so it scans the flash, it runs in it. There's still a lot of file system tracing we're going to leave for the time being. And though I can probably remove that, that's from exec. Where is it? Here we go. These two lines and that line go. So it's starting in it. This is being printed from in it itself. In it then forks to try and run the first child process. Fork will create the new process and try to swap out the parent, which then fails because we haven't implemented that yet. Now, if I quit that, the swap is handled in this file here, which is a copy of physics' built-in generic swap routine. We can't use that. It's not really complicated. We can't use that because our memory map on the ESP8266 is a bit weird. And we have separate blocks of memory for code and data. So we can just make a few minor modifications here. There are three routines. There's swap out new, which writes out the current process to a new swap page. There's swap out, which writes out the current process to an existing swap page. And there's swap in that brings one back again. So let's just re-enable this. This should be set correctly. This is allocating the swap page. So we want to write out the process's uData block. We can use that code unmodified. We then want to write out the user data, which is here. So this is the length. We have defined our symbols here, which describe where everything is. So database and database, data top, data len, and code base, code top, code len. The data len is 64k and code len is 32k. Sorry, no it's not. Data len is 64k. Code len is 31.5k because we needed to steal half a kilobyte from the top of the code area for kernel stuff. However, we write out 32k because the uData block is half a k. So the total area per swap is a nice round 96k. So in order to write out the code, we want to write out... Sorry, let's do the data first, database one. What does the one do? Here's the routine that actually does the work. So we've got swap read and swap write. Page is... I'm not sure what page is. We set this global to it. But it's then not used when actually writing the data. Swap page does seem to be used by platform code. Trying to find a platform that's reasonably up to date. Sadly, Fuzik suffers a bit from BitRod. Page is... Looks machine translated. Let me try... Oh yeah, that is actually compiler output. Let's try NASCOM. Ah, right. What swap page does is when you tell the block device to do a data transfer, you can tell it whether it's going to be written into a kernel buffer which lives in the kernel address space or whether it's going to be written into a user space. And I think that what this is doing is setting swap page to one which is this. Is that telling it that it's supposed to be user data that we're writing directly from user memory into the swap device? This doesn't say there. Target page. I presume so. We don't use pages so we don't care. This is the block number. A block is 512 bytes. So UdataBlocks is one as we know from UdataBlocks is one. So we are going to write all our data which is 64k there. And we're going to write... So the first thing that goes in is the UdataBlock. Then we have the data area. So this is going to be UdataBlocks plus dataLen, BookShift, CodeLen, CodeBase, like so. And let's just enable debug and let's build it and see what it does. Now this is going to be the first time we are using the flash write. So it may go horribly wrong. Whoops, that's not a valid address. Let's see if I can find the top of this. Here it is. So we are swapping out Process 1 which is in it. That's correct. We then hit a fatal exception at this address. That is a ROM address. I've got the ROM disassembly here. It's a very round address which is suspicious. The virtual address is this. That looks extremely wrong. There is nothing at that address. Here we go. Okay, that's a dereference. That looks reasonable. What is this routine? It's very exceptiony. It might have hit a double fault. What is exception 28? This is the... Let me look for 28 in the vector table. Finding stuff is not so great in the docket. So it tells you what the exceptions are. It doesn't give you the vector number here. Right, exception causes. 28 is load prohibited. Okay, that's because it's trying to read that bad address which it's not allowed to do. Okay, that looks more or less right. Let's stick some tracing in. The problem I'm expecting to see if a problem shows up is trying to write the code out. If it uses a non 32-bit accesses that will fail because you're not allowed to read data. Sorry, you're not allowed to read instruction memory with byte or word. Accesses. Let me hit reset again. Too soon. Okay, got it. Oh, interesting. It hasn't got this far at all. So a swap map alloc failed. Right, 91. 93. And then it fails. I bet map has just returned minus one. I forget where that comes from. That looks like my tracing, to be honest. Process.c, yeah, that's one of mine. Right, what's happening is that swap write is failing and is returning enomem and then the routine that's actually called this has ignored the error message because the error code because there's usually nothing you can do about that. Okay, so let's take a look at swap map alloc which is the thing that seems to be failing. swap.c, swap map alloc. So this just keeps a list of... I think no one's called swap map init. It's the platform's job to do so which means that's my code. Trying to find a reasonably new platform to look at. I think this one is being used. Here we go. Right, that's simple to do. We've got a page map init. Remember, I actually set this up so I can search for stuff here. in swapr.c So that's not a way that I see. swap map init. Okay, let's try that then. Scan flash and it fails with a different exception this time. The address is now in our code. Right. So it's... So you've got to 102. Okay, it's tried to write to the disk. It's tried to write a logical block 29 sector 2 which is currently in logical block. And then it all goes horribly wrong presumably because of a bug in our right routine here. And this is the code address where it went wrong. So let's disassemble. Here we go. Although I'm a bit surprised because this does not look like a store... What was that exception anyway? Zero. Illegal instruction. Really illegal instruction. BNEI. Core architecture. This is a perfectly acceptable instruction. So what's probably happened is something else around here has gone wrong. Okay. So what we do is we write... We write out the trace message. We attempt to evict anything that the round robin block pointer is pointing at. Then we copy the data from the original block into the new free block. And that becomes the new block. Raw flash write is one of our routines. I know that flash address is correct because we're using it to read data. So we need more tracing. We got to here. We tried to copy physical block 41 to 0. And then it failed. Block 0, I do not believe, should be a spare block. But it shouldn't actually matter to be honest. It still shouldn't crash. I've been doing some reading up to try and find better FTL systems, i.e. ones that I didn't write. Okay. It's dying somewhere in the raw flash routines. FTL routines are big business because they're used by all the SST and SD card manufacturers. So I haven't found very much that's public. Okay. It's done the read. Now it's failed to do the write. Now I wonder if our prototype is wrong. So let's go and see what the Arduino did. This isn't what I wanted. I do not see a header. Unless it's this. Well, here's our write routine. It's just put A4 into A5, which means that A5 is not a parameter. So that gives us A2, A3 and A4, which is three parameters, which means it's probably the same prototype as read. And we know read works. So it could be something to do with our code here. It's not like this is particularly complex. I mean it's exactly the same code as for flash write, except this is exactly the same code as flash read, except with a write instead of a read. Raw flash write is here. This code gets written by the bootloader to the top of the instruction memory, which goes up to 80000. So we've got the arrays routine, the write routine and the read routine. We have to put them with a little bit of space left. We have to put these routines in the instruction RAM because we can't do this with the flash turned on. So it could be bailing out somewhere because we shouldn't need to flush any kind of cache. We just need to turn the flash execute in place back on again. I mean everything here looks perfectly normal. I will just put some tracing in here even. This is calling kprintf. We can't put tracing down here between the two cache lines because the flash isn't accessible. We can call the ROM routines so we can print single characters easily enough. So it does look like it doesn't want to write, or at least it doesn't want to return from writing. I did find this routine, which I based a fair bit of... Excuse me, I'm yawning a lot. I was up late last night, which I based a lot of the overall runtime on, which is actually using this kind of code. So this is disable reads SPI unlock. We did the arrays elsewhere. We do write. It then does a read. We turn everything back on again. It should be fine. So let's put some... Let's see what this does. A, B, C, D, E, F. It's done the right. But it dies with an illegal instruction the first time it calls kprintf. I think what's happened is the instruction cache, if there is one, has gone bad. So let's try one of these. Let's see what happens. No, that hasn't helped. Wait a minute. No, that's right. I think thought I might be writing into the flash that's used by the ROM, and causing corruption that way. But I can check that by just hitting the reset button. It will then try to reload from ROM, and it does the same thing again. So now I do think having the spare physical block at zero is a bit weird. But of course, like the first... the round robin block starts at zero. Let's just make that explicit. So the first thing that happens, and I assume we went through this the first time this code got run, is we'll have copied the entire sector elsewhere. So if this worked, so we can actually just write the flash. That will put everything back the way it was. And then... So very much not what I wanted. Let's just do that all over again, shall we? Yeah. We could rewrite the flash. That will put the spare block at the end of the file system like we were expecting, and then we can run it again and see what happens. If the file system got corrupted by an earlier crash, then that can cause bad things to happen. This file system is not what you'd call robust. Unfortunately, this means writing a megabyte, which takes a bit of time. It does seem that the raw flash code doesn't like running flash stuff immediately after a write. It was fine with a read, but not with a write. So it's possible that we need to do more flushing of some description in order to get things back into a state where we can run ROM code. We did seem to have done the arrays correctly. And I would expect that cache-read-enable would do whatever is necessary. And we are using cache-read-enable in our startup code to enable everything at boot time. So why would it be problematic here? So let's wait for this to finish. Then we can try it. Hopefully try and grab it before it spews too much stuff. Come on. I am thinking of using SPIFFS as the underlying store. Then I can put multiple files in for various reasons. I think I probably don't want to put one file per block. Okay, it's running. Okay, it did. Do I have the boot message? Yes, here we go. Right, so we start up. SPARE PHYSICAL BLOCK is 0, which is the wrong number. Okay, something is wrong. Out of the box, the SPARE PHYSICAL BLOCK is the last block in the file system. So if we're not scanning the entire thing, this has said 108k, which does not seem right. My math is incorrect. So we have one megabyte worth of logical file system, which is in this file, 1.0 megabytes. The FTL system can store 7 512 byte sectors per block, as a block being 64k. So 3584 bytes per flash block. Therefore, it's this many flash blocks, which is not a round number. Okay, so what is 300 by 3584? What is that? What is 293? Okay, it's not scanning the entire file system, which means that it's not seeing the SPARE BLOCK at the end, which is causing bad things to happen. So let's rebuild our flash. That's fine. The error is cosmetic, which gives us a 1050112 byte logical image, a 1.2 megabyte FTL image, and then we have to write it out again. And I'm just going to fast forward this bit, because I can't be bothered to think of things to say while I'm waiting. Okay, here we go, 798, 99. Ready to pause it. No SPARE BLOCK. That's not what I expected. So here is our image, which is honestly, it's a bit odd. Why is this... Hang on. I was wondering why there's stuff at the beginning of the file system, but where the swap stuff should be. So let's just recreate the entire file. There we go. This is all zeros except for... That's the block number in the FTL data. Somewhere in here, there should be the partition table. I didn't recreate the partition table. So it's overwritten the device node with some file system data, because when it bound the image to the loopback device, it didn't create the partition device nodes because there was no partition table. Okay, file system image. New primary partition. First sector one. Size is 4 by 96 is 384K. New partition two occupying all the rest of the space. There is our partition table. Right. Update the flash. Check to make sure it didn't do anything stupid. No, it didn't. Look at the image. Right, here we go. This is the DOS partition table at the beginning of the file system. Then we've got lots of zeros, which is the swap device. Lots and lots of zeros. Can't be bothered to just go through. Here is the top of the file system. I'm looking at the image. This is supposed to be zeros. So we look at the FTL. Here is the partition table. At the bottom, we have our spare block. So why has it not found it? 1050112 divided by 3584 is 293 blocks. Exactly. What? Okay, so we're looking at all the physical blocks. Okay. This stuff's annoying. Right. So this is the logical flash size. These are the number of blocks in the logical file system. However, we have one extra block, which is our spare, in the physical file system. So this is logical blocks, physical blocks, logical blocks. And this now wants to return the logical flash size, which is logical flash size. And this is physical blocks. Okay. So now if we write this file system that we've just made back and run this code, then it should scan the whole thing and find the spare block at the end. All right. Same deal. I'm just going to fast forward through the flash writing. Okay. Let's see what this does. Get ready to pause it. No spare block. All right. So let's take a look at the numbers. This is the size of the physical flash. This is what flash size here should be. So let's figure this out. 1050112 divided by 3584 gives the number of logical blocks. 293. So the number of physical blocks is going to be 294. So that's going to be 294 times 496, which is smaller than that. That is not right. So it has to be 294 4K blocks for the whole file system. So, okay, let's do this backwards. 128320 divided by 496 is 295 physical blocks. This is my MOOC FTL routine. It's not complicated. It simply reads stuff in seven blocks at a time and writes it out. And then at the end it outputs 4K worth of stuff. I'm not allowed to build this. Apparently I never actually... There you go. MOOC FTL. Oh, it doesn't automatically. Okay, so I should just be able to... Yeah, and that will rebuild MOOC FTL. So let's remove the file system.ftl. Update the flash. Okay, it says it's written 295 blocks, which it thinks it has. But why? There's only 293 blocks worth of data. This is... Yeah, I know what's going on. I think that FEOF isn't behaving the way it normally looks like it does. I think that this will only be set once I try to do a read at the end of the file. Let me just look at the test error indicator. It doesn't say in detail. So this is all pretty subtle. I think it's... When it reaches the last block, it goes through this loop and does the thing. But because it knows exactly how much data to expect, while the file pointer is pointing at the end of the file, it hasn't actually set the end of file flag until the first byte gets read. So it goes through this loop one more time. This then just reads... Well, this will read nothing and return an error. So do it this way. F seek. That gets the size of the file. So that should, I hope, produce a better result. Okay, run it. File system.image. File system.ftl. Hmm, still 4k. Am I getting my F seek and my L seek mixed up? Yes, I am. L seek takes a file descriptor and it returns the new position. F seek takes a file star and returns an error code. So I need to do that instead. Okay, that has produced a better number. Do our numbers actually make sense? 2672 divided by 3584 gives us... 257 logical blocks including the spare block at the end. So that gives us actually 256a logical file system. That's that big. That is not that big. So 112 divided by 358. It should be 293. Thus producing a FTL file which is that long. I didn't want to do that because now I have to create the partition table again. New primary partition. This is the swap partition. Oh, it's being clever. I need to actually set it to the right size. Which I want to be one meg. New primary partition. First partition plus 384k. New primary partition. Partition number two. Rest of the space. Yes. Right. Yes. Update flash. Yes. And that's produced a file. That's that. That's nothing. That's not big enough. It said it wrote 256 blocks. So if you go down the bottom, we see our block full of zeros. Right. That looks like the right number. 1204224. 1204224. Okay. So I need to check the FTL configuration. System.image. That's the right number. Okay. We don't need to rebuild the kernel, but we do need to rewrite the flash. So and another break. Okay. What's this going to do? Preparing for the pause it. Okay. It has found the physical spare physical block. It even looks like the right number. That's just wrong because this needs to return the number of blocks, not the, so the number of physics blocks as opposed to flash blocks. Past it. It's found the file system and it's then not mounted it. Well, let's just try that with the right number. This should be a fast one. Yes. No. Is there actually a file system here? 384K is, it's about that obviously. That looks like a file system. That looks like swap. Okay. That is a file system. So it's failing to mount, which means that it's clearly not look, it's either the wrong data is there or else it's looking in the wrong place. This root dev equals zero is suspicious. I remember seeing that yesterday that it should be using boot device two. Now I wonder whether it was actually booting off the entire file system except I'd never actually cleared the data at the beginning of the file system. So there was a fragment of file system there. So now it's trying to mount the whole thing, which is now empty and therefore we're hitting this error. But we have set the boot device there and boot device is set here. So it should know that we're returning from this. Let me just check to see whether it's building properly. Okay. Yes, it is. Hmm. Root dev does appear to be the wrong number, but we've defined it in config.h to two. So why is it printing a zero? Well, one reason could be that I haven't done a clean recently and it's building with the and that file got built with the wrong set of configuration options because the build system here doesn't really do dependencies very well. So let's just do that and see what it does. Okay. We now have root dev two. It's mounted in it, but it hasn't found it, which suggests that I've writing in it to the wrong place. Yes, I am. It's going into the dev directory. Actually, we have lots of stuff down here for useful commands, but I don't think we're in a place to actually run any SSH is simple shell. So let's just put that in. So we're going to have to reflash anyway. There's not quite enough space for everything. SH is built somewhere else and we haven't built it. So we'll have to do that at some point. Okay. Let's just do those. That's the wrong make file slash. Okay. Let's see what this does. I actually have a book here I'm reading during the flashing process. Right. Okay. That has not actually done anything to fix our exception problem, but it has fixed a whole bunch of other problems that would have shown up and confusingly at a later date. So I will actually keep that line of tracing. So I still want to know why this is failing. So I'm going to do a little bit of reading up and research offline and be back in a moment. Okay. It's a bit later. And I think I have figured out what's going on, which is I don't like that. Let me just pull the plug on that, which is that we weren't waiting for the flash to be idle after the right before turning the cash back on again, which then means that as soon as we call a routine that's in the flash, the execute in place stuff is unable to read it and everything falls over. So with the addition of this call, it does now get further. It does seem to be copying stuff. It's copy 0 to 293, 41 to 0, 1 to 41, 41 to 0, 2 to 41, 41 to 2, 3 to 41, 41 to 3. This doesn't look very ware-leveled, to be honest. Anyway, let's get rid of some of this tracing. Yeah, I pulled the plug on the board because if I just let it spin, chances are it would have been writing gibberish and using up all the flash erasers. So let's connect it up again and try that. Now I should get a better idea of what it's actually doing. Right. It's managed to corrupt the flash. So I'm going to have to write it again. Okay. So at this point, I am now thinking that my FTL routine is not working. So the tracing I got up here looked a bit weird. 35 to 46 and then 46 to 35 again. That's not right. So I am going to do more research and try and figure out how, see if I can figure out a better way of doing it. My logic did actually work the first time. Yeah. Okay. Back in a bit. Okay. So after doing a bit of reading, I have discovered a off-the-shelf and very small FTL library called Dara. Possibly pronouncing it correctly there. Now I could try and fix the existing FTL library. It's not very much to it. So it's probably possible. But honestly, I think that in the long run, it would be easier to use somebody else's because it's most likely to work. A bit of a shame as now I get to throw all this work away. But on the plus side, now I get to throw all this work away. So I've imported the source code in here into kernel lib. It's BSD MIT-ish licensed. So I can import it into physics easily enough. So let's start integrating it. So we need to build it. And this lives in the kernel side of things. That's libdara.c. Journal, map, like so. So what's CHSS? Okay. So that built with no warnings. That's nice. And you notice that there are these undefined symbols. This is the interface by which Dara talks to the outside world. So I need to hook these up to some definitions to actually do the work. This is not going to be difficult. This is not a lot to it. So lib, and I know I'm in the right place. libdara, this is the interface. There is a small structure which defines the layout of the flash. And it wants a page size. Dara will read and write single pages. So that's going to be 512 bytes for us. And there's the arrays block size, which we can use either 4K or 64K. I'm not sure which would be better. And here are the entry points. So we are going to put these in FTL. So the first thing is to define our structure. So that's going to be, oh yeah, we need to actually include the thing, which is in libdara, map.h, I believe is the entry point. Yes. And this is the API by which you actually use the thing. So read and write will be used to fetch and write back individual sectors. Copy, I'm not sure if copy is required. It would be rather nice if I didn't have to, but trim is used to tell Dara that given sector no longer contains data. We can improve performance a lot by adding support for this to the underlying file system layer. And I did already find the code in Fusix where we can put that. Sync syncs the file system. It makes sure that all the metadata is written back. And GC is the garbage collector. There's a number of allocated sectors, maximum capacity. So in order to initialize things, I believe I need to call map init. Okay, right. I've changed my mind. I'm not putting it in FTL.c. Let's just get rid of that so it builds. I'm going to put everything in dev flash, which is kind of where it belongs. So libdara, map.h. And libdara, nand.h. Okay, so this will fail to build because we don't have these functions. Okay, so we are reading. So we call Dara map read. We want the structure that defines the file system. Dara sector t is the logical block number, which is block up LBA. Page t is... No, I'm looking the wrong place. This one. This is the pointer where the transfer is done and a return error status. And this becomes Dara map. Right. Now the error status is Dara nand read. Oh, Dara nand read is a function I need to provide. It's only used here. Okay, so I don't know the difference between the value returned and the error code. Okay, the error code is more detailed data. The return value is just a success or failure. So I believe that should work. What is the error code? Okay, what's the... Okay, what I'm looking for is the set of numbers that correspond to the error code. And I can see that zero is success. So we want to return one on success. If we do that, that should work. And here, where we scan the flash, we actually want to call init. So that's going to be Dara map init, pointer to the structure, pointer to the callback. Wait, what is this? Dara nand, this structure. Wait, this structure defines the nand. And this structure is the working space for the library. So this is going to be... So I wonder how big that journal is. And I also wonder whether it wants to use dynamic memory allocation. I did have a look at it, but I think I might have missed that. I thought it did not use dynamic memory allocation anywhere, but it will need a buffer in order to do its garbage collection. So where is Dara journal init defined? Libdara map, let's see. Journal init. I need to pass one in. Okay. So we're also going to have static, unht journal buffer one sector. There's not a lot of documentation, to be honest. Let's just look to see if there's anything I've missed. Journal init. Well, here's the example. Page buff is a single 512 byte page. Good. So this is Dara map init. Pointed to the nand description structure. This structure does not want to be const, but this one does. Pointed to the page buffer is journal buff. And GC ratio is... What is GC ratio? This will be a ratio above which it will do garbage collection. If it gets more than this much full, it will try and garbage collect to free up empty pages, empty blocks which it can use to put data in. We do not actually know what the value represents. The ratio of garbage collection operations to real right for an automatic collection is active. Yeah, I have no idea what's a good number, so let's just put it right in the middle. And I think that's all we need. We do want to... Dara map capacity. So this will fetch the number of blocks in the logical file system. And I believe that is the front end to Dara. Without the trim stuff, but... Okay, so we need to define the... Oh yeah, we don't need these. And this wants to be like that. Okay, we now need to define the map structure and the interface. So the log2 page size is... A page is 512 bytes, so that is 9. One left-shifted 9 should give 512. Yeah, the number of pages within an arrays block... Well, this is 4K, so 4096. Is log2 a thing? No, 12. The number of arrays blocks is 4K chunks is... Well, this is the physical size of the file system. And I don't know yet how that corresponds to the logical size of the file system. Because we are going to have to write some code to actually create a flashable image. So let's just go with about a megabyte for now, which is 1024K, which is 256 blocks. Oops, num blocks. Okay, so that builds, but does not link. We now need to put the interface for which Dara will actually use to manipulate the flash. So the first one is DaraNandFlashIsBad, which tests to see whether a block is bad. That's an arrays block, which is not supported. DaraNandMarkBad will mark a block as bad, which again does nothing. DaraNandArrays will surprise, surprise arrays a flash block. And all we're going to do here is call rawflasharrays. And this is already set up to work with 4096 byte arrays blocks. We could use 64K arrays blocks, which is also supported. So that is just rawflasharraysB, DaraE None, return 0. DaraNandProg programs a sector, which is again equal to rawflash. RawflashWrite, physical subsector. Okay, we are actually going to change this. Physical is going to be the 4K block. Sector is the subsector, so flash address becomes. So DaraLock distinguishes between arrays blocks, which are 4K for us, and pages, which are 512 bytes, and numbers them both from zero. But I think I prefer using physical 4K blocks throughout. So we don't need to worry about the metadata block anymore. So that is very simply that. So in this code, this is a page number. No, let's not do that. That's a stupid idea. So this is block number. This is page number. So this becomes flash offset is in blocks. So let's put that here so we can change it. So the offset from the beginning of the... This is the offset of the flash partition from the beginning of the flash, plus page times 512. Alright, easy. And these prototypes should go into globals.h. We need to change them like so. Right, back to this code here. And this is going to be raw flash read, raw flash write, page number, and buffer. And then we do the same thing again for read. Once a write takes a const int, read does not. Great. So what's the... Oh, this reads a portion of a page. Interesting. Right, okay. Let us take these out completely and put them here in raw flash. So we no longer need these prototypes because we're not using them anymore. So where are we? NAND arrays. The raw flash arrays code goes here. This entire piece of code gets copied into instruction RAM because we can't manipulate the flash with the flash execute and play stuff turn on. So by inlining the Dara API, we actually make things a bit simpler. B is block number. P is page number. Dara NAND read, you give it a page, an offset into the page and the length. So that is this code, size t offset, size t length. So this is the address of the page plus the offset and we're only reading length bytes. Okay. And we get rid of this and we need to include libdaranand.h. Offset is spelled with a single f. Buffer is data both times. Okay. We no longer want to build ftl.c. Okay. We now need to define Dara NAND copy and Dara NAND is free. So Dara NAND is free. Test to see whether a given page is in use or not. Where is it? It is free. Yeah, the given page. And Dara NAND copy copies one page to another. Now these are dead easy to implement in our platform. The only problem is that we need a 512 byte block to actually work with. So go with internet is free. So the easiest way to do this is to read in the given page into a buffer. And then we scan the buffer to see if it contains anything other than ffs. Now the problem is we need a 512 byte buffer to do this. And I don't know if we can use the journal buffer. So let's have a it is used here. I don't want to have to allocate a new buffer which is just used in these two places. So this is actually looking at all the pages in a given. I think this is safe. We can use the journal buffer for this. So we do... If there is an error, then no, it's not free. Otherwise scan looking for a non-FF byte. The other one is copy. Copy is used to copy a page from one location to another in the flash. And the thing is, again, we need to have a buffer to copy into. Which is in journal.c and copy. I am going to guess that this is probably a pointer to the page in the journal buffer. Used in the garbage collection stuff, root meta. Oh, no it's not. It's on the stack. So let me just have a quick look to see where this structure is used. Page buffer. Only used in journal.c. I am actually increasingly uncomfortable about using this as a temporary buffer. So in both places. Now we can ask the kernel for a buffer. I am not sure that we can actually do this from inside the file system stuff because this might cause things to be flushed back to the file system. But let's have a quick look at the code. Where is it? Processor.c. No, no. I'm looking for... This is call.exe. Where in here we allocate... Here we go. Temp buffer. Allocate a scratch buffer. You see this then frees up an existing buffer. Try to find a non-busy buffer and write the data. Yeah, we can't do that. So let's just create a temporary buffer and use that. That way we know it will be safe. We are wasteful of memory but we actually have quite a lot of memory. We can probably claim back several kilobytes from the ROM code if we need it. Of course the more free memory we have at the end of the day, the bigger the kernel's buffer cache can be and therefore the file system will work. So dara map read dara source temp buff error. If there is an error exit otherwise write. This needs to be dest. Yeah, so complicated. Right, and that has actually made our image. We could run it but it ain't going to work because we don't have a file system in Flash. You know what, let's run it and just see what it does. Right, scanning flash. 66163k is not the number we wanted. So this should turn blocks into kilobytes. Dara map capacity. This looks not right actually. I think we need... Oh, that's the amount of space free. Because we have a complete garbage file system. It's... No, no, it is the maximum capacity. Interesting. I think... Yeah, this is the one that tells you how much usage there is. Let's do this. This will clear the entire map and format the flash. And it's still a garbage number. Number of arrays blocks is 4k. It's too big. We wanted 1 megabyte. That's 1024 kilobytes divided by 4k is... Oh, yeah, I should have multiplied that by 256. Okay, so we have actually told it that there are 256 physical blocks. Yeah, I'm a bit puzzled about what's going on here. The readme is pretty basic. Okay, that looks right. Yeah, I think we will have to put a proper file system in. I'm a bit surprised that Dara map clear did not do what I thought it was going to. Unless I need to call Dara map resume to read the previous state error, error, error, error. So this is intended for use in some platforms where it's perfectly fine. Whoa, that's interesting. Platforms where it's perfectly fine to reset the file system if it's invalid. And this would work for us except we do actually have to have data in the file system before we can boot. So we never want to have to reset the file system. Fatal exception at this address. Zero is an illegal instruction, which is what we were seeing before with when we were doing the programming. And I actually remembered that I forgot to wait for the SPI chip to finish after doing an array. So let's just try that and see what it does. Didn't help. So 401.021.402.1085. This is inside Dara. It has just called Dara and read and has returned. This is precisely the behavior we were seeing, which putting this in sorted. But I didn't think we needed to do that for reads. Okay, it didn't crash this time. So clearly we do need this. It has still reported the wrong file system size. Oh, wait a minute, wait a minute. And no, I want map.h capacity sector. What is a sector? Where is read and write? Okay, yeah, that should be 512 bytes sectors. I thought this might be returning the number of 4k arrays blocks. 256 4k blocks is 1 megabyte. Yes, a page size of 9 is 512 bytes. Does this garbage collection ratio do anything? Yes, it does. I'm not sure why the number goes down when we change the garbage collection value. Okay, smaller values do decrease the capacity, so that's correct. So if I change this to the absolute maximum, what do we get? Intriguing. Well, this is, I mean, it does seem to be working just not as well as I wanted. And there is this mysterious thing going on. 32327 sectors, that seems very, that seems very close to 32768, which is 32k, which is a very round number. So I think this warrants a further investigation. And the way in which we should do that is by implementing, by integrating Dara into mukftl here, we need mukftl in order to create a flashable image containing our file system. So let's do that. This will also give us a rather faster route to test with. But I need to take a break first before I can do that, so be back in a bit. Okay, so let's have a look at mukftl. So the first thing we need is, well, we got away without writing a rule for it, but we're actually going to need one now. So let's just do mukftl, it depends on mukftl.c and also the three, not that line, that line, the three Dara files. Eventually using the implicit rules, I ought to be able to think we should be able to get away with this. Let's see what that does. Okay, it has actually done the right thing. So make comes with a bunch of implicit rules for building C files using the default compiler, in which we are using here relocations in generic L. Right, what's happening here is that these .o files have been built from these files as part of the physics kernel. So in fact, they are ESPE8266 executables. So what we're actually going to have to do, I don't know if that will work. Yep, that is working. Right, so that's going to build mukftl binary with the Dara library in it. It's failing because we haven't done the rest of the... We haven't implemented our NAND layer. So let's just go ahead and do that. We're going to be using the same configuration, but we are going to create a static buffer to put the data into. So we want to make a one megabyte file system, well, one megabyte physical file system. So that gives that much for the... to store the actual data in. This is going to be our flash size divided by 496, the number of raise blocks. I want to include the Dara library, the Dara map.h and NAND.h. And now we actually have to write the... We have to do the implementation of the low-level functions, which should be really straightforward. So let's just copy all that. What we're going to do is assemble the flash file system in memory. So NAND arrays is going to be really simple. We're going to set data plus v times 496. So arrays will just clear a block to ffs. Program is going to be copied to from... read is going to be mem copy to the output data... Oh, you can't call it data. Flash data from flash data plus... This is a page number, so that's 512. p times 512, and that's a p plus offset length. Dara and NAND mark bad is... Well, both of these are a no-op because we're using RAM. Multiple definitions. Mark bad. Well, there is only one, so we need string.h. Interesting. Yeah, the implicit rule is not working for some reason. So let's just do it the old-fashioned way. Like that. Like that. No. Like... We thought that gave all dependencies of a makefile. Thank you, FSF, for not actually including the relevant stuff in the man page. This returns the first dependency, which isn't what we want. See, I always thought that returned the first dependency. One of the reasons I don't like make. Okay, that's better. It is that odd. Anyway, we're not bothering to compile anything to an object file. We're just throwing all four C files at the compiler, and it's fine. This is such a small program that it's not worth doing partial compilation. Okay, we want is free and we want copy. So is free is going to be flashdata plus p times 512. If the byte being addressed is not ff fail, otherwise succeed. And nand copy is uintht times 512. Mem copy pdesd psource 512. Okay, that's because pdesd can't be const. p is redefined. All right. We now have our mucftl building, but it's not actually doing anything because we haven't changed the actual implementation. So what we are going to do is initialize our new map, which is going to be this. Dara, nan, journal, buff, we need a journal buff. We've got a journal buff. We don't need a temp buff. This will wipe the flash. This will calculate the capacity. That was your normal printf temp buff. Okay, so now when we do update flash and it crashes, why does it crash? Well, the easiest way is to make sure we build with debugging and then run this with valgrind and this will give us a stack trace. Dara, nan, read, it's copying to the data from p512 offset, length. That looks right. Page three, offset zero, length 512. So it wants to read the, that's not right, 4099, really. Flash size 1024, 1024 is that. Is it because I forgot to put parentheses around here and it's doing the evaluation in the wrong order? No, it's not. Have I got my blocks and my pages the wrong way around? I don't think so. I mean, this is a block. It is 4k. This is a page. It's 512 bytes. Do I need to fail with an error? But because I have told it that we have this many blocks, shouldn't it be honoring that? Well, this might explain why we were seeing the wrong file system size. I want nan to h, nan to read. Read a portion of a page. Nor to the s or minus one if an error occurs. So it's trying to read page 4099. 4099 by 512 is about 2 megabytes, which is way beyond the size of our file system. Do I need to bounds check this? Let's try that and see if it behaves any differently. If p is greater than flash size, greater than or equal to flash size divided by 512, then the error is... The error is... interesting. There is no error for out of bounds, which makes me think that there actually isn't one. Let's just try... Let's try that and see what it does. I feel like this is very interesting. It's scanning up the file system in very big blocks and has eventually come up with this size for the total file system. Or this many blocks. It's scanning for some kind of boot block, I believe. But I'm a little surprised at the way it's doing it. I have a bit of a feeling that this is wrong. So libdaranand.h, the total number of arrays blocks. Oh, this is the number of pages within an arrays block. Not the number of bytes in an arrays block. So that is actually going to be that many. Right, that's more what we want. Good. So let's just change this. Excellent, we are making progress. Given the garbage collection ratio we gave it, we're getting three-quarters of a megabyte into a megabyte worth of flash. Which is okay, I suppose. Let's just try fiddling with the number and seeing how that changes. 64 gives us less. 255 just the maximum gives us... Okay, it doesn't actually make much difference with capacity. So let's just leave it the way it was and let's actually write our file system. So what we're going to do is for every page, read in the page and then write it using daramapwrite. The sector number is page no. Daras seems to be using block to mean an arrays block, page to mean a physical block, and sector to mean a logical block. So I think technically this should be, we're using the same terminology, this should be a sector. So this should be sectors. If sectors is greater than LBA, print f. The size of the input image is sectors, the total maximum is LBA. So this should be sector number. Data is buffer and error is error. Error t, error equals daraenun. Error is not equal to daraenun, meaning it's being set. Okay, and we lose that. And we want this to go here. And this is just going to write flashdata1 by flashsizebytes. We want it to outf. Okay, so let's run our file. 2051 is greater than, yes it is. Our logical file system is a megabyte. Our physical file system needs to be bigger than that. So let's make this 2 megabytes. Okay, we seem to have missed a few messages, but that has written out our file system to FTL, which is 2 megabytes. And what do we see in it? Zeroes. This is clearly a magic number. I'd expect to see a lot of zeroes. Actually, let's just put a few more bits and pieces in. Put that there. Now we're going to call daramapgc, so that we garbage-collect the file system before we actually write it out. And printfmapsize, this should give the size of the used part of the daraological file system. We'll be able to get more data in. So now when we run it, we see, okay. So we have about a megabyte and a half available. We've used about a megabyte. This doesn't mean the logical file system is going to be a megabyte and a half long because it's still just a megabyte, but that ought to work. So we want to flash it now. So USB tool port, it's UI, USB zero. Write flash to 1, 2, 3, 4, 5. 1 megabyte. FileSystem.ftl. And I don't know what these parameters do, but let's put them in anyway. Okay, it is now writing. Megabyte and a half in a... Megabyte and a half of data in two megabytes of flash is not brilliant. We may want to tweak that. We could use 64k arrays blocks, which this system supports that may give better utilization of the flash, but this will do for now. Okay, so we do need to tell the kernel the right number of file system blocks, which is... We should get mcftl to actually tell us, to be honest. No, it wants arrays blocks to be nand.num blocks. We still need to wait for that flash to finish. And we shouldn't update the flash file while that's running. So that gives us 512 physical arrays blocks, that is two megabytes of flash. So that's going to be 512. That builds. And now we just have to wait for the flash to finish, and then we boot the thing and see what it does. With luck, it will just work. I am not going to rely on luck. I want to see it working. But while we're waiting, let's just disassemble this. Let's take a look at the disassembly.dara. Let's do dara map read. So this is the generated code from the Dara library, which doesn't look too terrifying. Dara nand read. This is the code that's being put into RAM. So we want this to be as small as possible. Fortunately, there's now a reasonable amount of it, but at least it's still fitting. Actually, that's about the same amount as it was before. I'm quite impressed by the density of the code of this platform, to be honest. Because we're not doing much maths on the page number anymore, things are rather simpler. So we are this program. Use arrays. Prolog. Call the ROM function to turn off the excuse in place stuff. SPI unlock. Arrays sector, which is just adding a constant onto the block number, which is there. Three bytes, which is nice. Wait SPI idle. We have to pass in the address of a ROM structure. Read enable. IEQ restore, which is a pair of inline instructions. And return. It actually seems like setting this value is quite expensive. Anyway, that has finished. So let's just make sure our make file isn't going to rewrite the file system, which is not, and see what it does. Okay, that's not so brilliant. Scanning flash fatal exception 29. I don't know what 29 is, but it's there. It's NAND read. I think someone's passed in a null error pointer, which is a bit naughty. I wonder whether it was us. Right, well for a start, this is the wrong set of functions. We don't want to call map in these, because we actually want to look at the underlying structure. So 2512. That's just wrong. And that will not have been helping. Okay, write is called PROG program. That remains map. Does not take a pointer to the data structure. We don't want that one either. Here is a pointer from integer. No, no, so that was all wrong. We do want these. The functions are called PROG. Okay, so read here. We need to give it the offset and length like we did there. Daramap. This is called Daramap. Write Daranand PROG from incompatible pointer type. Yeah, these functions, these actually take a pointer to the NAND structure, because these are the low level functions. So it should be like that. Okay, that's better. Let's see what this does. That is producing the same error. So I have a feeling that we need to do this. I mean, this is a pretty common idiom when you are calling, but when you're passing an error code back and this allows the user to simply pass in a null to say that they don't care about the value. Whoa, whoa, it's working. This is, okay, that failed. Okay, so this has loaded init. It has swapped out the init process. The swap process is slower than I hoped it would be, but it does seem to have worked. And now we have failed with a null pointer exception at this line, which I vaguely recall you get if there's a double exception, because this seems to be buried somewhere inside the exception code. But that has actually worked. It scanned the flash correctly. Let me just fix the boot up message. It's reading data. It's correctly parsed the partition table. We've mounted the root file system. We have loaded and are running init. We have done the swap out here. This needs some work. It would be nice if we could not swap out the entire user memory areas. I believe the kernel does know how much is in use so that we can only swap out bits of it. I think reading is much faster than writing, which makes a lot of sense. It has printed swap out done and then crashed. I suspect the crash is happening. Where is do fork? Here is do fork. We've called swap out. I think something's gone wrong here. For a start, that corresponds to that, which is right, but we haven't loaded our variables back again. That's probably what's wrong. Let's just put these in and give it one more try. Why is this storing the PID? That's because that wants to be the child process. These are all Ls. This is backwards. We've lost A2, which is the PTAB pointer. We should be able to get it from the Udata, which is here. A3 is no longer Udata but we can do this. Why do we want the PTAB pointer? We don't. We want the PID. The PID has been stored at stack pointer plus 5. This should just be load A2 SP5 by 4. That will now return the child PID. I think that will be the parent PID. This is copying the code that happened elsewhere. Let's try that and see what it does. Starting in it, swap out. The data is now doing the code and it fails. This will need a bit more work but that's good progress. We have a much better FTL system. Let's remove our old one correctly even. Oh, we never got around to checking it in. No, wait. Okay, that's better. LibDara, so I think that's fine. KernelProcess.C, what's in there? Process.C. Let's get rid of that and I think we are good. Let's check that in. I think that's good progress. I'm going to call it here. This video is actually recorded in multiple chunks spread over time. Two days actually which you can tell by looking at the clock in the bottom right corner. Hopefully, the next piece of chunk will be to get swapping actually working properly. That will have us running proper programs and maybe we'll even get as far as running the shell. Let's type into the right window. Okay, well, I hope you enjoyed this video and there we go. Please let me know what you think in the comments.