Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion locale/circuitpython.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-02-07 10:02-0500\n"
"POT-Creation-Date: 2020-02-11 19:18-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand Down
8 changes: 7 additions & 1 deletion ports/nrf/boards/common.template.ld
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ MEMORY
/* SoftDevice 6.1.0 with 5 connections and various increases takes just under 64kiB.
/* To measure the minimum required amount of memory for given configuration, set this number
high enough to work and then check the mutation of the value done by sd_ble_enable. */
RAM (xrw) : ORIGIN = 0x20000000 + 64K, LENGTH = 256K - 64K
SPIM3_RAM (rw) : ORIGIN = 0x20000000 + 64K, LENGTH = 8K
RAM (xrw) : ORIGIN = 0x20000000 + 64K + 8K, LENGTH = 256K - 64K -8K

}

/* produce a link error if there is not this amount of RAM available */
Expand All @@ -37,6 +39,10 @@ _estack = ORIGIN(RAM) + LENGTH(RAM);
_ram_end = ORIGIN(RAM) + LENGTH(RAM);
_heap_end = 0x20020000; /* tunable */

/* nrf52840 SPIM3 needs its own area to work around hardware problems. Nothing else may use this space. */
_spim3_ram = ORIGIN(SPIM3_RAM);
_spim3_ram_end = ORIGIN(SPIM3_RAM) + LENGTH(RAM);

