Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stream obj failure on two STM32L4 custom targets #10425

Closed
Hoel opened this issue Apr 17, 2019 · 23 comments
Closed

Stream obj failure on two STM32L4 custom targets #10425

Hoel opened this issue Apr 17, 2019 · 23 comments

Comments

@Hoel
Copy link

Hoel commented Apr 17, 2019

Description

I added three custom targets, one STM32L1 : STM32L151CCU6 and two STM32L4 : STM32L451CEU6 and STM32L462CEU6, the required HAL files
image
image
have been grabbed from STM32CubeMX and are of same version as the other Nucleo STM32L4 official targets (STM32Cube_FW_L4_V1.11.0). ClI is up to date (installed on OSX with MBEDCLI installer). The project has been created two days ago from the CLi so mbed-os is also up to date. Project is building fine with all 3 targets.

The stream failure issue occurs on the two STM32L4 targets only (on the STM32L1 target everything works fine). It is caused by the serial port instance (uart3 in this case but i tried uart1 and uart2 with same result) and it occurs immediately, before the main is reached. Since the error is related to stream it also prevent mbed to display the error in serial port.

in mbed_config i enabled everything related to error capture:

#define MBED_CONF_PLATFORM_CRASH_CAPTURE_ENABLED                              1                                                                                                // set by library:platform
#define MBED_CONF_PLATFORM_CTHUNK_COUNT_MAX                                   8                                                                                                // set by library:platform
#define MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE                           9600                                                                                             // set by library:platform
#define MBED_CONF_PLATFORM_ERROR_ALL_THREADS_INFO                             1                                                                                                // set by library:platform
#define MBED_CONF_PLATFORM_ERROR_FILENAME_CAPTURE_ENABLED                     1                                                                                                // set by library:platform
#define MBED_CONF_PLATFORM_ERROR_HIST_ENABLED                                 1                                                                                                // set by library:platform
#define MBED_CONF_PLATFORM_ERROR_HIST_SIZE                                    4

I finally found a way to get the error printed on console, if i add a simple printf statement at the beginning of the main and then only declare the serial port inside the main afterwards then the error is printed (however it doesnt show the file or line number as it should, for some reasons).
Also, if i remove the serial port declaration everything works fine and the led is blinking as it should.

I exported to whole project to SW4STM32 in order to step debug and after hundreds of steps i landed in stream.cpp line 31 where the error occurs, fopen and the file object fails for some unknown reasons and the mbed error is thrown.

I tried to build with --profile=release in CLI, same error, i also tried to set MBED_CONF_RTOS_PRESENT to 0, same error. This target has 128K of RAM so it is unrelated to lack of ram, also it works fine on STM32L151 which has only 32K of RAM.

One interresting fact is that if i dont declare the serial port at all and only use stdio printf statements they are working fine, correctly redirected to the uart3 port which is set as default for STDIO and there is no stream error (maybe stream is not used in this case?).

Here is the console output when the stream failure occurs:

++ MbedOS Error Info ++
Error Status: 0x80010119 Code: 281 Module: 1
Error Message: Stream obj failure
Location: 0x800119F
Error Value: 0x0
Current Thread: main  Id: 0x20000F20 Entry: 0x800202B StackSize: 0x1000 StackMem: 0x200016B8 SP: 0x200025C4 
For more info, visit: https://mbed.com/s/error?error=0x80010119&tgt=L80_L462
-- MbedOS Error Info --

I dont think there are any official targets with STM32L451CE or STM32L462CE. If really needed i can send one of our targets along with a STLink for testing purppose, i can also run any binary / target folder that MBED or ST team would provide for test. Attached are the two target folders and the targets.json file.

targets.json.zip
Archive.zip

the main.cpp test file is as follow:

#include "mbed.h"

DigitalOut led(PA_15);
//Serial uart3(UART3_TX, UART3_RX, 9600);

int main() {
  printf("\r[MBED] init ok\r");
  Serial uart3(UART3_TX, UART3_RX, 9600);
  uart3.printf("[CLK] %dMHz\r", SystemCoreClock/1000000);
  while (true) {
      led = 0;
      wait(0.03);
      led = 1;
      wait(1.0);
  }
}

