# Memory The Hexagon processor features a load/store architecture, where numeric and logical instructions operate on registers. Explicit load instructions move operands from memory to registers, while store instructions move operands from registers to memory. A few instructions (mem-ops) perform numeric and logical operations directly on memory. The address space is unified: all accesses target the same linear address space, which contains both instructions and data. ## Hexagon processor memory model ### Address space The Hexagon processor has a 32-bit byte-addressable memory address space. The user application may access the entire linear address space. The Hexagon processor provides a virtual-to-physical address translation mechanism. ### Byte order The Hexagon processor is a little endian machine: the lowest address byte in memory is held in the least significant byte of a register, as shown in [Hexagon processor byte order](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-fig-hexagon-processor-byte-order). Page-1 Rectangle.224 A A Sheet.2 0 0 Sheet.3 31 31 Sheet.4 Address Contents Address Contents Rectangle.17 B B Rectangle.18 C C Rectangle.19 D D Rectangle.20 E E Rectangle.9 F F Rectangle.10 G G Rectangle.11 H H Sheet.12 0 0 Sheet.13 1 1 Sheet.14 2 2 Sheet.15 3 3 Sheet.16 4 4 Sheet.17 5 5 Sheet.18 6 6 Sheet.19 7 7 Sheet.20 Load byte Load byte Rectangle.21 A A Rectangle.22 - - Rectangle.41 - - Rectangle.42 - - Rectangle.25 A A Rectangle.26 B B Rectangle.27 - - Rectangle.28 - - Rectangle.29 A A Rectangle.30 B B Rectangle.43 C C Rectangle.44 D D Rectangle.45 A A Rectangle.46 B B Rectangle.47 C C Rectangle.48 D D Rectangle.49 E E Rectangle.50 F F Rectangle.51 G G Rectangle.52 H H Sheet.41 Load halfword Load halfword Sheet.42 Load word Load word Sheet.43 Load doubleword Load doubleword Sheet.44 63 63 Sheet.45 Register contents Register contents **Hexagon processor byte order** ### Alignment Even though the Hexagon processor memory is byte-addressable, instructions and data must be aligned in memory on specific address boundaries: - Instructions and instruction packets must be 32-bit aligned - Data must be aligned to its native access size. Any unaligned memory access causes a memory-alignment exception. Use the [permute](https://docs.qualcomm.com/doc/80-N2040-60/topic/data-processing.html#v79-prm-permute) instructions in applications that must reference unaligned vector data. The loads and stores still must be memory-aligned; however, the permute instructions enable rearrangement of the data in registers. Memory alignment restrictions | **Data type** | **Size (bits)** | **Exception when** | | --- | --- | --- | | Byte Unsigned byte | 8 | Never | | Halfword Unsigned halfword | 16 | LSB[0] != 0 [\[1\]](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#mem1) | | Word Unsigned word | 32 | LSB[1:0] != 00 | | Doubleword | 64 | LSB[2:0] != 000 | | Instruction Instruction packet | 32 | LSB[1:0] != 00 | [[1](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#id1)] LSB = Least significant bits of address ## Memory loads The Hexagon processor loads data from memory in byte, halfword, word, or doubleword sizes. The data types supported are signed or unsigned. The syntax used is memXX, where XX denotes the data type. The memory load instructions belong to the LD instruction class and can only execute in slots 0 or 1. Load instructions | **Syntax** | **Source size (bits)** | **Destination size (bits)** | **Data placement** | **Comment** | | --- | --- | --- | --- | --- | | Rd = memub(Rs) | 8 | 32 | Low 8 bits | Zero-extend 8 to 32 bits | | Rd = memb(Rs) | 8 | 32 | Low 8 bits | Sign-extend 8 to 32 bits | | Rd = memuh(Rs) | 16 | 32 | Low 16 bits | Zero-extend 16 to 32 bits | | Rd = memh(Rs) | 16 | 32 | Low 16 bits | Sign-extend 16 to 32 bits | | Rd = memubh(Rs) | 16 | 32 | Bytes 0 and 2 | Bytes 1 and 3 zeroed [\[2\]](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#mem2) | | Rd = membh(Rs) | 16 | 32 | Bytes 0 and 2 | Bytes 1 and 3 sign-extended | | Rd = memw(Rs) | 32 | 32 | All 32 bits | Load word | | Rdd = memubh(Rs) | 32 | 64 | Bytes 0,2,4,6 | Bytes 1, 3, 5, 7 zeroed | | Rdd = membh(Rs) | 32 | 64 | Bytes 0,2,4,6 | Bytes 1, 3, 5, 7 sign-extended | | Rdd = memd(Rs) | 64 | 64 | All 64 bits | Load doubleword | | Ryy = memh\_fifo(Rs) | 16 | 64 | High 16 bits | Shift vector and load halfword | | deallocframe | 64 | 64 | All 64 bits | See [Software stack](https://docs.qualcomm.com/doc/80-N2040-60/topic/software-stack.html) | | dealloc\_return | 64 | 64 | All 64 bits | See [Software stack](https://docs.qualcomm.com/doc/80-N2040-60/topic/software-stack.html) | [[2](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#id2)] The memubh and membh instructions load contiguous bytes from memory (either 2 or 4 bytes) and unpack these bytes into a vector of halfwords. The instructions are useful when bytes are used as input into halfword vector operations, which is common in video and image processing.. ## Memory stores The Hexagon processor stores data to memory in byte, halfword, word, or doubleword sizes. The syntax used is memX, where X denotes the data type. The memory store instructions belong to the ST instruction class and can only execute in slot 0 or, when part of a dual store, slot 1. Store instructions | **Syntax** | **Source size (bits)** | **Destination size (bits)** | **Comment** | | --- | --- | --- | --- | | memb(Rs) = Rt | 32 | 8 | Store byte (bits 7:0) | | memb(Rs) = #s8 | 8 | 8 | Store byte | | memh(Rs) = Rt | 32 | 16 | Store lower half (bits 15:0) | | memh(Rs) = Rt.H | 32 | 16 | Store upper half (bits 31:16) | | memh(Rs) = #s8 | 8 | 16 | Sign-extend 8 bits to 16 bits | | memw(Rs) = Rt | 32 | 32 | Store word | | memw(Rs) = #s8 | 8 | 32 | Sign-extend 8 bits to 32 bits | | memd(Rs) = Rtt | 64 | 64 | Store doubleword | | allocframe(#u11) | 64 | 64 | See [Software stack](https://docs.qualcomm.com/doc/80-N2040-60/topic/software-stack.html) | ## Dual stores Two memory store instructions can appear in the same instruction packet. The store instructions in a dual store must belong to the [ST](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#st) instruction class and can only execute in slots 0 and 1. The resulting operation is considered a dual store. For example: { memw(R5) = R2 // Dual store memh(R6) = R3 } Copy to clipboard Unlike most packetized operations, dual stores do not execute in parallel ([packet execution semantics](https://docs.qualcomm.com/doc/80-N2040-60/topic/instructions.html#v79-prm-packet-execution-semantics)). Instead, the store instruction in slot 1 effectively executes first, followed by the store instruction in slot 0. ## slot 1 store with slot 0 load A slot 1 store operation with a slot 0 load operation can appear in a packet. The packet attribute `:mem_noshuf` inhibits the instruction reordering that is otherwise done by the assembler. For example: { memw(R5) = R2 // slot 1 store R3 = memh(R6) // slot 0 load }:mem_noshuf Copy to clipboard Unlike most packetized operations, these memory operations do not execute in parallel ([packet execution semantics](https://docs.qualcomm.com/doc/80-N2040-60/topic/instructions.html#v79-prm-packet-execution-semantics)). Instead, the store instruction in slot 1 effectively executes first, followed by the load instruction in slot 0. If the addresses of the two operations are overlapping, the load receives the newly stored data. ## New-value stores A memory store instruction can store a register that is assigned a new value in the same [instruction packet](https://docs.qualcomm.com/doc/80-N2040-60/topic/instructions.html#v79-prm-instruction-packets)). The new-value store instructions belong to the NV instruction class and can only execute in slot 0. Append the suffix `.new` to the source register to express this feature. For example: { R2 = memh(R4+#8) // load halfword memw(R5) = R2.new // store newly-loaded value } Copy to clipboard New-value store instructions have the following restrictions: - If an instruction uses auto-increment or absolute-set addressing mode ([addressing modes](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-addressing-modes)), its address register cannot be used as the new-value register. - If an instruction produces a 64-bit result, its result registers cannot be used as the new-value register. - An instruction that conditionally sets the target register of a .new store is not valid. ([consuming scalar predicates](https://docs.qualcomm.com/doc/80-N2040-60/topic/conditional-execution.html#v79-prm-consuming-scalar-predicates)) { if (P1) R0 = sub(R2,R3) // R0 is conditionally set memw(R5) = R0.new // Not a valid store } Copy to clipboard ## Mem-ops Mem-ops perform basic arithmetic, logical, and bit operations directly on memory operands, without the need for a separate load or store. The mem-op instructions belong to the MEMOP instruction class and can only execute in slot 0. Mem-ops use byte, halfword, or word sizes. Mem-ops | **Syntax** | **Operation** | | --- | --- | | memXX(Rs+#u6) [+-|&] = Rt | Arithmetic/logical on memory | | memXX(Rs+#u6) [+-] = #u5 | Arithmetic on memory | | memXX(Rs+#u6) = clrbit(#u5) | Clear bit in memory | | memXX(Rs+#u6) = setbit(#u5) | Set bit in memory | ## Addressing modes Addressing modes | **Mode** | **Syntax** | **Operation** [\[3\]](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#mem3) | | --- | --- | --- | | [Absolute](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-absolute) | memXX(##address) | EA = address | | [Absolute-set](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-absolute-set) | memXX(Re=##address) | EA = address Re = address | | [Absolute with register offset](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-absolute-with-register-offset) | memXX(Ru<<#u2+##U32) | EA = imm + (Ru << #u2) | | [Global pointer relative](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-global-pointer-relative) | memXX(GP+#immediate) memXX(#immediate) | EA = GP + immediate | | [Indirect](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-indirect) | memXX(Rs) | EA = Rs | | [Indirect with offset](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-indirect-with-offset) | memXX(Rs+#s11) | EA = Rs + imm | | [Indirect with register offset](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-indirect-with-register-offset) | memXX(Rs+Ru<<#u2) | EA = Rs + (Ru << #u2) | | [Indirect with auto-increment immediate](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-indirect-with-auto-increment-immediate) | memXX(Rx++#s4) | EA = Rx;


