Skip to content

Commit

Permalink
Support keyboard input with -d option.
Browse files Browse the repository at this point in the history
  • Loading branch information
moizumi99 committed Aug 29, 2020
1 parent 1998f7b commit 19f3be3
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 62 deletions.
9 changes: 7 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ add_executable(cpu_test
system_call_emulator.cpp system_call_emulator.h
pte.cpp pte.h
Mmu.cpp Mmu.h
riscv_cpu_common.h Disassembler.cpp Disassembler.h PeripheralEmulator.cpp PeripheralEmulator.h)
riscv_cpu_common.h
Disassembler.cpp Disassembler.h
PeripheralEmulator.cpp PeripheralEmulator.h)

add_executable(RISCV_Emulator
bit_tools.cc
Expand All @@ -58,7 +60,10 @@ add_executable(RISCV_Emulator
system_call_emulator.h
pte.cpp pte.h
Mmu.cpp Mmu.h
riscv_cpu_common.h Disassembler.cpp Disassembler.h PeripheralEmulator.cpp PeripheralEmulator.h keyboard.cpp keyboard.h ScreenEmulation.cpp ScreenEmulation.h)
riscv_cpu_common.h
Disassembler.cpp Disassembler.h
PeripheralEmulator.cpp PeripheralEmulator.h
ScreenEmulation.cpp ScreenEmulation.h)

target_link_libraries(RISCV_Emulator ncurses)

Expand Down
60 changes: 46 additions & 14 deletions PeripheralEmulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// Created by moiz on 7/2/20.
//

#include "PeripheralEmulator.h"
#include <cassert>
#include <iostream>
#include "PeripheralEmulator.h"
#include "ScreenEmulation.h"

namespace RISCV_EMULATOR {
Expand All @@ -13,7 +13,7 @@ PeripheralEmulator::PeripheralEmulator(int mxl) : mxl_(mxl) {}

void PeripheralEmulator::SetMemory(std::shared_ptr<MemoryWrapper> memory) { memory_ = memory; }

void PeripheralEmulator::SetDiskImage(std::shared_ptr<std::vector<uint8_t>> disk_image) {disk_image_ = disk_image; };
void PeripheralEmulator::SetDiskImage(std::shared_ptr<std::vector<uint8_t>> disk_image) { disk_image_ = disk_image; };

void PeripheralEmulator::SetHostEmulationEnable(bool enable) { host_emulation_enable_ = enable; }

Expand Down Expand Up @@ -55,10 +55,15 @@ void PeripheralEmulator::CheckDeviceWrite(uint64_t address, int width, uint64_t
}
}

