An expression that uses division isn't working properly. What's wrong?

Due to implementation limitations, signed divides are not available.


Are functions reentrant?

Functions are not reentrant. The local variables are not placed on a stack; their address is set at compile time. If a function calls itself, or a function is being executed from both the main loop and within an interrupt, the second call to the function will use the same memory. In this case local variables will become corrupted.

ETPU_functions are reentrant, in the sense that multiple channels can dispatch the same function.

C functions called from within ETPU_functions are not reentrant.


C includes a register data type. Does eTPU_C allow register allocations? How do I access the P register from C?

eTPU_C does offer register-like types, with a few catches. You can use these types to access hardware registers directly, if necessary.

The traditional register alerts the compiler to allocate very fast storage (ie., a processor register) for a variable. It's a suggestion, and not mandatory. register might be useful on a system with lots of similar general-purpose registers. On eTPU, using register types is less of an advantage.

eTPU_C provides several specific register_xx types, one for each microcode-accessible register. Identifiers declared with them guarantee access to the named register.

Registers declared as global variables cannot be used to hold local variables or intermediate results.

register_xx variables actually point to a register, and therefore have no address value suitable for a pointer.


Can I use goto to jump between sections of the if()..else if()..else in an eTPU function?

In general, no. Remember that the bodies of this top-local-scope statement are individual eTPU threads. The compiler treats them as run-to-completion code, and may optimize them differently.

To share programming between more than one thread within an eTPU function, create a C function and call it from all relevant threads.


Does eTPU_C support signed divide?

No. Signed divide is not supported at this time.


Does eTPU_C support TPU3?

No. eTPU is the successor of TPU3 and shares some design heritage, but that's about it. They are completely different processors, both in functionality and programming terms.


eTPU is word-oriented. How does this affect eTPU_C?

It doesn't. Internally, eTPU_C deals with bytes; this makes it easier for C programmers to work with eTPU_C. During code generation, eTPU_C makes a conversion to sub-word calculations, using different strategies to get at different bytes.

The only caveat you need to remember is this: code for operations on byte-sized values can be particularly expensive in terms of instructions. Whether it's warranted is a matter for the designer to decide.

Byte-wide operations

This example demonstrates how eTPU_C handles byte-wide values.

In this example, eTPU_C "parses" the target address, and uses the appropriate byte of P to host the assigned value.

0000                                      int8 a;
0001                                      int8 * z;

                                          /* with pointers */
                                          z = &a;
0200 9FFF7B00   alu p = 0 ;
                    ram 0001 = p23_0.
0204 1C8F0FFE   alu a = #0x23.            *z = 0x23;
0208 9FEFFF00   ram diob = 0001.
020C FFEFF8D9   ram p31_0 = (diob).
0210 080BFBDA   alu nil = diob & 
0214 F0E0119F   if z==0 jump 0230,
0218 080BFBBA   alu nil = diob & 
021C F0C0115F   if z==0 jump 0228,flush.
0220 3FF9AFF4   alu p31_24 = a .
0224 F7C0121F   jump 0240,flush.
0228 3FF9BFF4   alu p23_16 = a .
022C F7C0121F   jump 0240,flush.
0234 3FF9CFF4   alu p15_8 = a .
0238 F7C0121F   jump 0240,flush.
023C 3FF9DFF4   alu p7_0 = a .
0240 FFFFF8D9   ram (diob) = p31_0.

Thinking of SPRAM as a byte-oriented memory, eTPU is big-endian: the MSB will appear in the (effectively) lowest address space.


eTPU_C is allocating my variables all over the place. Some are allocated out of order! What's wrong?

The compiler has specific rules about allocating variables, temporary locations, and registers.

Globals are allocated in low memory locations (from 0x0000 up). Locals are allocated in high locations (from 0x0400 down). These locations are reported in the listing file, at the left-hand side of declarations.

