Added Sonata simple SPI driver.
diff --git a/sdk/boards/sonata.json b/sdk/boards/sonata.json
index 9fdccd6..0930fcd 100644
--- a/sdk/boards/sonata.json
+++ b/sdk/boards/sonata.json
@@ -4,6 +4,10 @@
             "start" : 0x30000000,
             "end"   : 0x30004000
         },
+        "gpio" : {
+            "start" : 0x80000000,
+            "end"   : 0x80000020
+        },
         "clint": {
             "start" : 0x80040000,
             "end"   : 0x80050000
@@ -12,9 +16,17 @@
             "start" : 0x80100000,
             "end"   : 0x80100034
         },
-        "gpio" : {
-            "start" : 0x80000000,
-            "end"   : 0x80000020
+        "spi0": {
+            "start" : 0x80300000,
+            "end"   : 0x80301000
+        },
+        "spi1": {
+            "start" : 0x80301000,
+            "end"   : 0x80302000
+        },
+        "spi2": {
+            "start" : 0x80302000,
+            "end"   : 0x80303000
         },
         "plic": {
             "start" : 0x88000000,
diff --git a/sdk/include/platform/sunburst/platform-spi.hh b/sdk/include/platform/sunburst/platform-spi.hh
new file mode 100644
index 0000000..fd8aa19
--- /dev/null
+++ b/sdk/include/platform/sunburst/platform-spi.hh
@@ -0,0 +1,235 @@
+#pragma once
+#include <cdefs.h>
+#include <debug.hh>
+#include <stdint.h>
+
+/**
+ * A Simple Driver for the Sonata's SPI.
+ *
+ * Documentation source can be found at:
+ * https://github.com/lowRISC/sonata-system/blob/1a59633d2515d4fe186a07d53e49ff95c18d9bbf/doc/ip/spi.md
+ *
+ * Rendered documentation is served from:
+ * https://lowrisc.org/sonata-system/doc/ip/spi.html
+ */
+struct SonataSpi
+{
+	/**
+	 * The Sonata SPI block doesn't currently have support for interrupts.
+	 * The following registers are reserved for future use.
+	 */
+	uint32_t interruptState;
+	uint32_t interruptEnable;
+	uint32_t interruptTest;
+	/**
+	 * Configuration register. Controls how the SPI block transmits and
+	 * receives data. This register can be modified only whilst the SPI block
+	 * is idle.
+	 */
+	uint32_t configuration;
+	/**
+	 * Controls the operation of the SPI block. This register can
+	 * be modified only whilst the SPI block is idle.
+	 */
+	uint32_t control;
+	/// Status information about the SPI block
+	uint32_t status;
+	/**
+	 * Writes to this begin an SPI operation.
+	 * Writes are ignored when the SPI block is active.
+	 */
+	uint32_t start;
+	/**
+	 * Data from the receive FIFO. When read the data is popped from the FIFO.
+	 * If the FIFO is empty data read is undefined.
+	 */
+	uint32_t receiveFifo;
+	/**
+	 * Bytes written here are pushed to the transmit FIFO. If the FIFO is full
+	 * writes are ignored.
+	 */
+	uint32_t transmitFifo;
+
+	/// Configuration Register Fields
+	enum : uint32_t
+	{
+		/**
+		 * The length of a half period (i.e. positive edge to negative edge) of
+		 * the SPI clock, measured in system clock cycles reduced by 1. For
+		 * example, at a 50 MHz system clock, a value of 0 gives a 25 MHz SPI
+		 * clock, a value of 1 gives a 12.5 MHz SPI clock, a value of 2 gives
+		 * a 8.33 MHz SPI clock and so on.
+		 */
+		ConfigurationHalfClockPeriodMask = 0xffu << 0,
+		/*
+		 * When set the most significant bit (MSB) is the first bit sent and
+		 * received with each byte
+		 */
+		ConfigurationMSBFirst = 1u << 29,
+		/*
+		 * The phase of the spi_clk signal. when clockphase is 0, data is
+		 * sampled on the leading edge and changes on the trailing edge. The
+		 * first data bit is immediately available before the first leading edge
+		 * of the clock when transmission begins. When clockphase is 1, data is
+		 * sampled on the trailing edge and change on the leading edge.
+		 */
+		ConfigurationClockPhase = 1u << 30,
+		/*
+		 * The polarity of the spi_clk signal. When ClockPolarity is 0, clock is
+		 * low when idle and the leading edge is positive. When ClkPolarity is
+		 * 1, clock is high when idle and the leading edge is negative
+		 */
+		ConfigurationClockPolarity = 1u << 31,
+	};
+
+	/// Control Register Fields
+	enum : uint32_t
+	{
+		/// Write 1 to clear the transmit FIFO.
+		ControlTransmitClear = 1 << 0,
+		/// Write 1 to clear the receive FIFO.
+		ControlReceiveClear = 1 << 1,
+		/**
+		 * When set bytes from the transmit FIFO are sent. When clear the state
+		 * of the outgoing spi_cipo is undefined whilst the SPI clock is
+		 * running.
+		 */
+		ControlTransmitEnable = 1 << 2,
+		/**
+		 * When set incoming bits are written to the receive FIFO. When clear
+		 * incoming bits are ignored.
+		 */
+		ControlReceiveEnable = 1 << 3,
+		/**
+		 * The watermark level for the transmit FIFO, depending on the value
+		 * the interrupt will trigger at different points
+		 */
+		ControlTransmitWatermarkMask = 0xf << 4,
+		/**
+		 * The watermark level for the receive FIFO, depending on the value the
+		 * interrupt will trigger at different points
+		 */
+		ControlReceiveWatermarkMask = 0xf << 8,
+	};
+
+	/// Status Register Fields
+	enum : uint32_t
+	{
+		/// Number of items in the transmit FIFO.
+		StatusTxFifoLevel = 0xffu << 0,
+		/// Number of items in the receive FIFO.
+		StatusRxFifoLevel = 0xffu << 8,
+		/**
+		 * When set the transmit FIFO is full and any data written to it will
+		 * be ignored.
+		 */
+		StatusTxFifoFull = 1u << 16,
+		/**
+		 * When set the receive FIFO is empty and any data read from it will be
+		 * undefined.
+		 */
+		StatusRxFifoEmpty = 1u << 17,
+		/// When set the SPI block is idle and can accept a new start command.
+		StatusIdle = 1u << 18,
+	};
+
+	/// Start Register Fields
+	enum : uint32_t
+	{
+		/// Number of bytes to receive/transmit in the SPI operation
+		StartByteCountMask = 0x7ffu,
+	};
+
+	/// Flag set when we're debugging this driver.
+	static constexpr bool DebugSonataSpi = false;
+
+	/// Helper for conditional debug logs and assertions.
+	using Debug = ConditionalDebug<DebugSonataSpi, "Sonata SPI">;
+
+	/**
+	 * Initialises the SPI block
+	 *
+	 * @param ClockPolarity When false, the clock is low when idle and the
+	 *        leading edge is positive. When true, the opposite behaviour is
+	 *        set.
+	 * @param ClockPhase When false, data is sampled on the leading edge and
+	 *        changes on the trailing edge. When true, the opposite behaviour is
+	 *        set.
+	 * @param MsbFirst When true, the first bit of each byte sent is the most
+	 *        significant bit, as oppose to the least significant bit.
+	 * @param HalfClockPeriod The length of a half period of the SPI clock,
+	 *        measured in system clock cycles reduced by 1.
+	 */
+	void init(const bool     ClockPolarity,
+	          const bool     ClockPhase,
+	          const bool     MsbFirst,
+	          const uint16_t HalfClockPeriod) volatile
+	{
+		configuration = (ClockPolarity ? ConfigurationClockPolarity : 0) |
+		                (ClockPhase ? ConfigurationClockPhase : 0) |
+		                (MsbFirst ? ConfigurationMSBFirst : 0) |
+		                (HalfClockPeriod & ConfigurationHalfClockPeriodMask);
+	}
+
+	/// Waits for the SPI device to become idle
+	void wait_idle() volatile
+	{
+		// Wait whilst IDLE field in STATUS is low
+		while ((status & StatusIdle) == 0) {}
+	}
+
+	/**
+	 * Sends `len` bytes from the given `data` buffer,
+	 * where `len` is at most `0x7ff`.
+	 */
+	void blocking_write(const uint8_t data[], uint16_t len) volatile
+	{
+		Debug::Assert(len <= 0x7ff,
+		              "You can't transfer more than 0x7ff bytes at a time.");
+		len &= StartByteCountMask;
+
+		wait_idle();
+		control = ControlTransmitEnable;
+		start   = len;
+
+		uint32_t transmitAvailable = 0;
+		for (uint32_t i = 0; i < len; ++i)
+		{
+			if (transmitAvailable == 0)
+			{
+				while (transmitAvailable < 64)
+				{
+					// Read number of bytes in TX FIFO to calculate space
+					// available for more bytes
+					transmitAvailable = 64 - (status & StatusTxFifoLevel);
+				}
+			}
+			transmitFifo = data[i];
+			transmitAvailable--;
+		}
+	}
+
+	/*
+	 * Receives `len` bytes and puts them in the `data` buffer,
+	 * where `len` is at most `0x7ff`.
+	 *
+	 * This method will block until the requested number of bytes
+	 * has been seen. There is currently no timeout.
+	 */
+	void blocking_read(uint8_t data[], uint16_t len) volatile
+	{
+		Debug::Assert(len <= 0x7ff,
+		              "You can't receive more than 0x7ff bytes at a time.");
+		len &= StartByteCountMask;
+		wait_idle();
+		control = ControlReceiveEnable;
+		start   = len;
+
+		for (uint32_t i = 0; i < len; ++i)
+		{
+			// Wait for at least one byte to be available in the RX FIFO
+			while ((status & StatusRxFifoLevel) == 0) {}
+			data[i] = static_cast<uint8_t>(receiveFifo);
+		}
+	}
+};