Example 02: Reading and Writing SDRAM

Most interesting SpiNNaker applications require some sort of configuration data to be loaded onto the machine or produce result data which must be read back from the machine. Typically this is done by allocating and writing to or reading from the shared SDRAM on each SpiNNaker chip. In this example we’ll write a simple SpiNNaker application which, using a single core, adds two numbers loaded in SDRAM and writes the answer back to SDRAM.

Much of the code in this example is unchanged from the previous example so we will only discuss the changes.

Example Source code on GitHub

Allocating SDRAM

When both the host and SpiNNaker need to access the same block of SDRAM, such as when loading configuration data or reading back results, it is common for the host to allocate the SDRAM. In this example we’ll allocate some SDRAM on chip (0, 0). We’ll allocate a total of 12 bytes: 4 bytes (32 bits) each for the two values we want to be added and another 4 bytes for the result using sdram_alloc():

sdram_addr = mc.sdram_alloc(12, x=0, y=0, tag=1)

The sdram_alloc() method simply returns the address of a block of SDRAM on chip 0, 0 which was allocated.

We also need to somehow inform the SpiNNaker application of this address. To do this we can use the ‘tag’ using the argument to give an identifier to the allocated memory block. The SpiNNaker application then uses the sark_tag_ptr function to look up the address of an SDRAM block with a specified tag.

  uint32_t *numbers = sark_tag_ptr(spin1_get_core_id(), 0);

A tag is a user-defined identifier which must be unique to the SpiNNaker chip and application. By convention the SDRAM allocated for applications running on core 1 are given tag 1, those on core 2 given tag 2 and so on. This means the same application binary can be loaded onto multiple cores which can simply look up their core number to discover their unique SDRAM allocation’s address.

Reading and writing to SDRAM

We pick two random numbers to add together and write them to the SDRAM we just allocated. Note that we must pack our values into bytes using Python’s struct module. Since SpiNNaker is little-endian we must be careful to use the ‘<’ format string.

num_a = random.getrandbits(30)
num_b = random.getrandbits(30)
data = struct.pack("<II", num_a, num_b)

Next we simply write the bytes to the allocated block of SDRAM using write().

mc.write(sdram_addr, data, x=0, y=0)

After we’ve allocated and writen our config data to SDRAM we can load our application as usual. On the C side of our application, the SDRAM can be accessed like any other memory.

  numbers[2] = numbers[0] + numbers[1];

Warning

Though SpiNNaker’s SDRAM can be accessed just like normal memory within a SpiNNaker application, this comes with a significant performance penalty; ‘real’ applications should use DMA to access SDRAM.

Once the application has exited, the host can read the answer back using read().

result_data = mc.read(sdram_addr + 8, 4, x=0, y=0)
result, = struct.unpack("<I", result_data)
print("{} + {} = {}".format(num_a, num_b, result))

Finally, as in our last example we must send the stop signal using send_signal() to free up all SpiNNaker resources. This is important since until the ‘stop’ signal is sent the SDRAM and tag number will remain allocated and may not be reallocated.

mc.send_signal("stop")