If uart3 is declared after the LED then error occurs immediately and nothing is printed on console, if instead uart3 is declared only after the first printf statement, then the first printf statement works and the stream error is printed in console uart3.printf statement never works since it is after the stream error in both cases. If uart3 is never declared nor used and only stduio printf statements are used, everything works fine and no error occurs (but this is not an option since we need all 3 uart ports to be working).

Issue request type

[ ] Question
[ ] Enhancement
[x] Bug
@ciarmcom
Copy link
Member

Internal Jira reference: https://jira.arm.com/browse/MBOCUSTRIA-1151

@kjbracey
Copy link
Contributor

Assuming UART3 is not the console, then it sounds like the HAL is messing up the console serial port when UART3 is initialised.

This is either a bug in the HAL, or a pin mapping limitation.

The application only gets to specify pins, not UART instances. The UART instance is deduced from the pins. If on this chip your "UART3_RX" and "UART3_TX" pins are actually usable by UART1 as well, then maybe it's deciding to drive those pins using UART1.

You might need to modify pin mapping tables to change which the preferred UART instance is for your pins.

HALs don't record which pins have already been put into use - indeed some apps rely on them not doing so because they're sloppy about repeated initialization.

@Hoel
Copy link
Author

Hoel commented Apr 17, 2019

@kjbracey-arm

You might need to modify pin mapping tables to change which the preferred UART instance is for your pins.

UART3 is the console, i defined it in PeripheralNames.h

#define STDIO_UART_TX  PB_10
#define STDIO_UART_RX  PB_11
#define STDIO_UART     UART_3
  UART1_TX    = PA_9,
  UART1_RX    = PA_10,

  UART2_TX    = PA_2,
  UART2_RX    = PA_3,

  UART3_TX    = PB_10,
  UART3_RX    = PB_11,

Also i know that declaring a serial object with the same UART port as STUDIO is not a problem at all, i do it all the time and it never cause any issue. If we declare for example uart3 as Serial instance and uart3 as STDIO port, then both serial3.printf and printf can be used, at the same time, without causing any error.

I also tried to use UART1 and UART2 as console default and still have the same error, so its not related to which UART is used for console. Also as told, the exact same project unchanged works perfectly fine on the STM32L151 target.

If on this chip your "UART3_RX" and "UART3_TX" pins are actually usable by UART1 as well, then maybe it's deciding to drive those pins using UART1.

They may be but these GPIOs are not declared for both anyway, and i only use one instance here (uart3). Which pin is used by which UART is defined in PeripheralPins.c and it looks fine to me. Again, this very project is used with STM32L1 target without causing any error.

const PinMap PinMap_UART_TX[] = {
    {PA_2,      UART_2,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART2)}, // Connected to STDIO_UART_TX
    {PB_10,      UART_3,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)}, 
    {PA_2_ALT0, LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_LPUART1)}, // Connected to STDIO_UART_TX
    {PA_9,      UART_1,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART1)},
    {PB_6,      UART_1,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART1)},
    {NC,        NC,       0}
};

const PinMap PinMap_UART_RX[] = {
    {PA_3,      UART_2,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART2)},
    {PB_11,      UART_3,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART3)},
    {PA_3_ALT0, LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF8_LPUART1)},
    {PA_10,     UART_1,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART1)},
    {PA_15,     UART_2,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_USART2)}, // Connected to STDIO_UART_RX
    {PB_7,      UART_1,   STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_USART1)},
    {NC,        NC,       0}
};

I dont know how it could be a bug in the HAL because the STM32L1 target works with no problem, and these STM32L4 targets worked perfectly fine in previous version of MBED as well (that said we used MBED 2, not 5 at the time). I suspect it is a problem with mbed.

@Hoel
Copy link
Author

Hoel commented Apr 17, 2019

@kjbracey-arm

Assuming UART3 is not the console, then it sounds like the HAL is messing up the console serial port when UART3 is initialised.

MBED throws the stream error when UART3 is initialized, which ultimately prevent any further print to STDIO since MBED is stuck in a loop after the error occurs. i dont know if the stream error originates from HAL or MBED itself, it occurs as the result of fdopen() is null (presumably).

image

@kjbracey
Copy link
Contributor

