summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--atmel-samd/asf_conf/conf_dma.h2
-rw-r--r--atmel-samd/boards/circuitplayground_express/mpconfigboard.h5
-rw-r--r--atmel-samd/boards/feather_m0_express/mpconfigboard.h3
-rw-r--r--atmel-samd/boards/metro_m0_express/mpconfigboard.h3
-rw-r--r--atmel-samd/flash_api.h32
-rw-r--r--atmel-samd/internal_flash.c4
-rw-r--r--atmel-samd/main.c5
-rw-r--r--atmel-samd/shared_dma.c118
-rw-r--r--atmel-samd/shared_dma.h6
-rw-r--r--atmel-samd/spi_flash.c24
10 files changed, 184 insertions, 18 deletions
diff --git a/atmel-samd/asf_conf/conf_dma.h b/atmel-samd/asf_conf/conf_dma.h
index 82e59025d..afe790a64 100644
--- a/atmel-samd/asf_conf/conf_dma.h
+++ b/atmel-samd/asf_conf/conf_dma.h
@@ -46,6 +46,6 @@
#ifndef CONF_DMA_H_INCLUDED
#define CONF_DMA_H_INCLUDED
-# define CONF_MAX_USED_CHANNEL_NUM 2
+# define CONF_MAX_USED_CHANNEL_NUM 3
#endif
diff --git a/atmel-samd/boards/circuitplayground_express/mpconfigboard.h b/atmel-samd/boards/circuitplayground_express/mpconfigboard.h
index 375c28ddd..5eee39574 100644
--- a/atmel-samd/boards/circuitplayground_express/mpconfigboard.h
+++ b/atmel-samd/boards/circuitplayground_express/mpconfigboard.h
@@ -3,9 +3,8 @@
#define MICROPY_HW_BOARD_NAME "Adafruit CircuitPlayground Express"
#define MICROPY_HW_MCU_NAME "samd21g18"
-//#define MICROPY_HW_LED_MSC PIN_PA17
-
-#define SPI_FLASH_BAUDRATE (1000000)
+// Salae reads 12mhz which is the limit even though we set it to the safer 8mhz.
+#define SPI_FLASH_BAUDRATE (8000000)
// On-board flash
#define SPI_FLASH_MUX_SETTING SPI_SIGNAL_MUX_SETTING_E
diff --git a/atmel-samd/boards/feather_m0_express/mpconfigboard.h b/atmel-samd/boards/feather_m0_express/mpconfigboard.h
index 04b836b4e..b18de9544 100644
--- a/atmel-samd/boards/feather_m0_express/mpconfigboard.h
+++ b/atmel-samd/boards/feather_m0_express/mpconfigboard.h
@@ -5,7 +5,8 @@
#define MICROPY_HW_NEOPIXEL (&pin_PA06)
-#define SPI_FLASH_BAUDRATE (1000000)
+// Salae reads 12mhz which is the limit even though we set it to the safer 8mhz.
+#define SPI_FLASH_BAUDRATE (8000000)
#define SPI_FLASH_MUX_SETTING SPI_SIGNAL_MUX_SETTING_C
#define SPI_FLASH_PAD0_PINMUX PINMUX_PA08D_SERCOM2_PAD0 // MOSI
diff --git a/atmel-samd/boards/metro_m0_express/mpconfigboard.h b/atmel-samd/boards/metro_m0_express/mpconfigboard.h
index c7a53bcdb..ddee4ff70 100644
--- a/atmel-samd/boards/metro_m0_express/mpconfigboard.h
+++ b/atmel-samd/boards/metro_m0_express/mpconfigboard.h
@@ -8,7 +8,8 @@
#define MICROPY_HW_NEOPIXEL (&pin_PA30)
-#define SPI_FLASH_BAUDRATE (1000000)
+// Salae reads 12mhz which is the limit even though we set it to the safer 8mhz.
+#define SPI_FLASH_BAUDRATE (8000000)
#define SPI_FLASH_MUX_SETTING SPI_SIGNAL_MUX_SETTING_F
#define SPI_FLASH_PAD0_PINMUX PINMUX_DEFAULT // CS
diff --git a/atmel-samd/flash_api.h b/atmel-samd/flash_api.h
new file mode 100644
index 000000000..2847023d6
--- /dev/null
+++ b/atmel-samd/flash_api.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef __MICROPY_INCLUDED_ATMEL_SAMD_FLASH_API_H__
+#define __MICROPY_INCLUDED_ATMEL_SAMD_FLASH_API_H__
+
+extern void flash_init_vfs(fs_user_mount_t *vfs);
+extern void flash_flush(void);
+
+#endif // __MICROPY_INCLUDED_ATMEL_SAMD_FLASH_API_H__
diff --git a/atmel-samd/internal_flash.c b/atmel-samd/internal_flash.c
index 1ce38f0b0..9046edc4c 100644
--- a/atmel-samd/internal_flash.c
+++ b/atmel-samd/internal_flash.c
@@ -68,6 +68,10 @@ uint32_t internal_flash_get_block_count(void) {
void internal_flash_flush(void) {
}
+void flash_flush(void) {
+ internal_flash_flush();
+}
+
static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_block, uint32_t num_blocks) {
buf[0] = boot;
diff --git a/atmel-samd/main.c b/atmel-samd/main.c
index bc2979898..9ed1c3cd1 100644
--- a/atmel-samd/main.c
+++ b/atmel-samd/main.c
@@ -39,6 +39,7 @@
#endif
#include "autoreset.h"
+#include "flash_api.h"
#include "mpconfigboard.h"
#include "rgb_led_status.h"
#include "shared_dma.h"
@@ -66,8 +67,6 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) {
}
}
-extern void flash_init_vfs(fs_user_mount_t *vfs);
-
// we don't make this function static because it needs a lot of stack and we
// want it to be executed without using stack within main() function
void init_flash_fs(void) {
@@ -93,6 +92,8 @@ void init_flash_fs(void) {
vfs->flags &= ~FSUSER_USB_WRITEABLE;
res = f_mkfs("/flash", 0, 0);
+ // Flush the new file system to make sure its repaired immediately.
+ flash_flush();
if (res != FR_OK) {
MP_STATE_PORT(fs_user_mount)[0] = NULL;
return;
diff --git a/atmel-samd/shared_dma.c b/atmel-samd/shared_dma.c
index b4bbdd07a..d7063fb6b 100644
--- a/atmel-samd/shared_dma.c
+++ b/atmel-samd/shared_dma.c
@@ -23,14 +23,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
#include "shared_dma.h"
+#include "asf/sam0/drivers/system/interrupt/system_interrupt.h"
+
// We allocate two DMA resources for the entire lifecycle of the board (not the
// vm) because the general_dma resource will be shared between the REPL and SPI
// flash. Both uses must block each other in order to prevent conflict.
struct dma_resource audio_dma;
-struct dma_resource general_dma;
+struct dma_resource general_dma_tx;
+struct dma_resource general_dma_rx;
void init_shared_dma(void) {
struct dma_resource_config config;
@@ -45,10 +47,118 @@ void init_shared_dma(void) {
// Turn on the transfer complete interrupt so that the job_status changes to done.
g_chan_interrupt_flag[audio_dma.channel_id] |= (1UL << DMA_CALLBACK_TRANSFER_DONE);
+ // Prioritize the RX channel over the TX channel because TX can cause an RX
+ // overflow.
dma_get_config_defaults(&config);
- dma_allocate(&general_dma, &config);
+ config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
+ config.event_config.input_action = DMA_EVENT_INPUT_TRIG;
+ dma_allocate(&general_dma_rx, &config);
+ g_chan_interrupt_flag[general_dma_rx.channel_id] |= (1UL << DMA_CALLBACK_TRANSFER_DONE);
+
+ dma_get_config_defaults(&config);
+ config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
+ config.event_config.input_action = DMA_EVENT_INPUT_TRIG;
+ dma_allocate(&general_dma_tx, &config);
+ g_chan_interrupt_flag[general_dma_tx.channel_id] |= (1UL << DMA_CALLBACK_TRANSFER_DONE);
// Be sneaky and reuse the active descriptor memory.
audio_dma.descriptor = &descriptor_section[audio_dma.channel_id];
- general_dma.descriptor = &descriptor_section[general_dma.channel_id];
+ general_dma_rx.descriptor = &descriptor_section[general_dma_rx.channel_id];
+ general_dma_tx.descriptor = &descriptor_section[general_dma_tx.channel_id];
+}
+
+static uint8_t sercom_index(Sercom* sercom) {
+ return ((uint32_t) sercom - (uint32_t) SERCOM0) / 0x400;
+}
+
+static void dma_configure(uint8_t channel, uint8_t trigsrc) {
+ system_interrupt_enter_critical_section();
+ /** Select the DMA channel and clear software trigger */
+ DMAC->CHID.reg = DMAC_CHID_ID(channel);
+ DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
+ DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST;
+ DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << channel));
+ DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(DMA_PRIORITY_LEVEL_0) | \
+ DMAC_CHCTRLB_TRIGSRC(trigsrc) | \
+ DMAC_CHCTRLB_TRIGACT(DMA_TRIGGER_ACTION_BEAT);
+ system_interrupt_leave_critical_section();
+}
+
+enum status_code shared_dma_write(Sercom* sercom, const uint8_t* buffer, uint32_t length) {
+ if (general_dma_tx.job_status != STATUS_OK) {
+ return general_dma_tx.job_status;
+ }
+ dma_configure(general_dma_tx.channel_id, sercom_index(sercom) * 2 + 2);
+
+ // Set up TX second.
+ struct dma_descriptor_config descriptor_config;
+ dma_descriptor_get_config_defaults(&descriptor_config);
+ descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
+ descriptor_config.dst_increment_enable = false;
+ descriptor_config.block_transfer_count = length;
+ descriptor_config.source_address = ((uint32_t)buffer + length);
+ // DATA register is consistently addressed across all SERCOM modes.
+ descriptor_config.destination_address = ((uint32_t)&sercom->SPI.DATA.reg);
+
+ dma_descriptor_create(general_dma_tx.descriptor, &descriptor_config);
+ enum status_code status = dma_start_transfer_job(&general_dma_tx);
+ if (status != STATUS_OK) {
+ return status;
+ }
+
+ // Wait for the transfer to finish.
+ while (general_dma_tx.job_status == STATUS_BUSY) {}
+
+ // This transmit will cause the RX buffer overflow but we're OK with that.
+ // So, read the garbage data and clear the overflow flag.
+ sercom->SPI.DATA.reg;
+ sercom->SPI.DATA.reg;
+ sercom->SPI.STATUS.bit.BUFOVF = 1;
+ sercom->SPI.DATA.reg;
+
+ return general_dma_tx.job_status;
+}
+
+enum status_code shared_dma_read(Sercom* sercom, uint8_t* buffer, uint32_t length, uint8_t tx) {
+ if (general_dma_tx.job_status != STATUS_OK) {
+ return general_dma_tx.job_status;
+ }
+
+ dma_configure(general_dma_tx.channel_id, sercom_index(sercom) * 2 + 2);
+ dma_configure(general_dma_rx.channel_id, sercom_index(sercom) * 2 + 1);
+
+ // Set up RX first.
+ struct dma_descriptor_config descriptor_config;
+ dma_descriptor_get_config_defaults(&descriptor_config);
+ descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
+ descriptor_config.src_increment_enable = false;
+ descriptor_config.block_transfer_count = length;
+ // DATA register is consistently addressed across all SERCOM modes.
+ descriptor_config.source_address = ((uint32_t)&sercom->SPI.DATA.reg);
+ descriptor_config.destination_address = ((uint32_t)buffer + length);
+
+ dma_descriptor_create(general_dma_rx.descriptor, &descriptor_config);
+
+ // Set up TX to retransmit the same byte over and over.
+ dma_descriptor_get_config_defaults(&descriptor_config);
+ descriptor_config.beat_size = DMA_BEAT_SIZE_BYTE;
+ descriptor_config.src_increment_enable = false;
+ descriptor_config.dst_increment_enable = false;
+ descriptor_config.block_transfer_count = length;
+ descriptor_config.source_address = ((uint32_t)&tx);
+ // DATA register is consistently addressed across all SERCOM modes.
+ descriptor_config.destination_address = ((uint32_t)&sercom->SPI.DATA.reg);
+
+ dma_descriptor_create(general_dma_tx.descriptor, &descriptor_config);
+
+ // Start the RX job first so we don't miss the first byte. The TX job clocks
+ // the output.
+ general_dma_rx.transfered_size = 0;
+ dma_start_transfer_job(&general_dma_rx);
+ general_dma_tx.transfered_size = 0;
+ dma_start_transfer_job(&general_dma_tx);
+
+ // Wait for the transfer to finish.
+ while (general_dma_rx.job_status == STATUS_BUSY) {}
+ return general_dma_rx.job_status;
}
diff --git a/atmel-samd/shared_dma.h b/atmel-samd/shared_dma.h
index 409b06bac..190001b6b 100644
--- a/atmel-samd/shared_dma.h
+++ b/atmel-samd/shared_dma.h
@@ -30,8 +30,12 @@
#include "asf/sam0/drivers/dma/dma.h"
extern struct dma_resource audio_dma;
-extern struct dma_resource general_dma;
+extern struct dma_resource general_dma_tx;
+extern struct dma_resource general_dma_rx;
void init_shared_dma(void);
+enum status_code shared_dma_write(Sercom* sercom, const uint8_t* buffer, uint32_t length);
+enum status_code shared_dma_read(Sercom* sercom, uint8_t* buffer, uint32_t length, uint8_t tx);
+
#endif // __MICROPY_INCLUDED_ATMEL_SAMD_SHARED_DMA_H__
diff --git a/atmel-samd/spi_flash.c b/atmel-samd/spi_flash.c
index 6ed8e0cf8..812e4359e 100644
--- a/atmel-samd/spi_flash.c
+++ b/atmel-samd/spi_flash.c
@@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
- * Copyright (c) 2013, 2014 Damien P. George
+ * Copyright (c) 2016, 2017 Scott Shawcroft for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -37,6 +37,7 @@
#include "extmod/fsusermount.h"
#include "rgb_led_status.h"
+#include "shared_dma.h"
#define SPI_FLASH_PART1_START_BLOCK (0x1)
@@ -46,6 +47,7 @@
#define CMD_READ_DATA 0x03
#define CMD_SECTOR_ERASE 0x20
// #define CMD_SECTOR_ERASE CMD_READ_JEDEC_ID
+#define CMD_DISABLE_WRITE 0x04
#define CMD_ENABLE_WRITE 0x06
#define CMD_PAGE_PROGRAM 0x02
// #define CMD_PAGE_PROGRAM CMD_READ_JEDEC_ID
@@ -124,7 +126,7 @@ static bool read_flash(uint32_t address, uint8_t* data, uint32_t data_length) {
flash_enable();
status = spi_write_buffer_wait(&spi_flash_instance, read_request, 4);
if (status == STATUS_OK) {
- status = spi_read_buffer_wait(&spi_flash_instance, data, data_length, 0x00);
+ status = shared_dma_read(spi_flash_instance.hw, data, data_length, 0x00);
}
flash_disable();
return status == STATUS_OK;
@@ -149,7 +151,7 @@ static bool write_flash(uint32_t address, const uint8_t* data, uint32_t data_len
enum status_code status;
status = spi_write_buffer_wait(&spi_flash_instance, command, 4);
if (status == STATUS_OK) {
- status = spi_write_buffer_wait(&spi_flash_instance, data + bytes_written, page_size);
+ status = shared_dma_write(spi_flash_instance.hw, data + bytes_written, page_size);
}
flash_disable();
if (status != STATUS_OK) {
@@ -222,9 +224,8 @@ void spi_flash_init(void) {
uint8_t jedec_id_request[4] = {CMD_READ_JEDEC_ID, 0x00, 0x00, 0x00};
uint8_t response[4] = {0x00, 0x00, 0x00, 0x00};
flash_enable();
- volatile enum status_code status = spi_transceive_buffer_wait(&spi_flash_instance, jedec_id_request, response, 4);
+ spi_transceive_buffer_wait(&spi_flash_instance, jedec_id_request, response, 4);
flash_disable();
- (void) status;
if (response[1] == 0x01 && response[2] == 0x40 && response[3] == 0x15) {
flash_size = 1 << 21; // 2 MiB
sector_size = 1 << 12; // 4 KiB
@@ -234,6 +235,15 @@ void spi_flash_init(void) {
flash_size = 0;
}
+ // Turn off writes in case this is a microcontroller only reset.
+ uint8_t disable_write_request[1] = {CMD_DISABLE_WRITE};
+ uint8_t disable_response[1] = {0x00};
+ flash_enable();
+ spi_transceive_buffer_wait(&spi_flash_instance, disable_write_request, disable_response, 1);
+ flash_disable();
+
+ wait_for_flash_ready();
+
current_sector = NO_SECTOR_LOADED;
dirty_mask = 0;
MP_STATE_VM(flash_ram_cache) = NULL;
@@ -404,6 +414,10 @@ void spi_flash_flush(void) {
spi_flash_flush_keep_cache(false);
}
+void flash_flush(void) {
+ spi_flash_flush();
+}
+
// Builds a partition entry for the MBR.
static void build_partition(uint8_t *buf, int boot, int type,
uint32_t start_block, uint32_t num_blocks) {