1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
|
Microsoft's ThreadX for Xtensa Configurable Processors
Using the Xtensa Tools
1. Xtensa Configuration Requirements and Restrictions
The Xtensa configurable architecture supports a vast space of processor
features. This port of ThreadX to the Xtensa architecture is based on
a Cadence Design Systems RTOS porting layer that takes care of Xtensa specifics
that are common to most embedded real-time operating systems. It supports all
Xtensa features (including context-switching custom processor extensions
defined in the TIE language) with certain minimum requirements. You
must use Xtensa Tools to compile and link ThreadX for your particular
configuration. ThreadX uses the Xtensa Hardware Abstraction Layer (HAL)
to adapt your ThreadX compilation to your particular Xtensa processor
configuration. ThreadX also provides optional thread-safe support for
the Xtensa C library and the newlib C library distributed with Xtensa Tools
(for use in threads only, not in interrupt handlers).
ThreadX for Xtensa configurable processors requires the following minimum
processor configuration options:
- Timer interrupt option with at least one interruptible timer for ThreadX.
- Interrupt option (implied by the timer interrupt option).
- Xtensa Exception Architecture 2 (XEA2) or Exception Architecture 3 (XEA3).
Minimal support for certain evaluation boards is provided via a board
independent "XTBSP" API implemented by a board specific library. This
provides the board clock frequency and basic polled drivers for the display
and console I/O device (usually a UART), and ThreadX can be built to
take advantage of these (note: it is not specific to ThreadX and is not a
traditional RTOS "board support package" with RTOS specific interrupt-driven
drivers). Note that ThreadX can run on any Xtensa board without
this board support (a "raw" platform), but you will have to provide the
clock frequency and drivers for any on-board devices you want to use.
2. Building the ThreadX Library and the Xtensa Example
By default, you will build for the Xtensa instruction set simulator. If
you have a supported emulation board, you can build to run on that. You
will have to provide the XTBSP functions for any other board you want to
run on. We recommend you start by running on the simulator, then moving
to your hardware target or emulation platform.
ThreadX/Xtensa is built using the ThreadX Cmake infrastructure. See the
standard ThreadX documentation for details of the build process. In the
cmake/ directory there is a file named xtensa.cmake which defines the
Cmake configuration for the Xtensa target. Both Cmake and the Ninja tool
need to be installed to build ThreadX.
You will also need to ensure that the Xtensa SDK tools have been installed
and are on the path, and XTENSA_SYSTEM and XTENSA_CORE are set appropriately
in the environment. Refer to the SDK documentation for details.
The following commands will build the ThreadX library (some changes may be
required based on your setup) -
cmake -S $(TX_SRCDIR) -B $(TX_BLDDIR) -DCMAKE_TOOLCHAIN_FILE=$(TX_SRCDIR)/cmake/xtensa.cmake -GNinja $(TX_SRCDIR)
cmake --build $(TX_BLDDIR)
where TX_SRCDIR is the root of the ThreadX source tree, and TX_BLDDIR is
the location where you want the build output to go.
Once the library is built, the Xtensa example can be built by going into
the directory ports/xtensa/xcc/example_build and running this command -
xt-clang -mlongcalls -Os -g -I../../../../common/inc -I../../../../common_smp/inc
-I../../../../ports/xtensa/xcc/inc -o demo_threadx.exe demo_threadx.c
../../../../build/libthreadx.a -lxtutil
To build ThreadX with thread-safe C library support, define TX_THREAD_SAFE_CLIB
to 1 (this is set by default in tx_port.h). Note that the C library is only
safe for use in threads, not in interrupt handlers. It may also safely be used
in tx_application_define (after tx_kernel_enter, before threads are running).
3. Running the Example
The ThreadX Xtensa example is designed to be run on the Xtensa simulator (ISS)
or a hardware target. By default the example is built for the simulator target.
To run it,
xt-run demo_threadx.exe
or
xt-gdb demo_threadx.exe (if you want to run it under the debugger)
To execute on a supported hardware target, download the example to the board
following instructions in the SDK manuals. Make sure the board has been set
up with the correct configuration (if emulation board) and is set up to boot
from RAM and debug a downloaded program. Optionally you may connect a terminal
or terminal emulator to the serial port on the board with settings as described
in the board user manual, and see the output of printf on the terminal (if
the board support package supports directing standard output to the port).
To obtain I/O on a "raw" platform such as an unsupported board, you need
to provide low level I/O drivers (eg. inbyte() and outbyte() for character
I/O if you want to use printf etc.). You can run "raw" executables on
any Xtensa platform, including simulator and any board, but you will not
see any behavior specific to the platform (eg. display, printed output,
stopping simulation at end of program). You can, while debugging, use a
debugger mechanism called GDBIO to obtain basic I/O. This requires linking
with the gdbio support library. See the SDK documentation for details.
4. System Initialization
4.1 ThreadX Kernel
ThreadX uses the reset vector and low level initialization from the Xtensa
libraries provided by the linker support package (LSP). It does not provide
its own reset vector (which would have to be maintained with tools upgrades).
ThreadX is initialized by tx_kernel_enter which is usually called from
main. The primary initialization function is _tx_initialize_low_level
and is located in the file tx_initialize_low_level.c. This function is
responsible for setting up various system data structures. Interrupt and
exception vectors are linked into your program and installed at load time
(they are not dynamically installed). The interrupt and exception vectors
for the ThreadX Xtensa port are defined in xtensa_vectors.S (for XEA2) and
xtensa_vectors_xea3.S (for XEA3).
4.2 Memory Allocation
In addition, _tx_initialize_low_level also determines the first available
address for use by the application. By default, the first available address
is assumed to be above all linker-allocated sections at symbol _end. This
is passed to the application definition function, tx_application_define.
This is a convenience to the application developer. Ultimately the developer
has full control over memory allocation and can choose to use this or not.
The top of available memory is not provided and should be obtained from the
tools if needed.
4.3 Location and Extent of System (Interrupt) Stack
ThreadX/Xtensa supports a user-definable system stack. By default, the
top of the system stack is defined by the symbol _xt_interrupt_stack_top.
See the file xtensa_intr_asm.S for the default system stack definition.
This stack may be resized and/or relocated according to the application
needs. The application developer must ensure that the system stack is
sized appropriately for the application. All interrupts handled by
ThreadX will use the system stack. Handlers written in assembly must
not switch to the system stack, since it will not be possible to detect
whether the stack is currently in use.
The macro TX_SYSTEM_STACK_SIZE defines the size of the system stack.
As a convenience, a macro TX_MINIMUM_STACK_SYSTEM is provided with the
minimum size required for the system stack. This is based on the maximum
possible interrupt nesting level per the Xtensa processor configuration,
assuming very simple C handlers that do not call deeper than one or two
levels. If the application uses more complex handlers, it will be
necessary to add to this value (accounting for nesting) to determine
the space required for the system stack.
4.4 Location and Extent of C Library Heap
If the optional thread-safe support for the C library is used, memory
for the heap is allocated like this: by default, half the space between
the first available memory address and the end of system memory is
made available to the heap.
The heap location and limit are available in two global variables, and
can be fully customized in tx_application_define by assigning to them:
_tx_clib_heap_start Base address of heap.
_tx_clib_heap_end Upper limit address to which heap can grow.
This must be done BEFORE any C library calls. It is advised that it be
done at the beginning of tx_application_define.
Please note that when the thread-safe C library support is used, the heap
is not initialized before tx_kernel_enter has been called, so malloc will
fail. It is recommended to avoid C library calls that use the heap (such
as printf) outside of ThreadX (eg. in main). The C library is NOT safe
for use in interrupt or exception handlers, so this should be avoided.
4.5 Thread Stack Sizes
The application must ensure that every thread has enough space for its
stack. This must account for the deepest call depth and allow for one
interrupt stack frame as defined in xtensa_context.h . Several factors
influence the size of the stack required, including compiler optimization
level (-O0 is worst), use of TX_ENABLE_STACK_CHECKING option, and of
course your Xtensa configuration. Some stack size guidelines and macros
are provided in tx_port.h assuming no optimization (default, -O0).
Threads that call C library functions may need larger stacks than those
that don't. In particular, use of printf requires a very large stack and
will usually cause a stack overflow if inserted in a thread without
enlarging its stack size.
5. Assembler / Compiler Switches
The following are compiler switches used in building the ThreadX library
and demonstration system. These can be supplied by editing the xtensa.cmake
file or by overriding from the environment or command line.
Compiler Switch Meaning
-g Specifies debug information.
-c Specifies object code generation.
-On Sets compiler optimization level n (default -O0).
-mlongcalls Allows assembler and linker to convert call
instructions to longer indirect call sequences
when target is out of range.
-Dmacro Define a preprocessor macro with no value.
-Dmacro=value Define a preprocessor macro with a value.
Application Defines (preprocessor macros definable with the -D option):
TX_THREAD_SAFE_CLIB Enable support for thread-safe C library.
Only the Xtensa C library and the newlib library
are supported for thread-safe operation.
When this is enabled, half the available memory
space is allocated by default, below the system
stack, for the heap. The heap size and location
can be customized in tx_application_define.
Default off.
TX_ENABLE_STACK_CHECKING Enable generic ThreadX support for stack
overflow checking. This can help avoid long
debugging sessions or customer support calls
by identifying many crashes caused by stack
overflow. Use of this option adds a small
premium to the required thread stack sizes
(the premium is included by the stack size
convenience macros defined in tx_port.h).
TX_NO_TIMER Enable generic ThreadX footprint reduction when
no periodic tick is needed. The Xtensa port also
reduces footprint by removing all code related
to timer interrupts. Related XT_* options below
(XT_CLOCK_FREQ, XT_TICK_PER_SEC, XT_TIMER_INDEX)
have no effect under TX_NO_TIMER.
All generic ThreadX options in tx_user.h may also be defined with -D.
Note, the above defines are not specific to Xtensa processors, so
their names begin with "TX_". Defines below are unique to the Xtensa
port so have names beginning with "XT_".
XT_SIMULATOR Set this if building to run on the simulator.
Takes advantage of certain simulator control
and reporting facilities, and adjusts timing
of periodic tick to provide a more acceptable
performance in simulation (see XT_CLOCK_FREQ).
Set by default unless PLATFORM is overridden.
XT_BOARD=board Set this if building for a supported board.
The value should be the identifier of a board
supported in the Xtensa Tools or an external
package (eg. XT_BOARD=xtkc705 for the Xilinx
KC705 board). If the board is supported by an
external package, the variable XTENSA_BOARDS
should point to the root of the package. Set by
provided Makefile when PLATFORM=board and BOARD
is defined (eg. PLATFORM=board BOARD=xtkc705).
XT_CLOCK_FREQ=freq Specifies the target processor's clock
frequency in Hz. Used primarily to set the
timer that generates the periodic interrupt.
Defaults are provided and may be edited in
xtensa_timer.h (see comments there also).
Default for simulator provides more acceptable
performance, but cannot provide real-time
performance due to variation in simulation
speed per host platform and insufficient
cycles between interrupts to process them.
Supported board platforms by default leave
this undefined and compute the clock frequency
at initialization unless this is explicitly
defined.
XT_TICK_PER_SEC=n Specifies the frequency of the periodic tick.
XT_TIMER_INDEX=n Specifies which timer to use for ThreadX.
Set this if your Xtensa processor configuration
provides more than one suitable timer and you
want to override the default. See xtensa_timer.h.
XT_INTEXC_HOOKS Enables hooks in interrupt vector handlers
to support dynamic installation of exception
and interrupt handlers. Used by automatic
regression test programs. Disabled by default.
XT_USE_OVLY Enable code overlay support.
XT_USE_SWPRI Enable software prioritization of interrupts.
Enabling this will prioritize interrupts with
higher bit numbers over those with lower bit
numbers at the same level. This works only for
low and medium priority interrupts that can be
dispatched to C handlers on either NX cores or
on LX cores with 32 or fewer interrupts.
TX_SYSTEM_STACK_SIZE=n Specify the size of the interrupt stack, which
is the stack that all interrupt handlers switch
to while handling interrupts. See the section
describing the interrupt stack for more details.
6. Register Usage and Stack Frames
The Xtensa architecture specifies two ABIs that determine how the general
purpose registers a0-a15 are used: the windowed ABI, and the Call0 ABI.
The choice of ABI is made when configuring the processor or its associated
software. Xtensa processors may have other special registers (including
coprocessor registers and other TIE "states") that are independent of this
choice of ABI. See Xtensa documentation for more details.
In the windowed ABI the registers of the current window are used as follows:
a0 = return address
a1 = stack pointer (alias sp)
a2 = first argument and result of call (in simple cases)
a3-7 = second through sixth arguments of call (in simple cases).
Note that complex or large arguments are passed on the
stack. Details are in the Xtensa Tools manuals.
a8-a15 = available for use as temporaries.
There are no callee-save registers. The windowed hardware automatically
saves registers a0-a3 on a call4, a0-a8 on a call8, a0-a12 on a call12,
by rotating the register window. Hardware triggers window overflow and
underflow exceptions as necessary when registers outside the current
window need to be spilled to preallocated space in the stack frame, or
restored. Complete details are in the Xtensa manuals. The entire windowed
register file is saved and restored on interrupt or thread context switch.
Note that only call0 and call8 are available in XEA3 Windowed ABI.
The Call0 ABI does not make use of register windows, relying instead
on a fixed set of 16 registers without window rotation.
The Call0 ABI is more conventional and uses registers as follows:
a0 = return address
a1 = stack pointer (alias sp)
a2 = first argument and result of call (in simple cases)
a3-7 = second through sixth arguments of call (in simple cases).
Note that complex or large arguments are passed on the
stack. Details are in the Xtensa Tools manuals.
a8-a11 = scratch.
a12-a15 = callee-save (a function must preserve these for its caller).
On a ThreadX API call, callee-save registers are saved only when a thread
context switch occurs, and other registers are not saved at all (the caller
does not expect them to be preserved). On an interrupt, callee-saved
registers might only be saved and restored when a thread context-switch
occurs, but all other registers are always saved and restored.
An Xtensa processor has other special registers independent of the ABI,
depending on the configuration (including coprocessor registers and other
TIE state) that are part of the thread context. ThreadX preserves all such
registers over an unsolicited context-switch triggered by an interrupt
(including time-slice expiry). However it does NOT preserve these over
a solicited context-switch during a ThreadX API call. This bears some
explanation. These special registers are either ignored by the compiler
or treated as caller-saved, meaning that if kept "live" over a function
call (ie. need to be preserved) they must be saved and restored by the
caller. Since solicited entry to ThreadX is always made by a function
call, ThreadX assumes the caller has saved any of these registers that are
"live". ThreadX avoids a lot of overhead by not having to save and restore
every special register (there can be many) on every solicited context switch.
As a consequence, the application developer should NOT assume that special
registers are preserved over a ThreadX API call such as tx_thread_sleep.
If multiple threads use a register, the caller must save and restore it.
The saved context stack frames for context switches that occur as a result
of interrupt handling (interrupt frame) or from thread-level API calls
(solicited frame) are described in human readable form in xtensa_context.h.
All suspended threads have one of these two types of stack frames. The top
of the suspended thread's stack is pointed to by tx_thread_stack_ptr in the
associated thread control block TX_THREAD. An Xtensa architecture port-specific
extension to the thread control block tx_thread_solicited contains 1 for a
thread that is currently suspended from an API call, otherwise contains 0.
7. Improving Performance, Footprint, or Ease of Debugging
The distribution version of ThreadX is built with debug (-g) and without
compiler optimizations (-O0). This makes debugging easier inside ThreadX
itself. Of course, -O0 costs some performance. To make ThreadX run faster,
you can change the Makefile to enable the desired optimizations or set
a predefined optimization level (-O<level>) .
Maximum performance is achieved with -O3, but that might increase footprint
substantially. A good compromise is -O2. If code size is a concern, -Os may
yield better results. See the compiler manual for details.
You can eliminate the ThreadX basic API error checking by compiling your
application code with the symbol TX_DISABLE_ERROR_CHECKING defined before
tx_api.h is included.
The Xtensa architecture port-specific assembly files are coded with no
file-scope labels inside functions (all labels inside functions begin with
".L"). This allows a profiler to accurately associate an address with a
function, and also allows the debugger's stack trace to show the correct
function wherever the program counter is within that function. However
there are some tradeoffs in debugging. Local (".L") labels are not
visible to the debugger, so the following limitations may be observed
during debugging:
- You cannot set a breakpoint on a local label inside a function.
- Disassembly will show the entire function, but will get out of sync and
show incorrect opcodes if it crosses any padding before an aligned local
branch target (".L" label, not ".Ln"). Restart disassembly specifying an
address range explicitly between points where there is padding.
Since ThreadX is provided in source form, it is not difficult to remove
the ".L" and ".Ln" prefixes from local labels if you want them visible.
They can also be made visible by passing the '-L' option to the assembler
and linker (see the assembler and linker manuals for details).
8. Interrupt and Exception Handling
NOTE: The material in this section is mostly an overview. For a more
detailed explanation please refer to the Xtensa ISA manual and the System
Software manual.
ThreadX provides a complete set of efficient exception and first-level
interrupt handlers installed at the appropriate exception and interrupt
vector locations. The XEA2 architecture supports several different
classes of exceptions and interrupts. Being a configurable architecture,
many of these are optional, and the vector locations are determined by
your processor configuration. The handlers provided use conditional
compilation to adapt to your processor configuration and include only
the code that is needed.
Xtensa vector locations may reside almost anywhere, including in ROM.
And the amount of code space available at each of these locations is
often very small (e.g. due to following vectors). A small stub of code
installed at the vector jumps to the corresponding handler, usually in RAM.
The exception and interrupt handlers are defined in xtensa_vectors.S .
They are not specific to ThreadX, but call into ThreadX where appropriate
via macros defined in xtensa_rtos.h .
Interrupt/Exception Handling in XEA2
------------------------------------
The handlers provided for low and medium priority interrupts are just
dispatchers that save relevant state and call user-definable handlers.
See the files xtensa_vectors.S and xtensa_api.h for more details of how
to create and install application-specific user interrupt handlers.
Similarly, user-defined handlers can be installed for exceptions (other
than a few which are always handled by the OS).
The high priority interrupt handlers provided may be considered templates
into which the application adds code to service specific interrupts.
The places where application handlers should be inserted are tagged with
the comment "USER_EDIT" in xtensa_vectors.S.
This ThreadX port supports strict priority-based nesting of interrupts.
An interrupt may only nest on top of one of strictly lower priority.
Equal priority interrupts concurrently pending are handled in an
application-defined sequence before any lower priority interrupts
are handled. During interrupt and exception handling, the processor's
interrupt level (PS.INTLEVEL) is used to control the interrupt priority
level that can be accepted; interrupt sources are not controlled
individually by ThreadX (the application is free to access the INTENABLE
register directly to enable/disable individual interrupts, e.g. using
Xtensa HAL services). This approach provides the most deterministic
bounds on interrupt latency (for a given priority) and system stack depth.
Software prioritization of interrupts at the same priority is controlled
by the definition of XT_USE_SWPRI. See above for a description of this
parameter.
Interrupt and Exception Handling in XEA3
----------------------------------------
In XEA3 no distinction is made between low, medium and high priority interrupt
levels. Interrupts at all levels up to the highest (NMI) level can be dispatched
to handlers written in C. Interrupts are dispatched by the XEA3 dispatcher and
the handlers are found from a handler table. Handlers are installed into the
handler table by calling xt_set_interrupt_handler. As there is no concept of
a high priority interrupt any longer, handlers can only be specified per
interrupt, not per level. Since all levels can be handled in C, the value of
EXCM_LEVEL is set to the highest possible level (NMI level).
Software prioritization is not available for XEA3.
The following subsections describe the handling of each class of exception
and interrupt in more detail. Many have nothing to do with ThreadX but are
mentioned because there is code to handle them in xtensa_vectors.S.
8.1 User Exception and Interrupt Handler (Low/Medium Priority)
All Xtensa 'general exceptions' come to the user, kernel, or double
exception vector. The exception type is identified by the EXCCAUSE
special register (level 1 interrupts are one particular cause of a
general exception). This port sets up PS to direct all such exceptions
to the user vector. Exceptions taken at the other two vectors usually
indicate a kernel or application bug.
In XEA3, everything except a double exception will come to a common
vector. This vector saves the requisite processor state and then checks
for the highest priority interrupt to dispatch. If no interrupts are
pending then a check is made for exceptions.
Level 1 interrupts are identified at the beginning of the handler
and are dispatched to a dedicated handler. Then, syscall and alloca
exceptions are identified and dispatched to special handlers described
below. After this, coprocessor exceptions are identified and dispatched
to the coprocessor handler.
Any remaining exceptions are processed as follows:
Having allocated the exception stack frame, the user exception handler
calls _tx_thread_context_save, which saves the rest of the interrupt
context. After this the handler sets up a C environment and enables
the high-priority class of interrupts (which do not interact with
ThreadX), then reads EXCCAUSE and uses the cause (number) to index
into a table of user-specified handlers. The correct handler is then
called. If the handler returns, the context is restored and control
is returned to the code that caused the exception. The user-defined
handler may alter the saved context, or any other system state, that
allows the faulting instruction to be retried.
If the cause is a level 1 (low-priority) or medium-priority interrupt,
the handler enables all interrupts above that priority level after
saving the thread context and switching to the interrupt stack if it
is not a nested interrupt. It then sets up the environment for C code
and then calls the handler (found in the handler table) for the
interrupt number. If the user has not specified a handler, then the
default handler will be called, which will terminate the program.
If the interrupt is for the system timer, it calls a special interrupt
handler for the system timer tick, which calls _tx_timer_interrupt then
clears its bit from the mask. This interrupt cannot be hooked by the
user-defined handler.
Finally, the handler calls _tx_thread_context_restore to allow ThreadX
to perform any scheduling necessary and return either to the interrupted
thread or another.
If software prioritization is enabled, the handler will re-enable all
interrupts at the same level that are numerically higher than the current
one, before calling the user handler. This allows a higher priority
interrupt to pre-empt the lower priority handler.
8.2 Medium Priority Interrupt Handlers (XEA2)
Medium priority interrupts are those at levels 2 up to XCHAL_EXCM_LEVEL,
a configuration-specific maximum interrupt level affected by the global
'exception mode' bit in the processor status word (PS.EXCM).
Interrupt levels above XCHAL_EXCM_LEVEL are of the high-priority class.
The Xtensa hardware documentation considers medium priority interrupts
to be a special case of high-priority interrupts, but from a software
perspective they are very different.
Dispatch of medium-priority interrupts is discussed in the section
above.
8.3 High Priority Interrupt Handlers (XEA2)
High priority interrupts are those strictly above XCHAL_EXCM_LEVEL,
a configuration-specific maximum interrupt level affected by the
global 'exception mode' bit in the processor status word (PS.EXCM).
High priority handlers may not directly interact with ThreadX at all,
and are described here only for the sake of completeness. They must
be coded in assembler (may not be coded in C) and are intended to be
used for handling extremely high frequency hardware events that need
to be handled in only a few cycles. A high priority interrupt handler
may trigger a software interrupt at a medium or low priority level to
occasionally signal ThreadX. Please see Xtensa documentation.
There is a separate vector and a few special registers for each high
priority interrupt, providing for fast dispatch and efficient nesting
on top of lower priority interrupts. Handlers are templates included
only for the vectors that exist in your Xtensa processor configuration.
These templates are written for only one interrupt per high priority
level to minimize latency servicing very fast time-critical interrupts.
The vector code jumps to the corresponding first-level interrupt handler,
which then executes application-provided assembler code before returning
quickly to the interrupted thread or lower priority handler.
8.4 Kernel Exception Handler (XEA2)
Kernel mode is not used in this port of ThreadX, and therefore kernel
exceptions should not happen. A stub is provided for the vector that
triggers the debugger (if connected) or calls _xt_panic to freeze the
processor should a kernel exception occur.
8.5 Alloca Exception Handler
Alloca exceptions are generated by the 'movsp' instruction, which
is used only in the windowed ABI. Its purpose is to allocate some
space on top of the stack. Because the window hardware may have
spilled some registers to the 16 byte "base save" area below the
stack pointer, it is necessary to protect those values. The alloca
handler accomplishes this quickly without setting up an interrupt
frame or entering ThreadX, by emulating a register underflow and
re-executing 'movsp'.
8.6 Syscall Exception Handler
Syscall exceptions are generated by a 'syscall' instruction.
The windowed ABI specifies that executing this instruction with
a value of zero in register a2 must spill any unsaved registers
in the windowed register file to their pre-determined locations
on the caller's stack. The handler does exactly that, and skips
over the 'syscall' instruction before returning to the caller.
If a2 is non-zero, the handler returns a2 == -1 to the caller.
8.7 Co-Processor Exception Handler
A coprocessor exception is generated when a thread accesses a
coprocessor that it does not "own". Ownership represents which
thread's state is currently in the coprocessor. Co-processors are
context-switched "lazily" (on demand) only when a non-owning thread
uses a coprocessor instruction, otherwise a thread retains ownership
even when it is preempted from the main processor. The coprocessor
exception handler performs the context-switch and manages ownership.
Co-processors may not be used by any code outside the context of a
thread. A coprocessor exception triggered by code that is not part
of a running thread is a fatal error and ThreadX/Xtensa will panic.
This restriction is intended to reduce the overhead of saving and
restoring coprocessor state (which can be quite large) and in
particular remove that overhead from interrupt handlers. It also
reduces the thread stack size requirement by allowing coprocessor
state to be saved in the thread control block rather than the stack.
8.8 Debug Exception Handler
A debug exception is caused as a result of running code, such as by
a 'break' instruction or hardware breakpoints and watchpoints, or
as a result of an external debug interrupt, such as from an OCD based
debugger or multiprocessor debug events ("breakin/breakout"). If the
processor is running in OCD mode under control of an OCD-based debugger,
the trigger event immediately halts the processor and gives control to
the OCD debugger. Otherwise control is transferred to the debug vector.
The debug vector handler calls the simulator if running on the ISS,
which then takes control and interacts with any attached debugger.
If running on hardware and not in OCD mode, debug exceptions are not
expected, so the debug handler calls _xt_panic to freeze the processor.
8.9 Double Exception Handler
A double exception is a general exception that happens while the
processor is in exception mode (PS.EXCM set), and thus indicates a
bug in kernel code. The double exception vector handler triggers
the debugger (if connected) or calls _xt_panic to freeze the
processor.
8.10 Window Overflow and Underflow Exception Handlers
Window overflow and underflow handlers are required for use of the
windowed ABI. Each has its own dedicated vector and highly optimized
code that is independent of OS. See Xtensa documentation for details.
8.11 Hooks for Dynamic Installation of Handlers
Optional hooks are provided in the user exception and low level
interrupt handler and all medium and high priority interrupt handlers,
to dynamically install a handler function (which may be coded in C,
unless in a high-priority interrupt handler). These hooks are enabled
and used by automatic regression tests, they are not part of a normal
ThreadX build. However an application is free to take advantage of
them. The interrupt/exception hooks are described in xtensa_rtos.h .
It is recommended that the application not make use of these hooks, but
rather use xt_set_interrupt_handler() and xt_set_exception_handler()
to install application-specific handlers. This method is more convenient
and allows arguments to be passed to the handlers. Software prioritization
of interrupts works only with this method. See xtensa_api.h for details.
9. Overlay Support (XEA2 only)
ThreadX supports the overlay feature of the Xtensa toolsuite. To enable overlay support,
the ThreadX library should be built with XT_USE_OVLY defined. In addition, the linker
command line must use the overlay library via the -loverlay linker option and the
xtensa_overlay_os_hook.o object file must be explicitly specified in order to override
the overlay libary version.
You will also need to generate a custom LSP for overlay use. Please reference the
Xtensa System Software Reference and Xtenas Linker Support Packages (LSPs) for more
information on using overlays.
10. Revision History
For generic code revision information, please refer to the readme_threadx_generic.txt
file, which is included in your distribution. The following details the revision
information associated with this specific port of ThreadX:
04-02-2021 Release 6.1.6 changes:
tx_port.h Updated macro definition
12-31-2020 Initial Version 6.1.3
Copyright(c) 1996-2020 Microsoft Corporation
https://azure.com/rtos
|