Nested void functions: feature/expected behaviour or bug ?

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
PeSe_1509996
Level 3
Level 3
25 replies posted 10 sign-ins 5 questions asked

Dear Community Members,

I found a strange behaviour of nested void functions (void functions under other void functions),

I would like to ask community members : is it a feature/expected behaviour or bug ?

let me show you:

 

background info:

PSoC Creator 4.4 (4.4.0.80) with latest updates, CY8CKIT-059 PSoC 5LP Prototyping Kit 1.0 Rev.*A

setup: from desktop C# I communicate with PSoC firmware by USBFS sending commands to PSOC (DMA auto management) to call different functions, like :

under main  for(;;)
{

// Is there any new Out transaction..

  //  Data parsing ...

    // call correspoding function, e.g.:

Beep_VERY_Short_Nx( 3u); // 3x Beep by toggling pins of a buzzer and led

// important1: beep function takes time to run down

// important2: every line in beep function are void functions -see later 

}

my Beep function:

void  Beep_VERY_Short_Nx (uint32_t Nx ) // N times
{
Pin_BUZZER_Write(1u); // BUZZER default (POR) off: silent
for (uint32_t i = 0; i < Nx; i++)
{
Pin_BUZZER_Write(0u); // pull down => beep
CyDelay(25);
Pin_BUZZER_Write(1u); // pull UP => silent
CyDelay(25);
}
}

So far everything is fine, as expected, nothing special.

 

First case:

If I call this beep function It return immediately. I mean the function runs fine from start to end, but  in line in time (the order in which events occur) is:

1.) call Beep function,

2.) Beep function returns immediately (I see it as my firmware reply to desktop program by USBFS), this is my real quation.

3.)  hear the beeps as expected but after point 2, not before point 2.

(beep runs like as async / non-blocking function in PSoC 5lp)

let me emphasize: all functions in the body of beep function are voids:

as  void Pin... Write(); and void CyDelay(); functions are void functions.

 

If I rewrite the beep function method signature to non-void and call it, like:

uint8 result = Beep_VERY_Short_Nx( 3u );

The  order is:

1.) call Beep function

2.) beeps as expected

3.) Beep function return in the end of the process.

So, in this second case the code running as expected.

 

So, my question is: is it (immediately return of an 'all void  function') a feature/expected behaviour (e.g. from compiler optimization) or bug ?

 

Thanks for your kind reply,

Best Regards,

Péter

 

 

 

 

 

 

0 Likes
17 Replies
AlenAn14
Moderator
Moderator
Moderator
500 replies posted 100 solutions authored 250 replies posted

Hi @PeSe_1509996 ,

>> 2.) Beep function returns immediately (I see it as my firmware reply to desktop program by USBFS), this is my real quation.

Can you please elaborate on this as to how you know that the function is returning immediately?

Can you also let me know if you tried isolating the beep routine , i.e. by commenting out all other functions, like data parsing etc, in the super loop and only running the beep routine and see if you are still seeing this difference ?

Warm Regards
Alen

0 Likes

Dear Alen,

let me suggest that we focus on the original question - without asking back questions:

the above mentioned is a feature/expected behaviour or an unexpected/bug under PSoC/Creator enviroment ?

 

Cheers,

Péter

 

0 Likes

Péter,

A 'void' function does not immediately return even with optimizations on.

As expected, a 'void' function will complete its operation when all its sub-function calls are complete.  This means, in your case, all the Write(), CyDelay() calls within the for() loop.

It appears your Beep_VERY_Short_Nx () function is a blocking function.  However, this doesn't mean that interrupts or DMA operations are blocked.  They can still occur while your function is still executing.   Your function only blocks other operations in the main() task flow.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Dear Len,

Thanks for your perfect answer:  I think the same, we are on the same wavelength.

Nevertheless, the beep function void version returns almost immediately when checking things from the USBFS / desktop C# side 😞

See the followings:

USBFS_1:
EP1: Direction IN, Transfer Type: INT (means interrupt)
Interval: 4 ms
Max. Packet Size: 64 bytes

