5.1 C++ issues
5.2 Communication problems
5.3 Interrupts
5.4 LoadModule problems
5.4A malloc and other memory issues
5.5 pthreads
5.6 Reboot
5.7 Semaphores
5.8 Simulator (VxSim)
5.9 Task related items
5.10 Time related items
5.11 Wind Web Server related items
5.12 Zinc/windML related items
5.13 Other items
5.14 VxWorks AE questions
Index
Q: When I download some C++ code my target crashes, even when I don't start anything.
A: What is probably happening is any static or global classes that you
have defined are having their constructors called. The class is actually
being instantiated when the file is loaded into memory, thus the
constructor is called.
(From: nick_nw@my-deja.com)
Q: What is the syntax of "taskSpawn" for spawning a method of an object (C++) as a task.
A: See question 5.9-F.
Q: How can I call a C++ function from within a C-file?
A: See question 1.1-H.
Q: What is the preferred extension for C++ files?
A: For C++-files you can use .C (compared to .c for C-files), .cc, .cpp
or .cxx. Don't use .C (uppercase) as an extension for C++. It works ok on
Unix hosts, but won't work if you develop on Windoze... for obvious
reasons.
The ending I prefer is .cxx. Certain MS compilers don't like .cc, GCC
doesn't (or at least didn't in the past) like .cpp. Both take .cxx.
(From: engineer_scotty, sj@aracnet.com)
Q: Gotcha with C++ static constructors and GCC 2.95.x (PPC)
A: After several hours of debugging and pulling out my hair :), I've just found a (afaik undocumented) bug with GCC 2.95.x which may cause static constructors/destructors to NOT work when loaded onto the target using the target-based loader (the ld/loadModule) commands. Don't know if this effects the host-based shell or not.
Essentially, the problem is as follows: The munch script can be told to produce assembly language output (actually a bunch of __asm__ directives for the C compiler to swallow) or C output. When the latter is used, additional flags must be passed to the compiler (-fdollars-in-identifiers) to get the output of munch to compile; otherwise the presence of $ in the symbols will cause the compile to fail. For this reason, the Tornado makefiles default to using the -asm option; the assembler has no problem with dollar signs.
The munched file, when compiled (by either method) containts two arrays of function pointers, _ctors and _dtors; these are a NULL-terminated list of the "constructor functions" and "destructor functions" (functions, one per file that has static ctors/dtors, which call all the static ctors/dtors in the file). This seems to work fine with the 2.7.2 toolchain. But with the 2.95.x toolchain; using the assembler rather than the compiler causes a subtle change in the symbol table entry for the _ctors and _dtors symbols (one that will NOT show up with nmppc; only objdumpppc -t reveals this). The "object" bit, which I suppose didn't exist with the 2.7 binutils; is NOT set when the input to the compiler is assembly code.
When the output of munch is linked back in with the module; everything is
fine until you try loading it on the target. For some reason; loadLib
DOES NOT RECOGNIZE _ctors and _dtors as valid symbols and thus does NOT
load them into the symbol table. When cplusCtors is run (either manually,
or automatically if cplusXtorSet is used); it looks in the symbol table
to find _ctors; cannot find it; assumes that the module has no
constructors, and silently exits. (The same applies for cplusDtors and
_dtors).
The workaround is to add the line
MUNCH_FLAGS=
to your makefiles. This will force munch to output C code (which must be compiled with -fdollars-in-identifiers); which will have the "object" bit set in the generated object file. loadLib will then properly generate symbol table entries for _ctors and _dtors, and all will be well.
Again, this affects GCC 2.95.x (using DaveK's stuff) on PowerPC. Don't
know about other architectures. The 2.7.2 toolchain that ships with
Tornado also seems to be unaffected.
(From: sj@shell1.aracnet.com, Scott Johnson)
Q: Is there any instance where msgQDelete can be blocking?
A: Yes, as with a lot of create and delete functions in VxWorks,
msgQDelete()
will make use of the dynamic memory management
software to release the memory that was claimed for the message queue
data structures (both the kernel's object structure and the buffer for
messages in this case).
Since access to memory partitions is guarded by a mutex (a good idea!),
it is certainly possible that any xxxCreate or xxxDelete routine could
block. You shouldn't be using these functions in high performance blocks
of code - it is generally best to keep your create and delete operations
to initialisation/shutdown regions of the code.
Now, in the case of message queues, an additional step is performed: the
delete function makes sure that it 'holds' all the message slots so if
there are any messages already in the queue they will still be delivered
(and your task that is trying to delete the message queue will block
until they are). For most people, that is the desirable behaviour since
it makes sure that no messages are lost...
(From: john_94501@my-deja.com)
Q: In my BSP there is an intConnect, but no intDisconnect. How can I disconnect a function from a interrupt?
A: I have attached an "intDisconnect()
" routine for PPC that
has served me well for many years. Just include it as an extra object
when you are compiling your kernel. It takes one argument (the interrupt
vector).
intdisc.c
(From: David Abbott, abbottd@jlab.org)
Q: What does "workQPanic: Kernel work queue overflow" mean?
A: It usually means that one of your Interrupt Service Routines is not
clearing the IRQ status properly, so on exiting the ISR the interrupt
line is still active and it is just called again immediately. If you
can't work out which it might help to try and get a logic analyser to
look at the interrupts that are occurring.
(From: Andrew Johnson, anj@aps.anl.gov)
Q: What does the function "__get_eh_context" do?
A: "__get_eh_context" is an abbreviation for "get exception handler
context". This will be called if you have C++ code, you are executing in
an objects context and you have exception handling enabled. The code uses
the `this' pointer and can setup RTTI (run time type identification) and
other C++ facilities.
You could use only Embedded C++ constructs and you will probably be ok.
Alternatively, don't allocate any objects, even on the stack in your ISR.
Object construction can call malloc and other routines that you shouldn't
use. It will definitely setup an exception context so that if an
exception is thrown, the object can be deleted.
(From: Bill Pringlemeir, spam_account@sympatico.ca)
C++ can be used in ISRs with extreme caution, like having the ISR in a
separate module that you can build using -fno-exceptions and -fno-rtti
and being careful not to construct any objects and watching out for
temporaries caused by reference parameters and so on, but there are so
many pitfalls that my advice would be to always write such lowlevel code
as ISRs and even the lower layers of device drivers in plain C.
(From: Dave Korn)
Q: After calling intlock it seems that the interrupts are not locked when looking at the interrupt control register.
A: Seems that people still do not understand the scope of intLock(). It
works only for the task context that calls it. If that task blocks (or
exits), then interrupts will most probably be re-enabled. If the task
only blocked, when it resumes execution they will be locked again.
Why? Because the MSR (using PPC) is in the task context. You should not
block with interrupts locked (or, for that matter, after calling
taskLock() either). Switching to a different task, or the idle loop, will
re-enable the interrupts.
As for the wind shell (host shell), when you execute a command there,
the target agent spawns a task to call the function. So calling intLock()
from the wind shell results in a task on the target executing intLock()
and exiting. When the task exits, the scheduler will be invoked and when
the new task is selected, its value for the MSR will be restored.
(From: John, john_94501@yahoo.com)
(Note: this is also the case on MIPS. Also when calling intEnable on
MIPS the interrupt will only be enabled for the task where you called
intEnable; Johan)
Q: When I try to load a module with unresolved externals the function loadModule returns a module-ID instead of returning NULL, as described in the manual. How do I detect if there are unresolved externals?
A: First set errno to 0, then execute the call to loadModule, and if this
call returns a valid module-ID check errno again. If errno indicates an
error there probably were some unresolved externals. In this case it is
better to unload the module again.
(From : Bob Marino, ram@mr.picker.com)
Q: The ld command, run from a script, is behaving differently on a UNIX and an NT box.
A: I've observed a difference with the behavior of startup scripts. The ld command's normal form:
ld < foo
worked fine with a UNIX host, but failed on NT. I eventually found that the terminating >CR< was being included in the file name read from the script and passed back to the ftp server. The workaround was to use an alternate form of the ld command in startup scripts:
ld 1,0,"foo"
This works fine with both UNIX and NT hosts.
(From: Jerry Hudgins, jerry@e-farm.com)
Note: this section is added later, but to keep the order right and not have to renumber all sections I added it as 4A.
See section 6.2 for information about memory leak detection
Q: The VxWorks port of an alternative code for dynamic memory allocation, made by Doug Lea.
A: This post provides details of getting Doug Lea's allocator (dlalloc) to
work with vxWorks 5.4 (possibly 5.5).
See: "ftp://gee.cs.oswego.edu/pub/misc/malloc.c"
These posts are based on version 2.7.2 of the allocator.
There are three files.
Q: One of my tasks in getting suspended all the time, in memPartFree. What is causing this?
A: In vxWorks land, freeing same memory location twice will suspend the
task which has called free() second time. This will cause exception
similar to one above suspending the task.
The way to fix this is by making sure you don't free memory twice.
(From: Narendra Hadke, narenh@intotoinc.com)
Q: Has anyone ported pthreads.h from Linux to VxWorks?
A: Yeah, we've done that. The mapping from pthreads to VxWorks task_* et
al. is pretty straightforward. The only thing missing is condition
variables. They turned out to be kind of hard. You might also be able to
use VxWorks semaphores, but we took a different approach. We used a msgQ
and taskSuspend()/taskResume(). See this file for the implementation:
pthreads.c
The rest of the implementation is very easy, so I won't bore you with
the details.
(From: Craig Vanderborgh, craigv@voxware.com)
Q: Is there a way to implement the VxWorks API on top of pthreads, to simulate VxWorks on a different system?
A: Check out http://www.mvista.com -
they've recently published a white paper that discusses this and the
problems and differences between the two APIs.
(From: Andrew Johnson, anj@aps.anl.gov)
Q: When I do a hard reboot the system reacts differently than when using Ctrl-X. The reason is that Ctrl-X does a reboot(0), while a hard reboot does a reboot(2), a boot clear. How can I let Ctrl-X do a reboot(2)?
A: Unfortunately, the ^x functionality is hardwired to do a "reboot (BOOT_WARM_AUTOBOOT)" call when the reboot character is recognized. There isn't any way to change what type of reboot is done. Some possible ways around this (none pretty) are to:
The first option is the easiest by far.
(From: Fred Roeber, froeber@bbn.com)
Q: It looks like the priority inversion mechanism is not working correctly.
A: This may not be the issue here, but you should note the throw-away line in the semMLib description:
"The task will return to its normal priority only after relinquishing all of its mutual-exclusion semaphores that have the inversion-safety option enabled".
So if your Task Two happens to have hold of some other (probably
unrelated) inversion-safe mutex, this will prevent it from reverting to
its former priority, even though Task One is not waiting on the semaphore
you are currently focussing on. (This was the cause of a deadlock I
recently encountered!)
(From: Will Fookes, will@noSpam.wfookes.co.uk)
Q: How can I start multiple simulators on NT and have these communicating?
A: While in a VxWorks class, the instructor gave me the following info.
With this I was able to finally get it to work!
Just two things:
development tool components -> WDB agent components ->
select WDB connection -> WDB END driver connection
The info in in this file.
(From: Luc Pariseau, lparisea@nortelnetworks.com)
Q: How do I increase the memory of the simulator?
A: Use the /r switch to control the amount of memory of the simulator. The size is in bytes, and not in kBytes as the manual states. You also have to build a custom-configured simulator, as the default simulator does not support the /r-switch.
In case the command line is not an option, here is
another way: I think the magic line you want to change is in
01Simulator.win32.tcl: [::simulatorLaunch $imageName $pNo $workingDir
30000] \ and the 30000 will be your memory size. If you don't want to
brute force it you can add code to the showImageDlg procedure (look at
the dialog on the bottom of the file) to prompt you for it. Right now it
seems as if it's hardcoded on win32 platform and they prompt you only on
UNIX.. (look in the Launch directory for the UNIX implementation)
(From: Steven Szabo, Steven.Szabo@motorola.com)
Q: How can I have 2 simulators communicate with eachother on Win2K box?
A: You need to setup the ULIP driver correctly, which means you need to
enable IP-Routing.
Here is the result of my "ipconfig /all" (Sorry, output is in German!
(Ja means Yes, Nein means No)):
Windows 2000-IP-Konfiguration Hostname. . . . . . . . . . . . . : martin Primares DNS-Suffix . . . . . . . : Knotentyp . . . . . . . . . . . . : Hybridadapter IP-Routing aktiviert. . . . . . . : Ja <------------------- WINS-Proxy aktiviert. . . . . . . : Nein Ethernetadapter "vxSIM WindRiver": Verbindungsspezifisches DNS-Suffix: Beschreibung. . . . . . . . . . . : WindRiver ULIP Physikalische Adresse . . . . . . : 00-00-00-00-00-00 DHCP-aktiviert. . . . . . . . . . : Nein IP-Adresse. . . . . . . . . . . . : 90.0.0.254 <-------------------- Subnetzmaske. . . . . . . . . . . : 255.255.0.0 <------------------- Standardgateway . . . . . . . . . : DNS-Server. . . . . . . . . . . . :
Pinging from one to the other vxSim works fine. But you need to start the
Simulator using a processor number >0, then you will get the target
server calling parameters to start with wdbrpc: 90.0.0.2 -B wdbrpc
-Bt 3 -R f:\TEMP\tsfs -RW -n "vxsim1@martin"
Here is my output:
Simulator #1 to #3:
-> ping "90.0.0.4" PING 90.0.0.4: 56 data bytes 64 bytes from vxTarget (90.0.0.4): icmp_seq=0. time=0. ms
Simulator #3 to #1:
-> ping "90.0.0.2" PING 90.0.0.2: 56 data bytes 64 bytes from vxTarget (90.0.0.2): icmp_seq=0. time=0. ms
Simulator #3 to win2k host:
-> ping "90.0.0.254" PING 90.0.0.254: 56 data bytes 64 bytes from host (90.0.0.254): icmp_seq=0. time=0. ms
(From: Martin Raabe, Martin.Raabe@WindRiver.com)
Q: The font in my VxSim window is very small.
A: You are probably using Regional settings other than English. If you
change it to English, it should work. There is also another way where you
don't have to change regional settings and it involves tempering with the
code of VxWorks.exe.
(From: Tomislav Razov, tomislav.razov@siemens.hr)
It is also possible to use the following tool. The
exe file should be run after each time you run the simulator. We
integrated the call into the resposible TCL file, which is attached also. The tool (source and
executable) can be found here: SimFont.zip.
(From: Ofer Goren, ofer_goren@iamba.com)
Q: How can I make 2 simulators communicate on a Solaris box?
A: Enable IP forwarding on solaris.
ndd -set /dev/ip ip_forwarding 1
(From: Vijay Paul, paulv@lucent.com)
Q: What is the use of taskDelay(0)?
A: The following msg was posted by Dave Schmidt of Wind River on the newsgroup concerning taskDelay (0):
That will put the current task at the end of the ready queue for the current priority. It will allow any other tasks on the ready queue of the same priority to be run (sort of round robin, if you issue taskDelay(0) for them also when you want to go to the next task), of course all higher priority tasks will run and intterrupt service routines will also run.
Please note that this method will NOT allow tasks of lower priority to run, it just forces a rescheduling. If the task being delayed is the only one of that priority, then it will restart immediately.
Q: How do I use taskInit?
A: First: why use taskInit? If you want to synchronise startup use
taskInit and let the new task(s) wait on a semaphore.
But if you still want to use it, here are some examples:
From john_94501@my-deja.com:
This code is cut from something that uses taskInit in a portable way
(i.e. it takes into account the stack direction of the CPU being used).
This is another reason why I prefer to use taskSpawn!
int ret; char *memArea; /* initialize TCB and stack space */ memArea = (char *) malloc (STACK_ROUND_UP(STACK_SIZE) + sizeof (WIND_TCB) + 16); /* initialize task */ ret = taskInit ((WIND_TCB *) (memArea + 16), NULL, 150, VX_NO_STACK_FILL, #if (_STACK_DIR == _STACK_GROWS_DOWN) (char *) (memArea + STACK_SIZE + sizeof (WIND_TCB)), #else memArea + STACK_ROUND_UP (sizeof (WIND_TCB) + 16), #endif STACK_SIZE, (FUNCPTR) function, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); /* args */ if (ret == ERROR) { perror ("taskInit"); return(0); } else { taskActivate ((int) memArea); }
And that is basically it! Your task should be running by the end of that sequence. If you wish to delay the starting point just move the call to taskActivate().
Another example from steven.offenbacher@jhuapl.edu:
I also have a working example where I don't use malloc. I statically create the TCB and stack.
WIND_TCB tMonitorTcb; char tMonitorStack[STACK_SIZE]; /* Example #04. taskInit/taskActivate method using statically allocated TCB and stack */ status = taskInit(&tMonitorTcb, /* TCB Address */ "tMonitorMethod04", /* Task/Thread Name */ 240, /* Priority 0=Highest */ VX_NO_STACK_FILL, /* Options */ /* Determine which way stack grows and adjust address if necessary */ #if (_STACK_DIR == _STACK_GROWS_DOWN) tMonitorStack+STACK_SIZE, /* Stack base address */ #else tMontiorStack, /* Stack base address */ #endif STACK_SIZE, /* Stack size in bytes*/ (FUNCPTR) monitor, /* Task entry point */ 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0); /* VxW Req 10 parms */ /* Task exists but is not running at this point. The debugger can now attach to the task and set breakpoints provided the user does not detach or attach to some other task */ if (status == OK) /* Only activate if init ok */ status = taskActivate((int)&tMonitorTcb);/* Actually start task */
Q: When the priority of a task is requested using taskPriorityGet it is possible that this is not the real value, as this might change due to priority inversion. How do I get the right value?
A: The normal priority is kept in the TCB. It can be read using this command:
priority = (taskTcb(taskIdSelf()))->priNormal;
(From cwein@my-deja.com and Pete Kockritz, pkockritz@home.com)
Q: Using WindView, I've noticed a task that has just completed (eg. by returning) doing the following in close sucession:
Why this order, the task would have been deleted after the first call to taskDelete anyway.
A: The sequence is not only familiar, but it is correct, at least as far
as the deletion of the task... let me explain in more detail...
Normally, when one task deletes another the one doing the work has a
context, stack etc to execute the task deletion code on. This is
necessary, obviously, since the code performing the deletion is simply C
code, and might block or be interrupted.
Now, when a task deletes itself it has a small problem: it can't delete
its own stack and context information (TCB), since that would leave the
executing code without any stack or context. OK, it could lock interrupts
until it was all cleaned up, but that would not be good in a real time OS
where interrupt latency is an important factor.
So, the taskDelete() code performs some checks up front, and if it is
deleting itself the work is actually passed to another task to do
(tExcTask in this case). The actual code is a little more involved, but
this is a rough idea of what happens (function names not accurate, but
chosen to express the concept):
if (taskToDelete == taskIdSelf() { queueJob (taskDelete, taskToDelete); taskSuspend (0); }
Since the exception task is a high priority task it will preempt the task
deleting that was itself in most cases. At some point in the process, the
taskDelete operation blocked for something. At that time, the original
task was rescheduled, and promptly suspended itself (after all, it is
expecting to die very soon...).
Your description sounds pretty much like what I'd expect WindView to see
for a task deletion. The "exception" task is not really that well named
since it is used for more than just displaying exception messages.
Consider it to be a kernel thread, used for the OS to push work out to
task level, much like an ISR that signals an associated task to perform
the actual work.
(From: john_94501@my-deja.com)
Q: How can I build a stack-trace?
A: You can do this using an undocumented function called "trcStack". Here is all you need. You can even call xxxTrace(tid) from interrupt level... though logMsg() is far from ideal.
/******************************************************************************* * * xxxTracePrint - stack trace print function * * RETURNS: OK or ERROR */ static void xxxTracePrint ( INSTR *caller, int func, int nargs, int *args ) { char buf [250]; int ix; int len = 0; len += sprintf (&buf [len], "%#10x: %#10x (", (int)caller, func); for (ix = 0; ix < nargs; ix++) { if (ix != 0) len += sprintf (&buf [len], ", "); len += sprintf (&buf [len], "%#x", args [ix]); } len += sprintf (&buf [len], ")\n"); logMsg (buf); } /******************************************************************************* * * xxxTrace - stack trace * * RETURNS: OK or ERROR */ int xxxTrace ( WIND_TCB *pTcb ) { REG_SET regs; if (pTcb == NULL) return (ERROR); taskRegsGet ((int)pTcb, ®s); trcStack (®s, (FUNCPTR) xxxTracePrint, (int)pTcb); return (OK); }
(From: Geoffrey Espin, espin@idiom.com)
Q: What is the syntax of "taskSpawn" for spawning a method of an object (C++) as a task.
A: I use the following code snippets to spawn a method as a task. Dont forget the '(int)this' for the first parameter.
//start sampling data count and timestamp timeCodeGather = true; tid = taskSpawn("tTimestamp", 250, 0, 20000, (FUNCPTR)TimeStamp::addNewTimeStamp, (int)this, 0, 0, 0, 0, 0, 0, 0, 0, 0); void TimeStamp::stopTimeCodeGatheringTask() { //## begin TimeStamp::stopTimeCodeGatheringTask%1945920847.body preserve=yes int rd; char tmpbuf[20]; if (timeCodeGather) { //stop timecode gathering task taskDelete(tid); ........
From: Bob, Bob@dskti.com)
Q: How can I detect a stack overflow?
A: In the past I used the next code (typically for x86 targets):
int switchHandler(WIND_TCB *pOldTcb, WIND_TCB *pNewTcb) { if ((pOldTcb->regs.esp < (ULONG)pOldTcb->pStackLimit) || (!(pOldTcb->options & VX_NO_STACK_FILL) && ((int)*(int *)(pOldTcb->pStackLimit) != 0xeeeeeeee))) { logMsg("Stack overflow task 0x%08x\n", (int)pOldTcb,0,0,0,0,0); taskSuspend((int)pOldTcb); /* __asm__("int $12"); */ } return 0; } int start_application(void) { /* Enable kind of dynamic stack checking */ if (taskSwitchHookAdd(switchHandler) == ERROR) { printf("taskSwitchHookAdd error\n"); return ERROR; } ... }
(From: Gerald van Kampen, kam@oce.nl)
Q: I want to block a task until all children are in a certain state (initialised, finished, etc.).
A: This can be done using semaphores:
/* * Global semaphore used by each terminating child CSP task. */ SEM_ID childFinished; /* * A child task. */ void child (void) { /* Normal child like activities. */ semGive (childFinished); } #define NUM_CHILDREN 5 /* * Parent task. */ void parent (void) { int childCount = 0; /* * Use a FIFO if you are more interested in speed than the order that * tasks will be blocked in. */ if ((childFinished = semCCreate (SEM_Q_FIFO, NUM_CHILDREN)) == NULL) { /* We're not in the mood for creating children :-( */ return; } /* Spawn children, the fun part ;-) */ do { /* Wait for all the children to complete their chores */ semTake (childFinished, WAIT_FOREVER); } while (++childCount < NUM_CHILDREN); /* Tidy up after children (usual parental type stuff) */ semDelete (childFinshed); }
(From: Alex Goodyear, spamless.agood@jet.uk)
Q: What are +T, +I thingies in the "i" output?
The following is an excellent description of all these symbols by many
people on the net, including "Fred J. Roeber" and others:
Description Status symbol ===================================== ===============and task's priority inherited + I Delayed and suspended DELAY+S Pended and suspended PEND+S Pended and Delayed PEND+T Pended, delayed and suspended PEND+S+T
Q: I would like to know how to measure the system time at a specific
moment, then at another moment, only a few milliseconds later, to
determine elapsed time.
I have tried clock_gettime(), but this is no good. I have checked the
clock resolution and it is at 60 ticks per second.
A: The following was written by Fred Roeber (froeber@bbn.com) in the newsgroup:
The easiest/best way to measure detailed elapsed time is to use any "timestamp" driver that may be supplied as part of your board's BSP. The timestamp driver is a generic name for a high resolution timing factility that is intended for WindView use but can also be used by user applications. The documentation for creating/using the timestamp facility is in Appendix G of the WindView manual and the functions are defined in h/drv/timer/timerDev.h (see sysTimestamp...).
Many BSPs come with timestamp driver support even if you don't buy WindView. You can add the configuration option INCLUDE_TIMESTAMP to build it into your system. You can then call sysTimestampEnable to start it up and sysTimestampLock to read detailed time values. You can call sysTimestampPeriod to find out how often the timer rolls over and sysTimestampFreq to see the rate it ticks at (which is often fast enough to provide microsecond resolution).
The reason I prefer this approach to cranking up the clock rate is that you typically get much higher resolution values without bogging down the system with all the overhead of frequent timer interrupts.
As with most things in VxWorks, caveats are needed in applying a generic facility to specific boards. First, not all boards have timestamp driver support. Second, on those that do, the timestamp sometimes has a pretty small period (it rolls over frequently). For instance, on the PPC platform, the "standard" timestamp driver derives the timestamps from the decrementer timer which is also used for the system clock. This means the time value rolls over at the rate the system clock rate; making it pretty useless for measuring things with a long duration.
There is an alternative timer for some PPC boards that uses the Z8536 chip. The other day, we needed to time startup code on our Radstone PPC board and I found that with a little work the PPC timestamp functions fit the bill nicely. The ppcZ8536Timer.c can be rewritten such that it only provides auxilliary clock and timestamp support (with no system clock support - that comes from ppcDecTimer.c). By using the 16 bit timers 1 and 2 in "linked" mode it was possible to get a nice 32 bit timer that rolled over every 20 minutes in our case. We left timer 3 on the chip for auxilliary clock support. The original timestamp functionality in the driver was, unfortunately, fairly well broken but not unsalvageable. Just trying to point out that in some (all?) cases you have to be careful when using VxWorks drivers.
Caveats aside, people should really consider the timestamp routines. They can often provide good timing insight with minimum trouble. Fred
Q: When I create a timer function my program crashes after the time specified instead of executing the function I specified. What is happening?
A: One thing to watch out for (it got me) is that your handler for the
timer is executed IN THE CONTEXT OF THE TASK THAT CREATED IT. If the
timer was created by some startup task which has since died and gone
away, then this will quite likely crash your system the first time that
the timer callback function is activated!
(From: Will Fookes, will@noSpam.wfookes.co.uk)
Q: How do I determine the number of ticks remaining in a watchdog timer?
A: I got this code from my FAE a few years ago. You will have to search for what you need to include for the state #defines and the OBJ_VERIFY() macro.
typedef struct { int Ticks; int State; /* OUT_OF_Q, IN_Q, DEAD */ } WD_INFO, *pWD_INFO; STATUS wdGetInfo ( WDOG_ID wdId, pWD_INFO wdInf ) { FAST int lock = 0; int ticks = 0; int state = 0; /* LOCK INTERRUPTS */ lock = intLock (); /*Verify watchdog ID */ if (OBJ_VERIFY (wdId, wdClassId) != OK) { /* UNLOCK INTERRUPTS */ intUNLOCK (lock); return ERROR; } /* record state */ state = (int) wdId->status; /*If not in queue then ticks are zero*/ if (state == WDOG_IN_Q) { ticks = Q_KEY (&tickQHead, &wdId->tickNode, 1); //ticks = 0; } else { ticks = 0; } /* UNLOCK INTERRUPTS */ intUNLOCK (lock); /* fill structure */ wdInf->Ticks = ticks; wdInf->State = state & 0x3; return OK; }
(From: Bruce, intmainNOinSPAM@yahoo.com.invalid)
Q: Errors in the time modules.
A: taskDelay(-1) shows another bug in the vxWorks timer/tick code. It has
the (side) effect of setting vxTicks to zero. This corrupts the localtime
(and probably other things).
In fact taskDelay(x) will have the same effect if vxTicks + x >=
0x100000000.
If the system clock rate is 100Hz this happens after about 500 days
(because vxTicks wraps). At faster clock rates it will happen sooner.
Anyone trying for several years uptime?
Oh there is an undocumented upper limit on the clock rate. At rates above
4294 select() will fail to convert its 'usec' time into the correct
number of ticks.
(From: David Laight, dsl@tadpole.co.uk)
Q: How can I create a repeat with a small delay without using busy-waiting?
A: The reason for not using busy-waiting is to allow other tasks to make
use of the spare CPU cycles. But you must remember that context switching
is quite a slow operation. Two task-swaps every 20usec for 1ms.means 100
task swaps per millisecond which works out equivalent to a task-switching
tick rate of 100 kHz. Which would undoubtedly bog down the CPU pretty
badly, and so not produce the desired saving, to put it mildly...
The 'correct' way to achieve this goal is to use an interrupt driven off
a hardware timer. Do it something like this:
This solution then has the advantage that a) the 20usec. intervals are
accurately timed by hardware rather than approximated by delay loops that
will themselves be subject to interrupts and b) any spare CPU cycles are
given to another task, but the task that has called into the driver only
gets put to sleep once and woken up once.
Note that as described above, there will be a wasted 20usec delay at the
start while waiting for the timer to first count down. You could perhaps
do the first time from the foreground driver code at the same time as you
set the timer going. Perfection is left as an exercise for the reader :-)
(From: Dave Korn)
Q: How can I monitor the processor-load?
A: The code we use for CPU monitoring is here -
I've probably missed the odd definition from earlier in the file but I
think they should be pretty self-explanatory. It creates a task at
priority 255 = idle task level, which eats CPU and times itself. The
routine getCpu()
returns a doube which is CPU loading as a
%. You have to run the initialise function at a high priority so it gets
its autocalibration done properly.
(From: Andrew Johnson, anj@aps.anl.gov)
Another example.
Based on a discussion about this subject that I read here a few weeks
ago, I wrote this idle time monitor that I
have been testing. It seems to work very well.
You can add it to you build, then start it with nothing running
-> startCPUIdleMonitor
then spawn the task that prints the CPU Idle Time
-> period 10, getCPUIdlefloat
You will need to adjust the ZERO_LOAD_IDLE_COUNTER for your CPU. For my
50Mhz 860T it is around 377000
(From: RonnoBonno, RHNAssociates@yahoo.com)
Q: Can you give an example on how to use timers?
A: Take a look at this code. It should work
without modifications.
(From: Michel Hendriks, m.hendriksNOSPAM@pijnenburg.nl)
Q: I noticed a rollover in the SNMP variable sysUpTime after around 2,5 days.
A: This can be fixed by editing snmpSystem.h which is supplied as source
with WindNet, and calculating sysUpTime yourself rather than relying on
the value from m2SysGroupInfoGet().
(From: Will Fookes, will@noSpam.wfookes.co.uk)
Q: When a timer expires, the connected routine will execute in which task context ?
A: The POSIX timer will execute in task context, same task that created
the timer using POSIX signal mechanism, and you can taskLock() from
within.
In contrast Wind WatchDog (wdLib) will do a similar service, but mor
elight-weight, and will call the handler routing in interrupt context.
(From: Leonid Rosenboim)
Q: What is the difference between the watchdog library of VxWorks and the Posix watchdog mechanism?
A: The VxWorks watchdog (wdLib) is a software mechanism for many software modules to share a single hardware timer, that is the system clock. Each wdLib client can request a timeout which is a multiple of the system clock tick, and a user defined function (aka callback) is executed within interrupt service routine of the system clock interrupt.
Being run at ISR, the wdLib delivery callback is very accurate, as it is not subject to task priority, but the code therein should be kept as short as possibly, and comply with the limitations of an ISR, i.e. call only those system functions which are allowed for ISR, and avoid using floating point arithmetics.
The POSIX timer is an entirely different software mechanism, which uses the same system clock as its time reference, but uses the POSIX signal mechanism (versus callback) to deliver the timer alert to the requesting task, as a result, the signal function which is executed as a result of the timer expiration runs within the context of the task that requested the timer originally.
The signal delivery mechanism will be invoked effectively when the
receiving task is scheduled, which is subject to that task's priority, so
the delivery will be less accurate, and suffer greater delay, but is not
limitted with respect to the length or complexity of code that can be
executed therein. It will only effect the task which is associated with
that timer.
(From: Leonid Rosenboim)
Q: How do I connect a callback to the SNTP server?
A: When starting up, run sntpsConfig, this will connect the sntpsClockHook function.
STATUS sntpsClockHook(int request, void *buffer) { struct timespec timeval; long value; ULONG *pTime; if (request == SNTPS_ID) { strcpy ((char *)buffer, "pps"); return (OK); } if (request == SNTPS_RESOLUTION) { if (clock_getres (CLOCK_REALTIME, &timeval) == -1) return (ERROR); value = timeval.tv_nsec; *((ULONG *)buffer) = (ULONG)value; } if (request == SNTPS_TIME) { if (clock_gettime (CLOCK_REALTIME, &timeval) == -1) return (ERROR); pTime = (ULONG *)buffer; /* Copy number of seconds since 1900 to timestamp. */ *pTime++ = timeval.tv_sec + SNTP_UNIX_OFFSET; /* Convert nanoseconds to fractional part and copy to timestamp */ *pTime = sntpsNsecToFraction (timeval.tv_nsec); } return (OK); } STATUS sntpsConfig (void) { if (sntpsClockSet ((FUNCPTR)sntpsClockHook) != OK) return(ERROR); return(OK); }(From: Jimbob, jamesmh@excite.com)
Q: When I load a page from this server it takes a lot of time to load all the items on this page. How can I increase the speed?
A: Use more than 1 HTTP deamon for multiple HTTP request. You were overloading your HTTP deamon before.
#define HTTP_NR_OF_TASKS 1
Change this macro in your httpconf.c file.
(From: Computer Engineer, engineer4computer@netscape.net)
Q: A page loads good with Netscape, but IE seems to hang on an applet. When downloading using a different server everything goes OK.
A: Actually it is the same solution as on the previous question. The problem is, that IE loads the applets with another socket connection simultaneously to the rest of the page and since the WindWebServer is by default single tasked, it locks up. Setting
#define HTTP_NR_OF_TASKS 2
and the applets load on IE and NS as well.
(From: Andreas Vorgeitz, Andreas.Vorgeitz@de.bosch.com)
Q: How can I get the current cursor position?
A: The basic idea is to create input queues and attach them to the device. Then check the queue for a combination of conditions to see if the mouse has moved.
Here you will specify the queue and interrupt number related to the mouse and also port address.
More details with examples are in the Zinc manual.
(From: Vijay Kumar Peshkar, vpeshkar@cisco.com)
Q: Are there any alternatives to Zinc?
A: There were several discussion a few months back about this. You could try "groups.google.com" to look for them. Here are some links,
If you want a small gui that works out of the box with vxWorks, then you
can code with WindML itself. This use to be called UGL. It is certainly
capable of making a useful small screen GUI. Many features of Zinc really
aren't needed for many products IMHO. Zinc is nice in allowing GUI
development natively on a workstation or PC.
WindML is your best choice if you don't wish to port things.
Microwindows might be a good choice if you have the time to port things.
(From: Bill Pringlemeir, spam_account@sympatico.ca)
Q: How can I avoid that a combobox blocks input to a editbox?
A: If keyboardGrabId is NULL, then use the winRootGet() member to send
the keys to. While using Zinc 6.0, a combobox on the screen would cause
edit boxes to stop receiving keyboard input.
Using the following patch will solve this.
In WindML 2.0 $(WIND_BASE)/src/ugl/win/winroute.c,
[start patch] --- winroute2.c Wed Aug 15 14:16:12 2001 +++ winroute.c Wed Aug 15 14:15:51 2001 @@ -137,6 +137,9 @@ { UGL_WINDOW_ID keyboardGrabId = winKeyboardGrabGet (eventRouterId); + if (keyboardGrabId == UGL_NULL_ID) + keyboardGrabId = winRootGet (eventRouterId); + if (keyboardGrabId != UGL_NULL_ID) { pInputEvent->header.objectId = keyboardGrabId; [end patch]
(From: Bill Pringlemeir, spam_account@sympatico.ca)
Q: How can I save and restore the contents of the screen?
A: This works for me [change SCREEN_BASE define to match your video memory],
#include <VxWorks.h> #include <config.h> #include <stdlib.h> #include <stdio.h> #include <stddef.h> #include <string.h> #define SCREEN_BASE 0xc0000000 #define WIDTH 640 #define HEIGHT 480 void screenDump(unsigned char *name) { FILE *fp; if(name == NULL || name[0] == 0) name = "screen.out"; fp = fopen(name,"w+"); fwrite((char*)SCREEN_BASE, sizeof(char),WIDTH*HEIGHT*4,fp); fclose(fp); } void screenPump(unsigned char *name) { FILE *fp; if(name == NULL || name[0] == 0) name = "screen.out"; fp = fopen(name,"r"); fread((char*)SCREEN_BASE, sizeof(char),WIDTH*HEIGHT*4,fp); fclose(fp); }
I call these from the shell.
(From: Bill Pringlemeir, spam_account@sympatico.ca)
Q: How can I use a telephone-like keypad to enter alphanumerics?
A: For the most part WRS/Zinc seem to assume an `embedded PC' with 101
keyboard, mouse and VGA.
I had the same problem and concluded that the Zinc layer was the best
place to do this. I have some experience with hand helds, PDA and have
used different cell phones. The best way is to enable the `key value'
cycling when you are in the ZafFormatString class/gui element. The format
string specifies whether the keyboard entry is going to be numeric,
alpha, alphanumeric, etc. I have modified the file
"src/zinc/generic/i_fmtstr.cpp"; specifically the Event() functions
(especially E_KEY case (i hope you guessed that)).
case E_KEY: { static const char special[30] = { '$', '%', '.', ',', '?', '!', '+', '-', '*', '=', '^', '#',
[snip, pressing zero several times allows punctuation.]
static const char decode[10][8] = { {'0', ' ', '~'}, {'1', 'q', 'z', 'Q', 'Z'}, {'2', 'a', 'b', 'c', 'A', 'B', 'C'},
[snip, describes cycling sequence]
// Search for legal characters. ZafIChar tChar = event.key.value; bool stepForward = true; /* move cursor forward */ // If special key, overwrite previous value (space). if('~' == lastKeyValue) { for(int i = 0; i < sizeof(special); i++)
[snip]
// Check to see if we should not repeat a value. if(tChar != lastKeyValue) { if(D_ON == zKeyTimer->Event(ZafEventStruct(D_STATE))) { zKeyTimer->Event(ZafEventStruct(D_OFF)); MoveCursor(L_RIGHT);
[snip...
OK, this is somewhat ugly. I did this under time pressure as always. Run
through the `decode' array and look for two possible characters from
IsLegalChar() virtual (or callback) function. If two are found and then
we cycle through the values. Don't move the cursor forward and set a
timer to move the cursor when that happens... ]
// Cycle through keys. if(tChar >= '0' && tChar <= '9') { const char *p = &decode[tChar - '0'][0]; int i = curKeyIndex; do { if(IsLegalChar(p[i], cursor)) { tChar = p[i]; // multiple key values for this key? for(int j = i + 1; i != j; j = (j+1) %8) { if(IsLegalChar(p[j], cursor)) { stepForward = false; curKeyIndex = j; lastCursor = cursor; break; } } break; } i = (i+1) % 8; } while(i != curKeyIndex); } if(tChar == '~') {
[snip, if zero is pressed several times bring up the ZafStringEditor class. I have totatlly re-written this as well...]
ZafStringEditor *stringEditor = new ZafStringEditor; stringEditor->SetRequestor(this); } else if (IsLegalChar(tChar, cursor)) { ZafEventStruct tEvent = event; tEvent.key.value = tChar; ZafUpdateType updateType = stringData->Update(); stringData->SetUpdate(ZAF_UPDATE_NONE); stringData->Replace(cursor, 1, &tChar, 1); stringData->SetUpdate(updateType);
[only move forward if this is no possible cycling...]
if(stepForward) { MoveCursor(L_RIGHT); // Reset cycle code. lastKeyValue = 0; curKeyIndex = 0; } else { // Restart timer... zKeyTimer->Event(ZafEventStruct(D_INITIALIZE)); zKeyTimer->Event(ZafEventStruct(D_ON)); }
[...]
You must modify this for your application. I suggest that you get
together with someone who knows your market and customize the UI to best
fit your customers needs. This could be a key (*bad pun*) part of the
product. Depending on your market, you may need some characters like
"accente` gui`" [excuse my French] or other odd characters.
One strength of Zinc is that you can over-ride the behaviour of a class
to fit your UI requirements; especially over plan vanilla WindML. A
weakness is the resource overhead.
(From: Bill Pringlemeir, spam_account@sympatico.ca)
OpenGL programs running under VxWorks
I've written a layer between UGL/WindML and Mesa so now one can run
OpenGL applications on VxWorks 5.4/UGL 2.0. This layer opens the doors of
the embedded systems space to 3D graphics.
I've been inspired by xmesa sources thus I've used a similar design I've
also ported some demos to WindML from GLUT for testing.
Wind River Systems gave his consent to release my work under MIT
License. Thus many students and companies will be able to use Mesa on
VxWorks.
The code is available for download from: http://sourceforge.net/projects/mesa3d
(From: Stephane Raimbault, stephane.raimbault@deesse.univ-lemans.fr)
Q: How can I decode the error numbers?
A: VxWorks errnos are divided into two parts. The high 16 bits specify a
module number in which the error occurred, and the low 16 bits contain
the module-specific error code.
To identify the module, look in $WIND_BASE/target/h/vwModNum.h. The
beginning of the list looks like this:
/* module numbers - DO NOT CHANGE NUMBERS! Add or delete only! */ #define M_errno (0 << 16) /* THIS MUST BE ZERO! */ #define M_kernel (1 << 16) #define M_taskLib (3 << 16) #define M_dbgLib (4 << 16)
So for example if the errno is 0x320001, we look for module 50 (0x32 = 50) and find:
#define M_hostLib (50 << 16)
So the error occurred in hostLib. The header file for hostLib is of course hostLib.h, so we look there for the hostLib-specific error codes, and find:
/* status messages */ #define S_hostLib_UNKNOWN_HOST (M_hostLib | 1)
So error code 0x320001 is an Unknown Host error from hostLib!
(From: Dave Korn)
Q: How can I have a task that causes an (bus)error to continue after this occurs?
A: Take a look at this demo-code: sigVecDemo.c.
(From: Albert H Chen, albertchen@directvinternet.com)
Q: How can I use compression in my application?
A: You can use the inflateLib as it is provided with VxWorks. Another possibility is to use zLib. Get the source from http://www.gzip.org/zlib/ or http://www.info-zip.org/pub/infozip/zlib/. It should compile easily for vxWorks. This package is more flexible as you can handle gzip files.
# makefile. CPU = ?? Your CPU ?? TOOL = gnu EXTRA_DEFINE = -Wcomment MKDIR = mkdir TGT_DIR = $(WIND_BASE)/target OBJS= adler32.o compress.o crc32.o deflate.o gzio.o infblock.o infcodes.o \ inffast.o inflate.o inftrees.o infutil.o trees.o uncompr.o zutil.o all : ($OBJS) include $(TGT_DIR)/h/make/defs.bsp include $(TGT_DIR)/h/make/make.$(CPU)$(TOOL) include $(TGT_DIR)/h/make/defs.$(WIND_HOST_TYPE) ## Allow C++ sytle comments (tell Pre-processor). ADDED_CFLAGS += -Wp,-lang-c ADDED_CFLAGS += -DVXWORKS=1 ADDED_CFLAGS += -Wall -Winline -O2 -fomit-frame-pointer -fno-strength-reduce ADDED_CFLAGS += -DNO_DEFLATE=1
The functions in gzio.o are nice. I don't think you have to modify any of
the source.
(From: Bill Pringlemeir, spam_account@sympatico.ca)
I wrote a routine to uncompress an image compressed by deflate. The trick
is that you have to skip the first byte. I think it's a 0x08; it's a tag
that WRS adds to the start of the compressed block. Other than that it
was just a matter of using the zlib routines to decompress it.
I haven't tried to compress an image with anything other than deflate,
but it shouldn't be too hard.
I bet if you asked your FAE nicely, he'd get you the source to deflate
and inflateLib.
(From: pkockritz@mac.com, Pete Kockritz)
Q: How can I autostart a loaded module.
A: without changing loader code or implementing an own version of loader:
If you set C++ Constructor to automatic in VxWorks (standard), you might
wrap your application by a static 'startup' class. The creator of the
class may spawn the main task of your application.
(From: Michael Lawnick, Lawnick@softec.de)
Q: How can I check if the memory heap is corrupted?
A: Last week I was hunting down a nasty bug in our software. I finally
found it using a self written function that checks the VxWorks memory
list for errors.
The code is tested on a PPC 750 system, but should work on all systems.
These are the files:
(From: _remove_rene.straub@yetnet.ch, Rene Straub)
Q: How can I get the VxWorks and kernel version?
A: Use something similar to whats in WRS's bootConfig.c
#include#include (maybe some others also needed ...) printf (" CPU: %s\n", sysModel()); printf ("VxWorks: %s\n", vxWorksVersion); printf (" Kernel: %s\n", (char *)kernelVersion()); printf (" BSP: " BSP_VERSION BSP_REV "\n"); printf (" Creation date: %s, %s using GNU C %d.%d\n", __DATE__ , __TIME__ , __GNUC__, __GNUC_MINOR__ ); From: Melvin Gardipee "mbgardipee"@.NO_SPAM_HERE.raytheon.com)
5.14 VxWorks AE questions
Q: How can I access a fixed address register from within a user level domain?
A: This would work if the code was located in the kernel domain, but would not be allowed from a user level domain. Probably the simplest way of getting this to work from user level is to create a small function in your kernel that does this, make it an entry point and call it from your application...
(From: John, john_94501@yahoo.com)
Q: How can I spawn an application domain task from a privileged domain?
A: Actually there is a mechanism for this, and better still there is an example of using it included on every VxWorks AE CD/installation. I know, because I wrote the VxWorks AE portions of it!
Take a look in target/src/demo/PD and you will find an implementation of Eliza. This will show you how to create a domain dynamically, attach it to some code (so you don't even need a loader), and then start a task running in that new domain.
The call that starts the task is pdRun(). To make it work, somthing must have registered an init routine for the domain. There are two ways of doing that:
- As in the example, call pdInitRtnSet(). Be careful here: the routine that you set must be visible in the new domain as well as the current one - the example uses a shared library for this reason.
- Have a function called pdMain() in a module that you load dynamically into the new domain. The loader will automatically register this function for you as the domain's init routine. I have used this to run a multi-process graphical environment under VxWorks AE (part of the usability tests that we carried out before release).
(From: John, john_94501@yahoo.com)
Index
5.1 A When I download some C++ code my target crashes, even when I don't start anything. B What is the syntax of "taskSpawn" for spawning a method of an object (C++) as a task. C How can I call a C++ function from within a C-file? D What is the preferred extension for C++ files? E Gotcha with C++ static constructors and GCC 2.95.x (PPC) 5.2 A Is there any instance where msgQDelete can be blocking? 5.3 A In my BSP there is an intConnect, but no intDisconnect. How can I disconnect a function from a interrupt? B What does "workQPanic: Kernel work queue overflow" mean? C What does the function "__get_eh_context" do? D After calling intlock it seems that the interrupts are not locked when looking at the interrupt control register. 5.4 A When I try to load a module with unresolved externals the function loadModule returns a module-ID instead of returning NULL, as described in the manual. How do I detect if there are unresolved externals? B The ld command, run from a script, is behaving differently on a UNIX and an NT box. 5.4A A The VxWorks port of an alternative code for dynamic memory allocation, made by Doug Lea. B One of my tasks in getting suspended all the time, in memPartFree. What is causing this? 5.5 A Has anyone ported pthreads.h from Linux to VxWorks? B Is there a way to implement the VxWorks API on top of pthreads? 5.6 A When I do a hard reboot the system reacts differently than when using Ctrl-X. 5.7 A It looks like the priority inversion mechanism is not working correctly. 5.8 A How can I start multiple simulators on NT and have these communicating? B How do I increase the memory of the simulator? C How can I have 2 simulators communicate with eachother on Win2K box? D The font in my VxSim window is very small. E How can I make 2 simulators communicate on a Solaris box? 5.9 A What is the use of taskDelay(0)? B How do I use taskInit? C How do I get the real priority of a task? D Question about order of execution for a taskDelete(taskIdSelf). E How can I build a stack-trace? F What is the syntax of "taskSpawn" for spawning a method of an object (C++) as a task. G How can I detect a stack overflow? H I want to block a task until all children are in a certain state (initialised, finished, etc.). I What are +T, +I thingies in the "i" output? 5.10 A I would like to know how to measure the system time at a specific moment, then at another moment, only a few milliseconds later, to determine elapsed time. B When I create a timer function my program crashes after the time specified instead of executing the function I specified. What is happening? C How do I determine the number of ticks remaining in a watchdog timer? D Errors in the time modules. E How can I create a repeat with a small delay without using busy-waiting? F How can I monitor the processor-load? G Can you give an example on how to use timers? H Rollover in the SNMP variable sysUpTime after around 2,5 days. I When a timer expires, the connected routine will execute in which task context? J What is the difference between the watchdog library of VxWorks and the Posix watchdog mechanism? K How do I connect a callback to the SNTP server? 5.11 A When I load a page from this server it takes a lot of time to load all the items on this page. How can I increase the speed? B A page loads good with Netscape, but IE seems to hang on an applet. 5.12 A How can I get the current cursor position? B Are there any alternatives to Zinc? C How can I avoid that a combobox blocks input to a editbox? D How can I save and restore the contents of the screen? E How can I use a telephone-like keypad to enter alphanumerics? F OpenGL programs running under VxWorks 5.13 A How can I decode the error numbers? B How can I have a task that causes an (bus)error to continue after this occurs? C How can I use compression in my application? D How can I autostart a loaded module. E How can I check if the memory heap is corrupted? F How can I get the VxWorks and kernel version? 5.14 A [AE] How can I access a fixed address register from within a user level domain? B [AE] How can I spawn an application domain task from a privileged domain?
© J.A. Borkhuis, 2000 - 2005