void PeripheralEmulator::MemoryMappedValueUpdate() {
memory_->Write64(kTimerMtime, elapsed_cycles_);
void PeripheralEmulator::CheckDeviceRead(uint64_t address, int width) {
// Check if it reads from UART addresses.
if (kUartBase < address + width && address <= kUartBase) {
uart_read_ = true;
}
}

void PeripheralEmulator::MemoryMappedValueUpdate() { memory_->Write64(kTimerMtime, elapsed_cycles_); }

void PeripheralEmulator::Emulation() {
if (host_emulation_enable_) {
HostEmulation();
Expand Down Expand Up @@ -137,23 +142,50 @@ void PeripheralEmulator::UartInit() {

// Initialize the screen to use ncurse library.
scr_emulation = std::make_unique<ScreenEmulation>();
// No need to call endwin() explicitly afterward because it's called in the destructor.
// No need to call endwin() explicitly afterward because the destructor calls it.
}
void PeripheralEmulator::UartEmulation() {
// UART Rx.
if (uart_write_) {
scr_emulation->putchar(uart_write_value_);
// std::cout << static_cast<char>(uart_write_value_) << std::flush;
uart_write_ = false;
// TODO: Add interrupt processing.
}
// UART Tx.
// std::string input_string;
// std::cin >> input_string;
// if (!input_string.empty()) {
// for (auto s : input_string) {
// uart_queue.push(static_cast<uint8_t>(s));
// }
// }
if (uart_read_) {
ClearUartBuffer();
uart_read_ = false;
}
if (uart_full_) {
return;
}
if (!scr_emulation->CheckInput()) {
return;
}
int key_input = scr_emulation->GetKeyValue();
SetUartBuffer(key_input);
UartInterrupt();
}

void PeripheralEmulator::SetUartBuffer(int key) {
uint8_t uart_lsr_status = memory_->ReadByte(kUartLsr);
uart_lsr_status |= kUartLsrReady;
memory_->WriteByte(kUartRhr, static_cast<uint8_t>(key));
memory_->WriteByte(kUartLsr, uart_lsr_status);
uart_full_ = true;
}

void PeripheralEmulator::ClearUartBuffer() {
uint8_t uart_lsr_status = memory_->ReadByte(kUartLsr);
uart_lsr_status &= ~kUartLsrReady;
memory_->WriteByte(kUartLsr, uart_lsr_status);
uart_full_ = false;
}

void PeripheralEmulator::UartInterrupt() {
constexpr int kUartIrq = 10;
memory_->Write32(kPlicClaimAddress, kUartIrq);
uart_interrupt_ = true;
}

void PeripheralEmulator::TimerTick() {
Expand Down Expand Up @@ -324,7 +356,7 @@ void PeripheralEmulator::disc_access(uint64_t sector, uint64_t buffer_address, u
}

void PeripheralEmulator::process_used_buffer(uint64_t used_buffer_address, uint16_t index) {
// uint16_t flag = memory_->Read16(used_buffer_address);
// uint16_t flag = memory_->Read16(used_buffer_address);
// TODO: Add check of flag.
uint16_t current_used_index = memory_->Read16(used_buffer_address + 2);
memory_->Write32(used_buffer_address + 4 + current_used_index * 8, index);
Expand Down
11 changes: 11 additions & 0 deletions PeripheralEmulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class PeripheralEmulator {
// UART.
static constexpr uint64_t kUartBase = 0x10000000;
static constexpr uint64_t kUartSize = 6;
static constexpr uint8_t kUartLsrReady = 1;
static constexpr uint64_t kUartRhr = kUartBase;
static constexpr uint64_t kUartLsr = kUartBase + 5;

// Timer.
static constexpr uint64_t kTimerBase = 0x2000000;
Expand All @@ -66,6 +69,7 @@ class PeripheralEmulator {

void Initialize();
void CheckDeviceWrite(uint64_t address, int width, uint64_t data);
void CheckDeviceRead(uint64_t address, int width);
void MemoryMappedValueUpdate();

// Host Emulation.
Expand All @@ -87,6 +91,8 @@ class PeripheralEmulator {
void SetDeviceEmulationEnable(bool enable) { device_emulation_enable = enable; }
void UartInit();
void UartEmulation();
bool GetUartInterruptStatus() {return uart_interrupt_; }
void ClearUartInterruptStatus() { uart_interrupt_ = false;}

// Virtio Disk Emulation.
void VirtioInit();
Expand All @@ -108,9 +114,14 @@ class PeripheralEmulator {
bool device_emulation_enable = false;
bool uart_write_ = false;
bool uart_read_ = false;
bool uart_full_ = false;
uint8_t uart_write_value_ = 0;
std::queue<uint8_t> uart_queue;
bool uart_interrupt_ = false;
std::unique_ptr<ScreenEmulation> scr_emulation;
void SetUartBuffer(int key);
void ClearUartBuffer();
void UartInterrupt();

// Timer.
uint64_t elapsed_cycles_ = 0;
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,17 @@ Following system calls are supported with `-e` option with limitation.
_Support of xv6 is still under development._

This emulator is able to run [xv6 for riscv](https://github.com/mit-pdos/xv6-riscv) to show sh prompt.
(But nothing further because no key input is supported yet.)

You first need to compile [xv6 for riscv](https://github.com/mit-pdos/xv6-riscv) with CPUS=1 option.
Then, you need to copy `kernel/kernel` in the Emulator directory.
Also, you need `fs.img` (disk image) from xv6 too. `fs.img` is generated when running xv6 with `QEMU` option.

Run the emulator with `-64 -m -d -s fs.img` option.
Run the emulator with `-64 -m -d -s fs.img` options (64 bit mode, disable machine interrupt delegation, device emulation, load `fs.img` as storage image).

```
$ ./RISCV_Emulator -64 -m -d -s fs.img kernel
```

You will eventually see the prompt (`$`) pops up.
(Support of key input is planned with no commitment date.)
You can run the commands, for example by typing `ls` or `cat README`.
`Ctrl+C` and `Ctrl+P` are not supported yet.
20 changes: 19 additions & 1 deletion RISCV_cpu.cc
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ void RiscvCpu::LoadInstruction(uint32_t instruction, uint32_t rd, uint32_t rs1,
} else if (instruction == INST_LWU) {
load_data &= 0xFFFFFFFF;
}
peripheral_->CheckDeviceRead(address, width);

reg_[rd] = load_data;
}
Expand Down Expand Up @@ -877,6 +878,11 @@ bool RiscvCpu::TimerTick() {
return true;
}

void RiscvCpu::InterruptCheck() {
DiskInterruptCheck();
UartInterruptCheck();
}

void RiscvCpu::DiskInterruptCheck() {
if (virtio_interrupt_) {
virtio_interrupt_ = false;
Expand All @@ -885,6 +891,14 @@ void RiscvCpu::DiskInterruptCheck() {
}
}

void RiscvCpu::UartInterruptCheck() {
if (uart_interrupt_) {
uart_interrupt_ = false;
constexpr int kSupervisorExternalInterrupt = 9;
SetInterruptPending(kSupervisorExternalInterrupt);
}
}

void RiscvCpu::SetInterruptPending(int cause) {
constexpr uint64_t kSet = 1;
mip_ = bitset(mip_, 1, cause, kSet);
Expand Down Expand Up @@ -916,7 +930,7 @@ int RiscvCpu::RunCpu(uint64_t start_pc, bool verbose) {
do {
pc_ = next_pc_;
TimerTick();
DiskInterruptCheck();
InterruptCheck();
if (CheckPendingInterrupt()) {
continue;
}
Expand Down Expand Up @@ -1157,6 +1171,10 @@ void RiscvCpu::PeripheralEmulations() {
peripheral_->ClearInterruptStatus();
virtio_interrupt_ = true;
}
if (peripheral_->GetUartInterruptStatus()) {
peripheral_->ClearUartInterruptStatus();
uart_interrupt_ = true;
}
}

void RiscvCpu::Ecall() {
Expand Down
3 changes: 3 additions & 0 deletions RISCV_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,9 @@ class RiscvCpu {

private:
bool TimerTick();
void InterruptCheck();
void DiskInterruptCheck();
void UartInterruptCheck();
void PeripheralEmulations();
void SetInterruptPending(int cause);
void ClearInterruptPending(int cause);
Expand All @@ -201,6 +203,7 @@ class RiscvCpu {
bool host_emulation_ = false;
bool peripheral_emulation_ = false;
bool virtio_interrupt_ = false;
bool uart_interrupt_ = false;
bool disable_machine_interrupt_delegation_ = false;
uint64_t top_ = 0x80000000;
uint64_t bottom_ = 0x40000000;
Expand Down
24 changes: 20 additions & 4 deletions ScreenEmulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,35 @@ void ScreenEmulation::ScreenInit() {
keypad(stdscr, TRUE);
// Allow scrolling.
scrollok(stdscr, TRUE);
// Don't wait for return.
// Don't wait for key hit.
nodelay(stdscr, TRUE);
}

void ScreenEmulation::ScreenExit() {
endwin();
}

char ScreenEmulation::getchar() {
return getch();
bool ScreenEmulation::CheckInput() {
if (++counter_ < kThreshold) {
return key_valid_;
} else {
counter_ = 0;
}
int c = getch();
if (c == ERR) {
return key_valid_;
}
key_value_ = c;
key_valid_ = true;
return true;
}

void ScreenEmulation::putchar(char c) {
int ScreenEmulation::GetKeyValue() {
key_valid_ = false;
return key_value_;
}

void ScreenEmulation::putchar(int c) {
addch(c);
wrefresh(stdscr);
}
12 changes: 10 additions & 2 deletions ScreenEmulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@
#ifndef ASSEMBLER_TEST_SCREENEMULATION_H
#define ASSEMBLER_TEST_SCREENEMULATION_H

#include "ncurses.h"

class ScreenEmulation {
public:
ScreenEmulation();
~ScreenEmulation();

char getchar();
void putchar(char c);
bool CheckInput();
int GetKeyValue();
void putchar(int c);

int counter_ = 0;
static constexpr int kThreshold = 1000;

private:
int key_value_;
bool key_valid_ = false;
void ScreenInit();
void ScreenExit();
};
Expand Down
24 changes: 0 additions & 24 deletions keyboard.cpp

This file was deleted.

12 changes: 0 additions & 12 deletions keyboard.h

This file was deleted.

0 comments on commit 19f3be3

Please sign in to comment.