Skip to content

Commit

Permalink
dual mcu demo
Browse files Browse the repository at this point in the history
  • Loading branch information
c1570 committed Feb 4, 2023
1 parent 71e06fc commit a9d04bb
Show file tree
Hide file tree
Showing 9 changed files with 2,827 additions and 0 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ Other ways of creating LittleFS images can be found [here](https://github.com/wo

Currently, the filesystem is not writeable, as the SSI peripheral required for flash writing is not implemented yet. If you're interested in hacking, see the discussion in https://github.com/wokwi/rp2040js/issues/88 for a workaround.

### Dual RP2040 demo
```
npm install
npm run start:dual-mcu
```

This fires up two instances of rp2040js including some glue code that connects the GPIOs 2-10 in an open collector/pull up bus fashion.
It will write a VCD trace of bus signals to the file `dual-mcu-bus-trace.vcd` including a virtual signal indicating conflicts on the bus.
The demo programs on the two RP2040s pull a single line low each (MCU0: D0 then D1 then ... then D7, MCU1 in the opposite direction).

## Learn more

- [Live-coding stream playlist](https://www.youtube.com/playlist?list=PLLomdjsHtJTxT-vdJHwa3z62dFXZnzYBm)
Expand Down
107 changes: 107 additions & 0 deletions demo/dual-mcu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import * as fs from 'fs';
import { RP2040 } from '../src';
import { GPIOPinState } from '../src/gpio-pin';
import { bootromB1 } from './bootrom';
import { loadHex } from './intelhex';

const hex1 = fs.readFileSync('demo/dual-mcu/dual-mcu-0.hex', 'utf-8');
const hex2 = fs.readFileSync('demo/dual-mcu/dual-mcu-1.hex', 'utf-8');
const mcu1 = new RP2040();
const mcu2 = new RP2040();
mcu1.loadBootrom(bootromB1);
mcu2.loadBootrom(bootromB1);
loadHex(hex1, mcu1.flash, 0x10000000);
loadHex(hex2, mcu2.flash, 0x10000000);

mcu1.uart[0].onByte = (value) => {
process.stdout.write(new Uint8Array([value]));
};

mcu2.uart[0].onByte = (value) => {
process.stdout.write(new Uint8Array([value]));
};

// GPIOPinState: { Low, High, Input, InputPullUp, InputPullDown }

let pin_state: number[][] = [
[0,0,0,0,0,0,0], // result value
[3,3,3,3,3,3,3], // input from mcu1 (pullup initially)
[3,3,3,3,3,3,3] // input from mcu2 (pullup initially)
];
let pin_gpio: number[] = [2,3,4,5,6,7,8,9];
let pin_label: string[] = ["d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7"];
let vcd_file = fs.createWriteStream('dual-mcu-bus-trace.vcd', {});
let last_conflict_cycle: number = -1;

// This listener connects the two MCUs and writes a VCD signal trace file.
// This code assumes pullups enabled (open collector/pullup bus).
function pinListener(mcu_id: number, pin: number) {
return (state: GPIOPinState, oldState: GPIOPinState) => {
pin_state[mcu_id+1][pin] = state;
let v: number = ((pin_state[0+1][pin]===0)||(pin_state[1+1][pin]===0))?0:1;
mcu1.gpio[pin_gpio[pin]].setInputValue((v==1)?true:false);
mcu2.gpio[pin_gpio[pin]].setInputValue((v==1)?true:false);

// write signal to VCD file
let pin_vcd_id = String.fromCharCode(pin+34);
if(pin_state[0][pin]!==v) {
pin_state[0][pin]=v;
vcd_file.write(`#${mcu1.core.cycles} ${v}${pin_vcd_id}\n`);
}

// write conflict flag to VCD file
let conflict: boolean = ((pin_state[0+1][pin]===0)&&(pin_state[1+1][pin]===1))||((pin_state[0+1][pin]===1)&&(pin_state[1+1][pin]===0));
if(conflict) console.log(`Conflict on pin ${pin_label[pin]} at cycle ${mcu1.core.cycles} (${pin_state[0+1][pin]}/${pin_state[1+1][pin]})`);
let have_new_conflict = conflict&&(last_conflict_cycle === -1);
let conflict_recently_resolved = (!conflict)&&(last_conflict_cycle !== -1);
if(conflict_recently_resolved && (mcu1.core.cycles === last_conflict_cycle)) {
// one mcu set conflict and other resolved in same cycle:
// delay until next signal change so that the conflict signal is visible in VCD
return;
}
let write_conflict_flag: boolean = have_new_conflict || conflict_recently_resolved;
if(write_conflict_flag) {
vcd_file.write(`#${mcu1.core.cycles} ${conflict?1:0}!\n`);
}
last_conflict_cycle = conflict ? mcu1.core.cycles : -1;
};
}

for(let i = 0; i < pin_label.length; i++) {
mcu1.gpio[pin_gpio[i]].addListener(pinListener(0, i));
mcu2.gpio[pin_gpio[i]].addListener(pinListener(1, i));
}

mcu1.core.PC = 0x10000000;
mcu2.core.PC = 0x10000000;

// write VCD file header
vcd_file.write("$timescale 1ns $end\n");
vcd_file.write("$scope module logic $end\n");
vcd_file.write(`$var wire 1 ! bus_conflict $end\n`);
for(let pin = 0; pin < pin_label.length; pin++) {
let pin_vcd_id = String.fromCharCode(pin+34);
vcd_file.write(`$var wire 1 ${pin_vcd_id} ${pin_label[pin]} $end\n`);
}
vcd_file.write("$upscope $end\n");
vcd_file.write("$enddefinitions $end\n");

function run_mcus() {
let cycles_mcu2_behind = 0;
for (let i = 0; i < 100000; i++) {
if((mcu1.core.cycles%(1<<25))===0) console.log(`clock: ${mcu1.core.cycles/125000000} secs`);
// run mcu1 for one step, take note of how many cycles that took,
// then step mcu2 until it caught up.
let cycles = mcu1.core.cycles;
mcu1.step();
cycles_mcu2_behind += mcu1.core.cycles - cycles;
while(cycles_mcu2_behind > 0) {
cycles = mcu2.core.cycles;
mcu2.step();
cycles_mcu2_behind -= mcu2.core.cycles - cycles;
}
}
setTimeout(() => run_mcus(), 0);
}

run_mcus();
42 changes: 42 additions & 0 deletions demo/dual-mcu/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 3.12)
include(pico_sdk_import.cmake)

project(dual-mcu C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

pico_sdk_init()

add_executable(dual-mcu-0
dual-mcu.c
)

add_executable(dual-mcu-1
dual-mcu.c
)

target_compile_options(dual-mcu-0 PRIVATE -Wall -DMCU0)
target_compile_options(dual-mcu-1 PRIVATE -Wall -DMCU1)

target_link_libraries(dual-mcu-0
hardware_pio
pico_stdlib
pico_util
)

target_link_libraries(dual-mcu-1
hardware_pio
pico_stdlib
pico_util
)

pico_enable_stdio_usb(dual-mcu-0 0)
pico_enable_stdio_uart(dual-mcu-0 1)
pico_enable_stdio_usb(dual-mcu-1 0)
pico_enable_stdio_uart(dual-mcu-1 1)

pico_generate_pio_header(dual-mcu-0 ${CMAKE_CURRENT_LIST_DIR}/dual-mcu.pio)
pico_generate_pio_header(dual-mcu-1 ${CMAKE_CURRENT_LIST_DIR}/dual-mcu.pio)

pico_add_extra_outputs(dual-mcu-0)
pico_add_extra_outputs(dual-mcu-1)
Loading

0 comments on commit a9d04bb

Please sign in to comment.