2.4.3 Annotated resources

This section describes register, memory, and parameter annotated resources.

Syntax of annotated resources

Use the REGISTER, MEMORY, and PARAMETER keywords to specify annotated resources in the resources section.

The resource annotation has the following forms for defining single instances or arrays:

<resource_class>  [ <parameters> ]  identifier  “;”
<resource_class>  [ <parameters> ]  identifier  “[“ <size> “]”  “;”

Annotated resources - registers

This section describes LISA+ component registers.

Annotated resources - about registers

Registers are resources that store data.

You can specify a bit width and type for the register. The default data type is unsigned int and the default bit width is 32.

Register resource definition
Register set R consists of 32 registers that are 32 bits wide and a register file using the default types.
resources
{
    REGISTER { bitwidth(32) } R[32];
    int a, b; // not visible in a debugger
    REGISTER { is_program_counter(true) } pc;
    REGISTER gpr[32];
    REGISTER { bitwidth(64), type(int) } accu;
    REGISTER { type(float) } fpr[16];
}
Annotated resources - register parameters

Parameter names, types, default values, and descriptions of LISA+ component registers.

Table 2-1 Optional parameters for registers

Name Type Default Description
address Integer none

Address maps to a unique register ID.

attribute Access type read_write read_write, read_only, or write_only.
bitwidth Integer 32

Data type bit-width:

Integer
8, 16, 32, 64.
Floating point
32, 64.
Boolean
1.
String
-.
description String "" Description of the resource.
display_float_format String "%g" printf() format string for debugger display for floating point registers, only for type(float) and display_format(float).
display_format String "hex" Default display format for debuggers, supported formats are: hex, uint, int, bool, float, and string. Debuggers can always override this setting.
display_symbols String-list - Comma-separated list of strings replacing the numerical display. Not all tools implement this feature.
dwarf_id Integer - Dwarf register ID. If not set, the register does not have a DWARF register ID. Typically, the architecture ABI defines these.
groups String-list - Comma-separated list of register groups that the register is assigned to.
has_side_effects Boolean false Set to true if register access has side effects.
is_program_counter Boolean false Set to true if the resource is the program counter.
lsb_offset(Bit) - - Bit offset in the parent register.
name String resource name

Register name to display in, for example, a debugger. By default:

Non-array resources
“R” for register R, for example.
Array resources
Resource name then the decimal index. For example, R[0], R[1], … R[7] for register array R[8]. The name string can contain one printf() integer specifier. For example, REGISTER { name("R%u") } a[4]; results in registers R0, R1, R2, and R3 in the debugger.
name_index_base Integer 0 For register arrays with format specifiers in the name, for example name(“R%u”), this parameter specifies the start index. For example, name(“R%u”) and name_index_base(3) sets the first register array element to R3.
partof(ParentReg) - - Parent register.
pv_port Integer none Internal pv_port used to map read and write access to peripheral registers. The port must be a slave port of type PVDevice. If only one unique port of type PVDevice exists, you can omit this parameter.
read_behavior String none Read behavior that you define in case the automated mechanisms are not sufficient. Use of this behavior depends on the internal state.
read_function String none Name of the debug read access behavior.
read_mask Integer none Value for read accesses.
read_sec_mask Integer none Value for read accesses. For secure accesses, this value overwrites other masks.
reg_number Integer auto CADI register ID. The value of reg_number can be any 32-bit unsigned integer constant except for the reserved value 0xFFFFFFFF.
reg_number_increment Integer 1 reg_number increment between array elements, starting with reg_number. Applies only to register arrays and if reg_number is specified.
reset_value <type> 0, 0.0, "", or false

Reset value for bool, int, uint, float, or string. The value specified by the parameter is assigned to the register at initialization and reset execution. To avoid register initialization on reset, use UNINITIALIZED as the parameter argument.

type <type> uint Data type: bool, int, uint, float, or string.
virtual Boolean false

Optimizes code generation by preventing the allocation of host memory for the resource. If virtual is true, no variable is generated for the register or memory, the resource must have read and write access functions, and must not be referenced in LISA+ code. virtual does not apply to parameters.

