Program Control - wgrzegorz/c5xtools GitHub Wiki
Program Control
Normative source: SPRU056D, chapter 4 (Program Control). All vector addresses, register addresses, and bit positions below are transcribed from that chapter.
Program control covers how the TMS320C5x decides which instruction to fetch next: the program counter and the ways it is reloaded, the hardware stack that holds return addresses, conditional branch/call/return/execute, the two repeat mechanisms (single-instruction and block), the interrupt system, reset, and the power-down modes.
1. Program counter (PC)
The PC is a 16-bit register holding the address of the next instruction word to fetch from on-chip or off-chip program memory. Each cycle the PC is driven onto the program address bus (PAB); the addressed word is read into the instruction register (IREG), and the PC advances so the following fetch is ready.
The PC is not simply "plus one" every cycle. The value loaded depends on the operation (SPRU056D Table 4-1):
| Code operation | Value loaded into the PC |
|---|---|
| Sequential code | PC + 1 |
| Branch (B) | the long immediate word that follows the branch |
| Subroutine call | PC + 2 is pushed on the stack, then the PC takes the long immediate that follows the call; the return pops it back |
| Software interrupt (INTR, TRAP, NMI) or interrupt trap | the address of the matching interrupt vector |
| BLDD, BLDP, BLPD, MAC, MACD | a long immediate address |
| BACC, BACCD, CALA, TBLR, TBLW | the accumulator low byte (ACCL) |
| BLDD, BLDP, BLPD, MADD, MADS | the block move address register (BMAR) |
| End of a block repeat loop | the block repeat program-address start register (PASR) |
| Return instruction | the top of the stack |
During a repeat operation the repeated instruction is no longer prefetched, so the PC is free to walk program memory sequentially. The instructions that exploit this to fetch a second operand from program space are the multiply/accumulates (MAC, MACD, MADD, MADS), the program-to-data moves (BLPD, TBLR), and the data-to-program moves (BLDP, TBLW), plus the data-to-data move BLDD.
Program-memory address generation
Program memory holds code, tables, and immediate operands, and is reached only through the program bus. The PC supplies the address while instructions and long immediate operands are fetched; the PC is incremented during the read so the next fetch is lined up. On any address discontinuity (branch, call, return, interrupt, or block repeat) the appropriate value is loaded into the PC instead.
The PC can also address data in either program or data space. For a repeated 2-operand instruction this lets a second operand be fetched in parallel with the data bus, the array being swept by incrementing the PC. The block transfer instructions (BLDD, BLDP, BLPD) use both buses so the pipeline reads the next operand while writing the current one. BLPD takes its source address from the long immediate or from BMAR and fetches source data from program space; BLDP works the same way but uses the PAB for the destination. TBLR and TBLW behave like BLPD and BLDP except the PC is loaded from ACCL instead of the long immediate or BMAR, giving look-up-table access. MAC and MACD load the PC with the long immediate that follows the instruction; MADD and MADS load it from BMAR.
2. Hardware stack
The stack is 16 bits wide and 8 levels deep. It is reached directly with PUSH and POP, and is used automatically to save and restore the PC on calls and interrupts.
Behaviour on each operation:
- PUSH drives a value onto the top of stack (TOS); every existing level is pushed down one, and the eighth (bottom) level is lost. More than eight pushes without an intervening pop therefore discards the oldest data.
- POP lifts TOS off and moves every level up one. After the stack has been popped seven times in a row, any further pop keeps returning the bottom-level value, and all levels then hold that same value.
Four software instructions operate on the stack directly:
| Instruction | Operation |
|---|---|
| PUSH | push the accumulator low byte (ACCL) onto TOS |
| POP | pop TOS into ACCL |
| PSHD | push a data-memory value onto TOS |
| POPD | pop TOS into a data-memory location |
PSHD and POPD let a program build a deeper stack in data memory, so subroutines and interrupts can nest beyond eight levels.
The stack saves and restores the PC automatically. A return address is pushed when a subroutine is called (CALA, CALAD, CALL, CALLD, CC, CCD) or an interrupt is taken (a hardware interrupt, NMI, INTR, or TRAP). It is popped back into the PC on a return (RET, RETC, RETCD, RETD, RETE, RETI).
3. Conditional operations
Alongside the unconditional branch, call, and return, the 'C5x has conditional forms whose execution depends on the condition codes in the table below (SPRU056D Table 4-8).
| Mnemonic | Condition | Description |
|---|---|---|
| EQ | ACC = 0 | accumulator equal to 0 |
| NEQ | ACC != 0 | accumulator not equal to 0 |
| LT | ACC < 0 | accumulator less than 0 |
| LEQ | ACC <= 0 | accumulator less than or equal to 0 |
| GT | ACC > 0 | accumulator greater than 0 |
| GEQ | ACC >= 0 | accumulator greater than or equal to 0 |
| NC | C = 0 | carry bit cleared |
| C | C = 1 | carry bit set |
| NOV | OV = 0 | no accumulator overflow detected |
| OV | OV = 1 | accumulator overflow detected |
| BIO | BIO is low | BIO signal is low |
| NTC | TC = 0 | test/control flag cleared |
| TC | TC = 1 | test/control flag set |
| UNC | none | unconditional operation |
The conditional instructions are the branch BCND, the call CC, the return RETC, their delayed forms BCNDD / CCD / RETCD, and the conditional-execute XC. BCND, CC, and RETC are each resolved only in the fourth pipeline cycle, because the accumulator status from the previous instruction must have settled first. When the condition holds the pipeline is flushed and refilled, giving an effective four-cycle branch; when it fails the already-fetched next instruction decodes, giving two cycles. RETC is a 1-word instruction but, because of the possible PC discontinuity, times like BCND and CC.
Multiconditional tests
BCND, BCNDD, CC, CCD, RETC, RETCD, and XC can test more than one condition at once; every condition listed in the instruction must hold. Conditions are drawn from four groups, and at most four may be combined -- no two from the same group (SPRU056D Table 4-9):
| Group 1 | Group 2 | Group 3 | Group 4 |
|---|---|---|---|
| EQ | OV | C | TC |
| NEQ | NOV | NC | NTC |
| GT | BIO | ||
| LT | |||
| GEQ | |||
| LEQ |
So BCND BRANCH,LT,NOV,TC branches only if ACC < 0 and OV = 0 and TC = 1, but
LT and GEQ cannot be combined because both are in group 1.
Delayed forms and conditional execute
The delayed conditional instructions (BCNDD, CCD, RETCD) avoid the pipeline flush: the one 2-word instruction, or two 1-word instructions, that follow are executed while the branch target is being fetched, for an effective two cycles. The tested condition is not disturbed by those following instructions.
XC conditionally executes the code immediately after it instead of branching
around it: XC 1 guards one 1-word instruction, XC 2 guards one 2-word or two
1-word instructions. Its conditions are those of Table 4-8. The condition must
be stable one full cycle before XC executes; if it is not met, a NOP runs in
place of the guarded code. A single-word instruction placed directly before XC
should not change the tested condition, since an interrupt could trap between
them and alter it.
4. Single-instruction repeat (RPT)
RPT and RPTZ repeat the next instruction N + 1 times, where N is loaded into the 16-bit repeat counter RPTC, for a maximum of 65 536 executions. RPTC is cleared by reset and can be loaded only by RPT or RPTZ; although it is memory-mapped, writing to it directly can produce undesired results and should be avoided. RPTC decrements once per execution until it reaches 0.
Once a repeat is decoded, all interrupts -- including NMI, but not RS -- are masked until the loop finishes; the 'C5x still honours HOLD during the loop. Multicycle instructions such as the multiply/accumulates, block moves, I/O transfers, and table reads/writes are pipelined by the repeat so that, after the first iteration, they effectively complete one per cycle, with the absolute program or data address auto-incrementing. SPRU056D Tables 4-10 through 4-13 classify every instruction by repeatability (transformed to single-cycle, repeatable, not meaningful to repeat, and nonrepeatable); B, BACC, the calls and returns, IDLE/IDLE2, INTR/NMI/TRAP, RPT/RPTB/RPTZ, and the immediate-operand forms are among the nonrepeatable ones.
5. Block repeat (RPTB)
RPTB repeats a block of instructions N + 1 times, where N is loaded into the 16-bit block repeat counter BRCR, again to a maximum of 65 536. This gives zero-overhead FOR/DO loops. The mechanism uses three registers plus one flag:
| Element | Role |
|---|---|
| BRCR | block repeat counter; holds N (loop runs N + 1 times) |
| PASR | program-address start register; address of the instruction after RPTB |
| PAER | program-address end register; the RPTB long immediate operand |
| BRAF | block repeat active flag, a bit in PMST; set/cleared through PMST |
When RPTB executes it sets BRAF, loads PASR with the address of the next instruction, and loads PAER with its long immediate operand -- which must be the address of the word following the last loop instruction, minus 1 (so a 2-word instruction ending the loop is fully read). The block must contain at least three instruction words.
On every PC update PAER is compared with the PC. When they match, BRCR is checked: if BRCR > 0 it is decremented and PASR is copied into the PC, restarting the loop; otherwise BRAF is cleared and execution falls through past the loop.
There is only one set of block repeat registers, so nested block repeats require either saving the outer context (SMMR/LMMR on BRCR, PASR, PAER, then setting BRAF through PMST to resume the outer loop) or using RPTB for just the innermost loop and BANZD for the outer loops. A WHILE-style exit is built by clearing BRAF at least four instruction words before the loop end; the current pass finishes but the loop is not re-entered. RPT/RPTZ single-instruction repeats, BANZ/BANZD loops, and subroutine calls may appear inside a block repeat, and the loop resumes after a return. The single-word instruction at the very end of a block is not interruptible (unless the instruction before it is a single-word multicycle instruction); an interrupt latched while that last word is being fetched does not branch to its vector, which can lengthen worst-case interrupt latency.
6. Interrupts
The CPU supports 16 user-maskable interrupts (INT16-INT1); a given 'C5x device wires up only some of them (most use 9, the 'C57 uses 10, the rest are tied high). INT1-INT4 come from external hardware; on-chip sources are the timer (TINT), the serial ports (RINT, XINT, TRNT, TXNT, BRNT, BXNT), and the host port (HINT). There are also three software interrupt instructions (INTR, NMI, TRAP) and two external nonmaskable interrupts (RS and NMI). RS has the highest priority and INT16 the lowest. An INT1-INT4 or NMI pulse is valid when it is high for at least two machine cycles and low for at least three (at least four high and five low when the CPU is in IDLE2).
Vector locations and priorities
Interrupt vectors are two program-memory words apart so a branch fits in each slot (SPRU056D Table 4-14):
| Name | Dec | Hex | Priority | Function |
|---|---|---|---|---|
| RS | 0 | 0 | 1 (highest) | external nonmaskable reset signal |
| INT1 | 2 | 2 | 3 | external user interrupt #1 |
| INT2 | 4 | 4 | 4 | external user interrupt #2 |
| INT3 | 6 | 6 | 5 | external user interrupt #3 |
| TINT | 8 | 8 | 6 | internal timer interrupt |
| RINT | 10 | A | 7 | serial port receive interrupt |
| XINT | 12 | C | 8 | serial port transmit interrupt |
| TRNT | 14 | E | 9 | TDM port receive interrupt |
| TXNT | 16 | 10 | 10 | TDM port transmit interrupt |
| INT4 | 18 | 12 | 11 | external user interrupt #4 |
| (reserved) | 20-23 | 14-17 | N/A | reserved |
| HINT | 24 | 18 | --- | HINT ('C57 only) |
| (reserved) | 26-33 | 1A-21 | N/A | reserved |
| TRAP | 34 | 22 | N/A | software trap instruction |
| NMI | 36 | 24 | 2 | nonmaskable interrupt |
| (reserved) | 38-39 | 26-27 | N/A | reserved for emulation and test |
| (reserved) | 40-63 | 28-3F | N/A | software interrupts |
TRNT reads as RINT2 on the 'C52 and BRNT on the 'C56/'C57; TXNT reads as XINT2 on the 'C52 and BXNT on the 'C56/'C57.
The whole vector table can be relocated to the start of any 2K-word program page. The vector address is formed by concatenating the IPTR field of PMST with the interrupt number (1-16) shifted left by one. Reset clears IPTR, so the vectors map to page 0 and the reset vector is always at 0h; loading IPTR = 1, for instance, moves the INT5 vector to 080Ah.
Maskable interrupt flow
When any interrupt occurs its flag is set in the interrupt flag register (IFR), whether or not the interrupt is enabled. Pending interrupts are polled at the start of each machine cycle while INTM = 0, and the highest-priority enabled one is taken. An IFR flag (except from an active serial port) clears automatically when its trap is taken.
flowchart TD
A[Interrupt asserted] --> B[Flag set in IFR]
B --> C{INTM = 0 and IMR bit = 1?}
C -- no --> B2[Stay pending in IFR]
C -- yes, highest priority --> D[Finish current instruction]
D --> E[Globally disable interrupts: INTM = 1]
E --> F[Push PC to top of stack]
F --> G[Load PC with the interrupt vector address]
G --> H[Save key registers into shadow registers]
H --> I[IACK driven low]
I --> J[Clear the IFR flag]
J --> K[Run the ISR]
K --> L[RETI / RETE: pop stack, restore shadows]
The interrupt being acknowledged is reported on address bits A5-A1 at the falling edge of IACK; to decode it from on-chip vectors the CPU should run with AVIS = 0. When an interrupt is already being serviced, even a higher-priority one waits until interrupts are re-enabled, normally at the end of the ISR.
Interrupt flag register (IFR)
The IFR is a memory-mapped CPU register at data-memory address 06h. A 1 in a bit
means that interrupt is pending. A flag clears on reset, when its trap is taken,
or by writing a 1 to that bit (writing 0 has no effect); writing the IFR back
into itself clears all pending flags. For example SPLK #5,IFR clears INT1 and
INT3 only. If an interrupt exits IDLE/IDLE2 while interrupts are disabled
(INTM = 1), no IFR bit -- not even the one that caused the exit -- is cleared;
the flag clears only when the trap is actually taken with interrupts enabled.
Only the last of several pulses on one pin is seen if they arrive before the CPU
responds.
Interrupt mask register (IMR)
The IMR is a memory-mapped CPU register at data-memory address 04h, readable and writable. A 1 in an IMR bit enables that interrupt, provided INTM in ST0 is cleared. RS and NMI are not represented in the IMR and are unaffected by it.
The IFR and IMR share the same bit layout:
INTM bit
The INTM bit in ST0 globally gates all maskable interrupts: INTM = 0 enables
every unmasked interrupt, INTM = 1 disables them. INTM changes neither the IFR
nor the IMR. It is set by reset (RS active), by taking an interrupt trap, by the
NMI instruction, and by SETC INTM; it is cleared by CLRC INTM and by RETE.
Interrupts cannot be processed between a CLRC INTM (or SETC INTM) and the
following instruction -- both always complete first, which guarantees an ISR can
execute its return before the next interrupt and protects the PC stack.
Nonmaskable interrupts
RS and NMI ignore both INTM and the IMR. NMI serves as a soft reset or as the most time-critical event; unlike RS it initializes no control bits or registers and does not affect any 'C5x modes. On NMI the CPU completes all pipeline instructions, sets INTM = 1, and loads the PC with vector 24h. Because an NMI may be serviced even inside an ISR, key registers are not saved automatically. The NMI instruction performs the same action in software. The TRAP instruction vectors to 22h and likewise sets INTM = 1 without saving key registers. The INTR instruction lets software invoke any ISR; for INT1-INT4 it behaves like the external interrupt.
Interrupt context save
When a trap is taken the PC goes onto the 8-deep hardware stack (so subroutine
calls are still possible inside an ISR within the eight levels), and a 1-deep
shadow stack saves ACC, ACCB, ARCR, INDX, PMST, PREG, ST0, ST1, TREG0, TREG1,
and TREG2. The INTM bit of ST0 and the XF bit of ST1 are the exceptions -- INTM
is always set to disable interrupts. The shadows are copied back when RETI or
RETE runs, so the ISR needs no manual context save/restore. Because the shadow
stack is only one level deep, nested interrupts are not supported directly; a
software stack is needed for nesting. Interrupts stay disabled until a RETE or
CLRC INTM executes.
Latency
Minimum interrupt latency to IACK is eight cycles: three to synchronize the
interrupt externally, one for the CPU to recognize it, and four to execute the
jammed INTR and flush the pipeline. Multicycle instructions and wait states add
cycles. RPT and RPTZ delay interrupts (including NMI, but not RS) until every
repeat has finished, to protect the repeated instruction's parallel context.
HOLD takes precedence over interrupts: an interrupt that arrives during HOLD is
not taken until HOLDA is deasserted, unless the CPU is in concurrent hold
(HM = 0) with the vector table on-chip, in which case the interrupt is taken
regardless of HOLD. If the instruction just ahead of the jammed INTR is an
SST #0 while INTM was clear, INTM is set before that store and is saved as 1,
so a later ST0 restore would leave interrupts disabled; run SST #0 with
interrupts disabled, or re-enable them after reloading ST0.
7. Reset
Reset (RS) is a nonmaskable external interrupt and the highest priority event -- nothing preempts it. It aborts memory operations, so the system must be reinitialized after every reset. Driving RS low terminates execution and forces the PC to vector 0h; a hardware reset also clears all pending interrupt flags. For a clean power-up RS must be held low for at least six clock cycles (data lines go high-impedance, address lines are driven low); the 'C5x latches the pulse and stretches it internally. After RS has been high for 17 clock cycles, execution begins at 0h, which normally holds a branch to the initialization routine.
The reset sequence: (1) the running program is asynchronously aborted; (2) CPU status bits are set per the table below (any unlisted bits stay undefined and must be initialized in software); (3) the PC is cleared (the address bus is unknown while RS is low; if HOLD is asserted during reset, HOLDA is generated and the address lines go high-impedance until HOLD goes high); (4) a synchronized reset (SRESET) initializes the peripherals (SPRU056D Table 4-16).
CPU register bit status at reset (SPRU056D Table 4-15):
| Register | Bit | State | Result |
|---|---|---|---|
| ST0 | INTM | 1 | all maskable interrupts disabled (RS and NMI stay nonmaskable) |
| ST0 | OV | 0 | overflow bit cleared |
| ST1 | C | 1 | carry bit set |
| ST1 | CNF | 0 | DARAM block B0 mapped into data space |
| ST1 | HM | 1 | processor halts execution during HOLD |
| ST1 | PM | 0 | PREG output not shifted |
| ST1 | SXM | 1 | sign extension on data enabled |
| ST1 | XF | 1 | external flag pin set high |
| PMST | AVIS | 0 | internal program address appears at the address pins |
| PMST | BRAF | 0 | block repeat disabled |
| PMST | IPTR | 0 | reset vector cleared (vectors on page 0) |
| PMST | MP/MC | (pin) | MP/MC pin sampled to select on-chip ROM |
| PMST | NDX | 0 | 'C2x-compatible mode selected |
| PMST | OVLY | 0 | SARAM block not mapped to data space |
| PMST | RAM | 0 | SARAM block not mapped to program space |
| PMST | TRM | 0 | 'C2x-compatible mode selected |
| IFR | all | 0 | no interrupts pending |
| CBCR | CENB1 | 0 | circular buffer 1 disabled |
| CBCR | CENB2 | 0 | circular buffer 2 disabled |
| GREG | all | 0 | all data memory configured as local |
| RPTC | all | 0 | repeat counter cleared |
NMI can be used as a soft reset precisely because it does not abort memory operations or initialize these status bits. The peripheral registers are reset separately (SPRU056D Table 4-16): the wait-state registers (PDWSR, IOWSR, CWSR) come up with waits enabled, the serial-port and timer registers are cleared or reset, and the timer counts down from FFFFh.
8. Power-down mode
In power-down the 'C5x goes dormant and dissipates less power while keeping all internal state, so work resumes unchanged on exit. It is entered by IDLE, by IDLE2, or by driving HOLD low with HM set.
| Mode | Entered by | What stops | How it wakes |
|---|---|---|---|
| IDLE | IDLE instruction | all CPU activity except the system clock; peripherals and CLKOUT1 keep running | any interrupt, including one from an on-chip peripheral |
| IDLE2 | IDLE2 instruction | all CPU activity and the on-chip peripherals; CLKOUT1 is disabled | an external pin -- RS, NMI, INT1, INT2, INT3, or INT4 -- held active at least five machine cycles |
| HOLD | HOLD low with HM = 1 | CPU stops; address, data, and control lines go high-impedance | HOLD becoming inactive |
In both IDLE and IDLE2, if INTM = 0 when the waking interrupt arrives the CPU enters the ISR; if INTM = 1 it simply continues with the instruction after the IDLE/IDLE2. Because IDLE2 stops the peripherals, they cannot wake the CPU and should be reset when IDLE2 ends, especially if externally clocked. With HOLD and HM = 0 the buses go high-impedance but the CPU keeps executing internally, halting only when an off-chip access is needed; HOLD never stops the on-chip serial ports and timers regardless of HM.
License
MIT - (c) 2026 Grzegorz Worona [email protected]. See LICENSE.