1
0
mirror of https://github.com/mist-devel/mist-board.git synced 2026-01-27 20:27:12 +00:00

Native sd card verilator test bench

This commit is contained in:
harbaum
2014-12-15 13:29:26 +00:00
parent 00653a9931
commit dfd22b9804
20 changed files with 3449 additions and 0 deletions

View File

@@ -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 &

View File

@@ -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

View File

@@ -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:");
}

View File

@@ -0,0 +1,37 @@
/*-------------------------------------------*/
/* Integer type definitions for FatFs module */
/*-------------------------------------------*/
#ifndef _FF_INTEGER
#define _FF_INTEGER
#ifdef _WIN32 /* FatFs development platform */
#include <windows.h>
#include <tchar.h>
#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

View File

@@ -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).
/// <pre>
/// _________________
/// / 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
/// </pre>
/// \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

View File

@@ -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 <stdio.h>
#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<n> is the command sequense of CMD55-CMD<n> */
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;
}

View File

@@ -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

1145
tests/verilator/sdcard/pff.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

View File

@@ -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_subdir<b->is_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;i<n-1 && !sorted;i++){
p=h;sorted=1;
prev=0;
for(j=0;j<n-(i+1);j++){
// printf("p->issubdir:%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;
}

View File

@@ -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...
};

View File

@@ -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 */

View File

@@ -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.

View File

@@ -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 <till@harbaum.org>
//
// 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://www.gnu.org/licenses/>.
//
// 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

View File

@@ -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;i<b2c;i++) printf("%02x ", 0xff&ptr[i]);
printf(" ");
for(i=0;i<(16-b2c);i++) printf(" ");
for(i=0;i<b2c;i++) printf("%c", isprint(ptr[i])?ptr[i]:'.');
printf("\n");
ptr += b2c;
size -= b2c;
n += b2c;
}
}
Vsd_card* top = NULL;
VerilatedVcdC* tfp = NULL;
int evcnt = 0;
void dump() {
top->eval();
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);
}

View File

@@ -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 *);

View File

@@ -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();

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);