visible_in_debugger Boolean true Debug switch. true shows the register in the debugger, false hides it.
write_behavior String none Write behavior that you define in case the automated mechanisms are not sufficient. Use of this behavior depends on the internal state.
write_function String none Name of the debug write access behavior. The access functions must match the arguments and return type.
write_mask Integer none Value for write accesses.
write_sec_mask Integer none Value for write accesses. For secure accesses, this value overwrites other masks.
Annotated resources - IDs in register arrays

If a register array has an ID, the first register receives the value of the ID. The ID of each subsequent register in the array is the ID of the previous register plus one.

    REGISTER { reg_number(5) } R[32];

Register R[0] has ID 5, R[1]has ID 6, R[2] has ID 7, and so forth. Use the reg_number_increment parameter to step between registers in an array, starting with reg_number. For example, if reg_number_increment is set to 2, R[1] is not used.

Annotated resources - virtual registers

The virtual parameter enables the abstract declaration of a resource. If you use the LISA+ virtual parameter, you must implement a read and a write access behavior and not rely on the existence of a variable that has the resource name.

Use of virtual parameter
component foo
{
    resources
    {
        REGISTER { type(uint32_t), virtual(true), read_function(GetStatus),
            write_function(SetStatus) } STATUS;
        REGISTER { type(uint32_t) } ENABLED;
        REGISTER { type(uint32_t) } ACTIVE;
    }

    behavior GetStatus(uint32_t id, uint64_t *data, bool doSideEffects) :
       AccessFuncResult
    {
        *data = ENABLED & ACTIVE;
        return ACCESS_FUNC_OK;
    }
 
   behavior SetStatus(uint32_t id, const uint64_t *data, bool doSideEffects) :
      AccessFuncResult
    {
        return ACCESS_FUNC_OK;
    }
}

Component foo has registers STATUS, ENABLED and ACTIVE:

  • STATUS is visible to the debugger as a value that is the bit-wise AND operation of ACTIVE and ENABLED.
  • There is no reason to access STATUS from LISA+ code, so the design is enforced.
  • There is also no reason to write a value to STATUS, so using SetStatus returns the success flag.
Annotated resources - register access

Access registers in the same way as C variables.

Register assignment
behavior xxx_func
{
    R[4] = a;
    R[b] = gpr[a];
}
Annotated resources - component registers

LISA+ supports registers that are embedded within other registers. That is, they are a component, or child, of a parent register.

The implementation of component registers requires:

  • Component registers must be wholly embedded in their parent register. Each bit of the child register is found in the parent. Each component register therefore has exactly one parent.
  • Component registers must have the same bit sequence as the parent. The bits of the component register are in the same order as the corresponding bits of the parent register. You can shift the bit sequence, but cannot split or manipulate it in any other way.

The parameters that specify the component register are:

partof(Parent)
Parent is the name of the parent register. This parameter is represented in LISA+ by the partof resource attribute. The value specified in partof is the name of the parent register.
lsb_offset(Bit)
Bit is the Least Significant Bit (LSB) offset of the child in the parent register. This parameter is represented by the lsb_offset attribute that contains the LSB offset, in bits, of the child in the parent register. Specifying the LSB offset is optional and the default value is zero.

The registers behave like an integer of the size specified in the bitwidth attribute. However, all modifications on a child or parent register affect the value of the corresponding parent or child registers. This is handled automatically. A child register can also be a parent. This enables component register relationships to extend to component register hierarchies. The consistency of the hierarchies is enforced automatically.

Component register resource definition
REGISTER { bitwidth(64) } RAX;
REGISTER { bitwidth(32), partof(RAX) } EAX;
REGISTER { bitwidth(16), partof(EAX) } AX;
REGISTER { bitwidth(8), partof(AX) } AL;
REGISTER { bitwidth(8), partof(AX), lsb_offset(8) } AH;
Annotated resources - debugger register access functions

Override the default functions that a debugger calls to access registers, by using behaviors that conform to a specific prototype.

Table 2-2 Prototypes of debugger register access functions

Name Prototype
Register read functiona
behavior <name>(uint32_t reg_id, uint64_t *data,
                bool side_effects) : AccessFuncResult
Register write functionb
behavior <name>(uint32_t reg_id, const uint64_t *data,
                bool side_effects) : AccessFuncResult
String register read function
behavior <name>(uint32_t reg_id, string &data,
                bool side_effects) : AccessFuncResult
String register write function
behavior <name>(uint32_t reg_id, const string &data,
                bool side_effects) : AccessFuncResult