Also i know that declaring a serial object with the same UART port as STUDIO is not a problem at all, i do it all the time and it never cause any issue.

You may be getting away with it, but it causes quite a few issues internally. Quite a lot of juggling in the HAL and C retargetting is needed to stop the console+application use conflict causing problems, but not all targets support that juggling.

It's definitely not supported if using buffered serial.

Still, I would expect this STM platform to work as well as any other STM platform.

I also tried to use UART1 and UART2 as console default and still have the same error, so its not related to which UART is used for console.

When opening UART3 or the same UART as the console?

it occurs as the result of fdopen() is null (presumably).

If fdopen is returning NULL, it's vaguely possible it's just a memory exhaustion problem.

Or maybe OPEN_MAX is set unusually small, so it can't allocate a descriptor?

Can you isolate further where that fdopen is giving up? Note that that's a call in mbed_retarget.cpp - it's not reached the C library yet.

I gather this is GCC?

@0xc0170
Copy link
Contributor

0xc0170 commented Apr 17, 2019

cc @ARMmbed/team-st-mcd

@Hoel
Copy link
Author

Hoel commented Apr 17, 2019

@kjbracey-arm

You may be getting away with it, but it causes quite a few issues internally. Quite a lot of juggling in the HAL and C retargetting is needed to stop the console+application use conflict causing problems, but not all targets support that juggling.

It's definitely not supported if using buffered serial.

OK, i dont use buffered serial anyway, and ultimately i only need the Serial UART, STDIO is used only for finding the issue when it doesnt work as here.

When opening UART3 or the same UART as the console?

as told, console is UART3. i tried to set console to UART2 / UART1, and open Serial to UART3, it doesnt change anything, error still thrown.

If fdopen is returning NULL, it's vaguely possible it's just a memory exhaustion problem.

Well, i really doubt it, the target has 128K RAM, and my main is below, as you can see there is nothing in it.

#include "mbed.h"

DigitalOut led(PA_15);
//Serial uart3(UART3_TX, UART3_RX, 9600);

int main() {
  printf("\r[MBED] init ok\r");
  Serial uart3(UART3_TX, UART3_RX, 9600);
  uart3.printf("[CLK] %dMHz\r", SystemCoreClock/1000000);
  while (true) {
      led = 0;
      wait(0.03);
      led = 1;
      wait(1.0);
  }
}

Can you isolate further where that fdopen is giving up? Note that that's a call in mbed_retarget.cpp - it's not reached the C library yet.

How can i know the cause? i have a breakpoint at this line, i should check in mbed_retarget.cpp?

I gather this is GCC?

Yes right, it is GCC

***mbedcli path = /Applications/MBEDCLI.app/Contents/Resources/bin:/Applications/MBEDCLI.app/Contents/Resources/git/bin:/Applications/MBEDCLI.app/Contents/Resources/gcc/gcc-arm-none-eabi-6-2017-q2-update/bin:

@kjbracey
Copy link
Contributor

If you just step through the fdopen at source level, that should reveal something. The call chain should be mbed::fdopen(FileHandle *) -> ::fdopen(int) -> ::fopen() (in C library) -> _open() (back in mbed_retarget.cpp, line 471).

I don't see anything particularly target-specific here, oddly. Serial doesn't check for any errors coming back from the HAL - the object should construct and open itself fine, regardless of pins or HAL.

@Hoel
Copy link
Author

Hoel commented Apr 17, 2019

If you just step through the fdopen at source level, that should reveal something. The call chain should be mbed::fdopen(FileHandle *) -> ::fdopen(int) -> ::fopen() (in C library) -> _open() (back in mbed_retarget.cpp, line 471).

ok i will do that and let you know.

don't see anything particularly target-specific here, oddly. Serial doesn't check for any errors coming back from the HAL - the object should construct and open itself fine, regardless of pins or HAL.

Yes, i never came across this issue in the past. I can't affirm it is target specific but fact is the same project works perfectly with the STM32L1 target and fail with STM32L4 targets.

@Hoel
Copy link
Author

Hoel commented Apr 17, 2019

alright, so i stepped debugged in fdopen, landed in mabed_retarget, no error occur here, the returned object fh_i value is 0x03:

image

