commit 6c9a8b09d85d15e9abef421dbeeb198ba0ac075e
parent 223b2e994114d79ebbea84c66ccc70089adff096
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date: Sun, 16 Oct 2022 17:26:29 -0700
Pipeline suspension
Diffstat:
M | src/alu.rs | | | 15 | +++++++++++++++ |
M | src/data_path.rs | | | 108 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- |
M | src/instruction.rs | | | 77 | +++-------------------------------------------------------------------------- |
M | src/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,