Assembly Language Reference¶
Hardware Overview¶
Registers¶
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.
Pipeline Hazards¶
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]
jmp R0
jsr R0
Violating this rule will cause the previous value of R0 to be used instead of the newly loaded one.
Memory Architecture¶
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.
Program Flow¶
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 wev0
or 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.
Zero-Overhead Loop¶
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 biob0
or jmp
).
Assembler Overview¶
Code Placement¶
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.
Labels¶
The addr
and 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: task
/ struct
/ member
.
Immediate values¶
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, simm
and 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
- The
~
operator, which inverts all 32 bits in the expression calculation
Instruction Prefix¶
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.
Instruction Set¶
Instruction Timing¶
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 (
in
,out
,iobset
,iobclr
andiobtst
) 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 (
in
,out
,iobset
,iobclr
andiobtst
) to certain registers in AUX_SPIM and AUX_MAC will block until ongoing module activities have completed - Power management instructions (
wev0
,wev1
andsleep
), 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¶
Syntax | Description | Operation | Z | N | C | V | Encoding [15:0] | Prefix |
ld Rd, [#addr] | Load direct | Rd = mem[addr] | 0ddd.10aa.aaaa.aaaa |
addr | ||||
ld Rd, [Rs] | Load indirect | Rd = mem[Rs] | 1ddd.1111.0000.1sss |
|||||
ld Rd, [Rs++] | Load indirect, post-increment | Rd = mem[Rs], Rs++ | 1ddd.1111.0001.0sss |
|||||
ld Rd, [Rs+R0) | Load indexed | Rd = mem[Rs+R0] | 1ddd.1111.0001.1sss |
|||||
st Rd, [#addr] | Store direct | mem[addr] = Rd | 0ddd.11aa.aaaa.aaaa |
addr | ||||
st Rd, [Rs] | Store indirect | mem[Rs] = Rd | 1ddd.1111.0010.1sss |
|||||
st Rd, [Rs++] | Store indirect, post-increment | mem[Rs] = Rd, Rs++ | 1ddd.1111.0011.0sss |
|||||
st Rd, [Rs+R0] | Store indexed | mem[Rs+R0] = Rd | 1ddd.1111.0011.1sss |
I/O Word Access¶
Syntax | Description | Operation | Z | N | C | V | Encoding [15:0] | Prefix |
in Rd, [#addr] | Input direct | Rd = reg[addr] | 1ddd.1001.aaaa.aaaa |
addr | ||||
in Rd, [Rs] | Input indirect | Rd = reg[Rs] | 1ddd.1111.0000.0sss |
|||||
out Rd, [#addr] | Output direct | reg[addr] = Rd | 1ddd.1011.aaaa.aaaa |
addr | ||||
out Rd, [Rs] | Output indirect | reg[Rs] = Rd | 1ddd.1111.0010.0sss |
I/O Bit Access¶
Syntax | Description | Operation | Z | N | C | V | Encoding [15:0] | Prefix |
iobclr #imm, [#addr] | I/O bit clear direct | reg[addr] &= ~2^imm | 010i.01ii.aaaa.aaaa |
addr | ||||
iobset #imm, [#addr] | I/O bit set direct | reg[addr] |= 2^imm | 011i.01ii.aaaa.aaaa |
addr | ||||
iobtst #imm, [#addr] | I/O bit test direct | reg[addr] & 2^imm | x | 001i.01ii.aaaa.aaaa |
addr |
Register Access¶
Syntax | Description | Operation | Z | N | C | V | Encoding [15:0] | Prefix |
ld Rd, #simm | Load immediate | Rd = simm | 0ddd.00ii.iiii.iiii |
simm | ||||
ld Rd, Rs | Load register | Rd = Rs | 1ddd.1101.0100.0sss |
Logical Operations¶
Syntax | Description | Operation | Z | N | C | V | Encoding [15:0] | Prefix |
and Rd, #imm | AND immediate | Rd &= imm | x | x | 0 | 0 | 1ddd.0000.iiii.iiii |
imm |
or Rd, #imm | OR immediate | Rd |= imm | x | x | 0 | 0 | 1ddd.0010.iiii.iiii |
imm |
xor Rd, #imm | XOR immediate | Rd ^= imm | x | x | 0 | 0 | 1ddd.0100.iiii.iiii |
imm |
tst Rd, #imm | Test immediate | Rd & imm | x | x | 0 | 0 | 1ddd.1100.iiii.iiii |
imm |
and Rd, Rs | AND register | Rd &= Rs | x | x | 0 | 0 | 1ddd.1101.0000.0sss |
|
or Rd, Rs | OR register | Rd |= Rs | x | x | 0 | 0 | 1ddd.1101.0000.1sss |
|
xor Rd, Rs | XOR register | Rd ^= Rs | x | x | 0 | 0 | 1ddd.1101.0001.0sss |
|
tst Rd, Rs | Test register | Rd & Rs | x | x | 0 | 0 | 1ddd.1101.0011.0sss |
|
inv Rd | Invert register | Rd = ~Rd | x | x | 0 | 0 | 1ddd.1101.1001.0010 |
Arithmetic Operations¶
Syntax | Description | Operation | Z | N | C | V | Encoding [15:0] | Prefix |
add Rd, #simm | Add immediate | Rd += simm | x | x | x | x | 1ddd.1000.iiii.iiii |
simm |
cmp Rd, #simm | Compare immediate | Rd - simm | x | x | x | x | 1ddd.1010.iiii.iiii |
simm |
sub Rd, Rs | Subtract register | Rd -= Rs | x | x | x | x | 1ddd.1101.0001.1sss |
|
add Rd, Rs | Add register | Rd += Rs | x | x | x | x | 1ddd.1101.0010.0sss |
|
cmp Rd, Rs | Compare register | Rd - Rs | x | x | x | x | 1ddd.1101.0010.1sss |
|
subr Rd, Rs | Subtract reverse register | Rd = Rs - Rd | x | x | x | x | 1ddd.1101.0011.1sss |
|
abs Rd | Absolute register | Rd = (Rd >= 0) ? Rd : -Rd | x | x | x | x | 1ddd.1101.1001.0000 |
|
neg Rd | Negate register | Rd = -Rd | x | x | x | x | 1ddd.1101.1001.0001 |
Shift Operations¶
Syntax | Description | Operation | Z | N | C | V | Encoding [15:0] | Prefix |
lsl Rd, Rs | Logical shift left register | Rd <<= Rs[3:0] | x | x | x | 0 | 1ddd.1101.1000.0sss |
|
lsr Rd, Rs | Logical shift right register | Rd >>= Rs[3:0] | x | x | x | 0 | 1ddd.1101.1000.1sss |
|
asr Rd, Rs | Arithmetic shift right register | Rd >>= Rs[3:0], preserve sign | x | x | x | 0 | 1ddd.1101.1001.1sss |
|
lsl Rd, #imm | Logical shift left immediate* | Rd <<= imm | x | x | x | 0 | 1ddd.1101.1010.0iii |
|
lsr Rd, #imm | Logical shift right immediate* | Rd >>= imm | x | x | x | 0 | 1ddd.1101.1010.1iii |
|
asr Rd, #imm | Arithmetic shift right immediate* | Rd >>= imm, preserve sign | x | x | x | 0 | 1ddd.1101.1011.1iii |
* Shift immediate can be 1 to 8 bits, where imm = 0 corresponds to 8 bits.
Program Flow Control¶
Syntax | Description | Operation | Z | N | C | V | Encoding [15:0] | Prefix |
jmp addr | Jump direct | pc = addr | 0000.01aa.aaaa.aaaa |
addr | ||||
jsr addr | Jump subroutine direct | push(stack, pc + 1), pc = addr | 0001.01aa.aaaa.aaaa |
addr | ||||
jmp R0 | Jump indirect | pc = R0 | 1000.1101.1011.0111 |
|||||
jsr R0 | Jump subroutine indirect | push(stack, pc + 1), pc = R0 | 1001.1101.1011.0111 |
|||||
rts | Return subroutine | pc = pop(stack) | 1010.1101.1011.0111 |
|||||
bra rel | Branch relative | pc = pc + 1 + rel | 1000.1110.rrrr.rrrr |
|||||
b<cc> rel | Branch relative if condition is met | if (cc) pc = pc + 1 + rel | 1ccc.c110.rrrr.rrrr |
|||||
bev0 #ev, rel | Branch if event 0 | if (!events[ev]) pc = pc + 1 + rel | 1eee.0001.rrrr.rrrr |
|||||
bev1 #ev, rel | Branch if event 1 | if (events[ev]) pc = pc + 1 + rel | 1eee.0011.rrrr.rrrr |
For all other instructions, and also when not taking a conditional branch, the program counter is incremented by 1: pc = pc + 1.
Conditions¶
Syntax <cc> | Description | Condition | Encoding [3:0] |
gtu | Greater than, unsigned | !C & !Z | 0010 |
geu / iob0 | Greater or equal, unsigned / Tested I/O bit = 0 | !C | 0100 |
eq / z | Equal / Zero | Z | 0110 |
novf | Not overflow | !V | 1000 |
pos | Positive | !N | 1010 |
ges | Greater or equal, signed | (N & V) | (!N & !V) | 1100 |
gts | Greater than, signed | ((N & V) | (!N & !V)) & !Z | 1110 |
leu | Less or equal, unsigned | C | Z | 0011 |
ltu / iob1 | Less than, unsigned / Tested I/O bit = 1 | C | 0101 |
neq / nz | Not equal / Non-zero | !Z | 0111 |
ovf | Overflow | V | 1001 |
neg | Negative | N | 1011 |
lts | Less than, signed | (N & !V) | (!N & V) | 1101 |
les | Less or equal, signed | (N & !V) | (!N & V) | Z | 1111 |
Loop Flow Control¶
Syntax | Description | Operation | Z | N | C | V | Encoding [15:0] | Prefix |
loop R1, rel | Loop register, n = 1..255* | lc = LSB(R1), ls = pc + 1, le = pc + rel | 1000.0101.rrrr.rrrr |
|||||
loop #n, rel | Loop immediate, n = 2^x (x = 1..7)* | lc = n, ls = pc + 1, le = pc + rel | 1nnn.0101.rrrr.rrrr |
* The assembler offsets the end-of-loop label so it can be placed after the last instruction of the loop.
Note that rel
is signed, as for the relative branch instructions.
Power Management¶
Syntax | Description | Operation | Z | N | C | V | Encoding [15:0] | Prefix |
wev0 #ev | Wait for event 0 | Stop until events[ev] == 0 | 1eee.1101.1011.0000 |
|||||
wev1 #ev | Wait for event 1 | Stop until events[ev] == 1 | 1eee.1101.1011.0001 |
|||||
sleep | Sleep | Stop until wakeup, then pc = 2 * vector | 1011.1101.1011.0111 |
Miscellaneous¶
Syntax | Description | Operation | Z | N | C | V | Encoding [15:0] | Prefix |
nop | No operation | R7 = R7 (no effect) | 1111.1101.0100.0111 |
|||||
pfix #imm | Prefix* | Use prefix on next instruction | 1000.0110.iiii.iiii |
|||||
dw #imm | Data word | Inserts 16-bit immediate data value | iiii.iiii.iiii.iiii |
* The prefix instruction is inserted automatically by the assembler where needed. Do not insert this instruction manually.