commit 223b2e994114d79ebbea84c66ccc70089adff096
parent 84e7b944e169cde570e239e4f13f8786a462908c
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date: Sun, 16 Oct 2022 15:05:24 -0700
ALU shift, pipeline moves latches, more control bits
Diffstat:
M | src/data_path.rs | | | 108 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ |
M | src/instruction.rs | | | 86 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
M | src/system.rs | | | 12 | ++++++++++-- |
3 files changed, 193 insertions(+), 13 deletions(-)
diff --git a/src/data_path.rs b/src/data_path.rs
@@ -1,5 +1,4 @@
// RISC II emulated data path.
-// See `decode.rs` for the first step, and `commit.rs` for the third step.
// (C) Ryan Jeffrey <ryan@ryanmj.xyz>, 2022
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
@@ -25,6 +24,20 @@ use util::Result;
use crate::shifter::Shifter;
+pub type MicroOp = fn(dp: &mut DataPath);
+
+#[derive(Debug, Clone)]
+pub struct Control {
+ pub long: bool,
+ pub immediate: bool,
+ pub memory: bool,
+ pub store: bool,
+ pub pc_relative: bool,
+ pub signed_load: bool,
+ pub conditional: bool,
+ pub dest_is_psw: bool,
+}
+
/// RISC II emulated data path.
#[derive(Debug, Clone)]
pub struct DataPath {
@@ -88,6 +101,11 @@ pub struct DataPath {
imm_flag1: bool,
/// Immediate flag of the instruction (for currently executing instruction).
imm_flag2: bool,
+
+ /// Control bits.
+ control1: Control,
+ control2: Control,
+ control3: Control,
}
// Impls.
@@ -126,6 +144,9 @@ impl DataPath {
scc_flag3: false,
imm_flag1: false,
imm_flag2: false,
+ control1: Control::new(),
+ control2: Control::new(),
+ control3: Control::new(),
})
}
@@ -137,13 +158,18 @@ impl DataPath {
}
pub fn route_regs_to_alu(&mut self) {
- let src1 = self.rs1_2;
- let src2 = self.rs1_2;
- let cwp = self.psw.get_cwp();
- let read1 = self.regs.read(src1, cwp);
- let read2 = self.regs.read(src2, cwp);
- self.alu.ai = read1;
- self.alu.bi = read2;
+ if self.control1.pc_relative {
+ self.alu.ai = self.pc;
+ } else {
+ // TODO investigate interrupts. Should src2 be set no matter what?
+ let src1 = self.rs1_2;
+ let src2 = self.rs1_2;
+ let cwp = self.psw.get_cwp();
+ let read1 = self.regs.read(src1, cwp);
+ let read2 = self.regs.read(src2, cwp);
+ self.alu.ai = read1;
+ self.alu.bi = read2;
+ }
}
/// Decode the next instruction's (in `self.pins_in`) source registers.
@@ -175,6 +201,31 @@ impl DataPath {
&self.output_pins
}
+ pub fn route_imm_to_alu(&mut self) {
+ if self.control1.immediate {
+ self.alu.bi = self.dimm;
+ }
+ }
+
+ pub fn shift_pipeline_latches(&mut self) {
+ // Move the control bits.
+ self.control3 = self.control2.clone();
+ self.control2 = self.control1.clone();
+ // Move the destination register.
+ self.rd3 = self.rd2;
+ self.rd2 = self.rd1;
+ // Move the source registers.
+ self.rs1_2 = self.rs1_1;
+ self.rs2_2 = self.rs2_1;
+ // Move the scc flags.
+ self.scc_flag3 = self.scc_flag2;
+ self.scc_flag2 = self.scc_flag1;
+ // Move Imm flag.
+ self.imm_flag2 = self.imm_flag1;
+ // Move the opcode.
+ self.op2 = self.op1;
+ }
+
fn increment_pcs(&mut self) {
self.lstpc = self.pc;
self.pc = self.nxtpc;
@@ -238,6 +289,47 @@ impl DataPath {
pub fn set_psw(&mut self, psw: u16) {
self.psw = ProcessorStatusWord::from_u16(psw);
}
+
+ pub fn decode(&mut self) {
+ self.control1 = decode_opcode(self.pins_in);
+ }
+}
+
+impl Control {
+ pub fn new() -> Self {
+ Self {
+ long: false,
+ immediate: false,
+ memory: false,
+ store: false,
+ pc_relative: false,
+ signed_load: false,
+ conditional: false,
+ dest_is_psw: false,
+ }
+ }
+
+ pub fn init(
+ long: bool,
+ immediate: bool,
+ memory: bool,
+ store: bool,
+ pc_relative: bool,
+ signed_load: bool,
+ conditional: bool,
+ dest_is_psw: bool,
+ ) -> Self {
+ Self {
+ long: long,
+ immediate: immediate,
+ memory: memory,
+ store: store,
+ pc_relative: pc_relative,
+ signed_load: signed_load,
+ conditional: conditional,
+ dest_is_psw: dest_is_psw,
+ }
+ }
}
// Clock notes:
diff --git a/src/instruction.rs b/src/instruction.rs
@@ -16,22 +16,96 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use clock::Phase;
-use data_path::DataPath;
+use data_path::{Control, 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;
pub const RS1_LOC: u32 = 0x7c000;
pub const RS2_LOC: u32 = 0x1f;
pub const IMM19_LOC: u32 = 0x7FFFF;
pub const SHORT_SOURCE_TYPE_LOC: u32 = 0x2000;
+pub const OPCODE_LOC: u32 = 0xFE000000;
// Public functions.
+pub fn decode_opcode(instruction: u32) -> Control {
+ let memory = (instruction & (0b11 << 6) >> 6) == 1;
+ let store = (instruction & (0b111 << 5) >> 5) == 0b11;
+ let pc_relative = (memory && (instruction & 1) == 1)
+ || ((instruction & 0b11 == 0b01) && (instruction & (0b1111 << 3) == 1));
+ let signed_load = (instruction & (0b1111 << 3) == 0b0101) && (instruction & 0b10 == 0b10);
+ let conditional = instruction & (0b11111 << 2) == 0b00011;
+ let mut long = false;
+ let mut immediate = false;
+ let mut dst_is_psw = false;
+
+ let opcode = ((instruction & OPCODE_LOC) >> 25) as u8;
+ // TODO set ALU and shift operation.
+ // Match opcode's prefix.
+ match opcode >> 4 {
+ 0 => match opcode & 0xf {
+ 1 => {
+ // Calli.
+ }
+ 2 => {
+ // GetPSW
+ dst_is_psw = true;
+ }
+ 3 => {
+ // GetLPC
+ }
+ 4 => {
+ // GetLPC
+ }
+ 8 => {
+ // Callx
+ }
+ 9 => {
+ // Callr
+ long = true;
+ immediate = true;
+ }
+ 12 => {
+ // Jmpx
+ }
+ 13 => {
+ // Jmpr
+ long = true;
+ immediate = true;
+ }
+ 14 => {
+ // Ret
+ }
+ 15 => {
+ // Reti
+ }
+ _ => {}
+ },
+
+ _ => {}
+ }
+
+ immediate = if immediate {
+ immediate
+ } else {
+ instruction & SHORT_SOURCE_TYPE_LOC == 0
+ };
+
+ Control::init(
+ long,
+ immediate,
+ memory,
+ store,
+ pc_relative,
+ signed_load,
+ conditional,
+ dst_is_psw,
+ )
+}
+
// Enums and structs.
pub struct MicroOperation(fn(data_path: &mut DataPath) -> Self);
@@ -40,6 +114,12 @@ pub fn noop(dp: &mut DataPath) -> MicroOperation {
MicroOperation::new(noop)
}
+// Instructions change behavior of ALU, shifter, and for DIMM.
+// Also which register is loaded into the ALU (stores load Rd in bi).
+// Loads and stores suspend pipeline for 1 cycle.
+
+//pub fn add_begin(dp: &mut DataPath) -> MicroOperation {}
+
/// Types of conditionals the RISC II supports.
#[derive(PartialEq, Eq, Copy, Clone)]
pub enum Conditional {
diff --git a/src/system.rs b/src/system.rs
@@ -17,7 +17,7 @@
use clock::Clock;
use config::Config;
use cpu::OutputPins;
-use data_path::DataPath;
+use data_path::{Control, DataPath};
use instruction::{noop, MicroOperation};
use memory::Memory;
use util::Result;
@@ -69,7 +69,9 @@ impl System {
let dp = &mut self.data_path;
self.phase = match self.phase {
Phase::One => {
- // Registers are read and then send to the input latches of the ALU.
+ // Tell the pipeline we're moving on to the next instruction.
+ dp.shift_pipeline_latches();
+ // Registers are read and then sent to the input latches of the ALU.
dp.route_regs_to_alu();
Phase::Two
}
@@ -77,6 +79,9 @@ impl System {
// Memory copies output pin data for writing (if any writing is to be done).
dp.get_output_pins_ref().phase_two_copy(&mut self.pins_out);
+ // Route immediate to ALU.
+ dp.route_imm_to_alu();
+
// Route sources and immediate thru shifter.
Phase::Three
}
@@ -99,6 +104,9 @@ impl System {
// In actual RISCII this is where the source and dest registers are decoded
// for the next instruction, but that is unnecessary here.
self.pins_out.address = dp.get_out_address();
+
+ // Decode opcode.
+
Phase::One
}
Phase::Interrupt => Phase::One,