Skip to content

Commit

Permalink
cores/spi: Add new SPIMMAP core allowing doing SPI accesses directly …
Browse files Browse the repository at this point in the history
…from MMAP.

Implements a new SPIMMAP module, allowing accessing multiple SPI peripherals directly
from MMAP. It allows configurable SPI transactions: mode, bit order, and data width.

Developed and funded through a collaboration with MoTeC.

Example of integration:

# SPI MMAP ---------------------------------------------------------------------------------
spi_pads = Record([("clk", 1), ("cs_n", 8), ("mosi", 1), ("miso", 1)])
spi_mmap_tx_region = SoCRegion(origin=0x8000_0000, size=4096, cached=False)
spi_mmap_rx_region = SoCRegion(origin=0x8000_1000, size=4096, cached=False)
self.spi_mmap = SPIMMAP(
    pads          = spi_pads,
    data_width    = 32,
    sys_clk_freq  = sys_clk_freq,
    tx_origin     = spi_mmap_tx_region.origin,
    rx_origin     = spi_mmap_rx_region.origin,
    tx_fifo_depth = 32,
    rx_fifo_depth = 32,
)

self.bus.add_slave(name="spi_tx",
    slave  = self.spi_mmap.tx_mmap.bus,
    region = spi_mmap_tx_region,
)
self.bus.add_slave(name="spi_rx",
    slave  = self.spi_mmap.rx_mmap.bus,
    region = spi_mmap_rx_region,
)
self.irq.add("spi_mmap", use_loc_if_exists=True)


Example of use from CPU C firmware:

/* SPI TX Offsets */
#define SPI_TX_CTRL_ENABLE    (1 << 0)
#define SPI_TX_CTRL_THRESHOLD (1 << 16)

#define SPI_TX_STAT_ONGOING   (1 << 0)
#define SPI_TX_STAT_EMPTY     (1 << 1)
#define SPI_TX_STAT_FULL      (1 << 2)
#define SPI_TX_STAT_LEVEL     (1 << 16)

/* SPI RX Offsets */
#define SPI_RX_CTRL_ENABLE    (1 << 0)
#define SPI_RX_CTRL_THRESHOLD (1 << 16)

#define SPI_RX_STAT_ONGOING   (1 << 0)
#define SPI_RX_STAT_EMPTY     (1 << 1)
#define SPI_RX_STAT_FULL      (1 << 2)
#define SPI_RX_STAT_LEVEL     (1 << 16)

/* SPI TX/RX Engine */
#define SPI_TX_RX_ENGINE_ENABLE (1 << 0)

/* SPI SLOT Offsets */
#define SPI_SLOT_ENABLE   (1 <<  0)
#define SPI_SLOT_MODE     (1 <<  1)
#define SPI_SLOT_LENGTH   (1 <<  3)
#define SPI_SLOT_BITORDER (1 <<  5)
#define SPI_SLOT_LOOPBACK (1 <<  6)
#define SPI_SLOT_DIVIDER  (1 << 16)

/* SPI SLOT Values */
#define SPI_SLOT_MODE_0 0b00
#define SPI_SLOT_MODE_3 0b11

#define SPI_SLOT_LENGTH_32B 0b00
#define SPI_SLOT_LENGTH_16B 0b01
#define SPI_SLOT_LENGTH_8B  0b10

#define SPI_SLOT_BITORDER_MSB_FIRST 0b0
#define SPI_SLOT_BITORDER_LSB_FIRST 0b1

#define SPI_SLOT_EV_TX (1 << 0)
#define SPI_SLOT_EV_RX (1 << 1)


