riscii

An emulator for the RISC II
Log | Files | Refs | LICENSE

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:
Msrc/data_path.rs | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/instruction.rs | 86++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/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,