diff --git a/Makefile b/Makefile index 4af847029ee7b2667fb9c14747e3825eb1196688..6c8092c395ce60ea16c806f71d2a1011ae067d4a 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ ## Makefile for Miosix np embedded OS ## TFT:Terraneo Federico Technlogies ## +MAKEFILE_VERSION := 1.01 include miosix/config/Makefile.inc ## @@ -33,15 +34,15 @@ INCLUDE_DIRS := OBJ := $(addsuffix .o, $(basename $(SRC))) ## Includes the miosix base directory for C/C++ -CXXFLAGS := $(CXXFLAGS_BASE) -I. -I./miosix -I./miosix/$(ARCH_INC) \ - -I./miosix/$(BOARD_INC) $(INCLUDE_DIRS) -CFLAGS := $(CFLAGS_BASE) -I. -I./miosix -I./miosix/$(ARCH_INC) \ - -I./miosix/$(BOARD_INC) $(INCLUDE_DIRS) +CXXFLAGS := $(CXXFLAGS_BASE) -I. -Imiosix -Imiosix/arch/common \ + -Imiosix/$(ARCH_INC) -Imiosix/$(BOARD_INC) $(INCLUDE_DIRS) +CFLAGS := $(CFLAGS_BASE) -I. -Imiosix -Imiosix/arch/common \ + -Imiosix/$(ARCH_INC) -Imiosix/$(BOARD_INC) $(INCLUDE_DIRS) AFLAGS := $(AFLAGS_BASE) LFLAGS := $(LFLAGS_BASE) -LINK_LIBS := $(LIBS) -Wl,--start-group -L./miosix -lmiosix -lstdc++ -lc -lm \ - -lg -lgcc -Wl,--end-group +LINK_LIBS := $(LIBS) -L./miosix -Wl,--start-group -lmiosix -lstdc++ -lc -lm \ + -lgcc -Wl,--end-group all: all-recursive main @@ -70,8 +71,7 @@ main: main.elf main.elf: $(OBJ) miosix/libmiosix.a @ echo "linking" - $(CXX) $(LFLAGS) -o main.elf $(OBJ) miosix/$(BOOT_FILE) \ - miosix/kernel/syscalls.o $(LINK_LIBS) + $(CXX) $(LFLAGS) -o main.elf $(OBJ) miosix/$(BOOT_FILE) $(LINK_LIBS) %.o: %.s $(AS) $(AFLAGS) $< -o $@ diff --git a/miosix/Makefile b/miosix/Makefile index 73a59a75192a793ec4fd161f51cf98cb6a005362..99dcedb8b24303825cef4b723d6dd676bb6c0a3e 100644 --- a/miosix/Makefile +++ b/miosix/Makefile @@ -3,6 +3,7 @@ ## TFT:Terraneo Federico Technlogies ## This makefile builds the whole kernel ## +MAKEFILE_VERSION := 1.01 include config/Makefile.inc ## List of all Miosix OS source files that have no special requirements @@ -15,6 +16,7 @@ kernel/error.cpp \ kernel/pthread.cpp \ kernel/unistd.cpp \ kernel/stage_2_boot.cpp \ +kernel/syscalls.cpp \ kernel/scheduler/priority/priority_scheduler.cpp \ kernel/scheduler/control/control_scheduler.cpp \ kernel/scheduler/edf/edf_scheduler.cpp \ @@ -34,18 +36,18 @@ SRC += $(ARCH_SRC) OBJ := $(addsuffix .o, $(basename $(SRC))) ## Includes the miosix base directory for C/C++ -CXXFLAGS := $(CXXFLAGS_BASE) -I. -I./$(ARCH_INC) -I./$(BOARD_INC) -CFLAGS := $(CFLAGS_BASE) -I. -I./$(ARCH_INC) -I./$(BOARD_INC) +CXXFLAGS := $(CXXFLAGS_BASE) -I. -Iarch/common -I$(ARCH_INC) -I$(BOARD_INC) +CFLAGS := $(CFLAGS_BASE) -I. -Iarch/common -I$(ARCH_INC) -I$(BOARD_INC) AFLAGS := $(AFLAGS_BASE) -## Build libmiosix.a, syscalls.o and stage_1_boot.o (whose path is in BOOT_FILE) -## The files syscalls.o and stage_1_boot.o are compiled separately because -## they must not end up in libmiosix.a -all: $(OBJ) $(BOOT_FILE) kernel/syscalls.o +## Build libmiosix.a and stage_1_boot.o (whose path is in BOOT_FILE) +## The file stage_1_boot.o is compiled separately because +## it must not end up in libmiosix.a +all: $(OBJ) $(BOOT_FILE) $(AR) rcs libmiosix.a $(OBJ) clean: - rm $(OBJ) $(BOOT_FILE) kernel/syscalls.o libmiosix.a + rm $(OBJ) $(BOOT_FILE) libmiosix.a %.o: %.s $(AS) $(AFLAGS) $< -o $@ diff --git a/miosix/arch/cortexM3_stm32/common/drivers/dcc.cpp b/miosix/arch/common/drivers/dcc.cpp similarity index 100% rename from miosix/arch/cortexM3_stm32/common/drivers/dcc.cpp rename to miosix/arch/common/drivers/dcc.cpp diff --git a/miosix/arch/cortexM3_stm32/common/drivers/dcc.h b/miosix/arch/common/drivers/dcc.h similarity index 100% rename from miosix/arch/cortexM3_stm32/common/drivers/dcc.h rename to miosix/arch/common/drivers/dcc.h diff --git a/miosix/arch/cortexM3_stm32/stm32f103ve_mp3v2/interfaces-impl/disk.cpp b/miosix/arch/cortexM3_stm32/common/interfaces-impl/disk.cpp similarity index 99% rename from miosix/arch/cortexM3_stm32/stm32f103ve_mp3v2/interfaces-impl/disk.cpp rename to miosix/arch/cortexM3_stm32/common/interfaces-impl/disk.cpp index 5bc803ee745c2354913d2155390df9b658edf07a..e8073292bec55d342f3022e49f6befe80c64245f 100644 --- a/miosix/arch/cortexM3_stm32/stm32f103ve_mp3v2/interfaces-impl/disk.cpp +++ b/miosix/arch/cortexM3_stm32/common/interfaces-impl/disk.cpp @@ -1,4 +1,3 @@ - /*************************************************************************** * Copyright (C) 2010 by Terraneo Federico * * * diff --git a/miosix/arch/cortexM3_stm32/stm32f103ve_strive_mini/interfaces-impl/disk.cpp b/miosix/arch/cortexM3_stm32/stm32f103ve_strive_mini/interfaces-impl/disk.cpp deleted file mode 100644 index 5bc803ee745c2354913d2155390df9b658edf07a..0000000000000000000000000000000000000000 --- a/miosix/arch/cortexM3_stm32/stm32f103ve_strive_mini/interfaces-impl/disk.cpp +++ /dev/null @@ -1,1390 +0,0 @@ - -/*************************************************************************** - * Copyright (C) 2010 by Terraneo Federico * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * As a special exception, if other files instantiate templates or use * - * macros or inline functions from this file, or you compile this file * - * and link it with other works to produce a work based on this file, * - * this file does not by itself cause the resulting work to be covered * - * by the GNU General Public License. However the source code for this * - * file must still be made available in accordance with the GNU General * - * Public License. This exception does not invalidate any other reasons * - * why a work based on this file might be covered by the GNU General * - * Public License. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see <http://www.gnu.org/licenses/> * - ***************************************************************************/ - -#include "interfaces/disk.h" -#include "CMSIS/stm32f10x.h" -#include "CMSIS/core_cm3.h" -#include "interfaces/bsp.h" -#include "interfaces/delays.h" -#include "kernel/kernel.h" -#include <cstdio> -#include <cstring> - -//Note: enabling debugging might cause deadlock when using sleep() or reboot() -//The bug won't be fixed because debugging is only useful for driver development -///\internal Debug macro, for normal conditions -//#define DBG iprintf -#define DBG(x,...) ; -///\internal Debug macro, for errors only -//#define DBGERR iprintf -#define DBGERR(x,...) ; - -namespace miosix { - -/* - * Operating voltage of device. It is sent to the SD card to check if it can - * work at this voltage. Range *must* be within 28..36 - * Example 33=3.3v - */ -const unsigned char VOLTAGE=33; -const unsigned int VOLTAGE_MASK=1<<(VOLTAGE-13); //See OCR register in SD spec - -/** - * \internal - * Possible state of the cardType variable. - */ -enum CardType -{ - Invalid=0, ///<\internal Invalid card type - MMC=1<<0, ///<\internal if(cardType==MMC) card is an MMC - SDv1=1<<1, ///<\internal if(cardType==SDv1) card is an SDv1 - SDv2=1<<2, ///<\internal if(cardType==SDv2) card is an SDv2 - SDHC=1<<3 ///<\internal if(cardType==SDHC) card is an SDHC -}; - -///\internal Type of card. This variable is set in Disk::init() -static CardType cardType=Invalid; - -//SD card GPIOs -typedef Gpio<GPIOC_BASE,8> sdD0; -typedef Gpio<GPIOC_BASE,9> sdD1; -typedef Gpio<GPIOC_BASE,10> sdD2; -typedef Gpio<GPIOC_BASE,11> sdD3; -typedef Gpio<GPIOC_BASE,12> sdCLK; -typedef Gpio<GPIOD_BASE,2> sdCMD; - -// -// Class BufferConverter -// - -/** - * \internal - * Convert a single buffer of *fixed* and predetermined size to and from - * word-aligned. To do so, if the buffer is already word aligned a cast is made, - * otherwise a new buffer is allocated. - * Note that this class allocates at most ONE buffer at any given time. - * Therefore any call to toWordAligned(), toWordAlignedWithoutCopy(), - * toOriginalBuffer() or deallocateBuffer() invalidates the buffer previousy - * returned by toWordAligned() and toWordAlignedWithoutCopy() - */ -class BufferConverter -{ -public: - /** - * \internal - * The buffer will be of this size only. - */ - static const int BUFFER_SIZE=512; - - /** - * \internal - * \return true if the pointer is word aligned - */ - static bool isWordAligned(const unsigned char *x) - { - return (reinterpret_cast<const unsigned int>(x) & 0x3)==0; - } - - /** - * \internal - * Convert from a constunsigned char* buffer of size BUFFER_SIZE to a - * const unsigned int* word aligned buffer. - * If the original buffer is already word aligned it only does a cast, - * otherwise it copies the data on the original buffer to a word aligned - * buffer. Useful if subseqent code will read from the buffer. - * \param a buffer of size BUFFER_SIZE. Can be word aligned or not. - * \return a word aligned buffer with the same data of the given buffer - */ - static const unsigned int *toWordAligned(const unsigned char *buffer); - - /** - * \internal - * Convert from an unsigned char* buffer of size BUFFER_SIZE to an - * unsigned int* word aligned buffer. - * If the original buffer is already word aligned it only does a cast, - * otherwise it returns a new buffer which *does not* contain the data - * on the original buffer. Useful if subseqent code will write to the - * buffer. To move the written data to the original buffer, use - * toOriginalBuffer() - * \param a buffer of size BUFFER_SIZE. Can be word aligned or not. - * \return a word aligned buffer with undefined content. - */ - static unsigned int *toWordAlignedWithoutCopy(unsigned char *buffer); - - /** - * \internal - * Convert the buffer got through toWordAlignedWithoutCopy() to the - * original buffer. If the original buffer was word aligned, nothing - * happens, otherwise a memcpy is done. - * Note that this function does not work on buffers got through - * toWordAligned(). - */ - static void toOriginalBuffer(); - - /** - * \internal - * Can be called to deallocate the buffer - */ - static void deallocateBuffer(); - -private: - static unsigned char *originalBuffer; - static unsigned int *wordAlignedBuffer; -}; - -const unsigned int *BufferConverter::toWordAligned(const unsigned char *buffer) -{ - originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do - if(isWordAligned(buffer)) - { - return reinterpret_cast<const unsigned int*>(buffer); - } else { - if(wordAlignedBuffer==0) - wordAlignedBuffer=new unsigned int[BUFFER_SIZE/sizeof(unsigned int)]; - std::memcpy(wordAlignedBuffer,buffer,BUFFER_SIZE); - return wordAlignedBuffer; - } -} - -unsigned int *BufferConverter::toWordAlignedWithoutCopy( - unsigned char *buffer) -{ - if(isWordAligned(buffer)) - { - originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do - return reinterpret_cast<unsigned int*>(buffer); - } else { - originalBuffer=buffer; //Save original pointer for toOriginalBuffer() - if(wordAlignedBuffer==0) - wordAlignedBuffer=new unsigned int[BUFFER_SIZE/sizeof(unsigned int)]; - return wordAlignedBuffer; - } -} - -void BufferConverter::toOriginalBuffer() -{ - if(originalBuffer==0) return; - std::memcpy(originalBuffer,wordAlignedBuffer,BUFFER_SIZE); - originalBuffer=0; -} - -void BufferConverter::deallocateBuffer() -{ - originalBuffer=0; //Invalidate also original buffer - if(wordAlignedBuffer!=0) - { - delete[] wordAlignedBuffer; - wordAlignedBuffer=0; - } -} - -unsigned char *BufferConverter::originalBuffer=0; -unsigned int *BufferConverter::wordAlignedBuffer=0; - -// -// Class CmdResult -// - -/** - * \internal - * Contains the result of an SD/MMC command - */ -class CmdResult -{ -public: - - /** - * \internal - * Possible outcomes of sending a command - */ - enum Error - { - Ok=0, /// No errors - Timeout, /// Timeout while waiting command reply - CRCFail, /// CRC check failed in command reply - RespNotMatch,/// Response index does not match command index - ACMDFail /// Sending CMD55 failed - }; - - /** - * \internal - * Default constructor - */ - CmdResult(): cmd(0), error(Ok), response(0) {} - - /** - * \internal - * Constructor, set the response data - * \param cmd command index of command that was sent - * \param result result of command - */ - CmdResult(unsigned char cmd, Error error): cmd(cmd), error(error), - response(SDIO->RESP1) {} - - /** - * \internal - * \return the 32 bit of the response. - * May not be valid if getError()!=Ok or the command does not send a - * response, such as CMD0 - */ - unsigned int getResponse() { return response; } - - /** - * \internal - * \return command index - */ - unsigned char getCmdIndex() { return cmd; } - - /** - * \internal - * \return the error flags of the response - */ - Error getError() { return error; } - - /** - * \internal - * Checks if errors occurred while sending the command. - * \return true if no errors, false otherwise - */ - bool validateError(); - - /** - * \internal - * interprets this->getResponse() as an R1 response, and checks if there are - * errors, or everything is ok - * \return true on success, false on failure - */ - bool validateR1Response(); - - /** - * \internal - * Same as validateR1Response, but can be called with interrupts disabled. - * \return true on success, false on failure - */ - bool IRQvalidateR1Response(); - - /** - * \internal - * interprets this->getResponse() as an R6 response, and checks if there are - * errors, or everything is ok - * \return true on success, false on failure - */ - bool validateR6Response(); - - /** - * \internal - * \return the card state from an R1 or R6 resonse - */ - unsigned char getState(); - -private: - unsigned char cmd; ///<\internal Command index that was sent - Error error; ///<\internal possible error that occurred - unsigned int response; ///<\internal 32bit response -}; - -bool CmdResult::validateError() -{ - switch(error) - { - case Ok: - return true; - case Timeout: - DBGERR("CMD%d: Timeout\n",cmd); - break; - case CRCFail: - DBGERR("CMD%d: CRC Fail\n",cmd); - break; - case RespNotMatch: - DBGERR("CMD%d: Response does not match\n",cmd); - break; - case ACMDFail: - DBGERR("CMD%d: ACMD Fail\n",cmd); - break; - } - return false; -} - -bool CmdResult::validateR1Response() -{ - if(error!=Ok) return validateError(); - //Note: this number is obtained with all the flags of R1 which are errors - //(flagged as E in the SD specification), plus CARD_IS_LOCKED because - //locked card are not supported by this software driver - if((response & 0xfff98008)==0) return true; - DBGERR("CMD%d: R1 response error(s):\n",cmd); - if(response & (1<<31)) DBGERR("Out of range\n"); - if(response & (1<<30)) DBGERR("ADDR error\n"); - if(response & (1<<29)) DBGERR("BLOCKLEN error\n"); - if(response & (1<<28)) DBGERR("ERASE SEQ error\n"); - if(response & (1<<27)) DBGERR("ERASE param\n"); - if(response & (1<<26)) DBGERR("WP violation\n"); - if(response & (1<<25)) DBGERR("card locked\n"); - if(response & (1<<24)) DBGERR("LOCK_UNLOCK failed\n"); - if(response & (1<<23)) DBGERR("command CRC failed\n"); - if(response & (1<<22)) DBGERR("illegal command\n"); - if(response & (1<<21)) DBGERR("ECC fail\n"); - if(response & (1<<20)) DBGERR("card controller error\n"); - if(response & (1<<19)) DBGERR("unknown error\n"); - if(response & (1<<16)) DBGERR("CSD overwrite\n"); - if(response & (1<<15)) DBGERR("WP ERASE skip\n"); - if(response & (1<<3)) DBGERR("AKE_SEQ error\n"); - return false; -} - -bool CmdResult::IRQvalidateR1Response() -{ - if(error!=Ok) return false; - if(response & 0xfff98008) return false; - return true; -} - -bool CmdResult::validateR6Response() -{ - if(error!=Ok) return validateError(); - if((response & 0xe008)==0) return true; - DBGERR("CMD%d: R6 response error(s):\n",cmd); - if(response & (1<<15)) DBGERR("command CRC failed\n"); - if(response & (1<<14)) DBGERR("illegal command\n"); - if(response & (1<<13)) DBGERR("unknown error\n"); - if(response & (1<<3)) DBGERR("AKE_SEQ error\n"); - return false; -} - -unsigned char CmdResult::getState() -{ - unsigned char result=(response>>9) & 0xf; - DBG("CMD%d: State: ",cmd); - switch(result) - { - case 0: DBG("Idle\n"); break; - case 1: DBG("Ready\n"); break; - case 2: DBG("Ident\n"); break; - case 3: DBG("Stby\n"); break; - case 4: DBG("Tran\n"); break; - case 5: DBG("Data\n"); break; - case 6: DBG("Rcv\n"); break; - case 7: DBG("Prg\n"); break; - case 8: DBG("Dis\n"); break; - case 9: DBG("Btst\n"); break; - default: DBG("Unknown\n"); break; - } - return result; -} - -// -// Class Command -// - -/** - * \internal - * This class allows sending commands to an SD or MMC - */ -class Command -{ -public: - - /** - * \internal - * SD/MMC commands - * - bit #7 is @ 1 if a command is an ACMDxx. send() will send the - * sequence CMD55, CMDxx - * - bit from #0 to #5 indicate command index (CMD0..CMD63) - * - bit #6 is don't care - */ - enum CommandType - { - CMD0=0, //GO_IDLE_STATE - CMD2=2, //ALL_SEND_CID - CMD3=3, //SEND_RELATIVE_ADDR - ACMD6=0x80 | 6, //SET_BUS_WIDTH - CMD7=7, //SELECT_DESELECT_CARD - ACMD41=0x80 | 41, //SEND_OP_COND (SD) - CMD8=8, //SEND_IF_COND - CMD9=9, //SEND_CSD - CMD12=12, //STOP_TRANSMISSION - CMD13=13, //SEND_STATUS - CMD16=16, //SET_BLOCKLEN - CMD17=17, //READ_SINGLE_BLOCK - CMD18=18, //READ_MULTIPLE_BLOCK - ACMD23=0x80 | 23, //SET_WR_BLK_ERASE_COUNT (SD) - CMD24=24, //WRITE_BLOCK - CMD25=25, //WRITE_MULTIPLE_BLOCK - CMD55=55 //APP_CMD - }; - - /** - * \internal - * Send a command. - * \param cmd command index (CMD0..CMD63) or ACMDxx command - * \param arg the 32 bit argument to the command - * \return a CmdResult object - */ - static CmdResult send(CommandType cmd, unsigned int arg) - { - if(static_cast<unsigned char>(cmd) & 0x80) - { - DBG("ACMD%d\n",static_cast<unsigned char>(cmd) & 0x3f); - } else { - DBG("CMD%d\n",static_cast<unsigned char>(cmd) & 0x3f); - } - return IRQsend(cmd,arg); - } - - /** - * \internal - * Send a command. Can be called with interrupts disabled as it does not - * print any debug information. - * \param cmd command index (CMD0..CMD63) or ACMDxx command - * \param arg the 32 bit argument to the command - * \return a CmdResult object - */ - static CmdResult IRQsend(CommandType cmd, unsigned int arg); - - /** - * \internal - * Set the relative card address, obtained during initialization. - * \param r the card's rca - */ - static void setRca(unsigned short r) { rca=r; } - - /** - * \internal - * \return the card's rca, as set by setRca - */ - static unsigned int getRca() { return static_cast<unsigned int>(rca); } - -private: - static unsigned short rca;///<\internal Card's relative address -}; - -CmdResult Command::IRQsend(CommandType cmd, unsigned int arg) -{ - unsigned char cc=static_cast<unsigned char>(cmd); - //Handle ACMDxx as CMD55, CMDxx - if(cc & 0x80) - { - CmdResult r=IRQsend(CMD55,(static_cast<unsigned int>(rca))<<16); - if(r.IRQvalidateR1Response()==false) - return CmdResult(cc & 0x3f,CmdResult::ACMDFail); - //Bit 5 @ 1 = next command will be interpreted as ACMD - if((r.getResponse() & (1<<5))==0) - return CmdResult(cc & 0x3f,CmdResult::ACMDFail); - } - - //Send command - cc &= 0x3f; - unsigned int command=SDIO_CMD_CPSMEN | static_cast<unsigned int>(cc); - if(cc!=CMD0) command |= SDIO_CMD_WAITRESP_0; //CMD0 has no response - if(cc==CMD2) command |= SDIO_CMD_WAITRESP_1; //CMD2 has long response - if(cc==CMD9) command |= SDIO_CMD_WAITRESP_1; //CMD9 has long response - SDIO->ARG=arg; - SDIO->CMD=command; - - //CMD0 has no response, so wait until it is sent - if(cc==CMD0) - { - for(int i=0;i<500;i++) - { - if(SDIO->STA & SDIO_STA_CMDSENT) - { - SDIO->ICR=0x7ff;//Clear flags - return CmdResult(cc,CmdResult::Ok); - } - delayUs(1); - } - SDIO->ICR=0x7ff;//Clear flags - return CmdResult(cc,CmdResult::Timeout); - } - - //Command is not CMD0, so wait a reply - for(int i=0;i<500;i++) - { - unsigned int status=SDIO->STA; - if(status & SDIO_STA_CMDREND) - { - SDIO->ICR=0x7ff;//Clear flags - if(SDIO->RESPCMD==cc) return CmdResult(cc,CmdResult::Ok); - else return CmdResult(cc,CmdResult::RespNotMatch); - } - if(status & SDIO_STA_CCRCFAIL) - { - SDIO->ICR=SDIO_ICR_CCRCFAILC; - return CmdResult(cc,CmdResult::CRCFail); - } - if(status & SDIO_STA_CTIMEOUT) break; - delayUs(1); - } - SDIO->ICR=SDIO_ICR_CTIMEOUTC; - return CmdResult(cc,CmdResult::Timeout); -} - -unsigned short Command::rca=0; - -// -// Class DataResult -// - -/** - * \internal - * Contains the result of sending/receiving a data block - */ -class DataResult -{ -public: - - /** - * \internal - * Possible outcomes of sending or receiving data - */ - enum Error - { - Ok=0, - Timeout, - CRCFail, - RXOverrun, - TXUnderrun, - StartBitFail - }; - - /** - * \internal - * Default constructor - */ - DataResult(): error(Ok) {} - - /** - * \internal - * Constructor, set the result. - * \param error error type - */ - DataResult(Error error): error(error) {} - - /** - * \internal - * \return the error flags - */ - Error getError() { return error; } - - /** - * \internal - * Checks if errors occurred while sending/receiving data. - * \return true if no errors, false otherwise - */ - bool validateError(); - -private: - Error error; -}; - - -bool DataResult::validateError() -{ - switch(error) - { - case Ok: - return true; - case Timeout: - DBGERR("Data Timeout\n"); - break; - case CRCFail: - DBGERR("Data CRC Fail\n"); - break; - case RXOverrun: - DBGERR("Data overrun\n"); - break; - case TXUnderrun: - DBGERR("Data underrun\n"); - break; - case StartBitFail: - DBGERR("Data start bit Fail\n"); - break; - } - return false; -} - -// -// Class ClockController -// - -/** - * \internal - * This class controls the clock speed of the SDIO peripheral. The SDIO - * peripheral, when used in polled mode, requires two timing critical pieces of - * code: the one to send and the one to receive a data block. This because - * the peripheral has a 128 byte fifo while the block size is 512 byte, and - * if fifo underrun/overrun occurs the peripheral does not pause communcation, - * instead it simply aborts the data transfer. Since the speed of the code to - * read/write a data block depends on too many factors, such as compiler - * optimizations, code running from internal flash or external ram, and the - * cpu clock speed, a dynamic clocking approach was chosen. - */ -class ClockController -{ -public: - - /** - * \internal. Set a low clock speed of 400KHz or less, used for - * detecting SD/MMC cards. This function as a side effect enables 1bit bus - * width, and disables clock powersave, since it is not allowed by SD spec. - */ - static void setLowSpeedClock() - { - clockReductionAvailable=0; - // No hardware flow control, SDIO_CK generated on rising edge, 1bit bus - // width, no clock bypass, no powersave. - // Set low clock speed 400KHz, 72MHz/400KHz-2=178 - SDIO->CLKCR=CLOCK_400KHz; - SDIO->CLKCR |= SDIO_CLKCR_CLKEN; - } - - /** - * \internal - * Automatically select the data speed. - * Since the maximum speed depends on many factors, such as code running in - * internal or external RAM, compiler optimizations etc. this routine - * selects the highest sustainable data transfer speed. - * This is done by binary search until the highest clock speed that causes - * no errors is found. - * This function as a side effect enables 4bit bus width, and clock - * powersave. - */ - static void calibrateClockSpeed(); - - /** - * \internal - * Since clock speed is set dynamically by bynary search at runtime, a - * corner case might be that of a clock speed which results in unreliable - * data transfer, that sometimes succeeds, and sometimes fail. - * For maximum robustness, this function is provided to reduce the clock - * speed slightly in case a data transfer should fail after clock - * calibration. To avoid inadvertently considering other kind of issues as - * clock issues, this function can be called only MAX_ALLOWED_REDUCTIONS - * times after clock calibration, subsequent calls will fail. This will - * avoid other issues causing an ever decreasing clock speed. - * Can be called with interrupts disabled. - * \return true on success, false on failure - */ - static bool IRQreduceClockSpeed(); - - /** - * \internal - * Read and write operation do retry during normal use for robustness, but - * during clock claibration they must not retry for speed reasons. This - * member function returns 1 during clock claibration and MAX_RETRY during - * normal use. - */ - static unsigned char getRetryCount() { return retries; } - -private: - - /** - * \internal - * Value of SDIO->CLKCR that will give a 400KHz clock, depending on cpu - * clock speed. - */ - #ifdef SYSCLK_FREQ_72MHz - static const unsigned int CLOCK_400KHz=178; - #elif SYSCLK_FREQ_56MHz - static const unsigned int CLOCK_400KHz=138; - #elif SYSCLK_FREQ_48MHz - static const unsigned int CLOCK_400KHz=118; - #elif SYSCLK_FREQ_36MHz - static const unsigned int CLOCK_400KHz=88; - #elif SYSCLK_FREQ_24MHz - static const unsigned int CLOCK_400KHz=58; - #else - static const unsigned int CLOCK_400KHz=18; - #endif - - ///\internal Clock enabled, bus width 4bit, clock powersave enabled. - static const unsigned int CLKCR_FLAGS=SDIO_CLKCR_CLKEN | - SDIO_CLKCR_WIDBUS_0 | SDIO_CLKCR_PWRSAV; - - ///\internal Maximum number of calls to IRQreduceClockSpeed() allowed - static const unsigned char MAX_ALLOWED_REDUCTIONS=5; - - ///\internl value returned by getRetryCount() while *not* calibrating clock. - static const unsigned char MAX_RETRY=3; - - ///\internal Used to allow only one call to reduceClockSpeed() - static unsigned char clockReductionAvailable; - - static unsigned char retries; -}; - -void ClockController::calibrateClockSpeed() -{ - //During calibration we call Disk::read which will call reduceClockSpeed() - //so not to invalidate calibration clock reduction must not be available - clockReductionAvailable=0; - retries=1; - - DBG("Automatic speed calibration\n"); - unsigned int buffer[512/sizeof(unsigned int)]; - unsigned int minFreq=CLOCK_400KHz; //400KHz, independent of CPU clock - unsigned int maxFreq=1; //24MHz with CPU running @ 72MHz - unsigned int selected; - while(minFreq-maxFreq>1) - { - selected=(minFreq+maxFreq)/2; - DBG("Trying CLKCR=%d\n",selected); - SDIO->CLKCR=selected; - SDIO->CLKCR |= CLKCR_FLAGS; - if(Disk::read(reinterpret_cast<unsigned char*>(buffer),0,1)) - minFreq=selected; - else maxFreq=selected; - } - //Last round of algorithm - SDIO->CLKCR=maxFreq; - SDIO->CLKCR |= CLKCR_FLAGS; - if(Disk::read(reinterpret_cast<unsigned char*>(buffer),0,1)) - { - DBG("Optimal CLKCR=%d\n",maxFreq); - } else { - SDIO->CLKCR=minFreq; - SDIO->CLKCR |= CLKCR_FLAGS; - DBG("Optimal CLKCR=%d\n",minFreq); - } - - //Make clock reduction available - clockReductionAvailable=MAX_ALLOWED_REDUCTIONS; - retries=MAX_RETRY; -} - -bool ClockController::IRQreduceClockSpeed() -{ - //Ensure this function can be called only twice per calibration - if(clockReductionAvailable==0) return false; - clockReductionAvailable--; - - unsigned int currentClkcr=SDIO->CLKCR & 0xff; - if(currentClkcr==CLOCK_400KHz) return false; //No lower than this value - - //If the value of clockcr is low, increasing it by one is enough since - //frequency changes a lot, otherwise increase by 2. - if(currentClkcr<10) currentClkcr++; - else currentClkcr+=2; - - SDIO->CLKCR=currentClkcr; - SDIO->CLKCR |= CLKCR_FLAGS; - return true; -} - -unsigned char ClockController::clockReductionAvailable=false; -unsigned char ClockController::retries=ClockController::MAX_RETRY; - -// -// Data send/receive functions -// - -/** - * \internal - * Receive a data block. The end of the data block must be told to the SDIO - * peripheral in SDIO->DLEN and must match the size parameter given to this - * function. - * \param buffer buffer where to store received data. Its size must be >=size - * \param buffer size, which *must* be multiple of 8 words (32bytes) - * Note that the size parameter must be expressed in word (4bytes), while - * the value in SDIO->DLEN is expressed in bytes. - * \return a DataResult object - */ -static DataResult IRQreceiveDataBlock(unsigned int *buffer, unsigned int size) -{ - // A note on speed. - // Due to the auto calibration of SDIO clock speed being done with - // IRQreceiveDataBlock(), the speed of this function must be comparable - // with the speed of IRQsendDataBlock(), otherwise IRQsendDataBlock() - // will fail because of data underrun. - const unsigned int *bufend=buffer+size; - unsigned int status; - for(;;) - { - status=SDIO->STA; - if(status & (SDIO_STA_RXOVERR | SDIO_STA_DCRCFAIL | - SDIO_STA_DTIMEOUT | SDIO_STA_STBITERR | SDIO_STA_DBCKEND)) break; - if((status & SDIO_STA_RXFIFOHF) && (buffer!=bufend)) - { - //Read 8 words from the fifo, loop entirely unrolled for speed - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - } - } - SDIO->ICR=0x7ff;//Clear flags - if(status & SDIO_STA_RXOVERR) return DataResult(DataResult::RXOverrun); - if(status & SDIO_STA_DCRCFAIL) return DataResult(DataResult::CRCFail); - if(status & SDIO_STA_DTIMEOUT) return DataResult(DataResult::Timeout); - if(status & SDIO_STA_STBITERR) return DataResult(DataResult::StartBitFail); - //Read eventual data left in the FIFO - for(;;) - { - if((SDIO->STA & SDIO_STA_RXDAVL)==0) break; - *buffer=SDIO->FIFO; buffer++; - } - return DataResult(DataResult::Ok); -} - -/** - * \internal - * Send a data block. The end of the data block must be told to the SDIO - * peripheral in SDIO->DLEN and must match the size parameter given to this - * function. - * \param buffer buffer where to store received data. Its size must be >=size - * \param buffer size, which *must* be multiple of 8 words (32bytes). - * Note that the size parameter must be expressed in word (4bytes), while - * the value in SDIO->DLEN is expressed in bytes. - * \return a DataResult object - */ -static DataResult IRQsendDataBlock(const unsigned int *buffer, unsigned int size) -{ - // A note on speed. - // Due to the auto calibration of SDIO clock speed being done with - // IRQreceiveDataBlock(), the speed of this function must be comparable - // with the speed of IRQreceiveDataBlock(), otherwise this function - // will fail because of data underrun. - const unsigned int *bufend=buffer+size; - unsigned int status; - for(;;) - { - status=SDIO->STA; - if(status & (SDIO_STA_TXUNDERR | SDIO_STA_DCRCFAIL | - SDIO_STA_DTIMEOUT | SDIO_STA_STBITERR | SDIO_STA_DBCKEND)) break; - if((status & SDIO_STA_TXFIFOHE) && (buffer!=bufend)) - { - //Write 8 words to the fifo, loop entirely unrolled for speed - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - } - } - SDIO->ICR=0x7ff;//Clear flags - if(status & SDIO_STA_TXUNDERR) return DataResult(DataResult::TXUnderrun); - if(status & SDIO_STA_DCRCFAIL) return DataResult(DataResult::CRCFail); - if(status & SDIO_STA_DTIMEOUT) return DataResult(DataResult::Timeout); - if(status & SDIO_STA_STBITERR) return DataResult(DataResult::StartBitFail); - return DataResult(DataResult::Ok); -} - -/** - * \internal - * Wait until the card is ready for data transfer. - * Can be called independently of the card being selected. - * \return true on success, false on failure - */ -bool waitForCardReady() -{ - for(int i=0;i<300;i++) //Timeout 1.5 second - { - CmdResult cr=Command::send(Command::CMD13,Command::getRca()<<16); - if(cr.validateR1Response()==false) return false; - //Bit 8 in R1 response means ready for data. - if(cr.getResponse() & (1<<8)) return true; - Thread::sleep(5); - } - DBGERR("Timeout waiting card ready\n"); - return false; -} - -/** - * \internal - * Read a single block of 512 bytes from an SD/MMC card. - * Card must be selected prior to caling this function. - * \param buffer, a buffer whose size is >=512 bytes - * \param lba logical block address of the block to read. - */ -static bool singleBlockRead(unsigned char *buffer, unsigned int lba) -{ - if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC - - if(waitForCardReady()==false) return false; - - // Single block read - unsigned int* newBuf=BufferConverter::toWordAlignedWithoutCopy(buffer); - CmdResult cr; - DataResult dr; - bool failed=true; - for(;;) - { - // Since we read with polling, a context switch or interrupt here - // would cause a fifo overrun, so we disable interrupts. - FastInterruptDisableLock dLock; - - SDIO->DTIMER=1048576; - SDIO->DLEN=512; - //Block size 512 bytes, block data xfer, from card to controller - SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN; - - cr=Command::IRQsend(Command::CMD17,lba); - if(cr.IRQvalidateR1Response()) - { - dr=IRQreceiveDataBlock(newBuf,512/sizeof(unsigned int)); - SDIO->DCTRL=0; //Disable data path state machine - - //If failed because too slow check if it is possible to reduce speed - if(dr.getError()==DataResult::RXOverrun) - { - if(ClockController::IRQreduceClockSpeed()) - { - //Disabling interrupts for too long is bad - FastInterruptEnableLock eLock(dLock); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - if(cr.validateR1Response()) continue; - } - } - - if(dr.getError()==DataResult::Ok) failed=false; - } - break; - } - if(failed) - { - cr.validateR1Response(); - dr.validateError(); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - cr.validateR1Response(); - return false; - } - BufferConverter::toOriginalBuffer(); - return true; -} - -/** - * \internal - * Write a single block of 512 bytes to an SD/MMC card - * Card must be selected prior to caling this function. - * \param buffer, a buffer whose size is >=512 bytes - * \param lba logical block address of the block to write. - */ -static bool singleBlockWrite(const unsigned char *buffer, unsigned int lba) -{ - if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC - - if(waitForCardReady()==false) return false; - - // Single block write - const unsigned int* newBuf=BufferConverter::toWordAligned(buffer); - bool failed=true; - CmdResult cr; - DataResult dr; - for(;;) - { - // Since we write with polling, a context switch or interrupt here - // would cause a fifo overrun, so we disable interrupts. - FastInterruptDisableLock dLock; - - cr=Command::IRQsend(Command::CMD24,lba); - if(cr.IRQvalidateR1Response()) - { - SDIO->DTIMER=1048576; - SDIO->DLEN=512; - //Block size 512 bytes, block data xfer, from controller to card - SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTEN; - - dr=IRQsendDataBlock(newBuf,512/sizeof(unsigned int)); - SDIO->DCTRL=0; //Disable data path state machine - - //If failed because too slow check if it is possible to reduce speed - if(dr.getError()==DataResult::TXUnderrun) - { - if(ClockController::IRQreduceClockSpeed()) - { - //Disabling interrupts for too long is bad - FastInterruptEnableLock eLock(dLock); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - if(cr.validateR1Response()) continue; - } - } - - if(dr.getError()==DataResult::Ok) failed=false; - } - break; - } - if(failed) - { - cr.validateR1Response(); - dr.validateError(); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - cr.validateR1Response(); - return false; - } - return true; -} - -// -// Class CardSelector -// - -/** - * \internal - * Simple RAII class for selecting an SD/MMC card an automatically deselect it - * at the end of the scope. - */ -class CardSelector -{ -public: - /** - * \internal - * Constructor. Selects the card. - * The result of the select operation is available through its succeded() - * member function - */ - explicit CardSelector() - { - success=Command::send( - Command::CMD7,Command::getRca()<<16).validateR1Response(); - } - - /** - * \internal - * \return true if the card was selected, false on error - */ - bool succeded() { return success; } - - /** - * \internal - * Destructor, ensures that the card is deselected - */ - ~CardSelector() - { - Command::send(Command::CMD7,0); //Deselect card. This will timeout - } - -private: - bool success; -}; - -// -// Initialization helper functions -// - -/** - * \internal - * Datasheet says that there must be at least seven clock cycles between - * two accesses to SDIO->POWER, and this ensures this constraint. - */ -static inline void sevenNop() -{ - __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); -} - -/** - * \internal - * Initialzes the SDIO peripheral in the STM32 - */ -static void initSDIOPeripheral() -{ - { - //Doing read-modify-write on RCC->APBENR2 and gpios, better be safe - FastInterruptDisableLock lock; - RCC->APB2ENR |= RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN; - RCC->AHBENR |= RCC_AHBENR_SDIOEN; - sdD0::mode(Mode::ALTERNATE); - sdD1::mode(Mode::ALTERNATE); - sdD2::mode(Mode::ALTERNATE); - sdD3::mode(Mode::ALTERNATE); - sdCLK::mode(Mode::ALTERNATE); - sdCMD::mode(Mode::ALTERNATE); - } - - SDIO->POWER=0; //Power off state - SDIO->CLKCR=0; - SDIO->CMD=0; - SDIO->DCTRL=0; - SDIO->ICR=0xc007ff; - sevenNop(); - SDIO->POWER=SDIO_POWER_PWRCTRL_1 | SDIO_POWER_PWRCTRL_0; //Power on state - ClockController::setLowSpeedClock(); - sevenNop(); -} - -/** - * \internal - * Detect if the card is an SDHC, SDv2, SDv1, MMC - * \return Type of card: (1<<0)=MMC (1<<1)=SDv1 (1<<2)=SDv2 (1<<2)|(1<<3)=SDHC - * or Invalid if card detect failed. - */ -static CardType detectCardType() -{ - const int INIT_TIMEOUT=200; //200*10ms= 2 seconds - CmdResult r=Command::send(Command::CMD8,0x1aa); - if(r.validateError()) - { - //We have an SDv2 card connected - if(r.getResponse()!=0x1aa) - { - DBGERR("CMD8 validation: voltage range fail\n"); - return Invalid; - } - for(int i=0;i<INIT_TIMEOUT;i++) - { - //Bit 30 @ 1 = tell the card we like SDHCs - r=Command::send(Command::ACMD41,(1<<30) | VOLTAGE_MASK); - //ACMD41 sends R3 as response, whose CRC is wrong. - if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::CRCFail) - { - r.validateError(); - return Invalid; - } - if((r.getResponse() & (1<<31))==0) //Busy bit - { - Thread::sleep(10); - continue; - } - if((r.getResponse() & VOLTAGE_MASK)==0) - { - DBGERR("ACMD41 validation: voltage range fail\n"); - return Invalid; - } - DBG("ACMD41 validation: looped %d times\n",i); - if(r.getResponse() & (1<<30)) - { - DBG("SDHC\n"); - return SDHC; - } else { - DBG("SDv2\n"); - return SDv2; - } - } - DBGERR("ACMD41 validation: looped until timeout\n"); - return Invalid; - } else { - //We have an SDv1 or MMC - r=Command::send(Command::ACMD41,VOLTAGE_MASK); - //ACMD41 sends R3 as response, whose CRC is wrong. - if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::CRCFail) - { - //MMC card - DBG("MMC card\n"); - return MMC; - } else { - //SDv1 card - for(int i=0;i<INIT_TIMEOUT;i++) - { - //ACMD41 sends R3 as response, whose CRC is wrong. - if(r.getError()!=CmdResult::Ok && - r.getError()!=CmdResult::CRCFail) - { - r.validateError(); - return Invalid; - } - if((r.getResponse() & (1<<31))==0) //Busy bit - { - Thread::sleep(10); - //Send again command - r=Command::send(Command::ACMD41,VOLTAGE_MASK); - continue; - } - if((r.getResponse() & VOLTAGE_MASK)==0) - { - DBGERR("ACMD41 validation: voltage range fail\n"); - return Invalid; - } - DBG("ACMD41 validation: looped %d times\nSDv1\n",i); - return SDv1; - } - DBGERR("ACMD41 validation: looped until timeout\n"); - return Invalid; - } - } -} - -// -// Disk class -// - -bool Disk::isAvailable() -{ - bool result=sdCardSense(); - DBG("Disk::isAvailable(): %d\n",result); - return result; -} - -void Disk::init() -{ - initSDIOPeripheral(); - - // This is more important than it seems, since CMD55 requires the card's RCA - // as argument. During initalization, after CMD0 the card has an RCA of zero - // so without this line ACMD41 will fail and the card won't be initialized. - Command::setRca(0); - - //Send card reset command - CmdResult r=Command::send(Command::CMD0,0); - if(r.validateError()==false) return; - - cardType=detectCardType(); - if(cardType==Invalid) return; //Card detect failed - if(cardType==MMC) return; //MMC cards currently unsupported - - // Now give an RCA to the card. In theory we should loop and enumerate all - // the cards but this driver supports only one card. - r=Command::send(Command::CMD2,0); - //CMD2 sends R2 response, whose CMDINDEX field is wrong - if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::RespNotMatch) - { - r.validateError(); - return; - } - r=Command::send(Command::CMD3,0); - if(r.validateR6Response()==false) return; - Command::setRca(r.getResponse()>>16); - DBG("Got RCA=%u\n",Command::getRca()); - if(Command::getRca()==0) - { - //RCA=0 can't be accepted, since it is used to deselect cards - DBGERR("RCA=0 is invalid\n"); - return; - } - - //Lastly, try selecting the card and configure the latest bits - { - CardSelector selector; - if(selector.succeded()==false) return; - - r=Command::send(Command::CMD13,Command::getRca()<<16);//Get status - if(r.validateR1Response()==false) return; - if(r.getState()!=4) //4=Tran state - { - DBGERR("CMD7 was not able to select card\n"); - return; - } - - r=Command::send(Command::ACMD6,2); //Set 4 bit bus width - if(r.validateR1Response()==false) return; - - if(cardType!=SDHC) - { - r=Command::send(Command::CMD16,512); //Set 512Byte block length - if(r.validateR1Response()==false) return; - } - } - - // Now that card is initialized, perform self calibration of maximum - // possible read/write speed. This as a side effect enables 4bit bus width. - ClockController::calibrateClockSpeed(); - - DBG("Disk::init(): Success\n"); - diskInitialized=true; -} - -bool Disk::read(unsigned char *buffer, unsigned int lba, - unsigned char nSectors) -{ - DBG("Disk::read(): nSectors=%d\n",nSectors); - if(!BufferConverter::isWordAligned(buffer)) DBG("Buffer misaligned\n"); - - for(int i=0;i<ClockController::getRetryCount();i++) - { - //Select card - CardSelector selector; - if(selector.succeded()==false) continue; - - if(nSectors==1) - { - if(singleBlockRead(buffer,lba)==false) continue; - } else { - // Multiple block read - // Currently implemented with N calls to single block read - unsigned char *tempBuffer=buffer; - unsigned int tempLba=lba; - for(unsigned int i=0;i<nSectors;i++) - { - if(singleBlockRead(tempBuffer,tempLba)==false) continue; - tempBuffer+=512; - tempLba++; - } - } - if(i>0) DBGERR("Read: required %d retries\n",i); - return true; - } - return false; -} - -bool Disk::write(const unsigned char *buffer, unsigned int lba, - unsigned char nSectors) -{ - DBG("Disk::write(): nSectors=%d\n",nSectors); - if(!BufferConverter::isWordAligned(buffer)) DBG("Buffer misaligned\n"); - - for(int i=0;i<ClockController::getRetryCount();i++) - { - //Select card - CardSelector selector; - if(selector.succeded()==false) continue; - - if(nSectors==1) - { - if(singleBlockWrite(buffer,lba)==false) continue; - } else { - // Multiple block write - // Currently implemented with N calls to single block write - const unsigned char *tempBuffer=buffer; - unsigned int tempLba=lba; - for(unsigned int i=0;i<nSectors;i++) - { - if(singleBlockWrite(tempBuffer,tempLba)==false) continue; - tempBuffer+=512; - tempLba++; - } - } - if(i>0) DBGERR("Write: required %d retries\n",i); - return true; - } - return false; -} - -bool Disk::sync() -{ - DBG("Disk::sync()\n"); - //Note: no need to select card, since status can be queried even with card - //not selected. - return waitForCardReady(); -} - -bool Disk::diskInitialized=false; - -} //namespace miosix diff --git a/miosix/arch/cortexM3_stm32/stm32f103ze_redbull_v2/interfaces-impl/disk.cpp b/miosix/arch/cortexM3_stm32/stm32f103ze_redbull_v2/interfaces-impl/disk.cpp deleted file mode 100644 index 5bc803ee745c2354913d2155390df9b658edf07a..0000000000000000000000000000000000000000 --- a/miosix/arch/cortexM3_stm32/stm32f103ze_redbull_v2/interfaces-impl/disk.cpp +++ /dev/null @@ -1,1390 +0,0 @@ - -/*************************************************************************** - * Copyright (C) 2010 by Terraneo Federico * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * As a special exception, if other files instantiate templates or use * - * macros or inline functions from this file, or you compile this file * - * and link it with other works to produce a work based on this file, * - * this file does not by itself cause the resulting work to be covered * - * by the GNU General Public License. However the source code for this * - * file must still be made available in accordance with the GNU General * - * Public License. This exception does not invalidate any other reasons * - * why a work based on this file might be covered by the GNU General * - * Public License. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see <http://www.gnu.org/licenses/> * - ***************************************************************************/ - -#include "interfaces/disk.h" -#include "CMSIS/stm32f10x.h" -#include "CMSIS/core_cm3.h" -#include "interfaces/bsp.h" -#include "interfaces/delays.h" -#include "kernel/kernel.h" -#include <cstdio> -#include <cstring> - -//Note: enabling debugging might cause deadlock when using sleep() or reboot() -//The bug won't be fixed because debugging is only useful for driver development -///\internal Debug macro, for normal conditions -//#define DBG iprintf -#define DBG(x,...) ; -///\internal Debug macro, for errors only -//#define DBGERR iprintf -#define DBGERR(x,...) ; - -namespace miosix { - -/* - * Operating voltage of device. It is sent to the SD card to check if it can - * work at this voltage. Range *must* be within 28..36 - * Example 33=3.3v - */ -const unsigned char VOLTAGE=33; -const unsigned int VOLTAGE_MASK=1<<(VOLTAGE-13); //See OCR register in SD spec - -/** - * \internal - * Possible state of the cardType variable. - */ -enum CardType -{ - Invalid=0, ///<\internal Invalid card type - MMC=1<<0, ///<\internal if(cardType==MMC) card is an MMC - SDv1=1<<1, ///<\internal if(cardType==SDv1) card is an SDv1 - SDv2=1<<2, ///<\internal if(cardType==SDv2) card is an SDv2 - SDHC=1<<3 ///<\internal if(cardType==SDHC) card is an SDHC -}; - -///\internal Type of card. This variable is set in Disk::init() -static CardType cardType=Invalid; - -//SD card GPIOs -typedef Gpio<GPIOC_BASE,8> sdD0; -typedef Gpio<GPIOC_BASE,9> sdD1; -typedef Gpio<GPIOC_BASE,10> sdD2; -typedef Gpio<GPIOC_BASE,11> sdD3; -typedef Gpio<GPIOC_BASE,12> sdCLK; -typedef Gpio<GPIOD_BASE,2> sdCMD; - -// -// Class BufferConverter -// - -/** - * \internal - * Convert a single buffer of *fixed* and predetermined size to and from - * word-aligned. To do so, if the buffer is already word aligned a cast is made, - * otherwise a new buffer is allocated. - * Note that this class allocates at most ONE buffer at any given time. - * Therefore any call to toWordAligned(), toWordAlignedWithoutCopy(), - * toOriginalBuffer() or deallocateBuffer() invalidates the buffer previousy - * returned by toWordAligned() and toWordAlignedWithoutCopy() - */ -class BufferConverter -{ -public: - /** - * \internal - * The buffer will be of this size only. - */ - static const int BUFFER_SIZE=512; - - /** - * \internal - * \return true if the pointer is word aligned - */ - static bool isWordAligned(const unsigned char *x) - { - return (reinterpret_cast<const unsigned int>(x) & 0x3)==0; - } - - /** - * \internal - * Convert from a constunsigned char* buffer of size BUFFER_SIZE to a - * const unsigned int* word aligned buffer. - * If the original buffer is already word aligned it only does a cast, - * otherwise it copies the data on the original buffer to a word aligned - * buffer. Useful if subseqent code will read from the buffer. - * \param a buffer of size BUFFER_SIZE. Can be word aligned or not. - * \return a word aligned buffer with the same data of the given buffer - */ - static const unsigned int *toWordAligned(const unsigned char *buffer); - - /** - * \internal - * Convert from an unsigned char* buffer of size BUFFER_SIZE to an - * unsigned int* word aligned buffer. - * If the original buffer is already word aligned it only does a cast, - * otherwise it returns a new buffer which *does not* contain the data - * on the original buffer. Useful if subseqent code will write to the - * buffer. To move the written data to the original buffer, use - * toOriginalBuffer() - * \param a buffer of size BUFFER_SIZE. Can be word aligned or not. - * \return a word aligned buffer with undefined content. - */ - static unsigned int *toWordAlignedWithoutCopy(unsigned char *buffer); - - /** - * \internal - * Convert the buffer got through toWordAlignedWithoutCopy() to the - * original buffer. If the original buffer was word aligned, nothing - * happens, otherwise a memcpy is done. - * Note that this function does not work on buffers got through - * toWordAligned(). - */ - static void toOriginalBuffer(); - - /** - * \internal - * Can be called to deallocate the buffer - */ - static void deallocateBuffer(); - -private: - static unsigned char *originalBuffer; - static unsigned int *wordAlignedBuffer; -}; - -const unsigned int *BufferConverter::toWordAligned(const unsigned char *buffer) -{ - originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do - if(isWordAligned(buffer)) - { - return reinterpret_cast<const unsigned int*>(buffer); - } else { - if(wordAlignedBuffer==0) - wordAlignedBuffer=new unsigned int[BUFFER_SIZE/sizeof(unsigned int)]; - std::memcpy(wordAlignedBuffer,buffer,BUFFER_SIZE); - return wordAlignedBuffer; - } -} - -unsigned int *BufferConverter::toWordAlignedWithoutCopy( - unsigned char *buffer) -{ - if(isWordAligned(buffer)) - { - originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do - return reinterpret_cast<unsigned int*>(buffer); - } else { - originalBuffer=buffer; //Save original pointer for toOriginalBuffer() - if(wordAlignedBuffer==0) - wordAlignedBuffer=new unsigned int[BUFFER_SIZE/sizeof(unsigned int)]; - return wordAlignedBuffer; - } -} - -void BufferConverter::toOriginalBuffer() -{ - if(originalBuffer==0) return; - std::memcpy(originalBuffer,wordAlignedBuffer,BUFFER_SIZE); - originalBuffer=0; -} - -void BufferConverter::deallocateBuffer() -{ - originalBuffer=0; //Invalidate also original buffer - if(wordAlignedBuffer!=0) - { - delete[] wordAlignedBuffer; - wordAlignedBuffer=0; - } -} - -unsigned char *BufferConverter::originalBuffer=0; -unsigned int *BufferConverter::wordAlignedBuffer=0; - -// -// Class CmdResult -// - -/** - * \internal - * Contains the result of an SD/MMC command - */ -class CmdResult -{ -public: - - /** - * \internal - * Possible outcomes of sending a command - */ - enum Error - { - Ok=0, /// No errors - Timeout, /// Timeout while waiting command reply - CRCFail, /// CRC check failed in command reply - RespNotMatch,/// Response index does not match command index - ACMDFail /// Sending CMD55 failed - }; - - /** - * \internal - * Default constructor - */ - CmdResult(): cmd(0), error(Ok), response(0) {} - - /** - * \internal - * Constructor, set the response data - * \param cmd command index of command that was sent - * \param result result of command - */ - CmdResult(unsigned char cmd, Error error): cmd(cmd), error(error), - response(SDIO->RESP1) {} - - /** - * \internal - * \return the 32 bit of the response. - * May not be valid if getError()!=Ok or the command does not send a - * response, such as CMD0 - */ - unsigned int getResponse() { return response; } - - /** - * \internal - * \return command index - */ - unsigned char getCmdIndex() { return cmd; } - - /** - * \internal - * \return the error flags of the response - */ - Error getError() { return error; } - - /** - * \internal - * Checks if errors occurred while sending the command. - * \return true if no errors, false otherwise - */ - bool validateError(); - - /** - * \internal - * interprets this->getResponse() as an R1 response, and checks if there are - * errors, or everything is ok - * \return true on success, false on failure - */ - bool validateR1Response(); - - /** - * \internal - * Same as validateR1Response, but can be called with interrupts disabled. - * \return true on success, false on failure - */ - bool IRQvalidateR1Response(); - - /** - * \internal - * interprets this->getResponse() as an R6 response, and checks if there are - * errors, or everything is ok - * \return true on success, false on failure - */ - bool validateR6Response(); - - /** - * \internal - * \return the card state from an R1 or R6 resonse - */ - unsigned char getState(); - -private: - unsigned char cmd; ///<\internal Command index that was sent - Error error; ///<\internal possible error that occurred - unsigned int response; ///<\internal 32bit response -}; - -bool CmdResult::validateError() -{ - switch(error) - { - case Ok: - return true; - case Timeout: - DBGERR("CMD%d: Timeout\n",cmd); - break; - case CRCFail: - DBGERR("CMD%d: CRC Fail\n",cmd); - break; - case RespNotMatch: - DBGERR("CMD%d: Response does not match\n",cmd); - break; - case ACMDFail: - DBGERR("CMD%d: ACMD Fail\n",cmd); - break; - } - return false; -} - -bool CmdResult::validateR1Response() -{ - if(error!=Ok) return validateError(); - //Note: this number is obtained with all the flags of R1 which are errors - //(flagged as E in the SD specification), plus CARD_IS_LOCKED because - //locked card are not supported by this software driver - if((response & 0xfff98008)==0) return true; - DBGERR("CMD%d: R1 response error(s):\n",cmd); - if(response & (1<<31)) DBGERR("Out of range\n"); - if(response & (1<<30)) DBGERR("ADDR error\n"); - if(response & (1<<29)) DBGERR("BLOCKLEN error\n"); - if(response & (1<<28)) DBGERR("ERASE SEQ error\n"); - if(response & (1<<27)) DBGERR("ERASE param\n"); - if(response & (1<<26)) DBGERR("WP violation\n"); - if(response & (1<<25)) DBGERR("card locked\n"); - if(response & (1<<24)) DBGERR("LOCK_UNLOCK failed\n"); - if(response & (1<<23)) DBGERR("command CRC failed\n"); - if(response & (1<<22)) DBGERR("illegal command\n"); - if(response & (1<<21)) DBGERR("ECC fail\n"); - if(response & (1<<20)) DBGERR("card controller error\n"); - if(response & (1<<19)) DBGERR("unknown error\n"); - if(response & (1<<16)) DBGERR("CSD overwrite\n"); - if(response & (1<<15)) DBGERR("WP ERASE skip\n"); - if(response & (1<<3)) DBGERR("AKE_SEQ error\n"); - return false; -} - -bool CmdResult::IRQvalidateR1Response() -{ - if(error!=Ok) return false; - if(response & 0xfff98008) return false; - return true; -} - -bool CmdResult::validateR6Response() -{ - if(error!=Ok) return validateError(); - if((response & 0xe008)==0) return true; - DBGERR("CMD%d: R6 response error(s):\n",cmd); - if(response & (1<<15)) DBGERR("command CRC failed\n"); - if(response & (1<<14)) DBGERR("illegal command\n"); - if(response & (1<<13)) DBGERR("unknown error\n"); - if(response & (1<<3)) DBGERR("AKE_SEQ error\n"); - return false; -} - -unsigned char CmdResult::getState() -{ - unsigned char result=(response>>9) & 0xf; - DBG("CMD%d: State: ",cmd); - switch(result) - { - case 0: DBG("Idle\n"); break; - case 1: DBG("Ready\n"); break; - case 2: DBG("Ident\n"); break; - case 3: DBG("Stby\n"); break; - case 4: DBG("Tran\n"); break; - case 5: DBG("Data\n"); break; - case 6: DBG("Rcv\n"); break; - case 7: DBG("Prg\n"); break; - case 8: DBG("Dis\n"); break; - case 9: DBG("Btst\n"); break; - default: DBG("Unknown\n"); break; - } - return result; -} - -// -// Class Command -// - -/** - * \internal - * This class allows sending commands to an SD or MMC - */ -class Command -{ -public: - - /** - * \internal - * SD/MMC commands - * - bit #7 is @ 1 if a command is an ACMDxx. send() will send the - * sequence CMD55, CMDxx - * - bit from #0 to #5 indicate command index (CMD0..CMD63) - * - bit #6 is don't care - */ - enum CommandType - { - CMD0=0, //GO_IDLE_STATE - CMD2=2, //ALL_SEND_CID - CMD3=3, //SEND_RELATIVE_ADDR - ACMD6=0x80 | 6, //SET_BUS_WIDTH - CMD7=7, //SELECT_DESELECT_CARD - ACMD41=0x80 | 41, //SEND_OP_COND (SD) - CMD8=8, //SEND_IF_COND - CMD9=9, //SEND_CSD - CMD12=12, //STOP_TRANSMISSION - CMD13=13, //SEND_STATUS - CMD16=16, //SET_BLOCKLEN - CMD17=17, //READ_SINGLE_BLOCK - CMD18=18, //READ_MULTIPLE_BLOCK - ACMD23=0x80 | 23, //SET_WR_BLK_ERASE_COUNT (SD) - CMD24=24, //WRITE_BLOCK - CMD25=25, //WRITE_MULTIPLE_BLOCK - CMD55=55 //APP_CMD - }; - - /** - * \internal - * Send a command. - * \param cmd command index (CMD0..CMD63) or ACMDxx command - * \param arg the 32 bit argument to the command - * \return a CmdResult object - */ - static CmdResult send(CommandType cmd, unsigned int arg) - { - if(static_cast<unsigned char>(cmd) & 0x80) - { - DBG("ACMD%d\n",static_cast<unsigned char>(cmd) & 0x3f); - } else { - DBG("CMD%d\n",static_cast<unsigned char>(cmd) & 0x3f); - } - return IRQsend(cmd,arg); - } - - /** - * \internal - * Send a command. Can be called with interrupts disabled as it does not - * print any debug information. - * \param cmd command index (CMD0..CMD63) or ACMDxx command - * \param arg the 32 bit argument to the command - * \return a CmdResult object - */ - static CmdResult IRQsend(CommandType cmd, unsigned int arg); - - /** - * \internal - * Set the relative card address, obtained during initialization. - * \param r the card's rca - */ - static void setRca(unsigned short r) { rca=r; } - - /** - * \internal - * \return the card's rca, as set by setRca - */ - static unsigned int getRca() { return static_cast<unsigned int>(rca); } - -private: - static unsigned short rca;///<\internal Card's relative address -}; - -CmdResult Command::IRQsend(CommandType cmd, unsigned int arg) -{ - unsigned char cc=static_cast<unsigned char>(cmd); - //Handle ACMDxx as CMD55, CMDxx - if(cc & 0x80) - { - CmdResult r=IRQsend(CMD55,(static_cast<unsigned int>(rca))<<16); - if(r.IRQvalidateR1Response()==false) - return CmdResult(cc & 0x3f,CmdResult::ACMDFail); - //Bit 5 @ 1 = next command will be interpreted as ACMD - if((r.getResponse() & (1<<5))==0) - return CmdResult(cc & 0x3f,CmdResult::ACMDFail); - } - - //Send command - cc &= 0x3f; - unsigned int command=SDIO_CMD_CPSMEN | static_cast<unsigned int>(cc); - if(cc!=CMD0) command |= SDIO_CMD_WAITRESP_0; //CMD0 has no response - if(cc==CMD2) command |= SDIO_CMD_WAITRESP_1; //CMD2 has long response - if(cc==CMD9) command |= SDIO_CMD_WAITRESP_1; //CMD9 has long response - SDIO->ARG=arg; - SDIO->CMD=command; - - //CMD0 has no response, so wait until it is sent - if(cc==CMD0) - { - for(int i=0;i<500;i++) - { - if(SDIO->STA & SDIO_STA_CMDSENT) - { - SDIO->ICR=0x7ff;//Clear flags - return CmdResult(cc,CmdResult::Ok); - } - delayUs(1); - } - SDIO->ICR=0x7ff;//Clear flags - return CmdResult(cc,CmdResult::Timeout); - } - - //Command is not CMD0, so wait a reply - for(int i=0;i<500;i++) - { - unsigned int status=SDIO->STA; - if(status & SDIO_STA_CMDREND) - { - SDIO->ICR=0x7ff;//Clear flags - if(SDIO->RESPCMD==cc) return CmdResult(cc,CmdResult::Ok); - else return CmdResult(cc,CmdResult::RespNotMatch); - } - if(status & SDIO_STA_CCRCFAIL) - { - SDIO->ICR=SDIO_ICR_CCRCFAILC; - return CmdResult(cc,CmdResult::CRCFail); - } - if(status & SDIO_STA_CTIMEOUT) break; - delayUs(1); - } - SDIO->ICR=SDIO_ICR_CTIMEOUTC; - return CmdResult(cc,CmdResult::Timeout); -} - -unsigned short Command::rca=0; - -// -// Class DataResult -// - -/** - * \internal - * Contains the result of sending/receiving a data block - */ -class DataResult -{ -public: - - /** - * \internal - * Possible outcomes of sending or receiving data - */ - enum Error - { - Ok=0, - Timeout, - CRCFail, - RXOverrun, - TXUnderrun, - StartBitFail - }; - - /** - * \internal - * Default constructor - */ - DataResult(): error(Ok) {} - - /** - * \internal - * Constructor, set the result. - * \param error error type - */ - DataResult(Error error): error(error) {} - - /** - * \internal - * \return the error flags - */ - Error getError() { return error; } - - /** - * \internal - * Checks if errors occurred while sending/receiving data. - * \return true if no errors, false otherwise - */ - bool validateError(); - -private: - Error error; -}; - - -bool DataResult::validateError() -{ - switch(error) - { - case Ok: - return true; - case Timeout: - DBGERR("Data Timeout\n"); - break; - case CRCFail: - DBGERR("Data CRC Fail\n"); - break; - case RXOverrun: - DBGERR("Data overrun\n"); - break; - case TXUnderrun: - DBGERR("Data underrun\n"); - break; - case StartBitFail: - DBGERR("Data start bit Fail\n"); - break; - } - return false; -} - -// -// Class ClockController -// - -/** - * \internal - * This class controls the clock speed of the SDIO peripheral. The SDIO - * peripheral, when used in polled mode, requires two timing critical pieces of - * code: the one to send and the one to receive a data block. This because - * the peripheral has a 128 byte fifo while the block size is 512 byte, and - * if fifo underrun/overrun occurs the peripheral does not pause communcation, - * instead it simply aborts the data transfer. Since the speed of the code to - * read/write a data block depends on too many factors, such as compiler - * optimizations, code running from internal flash or external ram, and the - * cpu clock speed, a dynamic clocking approach was chosen. - */ -class ClockController -{ -public: - - /** - * \internal. Set a low clock speed of 400KHz or less, used for - * detecting SD/MMC cards. This function as a side effect enables 1bit bus - * width, and disables clock powersave, since it is not allowed by SD spec. - */ - static void setLowSpeedClock() - { - clockReductionAvailable=0; - // No hardware flow control, SDIO_CK generated on rising edge, 1bit bus - // width, no clock bypass, no powersave. - // Set low clock speed 400KHz, 72MHz/400KHz-2=178 - SDIO->CLKCR=CLOCK_400KHz; - SDIO->CLKCR |= SDIO_CLKCR_CLKEN; - } - - /** - * \internal - * Automatically select the data speed. - * Since the maximum speed depends on many factors, such as code running in - * internal or external RAM, compiler optimizations etc. this routine - * selects the highest sustainable data transfer speed. - * This is done by binary search until the highest clock speed that causes - * no errors is found. - * This function as a side effect enables 4bit bus width, and clock - * powersave. - */ - static void calibrateClockSpeed(); - - /** - * \internal - * Since clock speed is set dynamically by bynary search at runtime, a - * corner case might be that of a clock speed which results in unreliable - * data transfer, that sometimes succeeds, and sometimes fail. - * For maximum robustness, this function is provided to reduce the clock - * speed slightly in case a data transfer should fail after clock - * calibration. To avoid inadvertently considering other kind of issues as - * clock issues, this function can be called only MAX_ALLOWED_REDUCTIONS - * times after clock calibration, subsequent calls will fail. This will - * avoid other issues causing an ever decreasing clock speed. - * Can be called with interrupts disabled. - * \return true on success, false on failure - */ - static bool IRQreduceClockSpeed(); - - /** - * \internal - * Read and write operation do retry during normal use for robustness, but - * during clock claibration they must not retry for speed reasons. This - * member function returns 1 during clock claibration and MAX_RETRY during - * normal use. - */ - static unsigned char getRetryCount() { return retries; } - -private: - - /** - * \internal - * Value of SDIO->CLKCR that will give a 400KHz clock, depending on cpu - * clock speed. - */ - #ifdef SYSCLK_FREQ_72MHz - static const unsigned int CLOCK_400KHz=178; - #elif SYSCLK_FREQ_56MHz - static const unsigned int CLOCK_400KHz=138; - #elif SYSCLK_FREQ_48MHz - static const unsigned int CLOCK_400KHz=118; - #elif SYSCLK_FREQ_36MHz - static const unsigned int CLOCK_400KHz=88; - #elif SYSCLK_FREQ_24MHz - static const unsigned int CLOCK_400KHz=58; - #else - static const unsigned int CLOCK_400KHz=18; - #endif - - ///\internal Clock enabled, bus width 4bit, clock powersave enabled. - static const unsigned int CLKCR_FLAGS=SDIO_CLKCR_CLKEN | - SDIO_CLKCR_WIDBUS_0 | SDIO_CLKCR_PWRSAV; - - ///\internal Maximum number of calls to IRQreduceClockSpeed() allowed - static const unsigned char MAX_ALLOWED_REDUCTIONS=5; - - ///\internl value returned by getRetryCount() while *not* calibrating clock. - static const unsigned char MAX_RETRY=3; - - ///\internal Used to allow only one call to reduceClockSpeed() - static unsigned char clockReductionAvailable; - - static unsigned char retries; -}; - -void ClockController::calibrateClockSpeed() -{ - //During calibration we call Disk::read which will call reduceClockSpeed() - //so not to invalidate calibration clock reduction must not be available - clockReductionAvailable=0; - retries=1; - - DBG("Automatic speed calibration\n"); - unsigned int buffer[512/sizeof(unsigned int)]; - unsigned int minFreq=CLOCK_400KHz; //400KHz, independent of CPU clock - unsigned int maxFreq=1; //24MHz with CPU running @ 72MHz - unsigned int selected; - while(minFreq-maxFreq>1) - { - selected=(minFreq+maxFreq)/2; - DBG("Trying CLKCR=%d\n",selected); - SDIO->CLKCR=selected; - SDIO->CLKCR |= CLKCR_FLAGS; - if(Disk::read(reinterpret_cast<unsigned char*>(buffer),0,1)) - minFreq=selected; - else maxFreq=selected; - } - //Last round of algorithm - SDIO->CLKCR=maxFreq; - SDIO->CLKCR |= CLKCR_FLAGS; - if(Disk::read(reinterpret_cast<unsigned char*>(buffer),0,1)) - { - DBG("Optimal CLKCR=%d\n",maxFreq); - } else { - SDIO->CLKCR=minFreq; - SDIO->CLKCR |= CLKCR_FLAGS; - DBG("Optimal CLKCR=%d\n",minFreq); - } - - //Make clock reduction available - clockReductionAvailable=MAX_ALLOWED_REDUCTIONS; - retries=MAX_RETRY; -} - -bool ClockController::IRQreduceClockSpeed() -{ - //Ensure this function can be called only twice per calibration - if(clockReductionAvailable==0) return false; - clockReductionAvailable--; - - unsigned int currentClkcr=SDIO->CLKCR & 0xff; - if(currentClkcr==CLOCK_400KHz) return false; //No lower than this value - - //If the value of clockcr is low, increasing it by one is enough since - //frequency changes a lot, otherwise increase by 2. - if(currentClkcr<10) currentClkcr++; - else currentClkcr+=2; - - SDIO->CLKCR=currentClkcr; - SDIO->CLKCR |= CLKCR_FLAGS; - return true; -} - -unsigned char ClockController::clockReductionAvailable=false; -unsigned char ClockController::retries=ClockController::MAX_RETRY; - -// -// Data send/receive functions -// - -/** - * \internal - * Receive a data block. The end of the data block must be told to the SDIO - * peripheral in SDIO->DLEN and must match the size parameter given to this - * function. - * \param buffer buffer where to store received data. Its size must be >=size - * \param buffer size, which *must* be multiple of 8 words (32bytes) - * Note that the size parameter must be expressed in word (4bytes), while - * the value in SDIO->DLEN is expressed in bytes. - * \return a DataResult object - */ -static DataResult IRQreceiveDataBlock(unsigned int *buffer, unsigned int size) -{ - // A note on speed. - // Due to the auto calibration of SDIO clock speed being done with - // IRQreceiveDataBlock(), the speed of this function must be comparable - // with the speed of IRQsendDataBlock(), otherwise IRQsendDataBlock() - // will fail because of data underrun. - const unsigned int *bufend=buffer+size; - unsigned int status; - for(;;) - { - status=SDIO->STA; - if(status & (SDIO_STA_RXOVERR | SDIO_STA_DCRCFAIL | - SDIO_STA_DTIMEOUT | SDIO_STA_STBITERR | SDIO_STA_DBCKEND)) break; - if((status & SDIO_STA_RXFIFOHF) && (buffer!=bufend)) - { - //Read 8 words from the fifo, loop entirely unrolled for speed - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - } - } - SDIO->ICR=0x7ff;//Clear flags - if(status & SDIO_STA_RXOVERR) return DataResult(DataResult::RXOverrun); - if(status & SDIO_STA_DCRCFAIL) return DataResult(DataResult::CRCFail); - if(status & SDIO_STA_DTIMEOUT) return DataResult(DataResult::Timeout); - if(status & SDIO_STA_STBITERR) return DataResult(DataResult::StartBitFail); - //Read eventual data left in the FIFO - for(;;) - { - if((SDIO->STA & SDIO_STA_RXDAVL)==0) break; - *buffer=SDIO->FIFO; buffer++; - } - return DataResult(DataResult::Ok); -} - -/** - * \internal - * Send a data block. The end of the data block must be told to the SDIO - * peripheral in SDIO->DLEN and must match the size parameter given to this - * function. - * \param buffer buffer where to store received data. Its size must be >=size - * \param buffer size, which *must* be multiple of 8 words (32bytes). - * Note that the size parameter must be expressed in word (4bytes), while - * the value in SDIO->DLEN is expressed in bytes. - * \return a DataResult object - */ -static DataResult IRQsendDataBlock(const unsigned int *buffer, unsigned int size) -{ - // A note on speed. - // Due to the auto calibration of SDIO clock speed being done with - // IRQreceiveDataBlock(), the speed of this function must be comparable - // with the speed of IRQreceiveDataBlock(), otherwise this function - // will fail because of data underrun. - const unsigned int *bufend=buffer+size; - unsigned int status; - for(;;) - { - status=SDIO->STA; - if(status & (SDIO_STA_TXUNDERR | SDIO_STA_DCRCFAIL | - SDIO_STA_DTIMEOUT | SDIO_STA_STBITERR | SDIO_STA_DBCKEND)) break; - if((status & SDIO_STA_TXFIFOHE) && (buffer!=bufend)) - { - //Write 8 words to the fifo, loop entirely unrolled for speed - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - } - } - SDIO->ICR=0x7ff;//Clear flags - if(status & SDIO_STA_TXUNDERR) return DataResult(DataResult::TXUnderrun); - if(status & SDIO_STA_DCRCFAIL) return DataResult(DataResult::CRCFail); - if(status & SDIO_STA_DTIMEOUT) return DataResult(DataResult::Timeout); - if(status & SDIO_STA_STBITERR) return DataResult(DataResult::StartBitFail); - return DataResult(DataResult::Ok); -} - -/** - * \internal - * Wait until the card is ready for data transfer. - * Can be called independently of the card being selected. - * \return true on success, false on failure - */ -bool waitForCardReady() -{ - for(int i=0;i<300;i++) //Timeout 1.5 second - { - CmdResult cr=Command::send(Command::CMD13,Command::getRca()<<16); - if(cr.validateR1Response()==false) return false; - //Bit 8 in R1 response means ready for data. - if(cr.getResponse() & (1<<8)) return true; - Thread::sleep(5); - } - DBGERR("Timeout waiting card ready\n"); - return false; -} - -/** - * \internal - * Read a single block of 512 bytes from an SD/MMC card. - * Card must be selected prior to caling this function. - * \param buffer, a buffer whose size is >=512 bytes - * \param lba logical block address of the block to read. - */ -static bool singleBlockRead(unsigned char *buffer, unsigned int lba) -{ - if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC - - if(waitForCardReady()==false) return false; - - // Single block read - unsigned int* newBuf=BufferConverter::toWordAlignedWithoutCopy(buffer); - CmdResult cr; - DataResult dr; - bool failed=true; - for(;;) - { - // Since we read with polling, a context switch or interrupt here - // would cause a fifo overrun, so we disable interrupts. - FastInterruptDisableLock dLock; - - SDIO->DTIMER=1048576; - SDIO->DLEN=512; - //Block size 512 bytes, block data xfer, from card to controller - SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN; - - cr=Command::IRQsend(Command::CMD17,lba); - if(cr.IRQvalidateR1Response()) - { - dr=IRQreceiveDataBlock(newBuf,512/sizeof(unsigned int)); - SDIO->DCTRL=0; //Disable data path state machine - - //If failed because too slow check if it is possible to reduce speed - if(dr.getError()==DataResult::RXOverrun) - { - if(ClockController::IRQreduceClockSpeed()) - { - //Disabling interrupts for too long is bad - FastInterruptEnableLock eLock(dLock); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - if(cr.validateR1Response()) continue; - } - } - - if(dr.getError()==DataResult::Ok) failed=false; - } - break; - } - if(failed) - { - cr.validateR1Response(); - dr.validateError(); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - cr.validateR1Response(); - return false; - } - BufferConverter::toOriginalBuffer(); - return true; -} - -/** - * \internal - * Write a single block of 512 bytes to an SD/MMC card - * Card must be selected prior to caling this function. - * \param buffer, a buffer whose size is >=512 bytes - * \param lba logical block address of the block to write. - */ -static bool singleBlockWrite(const unsigned char *buffer, unsigned int lba) -{ - if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC - - if(waitForCardReady()==false) return false; - - // Single block write - const unsigned int* newBuf=BufferConverter::toWordAligned(buffer); - bool failed=true; - CmdResult cr; - DataResult dr; - for(;;) - { - // Since we write with polling, a context switch or interrupt here - // would cause a fifo overrun, so we disable interrupts. - FastInterruptDisableLock dLock; - - cr=Command::IRQsend(Command::CMD24,lba); - if(cr.IRQvalidateR1Response()) - { - SDIO->DTIMER=1048576; - SDIO->DLEN=512; - //Block size 512 bytes, block data xfer, from controller to card - SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTEN; - - dr=IRQsendDataBlock(newBuf,512/sizeof(unsigned int)); - SDIO->DCTRL=0; //Disable data path state machine - - //If failed because too slow check if it is possible to reduce speed - if(dr.getError()==DataResult::TXUnderrun) - { - if(ClockController::IRQreduceClockSpeed()) - { - //Disabling interrupts for too long is bad - FastInterruptEnableLock eLock(dLock); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - if(cr.validateR1Response()) continue; - } - } - - if(dr.getError()==DataResult::Ok) failed=false; - } - break; - } - if(failed) - { - cr.validateR1Response(); - dr.validateError(); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - cr.validateR1Response(); - return false; - } - return true; -} - -// -// Class CardSelector -// - -/** - * \internal - * Simple RAII class for selecting an SD/MMC card an automatically deselect it - * at the end of the scope. - */ -class CardSelector -{ -public: - /** - * \internal - * Constructor. Selects the card. - * The result of the select operation is available through its succeded() - * member function - */ - explicit CardSelector() - { - success=Command::send( - Command::CMD7,Command::getRca()<<16).validateR1Response(); - } - - /** - * \internal - * \return true if the card was selected, false on error - */ - bool succeded() { return success; } - - /** - * \internal - * Destructor, ensures that the card is deselected - */ - ~CardSelector() - { - Command::send(Command::CMD7,0); //Deselect card. This will timeout - } - -private: - bool success; -}; - -// -// Initialization helper functions -// - -/** - * \internal - * Datasheet says that there must be at least seven clock cycles between - * two accesses to SDIO->POWER, and this ensures this constraint. - */ -static inline void sevenNop() -{ - __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); -} - -/** - * \internal - * Initialzes the SDIO peripheral in the STM32 - */ -static void initSDIOPeripheral() -{ - { - //Doing read-modify-write on RCC->APBENR2 and gpios, better be safe - FastInterruptDisableLock lock; - RCC->APB2ENR |= RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN; - RCC->AHBENR |= RCC_AHBENR_SDIOEN; - sdD0::mode(Mode::ALTERNATE); - sdD1::mode(Mode::ALTERNATE); - sdD2::mode(Mode::ALTERNATE); - sdD3::mode(Mode::ALTERNATE); - sdCLK::mode(Mode::ALTERNATE); - sdCMD::mode(Mode::ALTERNATE); - } - - SDIO->POWER=0; //Power off state - SDIO->CLKCR=0; - SDIO->CMD=0; - SDIO->DCTRL=0; - SDIO->ICR=0xc007ff; - sevenNop(); - SDIO->POWER=SDIO_POWER_PWRCTRL_1 | SDIO_POWER_PWRCTRL_0; //Power on state - ClockController::setLowSpeedClock(); - sevenNop(); -} - -/** - * \internal - * Detect if the card is an SDHC, SDv2, SDv1, MMC - * \return Type of card: (1<<0)=MMC (1<<1)=SDv1 (1<<2)=SDv2 (1<<2)|(1<<3)=SDHC - * or Invalid if card detect failed. - */ -static CardType detectCardType() -{ - const int INIT_TIMEOUT=200; //200*10ms= 2 seconds - CmdResult r=Command::send(Command::CMD8,0x1aa); - if(r.validateError()) - { - //We have an SDv2 card connected - if(r.getResponse()!=0x1aa) - { - DBGERR("CMD8 validation: voltage range fail\n"); - return Invalid; - } - for(int i=0;i<INIT_TIMEOUT;i++) - { - //Bit 30 @ 1 = tell the card we like SDHCs - r=Command::send(Command::ACMD41,(1<<30) | VOLTAGE_MASK); - //ACMD41 sends R3 as response, whose CRC is wrong. - if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::CRCFail) - { - r.validateError(); - return Invalid; - } - if((r.getResponse() & (1<<31))==0) //Busy bit - { - Thread::sleep(10); - continue; - } - if((r.getResponse() & VOLTAGE_MASK)==0) - { - DBGERR("ACMD41 validation: voltage range fail\n"); - return Invalid; - } - DBG("ACMD41 validation: looped %d times\n",i); - if(r.getResponse() & (1<<30)) - { - DBG("SDHC\n"); - return SDHC; - } else { - DBG("SDv2\n"); - return SDv2; - } - } - DBGERR("ACMD41 validation: looped until timeout\n"); - return Invalid; - } else { - //We have an SDv1 or MMC - r=Command::send(Command::ACMD41,VOLTAGE_MASK); - //ACMD41 sends R3 as response, whose CRC is wrong. - if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::CRCFail) - { - //MMC card - DBG("MMC card\n"); - return MMC; - } else { - //SDv1 card - for(int i=0;i<INIT_TIMEOUT;i++) - { - //ACMD41 sends R3 as response, whose CRC is wrong. - if(r.getError()!=CmdResult::Ok && - r.getError()!=CmdResult::CRCFail) - { - r.validateError(); - return Invalid; - } - if((r.getResponse() & (1<<31))==0) //Busy bit - { - Thread::sleep(10); - //Send again command - r=Command::send(Command::ACMD41,VOLTAGE_MASK); - continue; - } - if((r.getResponse() & VOLTAGE_MASK)==0) - { - DBGERR("ACMD41 validation: voltage range fail\n"); - return Invalid; - } - DBG("ACMD41 validation: looped %d times\nSDv1\n",i); - return SDv1; - } - DBGERR("ACMD41 validation: looped until timeout\n"); - return Invalid; - } - } -} - -// -// Disk class -// - -bool Disk::isAvailable() -{ - bool result=sdCardSense(); - DBG("Disk::isAvailable(): %d\n",result); - return result; -} - -void Disk::init() -{ - initSDIOPeripheral(); - - // This is more important than it seems, since CMD55 requires the card's RCA - // as argument. During initalization, after CMD0 the card has an RCA of zero - // so without this line ACMD41 will fail and the card won't be initialized. - Command::setRca(0); - - //Send card reset command - CmdResult r=Command::send(Command::CMD0,0); - if(r.validateError()==false) return; - - cardType=detectCardType(); - if(cardType==Invalid) return; //Card detect failed - if(cardType==MMC) return; //MMC cards currently unsupported - - // Now give an RCA to the card. In theory we should loop and enumerate all - // the cards but this driver supports only one card. - r=Command::send(Command::CMD2,0); - //CMD2 sends R2 response, whose CMDINDEX field is wrong - if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::RespNotMatch) - { - r.validateError(); - return; - } - r=Command::send(Command::CMD3,0); - if(r.validateR6Response()==false) return; - Command::setRca(r.getResponse()>>16); - DBG("Got RCA=%u\n",Command::getRca()); - if(Command::getRca()==0) - { - //RCA=0 can't be accepted, since it is used to deselect cards - DBGERR("RCA=0 is invalid\n"); - return; - } - - //Lastly, try selecting the card and configure the latest bits - { - CardSelector selector; - if(selector.succeded()==false) return; - - r=Command::send(Command::CMD13,Command::getRca()<<16);//Get status - if(r.validateR1Response()==false) return; - if(r.getState()!=4) //4=Tran state - { - DBGERR("CMD7 was not able to select card\n"); - return; - } - - r=Command::send(Command::ACMD6,2); //Set 4 bit bus width - if(r.validateR1Response()==false) return; - - if(cardType!=SDHC) - { - r=Command::send(Command::CMD16,512); //Set 512Byte block length - if(r.validateR1Response()==false) return; - } - } - - // Now that card is initialized, perform self calibration of maximum - // possible read/write speed. This as a side effect enables 4bit bus width. - ClockController::calibrateClockSpeed(); - - DBG("Disk::init(): Success\n"); - diskInitialized=true; -} - -bool Disk::read(unsigned char *buffer, unsigned int lba, - unsigned char nSectors) -{ - DBG("Disk::read(): nSectors=%d\n",nSectors); - if(!BufferConverter::isWordAligned(buffer)) DBG("Buffer misaligned\n"); - - for(int i=0;i<ClockController::getRetryCount();i++) - { - //Select card - CardSelector selector; - if(selector.succeded()==false) continue; - - if(nSectors==1) - { - if(singleBlockRead(buffer,lba)==false) continue; - } else { - // Multiple block read - // Currently implemented with N calls to single block read - unsigned char *tempBuffer=buffer; - unsigned int tempLba=lba; - for(unsigned int i=0;i<nSectors;i++) - { - if(singleBlockRead(tempBuffer,tempLba)==false) continue; - tempBuffer+=512; - tempLba++; - } - } - if(i>0) DBGERR("Read: required %d retries\n",i); - return true; - } - return false; -} - -bool Disk::write(const unsigned char *buffer, unsigned int lba, - unsigned char nSectors) -{ - DBG("Disk::write(): nSectors=%d\n",nSectors); - if(!BufferConverter::isWordAligned(buffer)) DBG("Buffer misaligned\n"); - - for(int i=0;i<ClockController::getRetryCount();i++) - { - //Select card - CardSelector selector; - if(selector.succeded()==false) continue; - - if(nSectors==1) - { - if(singleBlockWrite(buffer,lba)==false) continue; - } else { - // Multiple block write - // Currently implemented with N calls to single block write - const unsigned char *tempBuffer=buffer; - unsigned int tempLba=lba; - for(unsigned int i=0;i<nSectors;i++) - { - if(singleBlockWrite(tempBuffer,tempLba)==false) continue; - tempBuffer+=512; - tempLba++; - } - } - if(i>0) DBGERR("Write: required %d retries\n",i); - return true; - } - return false; -} - -bool Disk::sync() -{ - DBG("Disk::sync()\n"); - //Note: no need to select card, since status can be queried even with card - //not selected. - return waitForCardReady(); -} - -bool Disk::diskInitialized=false; - -} //namespace miosix diff --git a/miosix/arch/cortexM3_stm32/stm32f103ze_stm3210e-eval/interfaces-impl/disk.cpp b/miosix/arch/cortexM3_stm32/stm32f103ze_stm3210e-eval/interfaces-impl/disk.cpp deleted file mode 100644 index 5bc803ee745c2354913d2155390df9b658edf07a..0000000000000000000000000000000000000000 --- a/miosix/arch/cortexM3_stm32/stm32f103ze_stm3210e-eval/interfaces-impl/disk.cpp +++ /dev/null @@ -1,1390 +0,0 @@ - -/*************************************************************************** - * Copyright (C) 2010 by Terraneo Federico * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * As a special exception, if other files instantiate templates or use * - * macros or inline functions from this file, or you compile this file * - * and link it with other works to produce a work based on this file, * - * this file does not by itself cause the resulting work to be covered * - * by the GNU General Public License. However the source code for this * - * file must still be made available in accordance with the GNU General * - * Public License. This exception does not invalidate any other reasons * - * why a work based on this file might be covered by the GNU General * - * Public License. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see <http://www.gnu.org/licenses/> * - ***************************************************************************/ - -#include "interfaces/disk.h" -#include "CMSIS/stm32f10x.h" -#include "CMSIS/core_cm3.h" -#include "interfaces/bsp.h" -#include "interfaces/delays.h" -#include "kernel/kernel.h" -#include <cstdio> -#include <cstring> - -//Note: enabling debugging might cause deadlock when using sleep() or reboot() -//The bug won't be fixed because debugging is only useful for driver development -///\internal Debug macro, for normal conditions -//#define DBG iprintf -#define DBG(x,...) ; -///\internal Debug macro, for errors only -//#define DBGERR iprintf -#define DBGERR(x,...) ; - -namespace miosix { - -/* - * Operating voltage of device. It is sent to the SD card to check if it can - * work at this voltage. Range *must* be within 28..36 - * Example 33=3.3v - */ -const unsigned char VOLTAGE=33; -const unsigned int VOLTAGE_MASK=1<<(VOLTAGE-13); //See OCR register in SD spec - -/** - * \internal - * Possible state of the cardType variable. - */ -enum CardType -{ - Invalid=0, ///<\internal Invalid card type - MMC=1<<0, ///<\internal if(cardType==MMC) card is an MMC - SDv1=1<<1, ///<\internal if(cardType==SDv1) card is an SDv1 - SDv2=1<<2, ///<\internal if(cardType==SDv2) card is an SDv2 - SDHC=1<<3 ///<\internal if(cardType==SDHC) card is an SDHC -}; - -///\internal Type of card. This variable is set in Disk::init() -static CardType cardType=Invalid; - -//SD card GPIOs -typedef Gpio<GPIOC_BASE,8> sdD0; -typedef Gpio<GPIOC_BASE,9> sdD1; -typedef Gpio<GPIOC_BASE,10> sdD2; -typedef Gpio<GPIOC_BASE,11> sdD3; -typedef Gpio<GPIOC_BASE,12> sdCLK; -typedef Gpio<GPIOD_BASE,2> sdCMD; - -// -// Class BufferConverter -// - -/** - * \internal - * Convert a single buffer of *fixed* and predetermined size to and from - * word-aligned. To do so, if the buffer is already word aligned a cast is made, - * otherwise a new buffer is allocated. - * Note that this class allocates at most ONE buffer at any given time. - * Therefore any call to toWordAligned(), toWordAlignedWithoutCopy(), - * toOriginalBuffer() or deallocateBuffer() invalidates the buffer previousy - * returned by toWordAligned() and toWordAlignedWithoutCopy() - */ -class BufferConverter -{ -public: - /** - * \internal - * The buffer will be of this size only. - */ - static const int BUFFER_SIZE=512; - - /** - * \internal - * \return true if the pointer is word aligned - */ - static bool isWordAligned(const unsigned char *x) - { - return (reinterpret_cast<const unsigned int>(x) & 0x3)==0; - } - - /** - * \internal - * Convert from a constunsigned char* buffer of size BUFFER_SIZE to a - * const unsigned int* word aligned buffer. - * If the original buffer is already word aligned it only does a cast, - * otherwise it copies the data on the original buffer to a word aligned - * buffer. Useful if subseqent code will read from the buffer. - * \param a buffer of size BUFFER_SIZE. Can be word aligned or not. - * \return a word aligned buffer with the same data of the given buffer - */ - static const unsigned int *toWordAligned(const unsigned char *buffer); - - /** - * \internal - * Convert from an unsigned char* buffer of size BUFFER_SIZE to an - * unsigned int* word aligned buffer. - * If the original buffer is already word aligned it only does a cast, - * otherwise it returns a new buffer which *does not* contain the data - * on the original buffer. Useful if subseqent code will write to the - * buffer. To move the written data to the original buffer, use - * toOriginalBuffer() - * \param a buffer of size BUFFER_SIZE. Can be word aligned or not. - * \return a word aligned buffer with undefined content. - */ - static unsigned int *toWordAlignedWithoutCopy(unsigned char *buffer); - - /** - * \internal - * Convert the buffer got through toWordAlignedWithoutCopy() to the - * original buffer. If the original buffer was word aligned, nothing - * happens, otherwise a memcpy is done. - * Note that this function does not work on buffers got through - * toWordAligned(). - */ - static void toOriginalBuffer(); - - /** - * \internal - * Can be called to deallocate the buffer - */ - static void deallocateBuffer(); - -private: - static unsigned char *originalBuffer; - static unsigned int *wordAlignedBuffer; -}; - -const unsigned int *BufferConverter::toWordAligned(const unsigned char *buffer) -{ - originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do - if(isWordAligned(buffer)) - { - return reinterpret_cast<const unsigned int*>(buffer); - } else { - if(wordAlignedBuffer==0) - wordAlignedBuffer=new unsigned int[BUFFER_SIZE/sizeof(unsigned int)]; - std::memcpy(wordAlignedBuffer,buffer,BUFFER_SIZE); - return wordAlignedBuffer; - } -} - -unsigned int *BufferConverter::toWordAlignedWithoutCopy( - unsigned char *buffer) -{ - if(isWordAligned(buffer)) - { - originalBuffer=0; //Tell toOriginalBuffer() that there's nothing to do - return reinterpret_cast<unsigned int*>(buffer); - } else { - originalBuffer=buffer; //Save original pointer for toOriginalBuffer() - if(wordAlignedBuffer==0) - wordAlignedBuffer=new unsigned int[BUFFER_SIZE/sizeof(unsigned int)]; - return wordAlignedBuffer; - } -} - -void BufferConverter::toOriginalBuffer() -{ - if(originalBuffer==0) return; - std::memcpy(originalBuffer,wordAlignedBuffer,BUFFER_SIZE); - originalBuffer=0; -} - -void BufferConverter::deallocateBuffer() -{ - originalBuffer=0; //Invalidate also original buffer - if(wordAlignedBuffer!=0) - { - delete[] wordAlignedBuffer; - wordAlignedBuffer=0; - } -} - -unsigned char *BufferConverter::originalBuffer=0; -unsigned int *BufferConverter::wordAlignedBuffer=0; - -// -// Class CmdResult -// - -/** - * \internal - * Contains the result of an SD/MMC command - */ -class CmdResult -{ -public: - - /** - * \internal - * Possible outcomes of sending a command - */ - enum Error - { - Ok=0, /// No errors - Timeout, /// Timeout while waiting command reply - CRCFail, /// CRC check failed in command reply - RespNotMatch,/// Response index does not match command index - ACMDFail /// Sending CMD55 failed - }; - - /** - * \internal - * Default constructor - */ - CmdResult(): cmd(0), error(Ok), response(0) {} - - /** - * \internal - * Constructor, set the response data - * \param cmd command index of command that was sent - * \param result result of command - */ - CmdResult(unsigned char cmd, Error error): cmd(cmd), error(error), - response(SDIO->RESP1) {} - - /** - * \internal - * \return the 32 bit of the response. - * May not be valid if getError()!=Ok or the command does not send a - * response, such as CMD0 - */ - unsigned int getResponse() { return response; } - - /** - * \internal - * \return command index - */ - unsigned char getCmdIndex() { return cmd; } - - /** - * \internal - * \return the error flags of the response - */ - Error getError() { return error; } - - /** - * \internal - * Checks if errors occurred while sending the command. - * \return true if no errors, false otherwise - */ - bool validateError(); - - /** - * \internal - * interprets this->getResponse() as an R1 response, and checks if there are - * errors, or everything is ok - * \return true on success, false on failure - */ - bool validateR1Response(); - - /** - * \internal - * Same as validateR1Response, but can be called with interrupts disabled. - * \return true on success, false on failure - */ - bool IRQvalidateR1Response(); - - /** - * \internal - * interprets this->getResponse() as an R6 response, and checks if there are - * errors, or everything is ok - * \return true on success, false on failure - */ - bool validateR6Response(); - - /** - * \internal - * \return the card state from an R1 or R6 resonse - */ - unsigned char getState(); - -private: - unsigned char cmd; ///<\internal Command index that was sent - Error error; ///<\internal possible error that occurred - unsigned int response; ///<\internal 32bit response -}; - -bool CmdResult::validateError() -{ - switch(error) - { - case Ok: - return true; - case Timeout: - DBGERR("CMD%d: Timeout\n",cmd); - break; - case CRCFail: - DBGERR("CMD%d: CRC Fail\n",cmd); - break; - case RespNotMatch: - DBGERR("CMD%d: Response does not match\n",cmd); - break; - case ACMDFail: - DBGERR("CMD%d: ACMD Fail\n",cmd); - break; - } - return false; -} - -bool CmdResult::validateR1Response() -{ - if(error!=Ok) return validateError(); - //Note: this number is obtained with all the flags of R1 which are errors - //(flagged as E in the SD specification), plus CARD_IS_LOCKED because - //locked card are not supported by this software driver - if((response & 0xfff98008)==0) return true; - DBGERR("CMD%d: R1 response error(s):\n",cmd); - if(response & (1<<31)) DBGERR("Out of range\n"); - if(response & (1<<30)) DBGERR("ADDR error\n"); - if(response & (1<<29)) DBGERR("BLOCKLEN error\n"); - if(response & (1<<28)) DBGERR("ERASE SEQ error\n"); - if(response & (1<<27)) DBGERR("ERASE param\n"); - if(response & (1<<26)) DBGERR("WP violation\n"); - if(response & (1<<25)) DBGERR("card locked\n"); - if(response & (1<<24)) DBGERR("LOCK_UNLOCK failed\n"); - if(response & (1<<23)) DBGERR("command CRC failed\n"); - if(response & (1<<22)) DBGERR("illegal command\n"); - if(response & (1<<21)) DBGERR("ECC fail\n"); - if(response & (1<<20)) DBGERR("card controller error\n"); - if(response & (1<<19)) DBGERR("unknown error\n"); - if(response & (1<<16)) DBGERR("CSD overwrite\n"); - if(response & (1<<15)) DBGERR("WP ERASE skip\n"); - if(response & (1<<3)) DBGERR("AKE_SEQ error\n"); - return false; -} - -bool CmdResult::IRQvalidateR1Response() -{ - if(error!=Ok) return false; - if(response & 0xfff98008) return false; - return true; -} - -bool CmdResult::validateR6Response() -{ - if(error!=Ok) return validateError(); - if((response & 0xe008)==0) return true; - DBGERR("CMD%d: R6 response error(s):\n",cmd); - if(response & (1<<15)) DBGERR("command CRC failed\n"); - if(response & (1<<14)) DBGERR("illegal command\n"); - if(response & (1<<13)) DBGERR("unknown error\n"); - if(response & (1<<3)) DBGERR("AKE_SEQ error\n"); - return false; -} - -unsigned char CmdResult::getState() -{ - unsigned char result=(response>>9) & 0xf; - DBG("CMD%d: State: ",cmd); - switch(result) - { - case 0: DBG("Idle\n"); break; - case 1: DBG("Ready\n"); break; - case 2: DBG("Ident\n"); break; - case 3: DBG("Stby\n"); break; - case 4: DBG("Tran\n"); break; - case 5: DBG("Data\n"); break; - case 6: DBG("Rcv\n"); break; - case 7: DBG("Prg\n"); break; - case 8: DBG("Dis\n"); break; - case 9: DBG("Btst\n"); break; - default: DBG("Unknown\n"); break; - } - return result; -} - -// -// Class Command -// - -/** - * \internal - * This class allows sending commands to an SD or MMC - */ -class Command -{ -public: - - /** - * \internal - * SD/MMC commands - * - bit #7 is @ 1 if a command is an ACMDxx. send() will send the - * sequence CMD55, CMDxx - * - bit from #0 to #5 indicate command index (CMD0..CMD63) - * - bit #6 is don't care - */ - enum CommandType - { - CMD0=0, //GO_IDLE_STATE - CMD2=2, //ALL_SEND_CID - CMD3=3, //SEND_RELATIVE_ADDR - ACMD6=0x80 | 6, //SET_BUS_WIDTH - CMD7=7, //SELECT_DESELECT_CARD - ACMD41=0x80 | 41, //SEND_OP_COND (SD) - CMD8=8, //SEND_IF_COND - CMD9=9, //SEND_CSD - CMD12=12, //STOP_TRANSMISSION - CMD13=13, //SEND_STATUS - CMD16=16, //SET_BLOCKLEN - CMD17=17, //READ_SINGLE_BLOCK - CMD18=18, //READ_MULTIPLE_BLOCK - ACMD23=0x80 | 23, //SET_WR_BLK_ERASE_COUNT (SD) - CMD24=24, //WRITE_BLOCK - CMD25=25, //WRITE_MULTIPLE_BLOCK - CMD55=55 //APP_CMD - }; - - /** - * \internal - * Send a command. - * \param cmd command index (CMD0..CMD63) or ACMDxx command - * \param arg the 32 bit argument to the command - * \return a CmdResult object - */ - static CmdResult send(CommandType cmd, unsigned int arg) - { - if(static_cast<unsigned char>(cmd) & 0x80) - { - DBG("ACMD%d\n",static_cast<unsigned char>(cmd) & 0x3f); - } else { - DBG("CMD%d\n",static_cast<unsigned char>(cmd) & 0x3f); - } - return IRQsend(cmd,arg); - } - - /** - * \internal - * Send a command. Can be called with interrupts disabled as it does not - * print any debug information. - * \param cmd command index (CMD0..CMD63) or ACMDxx command - * \param arg the 32 bit argument to the command - * \return a CmdResult object - */ - static CmdResult IRQsend(CommandType cmd, unsigned int arg); - - /** - * \internal - * Set the relative card address, obtained during initialization. - * \param r the card's rca - */ - static void setRca(unsigned short r) { rca=r; } - - /** - * \internal - * \return the card's rca, as set by setRca - */ - static unsigned int getRca() { return static_cast<unsigned int>(rca); } - -private: - static unsigned short rca;///<\internal Card's relative address -}; - -CmdResult Command::IRQsend(CommandType cmd, unsigned int arg) -{ - unsigned char cc=static_cast<unsigned char>(cmd); - //Handle ACMDxx as CMD55, CMDxx - if(cc & 0x80) - { - CmdResult r=IRQsend(CMD55,(static_cast<unsigned int>(rca))<<16); - if(r.IRQvalidateR1Response()==false) - return CmdResult(cc & 0x3f,CmdResult::ACMDFail); - //Bit 5 @ 1 = next command will be interpreted as ACMD - if((r.getResponse() & (1<<5))==0) - return CmdResult(cc & 0x3f,CmdResult::ACMDFail); - } - - //Send command - cc &= 0x3f; - unsigned int command=SDIO_CMD_CPSMEN | static_cast<unsigned int>(cc); - if(cc!=CMD0) command |= SDIO_CMD_WAITRESP_0; //CMD0 has no response - if(cc==CMD2) command |= SDIO_CMD_WAITRESP_1; //CMD2 has long response - if(cc==CMD9) command |= SDIO_CMD_WAITRESP_1; //CMD9 has long response - SDIO->ARG=arg; - SDIO->CMD=command; - - //CMD0 has no response, so wait until it is sent - if(cc==CMD0) - { - for(int i=0;i<500;i++) - { - if(SDIO->STA & SDIO_STA_CMDSENT) - { - SDIO->ICR=0x7ff;//Clear flags - return CmdResult(cc,CmdResult::Ok); - } - delayUs(1); - } - SDIO->ICR=0x7ff;//Clear flags - return CmdResult(cc,CmdResult::Timeout); - } - - //Command is not CMD0, so wait a reply - for(int i=0;i<500;i++) - { - unsigned int status=SDIO->STA; - if(status & SDIO_STA_CMDREND) - { - SDIO->ICR=0x7ff;//Clear flags - if(SDIO->RESPCMD==cc) return CmdResult(cc,CmdResult::Ok); - else return CmdResult(cc,CmdResult::RespNotMatch); - } - if(status & SDIO_STA_CCRCFAIL) - { - SDIO->ICR=SDIO_ICR_CCRCFAILC; - return CmdResult(cc,CmdResult::CRCFail); - } - if(status & SDIO_STA_CTIMEOUT) break; - delayUs(1); - } - SDIO->ICR=SDIO_ICR_CTIMEOUTC; - return CmdResult(cc,CmdResult::Timeout); -} - -unsigned short Command::rca=0; - -// -// Class DataResult -// - -/** - * \internal - * Contains the result of sending/receiving a data block - */ -class DataResult -{ -public: - - /** - * \internal - * Possible outcomes of sending or receiving data - */ - enum Error - { - Ok=0, - Timeout, - CRCFail, - RXOverrun, - TXUnderrun, - StartBitFail - }; - - /** - * \internal - * Default constructor - */ - DataResult(): error(Ok) {} - - /** - * \internal - * Constructor, set the result. - * \param error error type - */ - DataResult(Error error): error(error) {} - - /** - * \internal - * \return the error flags - */ - Error getError() { return error; } - - /** - * \internal - * Checks if errors occurred while sending/receiving data. - * \return true if no errors, false otherwise - */ - bool validateError(); - -private: - Error error; -}; - - -bool DataResult::validateError() -{ - switch(error) - { - case Ok: - return true; - case Timeout: - DBGERR("Data Timeout\n"); - break; - case CRCFail: - DBGERR("Data CRC Fail\n"); - break; - case RXOverrun: - DBGERR("Data overrun\n"); - break; - case TXUnderrun: - DBGERR("Data underrun\n"); - break; - case StartBitFail: - DBGERR("Data start bit Fail\n"); - break; - } - return false; -} - -// -// Class ClockController -// - -/** - * \internal - * This class controls the clock speed of the SDIO peripheral. The SDIO - * peripheral, when used in polled mode, requires two timing critical pieces of - * code: the one to send and the one to receive a data block. This because - * the peripheral has a 128 byte fifo while the block size is 512 byte, and - * if fifo underrun/overrun occurs the peripheral does not pause communcation, - * instead it simply aborts the data transfer. Since the speed of the code to - * read/write a data block depends on too many factors, such as compiler - * optimizations, code running from internal flash or external ram, and the - * cpu clock speed, a dynamic clocking approach was chosen. - */ -class ClockController -{ -public: - - /** - * \internal. Set a low clock speed of 400KHz or less, used for - * detecting SD/MMC cards. This function as a side effect enables 1bit bus - * width, and disables clock powersave, since it is not allowed by SD spec. - */ - static void setLowSpeedClock() - { - clockReductionAvailable=0; - // No hardware flow control, SDIO_CK generated on rising edge, 1bit bus - // width, no clock bypass, no powersave. - // Set low clock speed 400KHz, 72MHz/400KHz-2=178 - SDIO->CLKCR=CLOCK_400KHz; - SDIO->CLKCR |= SDIO_CLKCR_CLKEN; - } - - /** - * \internal - * Automatically select the data speed. - * Since the maximum speed depends on many factors, such as code running in - * internal or external RAM, compiler optimizations etc. this routine - * selects the highest sustainable data transfer speed. - * This is done by binary search until the highest clock speed that causes - * no errors is found. - * This function as a side effect enables 4bit bus width, and clock - * powersave. - */ - static void calibrateClockSpeed(); - - /** - * \internal - * Since clock speed is set dynamically by bynary search at runtime, a - * corner case might be that of a clock speed which results in unreliable - * data transfer, that sometimes succeeds, and sometimes fail. - * For maximum robustness, this function is provided to reduce the clock - * speed slightly in case a data transfer should fail after clock - * calibration. To avoid inadvertently considering other kind of issues as - * clock issues, this function can be called only MAX_ALLOWED_REDUCTIONS - * times after clock calibration, subsequent calls will fail. This will - * avoid other issues causing an ever decreasing clock speed. - * Can be called with interrupts disabled. - * \return true on success, false on failure - */ - static bool IRQreduceClockSpeed(); - - /** - * \internal - * Read and write operation do retry during normal use for robustness, but - * during clock claibration they must not retry for speed reasons. This - * member function returns 1 during clock claibration and MAX_RETRY during - * normal use. - */ - static unsigned char getRetryCount() { return retries; } - -private: - - /** - * \internal - * Value of SDIO->CLKCR that will give a 400KHz clock, depending on cpu - * clock speed. - */ - #ifdef SYSCLK_FREQ_72MHz - static const unsigned int CLOCK_400KHz=178; - #elif SYSCLK_FREQ_56MHz - static const unsigned int CLOCK_400KHz=138; - #elif SYSCLK_FREQ_48MHz - static const unsigned int CLOCK_400KHz=118; - #elif SYSCLK_FREQ_36MHz - static const unsigned int CLOCK_400KHz=88; - #elif SYSCLK_FREQ_24MHz - static const unsigned int CLOCK_400KHz=58; - #else - static const unsigned int CLOCK_400KHz=18; - #endif - - ///\internal Clock enabled, bus width 4bit, clock powersave enabled. - static const unsigned int CLKCR_FLAGS=SDIO_CLKCR_CLKEN | - SDIO_CLKCR_WIDBUS_0 | SDIO_CLKCR_PWRSAV; - - ///\internal Maximum number of calls to IRQreduceClockSpeed() allowed - static const unsigned char MAX_ALLOWED_REDUCTIONS=5; - - ///\internl value returned by getRetryCount() while *not* calibrating clock. - static const unsigned char MAX_RETRY=3; - - ///\internal Used to allow only one call to reduceClockSpeed() - static unsigned char clockReductionAvailable; - - static unsigned char retries; -}; - -void ClockController::calibrateClockSpeed() -{ - //During calibration we call Disk::read which will call reduceClockSpeed() - //so not to invalidate calibration clock reduction must not be available - clockReductionAvailable=0; - retries=1; - - DBG("Automatic speed calibration\n"); - unsigned int buffer[512/sizeof(unsigned int)]; - unsigned int minFreq=CLOCK_400KHz; //400KHz, independent of CPU clock - unsigned int maxFreq=1; //24MHz with CPU running @ 72MHz - unsigned int selected; - while(minFreq-maxFreq>1) - { - selected=(minFreq+maxFreq)/2; - DBG("Trying CLKCR=%d\n",selected); - SDIO->CLKCR=selected; - SDIO->CLKCR |= CLKCR_FLAGS; - if(Disk::read(reinterpret_cast<unsigned char*>(buffer),0,1)) - minFreq=selected; - else maxFreq=selected; - } - //Last round of algorithm - SDIO->CLKCR=maxFreq; - SDIO->CLKCR |= CLKCR_FLAGS; - if(Disk::read(reinterpret_cast<unsigned char*>(buffer),0,1)) - { - DBG("Optimal CLKCR=%d\n",maxFreq); - } else { - SDIO->CLKCR=minFreq; - SDIO->CLKCR |= CLKCR_FLAGS; - DBG("Optimal CLKCR=%d\n",minFreq); - } - - //Make clock reduction available - clockReductionAvailable=MAX_ALLOWED_REDUCTIONS; - retries=MAX_RETRY; -} - -bool ClockController::IRQreduceClockSpeed() -{ - //Ensure this function can be called only twice per calibration - if(clockReductionAvailable==0) return false; - clockReductionAvailable--; - - unsigned int currentClkcr=SDIO->CLKCR & 0xff; - if(currentClkcr==CLOCK_400KHz) return false; //No lower than this value - - //If the value of clockcr is low, increasing it by one is enough since - //frequency changes a lot, otherwise increase by 2. - if(currentClkcr<10) currentClkcr++; - else currentClkcr+=2; - - SDIO->CLKCR=currentClkcr; - SDIO->CLKCR |= CLKCR_FLAGS; - return true; -} - -unsigned char ClockController::clockReductionAvailable=false; -unsigned char ClockController::retries=ClockController::MAX_RETRY; - -// -// Data send/receive functions -// - -/** - * \internal - * Receive a data block. The end of the data block must be told to the SDIO - * peripheral in SDIO->DLEN and must match the size parameter given to this - * function. - * \param buffer buffer where to store received data. Its size must be >=size - * \param buffer size, which *must* be multiple of 8 words (32bytes) - * Note that the size parameter must be expressed in word (4bytes), while - * the value in SDIO->DLEN is expressed in bytes. - * \return a DataResult object - */ -static DataResult IRQreceiveDataBlock(unsigned int *buffer, unsigned int size) -{ - // A note on speed. - // Due to the auto calibration of SDIO clock speed being done with - // IRQreceiveDataBlock(), the speed of this function must be comparable - // with the speed of IRQsendDataBlock(), otherwise IRQsendDataBlock() - // will fail because of data underrun. - const unsigned int *bufend=buffer+size; - unsigned int status; - for(;;) - { - status=SDIO->STA; - if(status & (SDIO_STA_RXOVERR | SDIO_STA_DCRCFAIL | - SDIO_STA_DTIMEOUT | SDIO_STA_STBITERR | SDIO_STA_DBCKEND)) break; - if((status & SDIO_STA_RXFIFOHF) && (buffer!=bufend)) - { - //Read 8 words from the fifo, loop entirely unrolled for speed - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - *buffer=SDIO->FIFO; buffer++; - } - } - SDIO->ICR=0x7ff;//Clear flags - if(status & SDIO_STA_RXOVERR) return DataResult(DataResult::RXOverrun); - if(status & SDIO_STA_DCRCFAIL) return DataResult(DataResult::CRCFail); - if(status & SDIO_STA_DTIMEOUT) return DataResult(DataResult::Timeout); - if(status & SDIO_STA_STBITERR) return DataResult(DataResult::StartBitFail); - //Read eventual data left in the FIFO - for(;;) - { - if((SDIO->STA & SDIO_STA_RXDAVL)==0) break; - *buffer=SDIO->FIFO; buffer++; - } - return DataResult(DataResult::Ok); -} - -/** - * \internal - * Send a data block. The end of the data block must be told to the SDIO - * peripheral in SDIO->DLEN and must match the size parameter given to this - * function. - * \param buffer buffer where to store received data. Its size must be >=size - * \param buffer size, which *must* be multiple of 8 words (32bytes). - * Note that the size parameter must be expressed in word (4bytes), while - * the value in SDIO->DLEN is expressed in bytes. - * \return a DataResult object - */ -static DataResult IRQsendDataBlock(const unsigned int *buffer, unsigned int size) -{ - // A note on speed. - // Due to the auto calibration of SDIO clock speed being done with - // IRQreceiveDataBlock(), the speed of this function must be comparable - // with the speed of IRQreceiveDataBlock(), otherwise this function - // will fail because of data underrun. - const unsigned int *bufend=buffer+size; - unsigned int status; - for(;;) - { - status=SDIO->STA; - if(status & (SDIO_STA_TXUNDERR | SDIO_STA_DCRCFAIL | - SDIO_STA_DTIMEOUT | SDIO_STA_STBITERR | SDIO_STA_DBCKEND)) break; - if((status & SDIO_STA_TXFIFOHE) && (buffer!=bufend)) - { - //Write 8 words to the fifo, loop entirely unrolled for speed - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - SDIO->FIFO=*buffer; buffer++; - } - } - SDIO->ICR=0x7ff;//Clear flags - if(status & SDIO_STA_TXUNDERR) return DataResult(DataResult::TXUnderrun); - if(status & SDIO_STA_DCRCFAIL) return DataResult(DataResult::CRCFail); - if(status & SDIO_STA_DTIMEOUT) return DataResult(DataResult::Timeout); - if(status & SDIO_STA_STBITERR) return DataResult(DataResult::StartBitFail); - return DataResult(DataResult::Ok); -} - -/** - * \internal - * Wait until the card is ready for data transfer. - * Can be called independently of the card being selected. - * \return true on success, false on failure - */ -bool waitForCardReady() -{ - for(int i=0;i<300;i++) //Timeout 1.5 second - { - CmdResult cr=Command::send(Command::CMD13,Command::getRca()<<16); - if(cr.validateR1Response()==false) return false; - //Bit 8 in R1 response means ready for data. - if(cr.getResponse() & (1<<8)) return true; - Thread::sleep(5); - } - DBGERR("Timeout waiting card ready\n"); - return false; -} - -/** - * \internal - * Read a single block of 512 bytes from an SD/MMC card. - * Card must be selected prior to caling this function. - * \param buffer, a buffer whose size is >=512 bytes - * \param lba logical block address of the block to read. - */ -static bool singleBlockRead(unsigned char *buffer, unsigned int lba) -{ - if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC - - if(waitForCardReady()==false) return false; - - // Single block read - unsigned int* newBuf=BufferConverter::toWordAlignedWithoutCopy(buffer); - CmdResult cr; - DataResult dr; - bool failed=true; - for(;;) - { - // Since we read with polling, a context switch or interrupt here - // would cause a fifo overrun, so we disable interrupts. - FastInterruptDisableLock dLock; - - SDIO->DTIMER=1048576; - SDIO->DLEN=512; - //Block size 512 bytes, block data xfer, from card to controller - SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN; - - cr=Command::IRQsend(Command::CMD17,lba); - if(cr.IRQvalidateR1Response()) - { - dr=IRQreceiveDataBlock(newBuf,512/sizeof(unsigned int)); - SDIO->DCTRL=0; //Disable data path state machine - - //If failed because too slow check if it is possible to reduce speed - if(dr.getError()==DataResult::RXOverrun) - { - if(ClockController::IRQreduceClockSpeed()) - { - //Disabling interrupts for too long is bad - FastInterruptEnableLock eLock(dLock); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - if(cr.validateR1Response()) continue; - } - } - - if(dr.getError()==DataResult::Ok) failed=false; - } - break; - } - if(failed) - { - cr.validateR1Response(); - dr.validateError(); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - cr.validateR1Response(); - return false; - } - BufferConverter::toOriginalBuffer(); - return true; -} - -/** - * \internal - * Write a single block of 512 bytes to an SD/MMC card - * Card must be selected prior to caling this function. - * \param buffer, a buffer whose size is >=512 bytes - * \param lba logical block address of the block to write. - */ -static bool singleBlockWrite(const unsigned char *buffer, unsigned int lba) -{ - if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC - - if(waitForCardReady()==false) return false; - - // Single block write - const unsigned int* newBuf=BufferConverter::toWordAligned(buffer); - bool failed=true; - CmdResult cr; - DataResult dr; - for(;;) - { - // Since we write with polling, a context switch or interrupt here - // would cause a fifo overrun, so we disable interrupts. - FastInterruptDisableLock dLock; - - cr=Command::IRQsend(Command::CMD24,lba); - if(cr.IRQvalidateR1Response()) - { - SDIO->DTIMER=1048576; - SDIO->DLEN=512; - //Block size 512 bytes, block data xfer, from controller to card - SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTEN; - - dr=IRQsendDataBlock(newBuf,512/sizeof(unsigned int)); - SDIO->DCTRL=0; //Disable data path state machine - - //If failed because too slow check if it is possible to reduce speed - if(dr.getError()==DataResult::TXUnderrun) - { - if(ClockController::IRQreduceClockSpeed()) - { - //Disabling interrupts for too long is bad - FastInterruptEnableLock eLock(dLock); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - if(cr.validateR1Response()) continue; - } - } - - if(dr.getError()==DataResult::Ok) failed=false; - } - break; - } - if(failed) - { - cr.validateR1Response(); - dr.validateError(); - //After an error during data xfer the card might be a little - //confused. So send STOP_TRANSMISSION command to reassure it - cr=Command::send(Command::CMD12,0); - cr.validateR1Response(); - return false; - } - return true; -} - -// -// Class CardSelector -// - -/** - * \internal - * Simple RAII class for selecting an SD/MMC card an automatically deselect it - * at the end of the scope. - */ -class CardSelector -{ -public: - /** - * \internal - * Constructor. Selects the card. - * The result of the select operation is available through its succeded() - * member function - */ - explicit CardSelector() - { - success=Command::send( - Command::CMD7,Command::getRca()<<16).validateR1Response(); - } - - /** - * \internal - * \return true if the card was selected, false on error - */ - bool succeded() { return success; } - - /** - * \internal - * Destructor, ensures that the card is deselected - */ - ~CardSelector() - { - Command::send(Command::CMD7,0); //Deselect card. This will timeout - } - -private: - bool success; -}; - -// -// Initialization helper functions -// - -/** - * \internal - * Datasheet says that there must be at least seven clock cycles between - * two accesses to SDIO->POWER, and this ensures this constraint. - */ -static inline void sevenNop() -{ - __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); -} - -/** - * \internal - * Initialzes the SDIO peripheral in the STM32 - */ -static void initSDIOPeripheral() -{ - { - //Doing read-modify-write on RCC->APBENR2 and gpios, better be safe - FastInterruptDisableLock lock; - RCC->APB2ENR |= RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN; - RCC->AHBENR |= RCC_AHBENR_SDIOEN; - sdD0::mode(Mode::ALTERNATE); - sdD1::mode(Mode::ALTERNATE); - sdD2::mode(Mode::ALTERNATE); - sdD3::mode(Mode::ALTERNATE); - sdCLK::mode(Mode::ALTERNATE); - sdCMD::mode(Mode::ALTERNATE); - } - - SDIO->POWER=0; //Power off state - SDIO->CLKCR=0; - SDIO->CMD=0; - SDIO->DCTRL=0; - SDIO->ICR=0xc007ff; - sevenNop(); - SDIO->POWER=SDIO_POWER_PWRCTRL_1 | SDIO_POWER_PWRCTRL_0; //Power on state - ClockController::setLowSpeedClock(); - sevenNop(); -} - -/** - * \internal - * Detect if the card is an SDHC, SDv2, SDv1, MMC - * \return Type of card: (1<<0)=MMC (1<<1)=SDv1 (1<<2)=SDv2 (1<<2)|(1<<3)=SDHC - * or Invalid if card detect failed. - */ -static CardType detectCardType() -{ - const int INIT_TIMEOUT=200; //200*10ms= 2 seconds - CmdResult r=Command::send(Command::CMD8,0x1aa); - if(r.validateError()) - { - //We have an SDv2 card connected - if(r.getResponse()!=0x1aa) - { - DBGERR("CMD8 validation: voltage range fail\n"); - return Invalid; - } - for(int i=0;i<INIT_TIMEOUT;i++) - { - //Bit 30 @ 1 = tell the card we like SDHCs - r=Command::send(Command::ACMD41,(1<<30) | VOLTAGE_MASK); - //ACMD41 sends R3 as response, whose CRC is wrong. - if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::CRCFail) - { - r.validateError(); - return Invalid; - } - if((r.getResponse() & (1<<31))==0) //Busy bit - { - Thread::sleep(10); - continue; - } - if((r.getResponse() & VOLTAGE_MASK)==0) - { - DBGERR("ACMD41 validation: voltage range fail\n"); - return Invalid; - } - DBG("ACMD41 validation: looped %d times\n",i); - if(r.getResponse() & (1<<30)) - { - DBG("SDHC\n"); - return SDHC; - } else { - DBG("SDv2\n"); - return SDv2; - } - } - DBGERR("ACMD41 validation: looped until timeout\n"); - return Invalid; - } else { - //We have an SDv1 or MMC - r=Command::send(Command::ACMD41,VOLTAGE_MASK); - //ACMD41 sends R3 as response, whose CRC is wrong. - if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::CRCFail) - { - //MMC card - DBG("MMC card\n"); - return MMC; - } else { - //SDv1 card - for(int i=0;i<INIT_TIMEOUT;i++) - { - //ACMD41 sends R3 as response, whose CRC is wrong. - if(r.getError()!=CmdResult::Ok && - r.getError()!=CmdResult::CRCFail) - { - r.validateError(); - return Invalid; - } - if((r.getResponse() & (1<<31))==0) //Busy bit - { - Thread::sleep(10); - //Send again command - r=Command::send(Command::ACMD41,VOLTAGE_MASK); - continue; - } - if((r.getResponse() & VOLTAGE_MASK)==0) - { - DBGERR("ACMD41 validation: voltage range fail\n"); - return Invalid; - } - DBG("ACMD41 validation: looped %d times\nSDv1\n",i); - return SDv1; - } - DBGERR("ACMD41 validation: looped until timeout\n"); - return Invalid; - } - } -} - -// -// Disk class -// - -bool Disk::isAvailable() -{ - bool result=sdCardSense(); - DBG("Disk::isAvailable(): %d\n",result); - return result; -} - -void Disk::init() -{ - initSDIOPeripheral(); - - // This is more important than it seems, since CMD55 requires the card's RCA - // as argument. During initalization, after CMD0 the card has an RCA of zero - // so without this line ACMD41 will fail and the card won't be initialized. - Command::setRca(0); - - //Send card reset command - CmdResult r=Command::send(Command::CMD0,0); - if(r.validateError()==false) return; - - cardType=detectCardType(); - if(cardType==Invalid) return; //Card detect failed - if(cardType==MMC) return; //MMC cards currently unsupported - - // Now give an RCA to the card. In theory we should loop and enumerate all - // the cards but this driver supports only one card. - r=Command::send(Command::CMD2,0); - //CMD2 sends R2 response, whose CMDINDEX field is wrong - if(r.getError()!=CmdResult::Ok && r.getError()!=CmdResult::RespNotMatch) - { - r.validateError(); - return; - } - r=Command::send(Command::CMD3,0); - if(r.validateR6Response()==false) return; - Command::setRca(r.getResponse()>>16); - DBG("Got RCA=%u\n",Command::getRca()); - if(Command::getRca()==0) - { - //RCA=0 can't be accepted, since it is used to deselect cards - DBGERR("RCA=0 is invalid\n"); - return; - } - - //Lastly, try selecting the card and configure the latest bits - { - CardSelector selector; - if(selector.succeded()==false) return; - - r=Command::send(Command::CMD13,Command::getRca()<<16);//Get status - if(r.validateR1Response()==false) return; - if(r.getState()!=4) //4=Tran state - { - DBGERR("CMD7 was not able to select card\n"); - return; - } - - r=Command::send(Command::ACMD6,2); //Set 4 bit bus width - if(r.validateR1Response()==false) return; - - if(cardType!=SDHC) - { - r=Command::send(Command::CMD16,512); //Set 512Byte block length - if(r.validateR1Response()==false) return; - } - } - - // Now that card is initialized, perform self calibration of maximum - // possible read/write speed. This as a side effect enables 4bit bus width. - ClockController::calibrateClockSpeed(); - - DBG("Disk::init(): Success\n"); - diskInitialized=true; -} - -bool Disk::read(unsigned char *buffer, unsigned int lba, - unsigned char nSectors) -{ - DBG("Disk::read(): nSectors=%d\n",nSectors); - if(!BufferConverter::isWordAligned(buffer)) DBG("Buffer misaligned\n"); - - for(int i=0;i<ClockController::getRetryCount();i++) - { - //Select card - CardSelector selector; - if(selector.succeded()==false) continue; - - if(nSectors==1) - { - if(singleBlockRead(buffer,lba)==false) continue; - } else { - // Multiple block read - // Currently implemented with N calls to single block read - unsigned char *tempBuffer=buffer; - unsigned int tempLba=lba; - for(unsigned int i=0;i<nSectors;i++) - { - if(singleBlockRead(tempBuffer,tempLba)==false) continue; - tempBuffer+=512; - tempLba++; - } - } - if(i>0) DBGERR("Read: required %d retries\n",i); - return true; - } - return false; -} - -bool Disk::write(const unsigned char *buffer, unsigned int lba, - unsigned char nSectors) -{ - DBG("Disk::write(): nSectors=%d\n",nSectors); - if(!BufferConverter::isWordAligned(buffer)) DBG("Buffer misaligned\n"); - - for(int i=0;i<ClockController::getRetryCount();i++) - { - //Select card - CardSelector selector; - if(selector.succeded()==false) continue; - - if(nSectors==1) - { - if(singleBlockWrite(buffer,lba)==false) continue; - } else { - // Multiple block write - // Currently implemented with N calls to single block write - const unsigned char *tempBuffer=buffer; - unsigned int tempLba=lba; - for(unsigned int i=0;i<nSectors;i++) - { - if(singleBlockWrite(tempBuffer,tempLba)==false) continue; - tempBuffer+=512; - tempLba++; - } - } - if(i>0) DBGERR("Write: required %d retries\n",i); - return true; - } - return false; -} - -bool Disk::sync() -{ - DBG("Disk::sync()\n"); - //Note: no need to select card, since status can be queried even with card - //not selected. - return waitForCardReady(); -} - -bool Disk::diskInitialized=false; - -} //namespace miosix diff --git a/miosix/arch/cortexM4_stm32/common/drivers/dcc.cpp b/miosix/arch/cortexM4_stm32/common/drivers/dcc.cpp deleted file mode 100644 index cc50138b51110a678cadb45c49aaae18ffa6f1d4..0000000000000000000000000000000000000000 --- a/miosix/arch/cortexM4_stm32/common/drivers/dcc.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2011 by Terraneo Federico * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * As a special exception, if other files instantiate templates or use * - * macros or inline functions from this file, or you compile this file * - * and link it with other works to produce a work based on this file, * - * this file does not by itself cause the resulting work to be covered * - * by the GNU General Public License. However the source code for this * - * file must still be made available in accordance with the GNU General * - * Public License. This exception does not invalidate any other reasons * - * why a work based on this file might be covered by the GNU General * - * Public License. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see <http://www.gnu.org/licenses/> * - ***************************************************************************/ - -/* - * DCC support inspired by dcc_stdio.c in OpenOCD - */ - -#include <algorithm> -#include <cstring> -#include "pthread.h" -#include "miosix.h" -#include "dcc.h" - -using namespace std; - -/** - * Send data to the host - */ -static void send(unsigned char c) -{ - const unsigned int busy=1; - while(CoreDebug->DCRDR & busy) ; - CoreDebug->DCRDR=(static_cast<unsigned int>(c)<<8) | busy; -} - -/** - * Send a line of text to the host. - * OpenOCD will insert a \n after each line, unfortunately, - * as this complicates things. - */ -void debugStr(const char *str, int length) -{ - //If not being debugged, don't print anything - if((CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)==0) return; - - if(length<0) length=strlen(str); - if(length>0 && str[0]=='\r') str++, length--; //TODO: better \r\n removal - if(length>0 && str[0]=='\n') str++, length--; - if(length==0) return; - send(1); //1=sending a string - send(0); - send(length & 0xff); - send(length>>8); - for(int i=0;i<length;i++) send(*str++); - //OpenOCD expects data in 4 byte blocks, so send trailing zeros to align - int remaining=4-(length & 3); - if(remaining<4) for(int i=0;i<remaining;i++) send(0); -} - -/// Mutex for locking concurrent access to debug channel -static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; - -void debugWrite(const char *str, unsigned int len) -{ - pthread_mutex_lock(&mutex); - debugStr(str,len); - pthread_mutex_unlock(&mutex); -} - -void IRQdebugWrite(const char *str) -{ - //Actually, there is a race condition if IRQdebugWrite is called from an - //interrupt while the main code is printing with debugWrite, but it is - //not such an issue since IRQdebugWrite is called only - //- at boot before the kernel is started, so there are no other threads - //- in case of a serious error, to print what went wrong before rebooting - //In the second case, which is rare, the data may not be printed correctly - debugStr(str,-1); -} diff --git a/miosix/arch/cortexM4_stm32/common/drivers/dcc.h b/miosix/arch/cortexM4_stm32/common/drivers/dcc.h deleted file mode 100644 index 88abbb3bc10d6a646ef56f24644b7a994436fb8c..0000000000000000000000000000000000000000 --- a/miosix/arch/cortexM4_stm32/common/drivers/dcc.h +++ /dev/null @@ -1,46 +0,0 @@ - /*************************************************************************** - * Copyright (C) 2011 by Terraneo Federico * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * As a special exception, if other files instantiate templates or use * - * macros or inline functions from this file, or you compile this file * - * and link it with other works to produce a work based on this file, * - * this file does not by itself cause the resulting work to be covered * - * by the GNU General Public License. However the source code for this * - * file must still be made available in accordance with the GNU General * - * Public License. This exception does not invalidate any other reasons * - * why a work based on this file might be covered by the GNU General * - * Public License. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see <http://www.gnu.org/licenses/> * - ***************************************************************************/ - -#ifndef DCC_H -#define DCC_H - - /** - * Write debugging data. If a OpenOCD is connected via JTAG or SWD, - * data will be printed on the console - * \param str string, can also be not null terminated - * \param len string length - */ -void debugWrite(const char *str, unsigned int len); - - /** - * Write debugging data from within an interrupt or with interrupts disabled. - * If a OpenOCD is connected via JTAG or SWD, data will be printed on the console - * \param str string, needs to be null terminated - */ -void IRQdebugWrite(const char *str); - -#endif //DCC_H diff --git a/miosix/arch/cortexM4_stm32/stm32f407vg_stm32f4discovery/interfaces-impl/bsp_impl.h b/miosix/arch/cortexM4_stm32/stm32f407vg_stm32f4discovery/interfaces-impl/bsp_impl.h index d290929a515115a96e2fc04d8a22aaac22bf4ede..f2a82c9087e17a230a91aa41d4646c9f7bdadfdb 100644 --- a/miosix/arch/cortexM4_stm32/stm32f407vg_stm32f4discovery/interfaces-impl/bsp_impl.h +++ b/miosix/arch/cortexM4_stm32/stm32f407vg_stm32f4discovery/interfaces-impl/bsp_impl.h @@ -60,13 +60,13 @@ inline void ledOff() } ///\internal Pin connected to SD card detect -//TODO: no filesystem typedef Gpio<GPIOA_BASE,8> sdCardDetect; +//no filesystem typedef Gpio<GPIOA_BASE,8> sdCardDetect; /** * Polls the SD card sense GPIO * \return true if there is an uSD card in the socket. */ -/*TODO: no filesystem +/*no filesystem inline bool sdCardSense() { return sdCardDetect::value()==0; diff --git a/miosix/arch/cortexM4_stm32/stm32f407vg_stm32f4discovery/interfaces-impl/console.cpp b/miosix/arch/cortexM4_stm32/stm32f407vg_stm32f4discovery/interfaces-impl/console.cpp index fc7c3521124596b1182072143369bab6f4905bf3..43fad164e9ffaac70e3973483e5033e47c7f3043 100644 --- a/miosix/arch/cortexM4_stm32/stm32f407vg_stm32f4discovery/interfaces-impl/console.cpp +++ b/miosix/arch/cortexM4_stm32/stm32f407vg_stm32f4discovery/interfaces-impl/console.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Terraneo Federico * + * Copyright (C) 2010, 2011, 2012 by Terraneo Federico * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * diff --git a/miosix/config/Makefile.inc b/miosix/config/Makefile.inc index 4c719acf5666536532e45872629c56873e4a2ded..1b536101930b001d2ce2aaa36d76eab7022e8886 100644 --- a/miosix/config/Makefile.inc +++ b/miosix/config/Makefile.inc @@ -18,7 +18,7 @@ OPT_BOARD := stm32f103ze_stm3210e-eval #OPT_BOARD := stm32f100rb_stm32vldiscovery #OPT_BOARD := stm32f103ve_strive_mini #OPT_BOARD := stm32f103ze_redbull_v2 -àOPT_BOARD := stm32f407vg_stm32f4discovery +#OPT_BOARD := stm32f407vg_stm32f4discovery ## ## Optimization flags, choose one. @@ -143,6 +143,11 @@ endif ## a new board or porting Miosix to a new architecture ## ############################################################################ +ifneq ($(MAKEFILE_VERSION),1.01) + $(error You are using an incompatible makefile. Make sure it matches \ + the one distributed with the current version of the kernel) +endif + ## ## First, auto guess architecture name from board name ## @@ -183,6 +188,8 @@ CXXFLAGS_BASE := -D_MIOSIX=\"$(OPT_BOARD)\" ## ARCHITECTURE: arm7_lpc2000 ## ifeq ($(ARCH),arm7_lpc2000) + ## Base directory with header files for this board + ARCH_INC := arch/arm7_lpc2000/common ##------------------------------------------------------------------------- ## BOARD: lpc2138_miosix_board @@ -240,9 +247,6 @@ ifeq ($(ARCH),arm7_lpc2000) -Wl,-T./miosix/$(LINKER_SCRIPT) $(OPT_EXCEPT) \ $(OPT_OPTIMIZATION) -nostdlib - ## Base directory with header files for this board - ARCH_INC := arch/arm7_lpc2000/common - ## Select architecture specific files ## These are the files in arch/<arch name>/common #ARCH_SRC += Nothing to add @@ -257,6 +261,8 @@ ifeq ($(ARCH),arm7_lpc2000) ## ARCHITECTURE: cortexM3_stm32 ## else ifeq ($(ARCH),cortexM3_stm32) + ## Base directory with header files for this board + ARCH_INC := arch/cortexM3_stm32/common ##------------------------------------------------------------------------- ## BOARD: stm32f103ze_stm3210e-eval @@ -275,7 +281,7 @@ else ifeq ($(ARCH),cortexM3_stm32) ## These are the files in arch/<arch name>/<board name> ARCH_SRC := \ $(BOARD_INC)/interfaces-impl/console.cpp \ - $(BOARD_INC)/interfaces-impl/disk.cpp \ + $(ARCH_INC)/interfaces-impl/disk.cpp \ $(BOARD_INC)/interfaces-impl/bsp.cpp ## Add a #define to allow querying board name @@ -308,7 +314,7 @@ else ifeq ($(ARCH),cortexM3_stm32) ## These are the files in arch/<arch name>/<board name> ARCH_SRC := \ $(BOARD_INC)/interfaces-impl/console.cpp \ - $(BOARD_INC)/interfaces-impl/disk.cpp \ + $(ARCH_INC)/interfaces-impl/disk.cpp \ $(BOARD_INC)/interfaces-impl/bsp.cpp ## Add a #define to allow querying board name @@ -381,7 +387,7 @@ else ifeq ($(ARCH),cortexM3_stm32) ## These are the files in arch/<arch name>/<board name> ARCH_SRC := \ $(BOARD_INC)/interfaces-impl/console.cpp \ - $(BOARD_INC)/interfaces-impl/disk.cpp \ + $(ARCH_INC)/interfaces-impl/disk.cpp \ $(BOARD_INC)/interfaces-impl/bsp.cpp ## Add a #define to allow querying board name @@ -416,7 +422,7 @@ else ifeq ($(ARCH),cortexM3_stm32) ## These are the files in arch/<arch name>/<board name> ARCH_SRC := \ $(BOARD_INC)/interfaces-impl/console.cpp \ - $(BOARD_INC)/interfaces-impl/disk.cpp \ + $(ARCH_INC)/interfaces-impl/disk.cpp \ $(BOARD_INC)/interfaces-impl/bsp.cpp ## Add a #define to allow querying board name @@ -469,15 +475,12 @@ else ifeq ($(ARCH),cortexM3_stm32) -Wl,-T./miosix/$(LINKER_SCRIPT) $(OPT_EXCEPT) \ $(OPT_OPTIMIZATION) -nostdlib - ## Base directory with header files for this board - ARCH_INC := arch/cortexM3_stm32/common - ## Select architecture specific files ## These are the files in arch/<arch name>/common ARCH_SRC += \ + arch/common/drivers/dcc.cpp \ $(ARCH_INC)/core/interrupts.cpp \ $(ARCH_INC)/drivers/serial.cpp \ - $(ARCH_INC)/drivers/dcc.cpp \ $(ARCH_INC)/interfaces-impl/portability.cpp \ $(ARCH_INC)/interfaces-impl/delays.cpp \ $(ARCH_INC)/CMSIS/core_cm3.c \ @@ -487,6 +490,8 @@ else ifeq ($(ARCH),cortexM3_stm32) ## ARCHITECTURE: cortexM4_stm32 ## else ifeq ($(ARCH),cortexM4_stm32) + ## Base directory with else header files for this board + ARCH_INC := arch/cortexM4_stm32/common ##------------------------------------------------------------------------- ## BOARD: stm32f4discovery @@ -554,14 +559,11 @@ else ifeq ($(ARCH),cortexM4_stm32) -Wl,-T./miosix/$(LINKER_SCRIPT) $(OPT_EXCEPT) \ $(OPT_OPTIMIZATION) -nostdlib - ## Base directory with else header files for this board - ARCH_INC := arch/cortexM4_stm32/common - ## Select architecture specific files ## These are the files in arch/<arch name>/common ARCH_SRC += \ + arch/common/drivers/dcc.cpp \ $(ARCH_INC)/core/interrupts.cpp \ - $(ARCH_INC)/drivers/dcc.cpp \ $(ARCH_INC)/interfaces-impl/portability.cpp \ $(ARCH_INC)/interfaces-impl/delays.cpp \ $(ARCH_INC)/interfaces-impl/gpio_impl.cpp \ diff --git a/miosix/config/miosix_settings.h b/miosix/config/miosix_settings.h index d451d2b913f9e4f66d6c759f73e4f21f9adfccaf..879e4424ba23ff24ee989c90f4a4da29d74a0f90 100644 --- a/miosix/config/miosix_settings.h +++ b/miosix/config/miosix_settings.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008, 2009, 2010, 2011 by Terraneo Federico * + * Copyright (C) 2008, 2009, 2010, 2011, 2012 by Terraneo Federico * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * diff --git a/miosix/doc/textdoc/Changelog.txt b/miosix/doc/textdoc/Changelog.txt index b0fdb28751edf33d9fa75e8b284ed5e307a36f6a..d883c645d4cf975dea08ad067cafbd6fa84d0bf6 100644 --- a/miosix/doc/textdoc/Changelog.txt +++ b/miosix/doc/textdoc/Changelog.txt @@ -1,6 +1,14 @@ Changelog for Miosix np embedded OS +v1.60 +- Refactored the arch/ directory to avoid code duplication +- Breaking change in the Makefiles v1.59 +- Added arch/cortexM4_stm32 architecture. The current GCC used to build Miosix + does not yet support the M4 but since the instruction set is backward + compatible the kernel is currently compiled with -mcpu=cortex-m3. This + means that floating point operations are still emulated in software, and will + be fixed when the compiler is upgraded. - Added a couple of examples, including one using BufferQueue to synchronize between a thread and a DMA. - Added test to testsuite for BufferQueue diff --git a/miosix/doc/textdoc/Directories.txt b/miosix/doc/textdoc/Directories.txt index 53105b93cff27f079309d112a3df2cee5872e1a0..586d283879ada2be902e10bb78a41392ad160134 100644 --- a/miosix/doc/textdoc/Directories.txt +++ b/miosix/doc/textdoc/Directories.txt @@ -1,53 +1,60 @@ Miosix 1.50+ directory tree +Directories marked with [x] contain code or configuration files that are used to +build the kernel while directories marked with [ ] contain other stuff +(documentation, examples). -[ ] //Root directory, contains main.cpp and in general the application code +[x] //Root directory, contains main.cpp and in general the application code | - +--[ ] miosix //Contains all Miosix OS code + +--[x] miosix //Contains all Miosix OS code | - +--[ ] config //Contains arch-independent configuration files + +--[x] config //Contains arch-independent configuration files | - +--[ ] util //Contains utility code, architcture independent + +--[x] util //Contains utility code, architcture independent | - +--[ ] kernel //Contains the architecture independent kernel part + +--[x] kernel //Contains the architecture independent kernel part | | - | +--[ ] filesystem //filesystem code + | +--[x] filesystem //Filesystem code | | - | +--[ ] schedulers //Contains the various schedulers + | +--[x] schedulers //Contains the various schedulers | | - | +--[ ] priority + | +--[x] priority | | - | +--[ ] control_based + | +--[x] control_based | | - | +--[ ] edf + | +--[x] edf | +--[ ] doc //Kernel documentation (doxygen + pdf + txt) | - +--[ ] interfaces //Contains interfaces between kernel and architecture + +--[x] interfaces //Contains interfaces between kernel and architecture | //dependent code | - +--[ ] arch //Contains architecture dependent code + +--[ ] compiler //Contains scripts used to build GCC with the patches + | //required to compile the kernel + | + +--[ ] examples //Contains some example applications + | + +--[ ] testsuite //Contains the kernel testsuite + | + +--[ ] temp //Contains miscellaneous stuff + | + +--[ ] bootloaders //Contain custom bootloaders for some boards + | + +--[x] arch //Contains architecture dependent code + | + +--[x] common //Contains drivers that are usable across multiple arch | - +--[ ] arm7_lpc2000 //Name of folder is 'cpu model' + 'mcu family' + +--[x] arm7_lpc2000 //Name of folder is 'cpu model' + 'mcu family' | | - | +--[ ] common //Common code for this architecture + | +--[x] common //Common code for this architecture | | - | +--[ ] lpc2138_miosix_board //Name is 'chip name'+'_'+'board name' + | +--[x] lpc2138_miosix_board //Name is 'chip name'+'_'+'board name' | | - | +--[ ] interfaces-impl //Implmentation of miosix/interfaces + | +--[x] interfaces-impl //Implmentation of miosix/interfaces | | - | +--[ ] drivers //Serial ports, board support package, etc. + | +--[x] drivers //Serial ports, board support package, etc. | | - | +--[ ] core //Boot and exception handling code + | +--[x] core //Boot and exception handling code | - +--[ ] cortexM3_stm32 - | - +--[ ] common - | - +--[ ] stm32f103ze_stm3210e-eval - | - +--[ ] interfaces-impl - | - +--[ ] drivers - | - +--[ ] core - \ No newline at end of file + . + . Other target architectures + . diff --git a/miosix/examples/sad_trombone/Makefile b/miosix/examples/sad_trombone/Makefile index 2da45e6fa530e5e999cc5a636a585a46860395d8..9b484b4da268f9aa561a5e9c3f78b365117e79e3 100644 --- a/miosix/examples/sad_trombone/Makefile +++ b/miosix/examples/sad_trombone/Makefile @@ -2,6 +2,7 @@ ## Makefile for Miosix np embedded OS ## TFT:Terraneo Federico Technlogies ## +MAKEFILE_VERSION := 1.01 include miosix/config/Makefile.inc ## @@ -33,15 +34,15 @@ INCLUDE_DIRS := OBJ := $(addsuffix .o, $(basename $(SRC))) ## Includes the miosix base directory for C/C++ -CXXFLAGS := $(CXXFLAGS_BASE) -I. -I./miosix -I./miosix/$(ARCH_INC) \ - -I./miosix/$(BOARD_INC) $(INCLUDE_DIRS) -CFLAGS := $(CFLAGS_BASE) -I. -I./miosix -I./miosix/$(ARCH_INC) \ - -I./miosix/$(BOARD_INC) $(INCLUDE_DIRS) +CXXFLAGS := $(CXXFLAGS_BASE) -I. -Imiosix -Imiosix/arch/common \ + -Imiosix/$(ARCH_INC) -Imiosix/$(BOARD_INC) $(INCLUDE_DIRS) +CFLAGS := $(CFLAGS_BASE) -I. -Imiosix -Imiosix/arch/common \ + -Imiosix/$(ARCH_INC) -Imiosix/$(BOARD_INC) $(INCLUDE_DIRS) AFLAGS := $(AFLAGS_BASE) LFLAGS := $(LFLAGS_BASE) -LINK_LIBS := $(LIBS) -Wl,--start-group -L./miosix -lmiosix -lstdc++ -lc -lm \ - -lg -lgcc -Wl,--end-group +LINK_LIBS := $(LIBS) -L./miosix -Wl,--start-group -lmiosix -lstdc++ -lc -lm \ + -lgcc -Wl,--end-group all: all-recursive main @@ -70,8 +71,7 @@ main: main.elf main.elf: $(OBJ) miosix/libmiosix.a @ echo "linking" - $(CXX) $(LFLAGS) -o main.elf $(OBJ) miosix/$(BOOT_FILE) \ - miosix/kernel/syscalls.o $(LINK_LIBS) + $(CXX) $(LFLAGS) -o main.elf $(OBJ) miosix/$(BOOT_FILE) $(LINK_LIBS) %.o: %.s $(AS) $(AFLAGS) $< -o $@ diff --git a/miosix/testsuite/testsuite.cpp b/miosix/testsuite/testsuite.cpp index 5075d55f8dfe52964417a4822ab2934c63e5d0b7..4a288cf80752d0ce5224271e6ae4b43fe8503167 100644 --- a/miosix/testsuite/testsuite.cpp +++ b/miosix/testsuite/testsuite.cpp @@ -2963,7 +2963,7 @@ static void benchmark_1() t.start(); extern unsigned long _data asm("_data"); char *data=reinterpret_cast<char*>(&_data); - #ifdef _ARCH_CORTEXM3_STM32 + #if defined(_ARCH_CORTEXM3_STM32) || defined(_ARCH_CORTEXM4_STM32) memDump(data,2048); t.stop(); //every line dumps 16 bytes, and is 81 char long (considering \r\n) @@ -2981,7 +2981,7 @@ static void benchmark_1() t.interval()*1000)/TICK_FREQ); unsigned int baudrate=165888*10000/((t.interval()*1000)/TICK_FREQ); iprintf("Effective baud rate =%u\n",baudrate); - #endif //_ARCH_CORTEXM3_STM32 + #endif //_ARCH_CORTEXM3_STM32 || _ARCH_CORTEXM4_STM32 } // diff --git a/miosix/util/version.cpp b/miosix/util/version.cpp index f59c4e058a3f6389a10437b90ffb3ef9adce7b4b..747097702db88783daeabd1e28eb76db65ddba8b 100644 --- a/miosix/util/version.cpp +++ b/miosix/util/version.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010, 2011 by Terraneo Federico * + * Copyright (C) 2010, 2011, 2012 by Terraneo Federico * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -37,7 +37,7 @@ namespace miosix { #define AU #endif -const char AU ver[]="Miosix v1.59 (" _MIOSIX ", " __DATE__ " " __TIME__ CV ")"; +const char AU ver[]="Miosix v1.60 (" _MIOSIX ", " __DATE__ " " __TIME__ CV ")"; const char *getMiosixVersion() { diff --git a/miosix_np_2/nbproject/configurations.xml b/miosix_np_2/nbproject/configurations.xml index 8e1a4edea8b146fa0628279f167a2ff88dfc3d6f..d27495a61bccaac30109b2bc88171727433bfa77 100644 --- a/miosix_np_2/nbproject/configurations.xml +++ b/miosix_np_2/nbproject/configurations.xml @@ -36,6 +36,12 @@ <in>board_settings.h</in> </df> </df> + <df name="common"> + <df name="drivers"> + <in>dcc.cpp</in> + <in>dcc.h</in> + </df> + </df> <df name="cortexM3_stm32"> <df name="common"> <df name="CMSIS"> @@ -50,14 +56,13 @@ <in>interrupts.h</in> </df> <df name="drivers"> - <in>dcc.cpp</in> - <in>dcc.h</in> <in>serial.cpp</in> <in>serial.h</in> </df> <df name="interfaces-impl"> <in>arch_registers_impl.h</in> <in>delays.cpp</in> + <in>disk.cpp</in> <in>endianness_impl.h</in> <in>gpio_impl.h</in> <in>portability.cpp</in> @@ -84,7 +89,6 @@ <in>bsp.cpp</in> <in>bsp_impl.h</in> <in>console.cpp</in> - <in>disk.cpp</in> <in>hwmapping.h</in> </df> <in>board_settings.h</in> @@ -97,7 +101,6 @@ <in>bsp.cpp</in> <in>bsp_impl.h</in> <in>console.cpp</in> - <in>disk.cpp</in> <in>hwmapping.h</in> </df> <in>board_settings.h</in> @@ -110,7 +113,6 @@ <in>bsp.cpp</in> <in>bsp_impl.h</in> <in>console.cpp</in> - <in>disk.cpp</in> <in>hwmapping.h</in> </df> <in>board_settings.h</in> @@ -123,7 +125,6 @@ <in>bsp.cpp</in> <in>bsp_impl.h</in> <in>console.cpp</in> - <in>disk.cpp</in> </df> <in>board_settings.h</in> </df> @@ -132,11 +133,19 @@ <df name="common"> <df name="CMSIS"> <df name="Documentation"> + <in>CMSIS-SVD_Schema_1_0.xsd</in> + <in>CMSIS_CM4_SIMD.htm</in> + <in>CMSIS_Core.htm</in> + <in>CMSIS_DebugSupport.htm</in> + <in>CMSIS_History.htm</in> + <in>CMSIS_Logo_Final.jpg</in> + <in>CMSIS_System_View_Description.htm</in> </df> <in>core_cm4.h</in> <in>core_cm4_simd.h</in> <in>core_cmFunc.h</in> <in>core_cmInstr.h</in> + <in>index.htm</in> <in>stm32f4xx.h</in> <in>system_stm32f4xx.c</in> <in>system_stm32f4xx.h</in> @@ -146,8 +155,6 @@ <in>interrupts.h</in> </df> <df name="drivers"> - <in>dcc.cpp</in> - <in>dcc.h</in> </df> <df name="interfaces-impl"> <in>arch_registers_impl.h</in> @@ -171,6 +178,9 @@ <in>console.cpp</in> </df> <in>board_settings.h</in> + <in>stm32_1m+192k_ram.ld</in> + <in>stm32_1m+192k_rom.ld</in> + <in>stm32f4discovery.cfg</in> </df> </df> </df> @@ -272,7 +282,6 @@ <df name="miosix_np_2"> </df> <in>main.cpp</in> - <in>testsuite.cpp</in> </df> <logicalFolder name="ExternalFiles" displayName="Important Files" @@ -302,8 +311,11 @@ <incDir> <pElem>..</pElem> <pElem>../miosix</pElem> + <pElem>../miosix/arch/common</pElem> <pElem>../miosix/arch/cortexM3_stm32/common</pElem> <pElem>../miosix/arch/cortexM3_stm32/stm32f103ze_stm3210e-eval</pElem> + <pElem>../miosix/arch/cortexM4_stm32/common</pElem> + <pElem>../miosix/arch/cortexM4_stm32/stm32f407vg_stm32f4discovery</pElem> <pElem>../miosix/arch/arm7_lpc2000/common</pElem> <pElem>../miosix/arch/arm7_lpc2000/lpc2138_miosix_board</pElem> </incDir> @@ -316,8 +328,11 @@ <incDir> <pElem>..</pElem> <pElem>../miosix</pElem> + <pElem>../miosix/arch/common</pElem> <pElem>../miosix/arch/cortexM3_stm32/common</pElem> <pElem>../miosix/arch/cortexM3_stm32/stm32f103ze_stm3210e-eval</pElem> + <pElem>../miosix/arch/cortexM4_stm32/common</pElem> + <pElem>../miosix/arch/cortexM4_stm32/stm32f407vg_stm32f4discovery</pElem> <pElem>../miosix/arch/arm7_lpc2000/common</pElem> <pElem>../miosix/arch/arm7_lpc2000/lpc2138_miosix_board</pElem> </incDir> diff --git a/miosix_np_2/nbproject/private/configurations.xml b/miosix_np_2/nbproject/private/configurations.xml index ceed23356da1cb8dcffeb4a5ed02c74315d8ac50..20f1d270a8caf82d9e288f7b45572454f4be2ff8 100644 --- a/miosix_np_2/nbproject/private/configurations.xml +++ b/miosix_np_2/nbproject/private/configurations.xml @@ -20,6 +20,7 @@ <gdb_buildfirst gdb_buildfirst_overriden="false" gdb_buildfirst_old="false"/> </dbx_gdbdebugger> <gizmo_options version="3"> + <configurationname>GizmoSimple</configurationname> </gizmo_options> <nativedebugger version="1"> <engine>gdb</engine>