reg_id

holds the register ID of the register that is being accessed, that is, the argument used in the reg_number attribute. Modify the array index step size by using reg_number_increment if there is a register array.

For each register with name name, a constant REGISTER_ID_name is generated. An array register has one id for the base and one for each index. Form the index ID by appending an "_" and the index of the array entry.

data

is a buffer that read access functions must export to and write access functions must import from:

  • If the bitwidth is less than or equal to 64, the data pointer points to a single 64 bit quantity.
  • For larger registers, the data pointer points to an array of uint64_ts that holds the entire register value, starting with the Least Significant Bit (LSB) in data[0].

Note:

  • The write access function prototypes declare the data parameter as const.
  • A separate pair of prototypes exists specifically for registers of type string. The data parameter is a reference to a std::string for these functions.
side_effects

is a parameter indicating whether side effects of the access are enforced.

The side_effects parameter specifies whether a read or write involving a register or memory invokes specific side effects associated with that particular register or memory. The semantics of the side_effects parameter differ for read_function and write_function. For read_function, the semantics for side_effects are:

side_effects == false
The read must only return the value of the register and not cause any other side effects. The debugger calls the function with side_effects == false to display the value of the register.
side_effects == true
The read returns the value of the register and causes side effects that are associated with reading the register. Invoking side effects while reading a register is not common. Only make the debugger call read_function with side_effects == true if you want to trigger side effects.

For write_function, the semantics for side_effects are:

side_effects == false

The function might or might not cause side effects, depending on what the component can handle. Some side effects are required even if side_effects == false to keep the component in a consistent state. The only side effects invoked are those required to retain consistency.

The side effects are highly dependent on the modeled hardware. For example, if writing to a SIZE register adjusts the ENDPTR register, update the value of SIZE must reasonably also cause the side effect of updating ENDPTR. For this example, the side_effects parameter must be ignored for writes.

side_effects == true
The function causes all side effects that a normal bus write would cause. Invoking side effects for register writes is the most common use case.

Register and memory access functions use these LISA+ symbols to inform the calling code whether or not the access operation succeeded:

ACCESS_FUNC_OK

The call was successful.

ACCESS_FUNC_GeneralError
An error that the other error return values do not explain.
ACCESS_FUNC_UnknownCommand
The command is not recognized.
ACCESS_FUNC_IllegalArgument
At least one of the argument values is illegal.
ACCESS_FUNC_CmdNotSupported
The command is recognized but not supported.
ACCESS_FUNC_ArgNotSupported
An argument to the command is recognized but not supported. For example, the target does not support a particular type of complex breakpoint.
ACCESS_FUNC_InsufficientResources
Not enough memory or other resources exist to fulfill the command.
ACCESS_FUNC_TargetNotResponding
A time out has occurred across the CADI interface and the target did not respond to the command.
ACCESS_FUNC_TargetBusy
The target received a request, but is unable to process the command. The call can be attempted again after some time.
ACCESS_FUNC_BufferSize
Buffer too small, for char* types.
ACCESS_FUNC_SecurityViolation
Request was not fulfilled because of a security violation.
ACCESS_FUNC_PermissionDenied
Request was not fulfilled because permission was denied.
Read access function
Registers R1 and R2 receive CADI IDs and a shared read access function. The access function returns one's complement for R1 and two's complement for R2. If the access function is assigned to a different register, it reports an illegal argument.
resources
{
    REGISTER { read_function(my_read), reg_number(1) } R1;
    REGISTER { read_function(my_read), reg_number(2) } R2;
}

behavior my_read(uint32_t id, uint64_t *data, bool se) : AccessFuncResult
{
    if (id == 1)
        *data = ~R1;
    else if (id == 2)
        *data = ~R2 + 1;
    else
        return ACCESS_FUNC_IllegalArgument;
    return ACCESS_FUNC_OK;
}
Annotated resources - memory-mapped register access

PV peripherals implement a memory-mapped register by connecting an external slave port of type PVBus to an internal slave port of type PVDevice.

Register IDs are specified explicitly. They have the same value as the address offset. An internal read/write behavior implements the register accesses. Memory-mapped port accesses and debug accesses are directed to this behavior.

If a unique internal port of type PVDevice exists, all memory-mapped register read and write operations over this port are directed to the automatically generated access functions.