Rx += (imm) | | [Indirect with auto-increment register](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-indirect-with-auto-increment-register) | memXX(Rx++Mu) | EA = Rx;


Rx += Mu | | [Circular with auto-increment immediate](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-circular-with-auto-increment-immediate) | memXX(Rx++#s4:circ(Mu)) | EA = Rx;


Rx = circ\_add(Rx,imm,Mu) | | [Circular with auto-increment register](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-circular-with-auto-increment-register) | memXX(Rx++I:circ(Mu)) | EA = Rx;


Rx = circ\_add(Rx,I,Mu) | | [Bit-reversed with auto-increment register](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-bit-reversed-with-auto-increment-register) | memXX(Rx++Mu:brev) | EA = Rx.H + bit\_reverse(Rx.L) Rx += Mu | [[3](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#id3)] EA (effective address) is equivalent to VA (virtual address). ### Absolute The absolute addressing mode uses a 32-bit constant value as the effective memory address. For example: R2 = memw(##100000) // Load R2 with word from addr 100000 memw(##200000) = R4 // Store R4 to word at addr 200000 Copy to clipboard ### Absolute-set The absolute-set addressing mode assigns a 32-bit constant value to the specified general register, then uses the assigned value as the effective memory address. For example: R2 = memw(R1=##400000) // Load R2 with word from addr 400000 // and load R1 with value 400000 memw(R3=##600000) = R4 // Store R4 to word at addr 600000 // and load R3 with value 600000 Copy to clipboard ### Absolute with register offset The absolute with register offset addressing mode performs an arithmetic left shift of a 32-bit general register value by the amount specified in a 2-bit unsigned immediate value, and then adds the shifted result to an unsigned 32-bit constant value to create the 32-bit effective memory address. For example: R2 = memh(R3 << #3 + ##100000) // load R2 with signed halfword // from addr [100000 + (R3 << 3)] Copy to clipboard The 32-bit constant value is the base address, and the shifted result is the byte offset. ### Global pointer relative The global pointer relative addressing mode adds an unsigned offset value to the global data pointer GP to create the 32-bit effective memory address. Global pointer relative addresses can be expressed two ways in assembly language: - By explicitly adding an unsigned offset value to register GP - By specifying only an immediate value as the instruction operand For example: > > > R2 = memh(GP+#100) // Load R2 with signed halfword > // from [GP + 100 bytes] > Copy to clipboard Specifying only an immediate value causes the assembler and linker to automatically subtract the value of the special symbol \_SDA\_BASE\_ from the immediate value, and use the result as the effective offset from GP. For example: > > > R3 = memh(#2000) // Load R3 with signed halfword > // from [GP + #2000 - \_SDA_BASE] > Copy to clipboard The register field GP.GDP holds the global data pointer ([register GP](https://docs.qualcomm.com/doc/80-N2040-60/topic/registers.html#v79-prm-global-pointer)). GP.GDP contains an unsigned 26-bit value that specifies the most significant 26 bits of the 32-bit global data pointer. The least significant 6 bits of the pointer are always defined as zero. When expressed in assembly language, the immediate values used in global pointer relative addressing always specify byte offsets from the global data pointer. The byte offsets are integral multiples of the size of the instruction data type. The memory area can be up to 512 kB in length and must be aligned to a 64-byte boundary in memory. Offset ranges (global pointer relative) | **Data type** | **Offset range** | **Offsets are multiple of** | | --- | --- | --- | | doubleword | 0 … 524280 | 8 | | word | 0 … 262140 | 4 | | halfword | 0 … 131070 | 2 | | byte | 0 … 65535 | 1 | ### Indirect The indirect addressing mode uses a 32-bit value in a general register as the effective memory address. For example: R2 = memub(R1) // load R2 with unsigned byte from addr in R1 Copy to clipboard ### Indirect with offset The indirect with offset addressing mode adds an offset value to a general register value to create the 32-bit effective memory address. For example: R2 = memh(R3 + #100) // load R2 with signed halfword // from [R3 + 100 bytes] Copy to clipboard When expressed in assembly language, the immediate values always specify byte offsets from the general register value. The byte offsets are integral multiples of the size of the instruction data type. Offset ranges indirect with offset | **Data type** | **Signed offset range (non-conditional instructions)** | **Unsigned offset range (conditional instructions)** | **Offsets are multiple of** | | --- | --- | --- | --- | | doubleword | -8192 … 8184 | 0 … 504 | 8 | | word | -4096 … 4092 | 0 … 252 | 4 | | halfword | -2048 … 2046 | 0 … 126 | 2 | | byte | -1024 … 1023 | 0 … 63 | 1 | ### Indirect with register offset The indirect with register offset addressing mode adds a 32-bit general register value to the result created by performing an arithmetic left shift of a second 32-bit general register value by a 2-bit unsigned immediate value, forming the32-bit effective memory address. For example: R2 = memh(R3+R4<<#1) // load R2 with signed halfword // from [R3 + (R4 << 1)] Copy to clipboard The register values always specify byte addresses. ### Indirect with auto-increment immediate The indirect with auto-increment immediate addressing mode uses a 32-bit value stored in a general register to specify the effective memory address. However, after the address is accessed, a signed value (known as the increment) is added to the register so it specifies a different memory address (which is accessed in a subsequent instruction). For example: R2 = memw(R3++#4) // R3 contains the effective address // R3 is then incremented by 4 Copy to clipboard When expressed in assembly language, the immediate values always specify byte increments. The byte increments are integral multiples of the size of the instruction data type. Increment ranges (indirect with auto-increment immediate) | **Data type** | **Increment range** | **Increments are multiple of** | | --- | --- | --- | | doubleword | -64 … 56 | 8 | | word | -32 … 28 | 4 | | halfword | -16 … 14 | 2 | | byte | -8 … 7 | 1 | ### Indirect with auto-increment register The indirect with auto-increment register addressing mode is functionally equivalent to indirect with auto-increment immediate, but uses a modifier register Mx ([modifier registers](https://docs.qualcomm.com/doc/80-N2040-60/topic/registers.html#v79-prm-modifier-registers)) instead of an immediate value to hold the increment. For example: R2 = memw(R0++M1) // The effective addr is the value of R0. // Next, M1 is added to R0 and the result // is stored in R0. Copy to clipboard When auto-incrementing with a modifier register, the increment is a signed 32-bit value that is added to the general register. This offers two advantages over auto-increment immediate: - A larger increment range - Variable increments (because the modifier register can be programmed at runtime). The modifier register value always specifies a byte increment. The signed 32-bit increment range is identical for all instruction data types (doubleword, word, halfword, byte). ### Circular with auto-increment immediate The circular with auto-increment immediate addressing mode is a variant of indirect with auto- increment addressing. It accesses data buffers in a modulo wrap around fashion. The address modifier `:circ(Mx)` expresses circular addressing in assembly language. `Mx` specifies a modifier register that is programmed to specify the circular buffer length ([modifier registers](https://docs.qualcomm.com/doc/80-N2040-60/topic/registers.html#v79-prm-modifier-registers)). For example: Rd = memb(R2++#4:circ(Mx)) // Load from circ buffer memw(Rs++#8:circ(Mx)) = Rt // Store to circ buffer Copy to clipboard Program the following elements to use circular addressing: - The length field of the `Mx` register is set to the length (in bytes) of the circular buffer to access. A circular buffer can be from 4 to (2^17^-1) bytes long. - Bits 27:24 of the `Mx` register are always set to 0. - Set CSx to the start address of the circular buffer. Use CS0 and M0 together or CS1 and M1 together. The general register Rs specifies the effective address for the memory access. The following equation sets the new Rs value after the memory access: Rs = CSx + ((Rs + increment) % Mx.length) When expressed in assembly language, the immediate values always specify byte increments. The byte increments are integral multiples of the size of the instruction data type. Increment ranges (circular with auto-increment immediate addressing | **Data type** | **Increment range** | **Increments are multiple of** | | --- | --- | --- | | doubleword | -64 … 56 | 8 | | word | -32 … 28 | 4 | | halfword | -16 … 14 | 2 | | byte | -8 … 7 | 1 | Following this set of rules prevents undefined behavior: - The start address must be aligned to the native access size of the buffer elements. - The absolute value of the increment must be less than the buffer length. - The memory access size (1 for byte, 2 for halfword, 4 for word, 8 for doubleword) must be less than (Length-1). - Buffers must not wrap around in the 32-bit address space. The following example sets up and accesses a 150-byte circular buffer: R4.H = #0 // M0[27:24]= 0x0 R4.L = #150 // length = 150 M0 = R4 R2 = ##cbuf // start addr = cbuf CS0 = R2 R0 = memb(R2++#4:circ(M0)) // Load byte from circ buf // specified by M0/CS0 // inc R2 by 4 after load // wrap R2 around if >= 150 Copy to clipboard ### Circular with auto-increment register The circular with auto-increment register addressing mode is functionally equivalent to circular with auto-increment immediate, but uses a register instead of an immediate value to hold the increment. Register increments are specified in circular addressing instructions by using the symbol I as the increment (instead of an immediate value). For example: Rd = memw(Rs++I:circ(Mx)) // Load byte with incr of I*4 from // circ buf specified by Mx/CSx Copy to clipboard When auto-incrementing with a register, the increment is a signed 11-bit value that is added to the general register. The field Mx.I ([modifier registers](https://docs.qualcomm.com/doc/80-N2040-60/topic/registers.html#v79-prm-modifier-registers)) holds the signed 11-bit increment value. Compared to circular addressing with immediate increments, auto-incrementing with a register allows for larger increment ranges and variable increments. When expressed in assembly language, the Mx.I value always specifies byte increments. The byte increments are integral multiples of the size of the instruction data type. Increment ranges (circular with auto-increment register addressing) | **Data type** | **Increment range** | **Increments are multiple of** | | --- | --- | --- | | doubleword | -8192 … 8184 | 8 | | word | -4096 … 4092 | 4 | | halfword | -2048 … 2046 | 2 | | byte | -1024 … 1023 | 1 | ### Bit-reversed with auto-increment register The bit-reversed with auto-increment register addressing mode is a variant of indirect with auto- increment addressing - it accesses data buffers using an address value that is the bit-wise reversal of the value stored in the general register. The following defines bit-wise reversal of a 32-bit address value: - The lower 16 bits are transformed by exchanging bit 0 with bit 15, bit 1 with bit 14, and so on. - The upper 16 bits remain unchanged. The address modifier `brev` expresses bit-reversed addressing in assembly language. For example: Rd = memub(Rs++Mx:brev) // The address is (Rs.H \| bitrev(Rs.L)) // The original Rs (not reversed) is added // to Mx and written back to Rs Copy to clipboard The initial values for the address and increment must be set in bit-reversed form with the hardware bit-reversing the bit-reversed address value to form the effective address. The buffer length for a bit-reversed buffer must be an integral power of 2 with a maximum length of 64K bytes. To support bit-reversed addressing, buffers must be properly aligned in memory. A bit-reversed buffer is properly aligned when its starting byte address is aligned to a power of 2 greater than or equal to the buffer size (in bytes). For example: int bitrev_buf[256] attribute ((aligned(1024))); Copy to clipboard The bit-reversed buffer declared above is aligned to 1024 bytes because the buffer size is 1024 bytes (256 integer words x 4 bytes), and 1024 is an integral power of 2. The buffer location pointer for a bit-reversed buffer must be initialized so thatthe least-significant 16 bits of the address value are bit-reversed. The increment value must be initialized to the following value: bitreverse(buffer_size_in_bytes / 2) Copy to clipboard where bitreverse is defined as bit-reversing the least-significant 16 bits while leaving the remaining bits unchanged. Note To simplify the initialization of the bit-reversed pointer, align bit-reversed buffers to a 64K byte boundary. This initializes the bit-reversed pointer to the base address of the buffer, with no bit-reversing required for the least-significant 16 bits of the pointer value which the 64K alignment sets to 0). After a bit-reversed memory access completes, the general register increments by the increment value. The bit-reversal that is performed as part of the memory access never affects the value in the general register. Note The Hexagon processor only supports register increments for bit-reversed addressing. It does not support immediate increments. ## Conditional load/stores The compiler generates conditional loads and stores to increase instruction-level parallelism. Some load and store instructions can be executed conditionally based on predicate values that were set in a previous instruction. The instruction prefix `if (pred_expr)` expresses conditional loads and stores in assembly language, where `pred_expr` specifies a predicate register expression ([scalar predicates](https://docs.qualcomm.com/doc/80-N2040-60/topic/conditional-execution.html#v79-prm-scalar-predicates)). For example: if (P0) R0 = memw(R2) // Conditional load if (!P2) memh(R3 + #100) = R1 // Conditional store if (P1.new) R3 = memw(R3++#4) // Conditional load Copy to clipboard Condtional loads and stores do not support all addressing modes. [Addressing modes (conditional load/store)](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-tbl-addressing-modes-conditional-load-store) shows the supported modes. Condtional load/store addressing modes | **Addressing mode** | **Conditional** | | --- | --- | | Absolute | Yes | | Absolute-set | No | | Absolute with register offset | No | | Global pointer relative | No | | Indirect | Yes | | Indirect with offset | Yes | | Indirect with register offset | Yes | | Indirect with auto-increment immediate | Yes | | Indirect with auto-increment register | No | | Circular with auto-increment immediate | No | | Circular with auto-increment register | No | | Bit-reversed with auto-increment register | No | When a conditional load or store instruction uses [Indirect with offset](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#v79-prm-indirect-with-offset) addressing mode, the offset range is smaller than the range normally defined for indirect-with-offset addressing. Conditional and non-conditional offset ranges (indirect with offset addressing) | **Data type** | **Offset range (conditional)** | **Offset range (non-conditional)** | **Offsets are multiple of** | | --- | --- | --- | --- | | doubleword | 0 … 504 | -8192 … 8184 | 8 | | word | 0 … 252 | -4096 … 4092 | 4 | | halfword | 0 … 126 | -2048 … 2046 | 2 | | byte | 0 … 63 | -1024 … 1023 | 1 | Note For more information on conditional execution, see [Conditional execution](https://docs.qualcomm.com/doc/80-N2040-60/topic/conditional-execution.html). ## Cache memory The Hexagon processor has a cache-based memory architecture: - A level 1 instruction cache holds recently fetched instructions. - A level 1 data cache holds recently accessed data memory. Loads/stores that access memory through the level 1 caches are cached accesses. Loads/stores that bypass the level 1 caches are uncached accesses. The memory management unit (MMU) of the Hexagon processor can configure specific memory pages as cached or uncached. Hexagon supports two types of caching: - Write-through caching keep the cache data consistent with external memory. - Writeback caching stores data in the cache without writing to external memory immediately. Cached data that is inconsistent with external memory is referred to as dirty. ### Uncached memory In some cases, load/store operations bypass the cache memories and are serviced externally. For example, accessing memory-mapped I/O, registers, and peripheral devices, or other system defined entities. Hexagon catgorizes uncached memory as two distinct types: - Device-type is for accessing addresses that have side-effects such as a memory-mapped FIFO peripheral. The hardware ensures that interrupts do not cancel a pending device access. The hardware does not reorder device accesses. Mark peripheral control registers as device-type. - Uncached-type is for memory-like addresses where no side effects are associated with an access. The hardware can load from uncached memory multiple times and can reorder uncached accesses. Device-type memory is functionally identical to uncached-type memory for instruction fetches. ### Tightly coupled memory The Hexagon processor supports tightly coupled instruction memory at level 1, which is defined as memory with similar access properties to the instruction cache. Tightly coupled memory also implements a level 2 cache, which is defined as backing store to the level 1 caches. ### Cache maintenance operations The Hexagon processor includes dedicated cache maintenance instructions that invalidate cache data or push dirty data out to external memory. The cache maintenance instructions operate on specific memory addresses. If the instruction causes an address error (due to a privilege violation), the processor raises an exception. The exception to this rule is the `dcfetch` operation, which never causes a processor exception. Cache instructions (user-level) | **Syntax** | **Permitted in packet** | **Operation** | | --- | --- | --- | | icinva(Rs) | Solo [\[4\]](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#mem4) | Instruction cache invalidate.


Look up instruction cache at address Rs. If the address is
in the cache, invalidate it. | | dccleaninva(Rs) | slot 1 empty or ALU32 only | Data cache clean and invalidate.


Look up data cache at address Rs.


If the address is in the cache and has dirty data, flush
that data out to memory. The cache line is then invalidated,
whether or not dirty data was written. | | dccleana(Rs) | slot 1 empty or ALU32 only | Data cache clean.


Look up data cache at address Rs.


If the address is in the cache and has dirty data, flush
that data out to memory. | | dcinva(Rs) | slot 1 empty or ALU32 only | Maps to dccleaninva(Rs). | | dcfetch(Rs) | Normal [\[5\]](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#mem5) | Data cache prefetch.


Prefetch data at address Rs into the data cache.


Note


This instruction does not cause an exception. | | l2fetch(Rs,Rt) | ALU32 or


XTYPE only | L2 cache prefetch.


Prefetch data from memory specified by Rs and Rt into L2
cache. | [[4](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#id4)] *Solo* means that the instruction must not be grouped with other instructions in a packet. [[5](https://docs.qualcomm.com/doc/80-N2040-60/topic/memory.html#id5)] *Normal* means that the normal instruction-grouping constraints apply. The data cache coherency operations (including clean, invalidate, and clean and invalidate) affect both the L1 and L2 caches, and ensure that the memory hierarchy remains coherent. However, the instruction cache invalidate operation affects only the L1 cache. Therefore, invalidating instructions that might be in the L1 or L2 caches requires a two-step procedure: 1. Use icinva to invalidate instructions from the L1 cache. 2. Use dcinva separately to invalidate instructions from the L2 cache. ### Cache line zero The Hexagon processor includes the instruction `dczeroa`. This instruction allocates a line in the L1 data cache and clears it by setting the line to zero. The `dczeroa` operation has the same exception behavior as write-back stores. A packet with `dczeroa` must have slot 1 either empty or containing an ALU32 instruction. The behavior is as follows: - The Rs register value must be 32-byte aligned. If it is unaligned, the processor raises an unaligned error exception. - For a cache hit, the data cache clears the specified cache line and marks it dirty. - For a cache miss, the data cache does not fetch the specified cache line from external memory. Instead, the data cache allocates the line, clears the line, and marks it dirty. ### Cache prefetch types supported by the Hexagon processor #### Hardware-based instruction cache prefetching The USR.HFI ([User status register](https://docs.qualcomm.com/doc/80-N2040-60/topic/registers.html#v79-prm-user-status-register)) enables instruction cache prefetching on a per-hardware thread basis. #### Software-based data cache prefetching The Hexagon processor includes the `dcfetch` instruction, which queries the L1 data cache based on the address specified in the instruction: - If the address is present in the cache, no action is taken. - If the cache line for the address is missing, the processor attempts to fill the cache line from the next level of memory. The thread does not stall, but rather continues executing while the cache line fill occurs in the background. - If the address is invalid, no exception is generated and the `dcfetch` instruction is treated as a NOP. #### Software-based l2fetch The `l2fetch` instruction initiates background prefetch of code or data into the L2 cache. There are two forms of this instruction. In the first form, Rs,Rt specify the start address and dimensions of the area to prefetch as follows: - Rs specifies the 32-bit virtual start address. - Rt[15:8] = Width of a fetch block in bytes. - Rt[7:0] = Height: the number of width-sized blocks to fetch. - Rt[31:16] = Stride: an unsigned byte offset added to the pointer after fetching each width-sized block. In the second form, Rs,Rtt specify the start address and dimensions of the area to prefetch as follows: - Rs specifies the 32-bit virtual start address. - Rtt[31:16] = Width of a fetch block in bytes. - Rtt[15:0] = Height: the number of width-sized blocks to fetch. - Rtt[47:32] = Stride: an unsigned byte offset added to the pointer after fetching each width-sized block. - Rtt[48] = Direction. If 0, prefetches in row-major order. All cache lines in a row are fetched before proceeding to the next row. If 1, prefetches in column-major order. All cache lines in a column are fetched before proceeding to the next column. The following figure shows two examples of using the `l2fetch` instruction: Page-1 Rectangle.4 Rectangle Start address Start address Rectangle.2 Stride Stride Sheet.9 Rs Rs Sheet.10 Stride Stride Sheet.140 L2FETCH for box prefetch L2FETCH for box prefetch Rectangle.3 Prefetch area Prefetch area Dynamic connector.5 Dynamic connector.7 Dynamic connector.11 Sheet.6 Width Width Sheet.12 Height Height Directed line 1 Rectangle.14 Width Width Rectangle.15 Height Height Sheet.13 Rt Rt Rectangle.16 Rectangle.17 Start address Start address Rectangle.18 128 128 Sheet.19 Rs Rs Sheet.21 L2FETCH for large linear prefetch L2FETCH for large linear prefetch Directed line 1.28 Rectangle.29 128 128 Rectangle.30 Lines Lines Sheet.31 Rt Rt Sheet.23 0 0 Sheet.25 7 7 Sheet.27 8 8 Sheet.36 15 15 Sheet.37 16 16 Sheet.38 31 31 Sheet.39 0 0 Sheet.40 7 7 Sheet.41 8 8 Sheet.42 15 15 Sheet.43 16 16 Sheet.44 31 31 Sheet.22 128 × Lines 128 × Lines Rectangle.32 Rectangle.33 Rectangle.34 Rectangle.45 Rectangle.46 Dynamic connector.20 **Figure 5-2 L2fetch instruction** In the box prefetch, a 2-D range of memory is defined within a larger frame. The second example shows prefetch for a large linear area of memory which has size Lines \* 128. The `l2fetch` instruction is non-blocking. After the instruction is initiated, the program continues to the next instruction while prefetching occurs in the background. If and only if the lines are missing from the L2 cache, the hardware attempts to fetch them from the system memory. The hardware prefetch engine requests all lines in the programmed memory range and prefetches at a lower priority than demand fetches. This prevents prefetch from adding traffic while the system is under heavy load. If a program initiates an `l2fetch` while an older `l2fetch` request is pending, the new request is queued, up to 3 deep. If 3 `l2fetch` requests are pending, the next `l2fetch` stalls the hardware thread until the oldest request is complete. During the time an `l2fetch` is active for a thread, USR.PFA==1 indicates that prefetches are in progress. Executing an `l2fetch` with any subfield programmed as zero cancels all pending prefetches by the calling thread. The implementation may drop prefetches. #### Hardware-based data cache prefetching L1 data cache prefetching is enabled or disabled on a per-thread basis by setting USR.HFD [User status register](https://docs.qualcomm.com/doc/80-N2040-60/topic/registers.html#v79-prm-user-status-register). When data cache prefetching is enabled, the Hexagon processor observes patterns of data cache misses and attempts to predict future misses based on any recurring patterns of misses where the addresses are separated by a constant stride. If such patterns are found, the processor attempts to automatically prefetch future cache lines. Data cache prefetching is user-enabled at four levels: - HFD = 00: No prefetching - HFD = 01: Prefetch up to four lines for misses originating from a load, with a post-update addressing mode that occurs within a hardware loop - HFD = 10: Prefetch up to four lines for misses originating from loads that occur within a hardware loop - HFD = 11: Prefetch up to eight lines for misses originating from loads ## Memory ordering Some devices might require synchronization of loads and stores. In this case, a set of processor instructions enable programmer control of the synchronization and ordering of memory accesses. These are all solo user instructions and must be the only one in the packet; otherwise, the behavior is undefined. Memory ordering instructions | **Syntax** | **Operation** | | --- | --- | | isync | Instruction synchronize.


Ensures that the modifications of TLB, SSR, and SYSCFG are visible to other threads.


It must be issued as follows:
| | syncht | Global synchronization operation.


This instruction ensures that all currently outstanding memory transactions from
all threads have completed and are globally observable before continuing past this instruction. | | barrier | Set memory barrier.


Consistency domain synchronization operation.


This instruction ensures that currently outstanding scalar loads,
scalar stores, and cache operations from all threads within a consistency
domain have completed and are globally observable before continuing past this instruction. | The Hexagon processor treats data memory accesses and program memory accesses separately and holds them in separate caches. Software must ensure coherency between data and program code when necessary. For example, with generated or self-modified code, the modified code in the data cache can be inconsistent with the program cache. The software must explicitly force modified data cache lines to memory either by using a write-through policy, or through explicit cache clean instructions. Use a barrier instruction to ensure completion of the stores. Finally, invalidate relevant instruction cache contents in order to refetch the new instructions. ## Atomic operations The Hexagon processor includes a load locked / store conditional (LL/SC) mechanism to provide the atomic read-modify-write operation that is necessary to implement synchronization primitives such as semaphores and mutexes. These primitives synchronize the execution of different software programs running concurrently on the Hexagon processor. They can also provide atomic memory support between the Hexagon processor and external blocks. Atomic instructions | **Syntax** | **Description** | | --- | --- | | Rd = memw\_locked(Rs) | Load locked word.


Reserve lock on word at address Rs. | | memw\_locked(Rs,Pd) = Rt | Store conditional word.


If no other atomic operation has been performed at the
address (that is, atomicity is ensured), perform the store
to the word at address Rs and return TRUE in Pd; otherwise
return FALSE.


TRUE indicates that the LL and SC operations were performed
atomically. | | Rdd = memd\_locked(Rs) | Load locked doubleword.


Reserve lock on doubleword at address Rs. | | memd\_locked(Rs,Pd) = Rtt | Store conditional doubleword.


If no other atomic operation has been performed at the
address (that is, atomicity is ensured), perform the store
to the doubleword at address Rs and return TRUE in Pd;
otherwise return FALSE.


TRUE indicates that the LL and SC operations have been
performed atomically. | The recommended instruction sequence to acquire a spinlock is: // Assume address is held in R0 // Assume R1,R3,P0,P1 are scratch spinlock_lock: R3 = #1 lock_test_spin: R1 = memw_locked(R0) // Do normal test to wait P1 = cmp.eq(R1,#0) // for lock to be available if (!P1) jump lock_test_spin memw_locked(R0,P0) = r3 // Do store conditional (SC) if (!P0) jump lock_test_spin // was LL and SC done atomically? Copy to clipboard The recommended instruction sequence to release a spinlock is: // Assume address is held in R0 // Assume R1 is scratch spinlock unlock: R1 = #0 memw(R0) = R1 Copy to clipboard Use cacheable memory to synchronize software threads within Hexagon processor. To perform LL/SC operations with external memory the page attribute must be set to uncached. Otherwise the behavior is undefined. If software performs a load locked operation to an address that does not support atomic operations the behavior is undefined. Align the address used by the LL/SC operations to double-word addresses (8 bytes) for best performance Last Published: Jan 16, 2025 [Previous Topic Data processing](https://docs.qualcomm.com/bundle/publicresource/80-N2040-60/topics/data-processing.md) [Next Topic Conditional execution](https://docs.qualcomm.com/bundle/publicresource/80-N2040-60/topics/conditional-execution.md)