Skip to content

Commit

Permalink
[ui] refactor tui
Browse files Browse the repository at this point in the history
  • Loading branch information
Clo91eaf committed May 11, 2024
1 parent 2f77cd2 commit 0697f60
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 234 deletions.
43 changes: 29 additions & 14 deletions src/emulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ impl Emulator {

/// Start executing the emulator with difftest and tui.
pub fn start_diff_tui(&mut self, terminal: &mut Tui) {
self.ui.selected_tab.ui_buffer.diff.push("running".to_string());
self.ui.selected_tab.diff_buffer.diff.push("running".to_string());
let mut last_diff = DebugInfo::default();

while !self.ui.cmd.exit {
Expand All @@ -271,7 +271,7 @@ impl Emulator {
self
.ui
.selected_tab
.ui_buffer
.diff_buffer
.inst
.push(format!("pc: {:#x}, inst: {}", pc, self.cpu.inst));

Expand All @@ -280,7 +280,7 @@ impl Emulator {
self
.ui
.selected_tab
.ui_buffer
.diff_buffer
.diff
.push(format!("fatal pc: {:#x}, trap {:#?}", self.cpu.pc, trap));

Expand All @@ -297,16 +297,16 @@ impl Emulator {
self
.ui
.selected_tab
.ui_buffer
.diff_buffer
.cpu
.push(format!("record: true, pc: {:#x}, inst: {}", pc, self.cpu.inst));
.push(format!("record: true, pc: {:#x}, inst: {}", pc, self.cpu.inst));
break;
}
None => {
self
.ui
.selected_tab
.ui_buffer
.diff_buffer
.cpu
.push(format!("record: false, pc: {:#x}, inst: {}", pc, self.cpu.inst));
}
Expand All @@ -329,7 +329,7 @@ impl Emulator {
// should be `Exception::InstructionAccessFault`.
dut.data = self.cpu.bus.read(p_addr, crate::cpu::DOUBLEWORD).unwrap();

self.ui.selected_tab.ui_buffer.dut.push(format!(
self.ui.selected_tab.diff_buffer.dut.push(format!(
"{}, data_sram: addr: {:#x}, data: {:#018x}",
dut.ticks, data_sram.addr, dut.data
))
Expand All @@ -345,7 +345,7 @@ impl Emulator {
// should be `Exception::InstructionAccessFault`.
dut.inst = self.cpu.bus.read(p_pc, crate::cpu::WORD).unwrap() as u32;

self.ui.selected_tab.ui_buffer.dut.push(format!(
self.ui.selected_tab.diff_buffer.dut.push(format!(
"{}, inst_sram: pc: {:#x}, inst: {:#010x}",
dut.ticks, inst_sram.addr, dut.inst
))
Expand All @@ -356,7 +356,7 @@ impl Emulator {
break;
}
}
self.ui.selected_tab.ui_buffer.dut.push(format!(
self.ui.selected_tab.diff_buffer.dut.push(format!(
"{}, pc: {:#010x} wnum: {} wdata: {:#018x}",
dut.ticks,
dut.top.debug_pc(),
Expand All @@ -366,17 +366,32 @@ impl Emulator {

// ==================== diff ====================
if cpu_diff != dut_diff {
self.ui.selected_tab.ui_buffer.diff.clear();
self.ui.selected_tab.diff_buffer.diff.clear();

self
.ui
.selected_tab
.ui_buffer
.diff_buffer
.diff
.push("difftest failed. press 'q' or 'Q' to quit. ".to_string());
self.ui.selected_tab.ui_buffer.diff.push(format!("last: {}", last_diff));
self.ui.selected_tab.ui_buffer.diff.push(format!("cpu : {}", cpu_diff));
self.ui.selected_tab.ui_buffer.diff.push(format!("dut : {}", dut_diff));
self
.ui
.selected_tab
.diff_buffer
.diff
.push(format!("last: {}", last_diff));
self
.ui
.selected_tab
.diff_buffer
.diff
.push(format!("cpu : {}", cpu_diff));
self
.ui
.selected_tab
.diff_buffer
.diff
.push(format!("dut : {}", dut_diff));

self.quit(terminal);

Expand Down
228 changes: 8 additions & 220 deletions src/tui.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::io::{self, stdout, Stdout};
use std::{collections::VecDeque, fmt};

use crossterm::{execute, terminal::*};
use ratatui::{prelude::*, style::palette::tailwind, widgets::*};
use strum::{Display, EnumIter, FromRepr, IntoEnumIterator};
use ratatui::{prelude::*, widgets::*};

mod selected_tab;
use selected_tab::{SelectedTab, SelectedTabEnum};
use strum::IntoEnumIterator;

/// A type alias for the terminal type used in this application
pub type Tui = Terminal<CrosstermBackend<Stdout>>;
Expand All @@ -22,223 +24,12 @@ pub fn restore() -> io::Result<()> {
Ok(())
}

const INST_BUFFER_SIZE: usize = 10;
const CPU_BUFFER_SIZE: usize = 10;
const DUT_BUFFER_SIZE: usize = 10;
const DIFF_BUFFER_SIZE: usize = 5;

#[derive(Clone)]
pub struct InfoBuffer {
info: VecDeque<String>,
size: usize,
}

impl InfoBuffer {
fn new(size: usize) -> Self {
Self {
info: VecDeque::new(),
size,
}
}

pub fn push(&mut self, info: String) {
if self.info.len() >= self.size {
self.info.pop_front();
}
self.info.push_back(info);
}

pub fn clear(&mut self) {
self.info.clear();
}
}

impl fmt::Display for InfoBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for info in &self.info {
write!(f, "{}\n", info)?;
}
Ok(())
}
}

#[derive(Clone)]
pub struct UIBuffer {
pub inst: InfoBuffer,
pub cpu: InfoBuffer,
pub dut: InfoBuffer,
pub diff: InfoBuffer,
}

impl UIBuffer {
pub fn new() -> Self {
UIBuffer {
inst: InfoBuffer::new(INST_BUFFER_SIZE),
cpu: InfoBuffer::new(CPU_BUFFER_SIZE),
dut: InfoBuffer::new(DUT_BUFFER_SIZE),
diff: InfoBuffer::new(DIFF_BUFFER_SIZE),
}
}
}

#[derive(Default)]
pub struct UICommand {
pub r#continue: bool,
pub exit: bool,
}

#[derive(Default, Clone, Copy, Display, FromRepr, EnumIter)]
pub enum SelectedTabEnum {
#[default]
#[strum(to_string = "Main")]
Main,
#[strum(to_string = "Trace")]
Trace,
#[strum(to_string = "Difftest")]
Difftest,
}

#[derive(Clone)]
pub struct SelectedTab {
pub ui_buffer: UIBuffer,
state: SelectedTabEnum,
}

impl SelectedTab {
pub fn new() -> Self {
SelectedTab {
ui_buffer: UIBuffer::new(),
state: SelectedTabEnum::default(),
}
}
}

impl SelectedTabEnum {
fn title(self) -> Line<'static> {
format!(" {self} ")
.fg(tailwind::SLATE.c200)
.bg(self.palette().c900)
.into()
}

/// Get the previous tab, if there is no previous tab return the current tab.
pub fn previous(self) -> Self {
let current_index: usize = self as usize;
let previous_index = current_index.saturating_sub(1);
Self::from_repr(previous_index).unwrap_or(self)
}

/// Get the next tab, if there is no next tab return the current tab.
pub fn next(self) -> Self {
let current_index = self as usize;
let next_index = current_index.saturating_add(1);
Self::from_repr(next_index).unwrap_or(self)
}

const fn palette(self) -> tailwind::Palette {
match self {
Self::Main => tailwind::BLUE,
Self::Trace => tailwind::EMERALD,
Self::Difftest => tailwind::INDIGO,
}
}
}

impl Widget for SelectedTab {
fn render(self, area: Rect, buf: &mut Buffer) {
// in a real app these might be separate widgets
match self.state {
SelectedTabEnum::Main => self.render_main(area, buf),
SelectedTabEnum::Trace => self.render_trace(area, buf),
SelectedTabEnum::Difftest => self.render_difftest(area, buf),
}
}
}

impl SelectedTab {
fn render_main(self, area: Rect, buf: &mut Buffer) {
Paragraph::new("Welcome to the Ratatui tabs example!")
.block(self.block())
.render(area, buf);
}

fn render_trace(self, area: Rect, buf: &mut Buffer) {
Paragraph::new("Look! I'm different than others!")
.block(self.block())
.render(area, buf);
}

fn render_difftest(self, area: Rect, buf: &mut Buffer) {
// layout
let layout_vertical = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![
Constraint::Percentage(40),
Constraint::Percentage(40),
Constraint::Percentage(20),
])
.split(area);

let layout_horizontal = Layout::default()
.direction(Direction::Horizontal)
.constraints(vec![Constraint::Percentage(50), Constraint::Percentage(50)])
.split(layout_vertical[1]);

// render_frame
Paragraph::new(self.ui_buffer.inst.to_string())
.block(
Block::bordered()
.title("Instructions")
.title_alignment(Alignment::Left)
.border_type(BorderType::Rounded),
)
.style(Style::default().fg(Color::Cyan))
.left_aligned()
.render(layout_vertical[0], buf);

Paragraph::new(self.ui_buffer.cpu.to_string())
.block(
Block::bordered()
.title("CPU")
.title_alignment(Alignment::Left)
.border_type(BorderType::Rounded),
)
.style(Style::default().fg(Color::Cyan))
.left_aligned()
.render(layout_horizontal[0], buf);

Paragraph::new(self.ui_buffer.dut.to_string())
.block(
Block::bordered()
.title("DUT")
.title_alignment(Alignment::Left)
.border_type(BorderType::Rounded),
)
.style(Style::default().fg(Color::Cyan))
.left_aligned()
.render(layout_horizontal[1], buf);

Paragraph::new(self.ui_buffer.diff.to_string())
.block(
Block::bordered()
.title("Difftest Status")
.title_alignment(Alignment::Center)
.border_type(BorderType::Rounded),
)
.style(Style::default().fg(Color::Cyan))
.centered()
.render(layout_vertical[2], buf)
}

/// A block surrounding the tab's content
fn block(self) -> Block<'static> {
Block::bordered()
.border_set(symbols::border::PROPORTIONAL_TALL)
.padding(Padding::horizontal(1))
.border_style(self.state.palette().c700)
}
}

pub struct UI {
pub cmd: UICommand,
pub selected_tab: SelectedTab,
Expand Down Expand Up @@ -276,12 +67,9 @@ impl UI {
impl Widget for &UI {
fn render(self, area: Rect, buf: &mut Buffer) {
let layout_vertical = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![
Constraint::Percentage(5),
Constraint::Percentage(95),
])
.split(area);
.direction(Direction::Vertical)
.constraints(vec![Constraint::Percentage(5), Constraint::Percentage(95)])
.split(area);

self.render_tabs(layout_vertical[0], buf);
self.selected_tab.clone().render(layout_vertical[1], buf);
Expand Down
Loading

0 comments on commit 0697f60

Please sign in to comment.