If possible, the compiler will use a register instead of a memory location. Registers are assigned special pseudo-locations inside the compiler. In the listing file, the values appearing for declarations allocated from variables represent these internal pseudo-locations, even if they appear out of order or overlap other variables.


eTPU_C is giving "RAM allocated out of default RAM space" warnings. What's wrong?

This warning can occur when #pragma memory RAM or #pragma memory LOCAL directives declare RAM that doesn't start on a 4-byte boundary. Variable declarations will try to allocate locations partly outside of declared RAM space


How are structure bit fields dealt with?

As efficiently as possible. Bit fields are packed within 32-bit boundaries. Packed bit fields can have a dramatic impact on the amount of code generated.

Bit Fields

This example declares two structs, one with less than 32 bits of bit fields, and one with more.

                                          struct {
0000 01 00                                  unsigned int shortElement : 1;   /* 1 bit in size */
0000 11 01                                  unsigned int longestElement : 17; /* 17 bits */
0002 06 02                                  unsigned int longElement : 6;    /* 7 bits in size */
0000 0004                                 } myBitField;

                                          struct {
0000 0F 00                                  unsigned int fifteenbits : 15;
0001 04 07                                  int fourbits : 4;
0004 11 00                                  unsigned int seventeenbits: 17;
0004 0008                                 } myLongerBitField;

0200 9FEFFB00   ram p23_0 = 0001.         myBitField.shortElement = 1;
0204 0800DBA2   alu p7_0 = p7_0 | 
0208 9FFFFB00   ram 0001 = p23_0.
020C CFEFF000   ram p31_0 = 0000.         myBitField.longElement = 0x55;
0210 1FF03FE4   alu p = #0x03FFFF.
0214 3B180FF2   alu a = p & p ,ccs.
0218 19597382   alu p = a | #0x540000,
021C CFFFF000   ram 0000 = p31_0.

0220 9FEFFB01   ram p23_0 = 0005.         myLongerBitField.fourbits = -2;
0224 09F87FF2   alu p = p & #0xFF7FFF,
0228 1BE87392   alu p = p & #0xF8FFFF,
022C 000FF418   alu diob = #0xFF0000.
0230 3B787FF0   alu p = p | diob ,ccs.


How can I recover the the contents of the Capture registers after ERT_A/B have been written over by programming a match?


chan = chan;

The compiler will not optimize this out, as there are side effects.

Recovering the Capture register values

When a thread starts, ERT_A/B are loaded with the values of the Capture registers. This code reloads those values after using ERT_A/B for other purposes.

                                          /* Thread starts, ERT_A/B = Capture */

                                          erta = 0x555;
0200 015F253D   alu erta = #0x0555.
0204                                      ertb = 0xAAA;

0204 02AF36D9   alu ertb = #0x0AAA.
0208                                      channel.ERWA = 0b0;
                                          channel.ERWB = 0b0;

0208 7FF94F43   alu chan = chan ,ccs;     chan = chan;
                    chan write_erta,


How do I access parameters coherently?

Add the following declarations to your program:

#define _coherentread(a_source,a_dest,b_source,b_dest) \
   {register_diob diob; register_p p; diob = a_source; \
    p = b_source;  b_dest = p; a_dest = diob;};

#define _coherentwrite(a_dest,a_source,b_dest,b_source)\
   {register_diob diob; register_p p; diob = a_source; \
    p = b_source; a_dest = diob; b_dest = p; };

register_diob diob;
register_p    p;

#define CoherentWrite(a,av,b,bv) diob = av; p = bv; NOP(); a = diob; b = p;

where a_dest and b_dest are the variables, and a_source and b_source are values to assign to a_dest and b_dest.

There are a few restrictions for using this macro:

  • Only the b arguments can have 32 bit variables (because they use the p register). The a arguments in these macros must be 24 bits. The b arguments may be 24 or 32 bits.

  • Expressions for b arguments cannot use anything that requires the diob register (arrays, pointers in general). There are no restrictions on the a expressions.


