yosys icon indicating copy to clipboard operation
yosys copied to clipboard

Please add examples on how to use this module yowasp openfpgaloader

Open ramalhais opened this issue 1 year ago • 7 comments

An example of how to use this would be great! Thanks!

I tried this code:

async function loadModule() {
    try {
        // Dynamically import the module
        const module = await import('https://cdn.jsdelivr.net/npm/@yowasp/yosys/gen/bundle.js');
        
        // Once the module is imported, you can use its exports
        module.runYosys(["--version"]);
    } catch (error) {
        console.error('Error loading module:', error);
    }
}

// Call the function to load the module
loadModule();

but i get this error from the browser:

Uncaught (in promise) RangeError: WebAssembly.Instance is disallowed on the main thread, if the buffer size is larger than 8MB. Use WebAssembly.instantiate, or use the flag `--enable-features=WebAssemblyUnlimitedSyncCompilation`.
    at bundle.js:508:28
    at Application.instantiate (bundle.js:4460:28)
    at Application.execute (bundle.js:504:30)
    at Application.run (bundle.js:534:17)
    at bundle.js:531:46

ramalhais avatar Feb 13 '24 23:02 ramalhais

For the time being (until https://github.com/bytecodealliance/jco/issues/375 is fixed) you must use a Web Worker to use @yowasp/yosys.

whitequark avatar Mar 13 '24 05:03 whitequark

I've been working on integrating the jco updates but it's been a bit complex to test.

whitequark avatar Apr 15 '24 10:04 whitequark

Here's what i did to make it run:

Create index.html:

<!-- Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
These are new security requirements for using SharedArrayBuffer.

You can check in code if cross origin isolation is enabled:

if (crossOriginIsolated) {
    // SharedArrayBuffer is available
} -->

<script type="module" src="main.js"></script>
<script type="text/javascript">
</script>

Create main.js:

// Create a new worker from a JavaScript file
const worker = new Worker('worker.js');

// Handle messages sent from the worker
worker.onmessage = function(event) {
  console.log('Message from worker:', event.data);
};

// Send a message to the worker
worker.postMessage('Hello from the main thread!');

Create worker.js:

async function loadModule() {
    try {
        // Dynamically import the module
        // const module = await import('https://cdn.jsdelivr.net/npm/@yowasp/yosys/gen/bundle.js');
        const module = await import('https://cdn.jsdelivr.net/npm/@yowasp/[email protected]/gen/bundle.js');
        
        // Once the module is imported, you can use its exports
        module.runOpenFPGALoader(["--help"]);
        // module.runYosys(["--version"]);
        // module.runYosys(["-m openFPGALoader"]);
    } catch (error) {
        console.error('Error loading module:', error);
    }
}

// Call the function to load the module
loadModule();

Then serve these files on a webserver: python3 -m http.server

In chrome, install extension CORS Unblock, open a new tab, and enable CORS Unblock option: "Extra Options" -> "Append Headers to Allow Shared Array Buffer"

Access http://localhost:8000

You can see the output in the JS console.

Not sure how i can now use WebUSB and specify the device to OpenFPGALoader.

Hope this helps

ramalhais avatar Apr 30 '24 20:04 ramalhais

OK, i figured it out. Just ignore the bad coding :) The file to be flashed is passed as an argument in filesIn (filename and data) to the module function, and the filename is passed as a normal argument in args.

main.js:

var selectDevice = document.createElement("selectDevice");

// Step 2 (Optional): Set attributes
selectDevice.textContent = "selectDevice"; // Set button text
selectDevice.setAttribute("id", "selectDevice"); // Set button ID

// Step 3: Append the button to an existing DOM element
document.body.appendChild(selectDevice);

        // let filters = [];
        // filters.push({ 'vendorId': 0x0403 });
        // filters.push({ 'serialNumber': serial });
        // navigator.usb.requestDevice({ 'filters': filters }).then(
        //     async selectedDevice => {
        //         await device.open();
        //     }
        // ).catch(error => {
        //         statusDisplay.textContent = error;
        //     }
        // );

