Go Down

Topic: Does M4 of the Portenta H7 have access to the serial monitor? (Read 369 times) previous topic - next topic

jerteach


Just doing a few tests running both the M7 and M4 cores blinking green and red like the tutorial. 

https://www.arduino.cc/pro/tutorials/portenta-h7/por-ard-dcp

Also included a serial print for the M7 which worked fine for both 9600 and 115200 but when I tried a serial print from the M4 I got no errors but no printout. Is this the expected behavior or is there a trick I don't know about.


I was kind of expecting to be able to alternate the printout like I can the red and green LED's.




tjaekel

It looks like, the CM4 compile is clean, so it seems to have also a Serial object.
But I could imagine the following, why CM4 does not send anything to UART.

CM4 should be able to access the same UART peripheral in the MCU (from hardware point of view).

But think about, CM4 is like a completely different MCU, with its own code (CM7 and CM4 code must be compiled, linked independently, also loaded independently). So, both cores will have a runtime code, their own startup, their own memory regions (ROM and SRAM regions) and also their own Vector Table and ISR handlers.

So, I can imagine:
- CM4 runtime code generated from the same/similar source code for Serial, but it is compiled for CM4,
  as part of CM4: so, CM4 has a Serial object and CM7 has a Serial object, but no the same, not a
  single one
- CM4 might be released from Reset (via an instruction done by CM7), but it might not have same
  full system configuration on CM4 startup (e.g. maybe it will not configure pins, does not enable
  clocks etc.).
  OK, assuming CM7 would initialize the Peripherals, clocks, domains in system, so that CM4 can also
  potentially use UART.
- But, quite likely: if CM4 has its own Serial object (I cannot imagine that CM7 and CM4 share the same
  code, the same objects etc.) - even all the code is there, but maybe the needed Interrupts for UART
  are not properly configured for/in CM4
- CM4 has its own NVIC, own Vector Table, own ISR handlers: maybe the Vectors are set properly,
  but maybe Interrupts are not configured, not enabled etc.

Also not clear:
Assuming there is mbed, with an RTOS, so that Queues (for multi-thread communication on CM7) is possible - does the CM4 runs also an RTOS, also an mbed?
And if so, both systems would be completely independent: a thread on CM7 might talk only to a thread
on CM7, not to a thread of CM4 (but threads in CM4 via an RTOS on CM4).

I guess, there is (and has to be) an RPC mechanism, so that CM4 and CM7 can talk to each other.
The STM32H7 provides Hardware Semaphores which have to be used for such inter-core synchronisation.

I would guess, the CM4 runtime system is not the 'same' as CM7, it might have just a very simple
startup code, it does not initialize the system really completely, just the CM7 as main and master core does.
I assume, the CM4 runtime environment is just a subset, a bare minimum and maybe also not really complete yet (e.g. in terms of CM4 interrupt configuration and handling, same functioning HAL drivers).

It would be great and appreciated to see all the source code, including mbed, the startup code(s), to understand what each core will do (has done) before the Arduino Scatch (setup() and loop()) would be called.

BTW:
Assuming, CM4 and CM7 have their own Serial object. You cannot assume, when CM7 does Serial.begin(), that CM4 can continue to use the Serial. The Serial is a different one (like two objects of the same class), so that CM4 has to do really all from the beginning as well. You cannot assume that CM7 and CM4 can 'share' something done by the 'other guy'.

Multi-Core systems are not really so easy, even both CM7 and CM4 can access physical Peripherals in system. They run for sure on different ROM and SRAM regions, they do not share code or data. For both cores there 'must' be a compile and link process, separated for CM7 and CM4.
Even, most of the code (instructions) are identical, but the internal processor architectures, e.g. NVIC, Vector Table, access to FPU, DSP instructions etc. are different (or not there on CM4).

If all the HAL drivers are 'generic' in a way, that a compile define (e.g CM7 vs. CM4) would select the correct source code (e.g. to setup a Vector, to enable an Interrupt) - no clue. Or both cores have their own set of HAL drivers. But what CM4 would initialize when it is released - also no clue.

I would assume the system this way:
- CM7 is master, running mbed, an RTOS, doing all the system config stuff, tested and implement quite
  well
- but CM4 is just an 'auxiliary CPU', not a full stand-alone system, it might not run even an RTOS,
  neither potentially a full blown mbed and RTOS
- and there are differences between both runtime systems, what they would initialize in system,
  what they support via HAL drivers etc.
- and there might be an RPC mechanism provided so that CM4 and CM7 can communicate,
  synchronize each other - but how far it is implemented, tested and existing - no idea
- even both CPUs sit in the same system, can use the same hardware peripherals, but they behave
  in terms of software like two independent computers, not sharing any memory, code and data
  (actually: this STM32H747 is not really a multi-core system, it is more a multi-processor system)