later in bind_to_fd :

image

later in STD::fdopen:

image

Finally back to Stream::stream, the file pointer is 0x0 which doesnt seem right, hence the jump to MBED_ERROR1 rather than continuing to mbed_set_unbuffered_stream():

image

Stream::Stream(const char *name) : FileLike(name), _file(NULL)
{
    // No lock needed in constructor
    /* open ourselves */
    _file = fdopen(this, "w+");
    // fdopen() will make us buffered because Stream::isatty()
    // wrongly returns zero which is not being changed for
    // backward compatibility
    if (_file) {
        mbed_set_unbuffered_stream(_file);
    } else {
        MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_CODE_OPEN_FAILED), "Stream obj failure", _file);
    }
}

@Hoel
Copy link
Author

Hoel commented Apr 17, 2019

the problem seems to occur here, mbed_retarget.cpp, in std::*fdopen, the stream returned after std::fopen has a null pointer:

image

image

@Hoel
Copy link
Author

Hoel commented Apr 17, 2019

I just ran this test on our old project folder, mbed-os version : major 5, minor 10, patch 2 and everything works fine here, no stream error or anything. To go ahead i copied the whole target folder from this project to the new project (mbed-os version : major 5, minor 12, patch 0) and the stream error occurs immediately. So something changed between these two version which cause the stream error issue.

EDIT:
i tried to downgrade the new project from 5.12.0 to 5.10.2 with CLI :
mbed update mbed-os-5.10.2
downgrade seemed to work, i then built the project and uploaded to the target, sadly the stream error is still there, quite confusing...

EDIT2:
Apparently the CLI command mbed update doesnt work to downgrade, i checked mbed_version.h and it is still 5.12.0.

EDIT3:
OK, after a commit the downgrade to 5.10.2 worked, i then built, uploaded to target and no more stream error, so i can confirm the problem comes from MBED, is does not occur on 5.10.2, always occurs on 5.12.0 and only impact STM32L4 targets (STM32L151 target does work on both versions)

@kjbracey
Copy link
Contributor

From fopen, does it call _open in mbed_retarget.cpp? Or does it not get that far?

Are we sure it's not an allocation failure? Can you breakpoint malloc? (May be tricky without the source).

You say "This target has 128K of RAM". Fine, but does Mbed OS know that? Is the linker map set up correctly? Check how much you can actually allocate.

@kjbracey
Copy link
Contributor

Ah, that seems to be it. Your heap has no space allocated:

.heap (COPY):
{
    __end__ = .;
    end = __end__;
    *(.heap*)
    __HeapLimit = .;
} > SRAM1
PROVIDE(__heap_size = SIZEOF(.heap));

All the other STM targets have the line

        . = ORIGIN(RAM) + LENGTH(RAM) - STACK_SIZE;

before __HeapLimit to mark all remaining space as heap.

Apparently STM targets needed to be updated due to a change to their _sbrk implementation in #9571. (I wasn't aware of this, so it took a while for us to get there...)

If you're making new/custom targets, I suggest running the GreenTea test suite on them, to show up this sort of platform problem in a systematic way.

@Hoel
Copy link
Author

Hoel commented Apr 18, 2019

@kjbracey-arm
Yes it can reach _open in mbed_retarget, in fact it pass everything without error and goes back to Stream::stream.

Are we sure it's not an allocation failure? Can you breakpoint malloc? (May be tricky without the source).

I will once i have a moment, the failure is likely here

You say "This target has 128K of RAM". Fine, but does Mbed OS know that? Is the linker map set up correctly? Check how much you can actually allocate.

Yes, the linker file reflect that, by the way i told wrongly 128K,t it is actually 160K

/* Linker script to configure memory regions. */
MEMORY
{
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
  SRAM1 (rwx)  : ORIGIN = 0x20000188, LENGTH = 160k - 0x188
}

@Hoel
Copy link
Author

Hoel commented Apr 18, 2019

Ah, that seems to be it. Your heap has no space allocated:

.heap (COPY):
{
end = .;
end = end;
(.heap)
__HeapLimit = .;
} > SRAM1
PROVIDE(__heap_size = SIZEOF(.heap));
All the other STM targets have the line

    . = ORIGIN(RAM) + LENGTH(RAM) - STACK_SIZE;

