wokwi / rp2040js

A Raspberry Pi Pico Emulator in JavaScript
MIT License
384 stars 40 forks source link

Implement core1 #112

Open mingpepe opened 1 year ago

mingpepe commented 1 year ago

109

Test ok with pico-example code/multicore

Interpolator/Divider/Spinlock are not tested or implemented since I have no suitable code to verify.

urish commented 1 year ago

Thank you!

These is a pretty list big of changes, so it'll probably take me some time to go over everything.

Regarding Interpolator/Divider - as far as I remember, the only thing that we need is to have different one per core, so if two cores are using the divider / interpolator at the same time, they don't interfere with eachother:

image

So ideally, we'd want a set of Interpolator per core. It might make more sense to also move the divider logic into its own object, and then it'd be easier to create a dedicated copy of it for each core.

mingpepe commented 1 year ago

If interpolator and divider work properly for single core, I will work on it later to let each core has individual function.

But I do not have multi-core code about them to test, only single core(divider, interp).

urish commented 1 year ago

Thanks again! Is this ready for review or not yet?

mingpepe commented 1 year ago

Yes, it's ready.

urish commented 1 year ago

Now I just need to find a few hours to go through it :)

mingpepe commented 1 year ago

@urish Do you have time to check this PR?

urish commented 1 year ago

I added it to my todo list, I will make an effort to allocate some time this week or the next week.

Ideally, I'd also love to have the multicore test running as part of the CI. I've set up a GitHub action to run MicroPython tests two weeks ago, and hope to get some pico-sdk examples to run as part of the CI too.

c1570 commented 1 year ago

Edit:

c1570 commented 1 year ago

If I understand correctly, the bootloader leaves core1 in waiting state, but emulator-run.ts jumps into crt0 immediately, bypassing the bootloader. This makes core1 reach the code at https://github.com/raspberrypi/pico-sdk/blob/f396d05f8252d4670d4ea05c8b7ac938ef0cd381/src/rp2_common/pico_standard_link/crt0.S#L315 where things go wrong (possibly because core0 is still copying things to RAM at that point).

Adding mcu.core1.waiting = true; to emulator-run seems to fix the issue. :)

urish commented 1 year ago

Thanks for reviewing this @c1570! Unfortunately, I hadn't had a chance yet to review it myself.

I got the Pico SDK CI to work: https://github.com/wokwi/rp2040js/blob/master/.github/workflows/ci-pico-sdk.yml, which can be a good starting point for adding a multicore test to the CI.

c1570 commented 1 year ago

@urish How about just adding the needed example .hex files and use those? This would make tests easier/faster to run for users and allow testing things like -DPICO_COPY_TO_RAM more easily.

mingpepe commented 1 year ago

@1570 Where is the code to bypass bootloader in emulator-run.ts? I did not ever use -DPICO_COPY_TO_RAM before.

c1570 commented 1 year ago

@1570 Where is the code to bypass bootloader in emulator-run.ts? I did not ever use -DPICO_COPY_TO_RAM before.

https://github.com/wokwi/rp2040js/blob/6344182c491904aabe1051c87ec89615491e94b1/demo/emulator-run.ts#L21 jumps to 0x10000000 (the hex file contents) but the bootrom is at 0x0 basically (if I understand correctly).

mingpepe commented 1 year ago

@1570 Where is the code to bypass bootloader in emulator-run.ts? I did not ever use -DPICO_COPY_TO_RAM before.

https://github.com/wokwi/rp2040js/blob/6344182c491904aabe1051c87ec89615491e94b1/demo/emulator-run.ts#L21

jumps to 0x10000000 (the hex file contents) but the bootrom is at 0x0 basically (if I understand correctly).

Thanks.

mingpepe commented 1 year ago

@urish. To support for 2nd core with GDB, I just slightly traced code. Here are some issues to deal with.

  1. Which gdb server configuration do we need? a) The official config use smp and hwthread which shows only 1 TCP port to gdb client b) Expose 2 TCP ports to gdb clients => Two instance of GDBTCPServer with different port in our case.
  2. Currently timers in RealTimeClock do not distinguish for which core owned. When one core is stopped, I'm not sure if it's timers should keep going or should be paused (like DMA).

For gdb server config a), when 1 break point hit, two cores are stopped (I do not know if it can config to let another core free run). I think in this way some gdb commands currently not implemented (but I do not know the details about gdb command yet). This may be complicated compare to config b) but let me debug my code with VSCode extension which is good for me. For config b), I'm not sure if VSCode extension can achieve connecting 2 gdb server.

urish commented 1 year ago

Thanks for looking into this! Here are my thoughts:

  1. Which gdb server configuration do we need? single GDB port, multiple hw threads.
  2. From my experience, pausing everything (both cores & all timers) makes life much easier when debugging. You want to have everything as predictable as possible, so if you set a breakpoint and step over, you want to get consistent results regardless of how fast you're stepping over.

So config a), and stop everything when we hit a breakpoint (or debugger sends a break command).

mingpepe commented 1 year ago

If 2 cores always stopped simultaneously, do we need multi-thread to implement this feature? As I understanding, one thread is enough.

urish commented 1 year ago

You want some way to show GDB the state of each core - so it would show you the call stack of each core, and let you step the core that you are interested it.