EP2: Direction OUT, Transfer Type: INT (means interrupt)
Interval: 10 ms
Max. Packet Size: 64 bytes

Clipped details  from Cypress :

in main  for(;;)

/* USB: Check if configuration is changed. */
if (0u != USBFS_1_IsConfigurationChanged()) // Returns a nonzero value when a new configuration has been changed
{
/* Re-enable endpoint when device is configured.*/
if (0u != USBFS_1_GetConfiguration())
{
/*
The DMA auto management also requires registering SRAM buffers after the device has been enumerated.
Initialization of IN and OUT endpoints buffers: Register SRAM buffers for IN and OUT EP endpoints.
*/
USBFS_1_LoadInEP (IN_EP, buffer_in, BUFFER_SIZE_IN);
USBFS_1_ReadOutEP(OUT_EP, buffer_out, BUFFER_SIZE_OUT);
/* Enable OUT endpoint to receive data from host. */
USBFS_1_EnableOutEP(OUT_EP);
}
}

 

/* Check if data was received. */
// USBFS_OUT_BUFFER_FULL : An OUT transaction has occurred and data can be read
if (USBFS_1_OUT_BUFFER_FULL == USBFS_1_GetEPState(OUT_EP))
{
length_OUT = USBFS_1_GetEPCount(OUT_EP); /* Read number of received data bytes. */
// for (uint32_t i = 0; i < BUFFER_SIZE_OUT; i++){ appBufferOut[i] = 0u; }
memset(appBufferOut, 0u, sizeof appBufferOut); // for sure : zeroing receive buffer in advance
memcpy(appBufferOut, buffer_out, length_OUT); /* Copy data into the local buffer to handle it later. */


// received data parsing :
if (appBufferOut[0] == '(' && appBufferOut[63] == ')') // the first and last byte check as bound data format
{

if (appBufferOut[1] == 'B') // Beep command received
{
Beep_VERY_Short_SOS_Nx(3u); // void version

// Reply to host PC C#:
memset(buffer_in, 0u, sizeof buffer_in); // for sure zeroing transmit (PC IN) buffer in advance
buffer_in[0] = '(';
buffer_in[63] = ')';
buffer_in[1] = 1u; // means: command received, execution has taken place
buffer_in[2] = 1u; // means: no error

while (USBFS_1_IN_BUFFER_EMPTY != USBFS_1_GetEPState(IN_EP)) /* Wait until IN buffer becomes empty (host has read data). */
{
}
/* The DMA request is generated to write 32 bytes into IN endpoint buffer. When
* DMA is done the arbiter interrupt fires and makes endpoint available to be
* read by host. As soon as host starts reading next DMA transfer starts to fill
* common area.
* The DMA keeps transferring data until all data has been transferred. The
* DMA can be paused if common area is full.
*/
USBFS_1_LoadInEP(IN_EP, NULL, BUFFER_SIZE_IN);
}

}

USBFS_1_EnableOutEP(OUT_EP); /* Enable OUT endpoint to receive data from host. */
}

 

Clipped details  from Windows / C# point of view:

CyUSB.dll, .NET Interface to CyUSB3.sys driver - Release 1.2.3, version: 1.2.3.0

Interrupt end points:

CyInterruptEndPoint inEndpoint = null;

CyInterruptEndPoint outEndpoint = null;

const int XFERSIZE_IN = 64;

const int XFERSIZE_OUT = 64;

byte[] inData = new byte[XFERSIZE_IN];

byte[] outData = new byte[XFERSIZE_OUT];

...

int xferLen = XFERSIZE_OUT;

// Sending beep command : outData[1] = 'B'

bool bResultT = outEndpoint.XferData(ref outData, ref xferLen); // „XferData‟ does not return until the full data is transferred.

// RECEIVE: 
xferLen = XFERSIZE_IN; 
// Returns true if the transaction successfully completes before TimeOut has elapsed
// „XferData‟ does not return until the full data is transferred.
// In other words, the application is blocked until the full data transfer completes.
inEndpoint.TimeOut = inEndpointTimeOut; // default 10000 