How do I access the angle clock?

The angle clock hardware allows microcode to watch the turning of a gear, and act at angles or rotations.

The TPR register, type register_tpr is specified as a structure:

struct tpr_struct {
  int TICKS   : 10;
  int TPR10   : 1;
  int HOLD    : 1;
  int IPH     : 1;
  int MISSCNT : 2;
  int LAST    : 1;
  } ;

Also, AddAngle() and SubAngle() macros (that perform modulo angle operations) are available in etpuc_util.h.


How do I cause a global exception?

The eTPU requires a value of 0x02 in the CIRC instruction field.

In C, CIRC is part of the channel structure:

channel.CIRC = 0x02

Alternatively, use a macro from eTPUC_common.h.

#define SetGlobalException()        (channel.CIRC = 2)

How do I enable or disable match events/the ME flag/the PP (parameter preload) flag in an Entry Point?

Use the intrinsic functions enable_match() and disable_match() to do this.

These functions generate no code. Look for the results in the listing file, in the entry point report following each part of the ETPU_function if()/else if()/else structure.

Enabling matches during a thread

Note: some additional entry report lines deleted for space

                                        void handler(void)
                                          if(pin == 1)
0204 4FFFFFFF   nop.                          NOP();
0208 6FFFFFFF   end.                        }
0014 40 81        00 S0A P01 ME 0204  HSR 0     lsr 1  m1 1  m2 1  pin x  flag1 x  flag0 0
0000             Thread Local RAM size
                                          else if(pin == 0)
020C 4FFFFFFF   nop.                          NOP();
0210 6FFFFFFF   end.                        }
0018 00 83        00 S0C P01 MD 020C  HSR 0     lsr 0  m1 0  m2 1  pin 0  flag1 x  flag0 0
0000             Thread Local RAM size

By default, match events are enabled.

To set or clear the PP flag, use either of the preload_p01() or preload_p23() intrinsics.


How do I generate a S-record file of the executable?

To generate an S-record executable file, do the following:

  1. Open your project in BCLIDE.

  2. Choose Project|Properties, and click on the Compiler Properties tab.

  3. In the Hex Dump File dropdown box, choose either "S1", "S2", or "S3" for the appropriate S-record output.

  4. Ensure that the command line option +delf is not present in the Additional Options below.

  5. Click OK, and compile your project as usual.

If you're not working within BCLIDE, add +dS1 or +dS2 or +dS3 to the compiler command line.

This will generate an S-record file for the entire executable.

The file will be zero-based and a complete executable, as it is generated from eTPU's point of view. To extract any pieces out of the file or alter it in other ways (such as changing the base address of the file), you'll need to use an external tool.


How do I interpret the reports the compiler generates?

eTPU_C generates some reports in the listing file that describe variables and ETPU_functions.

00F8 clocal                           signed int16        0200  0203

This type of report appears in the RAM usage map. It reports on the location that a variable occupies in RAM (some values also represent registers or parameter offsets) and its type.

It describes the range of instructions in the program for which this declaration is in scope and valid. Local variables allocated to the same locations are not overlapping so long as their scope ranges are different.

  0           one  0200  0210  (4 words)  SRAM =  0  Local RAM =  8

Each ETPU_function has an entry in the eTPU Function Summary. The function number at left describes its entries' position in the entry table. The record describes the function's lowest and highest ROM bytes and the number of 32-bit instruction words it uses.

Its parameter RAM and local RAM usage is also reported. Local RAM usage is for the entire function execution: memory allocated to local storage needs to equal the largest local RAM specification given here. The amount of parameter RAM needed can be used in host interface programs by expanding the host information macro ::ETPUfunctionframeram(x), where x is either the function number or name given in these reports.


How do I make better use of ETPU_function parameters from inside a subordinate C function?