let filters = [];
// filters.push({ 'vendorId': 0x0403 });
// filters.push({ 'serialNumber': '123456' });
// filters.push({ 'productId': 0x5678 });
// Each filter object can have the following properties:

// vendorId
// productId
// classCode
// subclassCode
// protocolCode
// serialNumber

selectDevice.addEventListener("click", async () => {

  // navigator.usb
  // .requestDevice({ filters })
  // .then((usbDevice) => {
  //   console.log(`Product name: ${usbDevice.productName}`);
  // })
  // .catch((e) => {
  //   console.error(`There is no device. ${e}`);
  // });

  await navigator.usb.requestDevice({
    filters: filters,
  });

  // const devices = await navigator.usb.getDevices();
  // for (const device of devices) {
  //   console.log("Opening device: " + device);
  //   await device.open();
  // }
});

// Create a new worker from a JavaScript file
const worker = new Worker('worker.js');

// Handle messages sent from the worker
worker.onmessage = function(event) {
  console.log('Message from worker:', event.data);
};

// Send a message to the worker
worker.postMessage('Hello from the main thread!');

worker.js:

async function loadModule() {
    try {
        // Dynamically import the module
        // const module = await import('https://cdn.jsdelivr.net/npm/@yowasp/yosys/gen/bundle.js');
        const module = await import('https://cdn.jsdelivr.net/npm/@yowasp/[email protected]/gen/bundle.js');
        
        console.log("Listing USB devices");
        const devices = await navigator.usb.getDevices();
        for (const device of devices) {
            console.log("Device: %o", device);
            // try {
            //     await device.open();
            // } catch (error) {
            //     console.log("ERROR:", error);
            // }
            
        }

        // console.log("runOpenFPGALoader: %o", module.runOpenFPGALoader);
        // module.runOpenFPGALoader(["--help"]);

        // Once the module is imported, you can use its exports
        // console.log("OpenFPGALoader Scanning USB devices");
        // module.runOpenFPGALoader(["--scan-usb"]);


        const fileUrl = 'next_kms.fs';
        var fileContent;

        await fetch(fileUrl)
            .then(response => {
                // Check if the request was successful
                if (!response.ok) {
                    throw new Error('Network response was not ok');
                }
                // Read the response body as ArrayBuffer (binary data)
                return response.arrayBuffer();
            })
            .then(arrayBuffer => {
                // Create a Uint8Array from the ArrayBuffer
                const uint8Array = new Uint8Array(arrayBuffer);
                fileContent = uint8Array;
                // Do something with the Uint8Array, for example, log its length
                console.log('Length of Uint8Array:', uint8Array.length);
                // You can perform any processing with the Uint8Array here
            })
            .catch(error => {
                console.error('There was a problem fetching the file:', error);
            });

        var filesData = { "data.fs": fileContent };
        // console.log("filesData: %o",filesData);
        const moduleArgs = ["-b", "tangnano9k", "-v", "-f", "data.fs"];

        console.log("OpenFPGALoader Flashing");
        module.runOpenFPGALoader(moduleArgs, filesData);

        // module.runYosys(["--version"]);
        // module.runYosys(["-m openFPGALoader"]);
    } catch (error) {
        console.error('Error loading module:', error);
    }
}

// Call the function to load the module
loadModule();

ramalhais avatar May 01 '24 01:05 ramalhais

You can check working example at https://ramalhais.github.io/openfpgaloader-webflash/

ramalhais avatar May 01 '24 23:05 ramalhais

Hm, I feel like there should be a way to do it without SAB.

whitequark avatar May 01 '24 23:05 whitequark

For the time being (until bytecodealliance/jco#375 is fixed) you must use a Web Worker to use @yowasp/yosys.

Good news: I've tested the changes there so you should be able to use @yowasp/yosys without a Worker soon.

whitequark avatar May 11 '24 08:05 whitequark

@yowasp/yosys version 0.43.36-dev.746 can now be used on the main thread in Chrome.

whitequark avatar Jun 28 '24 16:06 whitequark