2.2.4. Memory

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

The parameters listed in Table 2.3 can be given to memories:

Table 2.3. Parameters for memory, bus, and address space 

ParameterTypeDefaultDescription
allow_unaligned_accessbooleanfalseAllow unaligned access. If this is true then accesses that are not naturally aligned, such as a 32-bit access on a non32-bit boundary, are allowed and have the expected result. If this is false such unaligned accesses are not allowed.
attributeaccess typeread_write

Access type as one of:

  • read_write

  • read_only

  • write_only.

descriptionstring""Description of the space
endiannessbig or littlelittleSelect between little and big endianness.
executablebooleanfalseThis flag identifies memory blocks that can hold executable code.
mau_sizeinteger8Size of the Minimum Addressable Unit (MAU) in bits. Admissible values are 8, 16, 32, and 64.
pagedbooleantrueAllow 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_functionstringnoneName of the debug read access behavior.
space_idinteger-1Specifies the memory space id. If not defined, space_id is automatically generated.
supported_multiples_of_maustring1Permitted multiples of MAU for memory accesses. Multiple values must be separated by commas.
virtualbooleanfalse

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 this attribute is set to true, no variable is generated for this parameter. The read and write functions are responsible for providing and storing the value. The parameter is virtual because it is modeled through the read and write functions.

write_functionstringnoneName of the debug write access behavior.

The array size can be specified using:

Example 2.7. Resources definition

    resources
    {
        MEMORY { mau_size(8) } progmem[64k];
        MEMORY { mau_size(32) } datamem[128k-100];
    }

Read and write accesses

Memory resources can be accessed using C-array like syntax as shown in Example 2.9:

Example 2.8. 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 Minimal Addressable Unit (MAU). A 32-bit unsigned integer, for example, is the access size for a memory with a mau_size of 32.

The access functions listed in Example 2.9 can also be used to access memory:

Example 2.9. 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 );

These access functions can be used to read or write 8, 16, 32 and 64-bit quantities from or to memory. The size of the access is implied by the function name. 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. See Example 2.10.

Example 2.10. 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);
}

Debugger memory read and write access functions

The access functions for memory are similar to the access functions for registers. You can control the way in which a memory resource is accessed by the debugger by defining your own debugger access functions. A maximum of one read-access function and one write-access function can be specified in the resources section.

Access functions are implemented as LISA+ behaviors in the same component that holds the memory resource. They are designated as access functions by the read_function and write_function resource parameters. Refer to the CADIMemRead and CADIMemWrite function descriptions for more information. See the Model Debugger for Fast Models User Guide.

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 the following 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

where:

space_id

is an integer value that is a unique identifier for a memory space.

block_id

is an integer value that, for the specified memory space, is a unique identifier for a memory block.

offset

is 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

is the size of the access relative to the size of the MAU. A memory access might involve reading or writing multiple MAU quantities.

data

is 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

is 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. See Debugger register access functions.

MemoryAccessContext

is a pointer to a MemoryAccessContext object. This provides extensibility to the prototype and the MemoryAccessContext class can be enriched if required. There are currently the following interfaces:

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

The reason for accessing these values though MemoryAccessContext rather than by coding them as constants is to lower the maintenance hazard that would arise if a memory attribute changes.

Example 2.11 shows an example of access functions:

Example 2.11. 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;
}

In Example 2.12, two memory resources, m1 and m2, are declared and a read access function, my_read, is provided 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 runtime by means of the space_id and block_id parameters. For Example 2.12, this is accomplished with a simple if / else sequence.

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.

Example 2.12. Implementation of a read access function

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 from being allocated 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.

Copyright © 2007-2009 ARM Limited. All rights reserved.ARM DUI 0372G
Non-Confidential