Files
lopez-repo/blog-esp32.html

443 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Running Spider on ESP32 — Diego López</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400&family=Syne:wght@400;700;800&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0a0a0a;
--surface: #111111;
--border: #1f1f1f;
--accent: #00e5a0;
--accent2: #ff6b35;
--text: #e8e8e8;
--muted: #666;
--code-bg: #0d1117;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg);
color: var(--text);
font-family: 'Syne', sans-serif;
line-height: 1.7;
min-height: 100vh;
}
/* ── Header ── */
header {
border-bottom: 1px solid var(--border);
padding: 2rem 0;
position: relative;
overflow: hidden;
}
header::before {
content: '';
position: absolute;
top: -60px; left: -60px;
width: 300px; height: 300px;
background: radial-gradient(circle, rgba(0,229,160,0.08) 0%, transparent 70%);
pointer-events: none;
}
.header-inner {
max-width: 860px;
margin: 0 auto;
padding: 0 2rem;
}
.tag {
font-family: 'Space Mono', monospace;
font-size: 0.7rem;
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.15em;
border: 1px solid var(--accent);
padding: 0.2rem 0.6rem;
display: inline-block;
margin-bottom: 1.2rem;
}
h1 {
font-size: clamp(2rem, 5vw, 3.2rem);
font-weight: 800;
line-height: 1.1;
letter-spacing: -0.02em;
margin-bottom: 1rem;
}
h1 span {
color: var(--accent);
}
.meta {
font-family: 'Space Mono', monospace;
font-size: 0.75rem;
color: var(--muted);
margin-top: 1.2rem;
}
/* ── Main content ── */
main {
max-width: 860px;
margin: 0 auto;
padding: 3rem 2rem 6rem;
}
/* ── Diagram box ── */
.diagram {
background: var(--code-bg);
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
padding: 1.5rem 2rem;
font-family: 'Space Mono', monospace;
font-size: 0.82rem;
color: #8b949e;
line-height: 2;
margin: 2rem 0;
overflow-x: auto;
}
.diagram .hl { color: var(--accent); }
.diagram .hl2 { color: var(--accent2); }
/* ── Section heading ── */
.step {
display: flex;
align-items: flex-start;
gap: 1.5rem;
margin: 3rem 0 1rem;
}
.step-num {
font-family: 'Space Mono', monospace;
font-size: 0.7rem;
color: var(--accent);
border: 1px solid var(--accent);
padding: 0.3rem 0.5rem;
white-space: nowrap;
margin-top: 0.3rem;
flex-shrink: 0;
}
.step h2 {
font-size: 1.4rem;
font-weight: 700;
letter-spacing: -0.01em;
}
p {
color: #b0b0b0;
margin-bottom: 1rem;
}
/* ── Code block ── */
pre {
background: var(--code-bg);
border: 1px solid var(--border);
padding: 1.4rem 1.6rem;
font-family: 'Space Mono', monospace;
font-size: 0.78rem;
line-height: 1.8;
overflow-x: auto;
margin: 1.2rem 0;
color: #8b949e;
position: relative;
}
pre .kw { color: #ff7b72; }
pre .str { color: #a5d6ff; }
pre .cmt { color: #3d4f5a; font-style: italic; }
pre .val { color: var(--accent); }
pre .flag { color: var(--accent2); }
/* ── Flags table ── */
.flag-grid {
display: grid;
grid-template-columns: auto 1fr;
gap: 0;
margin: 1.5rem 0;
border: 1px solid var(--border);
overflow: hidden;
}
.flag-grid .flag-name {
font-family: 'Space Mono', monospace;
font-size: 0.78rem;
color: var(--accent2);
padding: 0.7rem 1rem;
background: var(--code-bg);
border-bottom: 1px solid var(--border);
white-space: nowrap;
}
.flag-grid .flag-desc {
font-size: 0.85rem;
color: #b0b0b0;
padding: 0.7rem 1rem;
background: var(--surface);
border-bottom: 1px solid var(--border);
}
.flag-grid .flag-name:last-of-type,
.flag-grid .flag-desc:last-of-type {
border-bottom: none;
}
/* ── Result banner ── */
.result {
border: 1px solid var(--accent);
background: rgba(0,229,160,0.04);
padding: 1.5rem 2rem;
margin: 3rem 0;
position: relative;
}
.result::before {
content: 'CONFIRMED OUTPUT';
font-family: 'Space Mono', monospace;
font-size: 0.65rem;
color: var(--accent);
letter-spacing: 0.15em;
position: absolute;
top: -0.55rem;
left: 1.5rem;
background: var(--bg);
padding: 0 0.5rem;
}
.result pre {
border: none;
padding: 0;
background: transparent;
margin: 0;
}
.result pre .ok { color: var(--accent); }
/* ── Note ── */
.note {
border-left: 2px solid var(--accent2);
padding: 0.8rem 1.2rem;
background: rgba(255,107,53,0.04);
margin: 1.5rem 0;
font-size: 0.88rem;
color: #b0b0b0;
}
.note strong { color: var(--accent2); }
/* ── Footer ── */
footer {
border-top: 1px solid var(--border);
text-align: center;
padding: 2rem;
font-family: 'Space Mono', monospace;
font-size: 0.7rem;
color: var(--muted);
}
/* ── Animations ── */
@keyframes fadeUp {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
header, main > * {
animation: fadeUp 0.5s ease both;
}
main > *:nth-child(1) { animation-delay: 0.05s; }
main > *:nth-child(2) { animation-delay: 0.10s; }
main > *:nth-child(3) { animation-delay: 0.15s; }
main > *:nth-child(4) { animation-delay: 0.20s; }
main > *:nth-child(5) { animation-delay: 0.25s; }
</style>
</head>
<body>
<header>
<div class="header-inner">
<div class="tag">Spider Lang · Internship 2026</div>
<h1>Running Spider<br>on <span>ESP32</span></h1>
<p style="color:#888; max-width:560px; margin-top:0.8rem;">
How the Spider Runtime was cross-compiled for the Xtensa architecture
and flashed onto physical hardware using ESP-IDF.
</p>
<div class="meta">Diego Alberto López &nbsp;·&nbsp; Build System &nbsp;·&nbsp; April 2026</div>
</div>
</header>
<main>
<!-- Overview diagram -->
<div class="diagram">
<span class="hl">script.spider</span>
↓ Spider Compiler
<span class="hl">bytecode.spd</span> <span style="color:#3d4f5a">← Spider's own format, not x86, not Xtensa</span>
<span class="hl">Spider Runtime (C++)</span>
↓ xtensa-esp-elf-g++ <span style="color:#3d4f5a">← ESP-IDF toolchain</span>
<span class="hl2">spider_esp32.bin</span>
↓ esptool.py
<span class="hl">ESP32 flash memory</span> <span style="color:#3d4f5a">← Runtime lives here permanently</span>
</div>
<p>
Spider's bytecode is a neutral format that belongs to no specific processor.
The toolchain's job is not to convert bytecode — it compiles the
C++ source of the Runtime itself into Xtensa machine code.
Once flashed, the Runtime lives in the ESP32's flash memory and
interprets Spider bytecode at runtime.
</p>
<!-- Step 1 -->
<div class="step">
<div class="step-num">01</div>
<h2>Prerequisites</h2>
</div>
<p>Install ESP-IDF v5.x and add the Xtensa toolchain to your PATH:</p>
<pre><span class="cmt"># Add to ~/.bashrc to make it permanent</span>
<span class="kw">export</span> PATH=<span class="str">$PATH:/c/Espressif/tools/xtensa-esp-elf/esp-14.2.0_20251107/xtensa-esp-elf/bin</span></pre>
<!-- Step 2 -->
<div class="step">
<div class="step-num">02</div>
<h2>Compile flags for ESP32</h2>
</div>
<p>
The Runtime uses compile-time platform detection via <code style="font-family:'Space Mono',monospace;color:var(--accent);font-size:0.85em">distro_mcu.hpp</code>.
These flags tell it which hardware it is targeting:
</p>
<div class="flag-grid">
<div class="flag-name">-DESP32</div>
<div class="flag-desc">Activates ESP32 detection in distro_mcu.hpp</div>
<div class="flag-name">-DSPIDER_DISTRO_MICRO</div>
<div class="flag-desc">Enables microcontroller mode (reduced memory footprint)</div>
<div class="flag-name">-DSPIDER_OS_NONE</div>
<div class="flag-desc">Declares bare-metal environment, no OS</div>
<div class="flag-name">-mlongcalls</div>
<div class="flag-desc">Required for Xtensa's memory layout and call distances</div>
<div class="flag-name">-fno-exceptions -fno-rtti</div>
<div class="flag-desc">Disable C++ features unavailable on bare-metal</div>
<div class="flag-name">-O0</div>
<div class="flag-desc">No optimizations — faster compilation during development</div>
</div>
<!-- Step 3 -->
<div class="step">
<div class="step-num">03</div>
<h2>Build with ESP-IDF (CMake)</h2>
</div>
<p>
The bare-metal Makefile approach caused boot loops because the ESP32 bootloader
expects a specific binary format. The correct approach is to use ESP-IDF's
CMake build system, which generates the bootloader, partition table, and app
binary automatically.
</p>
<pre><span class="cmt"># From spider-micro-buildtools/esp32/idf_project/</span>
idf.py set-target esp32
idf.py build
idf.py -p COM6 flash monitor</pre>
<p>The CMakeLists.txt registers the Spider Runtime source files as a component:</p>
<pre><span class="kw">set</span>(SPIDER_RUNTIME_SRC
<span class="str">"${CMAKE_CURRENT_SOURCE_DIR}/../../../../spider-runtime/src"</span>
)
<span class="kw">idf_component_register</span>(
SRCS
<span class="str">"main.cpp"</span>
<span class="str">"${SPIDER_RUNTIME_SRC}/spider/runtime/Runtime.cpp"</span>
<span class="str">"${SPIDER_RUNTIME_SRC}/spider/runtime/cpu/CPU.cpp"</span>
<span class="cmt">... (all Runtime source files)</span>
PRIV_INCLUDE_DIRS <span class="str">"${SPIDER_RUNTIME_SRC}"</span>
)</pre>
<div class="note">
<strong>Note:</strong> Relative paths using <code style="font-family:'Space Mono',monospace;font-size:0.85em">CMAKE_CURRENT_SOURCE_DIR</code>
are used to avoid exposing personal directory structure in the repository.
</div>
<!-- Step 4 -->
<div class="step">
<div class="step-num">04</div>
<h2>app_main — The ESP32 entry point</h2>
</div>
<p>
ESP-IDF requires <code style="font-family:'Space Mono',monospace;color:var(--accent);font-size:0.85em">app_main()</code>
instead of the standard <code style="font-family:'Space Mono',monospace;color:var(--accent);font-size:0.85em">main()</code>.
The Spider Runtime is initialized here with a fixed amount of RAM:
</p>
<pre><span class="kw">#include</span> <span class="str">"esp_log.h"</span>
<span class="kw">#include</span> <span class="str">&lt;spider/runtime/Runtime.hpp&gt;</span>
<span class="kw">static const char*</span> TAG = <span class="str">"Spider"</span>;
<span class="kw">extern "C" void</span> app_main(<span class="kw">void</span>) {
ESP_LOGI(TAG, <span class="str">"Spider Runtime starting..."</span>);
spider::Runtime runtime(<span class="val">32</span> * <span class="val">1024</span>); <span class="cmt">// 32 KB RAM</span>
ESP_LOGI(TAG, <span class="str">"Spider Runtime initialized!"</span>);
ESP_LOGI(TAG, <span class="str">"RAM size: 32KB"</span>);
<span class="kw">while</span>(<span class="val">1</span>) { vTaskDelay(pdMS_TO_TICKS(<span class="val">1000</span>)); }
}</pre>
<!-- Confirmed output -->
<div class="result">
<pre>
<span class="ok">I (274) Spider: Spider Runtime starting...</span>
<span class="ok">I (274) Spider: Spider Runtime initialized!</span>
<span class="ok">I (274) Spider: RAM size: 32KB</span>
</pre>
</div>
<!-- Repo structure -->
<div class="step">
<div class="step-num">05</div>
<h2>Repository structure</h2>
</div>
<p>
The build tools live in <code style="font-family:'Space Mono',monospace;color:var(--accent);font-size:0.85em">spider-micro-buildtools</code>,
separate from the Runtime source. Both repos must be cloned at the same level:
</p>
<div class="diagram">
<span class="hl">Internship/</span>
├── <span class="hl">spider-runtime/</span> <span style="color:#3d4f5a">← Runtime C++ source</span>
└── <span class="hl2">spider-micro-buildtools/</span>
└── esp32/
├── Makefile <span style="color:#3d4f5a">← bare-metal reference build</span>
├── gen_makefile.py
├── main_esp32.cpp
├── README.md
└── <span class="hl2">idf_project/</span> <span style="color:#3d4f5a">← ESP-IDF project (use this)</span>
├── CMakeLists.txt
└── main/
├── CMakeLists.txt
└── main.cpp
</div>
</main>
<footer>
Spider Lang Internship 2026 &nbsp;·&nbsp; Diego Alberto López
</footer>
</body>
</html>