Author Topic: Simulator Stops Sending Serial Output  (Read 14709 times)

Offline danh

  • Newbie
  • *
  • Posts: 28
    • View Profile
Simulator Stops Sending Serial Output
« on: August 02, 2008, 01:21:19 AM »
I use one of the serial ports on my 52235 target board for debugging. One of the things I do with it is dump a bunch of settings. This works fine on my target board; but I just started doing the same thing running in the simulator, and sometimes it just hangs instead of sending the data.

Here's a test case that easily reproduces the issue:

Code: [Select]
   uStrcpy(buf, "This is a test, this is only a test!\r\n");
   for(i = 0 ; i < 50 ; i++) {
      while(!fnWrite(SerialPortID, 0, uStrlen(buf)))
         ;
      fnDebugMsg(buf);
   }

It hangs in the while loop that checks if there is space in the TX buffer, sucking up cpu cycles. I tried sticking a call to Sleep() in the while loop, and while the simulator doesn't then suck up cpu cycles, it still never gets out of the loop. If I just remove the loop it never hangs, but it only actually sends a subset of the data as you would expect.

Sometimes it works, sometimes it doesn't. I haven't figured out any pattern to it. I have tried it with COM2, which is on an internal PCI card, and I have also tried it on COM8, which is on an external USB-serial device. I get the same results either way.

Keep in mind it always works fine on my target board.

Has anyone else tried something like this with uarts in the simulator?

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3240
    • View Profile
    • uTasker
Re: Simulator Stops Sending Serial Output
« Reply #1 on: August 02, 2008, 12:27:06 PM »
Hi Dan

I think that I can explain what you have seen:

Basically, the uTasker simulator doesn't actually send any data to the serial port until the scheduler returns. What it does is set a flag to indicate that there is data waiting and then - when the present schedule has completed - the UART simulator will send x bytes to the UART (the amount of data depends on the UART speed and the system TICK, where it corresponds to the amount of data bytes which would normally be transferred at the given BAUD in one TICK).

This usually works adequately accurately but it is not identical to the operation on the target. The target will be sending its data over the UART (using interrupts at the BAUD rate) as other processing continues. This means that the output buffer will become empty if you wait (like when you add the loop) and so all data will be sent. In the case of the simulator, which doesn't actually start sending anything yet, the buffer will remain full and so the loop will hang.

The basic reason for the simulator operation is that interrupts are not handled in hardware. To simulate more exactly, additional threads would probably be required which would make it rather more complicated. The OS part and HW simulator part are very much separated, which has a lot of advantages with respect to simplicity (and probably reliability) but a couple of disadvantages with regard to absolute accuracy.

However there is another point:

If you run the test code on the target it will work since the CPU is really hanging around in the while loop until the data has been transmitted. This may not be the best solution since it means that other tasks in the project will be blocked during this time (which can be quite a long time if the BAUD rate is low). The idea is to avoid this by making use of the TX_FREE event, which will then also allow the simulator to operate accurately again (there should then be no noticeable differences to the target operation).

Consider the sub-routine below, which is also sending a number of copies of the test message:

Code: [Select]
int fnMyTest(int iStart, int iStop)
{
  CHAR buf[100];
   uStrcpy(buf, "This is a test, this is only a test!\r\n");
   for(i = iStart ; i < iStop ; i++) {
      if (!fnWrite(SerialPortID, 0, uStrlen(buf))) {
          fnDriver( SerialPortID, MODIFY_WAKEUP, (MODIFY_TX | OWN_TASK) );
          break;
      }
      fnDebugMsg(buf);
   }
   return iStart;
}

Here I have made the start and stop values adjustable - the first call will use 0 and 50 as parameters to result in the test as you defined it.
However, if the output buffer becomes full - rather than blocking while the transmission completes - the loop is terminated and the present loop counter returned. The owner task is entered so that it will be woken when the output buffer has adequate space to add more data. The task handling the test can save the returned value (static variable) and quit. Later, when the buffer has space again, the task receives the TX_FREE interrupt event and can recall the routine (repeating if necessary until the returned value is equal to the stop value).

See also the function static int fnDisplayHelp(int iStart) in debug.c in the demo project. This also uses this technique to generate menus, which works even with very small output buffer sizes (also Telnet output operates the same way). Note also its handling of TX_FREE in its task...

If the output buffer space is not checked, copies of data will be chopped as the buffer becomes full (remaining characters silently discarded) but using this mechanism allows the output UART buffer to be kept quite small (essentially it must have space for one write to be completely accepted).

Note that anther solution is to increase the size of the UART output buffer so that it accepts everything that is thrown at it (about 2k would be sufficient for handle your test case [once]).

Regards

Mark

Offline danh

  • Newbie
  • *
  • Posts: 28
    • View Profile
Re: Simulator Stops Sending Serial Output
« Reply #2 on: August 04, 2008, 05:52:39 PM »
Hi Mark,

Yes, that all makes sense. Good point about the looping and essentially blocking other tasks. I could live with that since I am dumping debug stuff only during development, but it is a bit sloppy. I reworked my code to use the TX_FREE event as you suggested, and now everything is happy.

Thanks!

Dan