Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MicroPython: Filesystem will not mount #88

Closed
jmdevy opened this issue Nov 19, 2021 · 15 comments
Closed

MicroPython: Filesystem will not mount #88

jmdevy opened this issue Nov 19, 2021 · 15 comments

Comments

@jmdevy
Copy link

jmdevy commented Nov 19, 2021

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
Copy link
Contributor

urish commented Nov 19, 2021

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?

@jmdevy
Copy link
Author

jmdevy commented Nov 19, 2021

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
Copy link
Contributor

urish commented Nov 19, 2021

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.

@jmdevy
Copy link
Author

jmdevy commented Nov 19, 2021

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

@urish
Copy link
Contributor

urish commented Nov 19, 2021

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

@jmdevy
Copy link
Author

jmdevy commented Nov 19, 2021

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
Copy link
Contributor

urish commented Nov 19, 2021

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
Copy link
Contributor

urish commented Nov 19, 2021

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.

@jmdevy
Copy link
Author

jmdevy commented Nov 19, 2021

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
Copy link
Contributor

urish commented Nov 19, 2021

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

@jmdevy
Copy link
Author

jmdevy commented Nov 22, 2021

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
Copy link
Contributor

urish commented Nov 22, 2021

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.

@jmdevy
Copy link
Author

jmdevy commented Nov 30, 2021

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
Copy link
Contributor

urish commented Nov 30, 2021

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 :)

@jmdevy
Copy link
Author

jmdevy commented Nov 30, 2021

Thanks, I'll take a look!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants