├── README.md ├── build.bat ├── build.sh ├── if_sim.c ├── if_sim.h ├── main.c ├── main_sim.c ├── mini-rv32ima.h ├── rv32.bin ├── sdcard.c ├── sdcard.h ├── sdprint.c ├── sdprint.h ├── spi.c ├── spi.h ├── types.h ├── uart.c └── uart.h /README.md: -------------------------------------------------------------------------------- 1 | # arv32-opt 2 | [Tested successfully!] Atmega328p port of mini-rv32ima. Let's run Linux on ~~the world's worst~~ one of the world's worst Linux PC (and beat Dmitry Grinberg) 3 | 4 | ![image](https://github.com/raspiduino/arv32-opt/assets/68118236/e457c5f7-110e-457c-ab98-48de47102af9) 5 | 6 | [Video on Youtube](https://youtu.be/ZzReAELagG4) 7 | 8 | Note: The code is in **pure AVR C**. Arduino IDE is just used as *serial terminal*. 9 | 10 | UPDATE: I found a worse Linux PC: [C64](https://github.com/onnokort/semu-c64)! 11 | 12 | ## What is this? 13 | This is a port of [mini-rv32ima](https://github.com/cnlohr/mini-rv32ima) (a minimum RISC-V emulator, capable of booting Linux) on atmega328p (the core of Arduino UNO, a 8-bit AVR microcontroller). So basically, this code is for **booting Linux on Arduino UNO**. 14 | 15 | Yes you are reading it correctly, Arduino UNO can (theorically, but not practically) boot Linux. And it definitely beats [Dmitry Grinberg's (once) world's worst Linux PC](https://dmitry.gr/?r=05.Projects&proj=07.%20Linux%20on%208bit). 16 | 17 | ## How does it work? 18 | The idea is really simple: you have an Arduino UNO (or atmega328p) to run the emulator's logic, and emulator's RAM is accessed via swapping with an SD card (which is communicated through SPI interface, see more below). The emulator also has 3 512-bytes cache (1 icache and 2 dcache interchangable) and lazy/delayed cache write system. 19 | 20 | The code is written in pure C (and not Arduino) to reduce Arduino overhead (if any). It initializes UART, SPI, SD card, and a digital input-pullup pin for triggering emulator state dump. Finally, it initialize cache, then mini-rv32ima and let the emulator does its works. 21 | 22 | ## How fast is it? 23 | About ~~175Hz - 205Hz~~ ~~426 - 600Hz~~ most of the time ~~700 Hz~~ 1500Hz, peak ~~1500Hz~~ 2000Hz, lowest 32Hz with `-O3` code on an Arduino UNO based on atmega328p, clocked at 16MHz, with a class 4 SDHC card connected via 1-bit SPI interface. Complete boot time (from start to shell) is about ~~15 hours and 44 minutes~~ [8 hours and 18 minutes](https://github.com/raspiduino/arv32-opt/issues/4#:~:text=get%20the%20boot%20time%20down%20to-,8%20hours%2C%2018%20minutes,-with%20the%20following%20changes%3A%0Aspi). 24 | 25 | Update 24/9/2023: The speed is double/tripled by implementing icache 26 | 27 | Update 26/9/2023: The speed is x1.5 by implementing 3 cache + lazy write system 28 | 29 | Update 25/10/2023: SPI speed is now 4MHz, thanks to @kittennbfive and @mjungwirth's [suggestions](https://github.com/raspiduino/arv32-opt/issues/4) 30 | 31 |
Why it's *that* slow? Read `Current issues and drawbacks` section below. 32 | 33 | ## Pinout 34 | The pinout is really simple. On Arduino UNO, it should be: 35 | 36 | |Arduino UNO pin|Connect to|Description| 37 | |---------------|----------|-----------| 38 | |9|ground-connected button|When connected to GND, emulator state and effective emulated speed will be dump via UART| 39 | |10|`CS` pin on SD card|SD's chip select/`CS`/`EN`/`SS` pin| 40 | |11|`MOSI` pin on SD card|SD's `DI`/`CMD` pin| 41 | |12|`MISO` pin on SD card|SD's `DO` pin| 42 | |13|`SCLK` pin on SD card|SD's clock/`SCK`/`CLK` pin| 43 | 44 | > [!WARNING] 45 | > Since Arduino UNO (atmega328p) uses **5V** logic level, while SD card use **3.3V (or lower)** logic level, you will need a **level shifter**. You can build your own using MOSFET, but the simplest way is to buy an SD card adapter. 46 | 47 | > [!NOTE] 48 | > You can change the `CS` and the pin used to dump state to any pins you want. Just modify the code (see below). 49 | 50 | ## Usage 51 | ### Preparing the SD card 52 | - First, you will need an SD card. Any type larger than 16MB. 53 | > [!WARNING] 54 | > You should backup all your files, since doing this SD card preparation will destroy your SD card's filesystem. 55 | - Then you need some tool to directly write a file to SD card. On Windows, you can use [HDD Raw Copy Tool](https://hddguru.com/software/HDD-Raw-Copy-Tool/), on *nix you can use `dd if=file.bin of=/dev/sdX conv=notrunc`. 56 | - Download the file [`arv32.bin`](https://github.com/raspiduino/arv32-opt/raw/main/rv32.bin) file. FYI, this file is generated by dumping `mini-rv32ima` emulator's RAM array to a file when it just finished loading the kernel and stuffs (but has not boot). You can generate your own version for newer updates of Linux kernel or Buildroot. 57 | - Write it directly to the SD card using the tools in step 2. 58 | - Now you are done preparing the card. 59 | 60 | > [!IMPORTANT] 61 | > The `Preparing the SD card` section must be repeated every time you boot your emulator. Otherwise it might not boot (if Linux has initialized enough and start cleaning the memory). This might be fixed in the future. 62 | 63 | ### Building the code 64 | You can just download the latest HEX file in [Release](https://github.com/raspiduino/arv32-opt/releases/). But if you want to build on your own, here are the steps: 65 | - First, clone the repo: `git clone https://github.com/raspiduino/arv32-opt`. Then `cd arv32-opt` 66 | - Then use build the code using the following commands: 67 | ```cmd 68 | 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 69 | 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 70 | 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 71 | 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 72 | 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 73 | 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 74 | avr-objcopy -O ihex -R .eeprom main.elf main.hex 75 | avr-size -A -d main.elf 76 | ``` 77 | - Result HEX file will be `main.hex` 78 | - You can then flash the code to the board using `avrdude -v -V -patmega328p -carduino "-PCOM10" -b115200 -D "-Uflash:w:main.hex:i"`. Replace `COM10` with your `COM` port (on Windows), or `/dev/ttyUSB*` port on Linux. (Idk about Mac, but it should be the same) 79 | 80 | > [!IMPORTANT] 81 | > If you build for another AVR microcontroller, or if you use different clock speed, please change the `-mmcu=atmega328p` and `-DF_CPU=16000000UL` options to match your situation. 82 | 83 | > [!NOTE] 84 | > Windows users can refer to the script `build.bat` in this repo. Just change the path to your correct path and it will work. 85 | 86 | ### Usage 87 | After completing 2 sections above, you are now ready to boot Linux on Arduino UNO. Just connect the SD card to Arduino UNO, insert the card in, then power the Arduino on. Open a serial connection at baudrate 9600 bps to connect. 88 |
You should see the following output (immidiately): 89 | ```text 90 | arv32-opt: mini-rv32ima on Arduino UNO 91 | SD card initialized successfully! 92 | ``` 93 | 94 | > [!IMPORTANT] 95 | > The `Preparing the SD card` section must be repeated every time you boot your emulator. Otherwise it might not boot (if Linux has initialized enough and start cleaning the memory). This might be fixed in the future. 96 | 97 | > [!IMPORTANT] 98 | > If you get `Error initializaing SD card` when you just plugged your board to your computer, just press the reset button. It should work. 99 | 100 | You can also dump the state of the emulator while it's running. Just connect a button to `GND` and then connect pin 9 to the button. When you click the button, state will be dump, and you should see something like this: 101 | ```text 102 | Effective emulated speed: 1442 Hz, dtime=233596ms, dcycle=336896 103 | Current AVR free memory: 155 bytes 104 | icache hit/miss: 17931786/983542; dcache hit/miss: 4078057/1731330 105 | ============================================================================== 106 | Dumping emulator state: 107 | Registers x0 - x31: 108 | 0x00000000 0x800E6B34 0x8041F7A0 0x80332FC8 109 | 0x80420000 0x9B779A7E 0xDFB0F4A6 0x29EEBE42 110 | 0x8041F890 0xDEC0D0A6 0x9EE3B78E 0xA125AEE8 111 | 0x0A29015A 0x969C9575 0x31D9B5BA 0x1A086279 112 | 0xEFA9A208 0xAF715DDD 0x10C7F938 0x42557158 113 | 0x9A2FA369 0x6AA2616B 0x8041F8B4 0x00000000 114 | 0x00000000 0x36AAA20D 0x4BD078AB 0x000AF715 115 | 0x68A94F06 0x53A31796 0xF822BFDD 0x1A73C5BB 116 | pc: 0x800E7144 117 | mstatus: 0x00001880 118 | cyclel: 0x0120A000 119 | cycleh: 0x00000000 120 | timerl: 0x00904E00 121 | timerh: 0x00000000 122 | timermatchl: 0x009050BF 123 | timermatchh: 0x00000000 124 | mscratch: 0x00000000 125 | mtvec: 0x80001CBC 126 | mie: 0x00000088 127 | mip: 0x00000000 128 | mepc: 0x800E6640 129 | mtval: 0x00000000 130 | mcause: 0x80000007 131 | extraflags: 0x019446E3 132 | ``` 133 | `Effective emulated speed` is the number of instructions the emulator can execute in 1 second at that time. `dtime` is the time difference between current time and the last time you dump the status. `dcycle` is the number of cycle (instructions) excuted from the last time you dump the status until now. 134 |
`Registers x0 - x31` sections show registers from `x0` to `x31`, listed in order from left to right then from top down. 135 | 136 | > [!NOTE] 137 | > The emulator will resume when you release the button (you should see something like `B1 is set to HIGH, emulator resume`). As long as you keep holding the button, the emulator will pause. You can also use a wire connected to GND instead of a button (just like me). 138 | 139 | ## Things can be changed in the code 140 | - UART baudrate @ `main.c` 141 | - SPI bus speed @ `main.c` 142 | - Dump state trigger button @ `main.c` 143 | - SD's CS pin @ `spi.h` 144 | 145 | ## Current issues and drawbacks 146 | - The effective emulated speed is super slow. This is mostly due to the high overhead of 1-bit SPI connection with SD card, when every instruction that access memory must also access SD card. 147 | - Max tested SPI bus speed is FCLK/4, which is 4MHz on 16MHz atmega328p. If you set the speed higher than this, SD card will not be stable. We don't know why, so please give us some light if you know the reason. 148 | - Automatically replacing new state is not implemented yet. So every time you run the emulator, you must repeat the `Preparing the SD card` section. 149 | 150 | ## Credits 151 | - [cnlohr](https://github.com/cnlohr) for writing [mini-rv32ima](https://github.com/cnlohr/mini-rv32ima/). 152 | - [ryanj1234](https://github.com/ryanj1234) for writing [SD_TUTORIAL_PART4](https://github.com/ryanj1234/SD_TUTORIAL_PART4). 153 | - [adnbr](https://github.com/adnbr/) for writing [1 ms counter](https://gist.github.com/adnbr/2439125) 154 | - [me (gvl610/raspiduino)](https://github.com/raspiduino) for bringing all this stuff together. 155 | 156 | ## As seen on 157 | [Hackaday](https://hackaday.com/2023/10/13/because-you-can-linux-on-an-arduino-uno/), [Hackster](https://www.hackster.io/news/giang-vinh-loc-creates-the-world-s-worst-linux-pc-using-an-arduino-uno-r3-and-its-atmega328p-e5ed03e3f594), [Habr](https://habr.com/ru/news/767550/), [Maker News](https://news.mkme.org/?p=66623), [internetua](https://internetua.com/entuziast-zapustiv-linux-na-arduino-uno), [futuranet.it](https://ei.futuranet.it/2023/10/24/arduino-uno-linux-su-atmega328/), [zhihu.com](https://zhuanlan.zhihu.com/p/662411944), [tuxmachines.org](https://news.tuxmachines.org/n/2023/10/14/Because_You_Can_Linux_On_An_Arduino_Uno.shtml), [arduino.cc forum](https://forum.arduino.cc/t/ot-linux-auf-nem-uno-wie-kommt-man-auf-solche-ideen/1178341/14), [sohu.com](https://www.sohu.com/a/729847177_256585?scm=1101.topic:90189:110070.0.1.0&spm=smpc.topic_229.block2_218_tI4wzp_1_fd.1.1699030935985ig99qAS_90189) 158 | 159 | ## One last thing 160 | If you can run this, you probably are running one of the world's worst Linux PC. Enjoy! 161 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | C:\Users\giang\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 -Ofast -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=main.lst -std=gnu99 main.c -o main.o 3 | C:\Users\giang\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 -Ofast -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=sdcard.lst -std=gnu99 sdcard.c -o sdcard.o 4 | C:\Users\giang\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 -Ofast -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=sdprint.lst -std=gnu99 sdprint.c -o sdprint.o 5 | C:\Users\giang\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 -Ofast -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=spi.lst -std=gnu99 spi.c -o spi.o 6 | C:\Users\giang\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 -Ofast -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=uart.lst -std=gnu99 uart.c -o uart.o 7 | C:\Users\giang\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 8 | C:\Users\giang\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 9 | C:\Users\giang\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7\bin\avr-size -A -d main.elf 10 | ::"C:\Users\giang\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/bin/avrdude" "-CC:\Users\giang\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf" -v -V -patmega328p -carduino "-PCOM3" -b115200 -D "-Uflash:w:main.hex:i" 11 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | avr() { 4 | echo "Building for AVR" 5 | avr-gcc -c -mmcu=atmega328p -I. -DF_CPU=16000000UL -Ofast -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=main.lst -std=gnu99 main.c -o main.o 6 | avr-gcc -c -mmcu=atmega328p -I. -DF_CPU=16000000UL -Ofast -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=sdcard.lst -std=gnu99 sdcard.c -o sdcard.o 7 | avr-gcc -c -mmcu=atmega328p -I. -DF_CPU=16000000UL -Ofast -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=sdprint.lst -std=gnu99 sdprint.c -o sdprint.o 8 | avr-gcc -c -mmcu=atmega328p -I. -DF_CPU=16000000UL -Ofast -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=spi.lst -std=gnu99 spi.c -o spi.o 9 | avr-gcc -c -mmcu=atmega328p -I. -DF_CPU=16000000UL -Ofast -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=uart.lst -std=gnu99 uart.c -o uart.o 10 | avr-gcc -mmcu=atmega328p -I. -DF_CPU=16000000UL -Ofast -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 11 | avr-objcopy -O ihex -R .eeprom main.elf main.hex 12 | avr-size -A -d main.elf 13 | echo "Done! Now flash the hex to your device (maybe using avrdude)" 14 | } 15 | 16 | sim() { 17 | echo "Building for simulation (debug)" 18 | gcc -I. -g3 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -std=gnu99 main_sim.c if_sim.c -o sim 19 | } 20 | 21 | sim_opt() { 22 | echo "Building for simulation (release)" 23 | gcc -I. -march=native -Ofast -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -std=gnu99 main_sim.c if_sim.c -o sim 24 | } 25 | 26 | clean() { 27 | # sim 28 | rm sim dump.bin 29 | 30 | # avr 31 | rm *.o *.lst *.map *.elf 32 | } 33 | 34 | # Check for help flag 35 | if [[ "$1" == "-h" || "$1" == "--help" || "$1" == "help" ]]; then 36 | echo "Usage: $0 (sim|sim_opt|avr)" 37 | echo " sim: (Debug) Build computer simulation (exact same logic, only interfaces changed)" 38 | echo " sim_opt: (Release) Build computer simulation (exact same logic, only interfaces changed)" 39 | echo " avr: (Release) Build for AVR" 40 | exit 0 41 | fi 42 | 43 | # Check for arguments 44 | if [[ "$1" == "sim" ]]; then 45 | sim 46 | else 47 | if [[ "$1" == "sim_opt" ]]; then 48 | sim_opt 49 | else 50 | if [[ "$1" == "avr" ]]; then 51 | avr 52 | else 53 | if [[ "$1" == "clean" ]]; then 54 | clean 55 | else 56 | echo "Invalid argument: '$1'" 57 | exit 1 58 | fi 59 | fi 60 | fi 61 | fi 62 | -------------------------------------------------------------------------------- /if_sim.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "if_sim.h" 6 | 7 | uint8_t mem[RAM_SIZE]; 8 | 9 | // UART 10 | const char hex_digits[] = "0123456789ABCDEF"; 11 | static int is_eofd; 12 | 13 | void UART_init() 14 | { 15 | return; // Do literally nothing 16 | } 17 | 18 | void UART_putc(const unsigned char data) 19 | { 20 | putchar(data); 21 | } 22 | 23 | void UART_puts(const char* charString) 24 | { 25 | // iterate through string 26 | while(*charString > 0) 27 | // print character 28 | UART_putc(*charString++); 29 | } 30 | 31 | void UART_puthex8(uint8_t val) 32 | { 33 | // extract upper and lower nibbles from input value 34 | uint8_t upperNibble = (val & 0xF0) >> 4; 35 | uint8_t lowerNibble = val & 0x0F; 36 | 37 | // convert nibble to its ASCII hex equivalent 38 | upperNibble += upperNibble > 9 ? 'A' - 10 : '0'; 39 | lowerNibble += lowerNibble > 9 ? 'A' - 10 : '0'; 40 | 41 | // print the characters 42 | UART_putc(upperNibble); 43 | UART_putc(lowerNibble); 44 | } 45 | 46 | unsigned char UART_getc(void) 47 | { 48 | if( is_eofd ) return 0xffffffff; 49 | char rxchar = 0; 50 | int rread = read(fileno(stdin), (char*)&rxchar, 1); 51 | 52 | if( rread > 0 ) // Tricky: getchar can't be used with arrow keys. 53 | return rxchar; 54 | else 55 | return -1; 56 | } 57 | 58 | unsigned char UART_available(void) 59 | { 60 | if( is_eofd ) return -1; 61 | int byteswaiting; 62 | ioctl(0, FIONREAD, &byteswaiting); 63 | if( !byteswaiting && write( fileno(stdin), 0, 0 ) != 0 ) { is_eofd = 1; return -1; } // Is end-of-file for 64 | return !!byteswaiting; 65 | } 66 | 67 | void UART_puthex32(unsigned long value) { 68 | UART_puts("0x"); 69 | char x[9]; 70 | unsigned char i, c; 71 | 72 | x[8] = '\0'; 73 | 74 | for (i = 0; i < 8; i++) { 75 | c = value & 0x0F; 76 | value >>= 4; 77 | c = (c >= 10) ? (c + 'A' - 10) : (c + '0'); 78 | x[7 - i] = c; 79 | } 80 | 81 | UART_puts(x); 82 | } 83 | 84 | void UART_putdec32(unsigned long value) { 85 | 86 | char x[16]; 87 | unsigned char i, c; 88 | 89 | x[sizeof(x) - 1] = 0; 90 | 91 | for(i = 0; i < sizeof(x) - 1; i++){ 92 | 93 | c = (value % 10) + '0'; 94 | value /= 10; 95 | x[sizeof(x) - 2 - i] = c; 96 | if(!value) break; 97 | } 98 | 99 | UART_puts(x + sizeof(x) - 2 - i); 100 | } 101 | 102 | // SPI 103 | void SPI_init(uint16_t initParams) { 104 | // Do literally nothing 105 | } 106 | 107 | // SD 108 | void SD_printDataErrToken(uint8_t token) { 109 | // Literally impossible in a simulation, but just in case 110 | printf("SD error: %x", token); 111 | } 112 | 113 | uint8_t SD_init() { 114 | // Open and read RAM file to memory array 115 | FILE *fp = fopen("rv32.bin", "rb"); 116 | if (fp == NULL) { 117 | perror("fopen"); 118 | return 1; 119 | } 120 | 121 | // Check if file size matches expected size 122 | fseek(fp, 0, SEEK_END); // Seek to the end of the file 123 | long actual_size = ftell(fp); // Get the file size 124 | rewind(fp); // Rewind to the beginning for reading 125 | 126 | if (actual_size != RAM_SIZE) { 127 | fprintf(stderr, "Warning: File size (%ld bytes) does not match expected size (%d bytes)\n", actual_size, RAM_SIZE); 128 | } 129 | 130 | // Read the entire file into the buffer 131 | size_t bytes_read = fread(mem, 1, RAM_SIZE, fp); 132 | if (bytes_read != RAM_SIZE) { 133 | fprintf(stderr, "fread error: %zu bytes read (expected %d)\n", bytes_read, RAM_SIZE); 134 | fclose(fp); 135 | return 1; 136 | } 137 | 138 | return 0; // Success 139 | } 140 | 141 | uint8_t SD_readSingleBlock(uint32_t addr, uint8_t *buf, uint8_t *token) { 142 | // Read 143 | memcpy(buf, mem + (addr * SD_BLOCK_LEN), 512); 144 | 145 | // Read success 146 | *token = 0xFF; 147 | } 148 | 149 | uint8_t SD_writeSingleBlock(uint32_t addr, uint8_t *buf, uint8_t *res) { 150 | // Write 151 | memcpy(mem + (addr * SD_BLOCK_LEN), buf, 512); 152 | 153 | // Write success 154 | *res = 0xFF; 155 | } 156 | 157 | // Misc 158 | void _delay_ms(unsigned int ms) { 159 | usleep(ms * 1000); 160 | } 161 | -------------------------------------------------------------------------------- /if_sim.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "types.h" 9 | 10 | // Config 11 | #define RAM_SIZE 16777216UL // Minimum RAM amount (in bytes), just tested (may reduce further by custom kernel) 12 | #define DTB_SIZE 1536 // DTB size (in bytes), must recount manually each time DTB changes 13 | #define INSTRS_PER_FLIP 1024 // Number of instructions executed before checking status. See loop() 14 | #define TIME_DIVISOR 2 15 | #define SD_BLOCK_LEN 512 16 | 17 | void _delay_ms(uint32_t ms); 18 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * arv32-opt: mini-rv32ima on Arduino UNO, but the code is written in pure C 3 | * Created by gvl610 4 | * mini-rv32ima is created by cnlohr. See https://github.com/cnlohr/mini-rv32ima 5 | * UART, SPI, and SD code is created by ryanj1234. See https://github.com/ryanj1234/SD_TUTORIAL_PART4 6 | */ 7 | 8 | /* 9 | * Pinout on Arduino UNO: 10 | * SD Arduino UNO 11 | * CS 10 12 | * MOSI 11 13 | * MISO 12 14 | * SCLK 13 15 | * 16 | * You can change the CS pin by modifying spi.h 17 | */ 18 | 19 | // Frequency that atmega328p is running on. Default is 16MHz on Arduino UNO board 20 | #ifndef F_CPU 21 | #define F_CPU 16000000UL 22 | #endif 23 | 24 | // UART baudrate. Default is 9600 25 | #define BAUD_RATE 9600 26 | 27 | // Enable/disable cache hit/miss information 28 | #define ENABLE_CACHE_STAT 29 | 30 | // Headers 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "uart.h" 40 | #include "spi.h" 41 | #include "sdcard.h" 42 | #include "sdprint.h" 43 | 44 | // mini-rv32ima variables 45 | int fail_on_all_faults = 0; 46 | uint64_t lastTime = 0; 47 | struct MiniRV32IMAState *core; 48 | 49 | /* 50 | * Some notes about this stupid cache system 51 | * We are running on Arduino UNO, which uses atmega328p and only has 2KB of RAM 52 | * From what I measured, we could use up to 3x 512 bytes cache and there are 53 | * still enough RAN left for other stuff (local variables, etc) 54 | * So we will implement a system with 3 caches, each 512 bytes. 55 | * 56 | * Each cache has a 512 bytes buffer, a tag (the sector number of that 512-bytes 57 | * block in RAM), and a "dirty" flag (see below). In our implementation, we use 58 | * an uint16_t age score variables. For each data read, we will +1 to the age 59 | * score, and for each data write, we will also +1 to the age score. 60 | * 61 | * Cache will be invaild by LRU mechanism: when there is a read/write request to 62 | * an address that is not currently in the cache pool, a least recently used 63 | * cache will be invalid and a new 512-bytes block contains that address will be 64 | * fetch into the newly invaild cache. If that cache is marked as "dirty" (data 65 | * changed compared to RAM), it will be flushed to RAM first, then get invalid. 66 | * 67 | * When there is a write request to an address already in the cache pool, it 68 | * will follow lazy write mechanism. That means the change will be kept in the 69 | * cache, the cache will be marked as "dirty", and the changes won't be written 70 | * to main RAM until it gets invalid. 71 | * 72 | * When a cache is assigned as icache, it age score will be set to max (0xFFFF 73 | * for uint16_t). When the pc moves to an address that is not in the current 74 | * icache, it will get invalid, and its age score will be reset to 0. If pc 75 | * moves to an address that is currently in another dcache, that dcache will 76 | * be used for both icache and dcache, and its age score will be set to 0xFFFF. 77 | * 78 | * IMO, I don't think a cache will often be assigned as both icache and dcache 79 | * as the same time. It might just happend in self-modifying code. 80 | */ 81 | 82 | struct cache { 83 | uint8_t buf[SD_BLOCK_LEN]; 84 | uint32_t tag; 85 | uint16_t age; 86 | bool flag; // False is dirty. Why not the opposite? AVR takes 2 instructions to store a 1 and just 1 to store a 0. 87 | }; 88 | 89 | struct cache pool[3]; 90 | 91 | // Cache hit / miss stat 92 | #ifdef ENABLE_CACHE_STAT 93 | uint32_t icache_hit = 0; 94 | uint32_t icache_miss = 0; 95 | uint32_t dcache_hit = 0; 96 | uint32_t dcache_miss = 0; 97 | #endif 98 | 99 | // Functions prototype 100 | static uint32_t HandleException( uint32_t ir, uint32_t retval ); 101 | static uint32_t HandleControlStore( uint32_t addy, uint32_t val ); 102 | static uint32_t HandleControlLoad( uint32_t addy ); 103 | static void HandleOtherCSRWrite( uint8_t * image, uint16_t csrno, uint32_t value ); 104 | static int32_t HandleOtherCSRRead( uint8_t * image, uint16_t csrno ); 105 | 106 | // Load / store helper 107 | static uint32_t store4(uint32_t ofs, uint32_t val); 108 | static uint16_t store2(uint32_t ofs, uint16_t val); 109 | static uint8_t store1(uint32_t ofs, uint8_t val); 110 | 111 | static uint32_t load4(uint32_t ofs); 112 | static uint16_t load2(uint32_t ofs); 113 | static uint8_t load1(uint32_t ofs); 114 | 115 | static uint32_t loadi(uint32_t ofs); 116 | 117 | // Other 118 | extern int __heap_start; 119 | extern int *__brkval; 120 | uint32_t last_cyclel = 0; // Last cyclel value 121 | void dump_state(void); 122 | 123 | // Config 124 | const uint32_t RAM_SIZE = 16777216UL; // Minimum RAM amount (in bytes), just tested (may reduce further by custom kernel) 125 | #define DTB_SIZE 1536 // DTB size (in bytes), must recount manually each time DTB changes 126 | #define INSTRS_PER_FLIP 1024 // Number of instructions executed before checking status. See loop() 127 | #define TIME_DIVISOR 2 128 | 129 | // Setup mini-rv32ima 130 | // This is the functionality we want to override in the emulator. 131 | // think of this as the way the emulator's processor is connected to the outside world. 132 | #define MINIRV32WARN( x... ) UART_pputs( x ); 133 | #define MINIRV32_DECORATE static 134 | #define MINI_RV32_RAM_SIZE RAM_SIZE 135 | #define MINIRV32_IMPLEMENTATION // Minimum rv32 emulator 136 | #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 ); } } 137 | #define MINIRV32_HANDLE_MEM_STORE_CONTROL( addy, val ) if( HandleControlStore( addy, val ) ) return val; 138 | #define MINIRV32_HANDLE_MEM_LOAD_CONTROL( addy, rval ) rval = HandleControlLoad( addy ); 139 | #define MINIRV32_OTHERCSR_WRITE( csrno, value ) HandleOtherCSRWrite( image, csrno, value ); 140 | #define MINIRV32_OTHERCSR_READ( csrno, value ) value = HandleOtherCSRRead( image, csrno ); 141 | #define MINIRV32_CUSTOM_MEMORY_BUS // Custom RAM handler for swapping to SD card 142 | 143 | // Macro for accessing RAM 144 | #define MINIRV32_STORE4( ofs, val ) store4(ofs, val) 145 | #define MINIRV32_STORE2( ofs, val ) store2(ofs, val) 146 | #define MINIRV32_STORE1( ofs, val ) store1(ofs, val) 147 | #define MINIRV32_LOAD4( ofs ) load4(ofs) 148 | #define MINIRV32_LOAD2_SIGNED( ofs ) (int8_t)load2(ofs) 149 | #define MINIRV32_LOAD2( ofs ) load2(ofs) 150 | #define MINIRV32_LOAD1_SIGNED( ofs ) (int8_t)load1(ofs) 151 | #define MINIRV32_LOAD1( ofs ) load1(ofs) 152 | #define MINIRV32_LOADI( ofs ) loadi(ofs) 153 | 154 | #include "mini-rv32ima.h" 155 | 156 | // millis implementation from https://gist.github.com/adnbr/2439125 157 | volatile unsigned long timer1_millis; 158 | unsigned long last_ms = 0; 159 | 160 | ISR (TIMER1_COMPA_vect) { 161 | timer1_millis++; 162 | } 163 | 164 | unsigned long millis(void) { 165 | unsigned long millis_return; 166 | 167 | // Ensure this cannot be disrupted 168 | ATOMIC_BLOCK(ATOMIC_FORCEON) { 169 | millis_return = timer1_millis; 170 | } 171 | 172 | return millis_return; 173 | } 174 | 175 | // Init cache helper 176 | void init_cache(uint8_t index, uint32_t tag, uint16_t age) { 177 | uint8_t token; 178 | uint8_t t = 0; 179 | 180 | // Read init sector 181 | read_init_begin: 182 | SD_readSingleBlock(tag, pool[index].buf, &token); // buf = first sector 183 | if(!(token & 0xF0)) 184 | { 185 | if (++t == 10) { 186 | UART_pputs("init_cache #"); 187 | UART_puthex32(index); 188 | UART_pputs(" error, error token:\r\n"); 189 | SD_printDataErrToken(token); 190 | UART_pputs("init_cache: failed 10 times in a row. Halting\r\n"); 191 | while(1); // Halt 192 | } 193 | 194 | _delay_ms(100); 195 | goto read_init_begin; 196 | } 197 | 198 | // Set other info 199 | pool[index].tag = tag; 200 | pool[index].age = age; 201 | pool[index].flag = 0; 202 | } 203 | 204 | // Entry point 205 | int main(void) { 206 | // Initialize UART 207 | UART_init(); 208 | 209 | // Say something, so people know that UART works 210 | UART_pputs("arv32-opt: mini-rv32ima on Arduino UNO\r\n"); 211 | 212 | // Initialize SPI 213 | SPI_init(SPI_MASTER | SPI_FOSC_16 | SPI_MODE_0); 214 | 215 | // Initialize SD card 216 | if(SD_init() != SD_SUCCESS) 217 | { 218 | UART_pputs("Error initializaing SD card\r\n"); 219 | while(1); // Deadloop 220 | } 221 | 222 | UART_pputs("SD card initialized successfully!\r\n"); 223 | 224 | // Initialize emulator struct 225 | core = (struct MiniRV32IMAState *)malloc(sizeof(struct MiniRV32IMAState)); 226 | memset(core, 0, sizeof(struct MiniRV32IMAState)); 227 | 228 | // Setup core 229 | core->pc = MINIRV32_RAM_IMAGE_OFFSET; 230 | core->regs[10] = 0x00; //hart ID 231 | 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) 232 | core->extraflags |= 3; // Machine-mode. 233 | 234 | // Setup cache 235 | // Init cache0 as icache 236 | init_cache(0, 0, 0xFF); // buf = 0x0 (code begin at 0x0) 237 | 238 | // Init cache1 as dcache 239 | init_cache(1, 6552, 0); // buf = second accessed address (got by dumping address) 240 | 241 | // Init cache2 as dcache 242 | init_cache(2, 3011, 0); // buf = third accessed address (got by dumping address) 243 | 244 | // Patch the ram to skip memory cleaning 245 | // We should patch it in the icache, not in the memory. Otherwise PC will jump to 0x0 for some reason 246 | // Maybe the code is actually copied to somewhere else 247 | // 0x00E6D863: Original opcode at 0xAC 248 | // 0x00E6C863: Opcode to skip the memory-cleaning loop. Got by disassembly 249 | // This works by changing BGE (branch greater or equal) to BLT (branch less than) 250 | pool[0].buf[0xAD] = 0xC8; 251 | 252 | // Set digital pin 9 to input pullup, see loop 253 | PORTB |= (1 << PINB1); 254 | 255 | // Init timer (from https://gist.github.com/adnbr/2439125) 256 | TCCR1B |= (1 << WGM12) | (1 << CS11); // CTC mode, Clock/8 257 | OCR1AH = 7; // Load the high byte of 2000 258 | OCR1AL = 208; // then the low byte into the output compare 259 | TIMSK1 |= (1 << OCIE1A); // Enable the compare match interrupt 260 | sei(); // Now enable global interrupts 261 | 262 | // Print current free memory 263 | UART_pputs("Current AVR free memory: "); 264 | UART_putdec32((int) SP - (__brkval == 0 ? (int) &__heap_start : (int) __brkval)); 265 | UART_pputs(" bytes\r\n"); 266 | 267 | // Emulator loop 268 | // It's stated in the AVR documentation that writing do ... while() is faster than while() 269 | do { 270 | // Print processor state if requested by user 271 | if (!(PINB & (1 << PINB1))) { 272 | // Calculate effective emulated speed 273 | unsigned long current_ms = millis(); 274 | UART_pputs("Effective emulated speed: "); 275 | UART_putdec32(((core->cyclel - last_cyclel) * 1000) / (current_ms - last_ms)); 276 | UART_pputs(" Hz, dtime="); 277 | UART_putdec32(current_ms - last_ms); 278 | UART_pputs("ms, dcycle="); 279 | UART_putdec32(core->cyclel - last_cyclel); 280 | UART_pputs("\r\n"); 281 | 282 | // Print current free memory 283 | UART_pputs("Current AVR free memory: "); 284 | UART_putdec32((int) SP - (__brkval == 0 ? (int) &__heap_start : (int) __brkval)); 285 | UART_pputs(" bytes\r\n"); 286 | 287 | // Print icache/dcache hit/miss 288 | #ifdef ENABLE_CACHE_STAT 289 | UART_pputs("icache hit/miss: "); 290 | UART_putdec32(icache_hit); 291 | UART_pputs("/"); 292 | UART_putdec32(icache_miss); 293 | UART_pputs("; dcache hit/miss: "); 294 | UART_putdec32(dcache_hit); 295 | UART_pputs("/"); 296 | UART_putdec32(dcache_miss); 297 | UART_pputs("\r\n"); 298 | #endif 299 | // Dump state 300 | dump_state(); 301 | UART_pputs("Dump completed. Emulator will continue when B1 is set back to HIGH\r\n"); 302 | 303 | // Wait until B1 is set back to HIGH 304 | while (!(PINB & (1 << PINB1))); 305 | UART_pputs("B1 is set to HIGH, emulator resume\r\n"); 306 | 307 | // Reset counters 308 | last_cyclel = core->cyclel; 309 | last_ms = millis(); 310 | } 311 | 312 | // Calculate pseudo time 313 | uint64_t * this_ccount = ((uint64_t*)&core->cyclel); 314 | uint32_t elapsedUs = 0; 315 | elapsedUs = *this_ccount / TIME_DIVISOR - lastTime; 316 | lastTime += elapsedUs; 317 | 318 | int ret = MiniRV32IMAStep( core, NULL, 0, elapsedUs, INSTRS_PER_FLIP ); // Execute upto INSTRS_PER_FLIP cycles before breaking out. 319 | switch( ret ) 320 | { 321 | case 0: break; 322 | case 1: _delay_ms(1); *this_ccount += INSTRS_PER_FLIP; break; 323 | //case 3: instct = 0; break; 324 | //case 0x7777: goto restart; //syscon code for restart 325 | case 0x5555: UART_pputs("POWEROFF\r\n"); while(1); //syscon code for power-off . halt 326 | default: UART_pputs("Unknown failure\r\n"); break; 327 | } 328 | } while (1); 329 | 330 | // Should not get here 331 | while(1); 332 | } 333 | 334 | // Exception handlers 335 | static uint32_t HandleException( uint32_t ir, uint32_t code ) 336 | { 337 | // Weird opcode emitted by duktape on exit. 338 | if( code == 3 ) 339 | { 340 | // Could handle other opcodes here. 341 | } 342 | return code; 343 | } 344 | 345 | static uint32_t HandleControlStore( uint32_t addy, uint32_t val ) 346 | { 347 | if( addy == 0x10000000 ) //UART 8250 / 16550 Data Buffer 348 | { 349 | UART_putc(val); 350 | } 351 | return 0; 352 | } 353 | 354 | 355 | static uint32_t HandleControlLoad( uint32_t addy ) 356 | { 357 | // Emulating a 8250 / 16550 UART 358 | if( addy == 0x10000005 ) 359 | return 0x60 | UART_available(); 360 | else if( addy == 0x10000000 && UART_available() ) 361 | return UART_getc(); 362 | return 0; 363 | } 364 | 365 | static void HandleOtherCSRWrite( uint8_t * image, uint16_t csrno, uint32_t value ) 366 | { 367 | if( csrno == 0x136 ) 368 | { 369 | UART_putdec32(value); 370 | } 371 | if( csrno == 0x137 ) 372 | { 373 | UART_puthex32(value); 374 | } 375 | else if( csrno == 0x138 ) 376 | { 377 | //Print "string" 378 | uint32_t ptrstart = value - MINIRV32_RAM_IMAGE_OFFSET; 379 | uint32_t ptrend = ptrstart; 380 | if( ptrstart >= RAM_SIZE ) { 381 | UART_pputs( "DEBUG PASSED INVALID PTR ("); 382 | UART_puthex32(value); 383 | UART_pputs(")\r\n"); 384 | } 385 | while( ptrend < RAM_SIZE ) 386 | { 387 | if( load1(ptrend) == 0 ) break; 388 | ptrend++; 389 | } 390 | if( ptrend != ptrstart ) { 391 | for (; ptrstart <= ptrend; ptrstart++) { 392 | UART_putc(load1(ptrstart)); 393 | } 394 | } 395 | } 396 | else if( csrno == 0x139 ) 397 | { 398 | UART_putc((uint8_t)value); 399 | } 400 | } 401 | 402 | static int32_t HandleOtherCSRRead( uint8_t * image, uint16_t csrno ) 403 | { 404 | if( csrno == 0x140 ) 405 | { 406 | if( !UART_available() ) return -1; 407 | return UART_getc(); 408 | } 409 | return 0; 410 | } 411 | 412 | // Cache helper functions 413 | 414 | /* 415 | * flag: 416 | * false -> data fetch, calculate sector 417 | * true -> instruction fetch, calculate sector 418 | * 419 | * return: cache buffer's address 420 | * 421 | * quick note: storing a 0 takes just a single instruction, but storing a 1 takes 2 on AVR 422 | */ 423 | 424 | uint8_t *read_buf(uint32_t ofs, bool flag, bool write) { 425 | // Calculate sector num 426 | // Dividing on AVR is a pain, so we should avoid that if we can 427 | uint32_t s = ofs / SD_BLOCK_LEN; 428 | 429 | // Check if the requested sector exists in the pool 430 | // We have 3 caches, so we can use if. If you implement more than 3 caches, you should 431 | // use for loop 432 | uint8_t ret = 0; 433 | if (s == pool[0].tag) { 434 | ret = 0; 435 | 436 | // Add age 437 | // Why there's no longer overflow check? 438 | // Because it's faster to (if any) overflow, invalidate, and reload cache than to check for overflow on every access. 439 | //if (pool[0].age <= 0xFFFE) { 440 | pool[0].age += 1; 441 | //} 442 | 443 | // Set dirty flag if needed 444 | if (write) { 445 | pool[0].flag = false; // false = dirty 446 | } 447 | } else if (s == pool[1].tag) { 448 | ret = 1; 449 | 450 | // Add age 451 | //if (pool[1].age <= 0xFFFE) { 452 | pool[1].age += 1; 453 | //} 454 | 455 | // Set dirty flag if needed 456 | if (write) { 457 | pool[1].flag = false; 458 | } 459 | } else if (s == pool[2].tag) { 460 | ret = 2; 461 | 462 | // Add age 463 | //if (pool[2].age <= 0xFFFE) { 464 | pool[2].age += 1; 465 | //} 466 | 467 | // Set dirty flag if needed 468 | // If you think this can be optimized to pool[2].flag = write, think again! 469 | if (write) { 470 | pool[2].flag = false; 471 | } 472 | } else { 473 | uint8_t lru = 2; 474 | 475 | if (flag) { 476 | // If icache miss -> invaild old icache 477 | if (pool[0].age == 0xFFFF) { 478 | lru = 0; 479 | pool[0].age = 0; 480 | goto continue_without_finding_lru; 481 | } else if (pool[1].age == 0xFFFF) { 482 | lru = 1; 483 | pool[1].age = 0; 484 | goto continue_without_finding_lru; 485 | } else { 486 | lru = 2; 487 | pool[2].age = 0; 488 | goto continue_without_finding_lru; 489 | } 490 | } 491 | 492 | // Cache miss -> invalid LRU and fetch new 493 | // Find LRU cache 494 | if (pool[lru].age > pool[1].age) { 495 | lru = 1; 496 | } 497 | 498 | if (pool[lru].age > pool[0].age) { 499 | lru = 0; 500 | } 501 | 502 | uint8_t token; 503 | continue_without_finding_lru: 504 | // Check if LRU cache if dirty 505 | if (!pool[lru].flag) { // false = dirty 506 | // Dirty -> flush to SD 507 | uint8_t t = 0; 508 | cache_write: 509 | SD_writeSingleBlock(pool[lru].tag, pool[lru].buf, &token); 510 | if (!(token == SD_DATA_ACCEPTED)) { 511 | if(!(token & 0xF0)) 512 | { 513 | if (++t == 10) { 514 | UART_pputs("cache_write @ "); 515 | UART_puthex32(pool[lru].tag); 516 | UART_pputs(" error, error token:\r\n"); 517 | SD_printDataErrToken(token); 518 | dump_state(); 519 | UART_pputs("cache_write: failed 10 times in a row. Halting\r\n"); 520 | while(1); // Halt 521 | } 522 | 523 | _delay_ms(100); 524 | goto cache_write; 525 | } 526 | } 527 | 528 | // Clear dirty flag 529 | pool[lru].flag = true; // true = not dirty 530 | } 531 | 532 | // Set new properties 533 | pool[lru].tag = s; 534 | if (flag) { 535 | // icache 536 | pool[lru].age = 0xFFFF; 537 | #ifdef ENABLE_CACHE_STAT 538 | icache_miss++; 539 | #endif 540 | } else { 541 | // dcache 542 | pool[lru].age = 1; // Already +1 for the access 543 | #ifdef ENABLE_CACHE_STAT 544 | dcache_miss++; 545 | #endif 546 | } 547 | 548 | // Set dirty flag if needed 549 | if (write) { 550 | pool[lru].flag = false; 551 | } 552 | 553 | // Fetch new sector into cache 554 | uint8_t t = 0; 555 | cache_read: 556 | SD_readSingleBlock(s, pool[lru].buf, &token); 557 | if (!(token == SD_START_TOKEN)) { 558 | if(!(token & 0xF0)) { 559 | if (++t == 10) { 560 | UART_pputs("cache_read @ "); 561 | UART_puthex32(s); 562 | UART_pputs(" error, error token:\r\n"); 563 | SD_printDataErrToken(token); 564 | dump_state(); 565 | UART_pputs("cache_read: failed 10 times in a row. Halting\r\n"); 566 | while(1); // Halt 567 | } 568 | 569 | _delay_ms(100); 570 | goto cache_read; 571 | } 572 | } 573 | 574 | // Return buffer address 575 | return &pool[lru].buf; 576 | } 577 | 578 | // Cache hit 579 | #ifdef ENABLE_CACHE_STAT 580 | if (flag) { 581 | icache_hit++; 582 | } else { 583 | dcache_hit++; 584 | } 585 | #endif 586 | 587 | // Return buffer address 588 | return &pool[ret].buf; 589 | } 590 | 591 | // Memory access functions 592 | static uint32_t loadi(uint32_t ofs) { 593 | // Load instruction from icache 594 | uint16_t r = ofs % 512; // Don't inline this. Clearly specify type for compiler to optimize. Else it will be seen as uint32_t 595 | //return *(uint32_t*)&pool[id].buf[(uint16_t)(ofs % 512)]; 596 | return *(uint32_t*)(read_buf(ofs, true, false) + r); 597 | } 598 | 599 | static uint32_t load4(uint32_t ofs) { 600 | uint16_t r = ofs % 512; // Don't inline this 601 | //return *(uint32_t*)&pool[id].buf[r]; 602 | return *(uint32_t*)(read_buf(ofs, false, false) + r); 603 | } 604 | static uint16_t load2(uint32_t ofs) { 605 | uint16_t r = ofs % 512; 606 | //return *(uint16_t *)&pool[id].buf[r]; 607 | return *(uint16_t*)(read_buf(ofs, false, false) + r); 608 | } 609 | static uint8_t load1(uint32_t ofs) { 610 | uint16_t r = ofs % 512; 611 | //return pool[id].buf[(uint16_t)(ofs % 512)]; 612 | return *(uint8_t*)(read_buf(ofs, false, false) + r); 613 | } 614 | 615 | static uint32_t store4(uint32_t ofs, uint32_t val) { 616 | uint16_t r = ofs % 512; 617 | 618 | // Store 619 | //*(uint32_t *)&pool[id].buf[r] = val; 620 | *(uint32_t *)(read_buf(ofs, false, true) + r) = val; 621 | 622 | // Return result 623 | return val; 624 | } 625 | 626 | static uint16_t store2(uint32_t ofs, uint16_t val) { 627 | uint16_t r = ofs % 512; 628 | 629 | // Store 630 | //*(uint16_t *)&pool[id].buf[r] = val; 631 | *(uint16_t *)(read_buf(ofs, false, true) + r) = val; 632 | 633 | // Return result 634 | return val; 635 | } 636 | 637 | static uint8_t store1(uint32_t ofs, uint8_t val) { 638 | uint16_t r = ofs % 512; 639 | 640 | // Store 641 | //*(uint8_t *)&pool[id].buf[r] = val; 642 | *(uint8_t *)(read_buf(ofs, false, true) + r) = val; 643 | 644 | // Return results 645 | return val; 646 | } 647 | 648 | void dump_state(void) { 649 | UART_pputs("==============================================================================\r\n"); 650 | UART_pputs("Dumping emulator state:\r\nRegisters x0 - x31:\r\n"); 651 | 652 | // Print registers 653 | for (uint8_t i = 0; i < 8; i++) { 654 | // Print 4 registers at once 655 | UART_puthex32(core->regs[i*4]); UART_pputs(" "); 656 | UART_puthex32(core->regs[i*4 + 1]); UART_pputs(" "); 657 | UART_puthex32(core->regs[i*4 + 2]); UART_pputs(" "); 658 | UART_puthex32(core->regs[i*4 + 3]); UART_pputs("\r\n"); 659 | } 660 | 661 | UART_pputs("pc: "); 662 | UART_puthex32(core->pc); 663 | UART_pputs("\r\nmstatus: "); 664 | UART_puthex32(core->mstatus); 665 | UART_pputs("\r\ncyclel: "); 666 | UART_puthex32(core->cyclel); 667 | UART_pputs("\r\ncycleh: "); 668 | UART_puthex32(core->cycleh); 669 | UART_pputs("\r\ntimerl: "); 670 | UART_puthex32(core->timerl); 671 | UART_pputs("\r\ntimerh: "); 672 | UART_puthex32(core->timerh); 673 | UART_pputs("\r\ntimermatchl: "); 674 | UART_puthex32(core->timermatchl); 675 | UART_pputs("\r\ntimermatchh: "); 676 | UART_puthex32(core->timermatchh); 677 | UART_pputs("\r\nmscratch: "); 678 | UART_puthex32(core->mscratch); 679 | UART_pputs("\r\nmtvec: "); 680 | UART_puthex32(core->mtvec); 681 | UART_pputs("\r\nmie: "); 682 | UART_puthex32(core->mie); 683 | UART_pputs("\r\nmip: "); 684 | UART_puthex32(core->mip); 685 | UART_pputs("\r\nmepc: "); 686 | UART_puthex32(core->mepc); 687 | UART_pputs("\r\nmtval: "); 688 | UART_puthex32(core->mtval); 689 | UART_pputs("\r\nmcause: "); 690 | UART_puthex32(core->mcause); 691 | UART_pputs("\r\nextraflags: "); 692 | UART_puthex32(core->extraflags); 693 | UART_pputs("\r\n==============================================================================\r\n"); 694 | } 695 | -------------------------------------------------------------------------------- /main_sim.c: -------------------------------------------------------------------------------- 1 | /* 2 | * arv32-opt: mini-rv32ima on Arduino UNO, but the code is written in pure C 3 | * Created by gvl610 4 | * mini-rv32ima is created by cnlohr. See https://github.com/cnlohr/mini-rv32ima 5 | * This is computer-simulated version of arv32-opt for debugging and testing new optimizations 6 | */ 7 | 8 | // Enable/disable cache hit/miss information 9 | #define ENABLE_CACHE_STAT 10 | 11 | // Headers 12 | #include 13 | #include 14 | #include "if_sim.h" 15 | #include "uart.h" 16 | #include "spi.h" 17 | #include "sdcard.h" 18 | #include "sdprint.h" 19 | #include "types.h" 20 | 21 | #define UART_pputs UART_puts 22 | 23 | // mini-rv32ima variables 24 | int fail_on_all_faults = 0; 25 | uint64_t lastTime = 0; 26 | struct MiniRV32IMAState *core; 27 | 28 | /* 29 | * Some notes about this stupid cache system 30 | * We are running on Arduino UNO, which uses atmega328p and only has 2KB of RAM 31 | * From what I measured, we could use up to 3x 512 bytes cache and there are 32 | * still enough RAN left for other stuff (local variables, etc) 33 | * So we will implement a system with 3 caches, each 512 bytes. 34 | * 35 | * Each cache has a 512 bytes buffer, a tag (the sector number of that 512-bytes 36 | * block in RAM), and a "dirty" flag (see below). In our implementation, we use 37 | * an uint16_t age score variables. For each data read, we will +1 to the age 38 | * score, and for each data write, we will also +1 to the age score. 39 | * 40 | * Cache will be invaild by LRU mechanism: when there is a read/write request to 41 | * an address that is not currently in the cache pool, a least recently used 42 | * cache will be invalid and a new 512-bytes block contains that address will be 43 | * fetch into the newly invaild cache. If that cache is marked as "dirty" (data 44 | * changed compared to RAM), it will be flushed to RAM first, then get invalid. 45 | * 46 | * When there is a write request to an address already in the cache pool, it 47 | * will follow lazy write mechanism. That means the change will be kept in the 48 | * cache, the cache will be marked as "dirty", and the changes won't be written 49 | * to main RAM until it gets invalid. 50 | * 51 | * When a cache is assigned as icache, it age score will be set to max (0xFFFF 52 | * for uint16_t). When the pc moves to an address that is not in the current 53 | * icache, it will get invalid, and its age score will be reset to 0. If pc 54 | * moves to an address that is currently in another dcache, that dcache will 55 | * be used for both icache and dcache, and its age score will be set to 0xFFFF. 56 | * 57 | * IMO, I don't think a cache will often be assigned as both icache and dcache 58 | * as the same time. It might just happend in self-modifying code. 59 | */ 60 | 61 | struct cache { 62 | uint8_t buf[SD_BLOCK_LEN]; 63 | uint32_t tag; 64 | uint16_t age; 65 | bool flag; // False is dirty. Why not the opposite? AVR takes 2 instructions to store a 1 and just 1 to store a 0. 66 | }; 67 | 68 | struct cache pool[3]; 69 | 70 | // Cache hit / miss stat 71 | #ifdef ENABLE_CACHE_STAT 72 | uint32_t icache_hit = 0; 73 | uint32_t icache_miss = 0; 74 | uint32_t dcache_hit = 0; 75 | uint32_t dcache_miss = 0; 76 | #endif 77 | 78 | // Functions prototype 79 | static uint32_t HandleException( uint32_t ir, uint32_t retval ); 80 | static uint32_t HandleControlStore( uint32_t addy, uint32_t val ); 81 | static uint32_t HandleControlLoad( uint32_t addy ); 82 | static void HandleOtherCSRWrite( uint8_t * image, uint16_t csrno, uint32_t value ); 83 | static int32_t HandleOtherCSRRead( uint8_t * image, uint16_t csrno ); 84 | 85 | // Load / store helper 86 | static uint32_t store4(uint32_t ofs, uint32_t val); 87 | static uint16_t store2(uint32_t ofs, uint16_t val); 88 | static uint8_t store1(uint32_t ofs, uint8_t val); 89 | 90 | static uint32_t load4(uint32_t ofs); 91 | static uint16_t load2(uint32_t ofs); 92 | static uint8_t load1(uint32_t ofs); 93 | 94 | static uint32_t loadi(uint32_t ofs); 95 | 96 | // Other 97 | uint32_t last_cyclel = 0; // Last cyclel value 98 | void dump_state(void); 99 | 100 | // Setup mini-rv32ima 101 | // This is the functionality we want to override in the emulator. 102 | // think of this as the way the emulator's processor is connected to the outside world. 103 | #define MINIRV32WARN( x... ) UART_pputs( x ); 104 | #define MINIRV32_DECORATE static 105 | #define MINI_RV32_RAM_SIZE RAM_SIZE 106 | #define MINIRV32_IMPLEMENTATION // Minimum rv32 emulator 107 | #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 ); } } 108 | #define MINIRV32_HANDLE_MEM_STORE_CONTROL( addy, val ) if( HandleControlStore( addy, val ) ) return val; 109 | #define MINIRV32_HANDLE_MEM_LOAD_CONTROL( addy, rval ) rval = HandleControlLoad( addy ); 110 | #define MINIRV32_OTHERCSR_WRITE( csrno, value ) HandleOtherCSRWrite( image, csrno, value ); 111 | #define MINIRV32_OTHERCSR_READ( csrno, value ) value = HandleOtherCSRRead( image, csrno ); 112 | #define MINIRV32_CUSTOM_MEMORY_BUS // Custom RAM handler for swapping to SD card 113 | 114 | // Macro for accessing RAM 115 | #define MINIRV32_STORE4( ofs, val ) store4(ofs, val) 116 | #define MINIRV32_STORE2( ofs, val ) store2(ofs, val) 117 | #define MINIRV32_STORE1( ofs, val ) store1(ofs, val) 118 | #define MINIRV32_LOAD4( ofs ) load4(ofs) 119 | #define MINIRV32_LOAD2_SIGNED( ofs ) (int8_t)load2(ofs) 120 | #define MINIRV32_LOAD2( ofs ) load2(ofs) 121 | #define MINIRV32_LOAD1_SIGNED( ofs ) (int8_t)load1(ofs) 122 | #define MINIRV32_LOAD1( ofs ) load1(ofs) 123 | #define MINIRV32_LOADI( ofs ) loadi(ofs) 124 | 125 | #include "mini-rv32ima.h" 126 | 127 | // Init cache helper 128 | void init_cache(uint8_t index, uint32_t tag, uint16_t age) { 129 | uint8_t token; 130 | uint8_t t = 0; 131 | 132 | // Read init sector 133 | read_init_begin: 134 | SD_readSingleBlock(tag, pool[index].buf, &token); // buf = first sector 135 | if(!(token & 0xF0)) 136 | { 137 | if (++t == 10) { 138 | UART_pputs("init_cache #"); 139 | UART_puthex32(index); 140 | UART_pputs(" error, error token:\r\n"); 141 | SD_printDataErrToken(token); 142 | UART_pputs("init_cache: failed 10 times in a row. Halting\r\n"); 143 | while(1); // Halt 144 | } 145 | 146 | _delay_ms(100); 147 | goto read_init_begin; 148 | } 149 | 150 | // Set other info 151 | pool[index].tag = tag; 152 | pool[index].age = age; 153 | pool[index].flag = 0; 154 | } 155 | 156 | #include 157 | 158 | clock_t last_ms; 159 | static void CtrlC() 160 | { 161 | // Calculate effective emulated speed 162 | clock_t current_ms = clock(); 163 | UART_pputs("Effective emulated speed: "); 164 | UART_putdec32(((core->cyclel - last_cyclel) * 1000) / (current_ms - last_ms) * 1000); 165 | UART_pputs(" Hz, dtime="); 166 | UART_putdec32((current_ms - last_ms) / 1000); 167 | UART_pputs("ms, dcycle="); 168 | UART_putdec32(core->cyclel - last_cyclel); 169 | UART_pputs("\r\n"); 170 | 171 | // Print icache/dcache hit/miss 172 | #ifdef ENABLE_CACHE_STAT 173 | UART_pputs("icache hit/miss: "); 174 | UART_putdec32(icache_hit); 175 | UART_pputs("/"); 176 | UART_putdec32(icache_miss); 177 | UART_pputs("; dcache hit/miss: "); 178 | UART_putdec32(dcache_hit); 179 | UART_pputs("/"); 180 | UART_putdec32(dcache_miss); 181 | UART_pputs("\r\n"); 182 | #endif 183 | // Dump state 184 | dump_state(); 185 | UART_pputs("Dump completed.\r\n"); 186 | exit( 0 ); 187 | } 188 | 189 | // Entry point 190 | int main(int argc, char** argv) { 191 | // Initialize UART 192 | UART_init(); 193 | signal(SIGINT, CtrlC); 194 | 195 | // Say something, so people know that UART works 196 | UART_pputs("arv32-opt: mini-rv32ima on Arduino UNO\r\n"); 197 | 198 | // Initialize SPI 199 | SPI_init(SPI_MASTER | SPI_FOSC_16 | SPI_MODE_0); 200 | 201 | // Initialize SD card 202 | if(SD_init() != SD_SUCCESS) 203 | { 204 | UART_pputs("Error initializaing SD card\r\n"); 205 | while(1); // Deadloop 206 | } 207 | 208 | UART_pputs("SD card initialized successfully!\r\n"); 209 | 210 | // Initialize emulator struct 211 | core = (struct MiniRV32IMAState *)malloc(sizeof(struct MiniRV32IMAState)); 212 | memset(core, 0, sizeof(struct MiniRV32IMAState)); 213 | 214 | // Setup core 215 | core->pc = MINIRV32_RAM_IMAGE_OFFSET; 216 | core->regs[10] = 0x00; //hart ID 217 | 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) 218 | core->extraflags |= 3; // Machine-mode. 219 | 220 | // Setup cache 221 | // Init cache0 as icache 222 | init_cache(0, 0, 0xFFFF); // buf = 0x0 (code begin at 0x0) 223 | 224 | // Init cache1 as dcache 225 | init_cache(1, 6552, 0); // buf = second accessed address (got by dumping address) 226 | 227 | // Init cache2 as dcache 228 | init_cache(2, 3011, 0); // buf = third accessed address (got by dumping address) 229 | 230 | // Patch the ram to skip memory cleaning 231 | // We should patch it in the icache, not in the memory. Otherwise PC will jump to 0x0 for some reason 232 | // Maybe the code is actually copied to somewhere else 233 | // 0x00E6D863: Original opcode at 0xAC 234 | // 0x00E6C863: Opcode to skip the memory-cleaning loop. Got by disassembly 235 | // This works by changing BGE (branch greater or equal) to BLT (branch less than) 236 | pool[0].buf[0xAD] = 0xC8; 237 | 238 | // Initial time 239 | last_ms = clock(); 240 | 241 | // Emulator loop 242 | // It's stated in the AVR documentation that writing do ... while() is faster than while() 243 | do { 244 | // Calculate pseudo time 245 | uint64_t * this_ccount = ((uint64_t*)&core->cyclel); 246 | uint32_t elapsedUs = 0; 247 | elapsedUs = *this_ccount / TIME_DIVISOR - lastTime; 248 | lastTime += elapsedUs; 249 | 250 | int ret = MiniRV32IMAStep( core, NULL, 0, elapsedUs, INSTRS_PER_FLIP ); // Execute upto INSTRS_PER_FLIP cycles before breaking out. 251 | switch( ret ) 252 | { 253 | case 0: break; 254 | case 1: _delay_ms(1); *this_ccount += INSTRS_PER_FLIP; break; 255 | //case 3: instct = 0; break; 256 | //case 0x7777: goto restart; //syscon code for restart 257 | case 0x5555: UART_pputs("POWEROFF\r\n"); while(1); //syscon code for power-off . halt 258 | default: UART_pputs("Unknown failure\r\n"); break; 259 | } 260 | } while (1); 261 | 262 | return 0; 263 | } 264 | 265 | // Exception handlers 266 | static uint32_t HandleException( uint32_t ir, uint32_t code ) 267 | { 268 | // Weird opcode emitted by duktape on exit. 269 | if( code == 3 ) 270 | { 271 | // Could handle other opcodes here. 272 | } 273 | return code; 274 | } 275 | 276 | static uint32_t HandleControlStore( uint32_t addy, uint32_t val ) 277 | { 278 | if( addy == 0x10000000 ) //UART 8250 / 16550 Data Buffer 279 | { 280 | UART_putc(val); 281 | } 282 | return 0; 283 | } 284 | 285 | 286 | static uint32_t HandleControlLoad( uint32_t addy ) 287 | { 288 | // Emulating a 8250 / 16550 UART 289 | if( addy == 0x10000005 ) 290 | return 0x60 | UART_available(); 291 | else if( addy == 0x10000000 && UART_available() ) 292 | return UART_getc(); 293 | return 0; 294 | } 295 | 296 | static void HandleOtherCSRWrite( uint8_t * image, uint16_t csrno, uint32_t value ) 297 | { 298 | if( csrno == 0x136 ) 299 | { 300 | UART_putdec32(value); 301 | } 302 | if( csrno == 0x137 ) 303 | { 304 | UART_puthex32(value); 305 | } 306 | else if( csrno == 0x138 ) 307 | { 308 | //Print "string" 309 | uint32_t ptrstart = value - MINIRV32_RAM_IMAGE_OFFSET; 310 | uint32_t ptrend = ptrstart; 311 | if( ptrstart >= RAM_SIZE ) { 312 | UART_pputs( "DEBUG PASSED INVALID PTR ("); 313 | UART_puthex32(value); 314 | UART_pputs(")\r\n"); 315 | } 316 | while( ptrend < RAM_SIZE ) 317 | { 318 | if( load1(ptrend) == 0 ) break; 319 | ptrend++; 320 | } 321 | if( ptrend != ptrstart ) { 322 | for (; ptrstart <= ptrend; ptrstart++) { 323 | UART_putc(load1(ptrstart)); 324 | } 325 | } 326 | } 327 | else if( csrno == 0x139 ) 328 | { 329 | UART_putc((uint8_t)value); 330 | } 331 | } 332 | 333 | static int32_t HandleOtherCSRRead( uint8_t * image, uint16_t csrno ) 334 | { 335 | if( csrno == 0x140 ) 336 | { 337 | if( !UART_available() ) return -1; 338 | return UART_getc(); 339 | } 340 | return 0; 341 | } 342 | 343 | // Cache helper functions 344 | 345 | /* 346 | * flag: 347 | * false -> data fetch, calculate sector 348 | * true -> instruction fetch, calculate sector 349 | * 350 | * return: cache buffer's address 351 | * 352 | * quick note: storing a 0 takes just a single instruction, but storing a 1 takes 2 on AVR 353 | */ 354 | 355 | uint8_t *read_buf(uint32_t ofs, bool flag, bool write) { 356 | // Calculate sector num 357 | // Dividing on AVR is a pain, so we should avoid that if we can 358 | uint32_t s = ofs / SD_BLOCK_LEN; 359 | 360 | // Check if the requested sector exists in the pool 361 | // We have 3 caches, so we can use if. If you implement more than 3 caches, you should 362 | // use for loop 363 | uint8_t ret = 0; 364 | if (s == pool[0].tag) { 365 | ret = 0; 366 | 367 | // Add age 368 | if (pool[0].age <= 0xFFFE) { 369 | pool[0].age += 1; 370 | } 371 | 372 | // Set dirty flag if needed 373 | if (write) { 374 | pool[0].flag = false; // false = dirty 375 | } 376 | } else if (s == pool[1].tag) { 377 | ret = 1; 378 | 379 | // Add age 380 | if (pool[1].age <= 0xFFFE) { 381 | pool[1].age += 1; 382 | } 383 | 384 | // Set dirty flag if needed 385 | if (write) { 386 | pool[1].flag = false; 387 | } 388 | } else if (s == pool[2].tag) { 389 | ret = 2; 390 | 391 | // Add age 392 | if (pool[2].age <= 0xFFFE) { 393 | pool[2].age += 1; 394 | } 395 | 396 | // Set dirty flag if needed 397 | // If you think this can be optimized to pool[2].flag = write, think again! 398 | if (write) { 399 | pool[2].flag = false; 400 | } 401 | } else { 402 | uint8_t lru = 2; 403 | 404 | if (flag) { 405 | // If icache miss -> invaild old icache 406 | if (pool[0].age == 0xFFFF) { 407 | lru = 0; 408 | pool[0].age = 0; 409 | goto continue_without_finding_lru; 410 | } else if (pool[1].age == 0xFFFF) { 411 | lru = 1; 412 | pool[1].age = 0; 413 | goto continue_without_finding_lru; 414 | } else { 415 | lru = 2; 416 | pool[2].age = 0; 417 | goto continue_without_finding_lru; 418 | } 419 | } 420 | 421 | // Cache miss -> invalid LRU and fetch new 422 | // Find LRU cache 423 | if (pool[lru].age > pool[1].age) { 424 | lru = 1; 425 | } 426 | 427 | if (pool[lru].age > pool[0].age) { 428 | lru = 0; 429 | } 430 | 431 | continue_without_finding_lru: 432 | // Check if LRU cache if dirty 433 | uint8_t token; 434 | if (!pool[lru].flag) { // false = dirty 435 | // Dirty -> flush to SD 436 | uint8_t t = 0; 437 | cache_write: 438 | SD_writeSingleBlock(pool[lru].tag, pool[lru].buf, &token); 439 | if (!(token == SD_DATA_ACCEPTED)) { 440 | if(!(token & 0xF0)) 441 | { 442 | if (++t == 10) { 443 | UART_pputs("cache_write @ "); 444 | UART_puthex32(pool[lru].tag); 445 | UART_pputs(" error, error token:\r\n"); 446 | SD_printDataErrToken(token); 447 | dump_state(); 448 | UART_pputs("cache_write: failed 10 times in a row. Halting\r\n"); 449 | while(1); // Halt 450 | } 451 | 452 | _delay_ms(100); 453 | goto cache_write; 454 | } 455 | } 456 | 457 | // Clear dirty flag 458 | pool[lru].flag = true; // true = not dirty 459 | } 460 | 461 | // Set new properties 462 | pool[lru].tag = s; 463 | if (flag) { 464 | // icache 465 | pool[lru].age = 0xFFFF; 466 | #ifdef ENABLE_CACHE_STAT 467 | icache_miss++; 468 | #endif 469 | } else { 470 | // dcache 471 | pool[lru].age = 1; // Already +1 for the access 472 | #ifdef ENABLE_CACHE_STAT 473 | dcache_miss++; 474 | #endif 475 | } 476 | 477 | // Set dirty flag if needed 478 | if (write) { 479 | pool[lru].flag = false; 480 | } 481 | 482 | // Fetch new sector into cache 483 | uint8_t t = 0; 484 | cache_read: 485 | SD_readSingleBlock(s, pool[lru].buf, &token); 486 | if (!(token == SD_START_TOKEN)) { 487 | if(!(token & 0xF0)) { 488 | if (++t == 10) { 489 | UART_pputs("cache_read @ "); 490 | UART_puthex32(s); 491 | UART_pputs(" error, error token:\r\n"); 492 | SD_printDataErrToken(token); 493 | dump_state(); 494 | UART_pputs("cache_read: failed 10 times in a row. Halting\r\n"); 495 | while(1); // Halt 496 | } 497 | 498 | _delay_ms(100); 499 | goto cache_read; 500 | } 501 | } 502 | 503 | // Return buffer address 504 | return &pool[lru].buf; 505 | } 506 | 507 | // Cache hit 508 | #ifdef ENABLE_CACHE_STAT 509 | if (flag) { 510 | icache_hit++; 511 | } else { 512 | dcache_hit++; 513 | } 514 | #endif 515 | 516 | // Return buffer address 517 | return &pool[ret].buf; 518 | } 519 | 520 | // Memory access functions 521 | static uint32_t loadi(uint32_t ofs) { 522 | // Load instruction from icache 523 | uint16_t r = ofs % 512; // Don't inline this. Clearly specify type for compiler to optimize. Else it will be seen as uint32_t 524 | //return *(uint32_t*)&pool[id].buf[(uint16_t)(ofs % 512)]; 525 | return *(uint32_t*)(read_buf(ofs, true, false) + r); 526 | } 527 | 528 | static uint32_t load4(uint32_t ofs) { 529 | uint16_t r = ofs % 512; // Don't inline this 530 | //return *(uint32_t*)&pool[id].buf[r]; 531 | return *(uint32_t*)(read_buf(ofs, false, false) + r); 532 | } 533 | static uint16_t load2(uint32_t ofs) { 534 | uint16_t r = ofs % 512; 535 | //return *(uint16_t *)&pool[id].buf[r]; 536 | return *(uint16_t*)(read_buf(ofs, false, false) + r); 537 | } 538 | static uint8_t load1(uint32_t ofs) { 539 | uint16_t r = ofs % 512; 540 | //return pool[id].buf[(uint16_t)(ofs % 512)]; 541 | return *(uint8_t*)(read_buf(ofs, false, false) + r); 542 | } 543 | 544 | static uint32_t store4(uint32_t ofs, uint32_t val) { 545 | uint16_t r = ofs % 512; 546 | 547 | // Store 548 | //*(uint32_t *)&pool[id].buf[r] = val; 549 | *(uint32_t *)(read_buf(ofs, false, true) + r) = val; 550 | 551 | // Return result 552 | return val; 553 | } 554 | 555 | static uint16_t store2(uint32_t ofs, uint16_t val) { 556 | uint16_t r = ofs % 512; 557 | 558 | // Store 559 | //*(uint16_t *)&pool[id].buf[r] = val; 560 | *(uint16_t *)(read_buf(ofs, false, true) + r) = val; 561 | 562 | // Return result 563 | return val; 564 | } 565 | 566 | static uint8_t store1(uint32_t ofs, uint8_t val) { 567 | uint16_t r = ofs % 512; 568 | 569 | // Store 570 | //*(uint8_t *)&pool[id].buf[r] = val; 571 | *(uint8_t *)(read_buf(ofs, false, true) + r) = val; 572 | 573 | // Return results 574 | return val; 575 | } 576 | 577 | void dump_state(void) { 578 | UART_pputs("==============================================================================\r\n"); 579 | UART_pputs("Dumping emulator state:\r\nRegisters x0 - x31:\r\n"); 580 | 581 | // Print registers 582 | for (uint8_t i = 0; i < 8; i++) { 583 | // Print 4 registers at once 584 | UART_puthex32(core->regs[i*4]); UART_pputs(" "); 585 | UART_puthex32(core->regs[i*4 + 1]); UART_pputs(" "); 586 | UART_puthex32(core->regs[i*4 + 2]); UART_pputs(" "); 587 | UART_puthex32(core->regs[i*4 + 3]); UART_pputs("\r\n"); 588 | } 589 | 590 | UART_pputs("pc: "); 591 | UART_puthex32(core->pc); 592 | UART_pputs("\r\nmstatus: "); 593 | UART_puthex32(core->mstatus); 594 | UART_pputs("\r\ncyclel: "); 595 | UART_puthex32(core->cyclel); 596 | UART_pputs("\r\ncycleh: "); 597 | UART_puthex32(core->cycleh); 598 | UART_pputs("\r\ntimerl: "); 599 | UART_puthex32(core->timerl); 600 | UART_pputs("\r\ntimerh: "); 601 | UART_puthex32(core->timerh); 602 | UART_pputs("\r\ntimermatchl: "); 603 | UART_puthex32(core->timermatchl); 604 | UART_pputs("\r\ntimermatchh: "); 605 | UART_puthex32(core->timermatchh); 606 | UART_pputs("\r\nmscratch: "); 607 | UART_puthex32(core->mscratch); 608 | UART_pputs("\r\nmtvec: "); 609 | UART_puthex32(core->mtvec); 610 | UART_pputs("\r\nmie: "); 611 | UART_puthex32(core->mie); 612 | UART_pputs("\r\nmip: "); 613 | UART_puthex32(core->mip); 614 | UART_pputs("\r\nmepc: "); 615 | UART_puthex32(core->mepc); 616 | UART_pputs("\r\nmtval: "); 617 | UART_puthex32(core->mtval); 618 | UART_pputs("\r\nmcause: "); 619 | UART_puthex32(core->mcause); 620 | UART_pputs("\r\nextraflags: "); 621 | UART_puthex32(core->extraflags); 622 | UART_pputs("\r\n==============================================================================\r\n"); 623 | } 624 | -------------------------------------------------------------------------------- /mini-rv32ima.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Charles Lohr, you may use this file or any portions herein under any of the BSD, MIT, or CC0 licenses. 2 | 3 | #ifndef _MINI_RV32IMAH_H 4 | #define _MINI_RV32IMAH_H 5 | 6 | /** 7 | To use mini-rv32ima.h for the bare minimum, the following: 8 | 9 | #define MINI_RV32_RAM_SIZE ram_amt 10 | #define MINIRV32_IMPLEMENTATION 11 | 12 | #include "mini-rv32ima.h" 13 | 14 | Though, that's not _that_ interesting. You probably want I/O! 15 | 16 | 17 | Notes: 18 | * There is a dedicated CLNT at 0x10000000. 19 | * There is free MMIO from there to 0x12000000. 20 | * You can put things like a UART, or whatever there. 21 | * Feel free to override any of the functionality with macros. 22 | */ 23 | 24 | #ifndef MINIRV32WARN 25 | #define MINIRV32WARN( x... ); 26 | #endif 27 | 28 | #ifndef MINIRV32_DECORATE 29 | #define MINIRV32_DECORATE static 30 | #endif 31 | 32 | #ifndef MINIRV32_RAM_IMAGE_OFFSET 33 | #define MINIRV32_RAM_IMAGE_OFFSET 0x80000000UL 34 | #endif 35 | 36 | #ifndef MINIRV32_POSTEXEC 37 | #define MINIRV32_POSTEXEC(...); 38 | #endif 39 | 40 | #ifndef MINIRV32_HANDLE_MEM_STORE_CONTROL 41 | #define MINIRV32_HANDLE_MEM_STORE_CONTROL(...); 42 | #endif 43 | 44 | #ifndef MINIRV32_HANDLE_MEM_LOAD_CONTROL 45 | #define MINIRV32_HANDLE_MEM_LOAD_CONTROL(...); 46 | #endif 47 | 48 | #ifndef MINIRV32_OTHERCSR_WRITE 49 | #define MINIRV32_OTHERCSR_WRITE(...); 50 | #endif 51 | 52 | #ifndef MINIRV32_OTHERCSR_READ 53 | #define MINIRV32_OTHERCSR_READ(...); 54 | #endif 55 | 56 | #ifndef MINIRV32_CUSTOM_MEMORY_BUS 57 | #define MINIRV32_STORE4( ofs, val ) *(uint32_t*)(image + ofs) = val 58 | #define MINIRV32_STORE2( ofs, val ) *(uint16_t*)(image + ofs) = val 59 | #define MINIRV32_STORE1( ofs, val ) *(uint8_t*)(image + ofs) = val 60 | #define MINIRV32_LOAD4( ofs ) *(uint32_t*)(image + ofs) 61 | #define MINIRV32_LOAD2( ofs ) *(uint16_t*)(image + ofs) 62 | #define MINIRV32_LOAD1( ofs ) *(uint8_t*)(image + ofs) 63 | #define MINIRV32_LOAD2_SIGNED( ofs ) *(int16_t*)(image + ofs) 64 | #define MINIRV32_LOAD1_SIGNED( ofs ) *(int8_t*)(image + ofs) 65 | #endif 66 | 67 | // As a note: We quouple-ify these, because in HLSL, we will be operating with 68 | // uint4's. We are going to uint4 data to/from system RAM. 69 | // 70 | // We're going to try to keep the full processor state to 12 x uint4. 71 | struct MiniRV32IMAState 72 | { 73 | uint32_t regs[32]; 74 | 75 | uint32_t pc; 76 | uint32_t mstatus; 77 | uint32_t cyclel; 78 | uint32_t cycleh; 79 | 80 | uint32_t timerl; 81 | uint32_t timerh; 82 | uint32_t timermatchl; 83 | uint32_t timermatchh; 84 | 85 | uint32_t mscratch; 86 | uint32_t mtvec; 87 | uint32_t mie; 88 | uint32_t mip; 89 | 90 | uint32_t mepc; 91 | uint32_t mtval; 92 | uint32_t mcause; 93 | 94 | // Note: only a few bits are used. (Machine = 3, User = 0) 95 | // Bits 0..1 = privilege. 96 | // Bit 2 = WFI (Wait for interrupt) 97 | // Bit 3+ = Load/Store reservation LSBs. 98 | uint32_t extraflags; 99 | }; 100 | 101 | #ifndef MINIRV32_STEPPROTO 102 | MINIRV32_DECORATE int32_t MiniRV32IMAStep( struct MiniRV32IMAState * state, uint8_t * image, uint32_t vProcAddress, uint32_t elapsedUs, int count ); 103 | #endif 104 | 105 | #ifdef MINIRV32_IMPLEMENTATION 106 | 107 | #ifndef MINIRV32_CUSTOM_INTERNALS 108 | #define CSR( x ) state->x 109 | #define SETCSR( x, val ) { state->x = val; } 110 | #define REG( x ) state->regs[x] 111 | #define REGSET( x, val ) { state->regs[x] = val; } 112 | #endif 113 | 114 | #ifndef MINIRV32_STEPPROTO 115 | MINIRV32_DECORATE int32_t MiniRV32IMAStep( struct MiniRV32IMAState * state, uint8_t * image, uint32_t vProcAddress, uint32_t elapsedUs, int count ) 116 | #else 117 | MINIRV32_STEPPROTO 118 | #endif 119 | { 120 | uint32_t new_timer = CSR( timerl ) + elapsedUs; 121 | if( new_timer < CSR( timerl ) ) CSR( timerh )++; 122 | CSR( timerl ) = new_timer; 123 | 124 | // Handle Timer interrupt. 125 | if( ( CSR( timerh ) > CSR( timermatchh ) || ( CSR( timerh ) == CSR( timermatchh ) && CSR( timerl ) > CSR( timermatchl ) ) ) && ( CSR( timermatchh ) || CSR( timermatchl ) ) ) 126 | { 127 | CSR( extraflags ) &= ~4; // Clear WFI 128 | CSR( mip ) |= 1<<7; //MTIP of MIP // https://stackoverflow.com/a/61916199/2926815 Fire interrupt. 129 | } 130 | else 131 | CSR( mip ) &= ~(1<<7); 132 | 133 | // If WFI, don't run processor. 134 | if( CSR( extraflags ) & 4 ) 135 | return 1; 136 | 137 | uint32_t trap = 0; 138 | uint32_t rval = 0; 139 | uint32_t pc = CSR( pc ); 140 | uint32_t cycle = CSR( cyclel ); 141 | 142 | if( ( CSR( mip ) & (1<<7) ) && ( CSR( mie ) & (1<<7) /*mtie*/ ) && ( CSR( mstatus ) & 0x8 /*mie*/) ) 143 | { 144 | // Timer interrupt. 145 | trap = 0x80000007; 146 | pc -= 4; 147 | } 148 | else // No timer interrupt? Execute a bunch of instructions. 149 | for( int icount = 0; icount < count; icount++ ) 150 | { 151 | uint32_t ir = 0; 152 | rval = 0; 153 | cycle++; 154 | uint32_t ofs_pc = pc - MINIRV32_RAM_IMAGE_OFFSET; 155 | 156 | if( ofs_pc >= MINI_RV32_RAM_SIZE ) 157 | { 158 | trap = 1 + 1; // Handle access violation on instruction read. 159 | break; 160 | } 161 | else if( ofs_pc & 3 ) 162 | { 163 | trap = 1 + 0; //Handle PC-misaligned access 164 | break; 165 | } 166 | else 167 | { 168 | ir = MINIRV32_LOADI( ofs_pc ); 169 | uint32_t rdid = (ir >> 7) & 0x1f; 170 | 171 | switch( ir & 0x7f ) 172 | { 173 | case 0x37: // LUI (0b0110111) 174 | rval = ( ir & 0xfffff000 ); 175 | break; 176 | case 0x17: // AUIPC (0b0010111) 177 | rval = pc + ( ir & 0xfffff000 ); 178 | break; 179 | case 0x6F: // JAL (0b1101111) 180 | { 181 | int32_t reladdy = ((ir & 0x80000000)>>11) | ((ir & 0x7fe00000)>>20) | ((ir & 0x00100000)>>9) | ((ir&0x000ff000)); 182 | if( reladdy & 0x00100000 ) reladdy |= 0xffe00000; // Sign extension. 183 | rval = pc + 4; 184 | pc = pc + reladdy - 4; 185 | break; 186 | } 187 | case 0x67: // JALR (0b1100111) 188 | { 189 | uint32_t imm = ir >> 20; 190 | int32_t imm_se = imm | (( imm & 0x800 )?0xfffff000:0); 191 | rval = pc + 4; 192 | pc = ( (REG( (ir >> 15) & 0x1f ) + imm_se) & ~1) - 4; 193 | break; 194 | } 195 | case 0x63: // Branch (0b1100011) 196 | { 197 | uint32_t immm4 = ((ir & 0xf00)>>7) | ((ir & 0x7e000000)>>20) | ((ir & 0x80) << 4) | ((ir >> 31)<<12); 198 | if( immm4 & 0x1000 ) immm4 |= 0xffffe000; 199 | int32_t rs1 = REG((ir >> 15) & 0x1f); 200 | int32_t rs2 = REG((ir >> 20) & 0x1f); 201 | immm4 = pc + immm4 - 4; 202 | rdid = 0; 203 | switch( ( ir >> 12 ) & 0x7 ) 204 | { 205 | // BEQ, BNE, BLT, BGE, BLTU, BGEU 206 | case 0: if( rs1 == rs2 ) pc = immm4; break; 207 | case 1: if( rs1 != rs2 ) pc = immm4; break; 208 | case 4: if( rs1 < rs2 ) pc = immm4; break; 209 | case 5: if( rs1 >= rs2 ) pc = immm4; break; //BGE 210 | case 6: if( (uint32_t)rs1 < (uint32_t)rs2 ) pc = immm4; break; //BLTU 211 | case 7: if( (uint32_t)rs1 >= (uint32_t)rs2 ) pc = immm4; break; //BGEU 212 | default: trap = (2+1); 213 | } 214 | break; 215 | } 216 | case 0x03: // Load (0b0000011) 217 | { 218 | uint32_t rs1 = REG((ir >> 15) & 0x1f); 219 | uint32_t imm = ir >> 20; 220 | int32_t imm_se = imm | (( imm & 0x800 )?0xfffff000:0); 221 | uint32_t rsval = rs1 + imm_se; 222 | 223 | rsval -= MINIRV32_RAM_IMAGE_OFFSET; 224 | if( rsval >= MINI_RV32_RAM_SIZE-3 ) 225 | { 226 | rsval += MINIRV32_RAM_IMAGE_OFFSET; 227 | if( rsval >= 0x10000000 && rsval < 0x12000000 ) // UART, CLNT 228 | { 229 | if( rsval == 0x1100bffc ) // https://chromitem-soc.readthedocs.io/en/latest/clint.html 230 | rval = CSR( timerh ); 231 | else if( rsval == 0x1100bff8 ) 232 | rval = CSR( timerl ); 233 | else 234 | MINIRV32_HANDLE_MEM_LOAD_CONTROL( rsval, rval ); 235 | } 236 | else 237 | { 238 | trap = (5+1); 239 | rval = rsval; 240 | } 241 | } 242 | else 243 | { 244 | switch( ( ir >> 12 ) & 0x7 ) 245 | { 246 | //LB, LH, LW, LBU, LHU 247 | case 0: rval = MINIRV32_LOAD1_SIGNED( rsval ); break; 248 | case 1: rval = MINIRV32_LOAD2_SIGNED( rsval ); break; 249 | case 2: rval = MINIRV32_LOAD4( rsval ); break; 250 | case 4: rval = MINIRV32_LOAD1( rsval ); break; 251 | case 5: rval = MINIRV32_LOAD2( rsval ); break; 252 | default: trap = (2+1); 253 | } 254 | } 255 | break; 256 | } 257 | case 0x23: // Store 0b0100011 258 | { 259 | uint32_t rs1 = REG((ir >> 15) & 0x1f); 260 | uint32_t rs2 = REG((ir >> 20) & 0x1f); 261 | uint32_t addy = ( ( ir >> 7 ) & 0x1f ) | ( ( ir & 0xfe000000 ) >> 20 ); 262 | if( addy & 0x800 ) addy |= 0xfffff000; 263 | addy += rs1 - MINIRV32_RAM_IMAGE_OFFSET; 264 | rdid = 0; 265 | 266 | if( addy >= MINI_RV32_RAM_SIZE-3 ) 267 | { 268 | addy += MINIRV32_RAM_IMAGE_OFFSET; 269 | if( addy >= 0x10000000 && addy < 0x12000000 ) 270 | { 271 | // Should be stuff like SYSCON, 8250, CLNT 272 | if( addy == 0x11004004 ) //CLNT 273 | CSR( timermatchh ) = rs2; 274 | else if( addy == 0x11004000 ) //CLNT 275 | CSR( timermatchl ) = rs2; 276 | else if( addy == 0x11100000 ) //SYSCON (reboot, poweroff, etc.) 277 | { 278 | SETCSR( pc, pc + 4 ); 279 | return rs2; // NOTE: PC will be PC of Syscon. 280 | } 281 | else 282 | MINIRV32_HANDLE_MEM_STORE_CONTROL( addy, rs2 ); 283 | } 284 | else 285 | { 286 | trap = (7+1); // Store access fault. 287 | rval = addy; 288 | } 289 | } 290 | else 291 | { 292 | switch( ( ir >> 12 ) & 0x7 ) 293 | { 294 | //SB, SH, SW 295 | case 0: MINIRV32_STORE1( addy, rs2 ); break; 296 | case 1: MINIRV32_STORE2( addy, rs2 ); break; 297 | case 2: MINIRV32_STORE4( addy, rs2 ); break; 298 | default: trap = (2+1); 299 | } 300 | } 301 | break; 302 | } 303 | case 0x13: // Op-immediate 0b0010011 304 | case 0x33: // Op 0b0110011 305 | { 306 | uint32_t imm = ir >> 20; 307 | imm = imm | (( imm & 0x800 )?0xfffff000:0); 308 | uint32_t rs1 = REG((ir >> 15) & 0x1f); 309 | uint32_t is_reg = !!( ir & 0x20 ); 310 | uint32_t rs2 = is_reg ? REG(imm & 0x1f) : imm; 311 | 312 | if( is_reg && ( ir & 0x02000000 ) ) 313 | { 314 | switch( (ir>>12)&7 ) //0x02000000 = RV32M 315 | { 316 | case 0: rval = rs1 * rs2; break; // MUL 317 | #ifndef CUSTOM_MULH // If compiling on a system that doesn't natively, or via libgcc support 64-bit math. 318 | case 1: rval = ((int64_t)((int32_t)rs1) * (int64_t)((int32_t)rs2)) >> 32; break; // MULH 319 | case 2: rval = ((int64_t)((int32_t)rs1) * (uint64_t)rs2) >> 32; break; // MULHSU 320 | case 3: rval = ((uint64_t)rs1 * (uint64_t)rs2) >> 32; break; // MULHU 321 | #else 322 | CUSTOM_MULH 323 | #endif 324 | 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 325 | case 5: if( rs2 == 0 ) rval = 0xffffffff; else rval = rs1 / rs2; break; // DIVU 326 | 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 327 | case 7: if( rs2 == 0 ) rval = rs1; else rval = rs1 % rs2; break; // REMU 328 | } 329 | } 330 | else 331 | { 332 | switch( (ir>>12)&7 ) // These could be either op-immediate or op commands. Be careful. 333 | { 334 | case 0: rval = (is_reg && (ir & 0x40000000) ) ? ( rs1 - rs2 ) : ( rs1 + rs2 ); break; 335 | case 1: rval = rs1 << (rs2 & 0x1F); break; 336 | case 2: rval = (int32_t)rs1 < (int32_t)rs2; break; 337 | case 3: rval = rs1 < rs2; break; 338 | case 4: rval = rs1 ^ rs2; break; 339 | case 5: rval = (ir & 0x40000000 ) ? ( ((int32_t)rs1) >> (rs2 & 0x1F) ) : ( rs1 >> (rs2 & 0x1F) ); break; 340 | case 6: rval = rs1 | rs2; break; 341 | case 7: rval = rs1 & rs2; break; 342 | } 343 | } 344 | break; 345 | } 346 | case 0x0f: // 0b0001111 347 | rdid = 0; // fencetype = (ir >> 12) & 0b111; We ignore fences in this impl. 348 | break; 349 | case 0x73: // Zifencei+Zicsr (0b1110011) 350 | { 351 | uint32_t csrno = ir >> 20; 352 | uint32_t microop = ( ir >> 12 ) & 0x7; 353 | if( (microop & 3) ) // It's a Zicsr function. 354 | { 355 | int rs1imm = (ir >> 15) & 0x1f; 356 | uint32_t rs1 = REG(rs1imm); 357 | uint32_t writeval = rs1; 358 | 359 | // https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf 360 | // Generally, support for Zicsr 361 | switch( csrno ) 362 | { 363 | case 0x340: rval = CSR( mscratch ); break; 364 | case 0x305: rval = CSR( mtvec ); break; 365 | case 0x304: rval = CSR( mie ); break; 366 | case 0xC00: rval = cycle; break; 367 | case 0x344: rval = CSR( mip ); break; 368 | case 0x341: rval = CSR( mepc ); break; 369 | case 0x300: rval = CSR( mstatus ); break; //mstatus 370 | case 0x342: rval = CSR( mcause ); break; 371 | case 0x343: rval = CSR( mtval ); break; 372 | case 0xf11: rval = 0xff0ff0ff; break; //mvendorid 373 | case 0x301: rval = 0x40401101; break; //misa (XLEN=32, IMA+X) 374 | //case 0x3B0: rval = 0; break; //pmpaddr0 375 | //case 0x3a0: rval = 0; break; //pmpcfg0 376 | //case 0xf12: rval = 0x00000000; break; //marchid 377 | //case 0xf13: rval = 0x00000000; break; //mimpid 378 | //case 0xf14: rval = 0x00000000; break; //mhartid 379 | default: 380 | MINIRV32_OTHERCSR_READ( csrno, rval ); 381 | break; 382 | } 383 | 384 | switch( microop ) 385 | { 386 | case 1: writeval = rs1; break; //CSRRW 387 | case 2: writeval = rval | rs1; break; //CSRRS 388 | case 3: writeval = rval & ~rs1; break; //CSRRC 389 | case 5: writeval = rs1imm; break; //CSRRWI 390 | case 6: writeval = rval | rs1imm; break; //CSRRSI 391 | case 7: writeval = rval & ~rs1imm; break; //CSRRCI 392 | } 393 | 394 | switch( csrno ) 395 | { 396 | case 0x340: SETCSR( mscratch, writeval ); break; 397 | case 0x305: SETCSR( mtvec, writeval ); break; 398 | case 0x304: SETCSR( mie, writeval ); break; 399 | case 0x344: SETCSR( mip, writeval ); break; 400 | case 0x341: SETCSR( mepc, writeval ); break; 401 | case 0x300: SETCSR( mstatus, writeval ); break; //mstatus 402 | case 0x342: SETCSR( mcause, writeval ); break; 403 | case 0x343: SETCSR( mtval, writeval ); break; 404 | //case 0x3a0: break; //pmpcfg0 405 | //case 0x3B0: break; //pmpaddr0 406 | //case 0xf11: break; //mvendorid 407 | //case 0xf12: break; //marchid 408 | //case 0xf13: break; //mimpid 409 | //case 0xf14: break; //mhartid 410 | //case 0x301: break; //misa 411 | default: 412 | MINIRV32_OTHERCSR_WRITE( csrno, writeval ); 413 | break; 414 | } 415 | } 416 | else if( microop == 0x0 ) // "SYSTEM" 0b000 417 | { 418 | rdid = 0; 419 | if( csrno == 0x105 ) //WFI (Wait for interrupts) 420 | { 421 | CSR( mstatus ) |= 8; //Enable interrupts 422 | CSR( extraflags ) |= 4; //Infor environment we want to go to sleep. 423 | SETCSR( pc, pc + 4 ); 424 | return 1; 425 | } 426 | else if( ( ( csrno & 0xff ) == 0x02 ) ) // MRET 427 | { 428 | //https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf 429 | //Table 7.6. MRET then in mstatus/mstatush sets MPV=0, MPP=0, MIE=MPIE, and MPIE=1. La 430 | // Should also update mstatus to reflect correct mode. 431 | uint32_t startmstatus = CSR( mstatus ); 432 | uint32_t startextraflags = CSR( extraflags ); 433 | SETCSR( mstatus , (( startmstatus & 0x80) >> 4) | ((startextraflags&3) << 11) | 0x80 ); 434 | SETCSR( extraflags, (startextraflags & ~3) | ((startmstatus >> 11) & 3) ); 435 | pc = CSR( mepc ) -4; 436 | } 437 | else 438 | { 439 | switch( csrno ) 440 | { 441 | case 0: trap = ( CSR( extraflags ) & 3) ? (11+1) : (8+1); break; // ECALL; 8 = "Environment call from U-mode"; 11 = "Environment call from M-mode" 442 | case 1: trap = (3+1); break; // EBREAK 3 = "Breakpoint" 443 | default: trap = (2+1); break; // Illegal opcode. 444 | } 445 | } 446 | } 447 | else 448 | trap = (2+1); // Note micrrop 0b100 == undefined. 449 | break; 450 | } 451 | case 0x2f: // RV32A (0b00101111) 452 | { 453 | uint32_t rs1 = REG((ir >> 15) & 0x1f); 454 | uint32_t rs2 = REG((ir >> 20) & 0x1f); 455 | uint32_t irmid = ( ir>>27 ) & 0x1f; 456 | 457 | rs1 -= MINIRV32_RAM_IMAGE_OFFSET; 458 | 459 | // We don't implement load/store from UART or CLNT with RV32A here. 460 | 461 | if( rs1 >= MINI_RV32_RAM_SIZE-3 ) 462 | { 463 | trap = (7+1); //Store/AMO access fault 464 | rval = rs1 + MINIRV32_RAM_IMAGE_OFFSET; 465 | } 466 | else 467 | { 468 | rval = MINIRV32_LOAD4( rs1 ); 469 | 470 | // Referenced a little bit of https://github.com/franzflasch/riscv_em/blob/master/src/core/core.c 471 | uint32_t dowrite = 1; 472 | switch( irmid ) 473 | { 474 | case 2: //LR.W (0b00010) 475 | dowrite = 0; 476 | CSR( extraflags ) = (CSR( extraflags ) & 0x07) | (rs1<<3); 477 | break; 478 | case 3: //SC.W (0b00011) (Make sure we have a slot, and, it's valid) 479 | rval = ( CSR( extraflags ) >> 3 != ( rs1 & 0x1fffffff ) ); // Validate that our reservation slot is OK. 480 | dowrite = !rval; // Only write if slot is valid. 481 | break; 482 | case 1: break; //AMOSWAP.W (0b00001) 483 | case 0: rs2 += rval; break; //AMOADD.W (0b00000) 484 | case 4: rs2 ^= rval; break; //AMOXOR.W (0b00100) 485 | case 12: rs2 &= rval; break; //AMOAND.W (0b01100) 486 | case 8: rs2 |= rval; break; //AMOOR.W (0b01000) 487 | case 16: rs2 = ((int32_t)rs2<(int32_t)rval)?rs2:rval; break; //AMOMIN.W (0b10000) 488 | case 20: rs2 = ((int32_t)rs2>(int32_t)rval)?rs2:rval; break; //AMOMAX.W (0b10100) 489 | case 24: rs2 = (rs2rval)?rs2:rval; break; //AMOMAXU.W (0b11100) 491 | default: trap = (2+1); dowrite = 0; break; //Not supported. 492 | } 493 | if( dowrite ) MINIRV32_STORE4( rs1, rs2 ); 494 | } 495 | break; 496 | } 497 | default: trap = (2+1); // Fault: Invalid opcode. 498 | } 499 | 500 | // If there was a trap, do NOT allow register writeback. 501 | if( trap ) 502 | break; 503 | 504 | if( rdid ) 505 | { 506 | REGSET( rdid, rval ); // Write back register. 507 | } 508 | } 509 | 510 | MINIRV32_POSTEXEC( pc, ir, trap ); 511 | 512 | pc += 4; 513 | } 514 | 515 | // Handle traps and interrupts. 516 | if( trap ) 517 | { 518 | if( trap & 0x80000000 ) // If prefixed with 1 in MSB, it's an interrupt, not a trap. 519 | { 520 | SETCSR( mcause, trap ); 521 | SETCSR( mtval, 0 ); 522 | pc += 4; // PC needs to point to where the PC will return to. 523 | } 524 | else 525 | { 526 | SETCSR( mcause, trap - 1 ); 527 | SETCSR( mtval, (trap > 5 && trap <= 8)? rval : pc ); 528 | } 529 | SETCSR( mepc, pc ); //TRICKY: The kernel advances mepc automatically. 530 | //CSR( mstatus ) & 8 = MIE, & 0x80 = MPIE 531 | // On an interrupt, the system moves current MIE into MPIE 532 | SETCSR( mstatus, (( CSR( mstatus ) & 0x08) << 4) | (( CSR( extraflags ) & 3 ) << 11) ); 533 | pc = (CSR( mtvec ) - 4); 534 | 535 | // If trapping, always enter machine mode. 536 | CSR( extraflags ) |= 3; 537 | 538 | trap = 0; 539 | pc += 4; 540 | } 541 | 542 | if( CSR( cyclel ) > cycle ) CSR( cycleh )++; 543 | SETCSR( cyclel, cycle ); 544 | SETCSR( pc, pc ); 545 | return 0; 546 | } 547 | 548 | #endif 549 | 550 | #endif 551 | 552 | -------------------------------------------------------------------------------- /rv32.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspiduino/arv32-opt/c9215986b1f0653a53fe1b07113bbe542b7ddca3/rv32.bin -------------------------------------------------------------------------------- /sdcard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "sdcard.h" 5 | #include "sdprint.h" 6 | #include "spi.h" 7 | #include "uart.h" 8 | 9 | /******************************************************************************* 10 | Initialize SD card 11 | *******************************************************************************/ 12 | uint8_t SD_init() 13 | { 14 | uint8_t res[5], cmdAttempts = 0; 15 | 16 | SD_powerUpSeq(); 17 | 18 | while((res[0] = SD_goIdleState()) != SD_IN_IDLE_STATE) 19 | { 20 | cmdAttempts++; 21 | if(cmdAttempts == CMD0_MAX_ATTEMPTS) 22 | { 23 | return SD_ERROR; 24 | } 25 | } 26 | 27 | _delay_ms(1); 28 | 29 | SD_sendIfCond(res); 30 | if(res[0] != SD_IN_IDLE_STATE) 31 | { 32 | return SD_ERROR; 33 | } 34 | 35 | if(res[4] != 0xAA) 36 | { 37 | return SD_ERROR; 38 | } 39 | 40 | cmdAttempts = 0; 41 | do 42 | { 43 | if(cmdAttempts == CMD55_MAX_ATTEMPTS) 44 | { 45 | return SD_ERROR; 46 | } 47 | 48 | res[0] = SD_sendApp(); 49 | if(SD_R1_NO_ERROR(res[0])) 50 | { 51 | res[0] = SD_sendOpCond(); 52 | } 53 | 54 | _delay_ms(1); 55 | 56 | cmdAttempts++; 57 | } 58 | while(res[0] != SD_READY); 59 | 60 | _delay_ms(1); 61 | 62 | SD_readOCR(res); 63 | 64 | SPI_init(SPI_MASTER | SPI_FOSC_4 | SPI_MODE_0); 65 | 66 | return SD_SUCCESS; 67 | } 68 | 69 | /******************************************************************************* 70 | Run power up sequence 71 | *******************************************************************************/ 72 | void SD_powerUpSeq() 73 | { 74 | // make sure card is deselected 75 | CS_DISABLE(); 76 | 77 | // give SD card time to power up 78 | _delay_ms(10); 79 | 80 | // select SD card 81 | SPI_transfer(0xFF); 82 | CS_DISABLE(); 83 | 84 | // send 80 clock cycles to synchronize 85 | for(uint8_t i = 0; i < SD_INIT_CYCLES; i++) 86 | SPI_transfer(0xFF); 87 | } 88 | 89 | /******************************************************************************* 90 | Send command to SD card 91 | *******************************************************************************/ 92 | void SD_command(uint8_t cmd, uint32_t arg, uint8_t crc) 93 | { 94 | // transmit command to sd card 95 | SPI_transfer(cmd|0x40); 96 | 97 | // transmit argument 98 | SPI_transfer((uint8_t)(arg >> 24)); 99 | SPI_transfer((uint8_t)(arg >> 16)); 100 | SPI_transfer((uint8_t)(arg >> 8)); 101 | SPI_transfer((uint8_t)(arg)); 102 | 103 | // transmit crc 104 | SPI_transfer(crc|0x01); 105 | } 106 | 107 | /******************************************************************************* 108 | Read R1 from SD card 109 | *******************************************************************************/ 110 | uint8_t SD_readRes1() 111 | { 112 | uint8_t i = 0, res1; 113 | 114 | // keep polling until actual data received 115 | while((res1 = SPI_transfer(0xFF)) == 0xFF) 116 | { 117 | i++; 118 | 119 | // if no data received for 8 bytes, break 120 | if(i > 8) break; 121 | } 122 | 123 | return res1; 124 | } 125 | 126 | /******************************************************************************* 127 | Read R2 from SD card 128 | *******************************************************************************/ 129 | void SD_readRes2(uint8_t *res) 130 | { 131 | // read response 1 in R2 132 | res[0] = SD_readRes1(); 133 | 134 | // read final byte of response 135 | res[1] = SPI_transfer(0xFF); 136 | } 137 | 138 | /******************************************************************************* 139 | Read R3 from SD card 140 | *******************************************************************************/ 141 | void SD_readRes3(uint8_t *res) 142 | { 143 | // read response 1 in R3 144 | res[0] = SD_readRes1(); 145 | 146 | // if error reading R1, return 147 | if(res[0] > 1) return; 148 | 149 | // read remaining bytes 150 | SD_readBytes(res + 1, R3_BYTES); 151 | } 152 | 153 | /******************************************************************************* 154 | Read R7 from SD card 155 | *******************************************************************************/ 156 | void SD_readRes7(uint8_t *res) 157 | { 158 | // read response 1 in R7 159 | res[0] = SD_readRes1(); 160 | 161 | // if error reading R1, return 162 | if(res[0] > 1) return; 163 | 164 | // read remaining bytes 165 | SD_readBytes(res + 1, R7_BYTES); 166 | } 167 | 168 | /******************************************************************************* 169 | Read specified number of bytes from SD card 170 | *******************************************************************************/ 171 | void SD_readBytes(uint8_t *res, uint8_t n) 172 | { 173 | while(n--) *res++ = SPI_transfer(0xFF); 174 | } 175 | 176 | /******************************************************************************* 177 | Command Idle State (CMD0) 178 | *******************************************************************************/ 179 | uint8_t SD_goIdleState() 180 | { 181 | // assert chip select 182 | SPI_transfer(0xFF); 183 | CS_ENABLE(); 184 | SPI_transfer(0xFF); 185 | 186 | // send CMD0 187 | SD_command(CMD0, CMD0_ARG, CMD0_CRC); 188 | 189 | // read response 190 | uint8_t res1 = SD_readRes1(); 191 | 192 | // deassert chip select 193 | SPI_transfer(0xFF); 194 | CS_DISABLE(); 195 | SPI_transfer(0xFF); 196 | 197 | return res1; 198 | } 199 | 200 | /******************************************************************************* 201 | Send Interface Conditions (CMD8) 202 | *******************************************************************************/ 203 | void SD_sendIfCond(uint8_t *res) 204 | { 205 | // assert chip select 206 | SPI_transfer(0xFF); 207 | CS_ENABLE(); 208 | SPI_transfer(0xFF); 209 | 210 | // send CMD8 211 | SD_command(CMD8, CMD8_ARG, CMD8_CRC); 212 | 213 | // read response 214 | SD_readRes7(res); 215 | //SD_readBytes(res + 1, R7_BYTES); 216 | 217 | // deassert chip select 218 | SPI_transfer(0xFF); 219 | CS_DISABLE(); 220 | SPI_transfer(0xFF); 221 | } 222 | 223 | /******************************************************************************* 224 | Read Status 225 | *******************************************************************************/ 226 | void SD_sendStatus(uint8_t *res) 227 | { 228 | // assert chip select 229 | SPI_transfer(0xFF); 230 | CS_ENABLE(); 231 | SPI_transfer(0xFF); 232 | 233 | // send CMD13 234 | SD_command(CMD13, CMD13_ARG, CMD13_CRC); 235 | 236 | // read response 237 | SD_readRes2(res); 238 | 239 | // deassert chip select 240 | SPI_transfer(0xFF); 241 | CS_DISABLE(); 242 | SPI_transfer(0xFF); 243 | } 244 | 245 | /******************************************************************************* 246 | Read single 512 byte block 247 | token = 0xFE - Successful read 248 | token = 0x0X - Data error 249 | token = 0xFF - timeout 250 | *******************************************************************************/ 251 | uint8_t SD_readSingleBlock(uint32_t addr, uint8_t *buf, uint8_t *token) 252 | { 253 | uint8_t res1, read; 254 | uint16_t readAttempts; 255 | 256 | // set token to none 257 | *token = 0xFF; 258 | 259 | // assert chip select 260 | SPI_transfer(0xFF); 261 | CS_ENABLE(); 262 | SPI_transfer(0xFF); 263 | 264 | // send CMD17 265 | SD_command(CMD17, addr, CMD17_CRC); 266 | 267 | // read R1 268 | res1 = SD_readRes1(); 269 | 270 | // if response received from card 271 | if(res1 != 0xFF) 272 | { 273 | // wait for a response token (timeout = 100ms) 274 | readAttempts = 0; 275 | while(++readAttempts != SD_MAX_READ_ATTEMPTS) 276 | if((read = SPI_transfer(0xFF)) != 0xFF) break; 277 | 278 | // if response token is 0xFE 279 | if(read == SD_START_TOKEN) 280 | { 281 | // read 512 byte block 282 | for(uint16_t i = 0; i < SD_BLOCK_LEN; i++) *buf++ = SPI_transfer(0xFF); 283 | 284 | // read 16-bit CRC 285 | SPI_transfer(0xFF); 286 | SPI_transfer(0xFF); 287 | } 288 | 289 | // set token to card response 290 | *token = read; 291 | } 292 | 293 | // deassert chip select 294 | SPI_transfer(0xFF); 295 | CS_DISABLE(); 296 | SPI_transfer(0xFF); 297 | 298 | return res1; 299 | } 300 | 301 | #define SD_MAX_WRITE_ATTEMPTS 3907 302 | 303 | /******************************************************************************* 304 | Write single 512 byte block 305 | token = 0x00 - busy timeout 306 | token = 0x05 - data accepted 307 | token = 0xFF - response timeout 308 | *******************************************************************************/ 309 | uint8_t SD_writeSingleBlock(uint32_t addr, uint8_t *buf, uint8_t *token) 310 | { 311 | uint16_t readAttempts; 312 | uint8_t res1, read; 313 | 314 | // set token to none 315 | *token = 0xFF; 316 | 317 | // assert chip select 318 | SPI_transfer(0xFF); 319 | CS_ENABLE(); 320 | SPI_transfer(0xFF); 321 | 322 | // send CMD24 323 | SD_command(CMD24, addr, CMD24_CRC); 324 | 325 | // read response 326 | res1 = SD_readRes1(); 327 | 328 | // if no error 329 | if(res1 == SD_READY) 330 | { 331 | // send start token 332 | SPI_transfer(SD_START_TOKEN); 333 | 334 | // write buffer to card 335 | for(uint16_t i = 0; i < SD_BLOCK_LEN; i++) SPI_transfer(buf[i]); 336 | 337 | // wait for a response (timeout = 250ms) 338 | readAttempts = 0; 339 | while(++readAttempts != SD_MAX_WRITE_ATTEMPTS) 340 | if((read = SPI_transfer(0xFF)) != 0xFF) { *token = 0xFF; break; } 341 | 342 | // if data accepted 343 | if((read & 0x1F) == 0x05) 344 | { 345 | // set token to data accepted 346 | *token = 0x05; 347 | 348 | // wait for write to finish (timeout = 250ms) 349 | readAttempts = 0; 350 | while(SPI_transfer(0xFF) == 0x00) 351 | if(++readAttempts == SD_MAX_WRITE_ATTEMPTS) { *token = 0x00; break; } 352 | } 353 | } 354 | 355 | // deassert chip select 356 | SPI_transfer(0xFF); 357 | CS_DISABLE(); 358 | SPI_transfer(0xFF); 359 | 360 | return res1; 361 | } 362 | 363 | /******************************************************************************* 364 | Reads OCR from SD Card 365 | *******************************************************************************/ 366 | void SD_readOCR(uint8_t *res) 367 | { 368 | // assert chip select 369 | SPI_transfer(0xFF); 370 | CS_ENABLE(); 371 | uint8_t tmp = SPI_transfer(0xFF); 372 | 373 | if(tmp != 0xFF) while(SPI_transfer(0xFF) != 0xFF) ; 374 | 375 | // send CMD58 376 | SD_command(CMD58, CMD58_ARG, CMD58_CRC); 377 | 378 | // read response 379 | SD_readRes3(res); 380 | 381 | // deassert chip select 382 | SPI_transfer(0xFF); 383 | CS_DISABLE(); 384 | SPI_transfer(0xFF); 385 | } 386 | 387 | /******************************************************************************* 388 | Send application command (CMD55) 389 | *******************************************************************************/ 390 | uint8_t SD_sendApp() 391 | { 392 | // assert chip select 393 | SPI_transfer(0xFF); 394 | CS_ENABLE(); 395 | SPI_transfer(0xFF); 396 | 397 | // send CMD0 398 | SD_command(CMD55, CMD55_ARG, CMD55_CRC); 399 | 400 | // read response 401 | uint8_t res1 = SD_readRes1(); 402 | 403 | // deassert chip select 404 | SPI_transfer(0xFF); 405 | CS_DISABLE(); 406 | SPI_transfer(0xFF); 407 | 408 | return res1; 409 | } 410 | 411 | /******************************************************************************* 412 | Send operating condition (ACMD41) 413 | *******************************************************************************/ 414 | uint8_t SD_sendOpCond() 415 | { 416 | // assert chip select 417 | SPI_transfer(0xFF); 418 | CS_ENABLE(); 419 | SPI_transfer(0xFF); 420 | 421 | // send CMD0 422 | SD_command(ACMD41, ACMD41_ARG, ACMD41_CRC); 423 | 424 | // read response 425 | uint8_t res1 = SD_readRes1(); 426 | 427 | // deassert chip select 428 | SPI_transfer(0xFF); 429 | CS_DISABLE(); 430 | SPI_transfer(0xFF); 431 | 432 | return res1; 433 | } 434 | -------------------------------------------------------------------------------- /sdcard.h: -------------------------------------------------------------------------------- 1 | #ifndef _sd_card_h 2 | #define _sd_card_h 3 | 4 | // command definitions 5 | #define CMD0 0 6 | #define CMD0_ARG 0x00000000 7 | #define CMD0_CRC 0x94 8 | #define CMD8 8 9 | #define CMD8_ARG 0x0000001AA 10 | #define CMD8_CRC 0x86 11 | #define CMD9 9 12 | #define CMD9_ARG 0x00000000 13 | #define CMD9_CRC 0x00 14 | #define CMD10 9 15 | #define CMD10_ARG 0x00000000 16 | #define CMD10_CRC 0x00 17 | #define CMD13 13 18 | #define CMD13_ARG 0x00000000 19 | #define CMD13_CRC 0x00 20 | #define CMD17 17 21 | #define CMD17_CRC 0x00 22 | #define CMD24 24 23 | #define CMD24_CRC 0x00 24 | #define CMD55 55 25 | #define CMD55_ARG 0x00000000 26 | #define CMD55_CRC 0x00 27 | #define CMD58 58 28 | #define CMD58_ARG 0x00000000 29 | #define CMD58_CRC 0x00 30 | #define ACMD41 41 31 | #define ACMD41_ARG 0x40000000 32 | #define ACMD41_CRC 0x00 33 | 34 | #define SD_IN_IDLE_STATE 0x01 35 | #define SD_READY 0x00 36 | #define SD_R1_NO_ERROR(X) X < 0x02 37 | 38 | #define R3_BYTES 4 39 | #define R7_BYTES 4 40 | 41 | #define CMD0_MAX_ATTEMPTS 255 42 | #define CMD55_MAX_ATTEMPTS 255 43 | #define SD_ERROR 1 44 | #define SD_SUCCESS 0 45 | #define SD_MAX_READ_ATTEMPTS 3907 46 | #define SD_READ_START_TOKEN 0xFE 47 | #define SD_INIT_CYCLES 80 48 | 49 | #define SD_START_TOKEN 0xFE 50 | #define SD_ERROR_TOKEN 0x00 51 | 52 | #define SD_DATA_ACCEPTED 0x05 53 | #define SD_DATA_REJECTED_CRC 0x0B 54 | #define SD_DATA_REJECTED_WRITE 0x0D 55 | 56 | #define SD_BLOCK_LEN 512 57 | 58 | // SD functions 59 | uint8_t SD_init(void); 60 | void SD_powerUpSeq(void); 61 | void SD_command(uint8_t cmd, uint32_t arg, uint8_t crc); 62 | uint8_t SD_readRes1(void); 63 | void SD_readRes2(uint8_t *res); 64 | void SD_readRes3(uint8_t *res); 65 | void SD_readRes7(uint8_t *res); 66 | void SD_readBytes(uint8_t *res, uint8_t n); 67 | uint8_t SD_goIdleState(void); 68 | void SD_sendIfCond(uint8_t *res); 69 | void SD_sendStatus(uint8_t *res); 70 | void SD_readOCR(uint8_t *res); 71 | uint8_t SD_sendApp(void); 72 | uint8_t SD_sendOpCond(void); 73 | uint8_t SD_readSingleBlock(uint32_t addr, uint8_t *buf, uint8_t *error); 74 | uint8_t SD_writeSingleBlock(uint32_t addr, uint8_t *buf, uint8_t *res); 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /sdprint.c: -------------------------------------------------------------------------------- 1 | #include "sdprint.h" 2 | #include "sdcard.h" 3 | #include "uart.h" 4 | 5 | void SD_printR1(uint8_t res) 6 | { 7 | if(res == 0xFF) 8 | { UART_pputs("\tNo response\r\n"); return; } 9 | if(res & 0x80) 10 | { UART_pputs("\tError: MSB = 1\r\n"); return; } 11 | if(res == 0) 12 | { UART_pputs("\tCard Ready\r\n"); return; } 13 | if(PARAM_ERROR(res)) 14 | UART_pputs("\tParameter Error\r\n"); 15 | if(ADDR_ERROR(res)) 16 | UART_pputs("\tAddress Error\r\n"); 17 | if(ERASE_SEQ_ERROR(res)) 18 | UART_pputs("\tErase Sequence Error\r\n"); 19 | if(CRC_ERROR(res)) 20 | UART_pputs("\tCRC Error\r\n"); 21 | if(ILLEGAL_CMD(res)) 22 | UART_pputs("\tIllegal Command\r\n"); 23 | if(ERASE_RESET(res)) 24 | UART_pputs("\tErase Reset Error\r\n"); 25 | if(IN_IDLE(res)) 26 | UART_pputs("\tIn Idle State\r\n"); 27 | } 28 | 29 | void SD_printR2(uint8_t *res) 30 | { 31 | SD_printR1(res[0]); 32 | 33 | if(res[0] == 0xFF) return; 34 | 35 | if(res[1] == 0x00) 36 | UART_pputs("\tNo R2 Error\r\n"); 37 | if(OUT_OF_RANGE(res[1])) 38 | UART_pputs("\tOut of Range\r\n"); 39 | if(ERASE_PARAM(res[1])) 40 | UART_pputs("\tErase Parameter\r\n"); 41 | if(WP_VIOLATION(res[1])) 42 | UART_pputs("\tWP Violation\r\n"); 43 | if(CARD_ECC_FAILED(res[1])) 44 | UART_pputs("\tECC Failed\r\n"); 45 | if(CC_ERROR(res[1])) 46 | UART_pputs("\tCC Error\r\n"); 47 | if(ERROR(res[1])) 48 | UART_pputs("\tError\r\n"); 49 | if(WP_ERASE_SKIP(res[1])) 50 | UART_pputs("\tWP Erase Skip\r\n"); 51 | if(CARD_LOCKED(res[1])) 52 | UART_pputs("\tCard Locked\r\n"); 53 | } 54 | 55 | void SD_printR3(uint8_t *res) 56 | { 57 | SD_printR1(res[0]); 58 | 59 | if(res[0] > 1) return; 60 | 61 | UART_pputs("\tCard Power Up Status: "); 62 | if(POWER_UP_STATUS(res[1])) 63 | { 64 | UART_pputs("READY\r\n"); 65 | UART_pputs("\tCCS Status: "); 66 | if(CCS_VAL(res[1])){ UART_pputs("1\r\n"); } 67 | else UART_pputs("0\r\n"); 68 | } 69 | else 70 | { 71 | UART_pputs("BUSY\r\n"); 72 | } 73 | 74 | UART_pputs("\tVDD Window: "); 75 | if(VDD_2728(res[3])) UART_pputs("2.7-2.8, "); 76 | if(VDD_2829(res[2])) UART_pputs("2.8-2.9, "); 77 | if(VDD_2930(res[2])) UART_pputs("2.9-3.0, "); 78 | if(VDD_3031(res[2])) UART_pputs("3.0-3.1, "); 79 | if(VDD_3132(res[2])) UART_pputs("3.1-3.2, "); 80 | if(VDD_3233(res[2])) UART_pputs("3.2-3.3, "); 81 | if(VDD_3334(res[2])) UART_pputs("3.3-3.4, "); 82 | if(VDD_3435(res[2])) UART_pputs("3.4-3.5, "); 83 | if(VDD_3536(res[2])) UART_pputs("3.5-3.6"); 84 | UART_pputs("\r\n"); 85 | } 86 | 87 | void SD_printR7(uint8_t *res) 88 | { 89 | SD_printR1(res[0]); 90 | 91 | if(res[0] > 1) return; 92 | 93 | UART_pputs("\tCommand Version: "); 94 | UART_puthex8(CMD_VER(res[1])); 95 | UART_pputs("\r\n"); 96 | 97 | UART_pputs("\tVoltage Accepted: "); 98 | if(VOL_ACC(res[3]) == VOLTAGE_ACC_27_33) 99 | UART_pputs("2.7-3.6V\r\n"); 100 | else if(VOL_ACC(res[3]) == VOLTAGE_ACC_LOW) 101 | UART_pputs("LOW VOLTAGE\r\n"); 102 | else if(VOL_ACC(res[3]) == VOLTAGE_ACC_RES1) 103 | UART_pputs("RESERVED\r\n"); 104 | else if(VOL_ACC(res[3]) == VOLTAGE_ACC_RES2) 105 | UART_pputs("RESERVED\r\n"); 106 | else 107 | UART_pputs("NOT DEFINED\r\n"); 108 | 109 | UART_pputs("\tEcho: "); 110 | UART_puthex8(res[4]); 111 | UART_pputs("\r\n"); 112 | } 113 | 114 | void SD_printCSD(uint8_t *buf) 115 | { 116 | UART_pputs("CSD:\r\n"); 117 | 118 | UART_pputs("\tCSD Structure: "); 119 | UART_puthex8((buf[0] & 0b11000000) >> 6); 120 | UART_pputs("\r\n"); 121 | 122 | UART_pputs("\tTAAC: "); 123 | UART_puthex8(buf[1]); 124 | UART_pputs("\r\n"); 125 | 126 | UART_pputs("\tNSAC: "); 127 | UART_puthex8(buf[2]); 128 | UART_pputs("\r\n"); 129 | 130 | UART_pputs("\tTRAN_SPEED: "); 131 | UART_puthex8(buf[3]); 132 | UART_pputs("\r\n"); 133 | 134 | UART_pputs("\tDevice Size: "); 135 | UART_puthex8(buf[7] & 0b00111111); 136 | UART_puthex8(buf[8]); 137 | UART_puthex8(buf[9]); 138 | UART_pputs("\r\n"); 139 | } 140 | 141 | void SD_printBuf(uint8_t *buf) 142 | { 143 | uint8_t colCount = 0; 144 | for(uint16_t i = 0; i < SD_BLOCK_LEN; i++) 145 | { 146 | UART_puthex8(*buf++); 147 | if(colCount == 19) 148 | { 149 | UART_pputs("\r\n"); 150 | colCount = 0; 151 | } 152 | else 153 | { 154 | UART_putc(' '); 155 | colCount++; 156 | } 157 | } 158 | UART_pputs("\r\n"); 159 | } 160 | 161 | void SD_printDataErrToken(uint8_t token) 162 | { 163 | if(token & 0xF0) 164 | UART_pputs("\tNot Error token\r\n"); 165 | if(SD_TOKEN_OOR(token)) 166 | UART_pputs("\tData out of range\r\n"); 167 | if(SD_TOKEN_CECC(token)) 168 | UART_pputs("\tCard ECC failed\r\n"); 169 | if(SD_TOKEN_CC(token)) 170 | UART_pputs("\tCC Error\r\n"); 171 | if(SD_TOKEN_ERROR(token)) 172 | UART_pputs("\tError\r\n"); 173 | } 174 | -------------------------------------------------------------------------------- /sdprint.h: -------------------------------------------------------------------------------- 1 | #ifndef __SD_PRINT_H__ 2 | #define __SD_PRINT_H__ 3 | 4 | #ifdef __AVR__ 5 | #include 6 | #endif 7 | 8 | /* R1 MACROS */ 9 | #define PARAM_ERROR(X) X & 0b01000000 10 | #define ADDR_ERROR(X) X & 0b00100000 11 | #define ERASE_SEQ_ERROR(X) X & 0b00010000 12 | #define CRC_ERROR(X) X & 0b00001000 13 | #define ILLEGAL_CMD(X) X & 0b00000100 14 | #define ERASE_RESET(X) X & 0b00000010 15 | #define IN_IDLE(X) X & 0b00000001 16 | 17 | /* R2 MACROS */ 18 | #define OUT_OF_RANGE(X) X & 0b10000000 19 | #define ERASE_PARAM(X) X & 0b01000000 20 | #define WP_VIOLATION(X) X & 0b00100000 21 | #define CARD_ECC_FAILED(X) X & 0b00010000 22 | #define CC_ERROR(X) X & 0b00001000 23 | #define ERROR(X) X & 0b00000100 24 | #define WP_ERASE_SKIP(X) X & 0b00000010 25 | #define CARD_LOCKED(X) X & 0b00000001 26 | 27 | /* R3 MACROS */ 28 | #define POWER_UP_STATUS(X) X & 0x40 29 | #define CCS_VAL(X) X & 0x40 30 | #define VDD_2728(X) X & 0b10000000 31 | #define VDD_2829(X) X & 0b00000001 32 | #define VDD_2930(X) X & 0b00000010 33 | #define VDD_3031(X) X & 0b00000100 34 | #define VDD_3132(X) X & 0b00001000 35 | #define VDD_3233(X) X & 0b00010000 36 | #define VDD_3334(X) X & 0b00100000 37 | #define VDD_3435(X) X & 0b01000000 38 | #define VDD_3536(X) X & 0b10000000 39 | 40 | /* R7 MACROS */ 41 | #define CMD_VER(X) ((X >> 4) & 0xF0) 42 | #define VOL_ACC(X) (X & 0x1F) 43 | #define VOLTAGE_ACC_27_33 0b00000001 44 | #define VOLTAGE_ACC_LOW 0b00000010 45 | #define VOLTAGE_ACC_RES1 0b00000100 46 | #define VOLTAGE_ACC_RES2 0b00001000 47 | 48 | /* DATA ERROR TOKEN */ 49 | #define SD_TOKEN_OOR(X) X & 0b00001000 50 | #define SD_TOKEN_CECC(X) X & 0b00000100 51 | #define SD_TOKEN_CC(X) X & 0b00000010 52 | #define SD_TOKEN_ERROR(X) X & 0b00000001 53 | 54 | void SD_printR1(uint8_t res); 55 | void SD_printR2(uint8_t *res); 56 | void SD_printR3(uint8_t *res); 57 | void SD_printR7(uint8_t *res); 58 | void SD_printBuf(uint8_t *buf); 59 | void SD_printDataErrToken(uint8_t token); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /spi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "spi.h" 3 | 4 | /******************************************************************************* 5 | * spi.c v1 - 11/02/2018 6 | Initial definitions 7 | * spi.c v2 - 11/07/2018 8 | Updated initialization functions 9 | *******************************************************************************/ 10 | 11 | void SPI_init(uint16_t initParams) 12 | { 13 | // set CS, MOSI and SCK to output 14 | DDR_SPI |= (1 << CS) | (1 << MOSI) | (1 << SCK); 15 | 16 | // enable pull up resistor in MISO 17 | DDR_SPI |= (1 << MISO); 18 | 19 | // set SPI params 20 | SPCR = ((uint8_t) (initParams >> 8)) | (1 << SPE); 21 | SPSR = ((uint8_t) initParams); 22 | } 23 | 24 | uint8_t SPI_transfer(uint8_t data) 25 | { 26 | // load data into register 27 | SPDR = data; 28 | 29 | // Wait for transmission complete 30 | while(!(SPSR & (1 << SPIF))); 31 | 32 | // return SPDR 33 | return SPDR; 34 | } 35 | -------------------------------------------------------------------------------- /spi.h: -------------------------------------------------------------------------------- 1 | #ifndef __SPI_H__ 2 | #define __SPI_H__ 3 | 4 | /******************************************************************************* 5 | * spi.h v1 - 11/02/2018 6 | Initial definitions 7 | * spi.h v2 - 11/07/2018 8 | Updated initialization function 9 | *******************************************************************************/ 10 | 11 | #ifdef __AVR__ 12 | // pin definitions 13 | #define DDR_SPI DDRB 14 | #define PORT_SPI PORTB 15 | #define CS PINB2 16 | #define MOSI PINB3 17 | #define MISO PINB4 18 | #define SCK PINB5 19 | 20 | // macros 21 | #define CS_ENABLE() PORT_SPI &= ~(1 << CS) 22 | #define CS_DISABLE() PORT_SPI |= (1 << CS) 23 | #endif 24 | 25 | // initialization definitions 26 | #define SPI_MASTER 0b0001000000000000 27 | #define SPI_SLAVE 0b0000000000000000 28 | #define SPI_FOSC_4 0b0000000000000000 29 | #define SPI_FOSC_8 0b0000010100000000 30 | #define SPI_FOSC_16 0b0000000100000000 31 | #define SPI_FOSC_64 0b0000001000000000 32 | #define SPI_FOSC_128 0b0000001100000000 33 | #define SPI_2X_FOSC_2 0b0000000000000001 34 | #define SPI_2X_FOSC_8 0b0000000100000001 35 | #define SPI_2X_FOSC_32 0b0000001000000001 36 | #define SPI_2X_FOSC_64 0b0000001100000001 37 | #define SPI_INTERRUPTS 0b1000000000000000 38 | #define SPI_MODE_0 0b0000000000000000 39 | #define SPI_MODE_1 0b0000010000000000 40 | #define SPI_MODE_2 0b0000100000000000 41 | #define SPI_MODE_3 0b0000110000000000 42 | #define SPI_DEFAULT SPI_MASTER | SPI_FOSC_128 | SPI_MODE_0 43 | 44 | // SPI functions 45 | void SPI_init(uint16_t initParams); 46 | uint8_t SPI_transfer(uint8_t data); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /types.h: -------------------------------------------------------------------------------- 1 | #ifndef _TYPES_H_ 2 | #define _TYPES_H_ 3 | 4 | typedef unsigned long UInt32; 5 | typedef signed long Int32; 6 | typedef unsigned short UInt16; 7 | typedef signed short Int16; 8 | typedef unsigned char UInt8; 9 | typedef signed char Int8; 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /uart.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "uart.h" 4 | 5 | const char hex_digits[] = "0123456789ABCDEF"; 6 | 7 | void UART_init() 8 | { 9 | #ifndef BAUD_RATE 10 | #define BAUD_RATE 9600 11 | #endif 12 | 13 | // set rate 14 | UBRR0H = (unsigned char) (((F_CPU/(BAUD_RATE*16UL))) - 1) >> 8; 15 | UBRR0L = (unsigned char) ((F_CPU/(BAUD_RATE*16UL))) - 1; 16 | 17 | // Enable reciever and transmitter 18 | UCSR0B |= (1 << RXEN0)|(1 << TXEN0); 19 | } 20 | 21 | void UART_putc(const unsigned char data) 22 | { 23 | // wait for empty transmit buffer 24 | while(!(UCSR0A & (1< 0) 34 | // print character 35 | UART_putc(*charString++); 36 | } 37 | 38 | void UART_puthex8(uint8_t val) 39 | { 40 | // extract upper and lower nibbles from input value 41 | uint8_t upperNibble = (val & 0xF0) >> 4; 42 | uint8_t lowerNibble = val & 0x0F; 43 | 44 | // convert nibble to its ASCII hex equivalent 45 | upperNibble += upperNibble > 9 ? 'A' - 10 : '0'; 46 | lowerNibble += lowerNibble > 9 ? 'A' - 10 : '0'; 47 | 48 | // print the characters 49 | UART_putc(upperNibble); 50 | UART_putc(lowerNibble); 51 | } 52 | 53 | unsigned char UART_getc(void) 54 | { 55 | // wait for data to be received 56 | while(!(UCSR0A & (1 << RXC0))); 57 | 58 | // get data to output register 59 | return UDR0; 60 | } 61 | 62 | void UART_puts_p(const char* ps) 63 | { 64 | register char c; 65 | 66 | while ((c = pgm_read_byte(ps++))) 67 | UART_putc(c); 68 | } 69 | 70 | unsigned char UART_available(void) 71 | { 72 | return UCSR0A & (1 << RXC0); 73 | } 74 | 75 | void UART_puthex32(unsigned long value) { 76 | UART_pputs("0x"); 77 | char x[9]; 78 | unsigned char i, c; 79 | 80 | x[8] = '\0'; 81 | 82 | for (i = 0; i < 8; i++) { 83 | c = value & 0x0F; 84 | value >>= 4; 85 | c = (c >= 10) ? (c + 'A' - 10) : (c + '0'); 86 | x[7 - i] = c; 87 | } 88 | 89 | UART_puts(x); 90 | } 91 | 92 | void UART_putdec32(unsigned long value) { 93 | 94 | char x[16]; 95 | unsigned char i, c; 96 | 97 | x[sizeof(x) - 1] = 0; 98 | 99 | for(i = 0; i < sizeof(x) - 1; i++){ 100 | 101 | c = (value % 10) + '0'; 102 | value /= 10; 103 | x[sizeof(x) - 2 - i] = c; 104 | if(!value) break; 105 | } 106 | 107 | UART_puts(x + sizeof(x) - 2 - i); 108 | } 109 | -------------------------------------------------------------------------------- /uart.h: -------------------------------------------------------------------------------- 1 | #ifndef _uart_h 2 | #define _uart_h 3 | #ifdef __AVR__ 4 | #include 5 | #endif 6 | 7 | /************************************ 8 | * uart.h v1 - 11/02/2018 9 | ************************************/ 10 | #ifdef __AVR__ 11 | #define BAUD2BRR(x) (((F_CPU/((x)*16UL))) - 1) 12 | #define UART_pputs(x) UART_puts_p(PSTR(x)) 13 | #endif 14 | 15 | // UART functions 16 | void UART_init(void); 17 | void UART_putc(const unsigned char data); 18 | void UART_puts(const char* charString); 19 | void UART_puthex8(uint8_t val); 20 | unsigned char UART_getc(void); 21 | #ifdef __AVR__ 22 | void UART_puts_p(const char* ps); 23 | #endif 24 | unsigned char UART_available(void); 25 | void UART_puthex32(unsigned long value); 26 | void UART_putdec32(unsigned long value); 27 | 28 | #endif 29 | --------------------------------------------------------------------------------