- so, I would not assume that all works and in the same way on CM4 as on CM7, I could imagine that
  CM4 is pretty 'empty' in terms of runtime environment, HAL, drivers etc. ("we do not need all again
  on CM4 if CM7 provides it already" - and it would blow up the ROM and RAM space needed)

I will try to use Serial (UART) on CM4, potentially easier via an RPC mechanism (instead to let CM4
do all the initialization, UART handling, ISRs etc.).
Otherwise, if both, CM7 and CM4 want to use the same physical UART at the end - it becomes even more tricky to make it exclusive on the Peripheral Device.

So, the CM7 is the master and provides via messages, queues, RPC, ways that CM4 can request something on CM7, e.g. to send a string via UART on behalf.
It might be a bit of work, but it makes the system a bit more 'easier' to understand it, to deal with
concurrent access to single resources (such as devices).

tjaekel

There is already an example for RPC: CM4 sending requests to let print on Serial via CM7.
Look at:
C:\Users\<USER>\AppData\Local\Arduino15\packages\arduinobeta\hardware\mbed\1.2.1\libraries\RPC\examples
and find Scatch "RPC_m4.ino"  (for CM7 as well as CM4, via CORE_CM7 macro)

The example uses:
     
Code: [Select]
RPC1.print("Calling add ");
on the CM4 side.

This is what I had in mind (and would do, instead of a native Serial on CM4).
Based on this example, it looks like CM4 runs also an RTOS.

Here the entire Scatch:

Code: [Select]

#include "Arduino.h"
#include "RPC_internal.h"

#ifdef CORE_CM7
int add(int a, int b) {
  printf("calling add on M7\n");
  delay(1000);
  return a + b;
}
int subtract(int a, int b) {
  printf("calling subtract on M7\n");
  return a - b;
}
#endif

rtos::Thread t;

void call_substract() {
  while (1) {
    delay(700);
    RPC1.print("Calling subtract ");
    auto res = RPC1.call("sub", 12, 45).as<int>();;
    RPC1.println(res);
  }
}

void setup() {
  // put your setup code here, to run once:
  RPC1.begin();
  Serial.begin(115200);
  //while (!Serial) {}
  //Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
#ifdef CORE_CM7
  RPC1.bind("add", add);
  RPC1.bind("sub", subtract);
#else
  t.start(call_substract);
#endif
}

void loop() {
  // put your main code here, to run repeatedly:
#ifndef CORE_CM7
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);

  RPC1.print("Calling add ");
  auto res = RPC1.call("add", 12, 45).as<int>();;
  RPC1.println(res);
#else
  while (RPC1.available()) {
    Serial.write(RPC1.read());
  }
#endif
}

tjaekel

The IDE and the examples (with so many errors) drive me crazy.

OK, the RPC, with UART messages sent from CM4, and CM7 will do it on behalf - works.
But, the sample code has several issues (see below).

Here is my Scatch to let CM4 send messages via RPC to CM7, which will forward to Serial.


Code: [Select]

#include "Arduino.h"
#include "RPC_internal.h"

//in order to compile this, you had to add -fexceptions in file 'platform.txt' as option !!

#ifdef CORE_CM7
int add(int a, int b) {
  ////printf(" M7: calling add\n");
  //-->this printf does not work on UART !!
  Serial.println(" M7: calling add\n");
  return a + b;
}
int subtract(int a, int b) {
  printf(" M7: calling subtract\n");
  return a - b;
}
#endif

#ifndef CORE_CM7
//this is just for CM4
rtos::Thread t;

void call_substract() {
  //this is a CM4 RTOS thread
  while (1) {
    delay(1000);
    RPC1.print(" M4: Calling subtract | ");
    auto res = RPC1.call("sub", 12, 45).as<int>();
    RPC1.println(res);
  }
}
#endif

void setup() {
  //for both cores using RPC
  RPC1.begin();
#ifdef CORE_CM7
  Serial.begin(115200); //never mind: TeraTerm will receive with any baudrate set there
  //while (!Serial) {}
  //Serial.begin(115200);
  pinMode(LEDG, OUTPUT); //CM7 configures LED GPIO
  RPC1.bind("add", add);
  RPC1.bind("sub", subtract);
  Serial.println("CM7: release and boot CM4");
  //boot CM4
  //-->it has to be done in order to release CM4 !!
  LL_RCC_ForceCM4Boot();
#else
  t.start(call_substract);
#endif
}