If not already overwritten by read_function and write_function parameters, the new access functions are also used for debug accesses.

Masks are used to influence the register access:

  • There are masks for read and write operations.
  • If the device distinguishes between Secure and Non-secure accesses, an additional set of read and write masks can be provided for secure accesses.
  • If the mask parameter is omitted, full access is permitted.
  • A zero mask ignores the access but returns a complete response.

Two additional tokens can be used to generate error responses:

  • ABORT for an abort error response.
  • DECODEABORT for a decode error response.

If the automatic mechanisms are not sufficient, you can provide a local implementation that overrides the access behaviors. The arguments for the access behaviors are:

register_read_behavior(uint32_t reg_id, pv::ReadTransaction tx) :
    pv::Tx_Result
register_write_behavior(uint32_t reg_id, pv::WriteTransaction tx) :
    pv::Tx_Result

Use the ID of a register resource in the read and write behaviors.

Note:

Using memory-mapped register access features requires the Fast Models include files.

Annotated resources - memory

This section describes memory resources.

Annotated resources - about memory

Memory resources are C-array-like constructs that make their contents visible to a debugger. Declare them using the array syntax, where the size of the array is the size of the memory.

Annotated resources - memory parameters

Parameter names, types, default values, and descriptions for memories, buses, and address spaces.

Table 2-3 Parameters for MEMORY resources

Name Type Default Description
allow_unaligned_access Boolean false Permit unaligned access. If this is true then accesses that are not naturally aligned, for example a 32-bit access on a non-32-bit boundary, are permitted and have the expected result. If this is false such unaligned accesses are not permitted.
attribute Access type read_write read_write, read_only, or write_only.
description String "" Description of the space.
endianness Big or little little Select between little and big endianness.
executable Boolean false true for memory blocks that can hold executable code.
mau_size Integer 8 Size of the Minimum Addressable Unit (MAU) in bits. Admissible values are 8, 16, 32, and 64.
paged Boolean true Permit use of true paged memory. This means memory is not allocated completely at instantiation-time, but instead in pages on demand. This typically results in lower overall memory usage but leads to slower memory access. If the array size is > 0x10000, paging is enforced for the memory and the parameter is ignored.
read_function String none Name of the debug read access behavior.
space_id Integer -1 Specifies the memory space id. If not defined, space_id is automatically generated.
supported_multiples_of_mau String 1 Permitted multiples of MAU for memory accesses. Multiple values must be separated by commas.
virtual Boolean false

Optimizes code generation by avoiding the allocation of host memory for the resource. The resource must have read and write access functions and must not be referenced in LISA+ code. If virtual is true, no variable is generated for this parameter. The read and write functions provide and store the value. The parameter is virtual because the read and write functions model it.

write_function String none Name of the debug write access behavior.

Specify the array size using:

  • Pure integer values.
  • Suffixes for Kilo, Mega, Giga, Tera, or Peta, that is, 1K, 1M, 1G, 1T, or 1P relative to the mau_size. These suffixes indicate multipliers of 210, 220, 230, 240, and 250, respectively.
  • Expressions, for example 2k-1.
Memory resource definition
resources
{
    MEMORY { mau_size(8) } progmem[64k];
    MEMORY { mau_size(32) } datamem[128k-100];
}
Annotated resources - read and write accesses

Access memory resources using C-array like syntax.

C array memory accesses
behavior load(uint32_t address, uint8_t &data)
{
    data = dmem[address];
}

behavior store(uint32_t address, uint8_t data)
{
    dmem[address] = data;
}

The size of the memory access is always one Minimum Addressable Unit (MAU). A 32-bit unsigned integer, for example, is the access size for a memory with a mau_size of 32.

The memory access functions are:

Read and write accesses
resource.read8( uint32_t address, uint8_t & destination );
resource.read16( uint32_t address, uint16_t & destination );
resource.read32( uint32_t address, uint32_t & destination );
resource.read64( uint32_t address, uint64_t & destination );
resource.write8( uint32_t address, uint8_t source );
resource.write16( uint32_t address, uint16_t source );
resource.write32( uint32_t address, uint32_t source );
resource.write64( uint32_t address, uint64_t source );

