Assembly Language Reference¶
There are eight 16-bit general purpose registers, R0 through R7. A few instructions require dedicated use of R0 and R1.
There are four status flags zero (Z), negative (N), carry (C) and overflow (V).
There is a 16-bit program counter, PC. There is also a dedicated stack for storing the program counter, which allows for three levels of subroutine calls.
Due to internal pipelining and the desire to minimize register bypass logic, the instructions with dedicated use of R0 require some attention. R0 must not be loaded from memory or I/O by an instruction immediately preceding any instruction using R0 as a dedicated register. The affected instructions are:
ld Rd, [Rs+R0]
st Rd, [Rs+R0]
Violating this rule will cause the previous value of R0 to be used instead of the newly loaded one.
Memory Access to Instructions and Data¶
Data and instruction memory addressing is 16-bit oriented (an address increment of 1 corresponds to 2 bytes). Data words and instructions are mapped to the same memory address space:
- CC13x0/CC26x0: 2 KB AUX RAM, address range 0x0000 through 0x03FF.
- CC13x2/CC26x2 and CC13x4/CC26x4: 4 KB AUX RAM, address range 0x0000 through 0x07FF.
Data memory is accessed through load and store instructions, supporting direct, register indirect, register indirect with post-increment and register indexed modes. Only supported access size is 16-bit.
There is no access to memory other than the AUX RAM.
I/O Access to Module Registers¶
The Sensor Controller can access AUX domain module registers through the I/O address space. I/O addressing is 8-bit oriented (an address increment of 1 corresponds to 1 byte), except for the register aliasing space, that is addresses 0 to 255, where each entry maps to a 16-bit register.
I/O registers are accessed through input and output instructions, supporting:
- Direct 1-bit accesses for 8 lsbs of a 16-bit register
- Direct or register indirect 16-bit accesses
Unaligned 16-bit accesses are not supported.
There is no access to registers outside of the AUX domain. Also, there is no access to the AUX RAM and AUX_SCE module through the I/O address space.
General program flow is controlled by explicit use of jump/branch, conditional branch, subroutine call and return instructions. Execution can also be halted temporarily by using the
wev1 instructions to wait for the specified event before resuming execution. Preemption is in any case not supported.
The Sensor Controller enters standby mode by executing the
sleep instruction. From standby mode, it can wake up and start execution from one of N vectors:
- CC13x0/CC26x0: There are 4 vectors, 1 of which is user-programmable. Vectors and wake-up triggers are configured through AUX_EVCTL and AON_EVENT.
- CC13x2/CC26x2 and CC13x4/CC26x4: There are 8 vectors, 3 of which are user-programmable. Vectors and wake-up triggers are configured through AUX_SYSIF.
The Sensor Controller firmware framework handles transition to standby mode and wake-up.
There is support for one level of zero-overhead loops using the
loop instructions. This stores the addresses of the first and last instruction of the loop, and starts the loop counter.
Until the zero-overhead loop is completed, the last instruction of the loop will always trigger a branch to the first instruction of the loop (without delay). This means that the last instruction of the loop cannot be a Program Flow Control instruction (for example
There is no support for placing instructions at specific memory addresses. The first instruction in the firmware framework is placed at address 0, which is the first instruction of vector 0.
The SCIF driver source code generation in Sensor Controller Studio extracts the relevant addresses of task data structures and framework control data structures, so that these data structures are accessible from C code running on the System CPU. It creates matching “struct” typedefs to make such accesses seamless.
rel instruction operands shall be given as labels. To support short labels, sub-labels starting with “/” can be used.
someLabel: cmp R7, #0 bnz /subLabel ; Can use sub-label in the same context add R7, #1 /subLabel: ... anotherLabel: cmp R7, #0 jmp someLabel/subLabel ; Must expand /subLabel since it is in a different context
For task data structure members, three hierarchy levels are used:
Immediate values can be given as expressions, using the following syntax:
<expr> ::= '(' <expr> ' ' <op2> ' ' <expr> ')' | '(' <op1> <expr> ')' | '(' <expr> ')' | <value> <op2> ::= '<' | '<=' | '==' | '!=' | '>' | '>=' | '/' | '*' | '+' | '-' | '%' | '|' | '||' | '&' | '&&' | '^' | '<<' | '>>' <op1> ::= '-' | | '~' | '!' <value> ::= label (for example someLabel/subLabel or anotherLabel) | hexadecimal value (for example 0x12AB) | decimal value (for example 14 or -42)
A small subset of the immediate values,
rel , are signed, and will be sign-extended to 16 bits. All other immediate values are unsigned.
Immediate Value Range and Value Masking¶
To allow larger than 16-bit intermediate values, expression calculation works on 32-bit signed integers.
The assembler does not automatically mask out most significant bits when calculating expressions for immediate value operands. It will instead generate an error “Immediate value out of range” if the value is too high or too low. Manual masking (using the
& operator) is therefore required when using:
- Negative values with unsigned immediate value operands
~operator, which inverts all 32 bits in the expression calculation
Some instructions with 8- or 10-bit immediate value may be extended to 16-bit immediate value by executing a prefix instruction,
pfix , immediately before it. The prefix is valid for the next instruction only.
The last column below indicates which immediate value may be extended, if any.
The assembler adds prefix instructions automatically as needed, and detects immediate values that are invalid or out of range.
CC13x0/CC26x0: The Sensor Controller runs at 24 MHz in active mode.
CC13x2/CC26x2 and CC13x4/CC26x4: The Sensor Controller runs at 24 MHz or 2 MHz in active mode (statically configurable). The Sensor Controller runs at 2 MHz in low-power mode.
All Sensor Controller instructions use 2 clock cycles, with the following exceptions:
- I/O accesses (
iobtst) to AUX_TIMER2 registers (CC13x2/CC26x2 and CC13x4/CC26x4 only), AUX_ADI4 registers and AUX_DDI0_OSC registers, which go through multi-cycle interfaces
- CC13x2/CC26x2 and CC13x4/CC26x4 only: I/O accesses (
iobtst) to certain registers in AUX_SPIM and AUX_MAC will block until ongoing module activities have completed
- Power management instructions (
sleep), which are used to wait for event signals
The Sensor Controller has priority over System CPU and uDMA when accessing memory and module registers. ADI and DDI accesses can however be delayed due to ongoing System CPU accesses.
Status Bit Updates¶
In the tables below:
- x indicates that the instruction updates the status bit
- 0 indicates that the instruction always clears the status bit
- - indicates that the instruction does not update the status bit
The status bits are updated as follows:
- Z (zero) is set if the result is 0
- N (negative) is equal to the most significant bit of the result
- C (carry) update depends on the instruction type:
- I/O bit access: Set according to the value of the tested bit
- Logical operations: Always cleared.
- Arithmetic operations: Set according to a carry or borrow out of the most significant bit of the result
- Shift operations: Set according to the last bit shifted out, being it through the most significant or least significant bit. Set to 0 for shift by 0 bits.
- V (overflow) update depends on the instruction type:
- Logical operations: Always cleared
- Arithmetic operations: Set if arithmetic signed overflow occurs, cleared otherwise
- Shift operations: Always cleared
Memory Word Access¶
|ld Rd, [#addr]||Load direct||Rd = mem[addr]||
|ld Rd, [Rs]||Load indirect||Rd = mem[Rs]||
|ld Rd, [Rs++]||Load indirect, post-increment||Rd = mem[Rs], Rs++||
|ld Rd, [Rs+R0)||Load indexed||Rd = mem[Rs+R0]||
|st Rd, [#addr]||Store direct||mem[addr] = Rd||
|st Rd, [Rs]||Store indirect||mem[Rs] = Rd||
|st Rd, [Rs++]||Store indirect, post-increment||mem[Rs] = Rd, Rs++||
|st Rd, [Rs+R0]||Store indexed||mem[Rs+R0] = Rd||
I/O Word Access¶
|in Rd, [#addr]||Input direct||Rd = reg[addr]||
|in Rd, [Rs]||Input indirect||Rd = reg[Rs]||
|out Rd, [#addr]||Output direct||reg[addr] = Rd||
|out Rd, [Rs]||Output indirect||reg[Rs] = Rd||
I/O Bit Access¶
|iobclr #imm, [#addr]||I/O bit clear direct||reg[addr] &= ~2^imm||
|iobset #imm, [#addr]||I/O bit set direct||reg[addr] |= 2^imm||
|iobtst #imm, [#addr]||I/O bit test direct||reg[addr] & 2^imm||x||
|ld Rd, #simm||Load immediate||Rd = simm||
|ld Rd, Rs||Load register||Rd = Rs||
|and Rd, #imm||AND immediate||Rd &= imm||x||x||0||0||
|or Rd, #imm||OR immediate||Rd |= imm||x||x||0||0||
|xor Rd, #imm||XOR immediate||Rd ^= imm||x||x||0||0||
|tst Rd, #imm||Test immediate||Rd & imm||x||x||0||0||
|and Rd, Rs||AND register||Rd &= Rs||x||x||0||0||
|or Rd, Rs||OR register||Rd |= Rs||x||x||0||0||
|xor Rd, Rs||XOR register||Rd ^= Rs||x||x||0||0||
|tst Rd, Rs||Test register||Rd & Rs||x||x||0||0||
|inv Rd||Invert register||Rd = ~Rd||x||x||0||0||
|add Rd, #simm||Add immediate||Rd += simm||x||x||x||x||
|cmp Rd, #simm||Compare immediate||Rd - simm||x||x||x||x||
|sub Rd, Rs||Subtract register||Rd -= Rs||x||x||x||x||
|add Rd, Rs||Add register||Rd += Rs||x||x||x||x||
|cmp Rd, Rs||Compare register||Rd - Rs||x||x||x||x||
|subr Rd, Rs||Subtract reverse register||Rd = Rs - Rd||x||x||x||x||
|abs Rd||Absolute register||Rd = (Rd >= 0) ? Rd : -Rd||x||x||x||x||
|neg Rd||Negate register||Rd = -Rd||x||x||x||x||
|lsl Rd, Rs||Logical shift left register||Rd <<= Rs[3:0]||x||x||x||0||
|lsr Rd, Rs||Logical shift right register||Rd >>= Rs[3:0]||x||x||x||0||
|asr Rd, Rs||Arithmetic shift right register||Rd >>= Rs[3:0], preserve sign||x||x||x||0||
|lsl Rd, #imm||Logical shift left immediate*||Rd <<= imm||x||x||x||0||
|lsr Rd, #imm||Logical shift right immediate*||Rd >>= imm||x||x||x||0||
|asr Rd, #imm||Arithmetic shift right immediate*||Rd >>= imm, preserve sign||x||x||x||0||
* Shift immediate can be 1 to 8 bits, where imm = 0 corresponds to 8 bits.
Program Flow Control¶
|jmp addr||Jump direct||pc = addr||
|jsr addr||Jump subroutine direct||push(stack, pc + 1), pc = addr||
|jmp R0||Jump indirect||pc = R0||
|jsr R0||Jump subroutine indirect||push(stack, pc + 1), pc = R0||
|rts||Return subroutine||pc = pop(stack)||
|bra rel||Branch relative||pc = pc + 1 + rel||
|b<cc> rel||Branch relative if condition is met||if (cc) pc = pc + 1 + rel||
|bev0 #ev, rel||Branch if event 0||if (!events[ev]) pc = pc + 1 + rel||
|bev1 #ev, rel||Branch if event 1||if (events[ev]) pc = pc + 1 + rel||
For all other instructions, and also when not taking a conditional branch, the program counter is incremented by 1: pc = pc + 1.
|Syntax <cc>||Description||Condition||Encoding [3:0]|
|gtu||Greater than, unsigned||!C & !Z||
|geu / iob0||Greater or equal, unsigned / Tested I/O bit = 0||!C||
|eq / z||Equal / Zero||Z||
|ges||Greater or equal, signed||(N & V) | (!N & !V)||
|gts||Greater than, signed||((N & V) | (!N & !V)) & !Z||
|leu||Less or equal, unsigned||C | Z||
|ltu / iob1||Less than, unsigned / Tested I/O bit = 1||C||
|neq / nz||Not equal / Non-zero||!Z||
|lts||Less than, signed||(N & !V) | (!N & V)||
|les||Less or equal, signed||(N & !V) | (!N & V) | Z||
Loop Flow Control¶
|loop R1, rel||Loop register, n = 1..255*||lc = LSB(R1), ls = pc + 1, le = pc + rel||
|loop #n, rel||Loop immediate, n = 2^x (x = 1..7)*||lc = n, ls = pc + 1, le = pc + rel||
* The assembler offsets the end-of-loop label so it can be placed after the last instruction of the loop.
rel is signed, as for the relative branch instructions.
|wev0 #ev||Wait for event 0||Stop until events[ev] == 0||
|wev1 #ev||Wait for event 1||Stop until events[ev] == 1||
|sleep||Sleep||Stop until wakeup, then pc = 2 * vector||
|nop||No operation||R7 = R7 (no effect)||
|pfix #imm||Prefix*||Use prefix on next instruction||
|dw #imm||Data word||Inserts 16-bit immediate data value||
* The prefix instruction is inserted automatically by the assembler where needed. Do not insert this instruction manually.