void loop() {
  // put your main code here, to run repeatedly:
#ifndef CORE_CM7
  //this is the CM4 main thread
  digitalWrite(LEDG, HIGH); //CM4 toggles the LED (GPIO)
  delay(1000);
  digitalWrite(LEDG, LOW);
  delay(1000);

  RPC1.print(" M4: Calling add | ");
  auto res = RPC1.call("add", 12, 45).as<int>();
  RPC1.println(res);
#else
  //let CM7 also do something, to see CM7 runs and prints as well
  add(10, 20);
  delay(1000);

  //CM7 will check for RPC calls and forward to UART
  while (RPC1.available()) {
    Serial.write(RPC1.read());
  }
#endif
}


Attention: in order to compile this without errors - you had to add -fexceptions in file 'platform.txt' as C and CPP compile option (find this file in your hidden AppData folder - "
rrrr"

The original RPC example has THREE major issues:
- it does not release the CM4 (it has to be done by CM7) - a function call added
- there is a macro used as "ifdef CORE_CM8" (Ok, it skips the CM7 code for add() and
  subtract() functions which are anyway not used (why not?)
- the printf on CM7 does not come out on Serial (UART), you had to use still Serial.
- and, actually, a thread was also created on CM7 (code not enclosed in an #ifndef) - added

I have fixed this example code, changed a bit the messages so that you can see which 'core' sends it.
It works, CM4 can send RPC messages and CM7 will spit it out on Serial (UART).
The messages on UART terminal come a bit strange, because CM7 and two threads on CM4 running, using Serial in an interleaved way.

To be honest: I do not understand the Arduino Pro IDE (often it compiles files from a cache, not my
intention or not my actual workspace). And the examples have so many issues that I could cry.
(OK, not familiar with Arduino, a first time user, but I think, I will change my system to another IDE,
e.g. AC6 System Workbench, hook up a real ST-LINK debugger etc., especially due to fact - there is
not any real debugger available yet in IDE).

tjaekel

Sorry, there is not a CORE_CM8, maybe a typo when I touched the file.
But anyway, the CM4 does not seem to be released from reset.

jerteach

Thanks for your thoughtful reply's Tjaekel. I feel your pain.   That reminds me, the Portenta specific examples are very confusing. They don't seem to have much explanation about what they do. I will put this issue in another thread.

jerteach

Thanks TJaekel.

I got it working thanks to your examples.


https://github.com/hpssjellis/my-examples-for-the-arduino-portentaH7/blob/master/my02-dual-core-RPC.ino



The -fexceptions compiler option was a bit trickier than I expected. I put notes in the top of my file.


jerteach

The example uses:
    
Code: [Select]
RPC1.print("Calling add ");
on the CM4 side.


Tjaekel, that suggestion was very useful to me, along with your running code. My next RPC adventure is to try to pass a variable from the M4 core to M7. Any suggestions? I am thinking something like a function run on M7 that changes an M7 global variable. (Probably really bad programming concept, but I just want to see if it works) Possibly some code like this:


#ifdef CORE_CM7

int myIntGlobal = 3333;

void passToM7(int a) {
  myIntGlobal = a
}

#endif





and then somewhere in the M4 code something like

 auto RPC1.call("passToM7", 12).as<int>();   // not sure if the .as<int> is needed


With M7 constantly printing out every second whatever is in myIntGlobal


I will try my own code but any suggestions anyone? Other than what a bad idea this is :)



jerteach

Here is my solution for serial monitor with the M4 core. I have a working program that use RPC (remote procedure call) to let the M7 serial print print a call from the M4.

Not as hard as it sounds

https://github.com/hpssjellis/my-examples-for-the-arduino-portentaH7/blob/master/my02b-dual-core-RPC.ino


jwestmoreland

Jeremy,

That's pretty cool - I'll have to give it a try.

I noticed you posted this:

NOPE: HELLO_WORLD DOES NOT COMPILE ON THE PORTENTA, SAME KIND OF ISSUE AS THE NANO 33 ble USING THE MBED BOARD. CAN'T FIND THE CORRECT PORT AFTER UPLOAD.

This can happen on the Pro IDE v0.0.6 - I've been trying to get ThreadDebug working - it'll interfere with the upload on subsequent builds - to get around this:

*)  Put the board into bootloader mode (two pushes of the reset button, as you know) - make sure you get the 'breathing LED' - on the H7 it's a green breathing LED.

*)  Use the 1.8.13 tools.  Just upload using the com port that gets grabbed in bootloader mode.  It should work.  The 1.8.13 tools seem to be OK after the board resets; unlike the current Pro IDE 0.0.6.

Hope this helps,
John



jerteach

Jeremy,


I noticed you posted this:

NOPE: HELLO_WORLD DOES NOT COMPILE ON THE PORTENTA, SAME KIND OF ISSUE AS THE NANO 33 ble USING THE MBED BOARD. CAN'T FIND THE CORRECT PORT AFTER UPLOAD.

Hi John. I am posting my issues and then fixing them but forgetting to change my posts. I have that Machine Learning program working now.




Go Up