Use these access functions to read or write 8, 16, 32 and 64-bit quantities from or to memory. The function name implies the size of the access. Only functions with a bitwidth greater than or equal to the mau_size of the memory can be used on a memory. If the bitwidth of the access is greater than the mau_size the result depends on the endianness of the memory.

Mixed bitwidth accesses
behavior load(uint32_t address, uint8_t &data)
{
        dmem.read8(address, data);
}

behavior store(uint32_t address, uint8_t data)
{
        dmem.write8(address, data);
}
Annotated resources - debugger memory read- and write-access functions

The access functions for memory are similar to the access functions for registers. You can attach debugger access functions to REGISTER and MEMORY resources.

The component that holds the memory resource implements the access functions as LISA+ behaviors. The read_function and write_function resource parameters designate the access functions. Define the debugger access functions as behaviors, but attach them in the resources statements (REGISTER or MEMORY), as here. See the CADIMemRead and CADIMemWrite function descriptions for more information.

resources
{
    MEMORY { mau_size(8), read_function(my_read) } progmem[64];
    MEMORY { mau_size(32), write_function(my_write) } datamem[64];
}

The access functions must conform to these prototypes:

Read function
behavior read_function_name (uint32_t space_id,
                uint32_t block_id,
                uint64_t offset,
                uint32_t size_in_maus,
                uint64_t *data,
                bool side_effects,
                sg::MemoryAccessContext *mac) : AccessFuncResult
Write function
behavior write_function_name (uint32_t space_id,
                uint32_t block_id,
                uint64_t offset,
                uint32_t size_in_maus,
                const uint64_t *data,
                bool side_effects,
                sg::MemoryAccessContext *mac) : AccessFuncResult
space_id
an integer value that is a unique identifier for a memory space.
block_id
an integer value that, for the specified memory space, is a unique identifier for a memory block. This is unused and always 0.
offset
an absolute numerical offset into the space and block denoted by the space_id and block_id parameters. It designates the starting address for the memory access.
size_in_maus
the size of the access relative to the size of the MAU. A memory access might involve reading or writing multiple MAU quantities.
data
the buffer from which data is read or to which data is written. Its type is a pointer to a 64-bit unsigned integer. This size is equal to the largest MAU size currently supported and effectively eliminates endianness concerns. In the implementation, data is actually an array of size_in_maus size. Write functions protect this array by declaring the data pointer const.
side_effects
a parameter that indicates whether the side effects for the access must be enforced. The use of this parameter with memory reads and writes is completely analogous to its use with registers.
MemoryAccessContext

a pointer to a MemoryAccessContext object. This provides extensibility to the prototype, and the MemoryAccessContext class can be enriched if required. The current interfaces are:

GetAccessSizeInMaus()

returns how many MAUs of memory area have to be read/written.

GetMauInBytes()
returns the size of a MAU, measured in bytes.
GetMauInBits()
returns the size of a MAU, measured in bits.

Note:

Accessing these values though MemoryAccessContext, rather than by coding them as constants, lowers the maintenance hazard arising from a memory attribute change.
Read and write access functions
behavior my_read(uint32_t space_id,
                 uint32_t block_id,
                 uint64_t offset,
                 uint32_t size_in_maus,
                 uint64_t *data,
                 bool side_effects,
                 MemoryAccessContext *mac) : AccessFuncResult
{
    *data = progmem[offset];
    return ACCESS_FUNC_OK;
}

behavior my_write(uint32_t space_id,
                  uint32_t block_id,
                  uint64_t offset,
                  uint32_t size_in_maus,
                  const uint64_t *data,
                  bool side_effects,
                  MemoryAccessContext *mac) : AccessFuncResult
{
    datamem[offset] = (uint32_t) *data;
    return ACCESS_FUNC_OK;
}
Implementation of a read access function

Here are declarations of two memory resources, m1 and m2, and a read access function, my_read, for them. It is possible for multiple memory spaces and blocks to share an access function because the distinction between them can be made at run-time by means of the space_id and block_id parameters. Here, a simple if / else sequence distinguishes them.

After determining the resource to be accessed, data is copied into the data buffer. The for loop runs for size_in_maus times, copying one MAU quantity on each iteration and checking whether the accesses are within bounds or not.

resources
{
    MEMORY { space_id(1), mau_size(8), read_function(my_read) } m1[64];
    MEMORY { space_id(2), mau_size(32), read_function(my_read) } m2[64];
}