bool bResultR = inEndpoint.XferData(ref inData, ref xferLen); // This is where strange  thing happens: get back result immediately (before beeps finished at Cy firmware)

/* Note1:  the 'too soon' inData contains the expceted result !!:  see Cy firmware reply part:

buffer_in[0] = '(';
buffer_in[63] = ')';
buffer_in[1] = 1u; // means: command received, execution has taken place
buffer_in[2] = 1u; // means: no error

*/

// Note2: anyway: the beep function fired and done after this.

Best Regards,

Péter

0 Likes

Péter,

Maybe this information will help:

USBFS = USB Full Speed.   This means the top data rate it can achieve is 12 Mbits/s.

Using 8 bit data and accounting for packet overhead you can achieve about 1.2 to 1.3 MBytes/s.

Therefore you you are intending to send 1000 Bytes of data from the PSoC to your PC terminal program, it will actual take < 840us to complete the transfer to the PC.    If you're using a PC terminal program set at 9600 baud it will display the 1000 bytes of data at a slower rate (~ 1 sec nearly 1000x slower).  The reality is that the 1000 bytes are sent at 1.2 Mbps from the PSoC and is buffered in the PC.  The PC terminal program then 'meters' the data at your desired speed.  This means you could set the PC terminal program to 10 Mbps and not miss any data from the PSoC.

Therefore, this is my understanding why :


Nevertheless, the beep function void version returns almost immediately when checking things from the USBFS / desktop C# side 😞

...

bool bResultR = inEndpoint.XferData(ref inData, ref xferLen); // This is where strange  thing happens: get back result immediately (before beeps finished at Cy firmware)

...


The actual transfer from the PSoC occurs very fast and the data transfer is completed quickly.

I'm assuming you set the beeping function timing to approximately equal the 'expected' data rate at the PC end.  This is not the case.

The USB standard is designed to support multiple end-point nodes on the same wire.  This means it will try to communicate at the maximum allowable data rate (12 Mbps) for each end-point session.  The 'illusion' of a COM port emulation at slower data rates on the PC end is more to be compatible with legacy programs.  The COM port (UART-style) emulation will actually use a flow-control within the PC to ensure the data is processed at the proper speed.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Dear Len,

Thanks for your kind help,  I know that such a conversation is always an interesting situation (one side does not really know how educated or experienced old bear the other side is). I'm an almost fifty-year-old developer (engineer), I've been working with PSoC 5lp since about 2015 or 2014 (do not remember).

 

For your kind information: in the prototype phase we also use the Cypress UART component and with it the C# System.IO.Ports.SerialPort object.

 

But then - specifically as we are talking about now - we use the Cypress USBFS component (mostly in INT or bulk modes) from the PC / C# side and using CyUSB.NET (CyUSB.dll) (which is just a .NET wrapper for CyUSB3.sys driver). So we don't use a PC terminal program (such as Teraterm) or anything that appears as a virtual COM port from the Windows / C# side (even if I write the C# software). Just because the experience was that it is better to use the direct line: use CyUSB.dll instead of the SerialPort object (mainly due to performance). 

 

Like what you're talking about: CyUSB.NET in PC side is almost as fast as the PSoC side (compared to Terminal Programs / SerialPort).

 

