Reorganize source tree: kernel/, v3/, v4/ split and board infrastructure

Source tree reorganization:
- Move StarForth v3 engine to v3/ (src/, include/, Makefile)
- Move kernel to kernel/ (src/, include/, linker/, Makefile)
- Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md)
- Move FABRIC-0..4.md to docs/fabric/
- Move ONTOLOGY.md and ROADMAP.md to docs/

Board infrastructure:
- Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/
- Each board has board.mk (ISA, CPU flags, boot recipe) and README.md
- Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET
- make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board
- ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet)
- scripts/mkdiskimage.sh builds disk images for all boards

Docs pipeline:
- docs/book/ with LaTeX master (main.tex) and Makefile
- pandoc converts Markdown to LaTeX at build time
- Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks)
- make docs builds single PDF (754 pages, 0 missing characters)
- make docs TARGET=<board> adds board appendix
- build/docs/<book|board>/meta.tex stamps git commit into PDF

Bug fixes:
- 42 include paths that only worked by accident now use correct relative paths
- clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build)
- Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved
- Doxyfile, .clang-tidy, README.md, Kconfig paths updated

Verified:
- Hosted v3 build passes 1012 tests, 0 failures
- SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE)
- Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes
- make clean TARGET=<board> removes only that board and its ISA objects
- make all builds all boards, hosted v3, and docs in one run

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
rajames
2026-10-01 15:40:09 -04:00
co-authored by Junie
parent 3c709c115b
commit a8b70e88d3
630 changed files with 2646 additions and 2036 deletions
+164
View File
@@ -0,0 +1,164 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
* platform/alloc_host.c - Host (Linux/POSIX) memory allocator
*
* Simple wrapper around standard malloc/free with statistics tracking.
*/
#include "../../include/platform_alloc.h"
#include <stdlib.h>
#include <string.h>
/* Statistics tracking */
static sf_alloc_stats_t g_stats = {0};
/**
* @brief Initialise the host allocator statistics.
*
* Resets the @c g_stats struct to zero. Safe to call multiple times;
* subsequent calls simply reset the counters. No heap memory is allocated
* by this function — the underlying allocator is the system @c malloc.
*
* @return 0 always (cannot fail on the hosted path)
*/
int sf_alloc_init(void)
{
memset(&g_stats, 0, sizeof(g_stats));
return 0;
}
/**
* @brief Allocate @c size bytes from the system heap.
*
* Delegates to @c malloc(). On success, increments @c g_stats.used_bytes
* and @c g_stats.alloc_count, and updates @c g_stats.peak_bytes if the
* new used total exceeds the recorded peak.
*
* @param size Number of bytes to allocate; 0 returns NULL
* @return Pointer to allocated memory, or NULL on allocation failure or zero size
*/
void* sf_malloc(size_t size)
{
if (size == 0) return NULL;
void* ptr = malloc(size);
if (ptr)
{
g_stats.used_bytes += size;
g_stats.alloc_count++;
if (g_stats.used_bytes > g_stats.peak_bytes)
{
g_stats.peak_bytes = g_stats.used_bytes;
}
}
return ptr;
}
/**
* @brief Allocate a zero-initialised array of @c count elements of @c size bytes each.
*
* Delegates to @c calloc(). Updates statistics the same way as @c sf_malloc():
* adds @c count*size to @c used_bytes, increments @c alloc_count, and updates
* @c peak_bytes.
*
* @param count Number of elements; 0 returns NULL
* @param size Size of each element in bytes; 0 returns NULL
* @return Pointer to zero-filled memory, or NULL on allocation failure or zero arguments
*/
void* sf_calloc(size_t count, size_t size)
{
if (count == 0 || size == 0) return NULL;
void* ptr = calloc(count, size);
if (ptr)
{
size_t total = count * size;
g_stats.used_bytes += total;
g_stats.alloc_count++;
if (g_stats.used_bytes > g_stats.peak_bytes)
{
g_stats.peak_bytes = g_stats.used_bytes;
}
}
return ptr;
}
/**
* @brief Resize a previously allocated block to @c new_size bytes.
*
* Delegates to @c realloc(). On success, increments @c g_stats.alloc_count.
* The exact byte delta is not tracked because the original allocation size is
* not stored alongside the pointer — @c used_bytes is therefore approximate
* after any @c sf_realloc() call. For accurate accounting, wrap allocations
* with size-tracking headers.
*
* @param ptr Pointer to an existing allocation, or NULL (behaves like @c sf_malloc)
* @param new_size Desired new size in bytes; 0 may free the block (platform-defined)
* @return Pointer to resized memory, or NULL on failure (original block unchanged)
*/
void* sf_realloc(void* ptr, size_t new_size)
{
void* new_ptr = realloc(ptr, new_size);
if (new_ptr)
{
/* Note: Can't track exact delta without size tracking */
g_stats.alloc_count++;
}
return new_ptr;
}
/**
* @brief Release a previously allocated block back to the system heap.
*
* Delegates to @c free(). Increments @c g_stats.free_count on non-NULL @c ptr.
* The freed byte count is not subtracted from @c used_bytes because the
* original size is not tracked — see @c sf_realloc() note.
*
* @param ptr Pointer to free; NULL is a safe no-op
*/
void sf_free(void* ptr)
{
if (!ptr) return;
/* Note: We can't track exact bytes freed without size tracking.
* For accurate stats, would need to wrap allocations with headers.
* For now, just count frees. */
g_stats.free_count++;
free(ptr);
}
/**
* @brief Copy the current allocator statistics into @c stats.
*
* Fills @c *stats with a snapshot of @c g_stats. Note that @c used_bytes
* overstates the live total when blocks have been freed or reallocated,
* because freed sizes are not tracked. @c free_count is accurate.
*
* @param stats Output buffer to populate; no-op if NULL
*/
void sf_alloc_get_stats(sf_alloc_stats_t* stats)
{
if (!stats) return;
*stats = g_stats;
}