/* define output sections */
SECTIONS
{
Expand Down
72 changes: 55 additions & 17 deletions ports/nrf/common-hal/busio/SPI.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,47 +22,55 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <string.h>

#include "shared-bindings/busio/SPI.h"
#include "py/mperrno.h"
#include "py/runtime.h"

#include "nrfx_spim.h"
#include "nrf_gpio.h"

// These are in order from ighest available frequency to lowest (32MHz first, then 8MHz).
STATIC spim_peripheral_t spim_peripherals[] = {
#if NRFX_CHECK(NRFX_SPIM3_ENABLED)
// SPIM3 exists only on nRF52840 and supports 32MHz max. All other SPIM's are only 8MHz max.
// Allocate SPIM3 first.
{ .spim = NRFX_SPIM_INSTANCE(3),
.max_frequency_MHz = 32,
.max_frequency = 32000000,
.max_xfer_size = SPIM3_EASYDMA_MAXCNT_SIZE,
},
#endif
#if NRFX_CHECK(NRFX_SPIM2_ENABLED)
// SPIM2 is not shared with a TWIM, so allocate before the shared ones.
{ .spim = NRFX_SPIM_INSTANCE(2),
.max_frequency_MHz = 8,
.max_frequency = 8000000,
.max_xfer_size = SPIM2_EASYDMA_MAXCNT_SIZE,
},
#endif
#if NRFX_CHECK(NRFX_SPIM1_ENABLED)
// SPIM1 and TWIM1 share an address.
{ .spim = NRFX_SPIM_INSTANCE(1),
.max_frequency_MHz = 8,
.max_frequency = 8000000,
.max_xfer_size = SPIM1_EASYDMA_MAXCNT_SIZE,
},
#endif
#if NRFX_CHECK(NRFX_SPIM0_ENABLED)
// SPIM0 and TWIM0 share an address.
{ .spim = NRFX_SPIM_INSTANCE(0),
.max_frequency_MHz = 8,
.max_frequency = 8000000,
.max_xfer_size = SPIM0_EASYDMA_MAXCNT_SIZE,
},
#endif
};

STATIC bool never_reset[MP_ARRAY_SIZE(spim_peripherals)];

// Separate RAM area for SPIM3 transmit buffer to avoid SPIM3 hardware errata.
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52840_Rev2%2FERR%2FnRF52840%2FRev2%2Flatest%2Fanomaly_840_198.html
extern uint32_t _spim3_ram;
STATIC uint8_t *spim3_transmit_buffer = (uint8_t *) &_spim3_ram;

void spi_reset(void) {
for (size_t i = 0 ; i < MP_ARRAY_SIZE(spim_peripherals); i++) {
if (never_reset[i]) {
Expand Down Expand Up @@ -122,7 +130,7 @@ static nrf_spim_frequency_t baudrate_to_spim_frequency(const uint32_t baudrate)
}

void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t * clock, const mcu_pin_obj_t * mosi, const mcu_pin_obj_t * miso) {
// Find a free instance.
// Find a free instance, with most desirable (highest freq and not shared) allocated first.
self->spim_peripheral = NULL;
for (size_t i = 0 ; i < MP_ARRAY_SIZE(spim_peripherals); i++) {
if ((spim_peripherals[i].spim.p_reg->ENABLE & SPIM_ENABLE_ENABLE_Msk) == 0) {
Expand All @@ -137,7 +145,8 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *

nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG(NRFX_SPIM_PIN_NOT_USED, NRFX_SPIM_PIN_NOT_USED,
NRFX_SPIM_PIN_NOT_USED, NRFX_SPIM_PIN_NOT_USED);
config.frequency = NRF_SPIM_FREQ_8M;

config.frequency = baudrate_to_spim_frequency(self->spim_peripheral->max_frequency);

config.sck_pin = clock->number;
self->clock_pin_number = clock->number;
Expand Down Expand Up @@ -189,8 +198,7 @@ bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, ui

// Set desired frequency, rounding down, and don't go above available frequency for this SPIM.
nrf_spim_frequency_set(self->spim_peripheral->spim.p_reg,
baudrate_to_spim_frequency(MIN(baudrate,
self->spim_peripheral->max_frequency_MHz * 1000000)));
baudrate_to_spim_frequency(MIN(baudrate, self->spim_peripheral->max_frequency)));

nrf_spim_mode_t mode = NRF_SPIM_MODE_0;
if (polarity) {
Expand Down Expand Up @@ -224,21 +232,36 @@ void common_hal_busio_spi_unlock(busio_spi_obj_t *self) {
}

bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) {
if (len == 0)
if (len == 0) {
return true;
}

const bool is_spim3 = self->spim_peripheral->spim.p_reg == NRF_SPIM3;

const uint32_t max_xfer_size = self->spim_peripheral->max_xfer_size;
const uint32_t parts = len / max_xfer_size;
const uint32_t remainder = len % max_xfer_size;

for (uint32_t i = 0; i < parts; ++i) {
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(data + i * max_xfer_size, max_xfer_size);
uint8_t *start = (uint8_t *) (data + i * max_xfer_size);
if (is_spim3) {
// If SPIM3, copy into unused RAM block, and do DMA from there.
memcpy(spim3_transmit_buffer, start, max_xfer_size);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should put the displayio buffer in this ram as well. That way we could check the location of the buffer and skip the memcpy.

Copy link
Collaborator Author

@dhalbert dhalbert Feb 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds like a good idea, since the 8kB is otherwise wasted. In fact, in the current implementation, we are only using the first 8 bytes or something like that. But we have to allocate the whole 8kB because otherwise there could be errors caused by contention on the block.

The buffer would be in that space conditionally; you'd check that the display SPIM is SPIM3 when making the decision.

start = spim3_transmit_buffer;
}
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(start, max_xfer_size);
if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS)
return false;
}

if (remainder > 0) {
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(data + parts * max_xfer_size, remainder);
uint8_t *start = (uint8_t *) (data + parts * max_xfer_size);
if (is_spim3) {
// If SPIM3, copy into unused RAM block, and do DMA from there.
memcpy(spim3_transmit_buffer, start, remainder);
start = spim3_transmit_buffer;
}
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TX(start, remainder);
if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS)
return false;
}
Expand All @@ -247,8 +270,9 @@ bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size
}

bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) {
if (len == 0)
if (len == 0) {
return true;
}

const uint32_t max_xfer_size = self->spim_peripheral->max_xfer_size;
const uint32_t parts = len / max_xfer_size;
Expand All @@ -270,23 +294,37 @@ bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len,
}

bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, uint8_t *data_out, uint8_t *data_in, size_t len) {
if (len == 0)
if (len == 0) {
return true;
}

const bool is_spim3 = self->spim_peripheral->spim.p_reg == NRF_SPIM3;

const uint32_t max_xfer_size = self->spim_peripheral->max_xfer_size;
const uint32_t parts = len / max_xfer_size;
const uint32_t remainder = len % max_xfer_size;

for (uint32_t i = 0; i < parts; ++i) {
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(data_out + i * max_xfer_size, max_xfer_size,
uint8_t *out_start = (uint8_t *) (data_out + i * max_xfer_size);
if (is_spim3) {
// If SPIM3, copy into unused RAM block, and do DMA from there.
memcpy(spim3_transmit_buffer, out_start, max_xfer_size);
out_start = spim3_transmit_buffer;
}
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(out_start, max_xfer_size,
data_in + i * max_xfer_size, max_xfer_size);
if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS)
return false;
}

if (remainder > 0) {
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(data_out + parts * max_xfer_size, remainder,
uint8_t *out_start = (uint8_t *) (data_out + parts * max_xfer_size);
if (is_spim3) {
// If SPIM3, copy into unused RAM block, and do DMA from there.
memcpy(spim3_transmit_buffer, out_start, remainder);
out_start = spim3_transmit_buffer;
}
const nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_SINGLE_XFER(out_start, remainder,
data_in + parts * max_xfer_size, remainder);
if (nrfx_spim_xfer(&self->spim_peripheral->spim, &xfer, 0) != NRFX_SUCCESS)
return false;
Expand Down Expand Up @@ -325,9 +363,9 @@ uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t* self) {
}

uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t* self) {
return 0;
return (self->spim_peripheral->spim.p_reg->CONFIG & SPIM_CONFIG_CPHA_Msk) >> SPIM_CONFIG_CPHA_Pos;
}

uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t* self) {
return 0;
return (self->spim_peripheral->spim.p_reg->CONFIG & SPIM_CONFIG_CPOL_Msk) >> SPIM_CONFIG_CPOL_Pos;
}
2 changes: 1 addition & 1 deletion ports/nrf/common-hal/busio/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