before __HeapLimit to mark all remaining space as heap.

Apparently STM targets needed to be updated due to a change to their _sbrk implementation in #9571. (I wasn't aware of this, so it took a while for us to get there...)

Oh right, i see now, i will make these changes and see

EDIT :
mmh, looks like the linker file need more update

Link: MBED_TEMPLATE_5.12.0
/Applications/MBEDCLI.app/Contents/Resources/gcc/gcc-arm-none-eabi-6-2017-q2-update/bin/../lib/gcc/arm-none-eabi/6.3.1/../../../../arm-none-eabi/bin/ld:./BUILD/L80_L462/GCC_ARM/.link_script.ld:0: warning: memory region `RAM' not declared
./BUILD/L80_L462/GCC_ARM/.link_script.ld:81: undefined symbol `STACK_SIZE' referenced in expression
collect2: error: ld returned 1 exit status
[ERROR] /Applications/MBEDCLI.app/Contents/Resources/gcc/gcc-arm-none-eabi-6-2017-q2-update/bin/../lib/gcc/arm-none-eabi/6.3.1/../../../../arm-none-eabi/bin/ld:./BUILD/L80_L462/GCC_ARM/.link_script.ld:0: warning: memory region `RAM' not declared
./BUILD/L80_L462/GCC_ARM/.link_script.ld:81: undefined symbol `STACK_SIZE' referenced in expression
collect2: error: ld returned 1 exit status

EDIT2:
Alright, corrected the linker file, built, uploaded and finally it works with mbed 5.12.0 !
Thank you Kevin for finding the issue

@Hoel Hoel closed this as completed Apr 18, 2019
@0xc0170
Copy link
Contributor

0xc0170 commented Apr 18, 2019

@kjbracey-arm 💯

@jeromecoutant
Copy link
Collaborator

Hi @Hoel
Could you propose a PR for the needed patch ?

@pmancele
Copy link
Contributor

@Hoel I have the same issue on STM32L4 using the USBSerial class (that heritates from Stream)

Can you provide the modification you applied to the linker script ?

Thanks !

@Hoel
Copy link
Author

Hoel commented Apr 26, 2019

Hello, alright, i will provide you my linker script later today

@Hoel
Copy link
Author

Hoel commented Apr 26, 2019

@jeromecoutant
alright, i will propose a PR for this issue in a short while. The PR will be our custom targets definitions which include the fixed linker script.

@pmancele
Copy link
Contributor

Ok i can confirm that the issue is resolved by adding the following line to the linker script :

. = ORIGIN(RAM) + LENGTH(RAM) - STACK_SIZE;
It was not present on my linker script cause I am also working on a custom board based on STM32L4A6RG

@Hoel
Copy link
Author

Hoel commented Apr 29, 2019

Hello,
Sorry i had no chance to follow-up on the issue and i dont even have the time right now to make a PR because we have another issue with LPTimer, however here are the mandatory changes in startup file:

LoopCopyDataInit:
	ldr	r0, =_sdata
	ldr	r3, =_edata
	adds	r2, r0, r1
	cmp	r2, r3
	bcc	CopyDataInit
//	ldr	r2, =_sbss
//	b	LoopFillZerobss
/* Zero fill the bss segment. */
//FillZerobss:
//	movs	r3, #0
//	str	r3, [r2], #4

//LoopFillZerobss:
//	ldr	r3, = _ebss
//	cmp	r2, r3
//	bcc	FillZerobss

/* Call the clock system intitialization function.*/
    bl  SystemInit
/* Call static constructors */
//  bl __libc_init_array
/* Call the application's entry point.*/
//	bl	main
  bl  _start
  bx  lr

and in linker file:

on top:

#if !defined(MBED_BOOT_STACK_SIZE)
    #define MBED_BOOT_STACK_SIZE 0x400
#endif

STACK_SIZE = MBED_BOOT_STACK_SIZE;

line 137

    .heap (COPY):
    {
        __end__ = .;
        end = __end__;
        *(.heap*)
        . = ORIGIN(SRAM1) + LENGTH(SRAM1) - STACK_SIZE;
        __HeapLimit = .;
    } > SRAM1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants