commit 883e60896e55db7a2f512ca411fc1036dbc086d4
parent a448398b21b9cf2ec89bf9d7867baa0b3a001e5a
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date: Fri, 14 Oct 2022 15:58:29 -0700
Finish output pins, move clock, etc..
Diffstat:
7 files changed, 261 insertions(+), 40 deletions(-)
diff --git a/src/clock.rs b/src/clock.rs
@@ -14,44 +14,80 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use config::Config;
+use std::fmt;
+use std::time::{Duration, Instant};
+
+use crate::system;
/// Phases for RISCII's multi (4) phase clock non-overlapping clock.
+#[derive(PartialEq, Eq, Clone)]
pub enum Phase {
/// Phase one of the RISCII's clock. During this phase the register file
- /// is read and forwarded to the shifter and ALU.
- One,
+ /// is read and forwarded to the shifter and ALOE.
+ One = 1,
/// Phase two of the RISCII's clock. During this phase the immediate value
/// is routed through the shifter. The destination register for the previous
/// instruction is decoded.
- Two,
+ Two = 2,
/// Phase three of the RISCII's clock. During this phase the ALU computes
/// its result value and the previous instruction's result is written to
/// the destination register.
- Three,
+ Three = 3,
/// Phase four of the RISCII's clock. During this phase the source and destination
/// registers are decoded. Load instructions use the shifter to align data.
- Four,
+ Four = 4,
/// Special interrupt phase TODO.
- Interrupt,
+ Interrupt = 5,
}
#[derive(PartialEq, Eq, Clone)]
pub struct Clock {
rate: u64,
count: u64,
- phase: Phase,
+ last_time: Instant,
+ seconds_coutner: Duration,
}
impl Clock {
- pub fn tick(clock: &mut Self) {
- // TODO cycle accurate clock.
+ pub fn tick(&mut self, phase: Phase) {
+ match phase {
+ Phase::One => {
+ self.count += 1;
+ }
+ _ => {}
+ }
+ }
+
+ pub fn tick_and_wait(&mut self, phase: Phase) {
+ match phase {
+ Phase::One => {
+ self.count += 1;
+ if self.count == self.rate {
+ self.idle_clock();
+ }
+ }
+ _ => {}
+ }
}
pub fn new(config: &Config) -> Self {
Self {
- rate: config.clock_rate,
+ rate: config.get_clock_rate(),
count: 0,
- phase: Phase::One,
+ last_time: Instant::now(),
+ seconds_coutner: Duration::new(0, 0),
+ }
+ }
+
+ fn idle_clock(&mut self) {
+ // Calc curTime - lastTime (in nanoseconds). If less than a second has
+ // passed, sleep until we've reached that next second.
+ const ONE_SECOND: Duration = Duration::from_secs(1);
+ let now = Instant::now();
+ let time_passed = now - self.last_time;
+ if time_passed < ONE_SECOND {
+ std::thread::sleep(time_passed);
+ self.last_time = now + time_passed;
}
}
}
diff --git a/src/config.rs b/src/config.rs
@@ -244,6 +244,10 @@ impl Config {
pub fn is_debug_mode(&self) -> bool {
self.debug_mode
}
+
+ pub fn get_clock_rate(&self) -> u64 {
+ self.clock_rate
+ }
}
// Local functions.
diff --git a/src/cpu.rs b/src/cpu.rs
@@ -84,6 +84,26 @@ pub struct ProcessorStatusWord(u16);
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct RegisterFile([u32; NUM_GLOBALS + NUM_WINDOW_REGISTERS]);
+/// CPU output pins to memory.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct OutputPins {
+ /// 32 bit memory output port. For sending data to memory (address).
+ pub address: u32,
+ /// 32 bit memory output port. For sending data to memory (data).
+ pub data: u32,
+ /// If the current memory write is a word.
+ pub width_code_word: bool,
+ /// If the current memory write is a half word.
+ pub width_code_half: bool,
+ /// If the current memory operation is a write (true) or read (false).
+ pub read_write: bool,
+ /// If the system is currently in system mode.
+ pub system_mode: bool,
+ /// If the read/write data is an instruction (1) or data (0). For writes,
+ /// it is always data (0).
+ pub instr_or_data_write: bool,
+}
+
// Struct implementations.
impl RegisterFile {
@@ -208,9 +228,10 @@ impl RegisterFile {
/// [31-26] -> Ins
/// Anything outside this [0-31] range is an invalid argument.
/// # Arguments
- /// * `which` - Which register. [0-31] are the only valid values.
- /// * `psw` - Processor status object, contains window information.
- pub fn write(&mut self, address: u32, value: u32, cwp: u8) {
+ /// * `address` - Which register. [0-31] are the only valid values.
+ /// * `value` - Value to write into the register.
+ /// * `cwp` - Current window pointer. Used to determine the real address of the register.
+ pub fn write(&mut self, address: u8, value: u32, cwp: u8) {
let addr = address as usize;
let ptr = cwp as usize;
match addr {
@@ -392,6 +413,26 @@ CC Carry: {}",
}
}
+impl OutputPins {
+ pub fn new() -> Self {
+ Self {
+ address: 0,
+ data: 0,
+ width_code_half: false,
+ width_code_word: false,
+ read_write: false,
+ system_mode: false,
+ instr_or_data_write: false,
+ }
+ }
+
+ pub fn phase_two_copy(&self, other: &mut Self) {
+ let addr = other.address;
+ *other = *self;
+ other.address = addr;
+ }
+}
+
// Private functions.
/// Create a descriptive string for the system's privilege state bits.
diff --git a/src/data_path.rs b/src/data_path.rs
@@ -15,7 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use config::Config;
-use cpu::{ProcessorStatusWord, RegisterFile, SIZEOF_INSTRUCTION};
+use cpu::{OutputPins, ProcessorStatusWord, RegisterFile, SIZEOF_INSTRUCTION};
use instruction::noop;
use memory::Memory;
use std::fmt;
@@ -34,18 +34,6 @@ pub struct DataPath {
dst_latch: u32,
/// Source latch for the shifter and ALU.
src_latch: u32,
- /// Next instruction.
- next_instruction: u32,
- /// Destination register address.
- rd: u8,
- /// Source register one.
- ra: u8,
- /// Source register two.
- rb: u8,
- /// Opcode register.
- op: u8,
- /// Immediate register.
- imm: u32,
/// Next program counter, holds the address of the instruction being
/// fetched for the next cycle.
nxtpc: u32,
@@ -58,10 +46,42 @@ pub struct DataPath {
lstpc: u32,
/// 32 bit memory input pin. For receiving from main memory.
pins_in: u32,
- /// 32 bit memory output port. For sending data to memory (address).
- pins_out_addr: u32,
- /// 32 bit memory output port. For sending data to memory (data).
- pins_out_data: u32,
+ /// Pins for communicating with the outside world (memory).
+ output_pins: OutputPins,
+
+ // Control unit latches and registers.
+ /// Data from memory.
+ dimm: u32,
+ /// Immediate register (for instruction being decoded).
+ imm1: u32,
+ /// Immediate register (for currently executing instruction).
+ imm2: u32,
+ /// Byte address register, bottom two bits of memory address being accesses.
+ bar: u8,
+ /// Destination register address (for instruction being decoded).
+ rd1: u8,
+ /// Destination register address (for currently executing instruction).
+ rd2: u8,
+ /// Destination register address (for commiting/previous instruction).
+ rd3: u8,
+ /// Source register one.
+ ra: u8,
+ /// Source register two.
+ rb: u8,
+ /// Opcode register (for instruction being decoded).
+ op1: u8,
+ /// Opcode register (for currently executing instruction).
+ op2: u8,
+ /// SCC flag of the instruction (for instruction being decoded).
+ scc_flag1: bool,
+ /// SCC flag of the instruction (for currently executing instruction).
+ scc_flag2: bool,
+ /// SCC flag of the instruction (for commiting/previous instruction).
+ scc_flag3: bool,
+ /// Immediate flag of the instruction (for instruction being decoded).
+ imm_flag1: bool,
+ /// Immediate flag of the instruction (for currently executing instruction).
+ imm_flag2: bool,
}
// Impls.
@@ -77,21 +97,58 @@ impl DataPath {
psw: ProcessorStatusWord::new(),
src_latch: 0,
dst_latch: 0,
- next_instruction: noop(),
- rd: 0,
+ bar: 0,
+ rd1: 0,
+ rd2: 0,
+ rd3: 0,
ra: 0,
rb: 0,
- op: 0,
- imm: 0,
+ op1: 0,
+ op2: 0,
+ dimm: 0,
+ imm1: 0,
+ imm2: 0,
nxtpc: 0,
pc: 0,
lstpc: 0,
pins_in: 0,
- pins_out_addr: 0,
- pins_out_data: 0,
+ output_pins: OutputPins::new(),
+ scc_flag1: false,
+ scc_flag2: false,
+ scc_flag3: false,
+ imm_flag1: false,
+ imm_flag2: false,
})
}
+ pub fn commit(&mut self) {
+ let dest_value = self.dst_latch;
+ let dest_reg = self.rd3;
+ let cwp = self.psw.get_cwp();
+ self.regs.write(dest_reg, dest_value, cwp);
+ }
+
+ /// Decode the next instruction's (in `self.pins_in`) source registers.
+ pub fn decode_input_regs(&mut self) {
+ let next_instruction = self.pins_in;
+ }
+
+ pub fn set_input_pins(&mut self, value: u32) {
+ self.pins_in = value;
+ }
+
+ pub fn get_out_address(&self) -> u32 {
+ self.output_pins.address
+ }
+
+ pub fn get_out_data(&self) -> u32 {
+ self.output_pins.data
+ }
+
+ pub fn get_output_pins_ref(&self) -> &OutputPins {
+ &self.output_pins
+ }
+
fn increment_pcs(&mut self) {
self.lstpc = self.pc;
self.pc = self.nxtpc;
diff --git a/src/instruction.rs b/src/instruction.rs
@@ -1,4 +1,5 @@
// RISC II cpu instruction info.
+
// "execute" and then "commit".
// (C) Ryan Jeffrey <ryan@ryanmj.xyz>, 2022
// This program is free software: you can redistribute it and/or modify
@@ -14,8 +15,13 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
+use clock::Phase;
+use data_path::DataPath;
use std::fmt;
use std::fmt::LowerHex;
+use std::ops::Fn;
+
+use crate::data_path;
pub const SCC_LOC: u32 = 0x1000000;
pub const DEST_LOC: u32 = 0x00F80000;
@@ -25,8 +31,12 @@ pub const SHORT_SOURCE_TYPE_LOC: u32 = 0x2000;
// Public functions.
-pub fn noop() -> u32 {
- Instruction::And(ShortInstruction::new(false, 0, 0, ShortSource::Imm13(0))).encode()
+// Enums and structs.
+
+pub struct MicroOperation(fn(data_path: &mut DataPath) -> Self);
+
+pub fn noop(dp: &mut DataPath) -> MicroOperation {
+ MicroOperation::new(noop)
}
/// Types of conditionals the RISC II supports.
@@ -572,6 +582,19 @@ impl fmt::Display for Conditional {
}
}
+impl MicroOperation {
+ pub fn new(func: fn(data_path: &mut DataPath) -> Self) -> Self {
+ Self { 0: func }
+ }
+
+ // TODO temporary until implementing Fn becomes stable.
+ pub fn call(&self, data_path: &mut DataPath) -> Self {
+ self.0(data_path)
+ }
+}
+
+// Static functions.
+
fn get_opdata_from_cond(cond: Conditional) -> u8 {
type C = Conditional;
match cond {
diff --git a/src/main.rs b/src/main.rs
@@ -25,6 +25,7 @@ mod encode_test;
mod main_test;
// Modules declared as pub to shut up rust-analyzer about dead code.
+pub mod clock;
pub mod config;
pub mod cpu;
pub mod data_path;
diff --git a/src/system.rs b/src/system.rs
@@ -14,27 +14,86 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
+use clock::Clock;
use config::Config;
+use cpu::OutputPins;
use data_path::DataPath;
+use instruction::{noop, MicroOperation};
use memory::Memory;
use util::Result;
+use crate::clock::Phase;
+
pub struct System {
/// RISCII data path.
data_path: DataPath,
/// Memory state.
mem: Memory,
+ /// External, four phase clock.
+ clock: Clock,
+ /// Next micro operation to perform for the currently executing instruction.
+ op: MicroOperation,
+ /// Current CPU non-overlapping clock phase.
+ phase: Phase,
+ // TODO move below to an MMU emulator.
+ /// CPU's output pins, input pins for memory.
+ pins_out: OutputPins,
}
impl System {
pub fn new(config: &Config) -> Result<Self> {
+ let mut dp = DataPath::new(config)?;
+ let nop = noop(&mut dp);
Ok(Self {
- data_path: DataPath::new(config)?,
+ data_path: dp,
mem: Memory::new(config),
+ clock: Clock::new(config),
+ op: nop,
+ phase: Phase::One,
+ pins_out: OutputPins::new(),
})
}
pub fn get_mem_ref(&mut self) -> &mut Memory {
&mut self.mem
}
+
+ pub fn tick(&mut self) {
+ let cur_phase = self.phase.clone();
+ self.clock.tick_and_wait(cur_phase);
+
+ // Fetch
+ // Execute.
+ // Commit.
+
+ let dp = &mut self.data_path;
+ self.phase = match self.phase {
+ Phase::One => Phase::Two,
+ Phase::Two => {
+ dp.get_output_pins_ref().phase_two_copy(&mut self.pins_out);
+ Phase::Two
+ }
+ Phase::Three => {
+ // Finish read from last cycle.
+ let mem = &self.mem;
+ let addr = self.pins_out.address;
+ // TODO check for invalid address from MMU.
+ self.data_path.set_input_pins(match mem.get_word(addr) {
+ Ok(v) => v,
+ Err(_) => {
+ eprint!("Bad mem read: {}", addr);
+ 0
+ }
+ });
+ self.data_path.commit();
+ Phase::Four
+ }
+ Phase::Four => {
+ dp.decode_input_regs();
+ self.pins_out.address = dp.get_out_address();
+ Phase::One
+ }
+ Phase::Interrupt => Phase::One,
+ };
+ }
}