/* Test SPI with various length (BE) */
void test_spi_length_8_16_32(void) {
	volatile unsigned char  *spi_tx_8  = (unsigned char  *)SPI_TX_BASE;
	volatile unsigned short *spi_tx_16 = (unsigned short *)SPI_TX_BASE;
	volatile unsigned int   *spi_tx_32 = (unsigned int   *)SPI_TX_BASE;

	volatile unsigned char  *spi_rx_8  = (unsigned char  *)SPI_RX_BASE;
	volatile unsigned short *spi_rx_16 = (unsigned short *)SPI_RX_BASE;
	volatile unsigned int   *spi_rx_32 = (unsigned int   *)SPI_RX_BASE;

	int errors = 0;

	printf("Test SPI with various length (BE): 8, 16 and 32-bit...\n");

	/* Configure Slots */
	spi_mmap_ctrl_slot_control0_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								  4 * SPI_SLOT_DIVIDER
	);
	spi_mmap_ctrl_slot_control1_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								  4 * SPI_SLOT_DIVIDER
	);
	spi_mmap_ctrl_slot_control2_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								  4 * SPI_SLOT_DIVIDER
	);
	spi_mmap_ctrl_slot_control3_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								  4 * SPI_SLOT_DIVIDER
	);

	/* Enable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE);

	/* TX 8-bit transfers */
	spi_tx_8[0]  = 0x5a;
	spi_tx_8[4]  = 0x01;
	spi_tx_8[8]  = 0x5a;
	spi_tx_8[12] = 0x01;

	/* TX 16-bit transfers */
	spi_tx_16[0] = 0x5aa5;
	spi_tx_16[2] = 0x0102;
	spi_tx_16[4] = 0x5aa5;
	spi_tx_16[6] = 0x0102;

	/* TX 32-bit transfers */
	spi_tx_32[0] = 0x5aa55aa5;
	spi_tx_32[1] = 0x01020304;
	spi_tx_32[2] = 0x5aa55aa5;
	spi_tx_32[3] = 0x01020304;

	/* Small delay */
	busy_wait(1);

	/* Read RX 8-bit transfers */
	if (spi_rx_8[ 0] != 0x5a)
		errors++;
	if (spi_rx_8[ 4] != 0x01)
		errors++;
	if (spi_rx_8[ 8] != 0x5a)
		errors++;
	if (spi_rx_8[12] != 0x01)
		errors++;

	/* Read RX 16-bit transfers */
	if (spi_rx_16[0] != 0x5aa5)
		errors++;
	if (spi_rx_16[2] != 0x0102)
		errors++;
	if (spi_rx_16[4] != 0x5aa5)
		errors++;
	if (spi_rx_16[6] != 0x0102)
		errors++;

	/* Read RX 32-bit tranfers */
	if (spi_rx_32[0] != 0x5aa55aa5)
		errors++;
	if (spi_rx_32[1] != 0x01020304)
		errors++;
	if (spi_rx_32[2] != 0x5aa55aa5)
		errors++;
	if (spi_rx_32[3] != 0x01020304)
		errors++;

	/* Disable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE);

	/* Result */
	printf("errors: %d\n", errors);
}

/* Test SPI with various clk divider */
void test_spi_clk_divider(void) {
	volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE;
	volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE;

	int errors = 0;

	printf("Test SPI with various clk divider: 4, 8, 16 and 32...\n");

	/* Configure Slots */
	spi_mmap_ctrl_slot_control0_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								  4 * SPI_SLOT_DIVIDER
	);
	spi_mmap_ctrl_slot_control1_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								  8 * SPI_SLOT_DIVIDER
	);
	spi_mmap_ctrl_slot_control2_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								 16 * SPI_SLOT_DIVIDER
	);
	spi_mmap_ctrl_slot_control3_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								 32 * SPI_SLOT_DIVIDER
	);

	/* Enable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE);

	/* TX 32-bit transfers */
	spi_tx_32[0] = 0x01020304;
	spi_tx_32[1] = 0x5aa55aa5;
	spi_tx_32[2] = 0x01020304;
	spi_tx_32[3] = 0x5aa55aa5;

	/* Small delay */
	busy_wait(1);

	/* Read RX 32-bit tranfers */
	if (spi_rx_32[0] != 0x01020304)
		errors++;
	if (spi_rx_32[1] != 0x5aa55aa5)
		errors++;
	if (spi_rx_32[2] != 0x01020304)
		errors++;
	if (spi_rx_32[3] != 0x5aa55aa5)
		errors++;

	/* Disable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE);

	/* Result */
	printf("errors: %d\n", errors);
}

/* Test SPI with various SPI modes */
void test_spi_modes(void) {
	volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE;
	volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE;

	int errors = 0;

	printf("Test SPI with various SPI modes: 0 and 3...\n");

	/* Configure Slots */
	spi_mmap_ctrl_slot_control0_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								  4 * SPI_SLOT_DIVIDER
	);
	spi_mmap_ctrl_slot_control1_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_3 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								  4 * SPI_SLOT_DIVIDER
	);

	/* Enable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE);

	/* TX 32-bit transfers */
	spi_tx_32[0] = 0x5aa55aa5;
	spi_tx_32[1] = 0x5aa55aa5;

	/* Small delay */
	busy_wait(1);

	/* Read RX 32-bit tranfers */
	if (spi_rx_32[0] != 0x5aa55aa5)
		errors++;
	if (spi_rx_32[1] != 0x5aa55aa5)
		errors++;

	/* Disable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE);

	/* Result */
	printf("errors: %d\n", errors);
}

/* Test SPI with various bitorders */
void test_spi_bitorders(void) {
	volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE;
	volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE;

	int errors = 0;

	printf("Test SPI with various bitorders: MSB and LSB first...\n");

	/* Configure Slots */
	spi_mmap_ctrl_slot_control0_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								  4 * SPI_SLOT_DIVIDER
	);
	spi_mmap_ctrl_slot_control1_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_LSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								  4 * SPI_SLOT_DIVIDER
	);

	/* Enable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE);

	/* TX 32-bit transfers */
	spi_tx_32[0] = 0xff000000;
	spi_tx_32[1] = 0xff000000;

	/* Small delay */
	busy_wait(1);

	/* Read RX 32-bit tranfers */
	if (spi_rx_32[0] != 0xff000000)
		errors++;
	if (spi_rx_32[1] != 0xff000000)
		errors++;

	/* Disable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE);

	/* Result */
	printf("errors: %d\n", errors);
}

