Memory Access

A common task in many models is to access memory, reasons for this may include DMA transactions to be simulated, RMAP access from SpaceWire controllers, accessing send-lists in a bus controller etc.

There are two ways to access memory. One is to use the same memory access interface that you have implemented for register address decoding (by connecting to the memory space object), the other option is to use the MemoryIface, this interface provides two methods, readBytes and writeBytes. The nice thing with these is that they allow you to read out large blocks of data and potentially swap data to host endianess if needed for different data sizes.

The MemoryIface is provided by the MemorySpace class, so the easy way to get access to it is to go through an instance of that.

Working from the previous model, add an interface reference to the model:

temu_MemoryIfaceRef mem; // In the device struct
// In the plug-in init function:
temu_addProperty(c, "memory", offsetof(MyModel, mem),
    teTY_IfaceRef, 1,
    NULL, NULL, "memory space");

Now, let’s extend the device model’s register write function, we are going to treat the written value as an address and turn our device into a word inverter, we can write a new RTEMS application for triggering this process:

rtems_task
Init(rtems_task_argument ignored)
{
  volatile unsigned data[4] = {0x01234567, 0x89abcdef,
                               0xaaaaaaaa, 0x55555555};
  printk("data before: %x %x %x %x\n",
         data[0], data[1], data[2], data[3]);
  *(volatile unsigned*)(0x80001000) = (unsigned)&data[0];
  printk("data inverted: %x %x %x %x\n",
         data[0], data[1], data[2], data[3]);
  exit(0);
}

In the write handler, you can add a snippet like this:

  uint32_t buffer[4] = {0};
  // Read out 16 bytes of type word (1 << 2 bytes each)
  model->mem.Iface->readBytes(model->mem.Obj, &buffer[0],
  pv.u32,16, 2);
  temu_logInfo(model, "before invert: %x %x %x %x",
  buffer[0], buffer[1], buffer[2], buffer[3]);
  for (int i = 0 ; i < 4 ; ++i) {
    buffer[i] = ~buffer[i];
  }
  model->mem.Iface->writeBytes(model->mem.Obj, pv.u32, 16,
     &buffer[0], 2);

You need to connect the memory interface as well:

temu> connect a=foo.memory b=mem0:MemoryIface

Now, when running it, you will see that data is copied back and forth between the models and properly updated by the device model.