parent
a2c463deb0
commit
24a5752f94
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
32
atmel-samd/flash_api.h
Normal file
32
atmel-samd/flash_api.h
Normal file
@ -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__
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user