RTEMS 7 Shell on STM32F4 (BSP): Double Enter Required to Execute Command

Hi RTEMS Community,

I’m facing an issue with the RTEMS 7 shell on STM32F4 where commands require pressing Enter twice to execute (in Putty). (Important addition: if I do not enter a command and press enter, then the processing is correct and I see a new prompt.) This issue occurs in both PuTTY and TeraTerm, despite trying all possible terminal settings. Below are UART logs from the controller and terminal.

RTEMS SHELL sent to Putty over RX:

Id Time[ns] 1:UART: RX
1 1199981500.00 t
2 1322647000.00 e
3 1639511000.00 s
4 1757507500.00 t
5 2449988000.00 [0D]
6 2450074500.00 [0A]
7 2451689500.00 [1B]
8 2451776000.00 [
9 2451862500.00 1
10 2451949000.00 8
11 2452035500.00 t
12 2453945000.00 [1B]
13 2454031000.00 [
14 2454117500.00 >
15 2454204000.00 0
16 2454290500.00 q
17 2874987500.00 T
18 2875074000.00 e
19 2875160500.00 s
20 2875247000.00 t
21 2875333500.00 [20]
22 2875420000.00 c
23 2875506500.00 o
24 2875593000.00 m
25 2875679500.00 m
26 2875766000.00 a
27 2875852000.00 n
28 2875938500.00 d
29 2876025000.00 [20]
30 2876111500.00 e
31 2876198000.00 x
32 2876284500.00 e
33 2876371000.00 c
34 2876457500.00 u
35 2876544000.00 t
36 2876630500.00 e
37 2876717000.00 d
38 2876803500.00 !
39 2876890000.00 [0D]
40 2876976500.00 [0A]
41 2877365500.00 S
42 2877452000.00 H
43 2877538500.00 L
44 2877625000.00 L
45 2877711500.00 [20]
46 2877798000.00 [
47 2877884500.00 /
48 2877971000.00 ]
49 2878057500.00 [20]
50 2878144000.00 #
51 2878230000.00 [20]

Terminal Putty sent over tx:

Id Time[ns] 1:UART: TX
1 1199997500.00 t
2 1300903000.00 e
3 1461842000.00 s
4 1580280000.00 t
5 2555126500.00 [0D]
6 2559286500.00 [1B]
7 2559382000.00 [
8 2559477500.00 8
9 2559572500.00 ;
10 2559668000.00 2
11 2559763500.00 4
12 2559858500.00 ;
13 2559954000.00 8
14 2560049500.00 0
15 2560144500.00 t
16 4706059000.00 [0D]

Has anyone encountered this issue or can suggest a fix? (I assume the problem is in the escape sequences, but I don’t have enough knowledge to figure it out) Thanks!

BSP stm32f4 USART read modification:

#ifndef BSP_CONSOLE_USE_INTERRUPTS
static int usart_read_polled(int minor)
{
  const console_tbl *ct = Console_Port_Tbl[minor];
  volatile stm32f4_usart *usart = usart_get_regs(ct);
  
  // Ready data
  while ((usart->sr & STM32F4_USART_SR_RXNE) == 0);
  
  // Return data
  return STM32F4_USART_DR_GET(usart->dr);
}
#endif

It is quite possibly a CR/LF setting being wrong in Putty. See https://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter4.html#i27

0x0A and 0x0D are LF and CR. There are ASCII tables commonly available. On Linux “man ascii” should have one.

I just figured out the error, the problem turned out to be in the BSP driver.

I made a mistake in BSP console/usart.c file:

I change this code:

static int usart_read_polled(int minor)
{
  const console_tbl *ct = Console_Port_Tbl [minor];
  volatile stm32f4_usart *usart = usart_get_regs(ct);

  if ((usart->sr & STM32F4_USART_SR_RXNE) != 0) {
    return STM32F4_USART_DR_GET(usart->dr);
  } else {
    return -1;
  }
}

To this code:

static int usart_read_polled(int minor)
{
  const console_tbl *ct = Console_Port_Tbl[minor];
  volatile stm32f4_usart *usart = usart_get_regs(ct);
  
  // Ready data
  while ((usart->sr & STM32F4_USART_SR_RXNE) == 0);
  
  // Return data
  return STM32F4_USART_DR_GET(usart->dr);

However, the while ((usart->sr & STM32F4_USART_SR_RXNE) == 0); function also blocks input, violating the driver’s convention to return -1. I made this mistake when I couldn’t figure out why the shell wasn’t starting.

1 Like

If this is code already in RTEMS, please file an issue and a merge request. We don’t want someone else having to debug this.

Thanks.

No changes are required, everything is working fine. This is my mistake, as I’m just starting to learn RTEMS and I’ve come across a lot of new concepts.

I can’t understand the purpose of these functions in console-config.c. I did not find any mention of them in the BSP manual.

What these variables are responsible for? One of them is always NULL, despite this, reading from the console works.

BSP_output_char_function_type BSP_output_char = output_char;
BSP_polling_getchar_function_type BSP_poll_char = NULL;

You have wandered into an area where every BSP has a console driver but there are multiple construction patterns based on what’s needed and what hardware is available. At the top of this software stack is POSIX calls like read() and write(). These device drivers use the termios support from cpukit/libcsupport for a lot of hardware independent functionality. Termios is from POSIX and includes everything from being able to set baud rate to ensuring a backspace is seen by the user as -- to overwrite the last character with nothing. The RTEMS termios implementation interfaces at the bottom with device drivers but the portable termios part provides buffering, etc. This is what printf(), scanf(), etc rely on.

There is a simple framework (bsps/shared/dev/serial/console-polled.c) to provide a termios console port when you only have a single UART and it is just polled. The read and write polled functions are like the ones it integrates with. When you print, each character is polled out. If you call read(), etc. termios will call the poll for a character function. If no character is available and -1 is returned, it sleeps for one tick and then tries again. This lets applications block for serial input even if the UART driver is not interrupt driven.

RTEMS also has “kernel debug IO” which includes printk(). This function does not have all the format specifiers that printf() does and is thus smaller. Additionally, it ONLY polls for characters in and out. It uses the BSP provided functions via the function pointers you cited and I am copying again:

BSP_output_char_function_type BSP_output_char = output_char;
BSP_polling_getchar_function_type BSP_poll_char = NULL;

RTEMS uses newlib for its C Library. Most of what it provides is “section 3” in the POSIX/UNIX/Linux. Section 3 library functions are higher level than Section 2 System Calls. Section 3 includes printf() and scanf(). Section 2 includes write() and read(). This is a wikipedia page with a table describing the sections.

RTEMS has a lot of layering some of which is based on traditional layering of POSIX systems. In addition, the BSPs have a lot of small to medium sized reusable pieces. Our goal is to help people avoid duplicating code when two boards have similar hardware. And now a common case is that two boards may vary only in minor ways that can be accounted for with configuration options in the config.ini file passed to waf configure.

1 Like

Hi Joel,

Thanks very much for the description of the BSP function pointers for output_char and getchar as this is something I have been trying to understand as well.

Per my thread on the beaglebone BSP console driver not working as expected, I’ve also come across the following at the bottom of a few BSPs console drivers implementations:

RTEMS_SYSINIT_ITEM(
  uart_probe,
  RTEMS_SYSINIT_BSP_START,
  RTEMS_SYSINIT_ORDER_LAST_BUT_5
);

Could you shed some light on why this is required please?

1 Like

The SYSINIT() support falls under Arthur C. Clarke’s famous quote “Any sufficiently advanced technology is indistinguishable from magic.” :slight_smile:

The simplest way to think of it is as a special set of constructor functions that are organised into an ordered list by the linker and invoked by RTEMS during initialisation. You can have multiple
Linker Sets in an executable but this one is for RTEMS System Initialisation. The code that invokes them is in rtems_initialize_executive().

RTEMS_SYSINIT_ITEM() is used to add a function to the system initialisation set. The one you pasted in can be read as: Invoke uart_probe() during the RTEMS_SYSINIT_BSP_START step of initialisation. RTEMS_SYSINIT_ORDER_LAST_BUT_5 says where it goes in the set of system initialisation steps executed at the bsp start step. The set of steps are defined in rtems/sysinit.h. This file has the System Initialisation Steps and ordering within each steps

There are at least two ways to see what functions are in the ordered set.

Use nm to see the list contents. Search for _Linker_set_begin. That’s the start of the set of methods

$ m68k-rtems7-nm -g -n ./build/m68k/uC5282/testsuites/samples/hello.exe
00054e1a T _Linker_set__Sysinit_begin
00054e1a T _Linker_set__Sysinit__Workspace_Handler_initialization
00054e1e T _Linker_set__Sysinit__Malloc_Initialize
00054e22 T _Linker_set__Sysinit_bsp_start
00054e26 T _Linker_set__Sysinit__User_extensions_Handler_initialization
00054e2a T _Linker_set__Sysinit_rtems_initialize_data_structures
00054e2e T _Linker_set__Sysinit__RTEMS_tasks_Manager_initialization
00054e32 T _Linker_set__Sysinit__Thread_Create_idle
00054e36 T _Linker_set__Sysinit_rtems_libio_init
00054e3a T _Linker_set__Sysinit_rtems_filesystem_initialize
00054e3e T _Linker_set__Sysinit__Console_simple_Initialize
00054e42 T _Linker_set__Sysinit__RTEMS_tasks_Initialize_user_task
00054e46 T _Linker_set__Sysinit_rtems_libio_post_driver
00054e4a T _Copyright_Notice
00054e4a T _Linker_set__Sysinit_end
  • The second way is using rtems-exeinfo. The help isn’t helping me give a command line. @kiwichris can you fill this one in? If you can edit this bullet, much appreciated.

This combined with compiling with function and data sections and linking with –gc-sections eliminates all function and data items that are not referenced. Sysinit only pulls in the initialisation functions used. Before the change to a linker set for RTEMS initialisation, the initialisation would directly invoke a lot more functions. You had to initialise every manager which might be used.

.

1 Like

@JoelSherrill the user manual’s example section provides the information : 14.3. RTEMS Executable Infomation — RTEMS User Manual 7.8923d90 (14th April 2025) documentation.

Just add the -I option. The SYSINIT data is held in the RTEMS init sections. They are a form of static construction used in C++ or the GNU C attribute.

@kiwichris Do you mind double checking that the help from the rtems-exeinfo is complete? I didn’t see anything that hinted at how to see the sysinit section contents.

Just checked on a large and complex application and it is showing the sort of data I expect. I did not check it is complete.

Could you please explain what you saw and what you expected?