wokwi/rp2040js

Uncaught RangeError: Offset is outside the bounds of the DataView (in browser)

jmdevy opened this issue · 3 comments

Hi,

I am trying to get the MicroPython REPL to work in the latest 0.14.3 version using MicroPython 1.17 in Google Chrome 94.0.4606.61

I have two files for this project that I have heavily modified: main.js and load-uf2.js

main.js

import { USBCDC } from './rp2040js/dist/esm/usb/cdc.js';
import { ConsoleLogger, LogLevel } from './rp2040js/dist/esm/utils/logging.js';
import { bootromB1 } from './bootrom.js';
import { loadUF2 } from './load-uf2.js';

console.log("Started");

const mcu = new RP2040();
mcu.loadBootrom(bootromB1);
mcu.logger = new ConsoleLogger(LogLevel.Error);
loadUF2('rp2-pico-20210902-v1.17.uf2', mcu);

const cdc = new USBCDC(mcu.usbCtrl);
cdc.onDeviceConnected = () => {
  // We send a newline so the user sees the MicroPython prompt
  cdc.sendSerialByte('\r'.charCodeAt(0));
  cdc.sendSerialByte('\n'.charCodeAt(0));
};
cdc.onSerialData = (value) => {
  console.log(value);
};

mcu.PC = 0x10000000;
mcu.execute();

load-uf2.js

import { RP2040 } from './rp2040js/dist/esm/rp2040.js';

export async function loadUF2(filename, rp2040) {
  fetch(filename)
  .then(res => res.blob()) // Gets the response and returns it as a blob
  .then(blob =>{
    var file = new FileReader();
    file.readAsArrayBuffer(blob);

    file.onloadend = function () {
      var fileData = new Uint8Array(file.result);
      var fileIndex = 0;

      while(fileIndex + 512 < fileData.length + 512){
        const dataBlock = fileData.slice(fileIndex, fileIndex + 512);
        const block = decodeBlock(dataBlock);
        const { flashAddress, payload } = block;
        rp2040.flash.set(payload, flashAddress - 0x10000000);
        fileIndex = fileIndex + 512;
      }
      console.log(fileIndex)
    };
  });
}

The project structure is as below (in case that helps)
image

Upon accessing the page I can do some debugging and see that all the uf2 blocks are decoding just as in the node example, but I end up with this error:
image

Uncaught RangeError: Offset is outside the bounds of the DataView at DataView.getUint16 (<anonymous>) at RP2040.readUint16 (rp2040.js:329) at RP2040.executeInstruction (rp2040.js:739) at RP2040.execute (rp2040.js:1494) at rp2040.js:1497

Any ideas why this error happens in the browser but not in the provided node example?

urish commented

Seems like you are starting the simulation too soon - before the UF2 file is fully loaded.

loadUF2 is an asynchronous function, so the code should wait for its completion before starting the simulation.

e.g.

loadUF2('rp2-pico-20210902-v1.17.uf2', mcu)
  .then(() => {
    mcu.PC = 0x10000000;
    mcu.execute();
  })
  .catch(console.error);

When you get something that works, perhaps we can link to it from the README

Although the code you provided didn't fix it (don't know why yet) the explanation did. Setting a timeout that executes code 1 second after page load worked.

And yes, once/if I get a refined version going I can provide an example project

urish commented

The reason it didn't work - you need to add an await before the fetch in your loadUF2() function. Otherwise, it doesn't wait for the async operation to complete correctly. You'd also need to wrap the fileReader with a promise. But instead of using a fileReader, you could actually just get the arrayBuffer right away from the fetch() response, further simplifying the code.

This is how I'd write loadUF2(), also replacing the then() syntax with await:

export async function loadUF2(filename, rp2040) {
  const res = await fetch(filename);
  const buffer = await res.arrayBuffer();
  const fileData = new Uint8Array(buffer);
  let fileIndex = 0;
  while (fileIndex + 512 < fileData.length + 512) {
    const dataBlock = fileData.slice(fileIndex, fileIndex + 512);
    const block = decodeBlock(dataBlock);
    const { flashAddress, payload } = block;
    rp2040.flash.set(payload, flashAddress - 0x10000000);
    fileIndex = fileIndex + 512;
  }
  console.log(fileIndex)
}

Overall, I'd suggest learning about how promises work in JavaScript. Spending half a day on this subject will save you a lot of frustration later!