├── 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 | 
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 |
--------------------------------------------------------------------------------