typedef struct {
nrfx_spim_t spim;
uint8_t max_frequency_MHz;
uint32_t max_frequency;
uint8_t max_xfer_size;
} spim_peripheral_t;

Expand Down
18 changes: 8 additions & 10 deletions ports/nrf/nrfx_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,9 @@

// CIRCUITPY_NRF_NUM_I2C is 1 or 2 to choose how many I2C (TWIM) peripherals
// to provide.
// This can go away once we have SPIM3 working: then we can have two
// I2C and two SPI.
// With SPIM3 working we can have two I2C and two SPI.
#ifndef CIRCUITPY_NRF_NUM_I2C
#define CIRCUITPY_NRF_NUM_I2C 1
#define CIRCUITPY_NRF_NUM_I2C 2
#endif

#if CIRCUITPY_NRF_NUM_I2C != 1 && CIRCUITPY_NRF_NUM_I2C != 2
Expand All @@ -42,13 +41,12 @@
#define NRFX_SPIM1_ENABLED 1
#endif
#define NRFX_SPIM2_ENABLED 1
// DON'T ENABLE SPIM3 DUE TO ANOMALY WORKAROUND FAILURE (SEE ABOVE).
// #ifdef NRF52840_XXAA
// #define NRFX_SPIM_EXTENDED_ENABLED 1
// #define NRFX_SPIM3_ENABLED 1
// #else
// #define NRFX_SPIM3_ENABLED 0
// #endif
#ifdef NRF52840_XXAA
#define NRFX_SPIM_EXTENDED_ENABLED 1
#define NRFX_SPIM3_ENABLED 1
#else
#define NRFX_SPIM3_ENABLED 0
#endif


#define NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY 7
Expand Down
7 changes: 7 additions & 0 deletions shared-bindings/busio/SPI.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@
//|
//| Construct an SPI object on the given pins.
//|
//| ..note:: The SPI peripherals allocated in order of desirability, if possible,
//| such as highest speed and not shared use first. For instance, on the nRF52840,
//| there is a single 32MHz SPI peripheral, and multiple 8MHz peripherals,
//| some of which may also be used for I2C. The 32MHz SPI peripheral is returned
//| first, then the exclusive 8MHz SPI peripheral, and finally the shared 8MHz
//| peripherals.
//|
//| .. seealso:: Using this class directly requires careful lock management.
//| Instead, use :class:`~adafruit_bus_device.spi_device.SPIDevice` to
//| manage locks.
Expand Down