diff --git a/tests/verilator/sdcard/Makefile b/tests/verilator/sdcard/Makefile new file mode 100644 index 0000000..1cfcfe2 --- /dev/null +++ b/tests/verilator/sdcard/Makefile @@ -0,0 +1,31 @@ +PROJECT=sd_card +NOWARN = # --report-unoptflat # -Wno-UNOPTFLAT +XTRA_OBJS = mmc2.o utils.o diskio_mmc.o pff.o pff_file.o +ODIR = obj_dir +XTRA_OBJS_PATH=$(addprefix $(ODIR)/, $(XTRA_OBJS)) + +all: $(PROJECT).vcd + +$(ODIR)/%.o: %.c $(DEPS) + $(CC) -c -o $@ $< $(CFLAGS) + +$(ODIR)/stamp: $(PROJECT).v $(PROJECT)_tb.cpp + verilator $(NOWARN) --cc --trace --exe $(PROJECT).v $(PROJECT)_tb.cpp $(XTRA_OBJS) + touch $(ODIR)/stamp + +$(ODIR)/V$(PROJECT): $(ODIR)/stamp $(XTRA_OBJS_PATH) + make -j -C $(ODIR)/ -f V$(PROJECT).mk V$(PROJECT) + +$(PROJECT).vcd: $(ODIR)/V$(PROJECT) + $(ODIR)/V$(PROJECT) + +run: $(ODIR)/V$(PROJECT) + $(ODIR)/V$(PROJECT) + +clean: + rm -rf $(ODIR) + rm -f $(PROJECT).vcd + rm -f *~ + +view: $(PROJECT).vcd + gtkwave $< $(PROJECT).sav & diff --git a/tests/verilator/sdcard/diskio.h b/tests/verilator/sdcard/diskio.h new file mode 100644 index 0000000..a023075 --- /dev/null +++ b/tests/verilator/sdcard/diskio.h @@ -0,0 +1,42 @@ +/*----------------------------------------------------------------------- +/ PFF - Low level disk interface modlue include file (C)ChaN, 2009 +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO + +#include "integer.h" + + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + + +/* Results of Disk Functions */ +typedef enum { + RES_OK = 0, /* 0: Function succeeded */ + RES_ERROR, /* 1: Disk error */ + RES_NOTRDY, /* 2: Not ready */ + RES_PARERR /* 3: Invalid parameter */ +} DRESULT; + + +/*---------------------------------------*/ +/* Prototypes for disk control functions */ + +DSTATUS disk_initialize (void); +DRESULT disk_readp (BYTE*, DWORD, WORD, WORD); +DRESULT disk_writep (const BYTE* buff, DWORD sofs, DWORD count); +void disk_writeflush(); + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ + +/* Card type flags (CardType) */ +#define CT_MMC 0x01 /* MMC ver 3 */ +#define CT_SD1 0x02 /* SD ver 1 */ +#define CT_SD2 0x04 /* SD ver 2 */ +#define CT_SDC (CT_SD1|CT_SD2) /* SD */ +#define CT_BLOCK 0x08 /* Block addressing */ + +#define _DISKIO +#endif diff --git a/tests/verilator/sdcard/diskio_mmc.c b/tests/verilator/sdcard/diskio_mmc.c new file mode 100644 index 0000000..fe66aa6 --- /dev/null +++ b/tests/verilator/sdcard/diskio_mmc.c @@ -0,0 +1,147 @@ +#include "diskio.h" + +#include "mmc.h" +#include "spi.h" + +//#include "printf.h" + +void mmcReadCached(u32 sector); +u32 n_actual_mmc_sector; +extern unsigned char mmc_sector_buffer[512]; + +void mmcReadCached(u32 sector) +{ + //debug("mmcReadCached"); + //plotnext(toatarichar(' ')); + //plotnextnumber(sector); + //debug("\n"); + if(sector==n_actual_mmc_sector) return; + //debug("mmcReadREAL"); + //plotnext(toatarichar(' ')); + //plotnextnumber(sector); + //debug("\n"); + + u08 ret,retry; + //predtim nez nacte jiny, musi ulozit soucasny + // TODO mmcWriteCachedFlush(); + //az ted nacte novy + retry=0; //zkusi to maximalne 256x + do + { + ret = mmcRead(sector); //vraci 0 kdyz ok + retry--; + } while (ret && retry); + while(ret); //a pokud se vubec nepovedlo, tady zustane zablokovany cely SDrive! + n_actual_mmc_sector=sector; +} + + + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize (void) +{ + DSTATUS stat; + + //printf(" in init "); + n_actual_mmc_sector = 0xffffffff; + for(;;) + { + mmc_init(); + if (0==mmcRead(1)) + break; + } + + //printf(" setting freq "); + + set_spi_clock_freq(); + + stat = RES_OK; + + return stat; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Partial Sector */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_readp ( + BYTE* dest, /* Pointer to the destination object */ + DWORD sector, /* Sector number (LBA) */ + WORD sofs, /* Offset in the sector */ + WORD count /* Byte count (bit15:destination) */ +) +{ + DRESULT res; + + /*debug("readp:"); + plotnextnumber(sector); + debug(" "); + plotnextnumber((int)dest); + debug(" "); + plotnextnumber(sofs); + debug(" "); + plotnextnumber(count); + debug(" "); + plotnextnumber(atari_sector_buffer); + debug(" "); + plotnextnumber(mmc_sector_buffer); + debug("\n"); + */ + // Put your code here + mmcReadCached(sector); + for(;count>0;++sofs,--count) + { + unsigned char x = mmc_sector_buffer[sofs]; + //printf("char:%c loc:%d ", x,sofs); + *dest++ = x; + } + + res = RES_OK; + + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Partial Sector */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_writep (const BYTE* buff, DWORD sofs, DWORD count) +{ + DRESULT res; + + int i=sofs; + int end=sofs+count; + int pos = 0; + for (;i!=end;++i,++pos) + { + mmc_sector_buffer[i] = buff[pos]; + //printf("char:%c loc:%d,", buff[pos],i); + } + + res = RES_OK; + + return res; +} + +void disk_writeflush() +{ + // Finalize write process + int retry=16; //zkusi to maximalne 16x + int ret; + //printf(":WSECT:%d",n_actual_mmc_sector); + do + { + ret = mmcWrite(n_actual_mmc_sector); //vraci 0 kdyz ok + retry--; + } while (ret && retry); + //printf(":WD:"); +} + + diff --git a/tests/verilator/sdcard/integer.h b/tests/verilator/sdcard/integer.h new file mode 100644 index 0000000..085b20a --- /dev/null +++ b/tests/verilator/sdcard/integer.h @@ -0,0 +1,37 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _FF_INTEGER +#define _FF_INTEGER + +#ifdef _WIN32 /* FatFs development platform */ + +#include +#include + +#else /* Embedded platform */ + +/* This type MUST be 8 bit */ +typedef unsigned char BYTE; +typedef unsigned char u08; +typedef unsigned char uint8_t; + +/* These types MUST be 16 bit */ +typedef short SHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; +typedef unsigned short u16; + +/* These types MUST be 16 bit or 32 bit */ +typedef int INT; +typedef unsigned int UINT; + +/* These types MUST be 32 bit */ +typedef long LONG; +typedef unsigned int DWORD; +typedef unsigned int u32; + +#endif + +#endif diff --git a/tests/verilator/sdcard/mmc.h b/tests/verilator/sdcard/mmc.h new file mode 100644 index 0000000..d188e11 --- /dev/null +++ b/tests/verilator/sdcard/mmc.h @@ -0,0 +1,145 @@ +/*! \file mmc.h \brief MultiMedia and SD Flash Card Interface. */ +//***************************************************************************** +// +// File Name : 'mmc.h' +// Title : MultiMedia and SD Flash Card Interface +// Author : Pascal Stang - Copyright (C) 2004 +// Created : 2004.09.22 +// Revised : 2004.09.22 +// Version : 0.1 +// Target MCU : Atmel AVR Series +// Editor Tabs : 4 +// +/// \ingroup driver_hw +/// \defgroup mmc MultiMedia and SD Flash Card Interface (mmc.c) +/// \code #include "mmc.h" \endcode +/// \par Description +/// This library offers some simple functions which can be used +/// to read and write data on a MultiMedia or SecureDigital (SD) Flash +/// Card. Although MM and SD Cards are designed to operate with their own +/// special bus wiring and protocols, both types of cards also provide a +/// simple SPI-like interface mode which is exceptionally useful when +/// attempting to use the cards in embedded systems. +/// +/// \par Wiring +/// To work with this library, the card must be wired to the SPI port of +/// the Atmel microcontroller as described below. Typical cards can +/// operate at up to 25MHz maximum SPI clock rate (thus faster than most +/// AVR's maximum SPI clock rate). +///
+///	      _________________
+///	     /  1 2 3 4 5 6 78 |	<- view of MMC/SD card looking at contacts
+///	    / 9                |	Pins 8 and 9 are present only on SD cards
+///	    |    MMC/SD Card   |
+///	    |                  |
+///	    --------------------
+///	    1 - CS   (chip select)          - wire to any available I/O pin(*)
+///	    2 - DIN  (data in, card<-host)  - wire to SPI MOSI pin
+///	    3 - VSS  (ground)               - wire to ground
+///	    4 - VDD  (power, 3.3V only?)    - wire to power (MIGHT BE 3.3V ONLY!)
+///	    5 - SCLK (data clock)           - wire to SPI SCK pin
+///	    6 - VSS  (ground)               - wire to ground
+///	    7 - DOUT (data out, card->host) - wire to SPI MISO pin
+///
+///	    (*) you must define this chip select I/O pin in mmcconf.h
+/// 
+/// \note This code is currently below version 1.0, and therefore is considered +/// to be lacking in some functionality or documentation, or may not be fully +/// tested. Nonetheless, you can expect most functions to work. +// +// ---------------------------------------------------------------------------- +// 17.8.2008 +// Bob!k & Raster, C.P.U. +// Original code was modified especially for the SDrive device. +// Some parts of code have been added, removed, rewrited or optimized due to +// lack of MCU AVR Atmega8 memory. +// ---------------------------------------------------------------------------- +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + + +#ifndef MMC_H +#define MMC_H + +#include "integer.h" + +// constants/macros/typdefs +// MMC commands (taken from sandisk MMC reference) +#define MMC_GO_IDLE_STATE 0 ///< initialize card to SPI-type access +#define MMC_SEND_OP_COND 1 ///< set card operational mode +#define MMC_SEND_CSD 9 ///< get card's CSD +#define MMC_SEND_CID 10 ///< get card's CID +#define MMC_SEND_STATUS 13 +#define MMC_SET_BLOCKLEN 16 ///< Set number of bytes to transfer per block +#define MMC_READ_SINGLE_BLOCK 17 ///< read a block +#define MMC_WRITE_BLOCK 24 ///< write a block +#define MMC_PROGRAM_CSD 27 +#define MMC_SET_WRITE_PROT 28 +#define MMC_CLR_WRITE_PROT 29 +#define MMC_SEND_WRITE_PROT 30 +#define MMC_TAG_SECTOR_START 32 +#define MMC_TAG_SECTOR_END 33 +#define MMC_UNTAG_SECTOR 34 +#define MMC_TAG_ERASE_GROUP_START 35 ///< Sets beginning of erase group (mass erase) +#define MMC_TAG_ERARE_GROUP_END 36 ///< Sets end of erase group (mass erase) +#define MMC_UNTAG_ERASE_GROUP 37 ///< Untag (unset) erase group (mass erase) +#define MMC_ERASE 38 ///< Perform block/mass erase +#define MMC_CRC_ON_OFF 59 ///< Turns CRC check on/off +// R1 Response bit-defines +#define MMC_R1_BUSY 0x80 ///< R1 response: bit indicates card is busy +#define MMC_R1_PARAMETER 0x40 +#define MMC_R1_ADDRESS 0x20 +#define MMC_R1_ERASE_SEQ 0x10 +#define MMC_R1_COM_CRC 0x08 +#define MMC_R1_ILLEGAL_COM 0x04 +#define MMC_R1_ERASE_RESET 0x02 +#define MMC_R1_IDLE_STATE 0x01 +// Data Start tokens +#define MMC_STARTBLOCK_READ 0xFE ///< when received from card, indicates that a block of data will follow +#define MMC_STARTBLOCK_WRITE 0xFE ///< when sent to card, indicates that a block of data will follow +#define MMC_STARTBLOCK_MWRITE 0xFC +// Data Stop tokens +#define MMC_STOPTRAN_WRITE 0xFD +// Data Error Token values +#define MMC_DE_MASK 0x1F +#define MMC_DE_ERROR 0x01 +#define MMC_DE_CC_ERROR 0x02 +#define MMC_DE_ECC_FAIL 0x04 +#define MMC_DE_OUT_OF_RANGE 0x04 +#define MMC_DE_CARD_LOCKED 0x04 +// Data Response Token values +#define MMC_DR_MASK 0x1F +#define MMC_DR_ACCEPT 0x05 +#define MMC_DR_REJECT_CRC 0x0B +#define MMC_DR_REJECT_WRITE_ERROR 0x0D + +// functions + +//! Initialize AVR<->MMC hardware interface. +/// Prepares hardware for MMC access. +void mmc_init(void); + +//! Initialize the card and prepare it for use. +/// Returns zero if successful. +//u08 mmcReset(void); + +//! Send card an MMC command. +/// Returns R1 result code. +//u08 mmcSendCommand(u08 cmd, u32 arg); + +//! Read 512-byte sector from card to buffer +/// Returns zero if successful. +u08 mmcRead(u32 sector); + +//! Write 512-byte sector from buffer to card +/// Returns zero if successful. +u08 mmcWrite(u32 sector); + +//! Internal command function. +/// Issues a generic MMC command as specified by cmd and arg. +//u08 mmcCommand(u08 cmd, u32 arg); + +#endif diff --git a/tests/verilator/sdcard/mmc2.c b/tests/verilator/sdcard/mmc2.c new file mode 100644 index 0000000..4a4beec --- /dev/null +++ b/tests/verilator/sdcard/mmc2.c @@ -0,0 +1,269 @@ +/*-------------------------------------------------------------------------*/ +/* PFF - Low level disk control module for PIC (C)ChaN, 2014 */ +/*-------------------------------------------------------------------------*/ + +#include "pff.h" +#include "diskio.h" + +//#include "printf.h" + +/*-------------------------------------------------------------------------*/ +/* Platform dependent macros and functions needed to be modified */ +/*-------------------------------------------------------------------------*/ + +#include + +#define SELECT() mmcChipSelect(1) +#define DESELECT() mmcChipSelect(0) + +unsigned char mmc_sector_buffer[512]; + +extern u32 n_actual_mmc_sector; + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + +/* Definitions for MMC/SDC command */ +#define CMD0 (0x40+0) /* GO_IDLE_STATE */ +#define CMD1 (0x40+1) /* SEND_OP_COND (MMC) */ +#define ACMD41 (0xC0+41) /* SEND_OP_COND (SDC) */ +#define CMD8 (0x40+8) /* SEND_IF_COND */ +#define CMD16 (0x40+16) /* SET_BLOCKLEN */ +#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ +#define CMD24 (0x40+24) /* WRITE_BLOCK */ +#define CMD55 (0x40+55) /* APP_CMD */ +#define CMD58 (0x40+58) /* READ_OCR */ + + +/* Card type flags (CardType) */ +#define CT_MMC 0x01 /* MMC ver 3 */ +#define CT_SD1 0x02 /* SD ver 1 */ +#define CT_SD2 0x04 /* SD ver 2 */ +#define CT_BLOCK 0x08 /* Block addressing */ + +BYTE CardType; + + +extern int debug_pos; + +void disk_debug() +{ +/* { + char buffer[512]; + set_pause_6502(1); + freeze(); + debug_pos = 0; + + printf("Hello world 5"); + debug_pos = 40; + + printf("Di"); + + debug_pos = 80; + disk_initialize(); + + printf("Did:%02x",CardType); + + debug_pos = 120; + + n_actual_mmc_sector = -1; + printf(" PP"); + disk_readp(&buffer[0],0,0,512); + printf(" DD"); + hexdump_pure(&buffer[0],512); + + wait_us(10000000); + restore(); + } +*/ +} + +/*-----------------------------------------------------------------------*/ +/* Send a command packet to the SDC/MMC */ +/*-----------------------------------------------------------------------*/ + +static +BYTE send_cmd ( + BYTE cmd, /* 1st byte (Start + Index) */ + DWORD arg /* Argument (32 bits) */ +) +{ + BYTE n, res; + + + if (cmd & 0x80) { /* ACMD is the command sequense of CMD55-CMD */ + cmd &= 0x7F; + res = send_cmd(CMD55, 0); + if (res > 1) return res; + } + + /* Select the card */ + DESELECT(); + spiTransferFF(); + SELECT(); + spiTransferFF(); + + /* Send a command packet */ + spiTransferByte(cmd); /* Start + Command index */ + spiTransferByte((BYTE)(arg >> 24)); /* Argument[31..24] */ + spiTransferByte((BYTE)(arg >> 16)); /* Argument[23..16] */ + spiTransferByte((BYTE)(arg >> 8)); /* Argument[15..8] */ + spiTransferByte((BYTE)arg); /* Argument[7..0] */ + n = 0x01; /* Dummy CRC + Stop */ + if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) */ + if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) */ + spiTransferByte(n); + + /* Receive a command response */ + n = 10; /* Wait for a valid response in timeout of 10 attempts */ + do { + res = spiTransferFF(); + } while ((res & 0x80) && --n); + + // printf("cmd(%x) = %x\n", cmd, res); + + return res; /* Return with the response value */ +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +---------------------------------------------------------------------------*/ + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +void mmc_init (void) +{ + BYTE n, cmd, ty, ocr[4]; + UINT tmr; + + // initialize SPI interface + spiInit(); + + DESELECT(); + for (n = 10; n; n--) spiTransferFF(); /* 80 Dummy clocks with CS=H */ + + ty = 0; + if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ + if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2 */ + for (n = 0; n < 4; n++) { + ocr[n] = spiTransferFF(); /* Get trailing return value of R7 resp */ + // printf("OCR[%d]=%x\n", n, ocr[n]); + } + if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ + for (tmr = 10000; tmr && send_cmd(ACMD41, 1UL << 30); tmr--) wait_us(100); /* Wait for leaving idle state (ACMD41 with HCS bit) */ + if (tmr && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */ + for (n = 0; n < 4; n++) { + ocr[n] = spiTransferFF(); + // printf("OCR[%d]=%x\n", n, ocr[n]); + } + ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 (HC or SC) */ + } + } + } else { /* SDv1 or MMCv3 */ + if (send_cmd(ACMD41, 0) <= 1) { + ty = CT_SD1; cmd = ACMD41; /* SDv1 */ + } else { + ty = CT_MMC; cmd = CMD1; /* MMCv3 */ + } + for (tmr = 10000; tmr && send_cmd(cmd, 0); tmr--) wait_us(100); /* Wait for leaving idle state */ + if (!tmr || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */ + ty = 0; + } + } + CardType = ty; + DESELECT(); + spiTransferFF(); + + // printf("TYPE=%x\n", ty); +} + + + +/*-----------------------------------------------------------------------*/ +/* Read partial sector */ +/*-----------------------------------------------------------------------*/ + +u08 mmcRead(u32 sector) +{ + BYTE rc; + UINT bc; + int res = 0; + + // printf("mr:%x",sector); + + if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */ + + if (send_cmd(CMD17, sector) == 0) { /* READ_SINGLE_BLOCK */ + + bc = 40000; + do { /* Wait for data packet */ + rc = spiTransferFF(); + } while (rc == 0xFF && --bc); + + if (rc == 0xFE) { /* A data packet arrived */ + bc = 514 - 512; + + u08 *buffer=mmc_sector_buffer; //natvrdo! + + spiReceiveData(buffer,buffer+512); + // hexdump(buffer, 512, 0); + + /* Skip remaining bytes and CRC */ + do spiTransferFF(); while (--bc); // checksum + } + } + else + { + res = 1; + } + + DESELECT(); + spiTransferFF(); + // printf("done\n"); + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write partial sector */ +/*-----------------------------------------------------------------------*/ + +u08 mmcWrite(u32 sc) +{ + u08 *buff=mmc_sector_buffer; //natvrdo! + + WORD wc; + + SELECT(); + + if (!(CardType & CT_BLOCK)) sc *= 512; /* Convert to byte address if needed */ + if (send_cmd(CMD24, sc) == 0) { /* WRITE_SINGLE_BLOCK */ + spiTransferByte(0xFF); spiTransferByte(0xFE); /* Data block header */ + wc = 512; /* Set byte counter */ + + while (wc) { /* Send data bytes to the card */ + spiTransferByte(*buff++); + wc--; + } + + DWORD bc = 2; + while (bc--) spiTransferByte(0); /* Fill left bytes and CRC with zeros */ + if ((spiTransferFF() & 0x1F) == 0x05) { /* Receive data resp and wait for end of write process in timeout of 500ms */ + for (bc = 5000; spiTransferFF() != 0xFF && bc; bc--) wait_us(100); /* Wait ready */ + } + DESELECT(); + spiTransferFF(); + } + return 0; +} diff --git a/tests/verilator/sdcard/mmcconf.h b/tests/verilator/sdcard/mmcconf.h new file mode 100644 index 0000000..c9691f7 --- /dev/null +++ b/tests/verilator/sdcard/mmcconf.h @@ -0,0 +1,33 @@ +/*! \file mmcconf.h \brief MultiMedia and SD Flash Card Interface Configuration. */ +//***************************************************************************** +// +// File Name : 'mmcconf.h' +// Title : MultiMedia and SD Flash Card Interface Configuration +// Author : Pascal Stang - Copyright (C) 2004 +// Created : 2004.09.22 +// Revised : 2004.09.22 +// Version : 0.1 +// Target MCU : Atmel AVR Series +// Editor Tabs : 4 +// +// NOTE: This code is currently below version 1.0, and therefore is considered +// to be lacking in some functionality or documentation, or may not be fully +// tested. Nonetheless, you can expect most functions to work. +// +// ---------------------------------------------------------------------------- +// 17.8.2008 +// Bob!k & Raster, C.P.U. +// Original code was modified especially for the SDrive device. +// Some parts of code have been added, removed, rewrited or optimized due to +// lack of MCU AVR Atmega8 memory. +// ---------------------------------------------------------------------------- +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + +#ifndef MMCCONF_H +#define MMCCONF_H + +#endif diff --git a/tests/verilator/sdcard/pff.c b/tests/verilator/sdcard/pff.c new file mode 100644 index 0000000..8a67759 --- /dev/null +++ b/tests/verilator/sdcard/pff.c @@ -0,0 +1,1145 @@ +/*----------------------------------------------------------------------------/ +/ Petit FatFs - FAT file system module R0.03 (C)ChaN, 2014 +/-----------------------------------------------------------------------------/ +/ Petit FatFs module is a generic FAT file system module for small embedded +/ systems. This is a free software that opened for education, research and +/ commercial developments under license policy of following trems. +/ +/ Copyright (C) 2014, ChaN, all right reserved. +/ +/ * The Petit FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------/ +/ Jun 15,'09 R0.01a First release. +/ +/ Dec 14,'09 R0.02 Added multiple code page support. +/ Added write funciton. +/ Changed stream read mode interface. +/ Dec 07,'10 R0.02a Added some configuration options. +/ Fixed fails to open objects with DBCS character. + +/ Jun 10,'14 R0.03 Separated out configuration options to pffconf.h. +/ Added _USE_LCC option. +/ Added _FS_FAT16 option. +/----------------------------------------------------------------------------*/ + +#include "pff.h" /* Petit FatFs configurations and declarations */ +#include "diskio.h" /* Declarations of low level disk I/O functions */ +//#include "printf.h" + + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + + +#if _PFATFS != 4004 /* Revision ID */ +#error Wrong include file (pff.h). +#endif + +#if _FS_FAT32 +#if !_FS_FAT16 && !_FS_FAT12 +#define _FS_32ONLY 1 +#else +#define _FS_32ONLY 0 +#endif +#else +#if !_FS_FAT16 && !_FS_FAT12 +#error Wrong _FS_FATxx setting. +#endif +#define _FS_32ONLY 0 +#endif + +#define ABORT(err) {fs->flag = 0; return err;} + + + +/*--------------------------------------------------------*/ +/* DBCS code ranges and SBCS extend char conversion table */ +/*--------------------------------------------------------*/ + +#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* Korean */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. (OEM) */ +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 720 /* Arabic (OEM) */ +#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek (OEM) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic (OEM) */ +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish (OEM) */ +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew (OEM) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian (OEM) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ +#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} + +#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1253 /* Greek (Windows) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ + 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} + +#elif _CODE_PAGE == 1254 /* Turkish (Windows) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1256 /* Arabic (Windows) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1257 /* Baltic (Windows) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} + +#else +#error Unknown code page. + +#endif + + + +/* Character code support macros */ + +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) + +#ifndef _EXCVT /* DBCS configuration */ + +#ifdef _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#ifdef _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* SBCS configuration */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _EXCVT */ + + +/* FatFs refers the members in the FAT structures with byte offset instead +/ of structure member because there are incompatibility of the packing option +/ between various compilers. */ + +#define BS_jmpBoot 0 +#define BS_OEMName 3 +#define BPB_BytsPerSec 11 +#define BPB_SecPerClus 13 +#define BPB_RsvdSecCnt 14 +#define BPB_NumFATs 16 +#define BPB_RootEntCnt 17 +#define BPB_TotSec16 19 +#define BPB_Media 21 +#define BPB_FATSz16 22 +#define BPB_SecPerTrk 24 +#define BPB_NumHeads 26 +#define BPB_HiddSec 28 +#define BPB_TotSec32 32 +#define BS_55AA 510 + +#define BS_DrvNum 36 +#define BS_BootSig 38 +#define BS_VolID 39 +#define BS_VolLab 43 +#define BS_FilSysType 54 + +#define BPB_FATSz32 36 +#define BPB_ExtFlags 40 +#define BPB_FSVer 42 +#define BPB_RootClus 44 +#define BPB_FSInfo 48 +#define BPB_BkBootSec 50 +#define BS_DrvNum32 64 +#define BS_BootSig32 66 +#define BS_VolID32 67 +#define BS_VolLab32 71 +#define BS_FilSysType32 82 + +#define MBR_Table 446 + +#define DIR_Name 0 +#define DIR_Attr 11 +#define DIR_NTres 12 +#define DIR_CrtTime 14 +#define DIR_CrtDate 16 +#define DIR_FstClusHI 20 +#define DIR_WrtTime 22 +#define DIR_WrtDate 24 +#define DIR_FstClusLO 26 +#define DIR_FileSize 28 + + + +/*-------------------------------------------------------------------------- + + Private Functions + +---------------------------------------------------------------------------*/ + + +static +FATFS *FatFs; /* Pointer to the file system object (logical drive) */ + + +/* Fill memory */ +static +void mem_set (void* dst, int val, int cnt) { + char *d = (char*)dst; + while (cnt--) *d++ = (char)val; +} + +/* Compare memory to memory */ +static +int mem_cmp (const void* dst, const void* src, int cnt) { + const char *d = (const char *)dst, *s = (const char *)src; + int r = 0; + while (cnt-- && (r = *d++ - *s++) == 0) ; + return r; +} + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static +CLUST get_fat ( /* 1:IO error, Else:Cluster status */ + CLUST clst /* Cluster# to get the link information */ +) +{ + BYTE buf[4]; + FATFS *fs = FatFs; + + if (clst < 2 || clst >= fs->n_fatent) /* Range check */ + return 1; + + switch (fs->fs_type) { +#if _FS_FAT12 + case FS_FAT12 : { + UINT wc, bc, ofs; + + bc = (UINT)clst; bc += bc / 2; + ofs = bc % 512; bc /= 512; + if (ofs != 511) { + if (disk_readp(buf, fs->fatbase + bc, ofs, 2)) break; + } else { + if (disk_readp(buf, fs->fatbase + bc, 511, 1)) break; + if (disk_readp(buf+1, fs->fatbase + bc + 1, 0, 1)) break; + } + wc = LD_WORD(buf); + return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + } +#endif +#if _FS_FAT16 + case FS_FAT16 : + if (disk_readp(buf, fs->fatbase + clst / 256, ((UINT)clst % 256) * 2, 2)) break; + return LD_WORD(buf); +#endif +#if _FS_FAT32 + case FS_FAT32 : + if (disk_readp(buf, fs->fatbase + clst / 128, ((UINT)clst % 128) * 4, 4)) break; + return LD_DWORD(buf) & 0x0FFFFFFF; +#endif + } + + return 1; /* An error occured at the disk I/O layer */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# / Get cluster field from directory entry */ +/*-----------------------------------------------------------------------*/ + +static +DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ + CLUST clst /* Cluster# to be converted */ +) +{ + FATFS *fs = FatFs; + + + clst -= 2; + if (clst >= (fs->n_fatent - 2)) return 0; /* Invalid cluster# */ + return (DWORD)clst * fs->csize + fs->database; +} + + +static +CLUST get_clust ( + BYTE* dir /* Pointer to directory entry */ +) +{ + FATFS *fs = FatFs; + CLUST clst = 0; + + + if (_FS_32ONLY || (_FS_FAT32 && fs->fs_type == FS_FAT32)) { + clst = LD_WORD(dir+DIR_FstClusHI); + clst <<= 16; + } + clst |= LD_WORD(dir+DIR_FstClusLO); + + return clst; +} + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Rewind directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_rewind ( + DIR *dj /* Pointer to directory object */ +) +{ + CLUST clst; + FATFS *fs = FatFs; + + + dj->index = 0; + clst = dj->sclust; + if (clst == 1 || clst >= fs->n_fatent) /* Check start cluster range */ + return FR_DISK_ERR; + if (_FS_FAT32 && !clst && (_FS_32ONLY || fs->fs_type == FS_FAT32)) /* Replace cluster# 0 with root cluster# if in FAT32 */ + clst = (CLUST)fs->dirbase; + dj->clust = clst; /* Current cluster */ + dj->sect = (_FS_32ONLY || clst) ? clust2sect(clst) : fs->dirbase; /* Current sector */ + + return FR_OK; /* Seek succeeded */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next2 ( /* FR_OK:Succeeded, FR_NO_FILE:End of table */ + DIR *dj /* Pointer to directory object */ +) +{ + CLUST clst; + WORD i; + FATFS *fs = FatFs; + + + i = dj->index + 1; + if (!i || !dj->sect) /* Report EOT when index has reached 65535 */ + return FR_NO_FILE; + + if (!(i % 16)) { /* Sector changed? */ + dj->sect++; /* Next sector */ + + if (dj->clust == 0) { /* Static table */ + if (i >= fs->n_rootdir) /* Report EOT when end of table */ + return FR_NO_FILE; + } + else { /* Dynamic table */ + if (((i / 16) & (fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(dj->clust); /* Get next cluster */ + if (clst <= 1) return FR_DISK_ERR; + if (clst >= fs->n_fatent) /* When it reached end of dynamic table */ + return FR_NO_FILE; /* Report EOT */ + dj->clust = clst; /* Initialize data for new cluster */ + dj->sect = clust2sect(clst); + } + } + } + + dj->index = i; + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( + DIR *dj, /* Pointer to the directory object linked to the file name */ + BYTE *dir /* 32-byte working buffer */ +) +{ + FRESULT res; + BYTE c; + + + res = dir_rewind(dj); /* Rewind directory object */ + if (res != FR_OK) return res; + + do { + res = disk_readp(dir, dj->sect, (dj->index % 16) * 32, 32) /* Read an entry */ + ? FR_DISK_ERR : FR_OK; + if (res != FR_OK) break; + c = dir[DIR_Name]; /* First character */ + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ + if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */ + break; + res = dir_next2(dj); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _USE_DIR +static +FRESULT dir_read ( + DIR *dj, /* Pointer to the directory object to store read object name */ + BYTE *dir, /* 32-byte working buffer */ + char *lfn_buffer /* 256-byte lfn buffer */ +) +{ + FRESULT res; + BYTE a, c; + + int lfn = 0; + char *lfn_pos = &lfn_buffer[255]; + lfn_buffer[255] = '\0'; + + res = FR_NO_FILE; + while (dj->sect) { + res = disk_readp(dir, dj->sect, (dj->index % 16) * 32, 32) /* Read an entry */ + ? FR_DISK_ERR : FR_OK; + if (res != FR_OK) break; + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ + a = dir[DIR_Attr] & AM_MASK; + + // printf("LFN2:%x:%x:%x:%x\n",a&AM_LFN==AM_LFN,dir[0],dir[0x1a],dir[0x1c]); + if (a&AM_LFN == AM_LFN && ((dir[0]&0xbf) < 16) && dir[0x1a]==0) // && dir[0x1c]!=0) + { + lfn_pos-=13; + char * ptr = lfn_pos; + int i; + for (i=1;i!=32;i+=2) + { + if (i==0xb) i=0xe; + if (i==0x1a) i=0x1c; + char b1 = dir[i]; + char b2 = dir[i+1]; + if (b2==0) *ptr++ = b1; else *ptr++ = '_'; + } + //printf("LFN seq:%d %c%c%c%c%c%c%c%c%c%c%c%c%c \n",dir[0],dir[1],dir[3],dir[5],dir[7],dir[9],dir[14],dir[16],dir[18],dir[20],dir[22],dir[24],dir[28],dir[30]); + lfn = 1; + } + + + if (c != 0xE5 && c != '.' && !(a & AM_VOL)) /* Is it a valid entry? */ + break; + res = dir_next2(dj); /* Next entry */ + if (res != FR_OK) break; + } + + lfn_buffer[0] = '\0'; + if (lfn) + { + strcpy(&lfn_buffer[0],lfn_pos); + } + + if (res != FR_OK) dj->sect = 0; + + return res; +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Pick a segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + + +static +FRESULT create_name ( + DIR *dj, /* Pointer to the directory object */ + const char **path /* Pointer to pointer to the segment in the path string */ +) +{ + BYTE c, ni, si, i, *sfn; + const char *p; +#if _USE_LCC +#ifdef _EXCVT + static const BYTE cvt[] = _EXCVT; +#endif +#endif + + /* Create file name in directory form */ + sfn = dj->fn; + mem_set(sfn, ' ', 11); + si = i = 0; ni = 8; + p = *path; + for (;;) { + c = p[si++]; + if (c <= ' ' || c == '/') break; /* Break on end of segment */ + if (c == '.' || i >= ni) { + if (ni != 8 || c != '.') break; + i = 8; ni = 11; + continue; + } +#if _USE_LCC +#ifdef _EXCVT + if (c >= 0x80) /* To upper extended char (SBCS) */ + c = cvt[c - 0x80]; +#endif + if (IsDBCS1(c) && i < ni - 1) { /* DBC 1st byte? */ + BYTE d = p[si++]; /* Get 2nd byte */ + sfn[i++] = c; + sfn[i++] = d; + } else +#endif + { /* Single byte code */ + if (_USE_LCC && IsLower(c)) c -= 0x20; /* toupper */ + sfn[i++] = c; + } + } + *path = &p[si]; /* Rerurn pointer to the next segment */ + + sfn[11] = (c <= ' ') ? 1 : 0; /* Set last segment flag if end of path */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ +#if _USE_DIR +static +void get_fileinfo ( /* No return code */ + DIR *dj, /* Pointer to the directory object */ + BYTE *dir, /* 32-byte working buffer */ + FILINFO *fno /* Pointer to store the file information */ +) +{ + BYTE i, c; + char *p; + + + p = fno->fname; + if (dj->sect) { + for (i = 0; i < 8; i++) { /* Copy file name body */ + c = dir[i]; + if (c == ' ') break; + if (c == 0x05) c = 0xE5; + *p++ = c; + } + if (dir[8] != ' ') { /* Copy file name extension */ + *p++ = '.'; + for (i = 8; i < 11; i++) { + c = dir[i]; + if (c == ' ') break; + *p++ = c; + } + } + fno->fattrib = dir[DIR_Attr]; /* Attribute */ + fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */ + fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */ + fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */ + } + *p = 0; +} +#endif /* _USE_DIR */ + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR *dj, /* Directory object to return last directory and found object */ + BYTE *dir, /* 32-byte working buffer */ + const char *path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + + + while (*path == ' ') path++; /* Strip leading spaces */ + if (*path == '/') path++; /* Strip heading separator if exist */ + dj->sclust = 0; /* Set start directory (always root dir) */ + + if ((BYTE)*path < ' ') { /* Null path means the root directory */ + res = dir_rewind(dj); + dir[0] = 0; + + } else { /* Follow path */ + for (;;) { + res = create_name(dj, &path); /* Get a segment */ + if (res != FR_OK) break; + res = dir_find(dj, dir); /* Find it */ + if (res != FR_OK) break; /* Could not find the object */ + if (dj->fn[11]) break; /* Last segment match. Function completed. */ + if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow path because it is a file */ + res = FR_NO_FILE; break; + } + dj->sclust = get_clust(dir); /* Follow next */ + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check a sector if it is an FAT boot record */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */ + BYTE *buf, /* Working buffer */ + DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ +) +{ + if (disk_readp(buf, sect, 510, 2)) /* Read the boot record */ + return 3; + + if (LD_WORD(buf) != 0xAA55) /* Check record signature */ + return 2; + + if (!_FS_32ONLY && !disk_readp(buf, sect, BS_FilSysType, 2) && LD_WORD(buf) == 0x4146) /* Check FAT12/16 */ + return 0; + if (_FS_FAT32 && !disk_readp(buf, sect, BS_FilSysType32, 2) && LD_WORD(buf) == 0x4146) /* Check FAT32 */ + return 0; + return 1; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Locical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT pf_mount ( + FATFS *fs /* Pointer to new file system object */ +) +{ + BYTE fmt, buf[36]; + DWORD bsect, fsize, tsect, mclst; + + + FatFs = 0; + + if (disk_initialize() & STA_NOINIT) /* Check if the drive is ready or not */ + return FR_NOT_READY; + + /* Search FAT partition on the drive */ + bsect = 0; + fmt = check_fs(buf, bsect); /* Check sector 0 as an SFD format */ + // printf("check_fs() = %d\n", fmt); + if (fmt == 1) { /* Not an FAT boot record, it may be FDISK format */ + /* Check a partition listed in top of the partition table */ + if (disk_readp(buf, bsect, MBR_Table, 16)) { /* 1st partition entry */ + fmt = 3; + } else { + if (buf[4]) { /* Is the partition existing? */ + bsect = LD_DWORD(&buf[8]); /* Partition offset in LBA */ + // printf("check partition at %d\n", bsect); + fmt = check_fs(buf, bsect); /* Check the partition */ + // printf("check_fs() = %d\n", fmt); + } + } + } + if (fmt == 3) return FR_DISK_ERR; + if (fmt) return FR_NO_FILESYSTEM; /* No valid FAT patition is found */ + + /* Initialize the file system object */ + if (disk_readp(buf, bsect, 13, sizeof (buf))) return FR_DISK_ERR; + + fsize = LD_WORD(buf+BPB_FATSz16-13); /* Number of sectors per FAT */ + if (!fsize) fsize = LD_DWORD(buf+BPB_FATSz32-13); + + fsize *= buf[BPB_NumFATs-13]; /* Number of sectors in FAT area */ + fs->fatbase = bsect + LD_WORD(buf+BPB_RsvdSecCnt-13); /* FAT start sector (lba) */ + fs->csize = buf[BPB_SecPerClus-13]; /* Number of sectors per cluster */ + fs->n_rootdir = LD_WORD(buf+BPB_RootEntCnt-13); /* Nmuber of root directory entries */ + tsect = LD_WORD(buf+BPB_TotSec16-13); /* Number of sectors on the file system */ + if (!tsect) tsect = LD_DWORD(buf+BPB_TotSec32-13); + mclst = (tsect /* Last cluster# + 1 */ + - LD_WORD(buf+BPB_RsvdSecCnt-13) - fsize - fs->n_rootdir / 16 + ) / fs->csize + 2; + fs->n_fatent = (CLUST)mclst; + + fmt = 0; /* Determine the FAT sub type */ + if (_FS_FAT12 && mclst < 0xFF7) + fmt = FS_FAT12; + if (_FS_FAT16 && mclst >= 0xFF8 && mclst < 0xFFF7) + fmt = FS_FAT16; + if (_FS_FAT32 && mclst >= 0xFFF7) + fmt = FS_FAT32; + if (!fmt) return FR_NO_FILESYSTEM; + fs->fs_type = fmt; + + if (_FS_32ONLY || (_FS_FAT32 && fmt == FS_FAT32)) + fs->dirbase = LD_DWORD(buf+(BPB_RootClus-13)); /* Root directory start cluster */ + else + fs->dirbase = fs->fatbase + fsize; /* Root directory start sector (lba) */ + fs->database = fs->fatbase + fsize + fs->n_rootdir / 16; /* Data start sector (lba) */ + + fs->flag = 0; + FatFs = fs; + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT pf_open ( + const char *path /* Pointer to the file name */ +) +{ + FRESULT res; + DIR dj; + BYTE sp[12], dir[32]; + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; /* Check file system */ + + fs->flag = 0; + dj.fn = sp; + res = follow_path(&dj, dir, path); /* Follow the file path */ + if (res != FR_OK) return res; /* Follow failed */ + if (!dir[0] || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */ + return FR_NO_FILE; + + fs->org_clust = get_clust(dir); /* File start cluster */ + fs->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ + fs->fptr = 0; /* File pointer */ + fs->flag = FA_OPENED; + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ +#if _USE_READ + +FRESULT pf_read ( + void* buff, /* Pointer to the read buffer (NULL:Forward data to the stream)*/ + UINT btr, /* Number of bytes to read */ + UINT* br /* Pointer to number of bytes read */ +) +{ + DRESULT dr; + CLUST clst; + DWORD sect, remain; + UINT rcnt; + BYTE cs, *rbuff = (BYTE *)buff; + FATFS *fs = FatFs; + + + *br = 0; + if (!fs) return FR_NOT_ENABLED; /* Check file system */ + if (!(fs->flag & FA_OPENED)) /* Check if opened */ + return FR_NOT_OPENED; + + remain = fs->fsize - fs->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + while (btr) { /* Repeat until all data transferred */ + if ((fs->fptr % 512) == 0) { /* On the sector boundary? */ + cs = (BYTE)(fs->fptr / 512 & (fs->csize - 1)); /* Sector offset in the cluster */ + if (!cs) { /* On the cluster boundary? */ + if (fs->fptr == 0) /* On the top of the file? */ + clst = fs->org_clust; + else + clst = get_fat(fs->curr_clust); + if (clst <= 1) ABORT(FR_DISK_ERR); + fs->curr_clust = clst; /* Update current cluster */ + } + sect = clust2sect(fs->curr_clust); /* Get current sector */ + if (!sect) ABORT(FR_DISK_ERR); + fs->dsect = sect + cs; + } + rcnt = 512 - (UINT)fs->fptr % 512; /* Get partial sector data from sector buffer */ + if (rcnt > btr) rcnt = btr; + dr = disk_readp(!buff ? 0 : rbuff, fs->dsect, (UINT)fs->fptr % 512, rcnt); + if (dr) ABORT(FR_DISK_ERR); + fs->fptr += rcnt; rbuff += rcnt; /* Update pointers and counters */ + btr -= rcnt; *br += rcnt; + } + + return FR_OK; +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ +#if _USE_WRITE + +FRESULT pf_write ( + const void* buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write (0:Finalize the current write operation) */ + UINT* bw /* Pointer to number of bytes written */ +) +{ + CLUST clst; + DWORD sect, remain; + const BYTE *p = buff; + BYTE cs; + UINT wcnt; + FATFS *fs = FatFs; + + + *bw = 0; + if (!fs) return FR_NOT_ENABLED; /* Check file system */ + if (!(fs->flag & FA_OPENED)) /* Check if opened */ + return FR_NOT_OPENED; + + if (!btw) { /* Finalize request */ + if ((fs->flag & FA__WIP) && disk_writep(0, 0)) ABORT(FR_DISK_ERR); + fs->flag &= ~FA__WIP; + return FR_OK; + } else { /* Write data request */ + if (!(fs->flag & FA__WIP)) /* Round-down fptr to the sector boundary */ + fs->fptr &= 0xFFFFFE00; + } + remain = fs->fsize - fs->fptr; + if (btw > remain) btw = (UINT)remain; /* Truncate btw by remaining bytes */ + + while (btw) { /* Repeat until all data transferred */ + if ((UINT)fs->fptr % 512 == 0) { /* On the sector boundary? */ + cs = (BYTE)(fs->fptr / 512 & (fs->csize - 1)); /* Sector offset in the cluster */ + if (!cs) { /* On the cluster boundary? */ + if (fs->fptr == 0) /* On the top of the file? */ + clst = fs->org_clust; + else + clst = get_fat(fs->curr_clust); + if (clst <= 1) ABORT(FR_DISK_ERR); + fs->curr_clust = clst; /* Update current cluster */ + } + sect = clust2sect(fs->curr_clust); /* Get current sector */ + if (!sect) ABORT(FR_DISK_ERR); + fs->dsect = sect + cs; + if (disk_writep(0, fs->dsect)) ABORT(FR_DISK_ERR); /* Initiate a sector write operation */ + fs->flag |= FA__WIP; + } + wcnt = 512 - (UINT)fs->fptr % 512; /* Number of bytes to write to the sector */ + if (wcnt > btw) wcnt = btw; + if (disk_writep(p, wcnt)) ABORT(FR_DISK_ERR); /* Send data to the sector */ + fs->fptr += wcnt; p += wcnt; /* Update pointers and counters */ + btw -= wcnt; *bw += wcnt; + if ((UINT)fs->fptr % 512 == 0) { + if (disk_writep(0, 0)) ABORT(FR_DISK_ERR); /* Finalize the currtent secter write operation */ + fs->flag &= ~FA__WIP; + } + } + + return FR_OK; +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ +#if _USE_LSEEK + +FRESULT pf_lseek ( + DWORD ofs /* File pointer from top of file */ +) +{ + CLUST clst; + DWORD bcs, sect, ifptr; + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; /* Check file system */ + if (!(fs->flag & FA_OPENED)) /* Check if opened */ + return FR_NOT_OPENED; + + if (ofs > fs->fsize) ofs = fs->fsize; /* Clip offset with the file size */ + ifptr = fs->fptr; + fs->fptr = 0; + if (ofs > 0) { + bcs = (DWORD)fs->csize * 512; /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fs->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ + ofs -= fs->fptr; + clst = fs->curr_clust; + } else { /* When seek to back cluster, */ + clst = fs->org_clust; /* start from the first cluster */ + fs->curr_clust = clst; + } + while (ofs > bcs) { /* Cluster following loop */ + clst = get_fat(clst); /* Follow cluster chain */ + if (clst <= 1 || clst >= fs->n_fatent) ABORT(FR_DISK_ERR); + fs->curr_clust = clst; + fs->fptr += bcs; + ofs -= bcs; + } + fs->fptr += ofs; + sect = clust2sect(clst); /* Current sector */ + if (!sect) ABORT(FR_DISK_ERR); + fs->dsect = sect + (fs->fptr / 512 & (fs->csize - 1)); + } + + return FR_OK; +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directroy Object */ +/*-----------------------------------------------------------------------*/ +#if _USE_DIR + +FRESULT pf_opendir ( + DIR *dj, /* Pointer to directory object to create */ + const char *path /* Pointer to the directory path */ +) +{ + FRESULT res; + BYTE sp[12], dir[32]; + FATFS *fs = FatFs; + + + if (!fs) { /* Check file system */ + res = FR_NOT_ENABLED; + } else { + dj->fn = sp; + res = follow_path(dj, dir, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + if (dir[0]) { /* It is not the root dir */ + if (dir[DIR_Attr] & AM_DIR) /* The object is a directory */ + dj->sclust = get_clust(dir); + else /* The object is not a directory */ + res = FR_NO_FILE; + } + if (res == FR_OK) + res = dir_rewind(dj); /* Rewind dir */ + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entry in Sequense */ +/*-----------------------------------------------------------------------*/ + +FRESULT pf_readdir ( + DIR *dj, /* Pointer to the open directory object */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + BYTE sp[12], dir[32]; + FATFS *fs = FatFs; + + + if (!fs) { /* Check file system */ + res = FR_NOT_ENABLED; + } else { + dj->fn = sp; + if (!fno) { + res = dir_rewind(dj); + } else { + res = dir_read(dj, dir, &fno->lfname[0]); + if (res == FR_NO_FILE) res = FR_OK; + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dj, dir, fno); /* Get the object information */ + res = dir_next2(dj); /* Increment index for next */ + if (res == FR_NO_FILE) res = FR_OK; + } + } + } + + if (fno->lfname[0] == '\0') strcpy(&fno->lfname[0],&fno->fname[0]); + //printf("%s %s(%d)\n",&fno->fname[0], &fno->lfname[0],strlen(&fno->lfname[0])); + return res; +} + +#endif /* _USE_DIR */ + diff --git a/tests/verilator/sdcard/pff.h b/tests/verilator/sdcard/pff.h new file mode 100644 index 0000000..0a88c92 --- /dev/null +++ b/tests/verilator/sdcard/pff.h @@ -0,0 +1,160 @@ +/*---------------------------------------------------------------------------/ +/ Petit FatFs - FAT file system module include file R0.03 (C)ChaN, 2014 +/----------------------------------------------------------------------------/ +/ Petit FatFs module is an open source software to implement FAT file system to +/ small embedded systems. This is a free software and is opened for education, +/ research and commercial developments under license policy of following trems. +/ +/ Copyright (C) 2014, ChaN, all right reserved. +/ +/ * The Petit FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/----------------------------------------------------------------------------*/ + +#ifndef _PFATFS +#define _PFATFS 4004 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "integer.h" +#include "pffconf.h" + +#if _PFATFS != _PFFCONF +#error Wrong configuration file (pffconf.h). +#endif + +#if _FS_FAT32 +#define CLUST DWORD +#else +#define CLUST WORD +#endif + + +/* File system object structure */ + +typedef struct { + BYTE fs_type; /* FAT sub type */ + BYTE flag; /* File status flags */ + BYTE csize; /* Number of sectors per cluster */ + BYTE pad1; + WORD n_rootdir; /* Number of root directory entries (0 on FAT32) */ + CLUST n_fatent; /* Number of FAT entries (= number of clusters + 2) */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (Cluster# on FAT32) */ + DWORD database; /* Data start sector */ + DWORD fptr; /* File R/W pointer */ + DWORD fsize; /* File size */ + CLUST org_clust; /* File start cluster */ + CLUST curr_clust; /* File current cluster */ + DWORD dsect; /* File current data sector */ +} FATFS; + + + +/* Directory object structure */ + +typedef struct { + WORD index; /* Current read/write index number */ + BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ + CLUST sclust; /* Table start cluster (0:Static table) */ + CLUST clust; /* Current cluster */ + DWORD sect; /* Current sector */ +} DIR; + + + +/* File status structure */ + +typedef struct { + DWORD fsize; /* File size */ + WORD fdate; /* Last modified date */ + WORD ftime; /* Last modified time */ + BYTE fattrib; /* Attribute */ + char fname[13]; /* File name */ + char lfname[256]; /* File name */ +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* 0 */ + FR_DISK_ERR, /* 1 */ + FR_NOT_READY, /* 2 */ + FR_NO_FILE, /* 3 */ + FR_NOT_OPENED, /* 4 */ + FR_NOT_ENABLED, /* 5 */ + FR_NO_FILESYSTEM /* 6 */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* Petit FatFs module application interface */ + +FRESULT pf_mount (FATFS* fs); /* Mount/Unmount a logical drive */ +FRESULT pf_open (const char* path); /* Open a file */ +FRESULT pf_read (void* buff, UINT btr, UINT* br); /* Read data from the open file */ +FRESULT pf_write (const void* buff, UINT btw, UINT* bw); /* Write data to the open file */ +FRESULT pf_lseek (DWORD ofs); /* Move file pointer of the open file */ +FRESULT pf_opendir (DIR* dj, const char* path); /* Open a directory */ +FRESULT pf_readdir (DIR* dj, FILINFO* fno); /* Read a directory item from the open directory */ + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + +/* File status flag (FATFS.flag) */ + +#define FA_OPENED 0x01 +#define FA_WPRT 0x02 +#define FA__WIP 0x40 + + +/* FAT sub type (FATFS.fs_type) */ + +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 + + +/* File attribute bits for directory entry */ + +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/*--------------------------------*/ +/* Multi-byte word access macros */ + +#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) +#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) +#else /* Use byte-by-byte access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8) +#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24) +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* _PFATFS */ diff --git a/tests/verilator/sdcard/pff_file.c b/tests/verilator/sdcard/pff_file.c new file mode 100644 index 0000000..c30e164 --- /dev/null +++ b/tests/verilator/sdcard/pff_file.c @@ -0,0 +1,456 @@ +#include "pff_file.h" + +#include "pff.h" +#include "utils.h" +#include "diskio.h" +#include "simplefile.h" +//#include "printf.h" + +struct SimpleFile * openfile; + +void * dir_cache; +int dir_cache_size; + +FATFS fatfs; +DIR dir; +FILINFO filinfo; + +int write_pending; + +#define translateStatus(res) (res == FR_OK ? SimpleFile_OK: SimpleFile_FAIL) +#define translateDStatus(res) (res == RES_OK ? SimpleFile_OK: SimpleFile_FAIL) + +/* +enum SimpleFileStatus translateStatus(FRESULT res) +{ + return res == FR_OK ? SimpleFile_OK: SimpleFile_FAIL; +} + +enum SimpleFileStatus translateDStatus(DSTATUS res) +{ + return res == RES_OK ? SimpleFile_OK: SimpleFile_FAIL; +}*/ + +char const * file_of(char const * path) +{ + char const * start = path + strlen(path); + while (start!=path) + { + --start; + if (*start == '/') + { + ++start; + break; + } + } + return start; +} + +void dir_of(char * dir, char const * path) +{ + char const * end = file_of(path); + if (end != path) + { + int len = end-path; + while (len--) + { + *dir++ = *path++; + } + --dir; + } + + *dir = '\0'; + return; +} + +char const * file_name(struct SimpleFile * file) +{ + return file_of(&file->path[0]); +} + +char const * file_path(struct SimpleFile * file) +{ + return &file->path[0]; +} + +void file_init(struct SimpleFile * file) +{ + file->path[0] = '\0'; + file->is_readonly = 1; + file->size = 0; +} + +void file_check_open(struct SimpleFile * file) +{ + if (openfile!=file) + { + file_write_flush(); + + pf_open(&file->path[0]); + openfile = file; + } +} + +enum SimpleFileStatus file_read(struct SimpleFile * file, void * buffer, int bytes, int * bytesread) +{ + UINT bytesread_word; + FRESULT res; + + file_write_flush(); + file_check_open(file); + + res = pf_read(buffer, bytes, &bytesread_word); + *bytesread = bytesread_word; + + return translateStatus(res); +} + +enum SimpleFileStatus file_write(struct SimpleFile * file, void * buffer, int bytes, int * byteswritten) +{ + UINT byteswritten_word; + FRESULT res; + + //printf("went\n"); + if (file->is_readonly) return SimpleFile_FAIL; + + file_check_open(file); + + int rem = bytes; + while (rem>0) + { + int sector = fatfs.fptr>>9; + int pos = fatfs.fptr&0x1ff; + + int bytes_this_cycle = rem; + if (bytes_this_cycle>(512-pos)) + bytes_this_cycle = 512-pos; + + //printf("file_write:%d/%d - %d/%d\n",sector,pos,bytes_this_cycle,bytes); + + if (sector != write_pending) + { + file_write_flush(); + } + + if (write_pending <0) + { + // read the sector into our 512 byte buffer... + pf_lseek(sector<<9); + int fptr = fatfs.fptr; + + char temp_buffer[1]; + pf_read(&temp_buffer[0], 1, &byteswritten_word); + + //printf("Writing initial pos:%d\n",pos); + + // seek to the initial pos + fatfs.fptr = fptr + pos; + + write_pending = sector; + } + + res = disk_writep(buffer, pos, bytes_this_cycle); + + fatfs.fptr += bytes_this_cycle; + rem-=bytes_this_cycle; + buffer+=bytes_this_cycle; + } + *byteswritten = bytes; + + //printf("wend\n"); + return translateStatus(res); +} + +enum SimpleFileStatus file_write_flush() +{ + if (write_pending >= 0) + { + //printf("wflush\n"); + disk_writeflush(); + write_pending = -1; + } + return SimpleFile_OK; +} + +enum SimpleFileStatus file_seek(struct SimpleFile * file, int offsetFromStart) +{ + FRESULT res; + + int location = offsetFromStart>>9; + if (write_pending >=0 && write_pending != offsetFromStart) + { + //printf("flush on seek\n"); + file_write_flush(); + } + + file_check_open(file); + + res = pf_lseek(offsetFromStart); + return translateStatus(res); +} + +int file_size(struct SimpleFile * file) +{ + return file->size; +} + +int file_readonly(struct SimpleFile * file) +{ + return file->is_readonly; +} + +int file_struct_size() +{ + return sizeof(struct SimpleFile); +} + +enum SimpleFileStatus file_open_name_in_dir(struct SimpleDirEntry * entry, char const * filename, struct SimpleFile * file) +{ + file_write_flush(); + + while (entry) + { + //printf("%s ",entry->filename_ptr); + if (0==stricmp(filename,entry->filename_ptr)) + { + return file_open_dir(entry, file); + } + entry = entry->next; + } + + return SimpleFile_FAIL; +} + +enum SimpleFileStatus file_open_name(char const * path, struct SimpleFile * file) +{ + char dirname[MAX_DIR_LENGTH]; + char const * filename = file_of(path); + dir_of(&dirname[0], path); + + file_write_flush(); + + //printf("filename:%s dirname:%s ", filename,&dirname[0]); + + struct SimpleDirEntry * entry = dir_entries(&dirname[0]); + return file_open_name_in_dir(entry,filename, file); +} + +enum SimpleFileStatus file_open_dir(struct SimpleDirEntry * dir, struct SimpleFile * file) +{ + FRESULT res; + + strcpy(&file->path[0],dir->path); + file->is_readonly = dir->is_readonly; + file->size = dir->size; + + file_write_flush(); + + res = pf_open(&file->path[0]); + openfile = file; + + return translateStatus(res); +} + +enum SimpleFileStatus dir_init(void * mem, int space) +{ + FRESULT fres; + DSTATUS res; + + write_pending = -1; + + //printf("dir_init\n"); + + dir_cache = mem; + dir_cache_size = space; + + //printf("disk_init go\n"); + res = disk_initialize(); + //printf("disk_init done\n"); + if (res!=RES_OK) return translateDStatus(res); + + //printf("pf_mount\n"); + fres = pf_mount(&fatfs); + //printf("pf_mount done\n"); + + return translateStatus(fres); +} + +// Read entire dir into memory (i.e. give it a decent chunk of sdram) +struct SimpleDirEntry * dir_entries(char const * dirPath) +{ + return dir_entries_filtered(dirPath,0); +} + +int dircmp(struct SimpleDirEntry * a, struct SimpleDirEntry * b) +{ + if (a->is_subdir==b->is_subdir) + return strcmp(a->lfn,b->lfn); + else + return a->is_subdiris_subdir; +} + +void sort_ll(struct SimpleDirEntry * h) +{ +//struct SimpleDirEntry +//{ +// char path[MAX_PATH_LENGTH]; +// char * filename_ptr; +// int size; +// int is_subdir; +// struct SimpleDirEntry * next; // as linked list - want to allow sorting... +//}; + + struct SimpleDirEntry * p,*temp,*prev; + int i,j,n,sorted=0; + temp=h; + prev=0; + for(n=0;temp!=0;temp=temp->next) n++; + + for(i=0;iissubdir:%d(%s) p->next->issubdir:%d(%s)",p->is_subdir,p->path,p->next->is_subdir,p->next->path); + + if(dircmp(p,p->next)>0) { + // printf("SWITCH!\n"); + struct SimpleDirEntry * a = p; + struct SimpleDirEntry * b = p->next; + a->next=b->next; + b->next=a; + if (prev) + prev->next=b; + p=b; + + sorted=0; + } + prev=p; + p=p->next; + } + } + + //temp=h; + //for(n=0;temp!=0;temp=temp->next) printf("POST:%s\n",temp->path); +} + +struct SimpleDirEntry * dir_entries_filtered(char const * dirPath,int(* filter)(struct SimpleDirEntry *)) +{ + int room = dir_cache_size/sizeof(struct SimpleDirEntry); + + file_write_flush(); + + //printf("opendir "); + if (FR_OK != pf_opendir(&dir,dirPath)) + { + //printf("FAIL "); + return 0; + } + //printf("OK "); + + struct SimpleDirEntry * prev = (struct SimpleDirEntry *)dir_cache; + strcpy(prev->path,".."); + strcpy(prev->lfn,".."); + prev->filename_ptr = prev->path; + prev->size = 0; + prev->is_subdir = 1; + prev->is_readonly = 1; + prev->next = 0; + --room; + + //int count=0; + struct SimpleDirEntry * entry = prev + 1; + while (room && FR_OK == pf_readdir(&dir,&filinfo) && filinfo.fname[0]!='\0') + { + char * ptr; + + if (filinfo.fattrib & AM_SYS) + { + continue; + } + if (filinfo.fattrib & AM_HID) + { + continue; + } + + //printf("next %x %d ",entry,room); + + entry->is_subdir = (filinfo.fattrib & AM_DIR) ? 1 : 0; + entry->is_readonly = (filinfo.fattrib & AM_RDO) ? 1 : 0; + + //printf("%s ",filinfo.fname); + + strcpy(&entry->path[0],dirPath); + ptr = &entry->path[0]; + ptr += strlen(&entry->path[0]); + *ptr++ = '/'; + entry->filename_ptr = ptr; + strcpy(ptr,filinfo.fname); + entry->size = filinfo.fsize; + + //printf("LFN:%s\n",&filinfo.lfname[0]); + strcpy(&entry->lfn[0],&filinfo.lfname[0]); + + //int count; + //printf("%d %s %s\n",count++, filinfo.fname, filinfo.lfname); + + + if (filter && !filter(entry)) + { + continue; + } + + entry->next = 0; + + if (prev) + prev->next = entry; + prev = entry; + entry++; + room--; + + //printf("n %d %d %x ",filinfo.fsize, entry->size, entry->next); + } + + //printf("dir_entries done "); + + /*struct SimpleDirEntry * begin = (struct SimpleDirEntry *) dir_cache; + int count = 0; + while (begin) + { + printf("%d %s\n",count++, begin->path); + begin = begin->next; + }*/ + + if (filter) + { + sort_ll((struct SimpleDirEntry *) dir_cache); + } + return (struct SimpleDirEntry *) dir_cache; +} + +char const * dir_path(struct SimpleDirEntry * entry) +{ + return &entry->path[0]; +} + +char const * dir_filename(struct SimpleDirEntry * entry) +{ + //return entry->filename_ptr; + return &entry->lfn[0]; +} + +int dir_filesize(struct SimpleDirEntry * entry) +{ + return entry->size; +} + +struct SimpleDirEntry * dir_next(struct SimpleDirEntry * entry) +{ + return entry->next; +} + +int dir_is_subdir(struct SimpleDirEntry * entry) +{ + return entry->is_subdir; +} + + diff --git a/tests/verilator/sdcard/pff_file.h b/tests/verilator/sdcard/pff_file.h new file mode 100644 index 0000000..b1a0be4 --- /dev/null +++ b/tests/verilator/sdcard/pff_file.h @@ -0,0 +1,28 @@ +#pragma once + +#include "simplefile.h" +#include "simpledir.h" + +#define MAX_DIR_LENGTH (9*5+1) +#define MAX_FILE_LENGTH (8+3+1+1) +#define MAX_PATH_LENGTH (9*5 + 8+3+1 + 1) + +// Do not access these directly... They vary by architecture, just the simplefile/simpledir interface is the same +struct SimpleFile +{ + char path[MAX_PATH_LENGTH]; + int is_readonly; + int size; +}; + +struct SimpleDirEntry +{ + char path[MAX_PATH_LENGTH]; + char * filename_ptr; + char lfn[256]; + int size; + int is_subdir; + int is_readonly; + struct SimpleDirEntry * next; // as linked list - want to allow sorting... +}; + diff --git a/tests/verilator/sdcard/pffconf.h b/tests/verilator/sdcard/pffconf.h new file mode 100644 index 0000000..7580099 --- /dev/null +++ b/tests/verilator/sdcard/pffconf.h @@ -0,0 +1,89 @@ +/*---------------------------------------------------------------------------/ +/ Petit FatFs - Configuration file R0.03 (C)ChaN, 2014 +/---------------------------------------------------------------------------*/ + +#ifndef _PFFCONF +#define _PFFCONF 4004 /* Revision ID */ + +/*---------------------------------------------------------------------------/ +/ Function Configurations +/---------------------------------------------------------------------------*/ + +#define _USE_READ 1 /* Enable pf_read() function */ +#define _USE_DIR 1 /* Enable pf_opendir() and pf_readdir() function */ +#define _USE_LSEEK 1 /* Enable pf_lseek() function */ +#define _USE_WRITE 0 /* Enable pf_write() function */ + +#define _FS_FAT12 0 /* Enable FAT12 */ +#define _FS_FAT16 1 /* Enable FAT16 */ +#define _FS_FAT32 1 /* Enable FAT32 */ + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/---------------------------------------------------------------------------*/ + +#define _USE_LCC 1 /* Allow lower case characters for path name */ + +#define _CODE_PAGE 437 +/* The _CODE_PAGE specifies the code page to be used on the target system. +/ SBCS code pages with _USE_LCC == 1 requiers a 128 byte of case conversion +/ table. This might occupy RAM on some platforms, e.g. avr-gcc. +/ When _USE_LCC == 0, _CODE_PAGE has no effect. +/ +/ 932 - Japanese Shift_JIS (DBCS, OEM, Windows) +/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows) +/ 949 - Korean (DBCS, OEM, Windows) +/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows) +/ 1250 - Central Europe (Windows) +/ 1251 - Cyrillic (Windows) +/ 1252 - Latin 1 (Windows) +/ 1253 - Greek (Windows) +/ 1254 - Turkish (Windows) +/ 1255 - Hebrew (Windows) +/ 1256 - Arabic (Windows) +/ 1257 - Baltic (Windows) +/ 1258 - Vietnam (OEM, Windows) +/ 437 - U.S. (OEM) +/ 720 - Arabic (OEM) +/ 737 - Greek (OEM) +/ 775 - Baltic (OEM) +/ 850 - Multilingual Latin 1 (OEM) +/ 858 - Multilingual Latin 1 + Euro (OEM) +/ 852 - Latin 2 (OEM) +/ 855 - Cyrillic (OEM) +/ 866 - Russian (OEM) +/ 857 - Turkish (OEM) +/ 862 - Hebrew (OEM) +/ 874 - Thai (OEM, Windows) +*/ + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/---------------------------------------------------------------------------*/ + +#define _WORD_ACCESS 0 +/* The _WORD_ACCESS option is an only platform dependent option. It defines +/ which access method is used to the word data on the FAT volume. +/ +/ 0: Byte-by-byte access. Always compatible with all platforms. +/ 1: Word access. Do not choose this unless under both the following conditions. +/ +/ * Address misaligned memory access is always allowed for ALL instructions. +/ * Byte order on the memory is little-endian. +/ +/ If it is the case, _WORD_ACCESS can also be set to 1 to improve performance and +/ reduce code size. Following table shows an example of some processor types. +/ +/ ARM7TDMI 0 ColdFire 0 V850E 0 +/ Cortex-M3 0 Z80 0/1 V850ES 0/1 +/ Cortex-M0 0 RX600(LE) 0/1 TLCS-870 0/1 +/ AVR 0/1 RX600(BE) 0 TLCS-900 0/1 +/ AVR32 0 RL78 0 R32C 0 +/ PIC18 0/1 SH-2 0 M16C 0/1 +/ PIC24 0 H8S 0 MSP430 0 +/ PIC32 0 H8/300H 0 x86 0/1 +*/ + +#endif /* _PFFCONF */ diff --git a/tests/verilator/sdcard/readme.txt b/tests/verilator/sdcard/readme.txt new file mode 100644 index 0000000..202da40 --- /dev/null +++ b/tests/verilator/sdcard/readme.txt @@ -0,0 +1,7 @@ +sd_card.v Verilator test suite +------------------------------ + +This suite runs a simulation of the native sd_card.v for the MiST using the +fatfs file system layer as a test client. The simulation includes the +data_io.v and needs a file named card.img containing a sd card image to +run against. diff --git a/tests/verilator/sdcard/sd_card.v b/tests/verilator/sdcard/sd_card.v new file mode 100644 index 0000000..95e5a18 --- /dev/null +++ b/tests/verilator/sdcard/sd_card.v @@ -0,0 +1,470 @@ +// +// sd_card.v +// +// This file implelents a sd card for the MIST board since on the board +// the SD card is connected to the ARM IO controller and the FPGA has no +// direct connection to the SD card. This file provides a SD card like +// interface to the IO controller easing porting of cores that expect +// a direct interface to the SD card. +// +// Copyright (c) 2014 Till Harbaum +// +// This source file is free software: you can redistribute it and/or modify +// it under the terms of the Lesser GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file 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. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// http://elm-chan.org/docs/mmc/mmc_e.html + +module sd_card ( + // link to user_io for io controller + output [31:0] io_lba, + output reg io_rd, + output reg io_wr, + input io_ack, + output io_conf, + output io_sdhc, + + // data coming in from io controller + input [7:0] io_din, + input io_din_strobe, + + // data going out to io controller + output [7:0] io_dout, + input io_dout_strobe, + + // configuration input + input allow_sdhc, + + input sd_cs, + input sd_sck, + input sd_sdi, + output reg sd_sdo +); + +// set io_rd once read_state machine starts waiting (rising edge of req_io_rd) +// and clear it once io controller uploads something (io_ack==1) +reg req_io_rd = 1'b0; // set when write_state is changed to RD_STATE_WAIT_IO +always @(posedge req_io_rd or posedge io_ack) begin + if(io_ack) io_rd <= 1'b0; + else io_rd <= 1'b1; +end + +reg req_io_wr = 1'b0; // set when write_state is changed to WR_STATE_BUSY +always @(posedge req_io_wr or posedge io_ack) begin + if(io_ack) io_wr <= 1'b0; + else io_wr <= 1'b1; +end + +wire [31:0] OCR = { 1'b0, io_sdhc, 30'h0 }; // bit30 = 1 -> high capaciry card (sdhc) +wire [7:0] READ_DATA_TOKEN = 8'hfe; + +// number of bytes to wait after a command before sending the reply +localparam NCR=4; + +localparam RD_STATE_IDLE = 2'd0; +localparam RD_STATE_WAIT_IO = 2'd1; +localparam RD_STATE_SEND_TOKEN = 2'd2; +localparam RD_STATE_SEND_DATA = 2'd3; +reg [1:0] read_state = RD_STATE_IDLE; + +localparam WR_STATE_IDLE = 3'd0; +localparam WR_STATE_EXP_DTOKEN = 3'd1; +localparam WR_STATE_RECV_DATA = 3'd2; +localparam WR_STATE_RECV_CRC0 = 3'd3; +localparam WR_STATE_RECV_CRC1 = 3'd4; +localparam WR_STATE_SEND_DRESP = 3'd5; +localparam WR_STATE_BUSY = 3'd6; +reg [2:0] write_state = WR_STATE_IDLE; + +reg [6:0] sbuf; +reg cmd55; +reg new_cmd_rcvd; +reg [7:0] cmd; +reg [2:0] bit_cnt; // counts bits 0-7 0-7 ... +reg [3:0] byte_cnt= 4'd15; // counts bytes + +reg [31:0] lba; +assign io_lba = io_sdhc?lba:{9'd0, lba[31:9]}; + +reg [7:0] reply; +reg [7:0] reply0, reply1, reply2, reply3; +reg [3:0] reply_len; + +// falling edge of io_ack signals that a sector to be read has been written into +// the sector buffer by the io controller. This signal is kept set as long +// as the read state machine is in the "wait for io controller" state (state 1) +wire rd_wait_io = (read_state != RD_STATE_IDLE); +reg rd_io_ack_i = 1'b0; +always @(negedge io_ack or negedge rd_wait_io) begin + if(!rd_wait_io) rd_io_ack_i <= 1'b0; + else rd_io_ack_i <= 1'b1; +end + +wire wr_wait_io = (write_state == WR_STATE_BUSY); +reg wr_io_ack_i = 1'b0; +always @(negedge io_ack or negedge wr_wait_io) begin + if(!wr_wait_io) wr_io_ack_i <= 1'b0; + else wr_io_ack_i <= 1'b1; +end + +// bring xx_io_ack into sd cards clock domain +reg wr_io_ack; +reg rd_io_ack; +always @(posedge sd_sck) begin + rd_io_ack <= rd_io_ack_i; + wr_io_ack <= wr_io_ack_i; +end + +// ------------------------- SECTOR BUFFER ----------------------- + +// the buffer itself. Can hold one sector +reg [7:0] buffer [511:0]; + +// ---------------- buffer read engine ----------------------- +reg [8:0] buffer_rptr; +reg buffer_read_strobe; +wire buffer_dout_strobe = buffer_read_strobe || io_dout_strobe; +reg [7:0] buffer_dout; +assign io_dout = buffer_dout; + +// buffer_rptr is increased in a diferent clock domain than it's +// evaluated. These single bit registers bring certain states from +// one domain into the other one in a safe (atomic) way +reg buffer_read_sector_done; +reg buffer_read_ciscid_done; + +always @(posedge buffer_dout_strobe or posedge new_cmd_rcvd) begin + if(new_cmd_rcvd == 1) begin + buffer_rptr <= 9'd0; + buffer_read_sector_done <= 1'b0; + buffer_read_ciscid_done <= 1'b0; + end else begin + buffer_dout <= buffer[buffer_rptr]; + buffer_rptr <= buffer_rptr + 9'd1; + if(buffer_rptr == 511) buffer_read_sector_done <= 1'b1; + if(buffer_rptr == 15) buffer_read_ciscid_done <= 1'b1; + end +end + +// ---------------- buffer write engine ----------------------- +reg [8:0] buffer_wptr; +reg buffer_write_strobe; +wire buffer_din_strobe = io_din_strobe || buffer_write_strobe; +wire [7:0] buffer_din = (cmd == 8'h51)?io_din:{sbuf, sd_sdi}; + +always @(posedge buffer_din_strobe or posedge new_cmd_rcvd) begin + if(new_cmd_rcvd == 1) + buffer_wptr <= 9'd0; + else begin + buffer[buffer_wptr] <= buffer_din; + buffer_wptr <= buffer_wptr + 9'd1; + end +end + +wire [7:0] WRITE_DATA_RESPONSE = 8'h05; + +// ------------------------- CSD/CID BUFFER ---------------------- +assign io_conf = (csd_wptr == 0); // csd_wptr still 0 -> configuration required + +// the 32 bytes as sent from the io controller +reg [7:0] cid [15:0]; +reg [7:0] csd [15:0]; +reg [7:0] conf; + +reg [7:0] cid_byte; +reg [7:0] csd_byte; +reg [5:0] csd_wptr = 6'd0; + +// conf[0]==1 -> io controller is using an sdhc card +wire io_has_sdhc = conf[0]; +assign io_sdhc = allow_sdhc && io_has_sdhc; + +always @(posedge io_din_strobe) begin + // if io controller sends data without asserting io_ack, then it's + // updating the config + if(!io_ack && (csd_wptr <= 32)) begin + + if(csd_wptr < 16) // first 16 bytes are cid + cid[csd_wptr[3:0]] <= io_din; + if((csd_wptr >= 16) && (csd_wptr < 32)) // then comes csd + csd[csd_wptr[3:0]] <= io_din; + if(csd_wptr == 32) // finally a config byte + conf <= io_din; + + csd_wptr <= csd_wptr + 6'd1; + end +end + +always @(posedge buffer_dout_strobe) begin + cid_byte <= cid[buffer_rptr[3:0]]; + csd_byte <= csd[buffer_rptr[3:0]]; +end + +// ----------------- spi transmitter -------------------- +// advance transmitter state machine on falling sck edge, so data is valid on the +// rising edge +always@(negedge sd_sck) begin + if(sd_cs == 0) begin + buffer_read_strobe <= 1'b0; + sd_sdo <= 1'b1; // default: send 1's (busy/wait) + req_io_rd <= 1'b0; + + if(byte_cnt == 5+NCR) begin + sd_sdo <= reply[~bit_cnt]; + + if(bit_cnt == 7) begin + // these three commands all have a reply_len of 0 and will thus + // not send more than a single reply byte + + // CMD9: SEND_CSD + // CMD10: SEND_CID + if((cmd == 8'h49)||(cmd == 8'h4a)) + read_state <= RD_STATE_SEND_TOKEN; // jump directly to data transmission + + // CMD17: READ_SINGLE_BLOCK + if(cmd == 8'h51) begin + read_state <= RD_STATE_WAIT_IO; // start waiting for data from io controller + req_io_rd <= 1'b1; // trigger request to io controller + end + end + end + else if((reply_len > 0) && (byte_cnt == 5+NCR+1)) + sd_sdo <= reply0[~bit_cnt]; + else if((reply_len > 1) && (byte_cnt == 5+NCR+2)) + sd_sdo <= reply1[~bit_cnt]; + else if((reply_len > 2) && (byte_cnt == 5+NCR+3)) + sd_sdo <= reply2[~bit_cnt]; + else if((reply_len > 3) && (byte_cnt == 5+NCR+4)) + sd_sdo <= reply3[~bit_cnt]; + else + sd_sdo <= 1'b1; + + // ---------- read state machine processing ------------- + + case(read_state) + RD_STATE_IDLE: ; + // don't do anything + + // waiting for io controller to return data + RD_STATE_WAIT_IO: begin + if(rd_io_ack && (bit_cnt == 7)) + read_state <= RD_STATE_SEND_TOKEN; + end + + // send data token + RD_STATE_SEND_TOKEN: begin + sd_sdo <= READ_DATA_TOKEN[~bit_cnt]; + + if(bit_cnt == 7) begin + read_state <= RD_STATE_SEND_DATA; // next: send data + buffer_read_strobe <= 1'b1; // trigger read of first data byte + end + end + + // send data + RD_STATE_SEND_DATA: begin + if(cmd == 8'h51) // CMD17: READ_SINGLE_BLOCK + sd_sdo <= buffer_dout[~bit_cnt]; + else if(cmd == 8'h49) // CMD9: SEND_CSD + sd_sdo <= csd_byte[~bit_cnt]; + else if(cmd == 8'h4a) // CMD10: SEND_CID + sd_sdo <= cid_byte[~bit_cnt]; + else + sd_sdo <= 1'b1; + + if(bit_cnt == 7) begin + // sent 512 sector data bytes? + if((cmd == 8'h51) && buffer_read_sector_done) // (buffer_rptr == 0)) + read_state <= RD_STATE_IDLE; // next: send crc. It's ignored so return to idle state + + // sent 16 cid/csd data bytes? + else if(((cmd == 8'h49)||(cmd == 8'h4a)) && buffer_read_ciscid_done) // && (buffer_rptr == 16)) + read_state <= RD_STATE_IDLE; // return to idle state + + else + buffer_read_strobe <= 1'b1; // not done yet -> trigger read of next data byte + end + end + endcase + + // ------------------ write support ---------------------- + // send write data response + if(write_state == WR_STATE_SEND_DRESP) + sd_sdo <= WRITE_DATA_RESPONSE[~bit_cnt]; + + // busy after write until the io controller sends ack + if(write_state == WR_STATE_BUSY) + sd_sdo <= 1'b0; + end +end + +// spi receiver +reg illegal_write_state /* synthesis noprune */; + +always @(posedge sd_sck or posedge sd_cs) begin + // cs is active low + if(sd_cs == 1) begin + bit_cnt <= 3'd0; + end else begin + illegal_write_state <= 1'b0; + new_cmd_rcvd <= 1'b0; + buffer_write_strobe <= 1'b0; + req_io_wr <= 1'b0; + bit_cnt <= bit_cnt + 3'd1; + + // assemble byte + if(bit_cnt != 7) + sbuf[6:0] <= { sbuf[5:0], sd_sdi }; + else begin + // finished reading one byte + // byte counter runs against 15 byte boundary + if(byte_cnt != 15) + byte_cnt <= byte_cnt + 4'd1; + + // byte_cnt > 6 -> complete command received + // first byte of valid command is 01xxxxxx + // don't accept new commands once a write or read command has been accepted + if((byte_cnt > 5) && (write_state == WR_STATE_IDLE) && + (read_state == RD_STATE_IDLE) && sbuf[6:5] == 2'b01) begin + byte_cnt <= 4'd0; + cmd <= { sbuf, sd_sdi}; + new_cmd_rcvd <= 1'b1; + + // set cmd55 flag if previous command was 55 + cmd55 <= (cmd == 8'h77); + end + + // parse additional command bytes + if(byte_cnt == 0) lba[31:24] <= { sbuf, sd_sdi}; + if(byte_cnt == 1) lba[23:16] <= { sbuf, sd_sdi}; + if(byte_cnt == 2) lba[15:8] <= { sbuf, sd_sdi}; + if(byte_cnt == 3) lba[7:0] <= { sbuf, sd_sdi}; + + // last byte received, evaluate + if(byte_cnt == 4) begin + + // default: + reply <= 8'h04; // illegal command + reply_len <= 4'd0; // no extra reply bytes + + // CMD0: GO_IDLE_STATE + if(cmd == 8'h40) + reply <= 8'h01; // ok, busy + + // CMD1: SEND_OP_COND + else if(cmd == 8'h41) + reply <= 8'h00; // ok, not busy + + // CMD8: SEND_IF_COND (V2 only) + else if(cmd == 8'h48) begin + reply <= 8'h01; // ok, busy + reply0 <= 8'h00; + reply1 <= 8'h00; + reply2 <= 8'h01; + reply3 <= 8'hAA; + reply_len <= 4'd4; + end + + // CMD9: SEND_CSD + else if(cmd == 8'h49) + reply <= 8'h00; // ok + + // CMD10: SEND_CID + else if(cmd == 8'h4a) + reply <= 8'h00; // ok + + // CMD16: SET_BLOCKLEN + else if(cmd == 8'h50) begin + // we only support a block size of 512 + if(io_lba == 32'd512) + reply <= 8'h00; // ok + else + reply <= 8'h40; // parmeter error + end + + // CMD17: READ_SINGLE_BLOCK + else if(cmd == 8'h51) + reply <= 8'h00; // ok + + // CMD24: WRITE_BLOCK + else if(cmd == 8'h58) begin + reply <= 8'h00; // ok + write_state <= WR_STATE_EXP_DTOKEN; // expect data token + end + + // ACMD41: APP_SEND_OP_COND + else if(cmd55 && (cmd == 8'h69)) + reply <= 8'h00; // ok, not busy + + // CMD55: APP_COND + else if(cmd == 8'h77) + reply <= 8'h01; // ok, busy + + // CMD58: READ_OCR + else if(cmd == 8'h7a) begin + reply <= 8'h00; // ok + + reply0 <= OCR[31:24]; // bit 30 = 1 -> high capacity card + reply1 <= OCR[23:16]; + reply2 <= OCR[15:8]; + reply3 <= OCR[7:0]; + reply_len <= 4'd4; + end + end + + // ---------- handle write ----------- + case(write_state) + // don't do anything in idle state + WR_STATE_IDLE: ; + + // waiting for data token + WR_STATE_EXP_DTOKEN: + if({ sbuf, sd_sdi} == 8'hfe ) + write_state <= WR_STATE_RECV_DATA; + + // transfer 512 bytes + WR_STATE_RECV_DATA: begin + // push one byte into local buffer + buffer_write_strobe <= 1'b1; + + // all bytes written? + if(buffer_wptr == 511) + write_state <= WR_STATE_RECV_CRC0; + end + + // transfer 1st crc byte + WR_STATE_RECV_CRC0: + write_state <= WR_STATE_RECV_CRC1; + + // transfer 2nd crc byte + WR_STATE_RECV_CRC1: + write_state <= WR_STATE_SEND_DRESP; + + // send data response + WR_STATE_SEND_DRESP: begin + write_state <= WR_STATE_BUSY; + req_io_wr <= 1'b1; // trigger write request to io ontroller + end + + // wait for io controller to accept data + WR_STATE_BUSY: + if(wr_io_ack) + write_state <= WR_STATE_IDLE; + + default: + illegal_write_state <= 1'b1; + endcase + end + end +end + +endmodule diff --git a/tests/verilator/sdcard/sd_card_tb.cpp b/tests/verilator/sdcard/sd_card_tb.cpp new file mode 100644 index 0000000..b44005e --- /dev/null +++ b/tests/verilator/sdcard/sd_card_tb.cpp @@ -0,0 +1,220 @@ +#include "Vsd_card.h" +#include "verilated.h" +#include "verilated_vcd_c.h" + +#include "integer.h" +extern "C" +{ + #include "spi.h" + #include "mmc.h" + #include "simpledir.h" + #include "simplefile.h" + void hexdump(void *, uint16_t, uint16_t); +} + +void hexdump(void *data, uint16_t size, uint16_t offset) { + uint8_t i, b2c; + uint16_t n=0; + char *ptr = (char*)data; + + if(!size) return; + + while(size>0) { + printf("%04x: ", n + offset); + + b2c = (size>16)?16:size; + for(i=0;ieval(); + tfp->dump (evcnt++); +} + +void host_init() { + // csd/cid + u08 cid_csd[] = { + 0x3e, 0x00, 0x00, 0x34, 0x38, 0x32, 0x44, 0x00, + 0x00, 0x38, 0x87, 0x43, 0xd8, 0x00, 0xc7, 0x0b, + 0x00, 0x7f, 0x00, 0x32, 0x5b, 0x59, 0x83, 0xbc, + 0xf6, 0xdb, 0xff, 0x9f, 0x96, 0x40, 0x00, 0x93, + 0x00 + }; + + int i; + + for(i=0;i<0x21;i++) { + // printf("INIT(%x)\n", cid_csd[i]); + + top->io_din = cid_csd[i]; + top->io_din_strobe = 1; + dump(); + top->io_din_strobe = 0; + dump(); + } +} + +extern "C" void wait_us(int); + +void spiReceiveData(u08 * from, u08 * to) { + int i; + for(i=0;i<512;i++) + *from++ = spiTransferByte(*to++); + + // hexdump(from-512, 512, 0); +} + +void spiInit() {} +void set_spi_clock_freq() {} +void wait_us(int) {} + +void mmcChipSelect(int select) { + top->sd_cs = select?0:1; +} + +void check4io() { + static int state = 0; + static u08 buffer[512]; + + // check for external request + if(top->io_rd) { + if(!state) { + FILE *file = fopen("card.img", "rb"); + if(!file) { perror(""); exit(-1); } + fseek(file, 512*top->io_lba, SEEK_SET); + fread(buffer, 1, 512, file); + fclose(file); + + // printf("SD RD %d\n", top->io_lba); + top->io_ack = 1; + dump(); + state = 512; + } + } + + if(state != 0) { + // printf("tx[%d]=%x\n", 512-state, buffer[512-state]); + + top->io_din = buffer[512-state]; + top->io_din_strobe = 1; + dump(); + top->io_din_strobe = 0; + dump(); + + state--; + + if(state == 0) { + // printf("TX done\n"); + top->io_ack = 0; + dump(); + } + } +} + +u08 spiTransferByte(u08 byte) { + int i; + u08 rval = 0; + + // printf("SPI(%x)=", byte); + + check4io(); + + dump(); + + for(i=0;i<8;i++) { + top->sd_sdi = (byte & 0x80)?1:0; + + dump(); + top->sd_sck = 1; + dump(); + top->sd_sck = 0; + + rval = (rval << 1)|(top->sd_sdo?1:0); + + byte <<= 1; + } + dump(); + + // printf("%x\n", rval); + + return rval; +} + +u08 spiTransferFF() { + return spiTransferByte(0xff); +} + +#define DIR_INIT_MEMSIZE 16*1024 +u08 mem[DIR_INIT_MEMSIZE]; +char ROM_DIR[]="/atari800/rom"; + +int main(int argc, char **argv, char **env) { + struct SimpleFile *file; + int i; + int clk; + Verilated::commandArgs(argc, argv); + // init top verilog instance + top = new Vsd_card; + + // init trace dump + Verilated::traceEverOn(true); + tfp = new VerilatedVcdC; + top->trace (tfp, 1000); + tfp->open ("sd_card.vcd"); + + // initialize simulation inputs + top->io_ack = 0; + top->io_din = 0x00; + top->io_din_strobe = 0; + top->io_dout_strobe = 0; + top->allow_sdhc = 1; + + top->sd_cs = 1; + top->sd_sck = 0; + top->sd_sdi = 1; + + file = (struct SimpleFile *)alloca(file_struct_size()); + file_init(file); + + host_init(); + + if (SimpleFile_OK == dir_init((void *)mem, DIR_INIT_MEMSIZE)) { + struct SimpleDirEntry * entries = dir_entries(ROM_DIR); + if (SimpleFile_OK == file_open_name_in_dir(entries, "atarixl.rom", file)) { + unsigned char b[512]; + int r = 0, total = 0; + + // dump current contents + printf("atarixl.rom found\n"); + + while((SimpleFile_OK == file_read(file, b, 512, &r)) && (r > 0)) { + // printf("read %d\n", r); + total += r; + } + + printf("read total of %d bytes\n", total); + + } else + printf("atarixl.rom not found\n"); + } else + printf("dir init failed\n"); + + tfp->close(); + + printf("MMC access done\n"); + + exit(0); +} + diff --git a/tests/verilator/sdcard/simpledir.h b/tests/verilator/sdcard/simpledir.h new file mode 100644 index 0000000..bf9d5ba --- /dev/null +++ b/tests/verilator/sdcard/simpledir.h @@ -0,0 +1,24 @@ +#pragma once + +#include "simplefile.h" + +// Extends simple dir with way of opening files and looking at dirs! +// Not all systems provide this... + +struct SimpleDirEntry; + +enum SimpleFileStatus file_open_name(char const * path, struct SimpleFile * file); +enum SimpleFileStatus file_open_name_in_dir(struct SimpleDirEntry * entries, char const * filename, struct SimpleFile * file); +enum SimpleFileStatus file_open_dir(struct SimpleDirEntry * filename, struct SimpleFile * file); + +// Reads entire dir into memory (i.e. give it a decent chunk of sdram) +enum SimpleFileStatus dir_init(void * mem, int space); +struct SimpleDirEntry * dir_entries_filtered(char const * dirPath, int (*filter)(struct SimpleDirEntry *)); +struct SimpleDirEntry * dir_entries(char const * dirPath); + +char const * dir_filename(struct SimpleDirEntry *); +char const * dir_path(struct SimpleDirEntry *); +int dir_filesize(struct SimpleDirEntry *); +struct SimpleDirEntry * dir_next(struct SimpleDirEntry *); +int dir_is_subdir(struct SimpleDirEntry *); + diff --git a/tests/verilator/sdcard/simplefile.h b/tests/verilator/sdcard/simplefile.h new file mode 100644 index 0000000..c364fdb --- /dev/null +++ b/tests/verilator/sdcard/simplefile.h @@ -0,0 +1,22 @@ +#pragma once + +enum SimpleFileStatus {SimpleFile_OK, SimpleFile_FAIL}; + +struct SimpleFile; + +// NB when switching file, the other file may loose its position, depending on implementation! + +int file_struct_size(); + +void file_init(struct SimpleFile * file); + +char const * file_path(struct SimpleFile * file); +char const * file_name(struct SimpleFile * file); +enum SimpleFileStatus file_read(struct SimpleFile * file, void * buffer, int bytes, int * bytesread); +enum SimpleFileStatus file_seek(struct SimpleFile * file, int offsetFromStart); +int file_size(struct SimpleFile * file); +int file_readonly(struct SimpleFile * file); + +enum SimpleFileStatus file_write(struct SimpleFile * file, void * buffer, int bytes, int * byteswritten); +enum SimpleFileStatus file_write_flush(); + diff --git a/tests/verilator/sdcard/spi.h b/tests/verilator/sdcard/spi.h new file mode 100644 index 0000000..8508a84 --- /dev/null +++ b/tests/verilator/sdcard/spi.h @@ -0,0 +1,68 @@ +/*! \file spi.h \brief SPI interface driver. */ +//***************************************************************************** +// +// File Name : 'spi.h' +// Title : SPI interface driver +// Author : Pascal Stang - Copyright (C) 2000-2002 +// Created : 11/22/2000 +// Revised : 06/06/2002 +// Version : 0.6 +// Target MCU : Atmel AVR series +// Editor Tabs : 4 +// +// NOTE: This code is currently below version 1.0, and therefore is considered +// to be lacking in some functionality or documentation, or may not be fully +// tested. Nonetheless, you can expect most functions to work. +// +/// \ingroup driver_avr +/// \defgroup spi SPI (Serial Peripheral Interface) Function Library (spi.c) +/// \code #include "spi.h" \endcode +/// \par Overview +/// Provides basic byte and word transmitting and receiving via the AVR +/// SPI interface.  Due to the nature of SPI, every SPI communication operation +/// is both a transmit and simultaneous receive. +/// +/// \note Currently, only MASTER mode is supported. +// +// ---------------------------------------------------------------------------- +// 17.8.2008 +// Bob!k & Raster, C.P.U. +// Original code was modified especially for the SDrive device. +// Some parts of code have been added, removed, rewrited or optimized due to +// lack of MCU AVR Atmega8 memory. +// ---------------------------------------------------------------------------- +// +// This code is distributed under the GNU Public License +// which can be found at http://www.gnu.org/licenses/gpl.txt +// +//***************************************************************************** + + +#ifndef SPI_H +#define SPI_H + +#include "integer.h" + +// function prototypes +void mmcChipSelect(int select); + +void set_spi_clock_freq(); + +// SPI interface initializer +void spiInit(void); + +// spiTransferByte(u08 data) waits until the SPI interface is ready +// and then sends a single byte over the SPI port. The function also +// returns the byte that was received during transmission. +u08 spiTransferByte(u08 data); + +// spiTransferWord(u08 data) works just like spiTransferByte but +// operates on a whole word (16-bits of data). +u08 spiTransferFF(); +void spiTransferTwoFF(); + +void spiDisplay(int i); + +void spiReceiveData(u08 * from, u08 * to); + +#endif diff --git a/tests/verilator/sdcard/utils.c b/tests/verilator/sdcard/utils.c new file mode 100644 index 0000000..18689a2 --- /dev/null +++ b/tests/verilator/sdcard/utils.c @@ -0,0 +1,50 @@ +#include "utils.h" + +int strcmp(char const * a, char const * b) +{ + while (*a || *b) + { + if (*a<*b) + return -1; + else if (*a>*b) + return 1; + + ++a; + ++b; + } + return 0; +} + +int stricmp(char const * a, char const * b) +{ + char buffer[128]; + char buffer2[128]; + stricpy(&buffer[0],a); + stricpy(&buffer2[0],b); + return strcmp(&buffer[0],&buffer2[0]); +} + +void strcpy(char * dest, char const * src) +{ + while (*dest++=*src++); +} + +void stricpy(char * dest, char const * src) +{ + while (*src) + { + char val = *src++; + if (val>='A' && val<='Z') val+=-'A'+'a'; + + *dest++ = val; + } + *dest = '\0'; +} + +int strlen(char const * a) +{ + int count; + for (count=0; *a; ++a,++count); + return count; +} + diff --git a/tests/verilator/sdcard/utils.h b/tests/verilator/sdcard/utils.h new file mode 100644 index 0000000..b6e51f4 --- /dev/null +++ b/tests/verilator/sdcard/utils.h @@ -0,0 +1,6 @@ +int strcmp(char const * a, char const * b); +int stricmp(char const * a, char const * b); +void strcpy(char * dest, char const * src); +void stricpy(char * dest, char const * src); +int strlen(char const * a); +