We have performed many, many performance tests 5-7 years ago and we know what CyUSB.NET can do (we are also satisfied with it, by the way: if something 'rougher' (more performant I mean) is needed, we use the Cypress USB3 (FX3) with its own firmware wrtitten be us - sorry, I wandered off. Back to my original train of thought:

 

So, as the implemented sequence on the PSoC side (like a Slave):
- has new data been received ?,
-if yes and beep command, then call beep function ...
- response to the PC !

 

and the sequence from PC side (like a Master):

- outEndpoint.XferData(ref outData, ref xferLen); // „XferData‟ does not return until the full data is transferred. // send 'packet ' (64 bytes) with beep command request to PSoC

(-the PSoC side should beep at this point)

-inEndpoint.XferData(ref inData, ref xferLen); // Read reply from PSoC

 

We don't understand how it can happen that we get the answer (reply) sooner (in addition to the PSoC side we modulated with a good answer) than we hear the beep.

 

At first we thought that it might be a bug in CyUSB.NET (the documentation clearly describes that the XferData function used to receive the response is blocking and whether it is not). But then we tested this and it works properly (according as the documentation says). And anyway, it can't be a desktop-side problem, because in Cypress's response it returns (correctly) the bytes that we set AFTER the beep. So, for me, the logical conclusion is that Cypress somehow changes the order (I have never seen this before in my life either: the USBFS response is called first and then the beep function - and in the firmware it is written exactly the other way around).

Let me emphasize that this only happens in the special case that there are only void statements under the called sub-function (like beep example).

It is also true that there is a workaround:

if we reWrite beep function to non-void and  put its return value in some fake variable. e.g.:

uint8 someFakeVariable = Beep_VERY_Short_SOS_Nx(3u); // non void version

or make a fake non void call inside Beep function , like uint result = DivideByTwo(4u) ..

then everything is as expected (order/sequence as written in the firmware, the order is: first the beep call, then the USBFS response to the PC).

 

That's why I think that this is not the interference of the USBFS polling interval (4 / 10 ms) with the main loop's data waiting and/or data processing or command execution, but:
-some bug with USBFS auto DMA implementation,
- or I missed something basic in the last 30 years 🙂

 

Anyone with a little PSoC and C# routines can try (reproduce) it.

 

Thanks your your kind reply,

Have a nice day !

Péter

 

 

 

 

 

 

 

0 Likes

Péter,

You are correct.  I didn't fully realize your experience level in the original conversations.  I'm glad to see you are a seasoned veteran in the embedded world.

I'm still trying to wrap my head around your issue.  Do you have a minimized version of your project for me to try out?

The 'void'/'non-void' observations you are citing seem a bit strange.

Your use of Write()s and CyDelay()s seem very straightforward.  Each are blocking functions.  This is especially true of CyDelay().   Because of its blocking nature, its use should be cautiously used in applications that require real-time processing of data.

When I need to time function calls, I use a 'framing' method.  I place a PIN_Write(1) when I enter the function and a PIN_Write(0) when I exit.  I can then monitor the assigned GPIO state with a scope.

Additionally, I realize the ARM C compiler has some very nice optimizations.   I am not aware of an optimization where the "bulk" of a void function is launched as a virtual task and the function returns immediately in the calling task if the return is void.

Are you using an RTOS on the PSoC side?

(Note:  I did get 'bit' by a optimization where a variable used in common between an ISR and the main task was assigned to a register.  This caused my ISR to erratically signal data to the main task.  I had to assign the shared variable as 'volatile' to get it to work properly.)

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Péter,

I've created a very simple project that uses your Beep_VERY_Short_Nx() function as a void.

I added a framing pulse prior to entering your function and upon exiting your function.

The main task is:

 

int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    for(;;)
    {
Pin_frame_Write(1u); // frame ON
		Beep_VERY_Short_Nx(10);
Pin_frame_Write(0u); // frame OFF
		CyDelay(1000);
    }
}

 

The void Beep_VERY_Short_Nx() does not return until the Buzzer pin is toggled (as expected).

PRINT_04.png

I realize my project does not include many of the additional resources that you are using such as the USBFS.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
PeSe_1509996
Level 3
Level 3
25 replies posted 10 sign-ins 5 questions asked

Dear Len,

First of all, thank you for your attention.

To answer your questions :

-'Are you using an RTOS on the PSoC side?': no, this project is a pure/bare PSoC.

-volatile qualifier for variables especially used under under any ISR: you know I'm seasoned veteran 🙂

of course: we know / dare / use it 🙂

As well as the other things, like:

CyGlobalIntEnable / CyDisableInts() / CyEnableInts(uint32 mask) / CyEnterCriticalSection(void) / CyExitCriticalSection(uint8 savedIntrStatus), etc.

 

