// Copyright (c) 2025-2026 taitep // SPDX-License-Identifier: BSD-2-Clause // // This file is part of TRVE (https://gitea.taitep.se/taitep/trve) // See LICENSE file in the project root for full license text. use std::{io, os::fd::AsFd, path::PathBuf, sync::Arc, time::Duration}; use clap::Parser; use nix::fcntl::{FcntlArg, OFlag, fcntl}; use trve::{ core::Core, devices, exceptions::MemoryExceptionType, gdb, mem::{MemConfig, MemDeviceInterface, MmioRoot, Ram}, }; use anyhow::Result; mod execload; /// Taitep's RISC-V Emulator #[derive(Parser)] struct Args { /// Path to ELF or raw binary executable to load executable: PathBuf, /// Make CPU wait for a GDB connection before starting execution #[arg(long)] wait: bool, } fn main() -> Result<()> { let args = Args::parse(); let mut ram = Ram::try_new(16 * 1024 * 1024)?; let buf = ram.buf_mut(); let entry_point = execload::load(args.executable, buf)?; let mut mmio_root = MmioRoot::default(); mmio_root.insert(0, Arc::new(DbgOut)); if let Err(e) = fcntl(io::stdin().as_fd(), FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) { eprintln!("Could not make stdout nonblocking, skipping. Error: {e}"); } let uart = devices::serial::SifiveUart::new_arc(); uart.clone().spawn_io_thread( std::io::stdin(), std::io::stdout(), Duration::from_millis(10), ); mmio_root.insert(0x10000, uart); let mem_cfg = MemConfig { ram: Arc::new(ram), mmio_root, }; let (cmd_sender, cmd_reciever) = crossbeam::channel::bounded(16); gdb::run_stub(cmd_sender.clone()); let mut core = Core::new(mem_cfg, cmd_reciever); core.reset(entry_point); if args.wait { core.run_waiting_for_cmd(); } else { core.run(); } Ok(()) } struct DbgOut; impl MemDeviceInterface for DbgOut { fn write_dword(&self, addr: u64, value: u64) -> Result<(), MemoryExceptionType> { eprintln!("Wrote DWord {value:016x} to Debug-Out address {addr:x}"); Ok(()) } fn write_word(&self, addr: u64, value: u32) -> Result<(), MemoryExceptionType> { eprintln!("Wrote Word {value:08x} to Debug-Out address {addr:x}"); Ok(()) } fn write_hword(&self, addr: u64, value: u16) -> Result<(), MemoryExceptionType> { eprintln!("Wrote HWord {value:04x} to Debug-Out address {addr:x}"); Ok(()) } fn write_byte(&self, addr: u64, value: u8) -> Result<(), MemoryExceptionType> { eprintln!("Wrote Byte {value:02x} to Debug-Out address {addr:x}"); Ok(()) } }