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:
31
tests/verilator/sdcard/Makefile
Normal file
31
tests/verilator/sdcard/Makefile
Normal 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 &
|
||||
42
tests/verilator/sdcard/diskio.h
Normal file
42
tests/verilator/sdcard/diskio.h
Normal 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
|
||||
147
tests/verilator/sdcard/diskio_mmc.c
Normal file
147
tests/verilator/sdcard/diskio_mmc.c
Normal 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:");
|
||||
}
|
||||
|
||||
|
||||
37
tests/verilator/sdcard/integer.h
Normal file
37
tests/verilator/sdcard/integer.h
Normal 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
|
||||
145
tests/verilator/sdcard/mmc.h
Normal file
145
tests/verilator/sdcard/mmc.h
Normal 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
|
||||
269
tests/verilator/sdcard/mmc2.c
Normal file
269
tests/verilator/sdcard/mmc2.c
Normal 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;
|
||||
}
|
||||
33
tests/verilator/sdcard/mmcconf.h
Normal file
33
tests/verilator/sdcard/mmcconf.h
Normal 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
1145
tests/verilator/sdcard/pff.c
Normal file
File diff suppressed because it is too large
Load Diff
160
tests/verilator/sdcard/pff.h
Normal file
160
tests/verilator/sdcard/pff.h
Normal 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 */
|
||||
456
tests/verilator/sdcard/pff_file.c
Normal file
456
tests/verilator/sdcard/pff_file.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
28
tests/verilator/sdcard/pff_file.h
Normal file
28
tests/verilator/sdcard/pff_file.h
Normal 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...
|
||||
};
|
||||
|
||||
89
tests/verilator/sdcard/pffconf.h
Normal file
89
tests/verilator/sdcard/pffconf.h
Normal 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 */
|
||||
7
tests/verilator/sdcard/readme.txt
Normal file
7
tests/verilator/sdcard/readme.txt
Normal 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.
|
||||
470
tests/verilator/sdcard/sd_card.v
Normal file
470
tests/verilator/sdcard/sd_card.v
Normal 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
|
||||
220
tests/verilator/sdcard/sd_card_tb.cpp
Normal file
220
tests/verilator/sdcard/sd_card_tb.cpp
Normal 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);
|
||||
}
|
||||
|
||||
24
tests/verilator/sdcard/simpledir.h
Normal file
24
tests/verilator/sdcard/simpledir.h
Normal 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 *);
|
||||
|
||||
22
tests/verilator/sdcard/simplefile.h
Normal file
22
tests/verilator/sdcard/simplefile.h
Normal 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();
|
||||
|
||||
68
tests/verilator/sdcard/spi.h
Normal file
68
tests/verilator/sdcard/spi.h
Normal 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
|
||||
50
tests/verilator/sdcard/utils.c
Normal file
50
tests/verilator/sdcard/utils.c
Normal 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;
|
||||
}
|
||||
|
||||
6
tests/verilator/sdcard/utils.h
Normal file
6
tests/verilator/sdcard/utils.h
Normal 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);
|
||||
|
||||
Reference in New Issue
Block a user