From 63f58e8f7aa7aff46a833630d174d6fefd4ac8f4 Mon Sep 17 00:00:00 2001 From: gvl610 Date: Sun, 24 Sep 2023 01:18:18 +0700 Subject: [PATCH] 24/9/2023 --- build.bat | 10 + main.c | 513 +++++++++++++++++++++++++++++++++++++++++++++ mini-rv32ima.h | 551 +++++++++++++++++++++++++++++++++++++++++++++++++ sdcard.c | 431 ++++++++++++++++++++++++++++++++++++++ sdcard.h | 76 +++++++ sdprint.c | 173 ++++++++++++++++ sdprint.h | 59 ++++++ spi.c | 34 +++ spi.h | 46 +++++ types.h | 11 + uart.c | 108 ++++++++++ uart.h | 22 ++ 12 files changed, 2034 insertions(+) create mode 100644 build.bat create mode 100644 main.c create mode 100644 mini-rv32ima.h create mode 100644 sdcard.c create mode 100644 sdcard.h create mode 100644 sdprint.c create mode 100644 sdprint.h create mode 100644 spi.c create mode 100644 spi.h create mode 100644 types.h create mode 100644 uart.c create mode 100644 uart.h diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..b05ae44 --- /dev/null +++ b/build.bat @@ -0,0 +1,10 @@ +@echo off +C:\Users\mmb\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\bin\avr-gcc -c -mmcu=atmega328p -I. -DF_CPU=16000000UL -O3 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=main.lst -std=gnu99 main.c -o main.o +C:\Users\mmb\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\bin\avr-gcc -c -mmcu=atmega328p -I. -DF_CPU=16000000UL -O3 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=sdcard.lst -std=gnu99 sdcard.c -o sdcard.o +C:\Users\mmb\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\bin\avr-gcc -c -mmcu=atmega328p -I. -DF_CPU=16000000UL -O3 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=sdprint.lst -std=gnu99 sdprint.c -o sdprint.o +C:\Users\mmb\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\bin\avr-gcc -c -mmcu=atmega328p -I. -DF_CPU=16000000UL -O3 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=spi.lst -std=gnu99 spi.c -o spi.o +C:\Users\mmb\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\bin\avr-gcc -c -mmcu=atmega328p -I. -DF_CPU=16000000UL -O3 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=uart.lst -std=gnu99 uart.c -o uart.o +C:\Users\mmb\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\bin\avr-gcc -mmcu=atmega328p -I. -DF_CPU=16000000UL -O3 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -std=gnu99 main.o sdcard.o sdprint.o spi.o uart.o --output main.elf -Wl,-Map=main.map,--cref +C:\Users\mmb\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\bin\avr-objcopy -O ihex -R .eeprom main.elf main.hex +C:\Users\mmb\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\bin\avr-size -A -d main.elf +::"C:\Users\mmb\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/bin/avrdude" "-CC:\Users\mmb\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf" -v -V -patmega328p -carduino "-PCOM10" -b115200 -D "-Uflash:w:main.hex:i" diff --git a/main.c b/main.c new file mode 100644 index 0000000..b9299d7 --- /dev/null +++ b/main.c @@ -0,0 +1,513 @@ +/* + * arv32-opt: mini-rv32ima on Arduino UNO, but the code is written in pure C + * Created by gvl610 + * mini-rv32ima is created by cnlohr. See https://github.com/cnlohr/mini-rv32ima + * UART, SPI, and SD code is created by ryanj1234. See https://github.com/ryanj1234/SD_TUTORIAL_PART4 + */ + +/* + * Pinout on Arduino UNO: + * SD Arduino UNO + * CS 10 + * MOSI 11 + * MISO 12 + * SCLK 13 + * + * You can change the CS pin by modifying spi.h + */ + +// Frequency that atmega328p is running on. Default is 16MHz on Arduino UNO board +#ifndef F_CPU +#define F_CPU 16000000 +#endif + +// UART baudrate. Default is 9600 +#define BAUD_RATE 9600 + +// Headers +#include +#include +#include +#include +#include +#include +#include +#include "uart.h" +#include "spi.h" +#include "sdcard.h" +#include "sdprint.h" +#include "types.h" + +// mini-rv32ima variables +int fail_on_all_faults = 0; +uint64_t lastTime = 0; +struct MiniRV32IMAState *core; + +// SD access variables +UInt8 buf[SD_BLOCK_LEN]; +UInt32 last_sector = 0; + +// Functions prototype +static UInt32 HandleException( UInt32 ir, UInt32 retval ); +static UInt32 HandleControlStore( UInt32 addy, UInt32 val ); +static UInt32 HandleControlLoad( UInt32 addy ); +static void HandleOtherCSRWrite( UInt8 * image, UInt16 csrno, UInt32 value ); + +// Load / store helper +static UInt32 store4(UInt32 ofs, UInt32 val); +static UInt16 store2(UInt32 ofs, UInt16 val); +static UInt8 store1(UInt32 ofs, UInt8 val); + +static UInt32 load4(UInt32 ofs); +static UInt16 load2(UInt32 ofs); +static UInt8 load1(UInt32 ofs); + +// Other +UInt32 last_cyclel = 0; // Last cyclel value +void dump_state(void); + +// Config +const UInt32 RAM_SIZE = 12582912; // Minimum RAM amount (in bytes), just tested (may reduce further by custom kernel) +#define DTB_SIZE 1536 // DTB size (in bytes), must recount manually each time DTB changes +#define INSTRS_PER_FLIP 1024 // Number of instructions executed before checking status. See loop() +#define TIME_DIVISOR 1 + +// Setup mini-rv32ima +// This is the functionality we want to override in the emulator. +// think of this as the way the emulator's processor is connected to the outside world. +#define MINIRV32WARN( x... ) UART_pputs( x ); +#define MINIRV32_DECORATE static +#define MINI_RV32_RAM_SIZE RAM_SIZE +#define MINIRV32_IMPLEMENTATION // Minimum rv32 emulator +#define MINIRV32_POSTEXEC( pc, ir, retval ) { if( retval > 0 ) { if( fail_on_all_faults ) { UART_pputs("FAULT\r\n"); return 3; } else retval = HandleException( ir, retval ); } } +#define MINIRV32_HANDLE_MEM_STORE_CONTROL( addy, val ) if( HandleControlStore( addy, val ) ) return val; +#define MINIRV32_HANDLE_MEM_LOAD_CONTROL( addy, rval ) rval = HandleControlLoad( addy ); +#define MINIRV32_OTHERCSR_WRITE( csrno, value ) HandleOtherCSRWrite( image, csrno, value ); +#define MINIRV32_CUSTOM_MEMORY_BUS // Custom RAM handler for swapping to SD card + +// Macro for accessing RAM +#define MINIRV32_STORE4( ofs, val ) store4(ofs, val) +#define MINIRV32_STORE2( ofs, val ) store2(ofs, val) +#define MINIRV32_STORE1( ofs, val ) store1(ofs, val) +#define MINIRV32_LOAD4( ofs ) load4(ofs) +#define MINIRV32_LOAD2_SIGNED( ofs ) (Int8)load2(ofs) +#define MINIRV32_LOAD2( ofs ) load2(ofs) +#define MINIRV32_LOAD1_SIGNED( ofs ) (Int8)load1(ofs) +#define MINIRV32_LOAD1( ofs ) load1(ofs) + +#include "mini-rv32ima.h" + +// millis implementation from https://gist.github.com/adnbr/2439125 +volatile unsigned long timer1_millis; +unsigned long last_ms = 0; + +ISR (TIMER1_COMPA_vect) { + timer1_millis++; +} + +unsigned long millis(void) { + unsigned long millis_return; + + // Ensure this cannot be disrupted + ATOMIC_BLOCK(ATOMIC_FORCEON) { + millis_return = timer1_millis; + } + + return millis_return; +} + +// Entry point +int main(void) { + // Initialize UART + UART_init(); + + // Say something, so people know that UART works + UART_pputs("arv32-opt: mini-rv32ima on Arduino UNO\r\n"); + + // Initialize SPI + SPI_init(SPI_MASTER | SPI_FOSC_16 | SPI_MODE_0); + + // Initialize SD card + if(SD_init() != SD_SUCCESS) + { + UART_pputs("Error initializaing SD card\r\n"); + while(1); // Deadloop + } + + UART_pputs("SD card initialized successfully!\r\n"); + + // Initialize emulator struct + core = (struct MiniRV32IMAState *)malloc(sizeof(struct MiniRV32IMAState)); + memset(core, 0, sizeof(struct MiniRV32IMAState)); + + // Setup core + core->pc = MINIRV32_RAM_IMAGE_OFFSET; + core->regs[10] = 0x00; //hart ID + core->regs[11] = RAM_SIZE - sizeof(struct MiniRV32IMAState) - DTB_SIZE + MINIRV32_RAM_IMAGE_OFFSET; // dtb_pa (Must be valid pointer) (Should be pointer to dtb) + core->extraflags |= 3; // Machine-mode. + + // Prefetch first sector + UInt8 token; + SD_readSingleBlock(0, buf, &token); + + // Set digital pin 9 to input pullup, see loop + PORTB |= (1 << PINB1); + + // Init timer (from https://gist.github.com/adnbr/2439125) + TCCR1B |= (1 << WGM12) | (1 << CS11); // CTC mode, Clock/8 + OCR1AH = 7; // Load the high byte of 2000 + OCR1AL = 208; // then the low byte into the output compare + TIMSK1 |= (1 << OCIE1A); // Enable the compare match interrupt + sei(); // Now enable global interrupts + + // Emulator loop + // It's stated in the AVR documentation that writing do ... while() is faster than while() + do { + // Print processor state if requested by user + if (!(PINB & (1 << PINB1))) { + // Calculate effective emulated speed + unsigned long current_ms = millis(); + UART_pputs("Effective emulated speed: "); + UART_putdec32(((core->cyclel - last_cyclel) * 1000) / (current_ms - last_ms)); + UART_pputs(" Hz, dtime="); + UART_putdec32(current_ms - last_ms); + UART_pputs("ms, dcycle="); + UART_putdec32(core->cyclel - last_cyclel); + UART_pputs("\r\n"); + + // Dump state + dump_state(); + UART_pputs("\r\nDump completed. Emulator will continue when B1 is set back to HIGH\r\n"); + + // Wait until B1 is set back to HIGH + while (!(PINB & (1 << PINB1))); + UART_pputs("B1 is set to HIGH, emulator resume\r\n"); + + // Reset counters + last_cyclel = core->cyclel; + last_ms = millis(); + } + + // Calculate pseudo time + uint64_t * this_ccount = ((uint64_t*)&core->cyclel); + UInt32 elapsedUs = 0; + elapsedUs = *this_ccount / TIME_DIVISOR - lastTime; + lastTime += elapsedUs; + + int ret = MiniRV32IMAStep( core, NULL, 0, elapsedUs, INSTRS_PER_FLIP ); // Execute upto INSTRS_PER_FLIP cycles before breaking out. + switch( ret ) + { + case 0: break; + case 1: _delay_ms(1); *this_ccount += INSTRS_PER_FLIP; break; + //case 3: instct = 0; break; + //case 0x7777: goto restart; //syscon code for restart + case 0x5555: UART_pputs("POWEROFF\r\n"); while(1); //syscon code for power-off . halt + default: UART_pputs("Unknown failure\r\n"); break; + } + } while (1); +} + +// Exception handlers +static UInt32 HandleException( UInt32 ir, UInt32 code ) +{ + // Weird opcode emitted by duktape on exit. + if( code == 3 ) + { + // Could handle other opcodes here. + } + return code; +} + +static UInt32 HandleControlStore( UInt32 addy, UInt32 val ) +{ + if( addy == 0x10000000 ) //UART 8250 / 16550 Data Buffer + { + UART_putc((char)val); + } + + return 0; +} + + +static UInt32 HandleControlLoad( UInt32 addy ) +{ + // Emulating a 8250 / 16550 UART + if ( addy == 0x10000005 ) + return 0x60 | UART_available(); + else if ( addy == 0x10000000 && (UART_available()) ) + return UART_getc(); + return 0; +} + +static void HandleOtherCSRWrite( UInt8 * image, UInt16 csrno, UInt32 value ) +{ + /*if( csrno == 0x136 ) + { + Serial.print(value); + } + if( csrno == 0x137 ) + { + Serial.print(value, HEX); + } + else if( csrno == 0x138 ) + { + // Print "string" + UInt32 ptrstart = value - MINIRV32_RAM_IMAGE_OFFSET; + UInt32 ptrend = ptrstart; + if( ptrstart >= ram_amt ) + printf( "DEBUG PASSED INVALID PTR (%08x)\n", value ); + while( ptrend < ram_amt ) + { + if( image[ptrend] == 0 ) break; + ptrend++; + } + if( ptrend != ptrstart ) + fwrite( image + ptrstart, ptrend - ptrstart, 1, stdout ); + }*/ +} + +void read_buf(UInt32 ofs, UInt8 flag) { + uint32_t s; + + /* + * Some operations might involve read/write bytes that are located between 2 sectors on + * the SD card. In that case, we have to fetch 2 continuous sectors at a time. Since + * we already know the last sector number, we can just read the n + 1 sector and skip + * the division (which is a pain on AVR). The flag parameter is used for this. When + * flag is 0, we will calculate the sector number. Else, we will just fetch the n + 1 + * sector. + */ + if (flag == 0) { + // Calculate sector num + // Dividing on AVR is a pain, so we should avoid that if we can + s = ofs / SD_BLOCK_LEN; + + // If sector num the same as last one, then return because we already read that sector + if (s == last_sector) + return; + else + last_sector = s; + } else { + // Fetch n + 1 sector + s = ++last_sector; + } + + UInt8 res, token; + UInt8 t = 0; +read_begin: + res = SD_readSingleBlock(s, buf, &token); + + // If no error -> return + if((res == 0x00) && (token == SD_START_TOKEN)) + return; + // Else if error token received, print + else if(!(token & 0xF0)) + { + UART_pputs("read_buf error, error token:\r\n"); + SD_printDataErrToken(token); + if (++t == 10) { + dump_state(); + UART_pputs("read_buf: failed 10 times in a row. Halting\r\n"); + while(1); // Halt + } + + goto read_begin; + } +} + +void write_buf(void) { + UInt8 res, token; + UInt8 t = 0; +write_begin: + // Write to last sector (since you already read last sector, then modify the content before you can write) + res = SD_writeSingleBlock(last_sector, buf, &token); + + // If no error -> return + if((res == 0x00) && (token == SD_DATA_ACCEPTED)) + return; + // Else if error token received, print + else if(!(token & 0xF0)) + { + UART_pputs("write_buf error, error token:\r\n"); + SD_printDataErrToken(token); + if (++t == 10) { + dump_state(); + UART_pputs("write_buf: failed 10 times in a row. Halting\r\n"); + while(1); // Halt + } + + goto write_begin; + } +} + +// Memory access functions +static UInt32 load4(UInt32 ofs) { + static int dh = 0; + UInt32 result; + + if (ofs == 0xB8) { + // Skip memory clean + UART_pputs("Currently at 0xB8\r\n"); + if (dh == 1) {while(1);} + dh = 1; + return 0xFEE6DCE3; // RISC-V opcode to skip the memory-cleaning loop. Got by disassembling the RAM dump + } + + UInt16 r = ofs % 512; + read_buf(ofs, 0); + if (r >= 509) { + // 1 - 3 bytes are in nth sector, and the others in n + 1 sector + // Read the nth sector and get the bytes in that sector + UInt8 i = 0; + for (; i < SD_BLOCK_LEN - r; i++) { + ((UInt8 *)&result)[i] = buf[r + i]; + } + + // Read the next sector and get the remaining bytes + read_buf(ofs, 1); + for (UInt8 j = 0; j < r - 508; j++) { + ((UInt8 *)&result)[i + j] = buf[j]; + } + + return result; + } + + ((UInt8 *)&result)[0] = buf[r]; // LSB + ((UInt8 *)&result)[1] = buf[r + 1]; + ((UInt8 *)&result)[2] = buf[r + 2]; + ((UInt8 *)&result)[3] = buf[r + 3]; // MSB + return result; +} +static UInt16 load2(UInt32 ofs) { + UInt16 result; + UInt16 r = ofs % 512; + read_buf(ofs, 0); + if (r == 511) { + // LSB located in nth sector + ((UInt8 *)&result)[0] = buf[511]; + + // MSB located in n + 1 sector + read_buf(ofs, 1); + ((UInt8 *)&result)[1] = buf[0]; + + return result; + } + + ((UInt8 *)&result)[0] = buf[r]; // LSB + ((UInt8 *)&result)[1] = buf[r + 1]; // MSB + return result; +} +static UInt8 load1(UInt32 ofs) { + read_buf(ofs, 0); + return buf[ofs % 512]; +} + +static UInt32 store4(UInt32 ofs, UInt32 val) { + UInt16 r = ofs % 512; + read_buf(ofs, 0); + if (r >= 509) { + // 1 - 3 bytes are in nth sector, and the others in n + 1 sector + // Read the nth sector and change the bytes in that sector + UInt8 i = 0; + for (; i < SD_BLOCK_LEN - r; i++) { + buf[r + i] = ((UInt8 *)&val)[i]; + } + + // Write back to that sector + write_buf(); + + // Read the next sector and get the remaining bytes + read_buf(ofs, 1); + for (UInt8 j = 0; j < r - 508; j++) { + buf[j] = ((UInt8 *)&val)[i + j]; + } + + // Write back to that sector + write_buf(); + return val; + } + + buf[r] = ((UInt8 *)&val)[0]; // LSB + buf[r + 1] = ((UInt8 *)&val)[1]; + buf[r + 2] = ((UInt8 *)&val)[2]; + buf[r + 3] = ((UInt8 *)&val)[3]; // MSB + write_buf(); + return val; +} + +static UInt16 store2(UInt32 ofs, UInt16 val) { + UInt16 r = ofs % 512; + read_buf(ofs, 0); + + if (r == 511) { + // LSB located in the nth sector + buf[511] = ((UInt8 *)&val)[0]; + write_buf(); + + // MSB located in the n + 1 sector + read_buf(ofs, 1); + buf[0] = ((UInt8 *)&val)[1]; + write_buf(); + return val; + } + + buf[r] = ((UInt8 *)&r)[0]; // LSB + buf[r + 1] = ((UInt8 *)&r)[1]; // MSB + write_buf(); + return val; +} + +static UInt8 store1(UInt32 ofs, UInt8 val) { + read_buf(ofs, 0); + buf[ofs % 512] = val; + write_buf(); + return val; +} + +void dump_state(void) { + UART_pputs("==============================================================================\r\n"); + UART_pputs("Dumping emulator state:\r\nRegisters x0 - x31:\r\n"); + + // Print registers + for (uint8_t i = 0; i < 8; i++) { + // Print 4 registers at once + UART_puthex32(core->regs[i*4]); UART_pputs(" "); + UART_puthex32(core->regs[i*4 + 1]); UART_pputs(" "); + UART_puthex32(core->regs[i*4 + 2]); UART_pputs(" "); + UART_puthex32(core->regs[i*4 + 3]); UART_pputs("\r\n"); + } + + UART_pputs("pc: "); + UART_puthex32(core->pc); + UART_pputs("\r\nmstatus: "); + UART_puthex32(core->mstatus); + UART_pputs("\r\ncyclel: "); + UART_puthex32(core->cyclel); + UART_pputs("\r\ncycleh: "); + UART_puthex32(core->cycleh); + UART_pputs("\r\ntimerl: "); + UART_puthex32(core->timerl); + UART_pputs("\r\ntimerh: "); + UART_puthex32(core->timerh); + UART_pputs("\r\ntimermatchl: "); + UART_puthex32(core->timermatchl); + UART_pputs("\r\ntimermatchh: "); + UART_puthex32(core->timermatchh); + UART_pputs("\r\nmscratch: "); + UART_puthex32(core->mscratch); + UART_pputs("\r\nmtvec: "); + UART_puthex32(core->mtvec); + UART_pputs("\r\nmie: "); + UART_puthex32(core->mie); + UART_pputs("\r\nmip: "); + UART_puthex32(core->mip); + UART_pputs("\r\nmepc: "); + UART_puthex32(core->mepc); + UART_pputs("\r\nmtval: "); + UART_puthex32(core->mtval); + UART_pputs("\r\nmcause: "); + UART_puthex32(core->mcause); + UART_pputs("\r\nextraflags: "); + UART_puthex32(core->extraflags); + UART_pputs("\r\n==============================================================================\r\n"); +} diff --git a/mini-rv32ima.h b/mini-rv32ima.h new file mode 100644 index 0000000..3125d97 --- /dev/null +++ b/mini-rv32ima.h @@ -0,0 +1,551 @@ +// Copyright 2022 Charles Lohr, you may use this file or any portions herein under any of the BSD, MIT, or CC0 licenses. + +#ifndef _MINI_RV32IMAH_H +#define _MINI_RV32IMAH_H + +/** + To use mini-rv32ima.h for the bare minimum, the following: + + #define MINI_RV32_RAM_SIZE ram_amt + #define MINIRV32_IMPLEMENTATION + + #include "mini-rv32ima.h" + + Though, that's not _that_ interesting. You probably want I/O! + + + Notes: + * There is a dedicated CLNT at 0x10000000. + * There is free MMIO from there to 0x12000000. + * You can put things like a UART, or whatever there. + * Feel free to override any of the functionality with macros. +*/ + +#ifndef MINIRV32WARN + #define MINIRV32WARN( x... ); +#endif + +#ifndef MINIRV32_DECORATE + #define MINIRV32_DECORATE static +#endif + +#ifndef MINIRV32_RAM_IMAGE_OFFSET + #define MINIRV32_RAM_IMAGE_OFFSET 0x80000000 +#endif + +#ifndef MINIRV32_POSTEXEC + #define MINIRV32_POSTEXEC(...); +#endif + +#ifndef MINIRV32_HANDLE_MEM_STORE_CONTROL + #define MINIRV32_HANDLE_MEM_STORE_CONTROL(...); +#endif + +#ifndef MINIRV32_HANDLE_MEM_LOAD_CONTROL + #define MINIRV32_HANDLE_MEM_LOAD_CONTROL(...); +#endif + +#ifndef MINIRV32_OTHERCSR_WRITE + #define MINIRV32_OTHERCSR_WRITE(...); +#endif + +#ifndef MINIRV32_OTHERCSR_READ + #define MINIRV32_OTHERCSR_READ(...); +#endif + +#ifndef MINIRV32_CUSTOM_MEMORY_BUS + #define MINIRV32_STORE4( ofs, val ) *(uint32_t*)(image + ofs) = val + #define MINIRV32_STORE2( ofs, val ) *(uint16_t*)(image + ofs) = val + #define MINIRV32_STORE1( ofs, val ) *(uint8_t*)(image + ofs) = val + #define MINIRV32_LOAD4( ofs ) *(uint32_t*)(image + ofs) + #define MINIRV32_LOAD2( ofs ) *(uint16_t*)(image + ofs) + #define MINIRV32_LOAD1( ofs ) *(uint8_t*)(image + ofs) + #define MINIRV32_LOAD2_SIGNED( ofs ) *(int16_t*)(image + ofs) + #define MINIRV32_LOAD1_SIGNED( ofs ) *(int8_t*)(image + ofs) +#endif + +// As a note: We quouple-ify these, because in HLSL, we will be operating with +// uint4's. We are going to uint4 data to/from system RAM. +// +// We're going to try to keep the full processor state to 12 x uint4. +struct MiniRV32IMAState +{ + uint32_t regs[32]; + + uint32_t pc; + uint32_t mstatus; + uint32_t cyclel; + uint32_t cycleh; + + uint32_t timerl; + uint32_t timerh; + uint32_t timermatchl; + uint32_t timermatchh; + + uint32_t mscratch; + uint32_t mtvec; + uint32_t mie; + uint32_t mip; + + uint32_t mepc; + uint32_t mtval; + uint32_t mcause; + + // Note: only a few bits are used. (Machine = 3, User = 0) + // Bits 0..1 = privilege. + // Bit 2 = WFI (Wait for interrupt) + // Bit 3+ = Load/Store reservation LSBs. + uint32_t extraflags; +}; + +#ifndef MINIRV32_STEPPROTO +MINIRV32_DECORATE int32_t MiniRV32IMAStep( struct MiniRV32IMAState * state, uint8_t * image, uint32_t vProcAddress, uint32_t elapsedUs, int count ); +#endif + +#ifdef MINIRV32_IMPLEMENTATION + +#ifndef MINIRV32_CUSTOM_INTERNALS +#define CSR( x ) state->x +#define SETCSR( x, val ) { state->x = val; } +#define REG( x ) state->regs[x] +#define REGSET( x, val ) { state->regs[x] = val; } +#endif + +#ifndef MINIRV32_STEPPROTO +MINIRV32_DECORATE int32_t MiniRV32IMAStep( struct MiniRV32IMAState * state, uint8_t * image, uint32_t vProcAddress, uint32_t elapsedUs, int count ) +#else +MINIRV32_STEPPROTO +#endif +{ + uint32_t new_timer = CSR( timerl ) + elapsedUs; + if( new_timer < CSR( timerl ) ) CSR( timerh )++; + CSR( timerl ) = new_timer; + + // Handle Timer interrupt. + if( ( CSR( timerh ) > CSR( timermatchh ) || ( CSR( timerh ) == CSR( timermatchh ) && CSR( timerl ) > CSR( timermatchl ) ) ) && ( CSR( timermatchh ) || CSR( timermatchl ) ) ) + { + CSR( extraflags ) &= ~4; // Clear WFI + CSR( mip ) |= 1<<7; //MTIP of MIP // https://stackoverflow.com/a/61916199/2926815 Fire interrupt. + } + else + CSR( mip ) &= ~(1<<7); + + // If WFI, don't run processor. + if( CSR( extraflags ) & 4 ) + return 1; + + uint32_t trap = 0; + uint32_t rval = 0; + uint32_t pc = CSR( pc ); + uint32_t cycle = CSR( cyclel ); + + if( ( CSR( mip ) & (1<<7) ) && ( CSR( mie ) & (1<<7) /*mtie*/ ) && ( CSR( mstatus ) & 0x8 /*mie*/) ) + { + // Timer interrupt. + trap = 0x80000007; + pc -= 4; + } + else // No timer interrupt? Execute a bunch of instructions. + for( int icount = 0; icount < count; icount++ ) + { + uint32_t ir = 0; + rval = 0; + cycle++; + uint32_t ofs_pc = pc - MINIRV32_RAM_IMAGE_OFFSET; + + if( ofs_pc >= MINI_RV32_RAM_SIZE ) + { + trap = 1 + 1; // Handle access violation on instruction read. + break; + } + else if( ofs_pc & 3 ) + { + trap = 1 + 0; //Handle PC-misaligned access + break; + } + else + { + ir = MINIRV32_LOAD4( ofs_pc ); + uint32_t rdid = (ir >> 7) & 0x1f; + + switch( ir & 0x7f ) + { + case 0x37: // LUI (0b0110111) + rval = ( ir & 0xfffff000 ); + break; + case 0x17: // AUIPC (0b0010111) + rval = pc + ( ir & 0xfffff000 ); + break; + case 0x6F: // JAL (0b1101111) + { + int32_t reladdy = ((ir & 0x80000000)>>11) | ((ir & 0x7fe00000)>>20) | ((ir & 0x00100000)>>9) | ((ir&0x000ff000)); + if( reladdy & 0x00100000 ) reladdy |= 0xffe00000; // Sign extension. + rval = pc + 4; + pc = pc + reladdy - 4; + break; + } + case 0x67: // JALR (0b1100111) + { + uint32_t imm = ir >> 20; + int32_t imm_se = imm | (( imm & 0x800 )?0xfffff000:0); + rval = pc + 4; + pc = ( (REG( (ir >> 15) & 0x1f ) + imm_se) & ~1) - 4; + break; + } + case 0x63: // Branch (0b1100011) + { + uint32_t immm4 = ((ir & 0xf00)>>7) | ((ir & 0x7e000000)>>20) | ((ir & 0x80) << 4) | ((ir >> 31)<<12); + if( immm4 & 0x1000 ) immm4 |= 0xffffe000; + int32_t rs1 = REG((ir >> 15) & 0x1f); + int32_t rs2 = REG((ir >> 20) & 0x1f); + immm4 = pc + immm4 - 4; + rdid = 0; + switch( ( ir >> 12 ) & 0x7 ) + { + // BEQ, BNE, BLT, BGE, BLTU, BGEU + case 0: if( rs1 == rs2 ) pc = immm4; break; + case 1: if( rs1 != rs2 ) pc = immm4; break; + case 4: if( rs1 < rs2 ) pc = immm4; break; + case 5: if( rs1 >= rs2 ) pc = immm4; break; //BGE + case 6: if( (uint32_t)rs1 < (uint32_t)rs2 ) pc = immm4; break; //BLTU + case 7: if( (uint32_t)rs1 >= (uint32_t)rs2 ) pc = immm4; break; //BGEU + default: trap = (2+1); + } + break; + } + case 0x03: // Load (0b0000011) + { + uint32_t rs1 = REG((ir >> 15) & 0x1f); + uint32_t imm = ir >> 20; + int32_t imm_se = imm | (( imm & 0x800 )?0xfffff000:0); + uint32_t rsval = rs1 + imm_se; + + rsval -= MINIRV32_RAM_IMAGE_OFFSET; + if( rsval >= MINI_RV32_RAM_SIZE-3 ) + { + rsval += MINIRV32_RAM_IMAGE_OFFSET; + if( rsval >= 0x10000000 && rsval < 0x12000000 ) // UART, CLNT + { + if( rsval == 0x1100bffc ) // https://chromitem-soc.readthedocs.io/en/latest/clint.html + rval = CSR( timerh ); + else if( rsval == 0x1100bff8 ) + rval = CSR( timerl ); + else + MINIRV32_HANDLE_MEM_LOAD_CONTROL( rsval, rval ); + } + else + { + trap = (5+1); + rval = rsval; + } + } + else + { + switch( ( ir >> 12 ) & 0x7 ) + { + //LB, LH, LW, LBU, LHU + case 0: rval = MINIRV32_LOAD1_SIGNED( rsval ); break; + case 1: rval = MINIRV32_LOAD2_SIGNED( rsval ); break; + case 2: rval = MINIRV32_LOAD4( rsval ); break; + case 4: rval = MINIRV32_LOAD1( rsval ); break; + case 5: rval = MINIRV32_LOAD2( rsval ); break; + default: trap = (2+1); + } + } + break; + } + case 0x23: // Store 0b0100011 + { + uint32_t rs1 = REG((ir >> 15) & 0x1f); + uint32_t rs2 = REG((ir >> 20) & 0x1f); + uint32_t addy = ( ( ir >> 7 ) & 0x1f ) | ( ( ir & 0xfe000000 ) >> 20 ); + if( addy & 0x800 ) addy |= 0xfffff000; + addy += rs1 - MINIRV32_RAM_IMAGE_OFFSET; + rdid = 0; + + if( addy >= MINI_RV32_RAM_SIZE-3 ) + { + addy += MINIRV32_RAM_IMAGE_OFFSET; + if( addy >= 0x10000000 && addy < 0x12000000 ) + { + // Should be stuff like SYSCON, 8250, CLNT + if( addy == 0x11004004 ) //CLNT + CSR( timermatchh ) = rs2; + else if( addy == 0x11004000 ) //CLNT + CSR( timermatchl ) = rs2; + else if( addy == 0x11100000 ) //SYSCON (reboot, poweroff, etc.) + { + SETCSR( pc, pc + 4 ); + return rs2; // NOTE: PC will be PC of Syscon. + } + else + MINIRV32_HANDLE_MEM_STORE_CONTROL( addy, rs2 ); + } + else + { + trap = (7+1); // Store access fault. + rval = addy; + } + } + else + { + switch( ( ir >> 12 ) & 0x7 ) + { + //SB, SH, SW + case 0: MINIRV32_STORE1( addy, rs2 ); break; + case 1: MINIRV32_STORE2( addy, rs2 ); break; + case 2: MINIRV32_STORE4( addy, rs2 ); break; + default: trap = (2+1); + } + } + break; + } + case 0x13: // Op-immediate 0b0010011 + case 0x33: // Op 0b0110011 + { + uint32_t imm = ir >> 20; + imm = imm | (( imm & 0x800 )?0xfffff000:0); + uint32_t rs1 = REG((ir >> 15) & 0x1f); + uint32_t is_reg = !!( ir & 0x20 ); + uint32_t rs2 = is_reg ? REG(imm & 0x1f) : imm; + + if( is_reg && ( ir & 0x02000000 ) ) + { + switch( (ir>>12)&7 ) //0x02000000 = RV32M + { + case 0: rval = rs1 * rs2; break; // MUL +#ifndef CUSTOM_MULH // If compiling on a system that doesn't natively, or via libgcc support 64-bit math. + case 1: rval = ((int64_t)((int32_t)rs1) * (int64_t)((int32_t)rs2)) >> 32; break; // MULH + case 2: rval = ((int64_t)((int32_t)rs1) * (uint64_t)rs2) >> 32; break; // MULHSU + case 3: rval = ((uint64_t)rs1 * (uint64_t)rs2) >> 32; break; // MULHU +#else + CUSTOM_MULH +#endif + case 4: if( rs2 == 0 ) rval = -1; else rval = ((int32_t)rs1 == INT32_MIN && (int32_t)rs2 == -1) ? rs1 : ((int32_t)rs1 / (int32_t)rs2); break; // DIV + case 5: if( rs2 == 0 ) rval = 0xffffffff; else rval = rs1 / rs2; break; // DIVU + case 6: if( rs2 == 0 ) rval = rs1; else rval = ((int32_t)rs1 == INT32_MIN && (int32_t)rs2 == -1) ? 0 : ((uint32_t)((int32_t)rs1 % (int32_t)rs2)); break; // REM + case 7: if( rs2 == 0 ) rval = rs1; else rval = rs1 % rs2; break; // REMU + } + } + else + { + switch( (ir>>12)&7 ) // These could be either op-immediate or op commands. Be careful. + { + case 0: rval = (is_reg && (ir & 0x40000000) ) ? ( rs1 - rs2 ) : ( rs1 + rs2 ); break; + case 1: rval = rs1 << (rs2 & 0x1F); break; + case 2: rval = (int32_t)rs1 < (int32_t)rs2; break; + case 3: rval = rs1 < rs2; break; + case 4: rval = rs1 ^ rs2; break; + case 5: rval = (ir & 0x40000000 ) ? ( ((int32_t)rs1) >> (rs2 & 0x1F) ) : ( rs1 >> (rs2 & 0x1F) ); break; + case 6: rval = rs1 | rs2; break; + case 7: rval = rs1 & rs2; break; + } + } + break; + } + case 0x0f: // 0b0001111 + rdid = 0; // fencetype = (ir >> 12) & 0b111; We ignore fences in this impl. + break; + case 0x73: // Zifencei+Zicsr (0b1110011) + { + uint32_t csrno = ir >> 20; + uint32_t microop = ( ir >> 12 ) & 0x7; + if( (microop & 3) ) // It's a Zicsr function. + { + int rs1imm = (ir >> 15) & 0x1f; + uint32_t rs1 = REG(rs1imm); + uint32_t writeval = rs1; + + // https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf + // Generally, support for Zicsr + switch( csrno ) + { + case 0x340: rval = CSR( mscratch ); break; + case 0x305: rval = CSR( mtvec ); break; + case 0x304: rval = CSR( mie ); break; + case 0xC00: rval = cycle; break; + case 0x344: rval = CSR( mip ); break; + case 0x341: rval = CSR( mepc ); break; + case 0x300: rval = CSR( mstatus ); break; //mstatus + case 0x342: rval = CSR( mcause ); break; + case 0x343: rval = CSR( mtval ); break; + case 0xf11: rval = 0xff0ff0ff; break; //mvendorid + case 0x301: rval = 0x40401101; break; //misa (XLEN=32, IMA+X) + //case 0x3B0: rval = 0; break; //pmpaddr0 + //case 0x3a0: rval = 0; break; //pmpcfg0 + //case 0xf12: rval = 0x00000000; break; //marchid + //case 0xf13: rval = 0x00000000; break; //mimpid + //case 0xf14: rval = 0x00000000; break; //mhartid + default: + MINIRV32_OTHERCSR_READ( csrno, rval ); + break; + } + + switch( microop ) + { + case 1: writeval = rs1; break; //CSRRW + case 2: writeval = rval | rs1; break; //CSRRS + case 3: writeval = rval & ~rs1; break; //CSRRC + case 5: writeval = rs1imm; break; //CSRRWI + case 6: writeval = rval | rs1imm; break; //CSRRSI + case 7: writeval = rval & ~rs1imm; break; //CSRRCI + } + + switch( csrno ) + { + case 0x340: SETCSR( mscratch, writeval ); break; + case 0x305: SETCSR( mtvec, writeval ); break; + case 0x304: SETCSR( mie, writeval ); break; + case 0x344: SETCSR( mip, writeval ); break; + case 0x341: SETCSR( mepc, writeval ); break; + case 0x300: SETCSR( mstatus, writeval ); break; //mstatus + case 0x342: SETCSR( mcause, writeval ); break; + case 0x343: SETCSR( mtval, writeval ); break; + //case 0x3a0: break; //pmpcfg0 + //case 0x3B0: break; //pmpaddr0 + //case 0xf11: break; //mvendorid + //case 0xf12: break; //marchid + //case 0xf13: break; //mimpid + //case 0xf14: break; //mhartid + //case 0x301: break; //misa + default: + MINIRV32_OTHERCSR_WRITE( csrno, writeval ); + break; + } + } + else if( microop == 0x0 ) // "SYSTEM" 0b000 + { + rdid = 0; + if( csrno == 0x105 ) //WFI (Wait for interrupts) + { + CSR( mstatus ) |= 8; //Enable interrupts + CSR( extraflags ) |= 4; //Infor environment we want to go to sleep. + SETCSR( pc, pc + 4 ); + return 1; + } + else if( ( ( csrno & 0xff ) == 0x02 ) ) // MRET + { + //https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf + //Table 7.6. MRET then in mstatus/mstatush sets MPV=0, MPP=0, MIE=MPIE, and MPIE=1. La + // Should also update mstatus to reflect correct mode. + uint32_t startmstatus = CSR( mstatus ); + uint32_t startextraflags = CSR( extraflags ); + SETCSR( mstatus , (( startmstatus & 0x80) >> 4) | ((startextraflags&3) << 11) | 0x80 ); + SETCSR( extraflags, (startextraflags & ~3) | ((startmstatus >> 11) & 3) ); + pc = CSR( mepc ) -4; + } + else + { + switch( csrno ) + { + case 0: trap = ( CSR( extraflags ) & 3) ? (11+1) : (8+1); break; // ECALL; 8 = "Environment call from U-mode"; 11 = "Environment call from M-mode" + case 1: trap = (3+1); break; // EBREAK 3 = "Breakpoint" + default: trap = (2+1); break; // Illegal opcode. + } + } + } + else + trap = (2+1); // Note micrrop 0b100 == undefined. + break; + } + case 0x2f: // RV32A (0b00101111) + { + uint32_t rs1 = REG((ir >> 15) & 0x1f); + uint32_t rs2 = REG((ir >> 20) & 0x1f); + uint32_t irmid = ( ir>>27 ) & 0x1f; + + rs1 -= MINIRV32_RAM_IMAGE_OFFSET; + + // We don't implement load/store from UART or CLNT with RV32A here. + + if( rs1 >= MINI_RV32_RAM_SIZE-3 ) + { + trap = (7+1); //Store/AMO access fault + rval = rs1 + MINIRV32_RAM_IMAGE_OFFSET; + } + else + { + rval = MINIRV32_LOAD4( rs1 ); + + // Referenced a little bit of https://github.com/franzflasch/riscv_em/blob/master/src/core/core.c + uint32_t dowrite = 1; + switch( irmid ) + { + case 2: //LR.W (0b00010) + dowrite = 0; + CSR( extraflags ) = (CSR( extraflags ) & 0x07) | (rs1<<3); + break; + case 3: //SC.W (0b00011) (Make sure we have a slot, and, it's valid) + rval = ( CSR( extraflags ) >> 3 != ( rs1 & 0x1fffffff ) ); // Validate that our reservation slot is OK. + dowrite = !rval; // Only write if slot is valid. + break; + case 1: break; //AMOSWAP.W (0b00001) + case 0: rs2 += rval; break; //AMOADD.W (0b00000) + case 4: rs2 ^= rval; break; //AMOXOR.W (0b00100) + case 12: rs2 &= rval; break; //AMOAND.W (0b01100) + case 8: rs2 |= rval; break; //AMOOR.W (0b01000) + case 16: rs2 = ((int32_t)rs2<(int32_t)rval)?rs2:rval; break; //AMOMIN.W (0b10000) + case 20: rs2 = ((int32_t)rs2>(int32_t)rval)?rs2:rval; break; //AMOMAX.W (0b10100) + case 24: rs2 = (rs2rval)?rs2:rval; break; //AMOMAXU.W (0b11100) + default: trap = (2+1); dowrite = 0; break; //Not supported. + } + if( dowrite ) MINIRV32_STORE4( rs1, rs2 ); + } + break; + } + default: trap = (2+1); // Fault: Invalid opcode. + } + + // If there was a trap, do NOT allow register writeback. + if( trap ) + break; + + if( rdid ) + { + REGSET( rdid, rval ); // Write back register. + } + } + + MINIRV32_POSTEXEC( pc, ir, trap ); + + pc += 4; + } + + // Handle traps and interrupts. + if( trap ) + { + if( trap & 0x80000000 ) // If prefixed with 1 in MSB, it's an interrupt, not a trap. + { + SETCSR( mcause, trap ); + SETCSR( mtval, 0 ); + pc += 4; // PC needs to point to where the PC will return to. + } + else + { + SETCSR( mcause, trap - 1 ); + SETCSR( mtval, (trap > 5 && trap <= 8)? rval : pc ); + } + SETCSR( mepc, pc ); //TRICKY: The kernel advances mepc automatically. + //CSR( mstatus ) & 8 = MIE, & 0x80 = MPIE + // On an interrupt, the system moves current MIE into MPIE + SETCSR( mstatus, (( CSR( mstatus ) & 0x08) << 4) | (( CSR( extraflags ) & 3 ) << 11) ); + pc = (CSR( mtvec ) - 4); + + // If trapping, always enter machine mode. + CSR( extraflags ) |= 3; + + trap = 0; + pc += 4; + } + + if( CSR( cyclel ) > cycle ) CSR( cycleh )++; + SETCSR( cyclel, cycle ); + SETCSR( pc, pc ); + return 0; +} + +#endif + +#endif + diff --git a/sdcard.c b/sdcard.c new file mode 100644 index 0000000..fbd7cad --- /dev/null +++ b/sdcard.c @@ -0,0 +1,431 @@ +#include +#include +#include +#include "sdcard.h" +#include "sdprint.h" +#include "spi.h" +#include "uart.h" + +/******************************************************************************* + Initialize SD card +*******************************************************************************/ +uint8_t SD_init() +{ + uint8_t res[5], cmdAttempts = 0; + + SD_powerUpSeq(); + + while((res[0] = SD_goIdleState()) != SD_IN_IDLE_STATE) + { + cmdAttempts++; + if(cmdAttempts == CMD0_MAX_ATTEMPTS) + { + return SD_ERROR; + } + } + + _delay_ms(1); + + SD_sendIfCond(res); + if(res[0] != SD_IN_IDLE_STATE) + { + return SD_ERROR; + } + + if(res[4] != 0xAA) + { + return SD_ERROR; + } + + cmdAttempts = 0; + do + { + if(cmdAttempts == CMD55_MAX_ATTEMPTS) + { + return SD_ERROR; + } + + res[0] = SD_sendApp(); + if(SD_R1_NO_ERROR(res[0])) + { + res[0] = SD_sendOpCond(); + } + + _delay_ms(1); + + cmdAttempts++; + } + while(res[0] != SD_READY); + + _delay_ms(1); + + SD_readOCR(res); + + return SD_SUCCESS; +} + +/******************************************************************************* + Run power up sequence +*******************************************************************************/ +void SD_powerUpSeq() +{ + // make sure card is deselected + CS_DISABLE(); + + // give SD card time to power up + _delay_ms(10); + + // select SD card + SPI_transfer(0xFF); + CS_DISABLE(); + + // send 80 clock cycles to synchronize + for(uint8_t i = 0; i < SD_INIT_CYCLES; i++) + SPI_transfer(0xFF); +} + +/******************************************************************************* + Send command to SD card +*******************************************************************************/ +void SD_command(uint8_t cmd, uint32_t arg, uint8_t crc) +{ + // transmit command to sd card + SPI_transfer(cmd|0x40); + + // transmit argument + SPI_transfer((uint8_t)(arg >> 24)); + SPI_transfer((uint8_t)(arg >> 16)); + SPI_transfer((uint8_t)(arg >> 8)); + SPI_transfer((uint8_t)(arg)); + + // transmit crc + SPI_transfer(crc|0x01); +} + +/******************************************************************************* + Read R1 from SD card +*******************************************************************************/ +uint8_t SD_readRes1() +{ + uint8_t i = 0, res1; + + // keep polling until actual data received + while((res1 = SPI_transfer(0xFF)) == 0xFF) + { + i++; + + // if no data received for 8 bytes, break + if(i > 8) break; + } + + return res1; +} + +/******************************************************************************* + Read R2 from SD card +*******************************************************************************/ +void SD_readRes2(uint8_t *res) +{ + // read response 1 in R2 + res[0] = SD_readRes1(); + + // read final byte of response + res[1] = SPI_transfer(0xFF); +} + +/******************************************************************************* + Read R3 from SD card +*******************************************************************************/ +void SD_readRes3(uint8_t *res) +{ + // read response 1 in R3 + res[0] = SD_readRes1(); + + // if error reading R1, return + if(res[0] > 1) return; + + // read remaining bytes + SD_readBytes(res + 1, R3_BYTES); +} + +/******************************************************************************* + Read R7 from SD card +*******************************************************************************/ +void SD_readRes7(uint8_t *res) +{ + // read response 1 in R7 + res[0] = SD_readRes1(); + + // if error reading R1, return + if(res[0] > 1) return; + + // read remaining bytes + SD_readBytes(res + 1, R7_BYTES); +} + +/******************************************************************************* + Read specified number of bytes from SD card +*******************************************************************************/ +void SD_readBytes(uint8_t *res, uint8_t n) +{ + while(n--) *res++ = SPI_transfer(0xFF); +} + +/******************************************************************************* + Command Idle State (CMD0) +*******************************************************************************/ +uint8_t SD_goIdleState() +{ + // assert chip select + SPI_transfer(0xFF); + CS_ENABLE(); + SPI_transfer(0xFF); + + // send CMD0 + SD_command(CMD0, CMD0_ARG, CMD0_CRC); + + // read response + uint8_t res1 = SD_readRes1(); + + // deassert chip select + SPI_transfer(0xFF); + CS_DISABLE(); + SPI_transfer(0xFF); + + return res1; +} + +/******************************************************************************* + Send Interface Conditions (CMD8) +*******************************************************************************/ +void SD_sendIfCond(uint8_t *res) +{ + // assert chip select + SPI_transfer(0xFF); + CS_ENABLE(); + SPI_transfer(0xFF); + + // send CMD8 + SD_command(CMD8, CMD8_ARG, CMD8_CRC); + + // read response + SD_readRes7(res); + //SD_readBytes(res + 1, R7_BYTES); + + // deassert chip select + SPI_transfer(0xFF); + CS_DISABLE(); + SPI_transfer(0xFF); +} + +/******************************************************************************* + Read Status +*******************************************************************************/ +void SD_sendStatus(uint8_t *res) +{ + // assert chip select + SPI_transfer(0xFF); + CS_ENABLE(); + SPI_transfer(0xFF); + + // send CMD13 + SD_command(CMD13, CMD13_ARG, CMD13_CRC); + + // read response + SD_readRes2(res); + + // deassert chip select + SPI_transfer(0xFF); + CS_DISABLE(); + SPI_transfer(0xFF); +} + +/******************************************************************************* + Read single 512 byte block + token = 0xFE - Successful read + token = 0x0X - Data error + token = 0xFF - timeout +*******************************************************************************/ +uint8_t SD_readSingleBlock(uint32_t addr, uint8_t *buf, uint8_t *token) +{ + uint8_t res1, read; + uint16_t readAttempts; + + // set token to none + *token = 0xFF; + + // assert chip select + SPI_transfer(0xFF); + CS_ENABLE(); + SPI_transfer(0xFF); + + // send CMD17 + SD_command(CMD17, addr, CMD17_CRC); + + // read R1 + res1 = SD_readRes1(); + + // if response received from card + if(res1 != 0xFF) + { + // wait for a response token (timeout = 100ms) + readAttempts = 0; + while(++readAttempts != SD_MAX_READ_ATTEMPTS) + if((read = SPI_transfer(0xFF)) != 0xFF) break; + + // if response token is 0xFE + if(read == SD_START_TOKEN) + { + // read 512 byte block + for(uint16_t i = 0; i < SD_BLOCK_LEN; i++) *buf++ = SPI_transfer(0xFF); + + // read 16-bit CRC + SPI_transfer(0xFF); + SPI_transfer(0xFF); + } + + // set token to card response + *token = read; + } + + // deassert chip select + SPI_transfer(0xFF); + CS_DISABLE(); + SPI_transfer(0xFF); + + return res1; +} + +#define SD_MAX_WRITE_ATTEMPTS 3907 + +/******************************************************************************* +Write single 512 byte block +token = 0x00 - busy timeout +token = 0x05 - data accepted +token = 0xFF - response timeout +*******************************************************************************/ +uint8_t SD_writeSingleBlock(uint32_t addr, uint8_t *buf, uint8_t *token) +{ + uint16_t readAttempts; + uint8_t res1, read; + + // set token to none + *token = 0xFF; + + // assert chip select + SPI_transfer(0xFF); + CS_ENABLE(); + SPI_transfer(0xFF); + + // send CMD24 + SD_command(CMD24, addr, CMD24_CRC); + + // read response + res1 = SD_readRes1(); + + // if no error + if(res1 == SD_READY) + { + // send start token + SPI_transfer(SD_START_TOKEN); + + // write buffer to card + for(uint16_t i = 0; i < SD_BLOCK_LEN; i++) SPI_transfer(buf[i]); + + // wait for a response (timeout = 250ms) + readAttempts = 0; + while(++readAttempts != SD_MAX_WRITE_ATTEMPTS) + if((read = SPI_transfer(0xFF)) != 0xFF) { *token = 0xFF; break; } + + // if data accepted + if((read & 0x1F) == 0x05) + { + // set token to data accepted + *token = 0x05; + + // wait for write to finish (timeout = 250ms) + readAttempts = 0; + while(SPI_transfer(0xFF) == 0x00) + if(++readAttempts == SD_MAX_WRITE_ATTEMPTS) { *token = 0x00; break; } + } + } + + // deassert chip select + SPI_transfer(0xFF); + CS_DISABLE(); + SPI_transfer(0xFF); + + return res1; +} + +/******************************************************************************* + Reads OCR from SD Card +*******************************************************************************/ +void SD_readOCR(uint8_t *res) +{ + // assert chip select + SPI_transfer(0xFF); + CS_ENABLE(); + uint8_t tmp = SPI_transfer(0xFF); + + if(tmp != 0xFF) while(SPI_transfer(0xFF) != 0xFF) ; + + // send CMD58 + SD_command(CMD58, CMD58_ARG, CMD58_CRC); + + // read response + SD_readRes3(res); + + // deassert chip select + SPI_transfer(0xFF); + CS_DISABLE(); + SPI_transfer(0xFF); +} + +/******************************************************************************* + Send application command (CMD55) +*******************************************************************************/ +uint8_t SD_sendApp() +{ + // assert chip select + SPI_transfer(0xFF); + CS_ENABLE(); + SPI_transfer(0xFF); + + // send CMD0 + SD_command(CMD55, CMD55_ARG, CMD55_CRC); + + // read response + uint8_t res1 = SD_readRes1(); + + // deassert chip select + SPI_transfer(0xFF); + CS_DISABLE(); + SPI_transfer(0xFF); + + return res1; +} + +/******************************************************************************* + Send operating condition (ACMD41) +*******************************************************************************/ +uint8_t SD_sendOpCond() +{ + // assert chip select + SPI_transfer(0xFF); + CS_ENABLE(); + SPI_transfer(0xFF); + + // send CMD0 + SD_command(ACMD41, ACMD41_ARG, ACMD41_CRC); + + // read response + uint8_t res1 = SD_readRes1(); + + // deassert chip select + SPI_transfer(0xFF); + CS_DISABLE(); + SPI_transfer(0xFF); + + return res1; +} diff --git a/sdcard.h b/sdcard.h new file mode 100644 index 0000000..eb6e418 --- /dev/null +++ b/sdcard.h @@ -0,0 +1,76 @@ +#ifndef _sd_card_h +#define _sd_card_h + +// command definitions +#define CMD0 0 +#define CMD0_ARG 0x00000000 +#define CMD0_CRC 0x94 +#define CMD8 8 +#define CMD8_ARG 0x0000001AA +#define CMD8_CRC 0x86 +#define CMD9 9 +#define CMD9_ARG 0x00000000 +#define CMD9_CRC 0x00 +#define CMD10 9 +#define CMD10_ARG 0x00000000 +#define CMD10_CRC 0x00 +#define CMD13 13 +#define CMD13_ARG 0x00000000 +#define CMD13_CRC 0x00 +#define CMD17 17 +#define CMD17_CRC 0x00 +#define CMD24 24 +#define CMD24_CRC 0x00 +#define CMD55 55 +#define CMD55_ARG 0x00000000 +#define CMD55_CRC 0x00 +#define CMD58 58 +#define CMD58_ARG 0x00000000 +#define CMD58_CRC 0x00 +#define ACMD41 41 +#define ACMD41_ARG 0x40000000 +#define ACMD41_CRC 0x00 + +#define SD_IN_IDLE_STATE 0x01 +#define SD_READY 0x00 +#define SD_R1_NO_ERROR(X) X < 0x02 + +#define R3_BYTES 4 +#define R7_BYTES 4 + +#define CMD0_MAX_ATTEMPTS 255 +#define CMD55_MAX_ATTEMPTS 255 +#define SD_ERROR 1 +#define SD_SUCCESS 0 +#define SD_MAX_READ_ATTEMPTS 1563 +#define SD_READ_START_TOKEN 0xFE +#define SD_INIT_CYCLES 80 + +#define SD_START_TOKEN 0xFE +#define SD_ERROR_TOKEN 0x00 + +#define SD_DATA_ACCEPTED 0x05 +#define SD_DATA_REJECTED_CRC 0x0B +#define SD_DATA_REJECTED_WRITE 0x0D + +#define SD_BLOCK_LEN 512 + +// SD functions +uint8_t SD_init(void); +void SD_powerUpSeq(void); +void SD_command(uint8_t cmd, uint32_t arg, uint8_t crc); +uint8_t SD_readRes1(void); +void SD_readRes2(uint8_t *res); +void SD_readRes3(uint8_t *res); +void SD_readRes7(uint8_t *res); +void SD_readBytes(uint8_t *res, uint8_t n); +uint8_t SD_goIdleState(void); +void SD_sendIfCond(uint8_t *res); +void SD_sendStatus(uint8_t *res); +void SD_readOCR(uint8_t *res); +uint8_t SD_sendApp(void); +uint8_t SD_sendOpCond(void); +uint8_t SD_readSingleBlock(uint32_t addr, uint8_t *buf, uint8_t *error); +uint8_t SD_writeSingleBlock(uint32_t addr, uint8_t *buf, uint8_t *res); + +#endif diff --git a/sdprint.c b/sdprint.c new file mode 100644 index 0000000..f9e247f --- /dev/null +++ b/sdprint.c @@ -0,0 +1,173 @@ +#include "sdprint.h" +#include "sdcard.h" +#include "uart.h" + +void SD_printR1(uint8_t res) +{ + if(res == 0xFF) + { UART_pputs("\tNo response\r\n"); return; } + if(res & 0x80) + { UART_pputs("\tError: MSB = 1\r\n"); return; } + if(res == 0) + { UART_pputs("\tCard Ready\r\n"); return; } + if(PARAM_ERROR(res)) + UART_pputs("\tParameter Error\r\n"); + if(ADDR_ERROR(res)) + UART_pputs("\tAddress Error\r\n"); + if(ERASE_SEQ_ERROR(res)) + UART_pputs("\tErase Sequence Error\r\n"); + if(CRC_ERROR(res)) + UART_pputs("\tCRC Error\r\n"); + if(ILLEGAL_CMD(res)) + UART_pputs("\tIllegal Command\r\n"); + if(ERASE_RESET(res)) + UART_pputs("\tErase Reset Error\r\n"); + if(IN_IDLE(res)) + UART_pputs("\tIn Idle State\r\n"); +} + +void SD_printR2(uint8_t *res) +{ + SD_printR1(res[0]); + + if(res[0] == 0xFF) return; + + if(res[1] == 0x00) + UART_pputs("\tNo R2 Error\r\n"); + if(OUT_OF_RANGE(res[1])) + UART_pputs("\tOut of Range\r\n"); + if(ERASE_PARAM(res[1])) + UART_pputs("\tErase Parameter\r\n"); + if(WP_VIOLATION(res[1])) + UART_pputs("\tWP Violation\r\n"); + if(CARD_ECC_FAILED(res[1])) + UART_pputs("\tECC Failed\r\n"); + if(CC_ERROR(res[1])) + UART_pputs("\tCC Error\r\n"); + if(ERROR(res[1])) + UART_pputs("\tError\r\n"); + if(WP_ERASE_SKIP(res[1])) + UART_pputs("\tWP Erase Skip\r\n"); + if(CARD_LOCKED(res[1])) + UART_pputs("\tCard Locked\r\n"); +} + +void SD_printR3(uint8_t *res) +{ + SD_printR1(res[0]); + + if(res[0] > 1) return; + + UART_pputs("\tCard Power Up Status: "); + if(POWER_UP_STATUS(res[1])) + { + UART_pputs("READY\r\n"); + UART_pputs("\tCCS Status: "); + if(CCS_VAL(res[1])){ UART_pputs("1\r\n"); } + else UART_pputs("0\r\n"); + } + else + { + UART_pputs("BUSY\r\n"); + } + + UART_pputs("\tVDD Window: "); + if(VDD_2728(res[3])) UART_pputs("2.7-2.8, "); + if(VDD_2829(res[2])) UART_pputs("2.8-2.9, "); + if(VDD_2930(res[2])) UART_pputs("2.9-3.0, "); + if(VDD_3031(res[2])) UART_pputs("3.0-3.1, "); + if(VDD_3132(res[2])) UART_pputs("3.1-3.2, "); + if(VDD_3233(res[2])) UART_pputs("3.2-3.3, "); + if(VDD_3334(res[2])) UART_pputs("3.3-3.4, "); + if(VDD_3435(res[2])) UART_pputs("3.4-3.5, "); + if(VDD_3536(res[2])) UART_pputs("3.5-3.6"); + UART_pputs("\r\n"); +} + +void SD_printR7(uint8_t *res) +{ + SD_printR1(res[0]); + + if(res[0] > 1) return; + + UART_pputs("\tCommand Version: "); + UART_puthex8(CMD_VER(res[1])); + UART_pputs("\r\n"); + + UART_pputs("\tVoltage Accepted: "); + if(VOL_ACC(res[3]) == VOLTAGE_ACC_27_33) + UART_pputs("2.7-3.6V\r\n"); + else if(VOL_ACC(res[3]) == VOLTAGE_ACC_LOW) + UART_pputs("LOW VOLTAGE\r\n"); + else if(VOL_ACC(res[3]) == VOLTAGE_ACC_RES1) + UART_pputs("RESERVED\r\n"); + else if(VOL_ACC(res[3]) == VOLTAGE_ACC_RES2) + UART_pputs("RESERVED\r\n"); + else + UART_pputs("NOT DEFINED\r\n"); + + UART_pputs("\tEcho: "); + UART_puthex8(res[4]); + UART_pputs("\r\n"); +} + +void SD_printCSD(uint8_t *buf) +{ + UART_pputs("CSD:\r\n"); + + UART_pputs("\tCSD Structure: "); + UART_puthex8((buf[0] & 0b11000000) >> 6); + UART_pputs("\r\n"); + + UART_pputs("\tTAAC: "); + UART_puthex8(buf[1]); + UART_pputs("\r\n"); + + UART_pputs("\tNSAC: "); + UART_puthex8(buf[2]); + UART_pputs("\r\n"); + + UART_pputs("\tTRAN_SPEED: "); + UART_puthex8(buf[3]); + UART_pputs("\r\n"); + + UART_pputs("\tDevice Size: "); + UART_puthex8(buf[7] & 0b00111111); + UART_puthex8(buf[8]); + UART_puthex8(buf[9]); + UART_pputs("\r\n"); +} + +void SD_printBuf(uint8_t *buf) +{ + uint8_t colCount = 0; + for(uint16_t i = 0; i < SD_BLOCK_LEN; i++) + { + UART_puthex8(*buf++); + if(colCount == 19) + { + UART_pputs("\r\n"); + colCount = 0; + } + else + { + UART_putc(' '); + colCount++; + } + } + UART_pputs("\r\n"); +} + +void SD_printDataErrToken(uint8_t token) +{ + if(token & 0xF0) + UART_pputs("\tNot Error token\r\n"); + if(SD_TOKEN_OOR(token)) + UART_pputs("\tData out of range\r\n"); + if(SD_TOKEN_CECC(token)) + UART_pputs("\tCard ECC failed\r\n"); + if(SD_TOKEN_CC(token)) + UART_pputs("\tCC Error\r\n"); + if(SD_TOKEN_ERROR(token)) + UART_pputs("\tError\r\n"); +} diff --git a/sdprint.h b/sdprint.h new file mode 100644 index 0000000..4ebc3b4 --- /dev/null +++ b/sdprint.h @@ -0,0 +1,59 @@ +#ifndef __SD_PRINT_H__ +#define __SD_PRINT_H__ + +#include + +/* R1 MACROS */ +#define PARAM_ERROR(X) X & 0b01000000 +#define ADDR_ERROR(X) X & 0b00100000 +#define ERASE_SEQ_ERROR(X) X & 0b00010000 +#define CRC_ERROR(X) X & 0b00001000 +#define ILLEGAL_CMD(X) X & 0b00000100 +#define ERASE_RESET(X) X & 0b00000010 +#define IN_IDLE(X) X & 0b00000001 + +/* R2 MACROS */ +#define OUT_OF_RANGE(X) X & 0b10000000 +#define ERASE_PARAM(X) X & 0b01000000 +#define WP_VIOLATION(X) X & 0b00100000 +#define CARD_ECC_FAILED(X) X & 0b00010000 +#define CC_ERROR(X) X & 0b00001000 +#define ERROR(X) X & 0b00000100 +#define WP_ERASE_SKIP(X) X & 0b00000010 +#define CARD_LOCKED(X) X & 0b00000001 + +/* R3 MACROS */ +#define POWER_UP_STATUS(X) X & 0x40 +#define CCS_VAL(X) X & 0x40 +#define VDD_2728(X) X & 0b10000000 +#define VDD_2829(X) X & 0b00000001 +#define VDD_2930(X) X & 0b00000010 +#define VDD_3031(X) X & 0b00000100 +#define VDD_3132(X) X & 0b00001000 +#define VDD_3233(X) X & 0b00010000 +#define VDD_3334(X) X & 0b00100000 +#define VDD_3435(X) X & 0b01000000 +#define VDD_3536(X) X & 0b10000000 + +/* R7 MACROS */ +#define CMD_VER(X) ((X >> 4) & 0xF0) +#define VOL_ACC(X) (X & 0x1F) +#define VOLTAGE_ACC_27_33 0b00000001 +#define VOLTAGE_ACC_LOW 0b00000010 +#define VOLTAGE_ACC_RES1 0b00000100 +#define VOLTAGE_ACC_RES2 0b00001000 + +/* DATA ERROR TOKEN */ +#define SD_TOKEN_OOR(X) X & 0b00001000 +#define SD_TOKEN_CECC(X) X & 0b00000100 +#define SD_TOKEN_CC(X) X & 0b00000010 +#define SD_TOKEN_ERROR(X) X & 0b00000001 + +void SD_printR1(uint8_t res); +void SD_printR2(uint8_t *res); +void SD_printR3(uint8_t *res); +void SD_printR7(uint8_t *res); +void SD_printBuf(uint8_t *buf); +void SD_printDataErrToken(uint8_t token); + +#endif diff --git a/spi.c b/spi.c new file mode 100644 index 0000000..578ccd2 --- /dev/null +++ b/spi.c @@ -0,0 +1,34 @@ +#include +#include "spi.h" + +/******************************************************************************* +* spi.c v1 - 11/02/2018 + Initial definitions +* spi.c v2 - 11/07/2018 + Updated initialization functions +*******************************************************************************/ + +void SPI_init(uint16_t initParams) +{ + // set CS, MOSI and SCK to output + DDR_SPI |= (1 << CS) | (1 << MOSI) | (1 << SCK); + + // enable pull up resistor in MISO + DDR_SPI |= (1 << MISO); + + // set SPI params + SPCR |= ((uint8_t) (initParams >> 8)) | (1 << SPE); + SPSR |= ((uint8_t) initParams); +} + +uint8_t SPI_transfer(uint8_t data) +{ + // load data into register + SPDR = data; + + // Wait for transmission complete + while(!(SPSR & (1 << SPIF))); + + // return SPDR + return SPDR; +} diff --git a/spi.h b/spi.h new file mode 100644 index 0000000..4849008 --- /dev/null +++ b/spi.h @@ -0,0 +1,46 @@ +#ifndef __SPI_H__ +#define __SPI_H__ + +/******************************************************************************* +* spi.h v1 - 11/02/2018 + Initial definitions +* spi.h v2 - 11/07/2018 + Updated initialization function +*******************************************************************************/ + +// pin definitions +#define DDR_SPI DDRB +#define PORT_SPI PORTB +#define CS PINB2 +#define MOSI PINB3 +#define MISO PINB4 +#define SCK PINB5 + +// macros +#define CS_ENABLE() PORT_SPI &= ~(1 << CS) +#define CS_DISABLE() PORT_SPI |= (1 << CS) + +// initialization definitions +#define SPI_MASTER 0b0001000000000000 +#define SPI_SLAVE 0b0000000000000000 +#define SPI_FOSC_4 0b0000000000000000 +#define SPI_FOSC_8 0b0000010100000000 +#define SPI_FOSC_16 0b0000000100000000 +#define SPI_FOSC_64 0b0000001000000000 +#define SPI_FOSC_128 0b0000001100000000 +#define SPI_2X_FOSC_2 0b0000000000000001 +#define SPI_2X_FOSC_8 0b0000000100000001 +#define SPI_2X_FOSC_32 0b0000001000000001 +#define SPI_2X_FOSC_64 0b0000001100000001 +#define SPI_INTERRUPTS 0b1000000000000000 +#define SPI_MODE_0 0b0000000000000000 +#define SPI_MODE_1 0b0000010000000000 +#define SPI_MODE_2 0b0000100000000000 +#define SPI_MODE_3 0b0000110000000000 +#define SPI_DEFAULT SPI_MASTER | SPI_FOSC_128 | SPI_MODE_0 + +// SPI functions +void SPI_init(uint16_t initParams); +uint8_t SPI_transfer(uint8_t data); + +#endif diff --git a/types.h b/types.h new file mode 100644 index 0000000..bb64bfe --- /dev/null +++ b/types.h @@ -0,0 +1,11 @@ +#ifndef _TYPES_H_ +#define _TYPES_H_ + +typedef unsigned long UInt32; +typedef signed long Int32; +typedef unsigned short UInt16; +typedef signed short Int16; +typedef unsigned char UInt8; +typedef signed char Int8; + +#endif diff --git a/uart.c b/uart.c new file mode 100644 index 0000000..237a91c --- /dev/null +++ b/uart.c @@ -0,0 +1,108 @@ +#include +#include +#include "uart.h" + +const char hex_digits[] = "0123456789ABCDEF"; + +void UART_init() +{ + #ifndef BAUD_RATE + #define BAUD_RATE 9600 + #endif + + // set rate + UBRR0H = (unsigned char) (((F_CPU/(BAUD_RATE*16UL))) - 1) >> 8; + UBRR0L = (unsigned char) ((F_CPU/(BAUD_RATE*16UL))) - 1; + + // Enable reciever and transmitter + UCSR0B |= (1 << RXEN0)|(1 << TXEN0); +} + +void UART_putc(const unsigned char data) +{ + // wait for empty transmit buffer + while(!(UCSR0A & (1< 0) + // print character + UART_putc(*charString++); +} + +void UART_puthex8(uint8_t val) +{ + // extract upper and lower nibbles from input value + uint8_t upperNibble = (val & 0xF0) >> 4; + uint8_t lowerNibble = val & 0x0F; + + // convert nibble to its ASCII hex equivalent + upperNibble += upperNibble > 9 ? 'A' - 10 : '0'; + lowerNibble += lowerNibble > 9 ? 'A' - 10 : '0'; + + // print the characters + UART_putc(upperNibble); + UART_putc(lowerNibble); +} + +unsigned char UART_getc(void) +{ + // wait for data to be received + while(!(UCSR0A & (1 << RXC0))); + + // get data to output register + return UDR0; +} + +void UART_puts_p(const char* ps) +{ + register char c; + + while ((c = pgm_read_byte(ps++))) + UART_putc(c); +} + +unsigned char UART_available(void) +{ + return UCSR0A & (1 << RXC0); +} + +void UART_puthex32(unsigned long value) { + UART_pputs("0x"); + char x[9]; + unsigned char i, c; + + x[8] = '\0'; + + for (i = 0; i < 8; i++) { + c = value & 0x0F; + value >>= 4; + c = (c >= 10) ? (c + 'A' - 10) : (c + '0'); + x[7 - i] = c; + } + + UART_puts(x); +} + +void UART_putdec32(unsigned long value) { + + char x[16]; + unsigned char i, c; + + x[sizeof(x) - 1] = 0; + + for(i = 0; i < sizeof(x) - 1; i++){ + + c = (value % 10) + '0'; + value /= 10; + x[sizeof(x) - 2 - i] = c; + if(!value) break; + } + + UART_puts(x + sizeof(x) - 2 - i); +} diff --git a/uart.h b/uart.h new file mode 100644 index 0000000..48a246c --- /dev/null +++ b/uart.h @@ -0,0 +1,22 @@ +#ifndef _uart_h +#define _uart_h +#include + +/************************************ +* uart.h v1 - 11/02/2018 +************************************/ +#define BAUD2BRR(x) (((F_CPU/((x)*16UL))) - 1) +#define UART_pputs(x) UART_puts_p(PSTR(x)) + +// UART functions +void UART_init(void); +void UART_putc(const unsigned char data); +void UART_puts(const char* charString); +void UART_puthex8(uint8_t val); +unsigned char UART_getc(void); +void UART_puts_p(const char* ps); +unsigned char UART_available(void); +void UART_puthex32(unsigned long value); +void UART_putdec32(unsigned long value); + +#endif