-for 'framing' (toggling a Pin for Start and Stop events and check it by scope or a logic analyzer: I did a lot in the past as this is the only way to know what happens in real time process. For this : I haven't looked at it this way for the referenced problem, because it looks trivial even without it. But otherwise, it's a good idea.

 

-For minimized sample working project (desktop and PSoC): 

I don't have a clipped version right now, but if it doesn't take too much time, I will make one.

 

It's always difficult to balance: live project, tight deadlines, there is already a workaround, but I almost always follow up on what I don't understand or what doesn't work according to expectations.
Give me some time to look.

 

Until then,

Best Regards,

Péter

 

 

 

 

 

0 Likes
lock attach
Attachments are accessible only for community members.

Péter,

Here's the project I was using if this helps.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
PeSe_1509996
Level 3
Level 3
25 replies posted 10 sign-ins 5 questions asked

Dear Len,
Big Thanks, but that's too clipped approach to the problem (as I mentioned the problem should come with USBFS auto DMA working togetheer with C# CyUSB.NET).

Do you have a CyKit-059 with a Windows with installed CyUSB3.sys and e.g.  Visual Studio 2019 or 2022 ?

Because if you have, I will deal with it for an hour to make a sample PSoC and C# (e.g. WPF  / .NET 4.8) sample for you.

 

In this case I replace or add a built in led to our buzzer pin.

 

Have a nice day !

Péter

0 Likes

Péter,


Do you have a CyKit-059 with a Windows with installed CyUSB3.sys and e.g.  Visual Studio 2019 or 2022 ?

Yes to both questions.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
lock attach
Attachments are accessible only for community members.

Dear Len,

If I make a clipped version (cutting out other commonly used elements, such as SPI master, I2C_1 + multiplexer, Digital input pins with interrupts, SW_Tx_UART_1, Counters, Timers, etc.),

I can't produce the strange phenomenon either 😞

Therefore, the workaround remains for now.

 

But since it's already done, I'm sending it over (it probably doesn't make sense, because I can't produce the strange phenomenon here either). Unless you could test it by putting it in a project that contains a few usual, more complicated elements (such I2C, SPI devices, Timers - mean some real sensors and actuators, like in my project etc.).

At least you can see that I worked on the case 🙂

 

some background info for test project:

-Use microUSB connector side of CyKit-059 (not Programmer/Debugger flat connector)

-The behavior of the buzzer has been extended to include the built in blue LED

-The project use fixed 64 byte packet with proprietary format for robust communication between PC and PSoC (fixed start and end char,  Counter, Random part, Command). The only important part in this case is the Command.

Some parts from this format are  clipped (like PSoC BusyState, ErrorState, CommandProcessed)

-Endianess: Little Endian for both party (Win PC and PSoC)

 

I don't think it's worth working too much with it, because it's very difficult to reproduce (I didn't succeed in the clipped version either).
Let's stick with it: we've already seen this once, but it will be on my check list in the future.

 

Best Regards,

Péter(ror)

 

 

 

 

0 Likes

Péter(ror),

I'll take a look at the clipped version.

Suggestion:  Compare the Disassembled version of Beep_VERY_Short_Nx() from the clipped project to the Disassembled version of the same function from the full project with the issue.

In effect, the two disassembled version should be nearly identical.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Péter(ror),

I've downloaded both uploads.  You are correct.  The issue doesn't occur in when accessing the void or non-void version of the code.

The 'clipped' version is missing some components.  I realize that your project is proprietary.  What are the missing components?  I can add them into the project with some minor code to exercise them.  Maybe this might force the issue to re-appear.

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Dear Len,

Please, give me your e-mail address and I will send some screenshot about it.

 

Let me emphasize that I don't think you should spend too much time with it: lest I spoil something and you spend too much time with it.

Regards,

Péter

 

 

0 Likes

Hi @PeSe_1509996  ,

Thread was locked due to inactivity for long time, you can continue the discussion on the topic by opening a new thread with reference to the locked one. The continuous discussion in an inactive thread may mostly be unattended by community users.

Thanks and Regards,
Alen
Cypress Semiconductor Corporation
An Infineon Technologies Company

0 Likes