Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 33 additions & 45 deletions src/app/headless.rs
Original file line number Diff line number Diff line change
@@ -1,70 +1,69 @@
use crate::hifive1b::Hifive1b;
use crate::utils::map_to_unixsocket;
use std::io::{self, Read};
use std::path::PathBuf;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::thread::spawn;
use std::thread::JoinHandle;

use crate::cli;
use crate::cpu::{create_cpu_thread, CPU};
use crate::cpu::{create_cpu_thread, AddrBus, CPU};
use crate::events::{CpuJob, Event};
use crate::periph;
use crate::periph::MmapPeripheral;

fn input_thread(sender: &Sender<Event>) {
fn input_thread(sender: &Sender<Event>, output: Option<&Sender<u8>>) {
println!("Use ^D to terminate.");
let mut buffer = [0; 1];
while let Ok(size) = io::stdin().read(&mut buffer) {
if size == 0 {
break;
}
if let Some(output) = output {
output.send(buffer[0]).unwrap();
}
}
sender.send(Event::ExitApp).unwrap();
}

fn create_input_thread(sender: Sender<Event>) -> JoinHandle<()> {
spawn(move || input_thread(&sender))
fn create_input_thread(sender: Sender<Event>, output: Option<Sender<u8>>) -> JoinHandle<()> {
spawn(move || input_thread(&sender, output.as_ref()))
}

fn headless_unix_socket(config: &cli::Config, socket_path: &PathBuf) {
pub fn headless(config: &cli::Config) {
let (event_sender, event_receiver): (Sender<Event>, Receiver<Event>) = channel();
let (cpu_sender, cpu_reader): (Sender<CpuJob>, Receiver<CpuJob>) = channel();

let tty = periph::new_unix_socket_uart(event_sender.clone(), socket_path);
let cpu_val = if config.bin {
let entry = config.entryaddress;
let baseaddress = config.baseaddress;
CPU::from_bin(&config.file, tty, entry, baseaddress)
} else {
CPU::from_elf(&config.file, tty)
};
let cpu = Arc::new(Mutex::new(cpu_val));
let mut hifive1b = Hifive1b::new(event_sender.clone());

create_input_thread(event_sender.clone());
let uart0 = hifive1b.uart0channel.take().unwrap();
if let Some(path) = &config.uart0 {
map_to_unixsocket(uart0, path.clone());
create_input_thread(event_sender.clone(), None);
} else {
let (uart_tx, uart_rx) = uart0;
create_input_thread(event_sender.clone(), Some(uart_tx));
spawn(move || loop {
while let Ok(data) = uart_rx.recv() {
print!("{:}", data as char);
}
});
}

cpu_job_loop(
config,
&cpu,
&event_receiver,
event_sender,
cpu_reader,
&cpu_sender,
);
}
let uart1 = hifive1b.uart1channel.take().unwrap();
if let Some(path) = &config.uart1 {
map_to_unixsocket(uart1, path.clone());
}

fn headless_stdio(config: &cli::Config) {
let (event_sender, event_receiver): (Sender<Event>, Receiver<Event>) = channel();
let (cpu_sender, cpu_reader): (Sender<CpuJob>, Receiver<CpuJob>) = channel();
let memory_map = hifive1b.memory.take().unwrap();

let tty = periph::new_stdio_uart(event_sender.clone());
let cpu_val = if config.bin {
let entry = config.entryaddress;
let baseaddress = config.baseaddress;
CPU::from_bin(&config.file, tty, entry, baseaddress)
CPU::from_bin(&config.file, memory_map, entry, baseaddress)
} else {
CPU::from_elf(&config.file, tty)
CPU::from_elf(&config.file, memory_map)
};
let cpu = Arc::new(Mutex::new(cpu_val));

cpu_job_loop(
config,
&cpu,
Expand All @@ -75,20 +74,9 @@ fn headless_stdio(config: &cli::Config) {
);
}

pub fn headless(config: &cli::Config) {
match config.uart_socket {
Some(ref path) => {
headless_unix_socket(config, path);
}
None => {
headless_stdio(config);
}
}
}

fn cpu_job_loop(
config: &cli::Config,
cpu: &Arc<Mutex<CPU<impl MmapPeripheral + 'static>>>,
cpu: &Arc<Mutex<CPU<impl AddrBus + Send + 'static>>>,
event_receiver: &Receiver<Event>,
event_sender: Sender<Event>,
cpu_reader: Receiver<CpuJob>,
Expand Down
40 changes: 29 additions & 11 deletions src/app/tui.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::hifive1b::Hifive1b;
use crate::utils::map_to_unixsocket;
use std::io;
use std::sync::mpsc;
use std::sync::mpsc::channel;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::thread::{spawn, JoinHandle};
Expand All @@ -14,9 +17,8 @@ use ratatui::{prelude::CrosstermBackend, Terminal};

use super::ui::ViewState;
use crate::cli;
use crate::cpu::{create_cpu_thread, CPU};
use crate::cpu::{create_cpu_thread, AddrBus, CPU};
use crate::events::{CpuJob, Event};
use crate::periph;

pub enum Job {
Step(usize),
Expand Down Expand Up @@ -53,7 +55,7 @@ fn create_input_thread(sender: Sender<Event>) -> JoinHandle<()> {
spawn(move || input_thread(&sender))
}

fn event_loop_tui<T: periph::MmapPeripheral>(
fn event_loop_tui<T: AddrBus>(
input: &Receiver<Event>,
cpu: &Arc<Mutex<CPU<T>>>,
cpu_sender: &Sender<CpuJob>,
Expand Down Expand Up @@ -135,21 +137,37 @@ fn event_loop_tui<T: periph::MmapPeripheral>(
}

pub fn tui(config: &cli::Config) {
let (tx, tui_reader): (mpsc::Sender<u8>, mpsc::Receiver<u8>) = mpsc::channel();
let (tui_writer, rx): (mpsc::Sender<u8>, mpsc::Receiver<u8>) = mpsc::channel();
let (event_sender, event_reader): (mpsc::Sender<Event>, mpsc::Receiver<Event>) =
mpsc::channel();
let (cpu_sender, cpu_reader): (mpsc::Sender<CpuJob>, mpsc::Receiver<CpuJob>) = mpsc::channel();
let (_tx, mut tui_reader): (mpsc::Sender<u8>, mpsc::Receiver<u8>) = mpsc::channel();
let (mut tui_writer, _rx): (mpsc::Sender<u8>, mpsc::Receiver<u8>) = mpsc::channel();

let buffered = periph::new_buffered_uart(rx, tx, event_sender.clone());
let (event_sender, event_reader): (Sender<Event>, Receiver<Event>) = channel();
let (cpu_sender, cpu_reader): (Sender<CpuJob>, Receiver<CpuJob>) = channel();

let mut hifive1b = Hifive1b::new(event_sender.clone());

let uart0 = hifive1b.uart0channel.take().unwrap();
if let Some(path) = &config.uart0 {
map_to_unixsocket(uart0, path.clone());
} else {
let (uart_tx, uart_rx) = uart0;
tui_reader = uart_rx;
tui_writer = uart_tx;
}

let uart1 = hifive1b.uart1channel.take().unwrap();
if let Some(path) = &config.uart1 {
map_to_unixsocket(uart1, path.clone());
}

let memory_map = hifive1b.memory.take().unwrap();

let cpu_val = {
if config.bin {
let entry = config.entryaddress;
let baseaddress = config.baseaddress;
CPU::from_bin(&config.file, buffered, entry, baseaddress)
CPU::from_bin(&config.file, memory_map, entry, baseaddress)
} else {
CPU::from_elf(&config.file, buffered)
CPU::from_elf(&config.file, memory_map)
}
};

Expand Down
13 changes: 4 additions & 9 deletions src/app/ui.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! The terminal user interface is the scope of this file.
use crate::cpu::AddrBus;
use crate::cpu::Register;
use crate::cpu::CPU;
use crate::instructions::Instruction;
use crate::periph::MmapPeripheral;
use anyhow::Error;
use crossterm::event::KeyEvent;
use crossterm::event::MouseEvent;
Expand Down Expand Up @@ -94,7 +94,7 @@ impl ViewState {
self.auto_step
}

fn instruction_log_block<T: MmapPeripheral>(log: Rect, cpu: &CPU<T>) -> Paragraph<'_> {
fn instruction_log_block<T: AddrBus>(log: Rect, cpu: &CPU<T>) -> Paragraph<'_> {
let log_height = log.height as usize;
let last_inst = cpu.last_n_instructions(log_height - 2);
let mut last_instruction_list: String = String::new();
Expand All @@ -114,7 +114,7 @@ impl ViewState {
Paragraph::new(text).block(Block::bordered().title(vec![Span::from("Last Instructions")]))
}

fn next_instruction_block<T: MmapPeripheral>(next: Rect, cpu: &CPU<T>) -> Paragraph<'_> {
fn next_instruction_block<T: AddrBus>(next: Rect, cpu: &CPU<T>) -> Paragraph<'_> {
let next_height = next.height as usize;
let mut next_inst = cpu.next_n_instructions(next_height - 1);
let _ = next_inst.remove(0);
Expand Down Expand Up @@ -253,12 +253,7 @@ impl ViewState {
frame.render_widget(paragraph, current_block);
}

pub fn ui<T: MmapPeripheral>(
&mut self,
f: &mut Frame,
cpu: &CPU<T>,
uart_rx: &mpsc::Receiver<u8>,
) {
pub fn ui<T: AddrBus>(&mut self, f: &mut Frame, cpu: &CPU<T>, uart_rx: &mpsc::Receiver<u8>) {
let size = f.size();

let chunks = Layout::default()
Expand Down
59 changes: 37 additions & 22 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ struct Args {
#[arg(long, default_value_t = false, verbatim_doc_comment)]
headless: bool,

/// If set, connects UART TX/RX to the specified unix socket.
/// If not set, the UART will be mapped to stdio.
#[arg(long, verbatim_doc_comment, requires("headless"))]
uart_socket: Option<std::path::PathBuf>,
/// If set, connects UART0 TX/RX to the specified unix socket.
/// If not set, the UART0 will be mapped to stdio.
#[arg(long, verbatim_doc_comment)]
uart0: Option<std::path::PathBuf>,

/// If set, connects UART1 TX/RX to the specified unix socket.
/// If not set, the UART1 will not be accessible.
#[arg(long, verbatim_doc_comment)]
uart1: Option<std::path::PathBuf>,

/// If set, the emulation result will be checked.
///
Expand Down Expand Up @@ -52,7 +57,8 @@ struct Args {
/// Longterm goal is having a `Config` struct that can be used to save & replay the emulator
pub struct Config {
pub headless: bool,
pub uart_socket: Option<std::path::PathBuf>,
pub uart0: Option<std::path::PathBuf>,
pub uart1: Option<std::path::PathBuf>,
pub testing: bool,
pub bin: bool,
pub entryaddress: usize,
Expand All @@ -66,27 +72,16 @@ impl Config {
let path = args.file;
let file =
std::fs::read(&path).context(format!("Could not read file {}", path.display()))?;
if let Some(ref socket_path) = args.uart_socket {
if socket_path.exists() {
let attr = std::fs::metadata(socket_path).context(format!(
"Unable to create unixsocket for the UART backend: {}",
socket_path.display()
))?;
if attr.file_type().is_socket() {
let _ = std::fs::remove_file(socket_path);
} else {
return Err(anyhow!(std::io::ErrorKind::AlreadyExists)).context(format!(
"Unable to create unixsocket for the UART backend: {}",
socket_path.display()
));
}
}
}

clear_socket(args.uart0.as_ref())?;
clear_socket(args.uart1.as_ref())?;

let entryaddress = usize_from_str(&args.entryaddress);
let baseaddress = usize_from_str(&args.baseaddress);
Ok(Self {
headless: args.headless,
uart_socket: args.uart_socket,
uart0: args.uart0,
uart1: args.uart1,
testing: args.testing,
bin: args.bin,
entryaddress,
Expand All @@ -96,6 +91,26 @@ impl Config {
}
}

fn clear_socket(uart_path: Option<&std::path::PathBuf>) -> anyhow::Result<()> {
if let Some(ref socket_path) = uart_path {
if socket_path.exists() {
let attr = std::fs::metadata(socket_path).context(format!(
"Unable to create unixsocket for the UART backend: {}",
socket_path.display()
))?;
if attr.file_type().is_socket() {
let _ = std::fs::remove_file(socket_path);
} else {
return Err(anyhow!(std::io::ErrorKind::AlreadyExists)).context(format!(
"Unable to create unixsocket for the UART backend: {}",
socket_path.display()
));
}
}
}
Ok(())
}

fn usize_from_str(text: &str) -> usize {
if text.starts_with("0x") {
usize::from_str_radix(text.trim_start_matches("0x"), 16).unwrap()
Expand Down
18 changes: 9 additions & 9 deletions src/cpu/executer.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
//! This file is scoped to a single function: `exec()`.
use std::cmp::{max, min};

use super::CPU;
use crate::{
instructions::{sign_extend, Instruction, RS1value, RS2value},
periph::MmapPeripheral,
};
use super::{AddrBus, CPU};
use crate::instructions::{sign_extend, Instruction, RS1value, RS2value};

macro_rules! add_signed {
($unsigned:expr, $signed:expr) => {{
Expand All @@ -17,7 +14,7 @@ macro_rules! add_signed {
}};
}

impl<T: MmapPeripheral> CPU<T> {
impl<T: AddrBus> CPU<T> {
/// Executes one instruction.
#[allow(clippy::too_many_lines)]
pub fn exec(
Expand Down Expand Up @@ -439,7 +436,8 @@ impl<T: MmapPeripheral> CPU<T> {
let addr = rs1 as usize;
let value = self.memory.read_word(addr)?;
self.register.write(rdindex, value);
self.memory.reservation = Some((addr, value));
//self.memory.reservation = Some((addr, value));
self.memory.set_reservation(addr, value);
}
Instruction::SCW(rdindex, rs1index, rs2index) => {
let rs1: RS1value = self.register.read(rs1index);
Expand All @@ -449,13 +447,15 @@ impl<T: MmapPeripheral> CPU<T> {
let value = self.memory.read_word(addr)?;

self.register.write(rdindex, 1);
if let Some(reservation) = self.memory.reservation {
//if let Some(reservation) = self.memory.reservation {
if let Some(reservation) = self.memory.get_reservation() {
if reservation.0 == addr && reservation.1 == value {
self.memory.write_word(addr, rs2)?;
self.register.write(rdindex, 0);
}
}
self.memory.reservation = None;
//self.memory.reservation = None;
self.memory.del_reservation();
}
Instruction::AMOSWAPW(rdindex, rs1index, rs2index) => {
let addr_rs1: RS1value = self.register.read(rs1index);
Expand Down
Loading