riscii

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

commit 6c9a8b09d85d15e9abef421dbeeb198ba0ac075e
parent 223b2e994114d79ebbea84c66ccc70089adff096
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date:   Sun, 16 Oct 2022 17:26:29 -0700

Pipeline suspension

Diffstat:
Msrc/alu.rs | 15+++++++++++++++
Msrc/data_path.rs | 108++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/instruction.rs | 77+++--------------------------------------------------------------------------
Msrc/system.rs | 41++++++++++++++++++++++++++++-------------
4 files changed, 140 insertions(+), 101 deletions(-)

diff --git a/src/alu.rs b/src/alu.rs @@ -83,6 +83,21 @@ impl ALU { pub fn subci(&self, carry: bool) -> u32 { self.bi - self.ai - (!carry as u32) } + + /// Right logical shift of the input latches. + pub fn shift_right_logical(&self) -> u32 { + self.ai >> self.bi + } + + /// Right arithmetic shift of the input latches. + pub fn shift_right_arithmetic(&self) -> u32 { + ((self.ai as i32) >> self.bi) as u32 + } + + /// Left arithmetic shift of the input latches. + pub fn shift_left_logical(&self) -> u32 { + self.ai << self.bi + } } impl fmt::Display for ALU { diff --git a/src/data_path.rs b/src/data_path.rs @@ -57,8 +57,6 @@ pub struct DataPath { /// (or last attempted to be executed). When running an interrupt `lstpc` /// holds the address of the instruction that was aborted. lstpc: u32, - /// 32 bit memory input pin. For receiving from main memory. - pins_in: u32, /// Pins for communicating with the outside world (memory). output_pins: OutputPins, /// Arithmetic logic unit. @@ -115,8 +113,8 @@ impl DataPath { /// a string on error. /// # Arguments /// * `config` - Emulator configuration. - pub fn new(config: &Config) -> Result<Self> { - Ok(Self { + pub fn new() -> Self { + Self { regs: RegisterFile::new(), psw: ProcessorStatusWord::new(), shifter: Shifter::new(), @@ -137,7 +135,6 @@ impl DataPath { nxtpc: 0, pc: 0, lstpc: 0, - pins_in: 0, output_pins: OutputPins::new(), scc_flag1: false, scc_flag2: false, @@ -147,7 +144,7 @@ impl DataPath { control1: Control::new(), control2: Control::new(), control3: Control::new(), - }) + } } pub fn commit(&mut self) { @@ -163,7 +160,7 @@ impl DataPath { } else { // TODO investigate interrupts. Should src2 be set no matter what? let src1 = self.rs1_2; - let src2 = self.rs1_2; + let src2 = self.rs2_2; let cwp = self.psw.get_cwp(); let read1 = self.regs.read(src1, cwp); let read2 = self.regs.read(src2, cwp); @@ -172,13 +169,8 @@ impl DataPath { } } - /// 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; + self.dimm = value; // Set other latches hooked up to memory data path. self.op1 = ((value & 0xFE000000) >> 25) as u8; self.imm_flag1 = value & SHORT_SOURCE_TYPE_LOC != 0; @@ -224,6 +216,18 @@ impl DataPath { self.imm_flag2 = self.imm_flag1; // Move the opcode. self.op2 = self.op1; + // Move the actual immediate. + self.dimm = if self.control2.long { + // Place into highest 19 bits. + self.imm << 13 + } else { + // Sign extend immediate to 32 bits. + if self.imm & SHORT_IMM_SIGN_LOC != 0 { + self.imm & SHORT_IMM_SIGNEXT_BITS + } else { + self.imm + } + }; } fn increment_pcs(&mut self) { @@ -291,7 +295,83 @@ impl DataPath { } pub fn decode(&mut self) { - self.control1 = decode_opcode(self.pins_in); + let instruction = self.dimm; + 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 + }; + + self.control1 = Control::init( + long, + immediate, + memory, + store, + pc_relative, + signed_load, + conditional, + dst_is_psw, + ); + } + + pub fn current_instruction_is_memory(&self) -> bool { + self.control2.memory } } diff --git a/src/instruction.rs b/src/instruction.rs @@ -28,83 +28,12 @@ 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; +pub const SHORT_IMM_SIGN_LOC: u32 = 0x1000; +pub const SHORT_IMM_SIGNEXT_BITS: u32 = 0xFFFFE000; // 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, - ) -} +pub fn decode_opcode(instruction: u32) -> Control {} // Enums and structs. diff --git a/src/system.rs b/src/system.rs @@ -38,11 +38,13 @@ pub struct System { // TODO move below to an MMU emulator. /// CPU's output pins, input pins for memory. pins_out: OutputPins, + /// True if the pipeline is currently suspended as a result of a memory operation. + pipeline_suspended: bool, } impl System { pub fn new(config: &Config) -> Result<Self> { - let mut dp = DataPath::new(config)?; + let mut dp = DataPath::new(); let nop = noop(&mut dp); Ok(Self { data_path: dp, @@ -51,6 +53,7 @@ impl System { op: nop, phase: Phase::One, pins_out: OutputPins::new(), + pipeline_suspended: false, }) } @@ -69,18 +72,22 @@ impl System { let dp = &mut self.data_path; self.phase = match self.phase { Phase::One => { - // 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(); + if !self.pipeline_suspended { + // 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 } Phase::Two => { // 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(); + if !self.pipeline_suspended { + // Route immediate to ALU. + dp.route_imm_to_alu(); + } // Route sources and immediate thru shifter. Phase::Three @@ -88,16 +95,22 @@ impl System { Phase::Three => { // Finish read from last cycle. let mem = &self.mem; - let addr = self.pins_out.address; // TODO check for invalid address from MMU. - dp.set_input_pins(match mem.get_word(addr) { + dp.set_input_pins(match mem.get_word(self.pins_out.address) { Ok(v) => v, Err(_) => { - eprint!("Bad mem read: {}", addr); + eprint!("Bad mem read: {}", self.pins_out.address); 0 } }); - self.data_path.commit(); + + if self.pipeline_suspended { + self.pipeline_suspended = false; + } else if dp.current_instruction_is_memory() { + // Commit the result of the last instruction. + dp.commit(); + self.pipeline_suspended = true; + } Phase::Four } Phase::Four => { @@ -105,8 +118,10 @@ impl System { // for the next instruction, but that is unnecessary here. self.pins_out.address = dp.get_out_address(); - // Decode opcode. - + if !self.pipeline_suspended { + dp.decode(); + } + // If the instruction was a load, shift the result if necessary. Phase::One } Phase::Interrupt => Phase::One,