behavior my_read(uint32_t space_id,
                 uint32_t block_id,
                 uint64_t offset,
                 uint32_t size_in_maus,
                 uint64_t *data,
                 bool side_effects,
                 MemoryAccessContext *mac) : AccessFuncResult
{
    if (space_id == 1)
    {
        for (int i = 0; (i < size_in_maus) && (offset + i < 64); ++i)
            data[i] = m1[offset + i];
    }
    else if (space_id == 2)
    {
        for (int i = 0; i < (size_in_maus) && (offset + i < 64); ++i)
            data[i] = m2[offset + i];
    }
    else
        return ACCESS_FUNC_IllegalArgument;
    return ACCESS_FUNC_OK;
}

Note:

If you set the virtual resource parameter to true, you prevent memory allocation at run-time. A virtual memory resource must provide debug read and write functions and cannot be directly accessed from LISA+ source code. If you do not define valid read and write access functions, or attempt to access the resource through LISA+ code, a build failure occurs.

Annotated resources - parameters

Parameters permit component configuration and parameterization of the system model at initialization-time or in run-time. They do not support array syntax.

Table 2-4 Parameters for parameters

Parameter Type Default Description
default Integer or string 0 or "" Default value is 0 for integers and the empty string for string parameters.
description String "" Plain text single line description of the parameter. The debugger might display this string next to the parameter to provide additional information about a parameter.
max Integer 0x7FFFFFFFFFFFFFFF Maximum admissible value.
min Integer 0x8000000000000000 Minimum admissible value. The maximum ranges are:
  • [0x0, 0x7FFFFFFFFFFFFFFF] for unsigned parameters.
  • [0x8000000000000000, 0x7FFFFFFFFFFFFFFF] for signed parameters.
name String "" A text tag for the parameter that is displayed in the GUI. Any printable symbol except for "#", ".", and "=" can be used in name. Double quote characters within the tag must be escaped with "\". If no name is specified, the parameter identifier is used.
type Integer, Boolean, or string int Data type. The type can also be intx_size or uintx_size where size is one of 8, 16, 32, or 64.
read_function String none Name of the read access behavior.
write_function String none Name of the write access behavior.
runtime Boolean false Switch between instantiation-time and run-time parameters. You can set instantiation-time parameters before the system instantiates. You cannot change them afterwards, or query them with a debugger. This is the default. You can change run-time parameters during run-time.

The access functions have similar semantics to those of registers. The access function prototypes are:

  • Integer and bool parameters:

    behavior my_read(uint32_t id, int64_t *data) : AccessFuncResult
    behavior my_write(uint32_t id, const int64_t *data) : AccessFuncResult
  • String parameters:

    behavior my_read(uint32_t id, string &data) : AccessFuncResult
    behavior my_write(uint32_t id, const string &data) : AccessFuncResult

For each parameter with name name, a constant PARAMETER_ID_name is generated. This is passed as id when the PARAMETER is read from or written to.

The default behavior for the read_function is to return the current value of the PARAMETER. The default behavior for the write_function is to set the value of the PARAMETER. If a write_function is specified, the parameter is no longer updated automatically. This update must be done in the write function.

Parameter resource definition
resources
{
    PARAMETER { typ(int), min(0), max(0xFFFF), default(0x80), name(”Base Address”)}
      baseAddress;
}

The parameter in the example would default to being called baseAddress if a name tag was not declared. When choosing parameter names or tags, you are strongly advised to adhere to the naming rules for C++ identifiers. This means you can use upper and lower case letters, numbers, and underscore characters. Avoid using hyphens, "-" in parameter names or tags. If you are supporting legacy code that uses hyphens in parameter names, you can use these old names within the name tag. However, the parameter name outside the braces must conform to C++ naming rules, and is what you must use in your LISA+ code.

Integer parameters in decimal format can contain binary multiplication suffixes. These left-shift the bits in parameter value by the corresponding power of two.

Table 2-5 Suffixes for parameter values

Suffix Name Multiplier
K Kilo 210
M Mega 220
G Giga 230
T Tera 240
P Peta 250
a

See the CADIRegRead function description.

b

See the CADIRegWrite function description.

Non-ConfidentialPDF file icon PDF version101092_0100_04_en
Copyright © 2014–2018 Arm Limited or its affiliates. All rights reserved.