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.