ETPU_function parameters don't just pertain to the code in the ETPU_function body; they are relevant to everything that happens in the thread. It would be undesirable to pass these parameters around, and very difficult to get a globally-valid reference.

This item describes a way to use a matched set of ETPU_function parameters and a special global pointer to get access to the parameters in a C function.

Procedure: Accessing C pointers

  1. Declare a global structure of the eTPU function parameters, and declare the chan_base register as a pointer to the structure. The parameters of the current can then be referenced by name by the called function. This does not incur code or execution time overhead.

    struct _xyz_arg {
      int chan_arg1,
    } register_chan_base *pxyz_arg;

    register_chan_base is the processor-specific type for the chan_base register; any declared variable of this type refers to chan_base.

  2. Define an ETPU_function with struct _xyz_arg as the parameters. This is a slight inconvenience, but has the same effect as if the structure members were individual parameters.

    int i;
    #pragma ETPU_function xyz @ 3;
    void xyz (struct _xyz_arg args)
      if (hsr == 3)
          i = args.chan_arg2;

    Within the ETPU_function, code accesses the parameters indirectly:

     i = args.chan_arg2;
  3. C functions access the ETPU_function prameters through the global chan_base pointer; it always points to the current ETPU_function parameter list.

    void func ( void )
      int x;
      x = pxyz_arg -> chan_arg3;

    Dereferencing this pointer by the "->" operator is the same as direct access to the eTPU function parameters, and doesn't require the address calculation at run-time in software. There is no code penalty to access parameters this way. The generated access code is identical to the parameter access in the eTPU function.


How do I perform a read_mer or read_mer12 operation?

Use the read_match() intrinsic function.


How do I re-read the Capture registers into ERT1/2? How do I read the Match registers into ERT1/2?

To refresh ERT1/2 with the value of the Capture registers, re-assign the chan register:

chan = chan;

To load ERT1/2 with the value of the Match registers, use the read_match() intrinsic.


How do I use eTPU semaphores?

The short answer: assign a number from 0 to 3 to channel.SMPR, and loop until channel.SMPR tests true. Perform the sensitive work to be protected by the semaphore, and assign -1 to channel.SMPR.

Alternatively, use these macros in eTPUC_common.h:

// Semaphore operations
#define IsSemaphoreLocked()   (channel.SMPR == 1)
#define LockSemaphore(num)    (channel.SMPR = num)
#define FreeSemaphore()       (channel.SMPR = -1)

Semaphores are intended for communication between eTPU engines, when two (or potentially more) of them appear in an eTPU subsystem. A channel can set a semaphore, (non-destructively) test whether the other engine set the same semaphore, and clear the semaphore it set.

Simply use the above statements in two ETPU_functions that differ in their channel assignment.

There's little reason to use them within one channel context: eTPU threads are run-to-completion. The end of a thread clears the semaphore, but leaving it that long is not recommended as it could significantly delay the work of the other execution engine.

[Important] Important

While you can set one of 4 different semaphore values, you don't need to test which number was set. Simply test channel.SMPR: if it's true, you've set the same semaphore as the other engine, and your work might interfere with its ETPU_function's work. Wait until channel.SMPR is clear before proceeding.


This example demonstrates semaphore operations. The two functions below would be called by ETPU_functions running on different engines. Since they both use semaphore 2, the protected sections will not execute at the same time.

                                        void one(void)
                                              channel.SMPR = 2;
0200 F7A0101F   if smlck==0 jump 0200,    while (channel.SMPR == 0);
0204 FFFFF7DB   ram lock_g2.

                                          /* Do protected work */

                                          channel.SMPR = -1;

                                          /* Continue */
0208 FFFFCCF9   return,noflush.         }
020C FFEFF7FB   ram free_g.

                                        void theother(void)
                                              channel.SMPR = 2;
0210 F7A0109F   if smlck==0 jump 0210,    while (channel.SMPR == 0);
0214 FFFFF7DB   ram lock_g2.

                                          /* Do protected work */

                                          channel.SMPR = -1;

                                          /* Continue */
