Skip to content

Commit 6dbf571

Browse files
authored
Pyodide-powered emulator in the broser
1 parent 2b93ab4 commit 6dbf571

File tree

17 files changed

+4533
-1
lines changed

17 files changed

+4533
-1
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ pip install -r requirements.txt
5757
├── advanced/freertos/ # FreeRTOS port
5858
├── advanced/micropython/ # MicroPython port
5959
├── advanced/circuitpython/ # CircuitPython port
60-
├── advanced/coremark/ # CoreMark port
60+
├── advanced/coremark/ # CoreMark port
61+
├── advanced/webapp/ # Browser-based port powered by Pyodide
6162
└── README.md # You're here!
6263
```
6364

@@ -235,6 +236,10 @@ Example Python programs using programmatic access to the emulator are provided i
235236
PYTHONPATH=. python tests/test_api_simple.py
236237
```
237238

239+
## Browser-based emulator
240+
241+
The emulator can run in a web browser thanks to [Pyodide](https://pyodide.org/). See `advanced/webapp`.
242+
238243
## 🧪 Running Unit Tests
239244
```
240245
cd riscv-tests

advanced/webapp/README.md

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# RISC-V Emulator - Browser Edition
2+
3+
A pure browser-based RISC-V (RV32IMAC) emulator powered by Pyodide and xterm.js.
4+
Developed using [Claude Code](https://www.claude.com/product/claude-code).
5+
6+
## Features
7+
8+
- **Pure client-side**: No backend server needed, runs entirely in your browser
9+
- **Full RV32IMAC support**: Base instruction set plus M (multiply/divide), A (atomics), and C (compressed instructions)
10+
- **Interactive terminal**: Character-at-a-time input via xterm.js with raw TTY support
11+
- **ELF and binary loading**: Load compiled programs directly from your filesystem
12+
- **Debugging tools**: Optional tracing for syscalls, traps, and function calls
13+
- **Timer support**: Machine timer interrupts for time-based programming
14+
- **Performance**: ~200K-2M instructions/second typical performance
15+
16+
## Quick Start
17+
18+
1. **Serve the webapp directory**:
19+
```bash
20+
cd advanced/webapp
21+
python3 -m http.server 8000
22+
```
23+
24+
2. **Open in browser**:
25+
Navigate to `http://localhost:8000`
26+
27+
3. **Load a program**:
28+
- Click "Load ELF/BIN" and select a compiled RISC-V program
29+
- Programs must be compiled for RV32I (optionally with M, A, C extensions)
30+
31+
4. **Run**:
32+
- Click "Run" to start execution
33+
- Interact with the program via the terminal
34+
- Click "Stop" to interrupt execution
35+
- Click "Reset" to clear emulator state
36+
37+
## Building Programs
38+
39+
Programs must be compiled for RISC-V RV32I. Example using the riscv-gnu-toolchain:
40+
41+
```bash
42+
# Simple program
43+
riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 \
44+
-nostartfiles -static -Tlinker.ld \
45+
--specs=nosys.specs -o program.elf program.c
46+
47+
# With multiply/divide support
48+
riscv64-unknown-elf-gcc -march=rv32im -mabi=ilp32 \
49+
-nostartfiles -static -Tlinker.ld \
50+
--specs=nosys.specs -o program.elf program.c
51+
```
52+
53+
See `../../tests/` for example programs.
54+
55+
## UI Controls
56+
57+
### Tracing Options
58+
- **Syscalls**: Log all syscall operations (exit, read, write, sbrk, etc.)
59+
- **Traps**: Log exceptions and interrupts
60+
- **Functions**: Log function calls (requires ELF with symbols)
61+
62+
### Checking Options
63+
- **Invariants**: Check CPU invariants (x0=0, PC bounds, stack bounds)
64+
- **Memory bounds**: Validate all memory accesses
65+
- **Text integrity**: Detect self-modifying code
66+
67+
### Features
68+
- **Timer**: Enable machine timer interrupts (CSR mode)
69+
- **RVC**: Enable compressed 16-bit instructions
70+
71+
### Configuration
72+
- **Registers**: Comma-separated list of registers to log (e.g., "pc,sp,ra,a0")
73+
- **RAM Size**: Emulated RAM in kilobytes (default: 1024 KB)
74+
75+
## Architecture
76+
77+
### Files Structure
78+
79+
```
80+
webapp/
81+
├── index.html # Main page
82+
├── css/
83+
│ └── styles.css # UI styling
84+
├── js/
85+
│ ├── main.js # Pyodide orchestration & execution loop
86+
│ ├── terminal.js # Xterm.js terminal with I/O bridging
87+
│ ├── fileloader.js # FileAPI for loading ELF/bin files
88+
│ └── controls.js # UI option management
89+
└── py/
90+
├── browser_entry.py # Python entry point for browser
91+
├── browser_syscalls.py # Browser-adapted syscall handler
92+
├── browser_logger.py # Logger outputting to JS console
93+
└── peripherals.py # Minimal peripherals (timer only)
94+
```
95+
96+
### Dependencies
97+
98+
- **Pyodide** (v0.24.1+): Python runtime for WebAssembly
99+
- **pyelftools**: ELF file parsing
100+
- **xterm.js** (v5.3.0): Terminal emulator
101+
- **Parent directory modules**: cpu.py, ram.py, machine.py, rvc.py (imported dynamically)
102+
103+
## Syscall Support
104+
105+
### Implemented Syscalls
106+
- `exit` (93): Program termination
107+
- `write` (64): Write to stdout/stderr → terminal
108+
- `read` (63): Read from stdin ← terminal
109+
- `sbrk` (214): Heap expansion
110+
- `fstat` (80): File status (faked for stdin/stdout/stderr)
111+
- `isatty` (89): TTY check (returns true for 0/1/2)
112+
- `getpid` (172): Get PID (returns 1)
113+
- `umask` (60): File creation mask
114+
115+
### Stubbed Syscalls (return -ENOSYS)
116+
- File operations: `openat`, `close`, `lseek`, `mkdirat`, `unlinkat`
117+
- Process control: `kill`
118+
119+
Programs relying on file I/O will fail. Future versions may support virtual filesystem.
120+
121+
## Performance
122+
123+
- **Chunked execution**: 10,000 instructions per frame (configurable in main.js)
124+
- **Frame rate**: Target 60 FPS = ~600K instructions/second
125+
- **Actual performance**: 200K - 2M IPS depending on browser and options enabled
126+
- **Tracing overhead**: ~3x slower when logging is enabled
127+
128+
## Limitations (Pilot Version)
129+
130+
1. **No file I/O**: `open()`, `read()`, `write()` on files return `-ENOSYS`
131+
2. **No block device**: No persistent storage emulation
132+
3. **Single-threaded**: Execution on main thread (Web Worker support planned)
133+
4. **No debugging UI**: No step-through or breakpoints yet
134+
5. **No save/restore**: Cannot snapshot emulator state
135+
136+
## Browser Compatibility
137+
138+
Tested on:
139+
- Chrome 90+
140+
- Firefox 88+
141+
- Safari 14+
142+
- Edge 90+
143+
144+
Requires modern JavaScript (ES6+) and WebAssembly support.
145+
146+
## Troubleshooting
147+
148+
### "Failed to initialize Pyodide"
149+
- Check browser console for detailed errors
150+
- Ensure internet connection (Pyodide loads from CDN)
151+
- Try reloading the page
152+
153+
### "Error loading file"
154+
- Verify file is valid ELF or binary
155+
- Check file was compiled for RV32I (not RV64)
156+
- Ensure file size < RAM size
157+
158+
### Terminal not responding
159+
- Check browser console for errors
160+
- Try clicking Reset and reloading the program
161+
- Verify Ctrl-C works to stop execution
162+
163+
### Slow performance
164+
- Disable tracing and checking options
165+
- Reduce RAM size if not needed
166+
- Try a different browser (Chrome typically fastest)
167+
168+
## Development
169+
170+
To modify the emulator:
171+
172+
1. **JavaScript changes**: Edit files in `js/` and reload page
173+
2. **Python changes**: Edit files in `py/` and reload page (Pyodide fetches on startup)
174+
3. **Core emulator changes**: Modify parent directory files (cpu.py, ram.py, machine.py)
175+
176+
## Future Enhancements
177+
178+
- Web Worker execution for better performance
179+
- Virtual filesystem (IndexedDB-backed)
180+
- Block device support
181+
- Step-through debugging UI
182+
- Breakpoints and memory inspector
183+
- State save/restore to localStorage
184+
- Preloaded example programs
185+
186+
## License
187+
188+
Same as parent project (see root directory).
189+
190+
## Credits
191+
192+
Built on the RISC-V emulator by Ciro Cattuto.
193+
Uses Pyodide, xterm.js, and pyelftools.

0 commit comments

Comments
 (0)