~zenomat/yanos

A learning fullstack OS for the esp32c3 RISC-V 32 board
heap_allocator: partial alignment impl
build: add flash and monitor support
heap_allocator: fix linked list issues

refs

main
browse  log 

clone

read-only
https://git.sr.ht/~zenomat/yanos
read/write
git@git.sr.ht:~zenomat/yanos

You can also use your local clone with git send-email.

Everything here is specific to the esp32-c3, which I have. It might work for other chips, but that is not guaranteed

#Building and running

#Building

nix develop
zig build
  • the build does the following:

    • build the project, applying ./link.ld
    • create a flash-able image
    riscv32-none-elf-objcopy -O binary target/riscv32imc-unknown-none-elf/release/bare-bones image.bin
    
  • if you want to check the generated assembly/sections/headers

riscv32-none-elf-objdump -d target/riscv32imc-unknown-none-elf/release/bare-bones

#RUnning

  • run in qemu
source /opt/esp-idf/export.sh
qemu-system-riscv32 -nographic \
                    -M esp32c3 \
                    -drive file=zig-out/bin/yanos.bin,if=mtd,format=raw \
                    -serial mon:stdio -s

#Knowledge

#Direct Boot

  • shoudln't really be used, I just like to torture myself and I have no idea what I am doing
  • should be used to bootstrap "new languages, frameworks and execution environments" (according to direct boot example
  • direct boot only happens, when
    1. Secure boot is disabled
    2. direct boot not disabled by EFUSE_DIS_LEGACY_SPI_BOOT
    3. ROM bootloader does not detect valid binary image
    4. first 8 bytes in flash are 1d 04 db ae 1d 04 db ae
      • this is handled by the linker script
  • prereqs 1-2 can be handled by doing some menuconfig shit in a normal esp project, but I think they are default, because I did nothing specific
  • the ROM bootloader then jumps to Flash start + 8
  • after booting here, the code needs to:
    • [x] set up global pointer register
      • the global pointer is used to optimize/enable at all (?) global variable access
      • in riscv this is a special register gp/x3
    • [x] set up stack pointer register
      • enable stack access
      • in riscv this is a special register sp/x2
    • [x] zero-initialize the .bss section
      • the .bss section stores static variables, which are not yet initialized
      • is expected to be 0
    • [ ] initialize the .data section, copying it from ROM
      • contains local and global static variables
    • [ ] write the vector table address to the MTVEC register (optional)
    • [ ] call C library initialization (optional)
    • [x] call the main function

#Watchdogs

  • after the above steps, the system will still continue to bootloop
  • this is because the watchdogs are not being fed (reset) or disabled
  • there are 3 watchdogs, one in each timer module
    • TIMG0 - the timer module
    • RTC - the rtc module
    • SWD - super watchdog timer
  • disable TIMG0:
    • disable write protection by writing 0x50D83AA1 to TIMG_WDT_WKEY
    • clear TIMG_WDT_FLASHBOOT_MOD_EN
    • then disable to timer by setting TIMG_WDT_EN
    • reenable write protection by zeroing TIMG_WDT_WKEY
  • disable RTC:
    • disable write protection by writing 0x50D83AA1 to RTC_CNTL_WDT_WKEY
    • clear RTC_CNTL_WDT_FLASHBOOT_MOD_EN
    • then disable to timer by setting RTC_CNTL_WDT_EN
    • reenable write protection by zeroing RTC_CNTL_WDT_WKEY
  • disable SWD
    • disable write protection for SWD write 0x8F1D312A to RTC_CNTL_SWD_WKEY
    • disable timer by writing 1 to RTC_CNTL_SWD_DISABLE
  • you might have noticed the write protection
  • those timers help to reset the board, in case it gets into a deadlock or some other precarious situation, so it isn't necessarily recommended to disable those timers
  • we will do it here, just to get something working

#UART

  • the esp32c3 offers a rom function for printing a single byte to the UART output: uart_tx_one_char(b: u8)
  • we can use that to split a message in single bytes and transmit them