0218 FFFFCCF9   return,noflush.         }
021C FFEFF7FB   ram free_g.


How should I initialize globals and static locals?

It's possible to use a Host Service Request to perform initialization, but we don't feel it's the best way.

We recommend using the host interface macros to create an initialization routine to run on the host.

Here is one way:

Host Initialization

This example shows a basic initialization process.

0200                                    #pragma write c, (

                                        int pram_location;
                                        int pram_top;
                                        #define init_pram(where, towhere) (pram_location = where, pram_top = towhere)
                                        #define alloc_pram(howmuch) (pram_location += howmuch)
                                        #define exceeds_pram(howmuch) ((pram_location + howmuch ) > pram_top ? 1 : 0)

                                        /* ETPUglobalimage expansion omitted due to size: using
                                           const char etpu_image[]; */

                                        void initialize_eTPU(void)
                                          /* initialize SCM */
                                          /* alternative, use ETPUcodeimagesize */
                                          for(int i; i < 4 ; i++)
                                            outb(ETPU_SCM_BASE+i, etpu_image[i]);

                                          /* initialize eTPU globals */
                                        #define __etpu_globals(offset, valtype, value) outb(ETPU_PRAM_BASE+offset, (valtype)value);

                                          ::ETPUglobals ;

                                          /* initialize channel 1 */
                                          /* ::ETPUfunctionnumber(handler) */
                                          CxCFS(1, 1);


                                          if(exceeds_pram(::ETPUfunctionframeram(handler))) error(OF_SOME_KIND);
                                          CxCBPA(1, alloc_pram(::ETPUfunctionframeram(handler)));

                                        #define __etpu_staticinit(offset, value) outb(CBPA_BASE(1)+offset, value);



