wokwi/rp2040js

MicroPython: Filesystem will not mount

jmdevy opened this issue · 15 comments

rp2040js: 0.14.6
MicroPython: 1.17
OS: Windows 10

Steps to reproduce:

  1. clone rp2040js: git clone https://github.com/wokwi/rp2040js.git
  2. cd rp2040js
  3. npm install
  4. npm run start:micropython
  5. wait for repl: f = open('test.txt', 'w')
  6. prints:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 19] ENODEV

Corresponds to filesystem not mounted: https://forum.micropython.org/viewtopic.php?t=1855#p10444

urish commented

The file system is not initialized by the emulator. If you want to have a file system, you'd need to create it yourself and make it available in a specific location in the simulated flash memory.

For MicroPython on the Raspberry Pi Pico, that'd be littlefs at flash offset 0xa0000, with a block size of 4k and 352 blocks.

What project are you working on?

OK,

So I can use littlefs to create a filesystem starting at 0xa0000 for 352 blocks at 4kB per block.

I have been able to successfully write files that MicroPython can see using the code you provided before: #81 (comment); however, I still get Errno 19 when creating files in MicroPython (step 5).

Here are the steps I use that allows MicroPython see files but still fails on MP file creation:

  1. Set each file in flash using littlefs.wasm
  2. copy flash to emulator flash

Here's the code for step 2:

console.log("Flash FS copy started");

littlefs._lfs_unmount(lfs);
littlefs._free(lfs);
littlefs._free(config);

rp2040.flash.set(flash, 0xa0000);

console.log("Flash FS copy ended");
urish commented

I thought your name was familiar :)

For proper writing to flash, you'd need to implement the SSI peripheral (see section 4.10 of the datasheet)

A workaround would be to patch the flash_range_program function from the bootrom. MicroPython uses that function to write to the Flash.

The details of that function are available in section 2.8.2.1.3.

image

You could also compile your own version of the bootrom, and replace this function with your own implementation.

How did the wokwi simulator do it? I'm guessing the bootrom workaround? I noticed that files can be created without the error.

urish commented

Yes, the bootrom workaround. I was too lazy to implement the SSI peripheral (thought that's obviously the better solution)

Does that edited bootrom code live anywhere? Still not clear exactly what's wrong with flash_range_program link (sorry if it's apparent, not as advanced as you).

urish commented

Don't worry about many asking questions. It's not an easy stuff, took me a while to figure it out too.

Nothing is wrong with flash_range_program. But instead of implementing the SSI peripheral so I could properly "talk" with this code, I replaced this function with a breakpoint instruction (asm("bkpt 27");). Then, the simulator catches this breakpoint instruction, and does the writing to flash.

When hitting that breakpoint, you'll find the parameters of the function in the r0, r1, r2 registers. They'll tell you where to write the data in the flash (addr), how many bytes to write (count), and where to find the data in the RAM (addr).

At that point, all you have to do is to read the data from the RAM, write it to the flash, and return to the caller (by copying the value of the LR register to the PC register).

urish commented

Looking at the bootrom's code, it seems like implementing the relevant part of the SSI peripheral should be too hard as well. It might be even easier than my workaround. If you want to try that and need some pointers, please let me know.

Clever.

I replaced flash_range_program in the bootrom with asm("bkpt 27") then complied and verified it works with the emulator.

How do I figure out what opcode to look for? It should be the last one executed in here, right?

urish commented

This one:

this.onBreak(imm8);

Note that it calls onBreak() with the argument for you, so you can just override onBreak with your own implementation that checks if the argument is 27

Is this about what you meant?

this.onBreak = (code) => {
  // TODO: raise HardFault exception
  // console.error('Breakpoint!', code);
  // console.log(code);
  
  if(code == 27){
      const flashAddr = this.registers[0];
      const ramAddr = this.registers[1] - RAM_START_ADDRESS;
      const count = this.registers[2];
  
      this.flash.set(this.sram.slice(ramAddr, ramAddr+count), flashAddr);
  
      // Copy LR to PC register
      this.registers[15] = this.registers[14];
  }else{
      this.stopped = true;
  }
};

File creation works through MicroPython now but only if I don't set the stop flag. Does this have to do with how I copy LR to PC? Should it be done in the bootrom? I'm not sure it matters if I copy LR to PC if I just don't stop the emulator, but I guess this means I get rid of the potential to use breakpoints in some cases?

urish commented

That sounds right. You can probably rewrite the last line before the else as this.PC = this.LR for clarity, but it'd still function the same. It will probably even if you don't copy PC to LR, I haven't tried but I see no reason why it wouldn't.

I wouldn't worry too much about the potential use of breakpoint. You still have 254 available "codes" for breakpoints. If I remember correctly, GDB always uses 0, so breakpoints in GDB still work.

Do you know if the littlefs.wasm package you made exposes directory functions?

For example, lfs_write_file can be called from JS through

writeFile = littlefs.cwrap(
  'lfs_write_file',
  ['number'],
  ['number', 'string', 'string', 'number']
);

Which I assume relates to this in littlefs

Does that mean I can do something similar for making directories based on this? I assume the way you compiled littlefs with emscripten needs to include some export flag for the function?

urish commented

I made the source code public, so you can have a look: https://github.com/wokwi/littlefs-wasm.

Most of the API is exported, and you can play around with test.js to experiment with different functions.

One day I may even get around to adding a REAMDE file there :)

Thanks, I'll take a look!