/* Test SPI TX/RX levels */
void test_spi_tx_rx_levels(void) {
	volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE;
	volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE;

	int i;
	int errors = 0;
	int pattern;

	printf("Test SPI TX/RX levels...\n");

	/* Configure Slots */
	spi_mmap_ctrl_slot_control0_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								128 * SPI_SLOT_DIVIDER
	);

	/* Enable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE);

	/* TX 32-bit transfers */
	pattern = 0x00000001;
	for (i=0; i<16; i++){
		if ((spi_mmap_ctrl_tx_status_read() >> 16) != i)
			errors++;
		spi_tx_32[0] = pattern;
	}

	/* Small delay */
	busy_wait(1);

	/* Read RX 32-bit tranfers */
	for (i=0; i<16; i++){
		pattern = spi_rx_32[0];
		if ((spi_mmap_ctrl_rx_status_read() >> 16) != (16-1-i))
			errors++;
	}

	if ((spi_mmap_ctrl_tx_status_read() >> 16) != 0)
		errors++;
	if ((spi_mmap_ctrl_rx_status_read() >> 16) != 0)
		errors++;

	/* Disable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE);

	/* Result */
	printf("errors: %d\n", errors);
}

/* Test SPI TX/RX IRQs */
void test_spi_tx_rx_irqs(void) {
	volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE;
	volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE;

	int errors = 0;
	int data __attribute__((unused));

	printf("Test SPI TX/RX IRQs...\n");

	/* Configure Slots */
	spi_mmap_ctrl_slot_control0_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								128 * SPI_SLOT_DIVIDER
	);

	/* Enable TX/RX EventManager */
	spi_mmap_ev_enable_write(0);
	spi_mmap_ev_pending_write(spi_mmap_ev_pending_read());
	spi_mmap_ev_enable_write(SPI_SLOT_EV_TX | SPI_SLOT_EV_RX);

	/* Enable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE);

	/* TX 32-bit transfers */
	spi_tx_32[0] = 0x00000001;

	/* Small delay */
	busy_wait(1);

	/* Verify TX/RX events */
	if (spi_mmap_ev_pending_read() != (SPI_SLOT_EV_TX | SPI_SLOT_EV_RX))
		errors++;

	/* Read RX 32-bit tranfers */
	data = spi_rx_32[0];

	/* Clear events */
	spi_mmap_ev_pending_write(spi_mmap_ev_pending_read());

	/* Verify TX/RX events */
	if (spi_mmap_ev_pending_read() != 0)
		errors++;

	/* Disable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE);

	/* Result */
	printf("errors: %d\n", errors);
}


/* Test SPI Back-to-Back */
void test_spi_back_to_back(void) {
	volatile unsigned int *spi_tx_32 = (unsigned int *)SPI_TX_BASE;
	volatile unsigned int *spi_rx_32 = (unsigned int *)SPI_RX_BASE;

	int errors = 0;

	printf("Test SPI Back-to-Back...\n");

	/* Configure Slots */
	spi_mmap_ctrl_slot_control0_write(
								  1 * SPI_SLOT_ENABLE   |
					SPI_SLOT_MODE_0 * SPI_SLOT_MODE     |
				SPI_SLOT_LENGTH_32B * SPI_SLOT_LENGTH   |
		SPI_SLOT_BITORDER_MSB_FIRST * SPI_SLOT_BITORDER |
								  1 * SPI_SLOT_LOOPBACK |
								  8 * SPI_SLOT_DIVIDER
	);

	/* Enable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(1 * SPI_TX_RX_ENGINE_ENABLE);

	/* TX 32-bit transfers */
	spi_tx_32[0] = 0x00000001;
	spi_tx_32[0] = 0x00000002;

	/* Small delay */
	busy_wait(1);

	/* Read RX 32-bit tranfers */
	if (spi_rx_32[0] != 0x00000001)
		errors++;
	if (spi_rx_32[0] != 0x00000002)
		errors++;

	/* Disable SPI Engine */
	spi_mmap_tx_rx_engine_control_write(0 * SPI_TX_RX_ENGINE_ENABLE);

	/* Result */
	printf("errors: %d\n", errors);
}
  • Loading branch information
enjoy-digital committed Aug 4, 2023
1 parent 036193d commit 688dae0
Show file tree
Hide file tree
Showing 2 changed files with 699 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
- litepcie/endpoint/tlp : Added optional Configuration/PTM TLP support to Packetizer/Depacketizer.
- liteth/arp : Added proper multi-entries ARP table.
- liteiclink/serdes : Added tx/rx_clk sharing capabilities on Xilinx transceivers.
- soc/cores/spi : Added new SPIMMAP core allowing SPI accesses through MMAP.

[> Changed
----------
Expand Down
Loading

0 comments on commit 688dae0

Please sign in to comment.