The generated host interface file (here edited for length) looks like this:

 int24 pram_location;
 int24 pram_top;
 #define init_pram(where, towhere) (pram_location = where, pram_top = towhere)
 #define alloc_pram(howmuch) (pram_location += howmuch)
 #define exceeds_pram(howmuch) ((pram_location + howmuch ) > pram_top ? 1 : 0)
 const char etpu_image[] = { 0xC0,0x85,0xC0,0x85,0xC0,0x85,0xC0,0x85,
 void initialize_eTPU(void)
   for(int24 i; i < 4 ; i++)
     outb(ETPU_SCM_BASE+i, etpu_image[i]);
 #define __etpu_globals(offset, valtype, value) outb(ETPU_PRAM_BASE+offset, (valtype)value);
   __etpu_globals(CC                               ,cc_reg    ,0x0000)
__etpu_globals(global1                          ,sint24    ,0x0001)
__etpu_globals(global2                          ,sint24    ,0x0005)
   CxCFS(1, 1);
   if(exceeds_pram(0x0000 )) error(OF_SOME_KIND);
   CxCBPA(1, alloc_pram(0x0000 ));
 #define __etpu_staticinit(offset, value) outb(CBPA_BASE(1)+offset, value);


How should I structure my code?

There are a few rules to follow:

  • Functions that might not get linked in to the final executable should be put in libraries (modules with #pragma library and #pragma endlibrary directives at the beginning and end, respectively). This instructs the compiler to omit generating code for them if they're not invoked.

    Order within the link is important too. Libraries containing any such functions must be linked in after the main program.

  • Note that the above doesn't change the C requirement that functions be prototyped before they are invoked. Your library should have a standard header file with function prototypes.

  • Both headers and program modules should have #ifdef/#define/#endif protection, to prevent compilation errors:

    #ifndef __MODULE_C
    #define __MODULE_C
    /* ... */
    #endif /* __MODULE_C */

    If you've written single-file libraries without this protection, it might cause a problem when compiling using Absolute Code Mode.


How should the host communicate with the eTPU?

There are a few ways:

  • You can pass information through static locals. The host sets these itself, during initialization.

    Use the ::ETPUlocation macro to identify the offset of the static in the function frame. Rather than assigning the value in ::ETPUstaticinit which comes from the eTPU_C program, simply set your own initial value.

    For more information on host interface macros, see the product documentation.


I declared a static local variable named the same as a function parameter. The compiler didn't catch the error. Is this a bug?

It's not a bug; it is a problem with the C language itself. The local shadows the parameter.


I have an application developed with an old version. Will it compile properly with the latest version?

Yes. If you have written your program according to our recommended practices in the product documentation, upgrading should be seamless. All Byte Craft Limited Code Development Systems have backward compatibility with previous language constructs.

Please note:

  • Improvements in the compiler can alter the generated code; this may impact your testing.
  • Device header files may change between versions. If you've written customized device header files, they may require some revision. Simply examine the stock headers that we ship with the product.
  • Remember to recompile your libraries when you upgrade your Code Development System product. All libraries to be linked or included into a program must be compiled with the same compiler.

I need to combine one eTPU executable into another eTPU executable. How can I do this?

Use the BClink directive ETPUIMAGE. During this linking, the specified executable will be linked in to the final executable. eTPU_C will perform checks to prevent conflicts between the two executables.

  1. Export the first executable with the following eTPU_C host interface macro in the first linker command file.
    #pragma write msc, ( ::ETPUcode );
  2. Import the first executable during linking for the second executable, using the ETPUIMAGE directive instead of an OBJECT or LIBRARY directive.

    Export a new executable and MISC value for the host compiler's use.

    // Output a code image of both links
    #pragma write q, (const u32 etpu_code[] = { ::ETPUcode32};);
    // Create the combined MISC value
    #pragma write q, ( #define MISC ::ETPUmisc );
    // Read the code image of the first link and combine it with the second link
    ETPUIMAGE= etpu_a_CPU.msc

I need to place variables manually. How can I do this?

You can place variables in their declaration:

type identifier @ address;

Simply append an @ symbol and a valid address. Since eTPU_C can calculate constant expressions at compile time, you can use relative addresses:

void xyz (int32 a,b,c)
  int16 myarg1 @ &a;
  int16 myarg2 @ &a + 2;
  int8  myarg3 @ &b;
  int8  myarg4 @ &b + 1;

	  /* ... */
[Important] Important

eTPU_C does not take such explicitly-placed variables into account when allocating memory for other variables. Check the listing file to ensure variables do not conflict.

Because eTPU is a multi-processor system (host and one or two engines), placing variables can avoid conflicts that semaphores or parameter coherency cannot resolve.


I want to assign an ETPU_function to more than one channel. Will all channels share static variables in common?

Each channel, when it runs a shared ETPU_function, can have its own function frame based at its own CPBA setting. This allocation contains parameters and local static variables.


I'm getting the error "OVERWRITING PREVIOUS ADDRESS...Conflict in location of entry table". What's wrong?

There are two main possibilities:

  • An ETPU_function number is duplicated between two ETPU_functions. Check the #pragma ETPU_function declarations in your program.

  • There are more than 8 ETPU_functions and the entry address table has not been moved. See the #pragma entryaddr directive.


I've performed an MDU division in my code. How can I access the remainder?

Access the MACH register directly:

Result = Value1 / Value2;  
Remainder = mach;

Is an eTPU_C available for Linux or Mac?

eTPU_C is a Windows executable. It should run under a Windows emulator or environment on Linux or Mac. However, we have little experience running it in these environments and cannot offer technical support for platform-specific issues. eTPU_C relies only on core functions in Windows that should be well supported in alternative environments.

Issues previously experienced include slow speed of execution, display and interactivity problems, and outright crashes.


Isn't hand-optimized code more efficient?

There is no technical reason why compiled code on the eTPU cannot be as tight as hand-optimized code. The ISO C standard for embedded systems overcomes previous C language limitations related to embedded.

There are three things that the eTPU_C compiler can do very well that hand optimization finds difficult.

  1. Computers are very good at accounting.
  2. Programmers tricks, once added to the knowledge base in the eTPU_C compiler, remains available for everyone's applications forever. The compiler is capable of combining these tricks into combinations that we have not anticipated.
  3. Most of the detail of the instruction set and eTPU architecture is embedded in the compiler. Application developers can rely on this and focus on application details.

In the eTPU_C compiler, we have placed in the code generator rules that allow for sub-command re-ordering. What we found was there are only a few cases where this can happen without un-wanted side effects. We can implement complex rules only as we gather more experience identifying what should be allowed. The problem is the same as for hand coding, except we only need to get the details right once.

Optimizers have three parameters to work with: code size, ram requirements and execution cycles.

  • Optimization occurs when one or more of the three is reduced but not at the expense of the remaining parameters.
  • Compression occurs when there is an exchange between parameters where one or more are reduced at the expense of the remaining parameters.

Another way to look at this: optimizers will not make a bubble sort into a quick sort, but they will make the best bubble sort possible.


My simulator is reporting that a MDU sub-instruction is "sampling the condition flags". Is this a problem?

No. This will not affect the correctness of the code. You can often disable this type of warning in the simulator software.


My threads aren't starting as expected. What's wrong?

Ensure that you give as much information to the compiler in the if()..else if()..else expressions as possible. Explicitly test lsr, m1/m2, pin, and flag0/flag1. Most importantly, add the hsr test explicitly, as the first term evaluated in an if() expression. Remember that hsr is always tested whether or not it's present in the thread expression. If it's not there, it is tested against a hsr value of 0.

Also note whether the #pragma ETPU_function directive specified the standard or alternate condition code encodings.


Some of my code was placed after a jump. Will it ever be reached?

eTPU_C is a pipelined processor. It can sometimes process instructions out of order. An jump takes time to process, so the following instruction can be loaded and executed before the jump takes place.

                                           erta = 23;
020C 1C5F2FFE   alu erta = #0x17.
0210                                       ertb = 45;
0210 1CBF3FBE   alu ertb = #0x2D.
0214                                       channel.ERWA = 0;
                                           channel.ERWB = 0;
                                           channel.MTD = 0;
0214 F7E0111F   jump 0220,noflush.         else
0218 FF3FACFB   chan write_erta,
                    mtd = enable_mtsr.

In this example, chan operations take place before the change in program flow. The noflush subinstruction in the jump prevents the processor from flushing the pipeline and obliterating the instruction following the jump.

A jump subinstruction with a flush subinstruction does not allow the instruction following to execute. The following instruction is probably an entry point for a different function or code block.


The compiler has optimized a local variable into a register. I'd prefer it take a memory location. How can I change this?

The compiler has correctly optimized the variable into a register because it could do so. Declare the variable as a static variable, and eTPU_C will be forced to allocate a location.


The documentation talks about a device header file and #pragma memory declarations. Where are they?

The compiler has defaults that apply to most eTPU programs, so device header files aren't strictly necessary to compile eTPU programs.

In some situations, you might want to create a header file with configuration statements that alter the compiler's default settings for code memory and parameter RAM. This file is typically described as the "device header file". It must be available to the compiler and BClink (if used).


The host and eTPU are performing read-modify-writes but the eTPU is overwriting the host's update. Is this a bug?

I'm having problems when the host and eTPU are using variables in the same PRAM word. Both are performing read-modify-write sequences on their own words, but the eTPU is overwriting the host's update. Is this a bug?

No, but it will require you to use semaphores to arbitrate access to the PRAM location.


The simulator warns about an MDU paired with a CCS subinstruction. It calls the choice "puzzling". What's wrong?

This is effectively a "don't care" operation. The MDU flags are always preserved. The compiler uses CCS for all math operations, except where explicitly disabled by optimization.


What causes threads to start?

Threads are started by eTPU events (host/link service or match/capture events). Execution starts at the ETPU_function associated with a channel.

In eTPU_C, the if()..else if()..else structure "tests" the channel conditions, and causes the appropriate code to execute. The compiler translates the if() structure into the channel condition encoding, and uses that as position information for the thread entry point in that ETPU_function's portion of the entry table.

Though all the tests available to you in this context are valid (they aren't misleading), some of the tests check what caused the thread to start, and others check parametric information.

  • Threads are actually invoked by Host Service Requests (hsr set to something other than 0), Link Service Requests from another thread, or Match or Transition events.

  • Pin state and flag states (Flag0 and Flag1) are testable but are never directly responsible for a thread starting.

  • There is always an implied test of hsr. A thread that doesn't explicitly test hsr will still only run when

    hsr == 0

What defaults are built in to the compiler?

The default SCM space for generated code starts at 0x200. This allows etpu functions 0-7 to be defined in the entry tables.

To allow more entries, this base rom space must be moved. This is done with

#pragma memory ROM [size] @ base_address;

For example:

#pragma memory ROM [0x8000-0x400] @ 0x400;

will allow for space for 16 entries in the eTPU function entry tables.

The default SPRAM space runs from 0x0000 to 0x0400. Allocations for global variables start at 0 and ascend. Locals and parameters not allocated from registers begin at the upper bound of SPRAM and descend.

You can change this setting with the #pragma memory RAM directive.


What is Absolute Code Mode?

Absolute Code Mode in Byte Craft Limited compilers generates executable code from the compiler without a linking step. Absolute Code Mode still allows you to select object code from libraries and include it as part of an application.

The compiler will read in any functions that are referenced in the eTPU_C program and are available in a library file. Simply #include the library's header file at the top of a program module, or (if there is no header) #include the library itself at the end of the main program.

For example, consider a program that uses spark, fuel, cam, and crank ETPU_functions. These statements will select the referenced eTPU functions from engine.lib and assign them to individual eTPU function numbers.

engine.lib members

#pragma ETPU_function spark @ 2;
#pragma ETPU_function fuel  @ 3;
#pragma ETPU_function cam   @ 4;
#pragma ETPU_function crank @ 5;

#include <engine.h>

When creating library files, enclose the code in #pragma library and #pragma endlibrary statements. These signal the compiler to read into the program only those functions that are referenced in the main program. Compile the library sources to an object file, and rename the object file with a .lib extension.

The significance of the .lib extension: .lib files are automatically included every time a .h file is #included in a source program. In all cases, library files should be renamed from .obj to .lib.


What other ways can I signal the host?

There are two other exception types: channel interrupts and data transfer interrupts. In those devices without DMA hardware, the two are indistinguishable. Assign values 0 or 1 to channel.CIRC, or use these macros from eTPUC_common.h.

// Channel control macros
#define SetChannelInterrupt()       (channel.CIRC = 0)
#define SetDataTransferInterrupt()  (channel.CIRC = 1)

What registers are available for inline assembly?

The DIOB, A, P in all of its forms (p31_0, p31_16, and so on) are all available for inline assembly essentially without any issues.

The B register is used as a temporary location in expression processing. It is usually freed by the end of a C statement, allowing inline assembly.

The MACH and MACL are sometimes used by the compiler in C functions that don't involve multiply and divide operations as storage for local variables. Register usage in inline assembly is NOT tracked by the compiler; diagnose any conflicts through the listing file.

The C, D and SR are used for local variables in ETPU_function threads. Any of these registers may be declared as global variables in an application; if so declared, the compiler will not use registers for local variables.


Who distributes your products?

See Distributors for an updated list of distributors.


Why does eTPU_C not perform this or that optimization?

Some optimizations verge on second-guessing the programmer. In some cases, especially those with possible side-effects, obeying the source code as written is preferable to reordering instructions, even if it means wasting an instruction or two.