Class: PipelineSim

Inherits:
Object
  • Object
show all
Defined in:
pipeline_simulation.rb

Overview

This class represents a simulation of a computer pipeline. Initialization:

simulation = PipelineSim.new(starting_address)
simulation.runthrough(instructions)

Definition of parameters:

starting_address: starting address of the first instruction (in hex)
instructions: array of mips instructions, in hex

Example:

> instructions = ["0x00a63820",
                  "0x8d0f0004",
                  "0xad09fffc",
                  "0x00625022",
                  "0x00000000",
                  "0x00000000",
                  "0x00000000",
                  "0x00000000"]
> simulation = PipelineSim.new("0x70000")
> simulation.runthrough(instructions)

Author:

  • Jennifer Konikowski <jmkoni@icloud.com>

Defined Under Namespace

Classes: MainMem, PipelineRegister, Regs

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(starting_address) ⇒ PipelineSim

Note:

With this simulation, there are 32 registers and a 1K main memory

Initializes new PipelineSim object

Examples:

Create a new pipeline simulation

simulation = PipelineSim.new("0x70000")

Parameters:

  • starting_address (String)

    starting address in hex



52
53
54
55
56
57
58
59
60
61
# File 'pipeline_simulation.rb', line 52

def initialize(starting_address)
  @mainMem = MainMem.new('7FF'.to_i(16))
  @regs = Regs.new(32)
  @cycle_num = 0
  @starting_address = starting_address
  @IfIdRegister = PipelineRegister.new("IF/ID Register", {instruction: "0x00000000"})
  @IdExRegister = PipelineRegister.new("ID/EX Register", {control: "000000000"})
  @ExMemRegister = PipelineRegister.new("EX/MEM Register", {control: "000000000"})
  @MemWbRegister = PipelineRegister.new("MEM/WB Register", {control: "000000000"})
end

Instance Attribute Details

#cycle_numInteger

Returns (and sets) cycle number

Returns:

  • (Integer)

    current cycle number



45
46
47
# File 'pipeline_simulation.rb', line 45

def cycle_num
  @cycle_num
end

#ExMemRegisterPipelineRegister

Returns (and sets) current content of the EX/MEM Register

Returns:

  • (PipelineRegister)

    contents of pipeline register, including title, read, and write



30
31
32
# File 'pipeline_simulation.rb', line 30

def ExMemRegister
  @ExMemRegister
end

#IdExRegisterPipelineRegister

Returns (and sets) current content of the ID/EX Register

Returns:

  • (PipelineRegister)

    contents of pipeline register, including title, read, and write



27
28
29
# File 'pipeline_simulation.rb', line 27

def IdExRegister
  @IdExRegister
end

#IfIdRegisterPipelineRegister

Returns (and sets) current content of the IF/ID Register

Returns:

  • (PipelineRegister)

    contents of pipeline register, including title, read, and write



24
25
26
# File 'pipeline_simulation.rb', line 24

def IfIdRegister
  @IfIdRegister
end

#mainMemMainMem

Returns (and sets) main memory

Returns:

  • (MainMem)

    representation of current main memory



36
37
38
# File 'pipeline_simulation.rb', line 36

def mainMem
  @mainMem
end

#MemWbRegisterPipelineRegister

Returns (and sets) current content of the MEM/WB Register

Returns:

  • (PipelineRegister)

    contents of pipeline register, including title, read, and write



33
34
35
# File 'pipeline_simulation.rb', line 33

def MemWbRegister
  @MemWbRegister
end

#regsRegs

Returns (and sets) registers

Returns:

  • (Regs)

    representation of current registers



39
40
41
# File 'pipeline_simulation.rb', line 39

def regs
  @regs
end

#starting_addressString (readonly)

Returns starting_address

Returns:

  • (String)

    starting address in hex



42
43
44
# File 'pipeline_simulation.rb', line 42

def starting_address
  @starting_address
end

Instance Method Details

#copyWriteToReadvoid

Note:

So it doesn't use the exact same hash object, we use #clone to copy from write to read.

This method returns an undefined value.

Copies the write side of each PipelineRegister to the read side of that register.



351
352
353
354
355
356
# File 'pipeline_simulation.rb', line 351

def copyWriteToRead
  [@IfIdRegister, @IdExRegister, @ExMemRegister, @MemWbRegister].each do |register|
    register.read = register.write.clone
    register.read[:control] = register.write[:control].clone if register.write[:control]
  end
end

#exStagevoid

This method returns an undefined value.

exStage: Execute Performs the instruction from the read side of the ID/EX Register and writes the results to the write side of the ID/EX Register.



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'pipeline_simulation.rb', line 213

def exStage
  # noop
  if @IdExRegister.read[:control] == "000000000"
    @ExMemRegister.write = {control: "000000000" }
  else
    # if this is the first one, set control to a hash
    if @ExMemRegister.write[:control] == "000000000"
      @ExMemRegister.write[:control] = {}
    end
    # copy control variables that need to go to Ex/Mem to Ex/Mem write
    @ExMemRegister.write[:control][:memWrite] = @IdExRegister.read[:control][:memWrite]
    @ExMemRegister.write[:control][:memRead] = @IdExRegister.read[:control][:memRead]
    @ExMemRegister.write[:control][:memToReg] = @IdExRegister.read[:control][:memToReg]
    @ExMemRegister.write[:control][:regWrite] = @IdExRegister.read[:control][:regWrite]
    @ExMemRegister.write[:control][:branch] = @IdExRegister.read[:control][:branch]
    @ExMemRegister.write[:incrPC] = @IdExRegister.read[:incrPC]
    # read in needed variables for later execution
    aLUSrc = @IdExRegister.read[:control][:aLUSrc]
    aLUOp = @IdExRegister.read[:control][:aLUOp]
    reg1 = @IdExRegister.read[:readReg1Value].to_i(16)
    reg2 = @IdExRegister.read[:readReg2Value].to_i(16)
    regDest = @IdExRegister.read[:control][:regDest]

    # if the first register, use 20-16
    # if the second, use 15-11
    # otherwise, it shouldn't be writing to a register, so don't care
    if regDest == 0
      @ExMemRegister.write[:writeRegNum] = @IdExRegister.read[:writeReg_20_16]
    elsif regDest == 1
      @ExMemRegister.write[:writeRegNum] = @IdExRegister.read[:writeReg_15_11]
    else
      @ExMemRegister.write[:writeRegNum] = "X"
    end

    # calculate ALUResult
    if @IdExRegister.read[:function] == "22"
      # sub
      aLUResult = (reg1 - reg2).to_s(16)
    elsif @IdExRegister.read[:function] == "20"
      # add
      aLUResult = (reg2 + reg1).to_s(16)
    else
      # lb & sb
      aLUResult = (reg1 + convert_to_signed_binary(@IdExRegister.read[:sEOffset].to_i(16).to_s(2))).to_s(16)
    end
    # this project is not handling branches
    @ExMemRegister.write[:calcBTA] = "X"
    @ExMemRegister.write[:zero] = "F"
    @ExMemRegister.write[:aLUResult] = aLUResult
    @ExMemRegister.write[:sWValue] = reg2.to_s(16)
  end
end

#idStagevoid

Note:

Determines whether instruction is r-format or i-format and uses the relevant function to further decode instructions

This method returns an undefined value.

idStage: Instruction Decode Reads the instruction from the read side of the IF/ID Register, decodes it, and writes the results to the write side of the ID/EX Register.



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'pipeline_simulation.rb', line 185

def idStage
  instruction = @IfIdRegister.read[:instruction]
  address = @IfIdRegister.read[:incrPC]
  if instruction.to_i(16) == 0
    # this is a noop
    @IdExRegister.write = {control: "000000000"}
  else
    # convert to binary
    binary_instr = sprintf("%b", instruction).rjust(32, '0')
    first_six = binary_instr[-32..-27].to_i(2)
    # determine if r or i, then decode
    if first_six == 0
      translate_r_format(binary_instr)
    else
      translate_i_format(binary_instr, address)
    end
    # regardless of type, add write/read values and incrPC
    @IdExRegister.write[:writeReg_15_11] = binary_instr[-16..-12].to_i(2)
    @IdExRegister.write[:writeReg_20_16] = binary_instr[-21..-17].to_i(2)
    @IdExRegister.write[:readReg1Value] = @regs.content[binary_instr[-26..-22].to_i(2)]
    @IdExRegister.write[:readReg2Value] = @regs.content[binary_instr[-21..-17].to_i(2)]
    @IdExRegister.write[:incrPC] = address if address
  end
end

#ifStage(instruction) ⇒ void

Note:

If the IF/ID Register already has an incrPC value, then increase it by 4. Otherwise, take the starting address

This method returns an undefined value.

ifStage: Instruction Fetch Fetch the next instruction and write it to the write side of the IF/ID Register

Parameters:

  • instruction (String)

    a MIPS instruction in hex



172
173
174
175
176
177
178
179
# File 'pipeline_simulation.rb', line 172

def ifStage(instruction)
  @IfIdRegister.write = { instruction: instruction }
  if @IfIdRegister.read[:incrPC]
    @IfIdRegister.write[:incrPC] = (@IfIdRegister.read[:incrPC].to_i(16) + 4).to_s(16)
  else
    @IfIdRegister.write[:incrPC] = (@starting_address.to_i(16) + 4).to_s(16)
  end
end

#memStagevoid

This method returns an undefined value.

memStage: Memory Access If the instruction is a load, then we get the data from the address calculated in the Execute stage and store it in the register. If it is a store, then we store the result in main memory.



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'pipeline_simulation.rb', line 269

def memStage
  # noop
  if @ExMemRegister.read[:control] == "000000000"
    @MemWbRegister.write = {control: "000000000" }
  else
    # if this is the first one, set control to a hash
    if @MemWbRegister.write[:control] == "000000000"
      @MemWbRegister.write[:control] = {}
    end
    memRead = @ExMemRegister.read[:control][:memRead]
    memWrite = @ExMemRegister.read[:control][:memWrite]
    if memRead == 1
      @MemWbRegister.write[:lWDataValue] = @mainMem.content[@ExMemRegister.read[:aLUResult].to_i(16)]
    elsif memWrite == 1
      @mainMem.content[@ExMemRegister.read[:aLUResult].to_i(16)] = @ExMemRegister.read[:sWValue]
    else
      @MemWbRegister.write[:lWDataValue] = "X"
    end
    @MemWbRegister.write[:aLUResult] = @ExMemRegister.read[:aLUResult]
    @MemWbRegister.write[:writeRegNum] = @ExMemRegister.read[:writeRegNum]
    @MemWbRegister.write[:control][:memToReg] = @ExMemRegister.read[:control][:memToReg]
    @MemWbRegister.write[:control][:regWrite] = @ExMemRegister.read[:control][:regWrite]
  end
end

#printOutEverythingvoid

This method returns an undefined value.

Prints out current clock cycle, registers, and all contents of the pipeline registers.



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'pipeline_simulation.rb', line 315

def printOutEverything
  puts "-----------------"
  puts "| Clock Cycle #{@cycle_num} |"
  puts "-----------------"
  @regs.show
  puts ""
  puts ""
  [@IfIdRegister, @IdExRegister, @ExMemRegister, @MemWbRegister].each do |register|
    [:write, :read].each do |side|
      puts register.title + " " + side.to_s.capitalize
      puts "--------------------"
      other_keys = ""
      # for each side, get the keys and values
      register.send(side).each do |key, value|
        # if it is the control hash
        if value.is_a?(Hash)
          control = key.to_s.capitalize + ": "
          value.each do |key, value|
            control += key.to_s + " = " + value.to_s + ", "
          end
          puts control
        # otherwise, it's just normal contents
        else
          other_keys += key.to_s + " = " + value.to_s + "  "
        end
      end
      puts other_keys
      puts ""
    end
    puts ""
  end
end

#runthrough(instructions) ⇒ Object

MEM/WB Register Read

--------------------
control = 000000000


153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'pipeline_simulation.rb', line 153

def runthrough(instructions)
  @cycle_num += 1
  instructions.each do | instruction |
    ifStage(instruction)
    idStage
    exStage
    memStage
    wbStage
    printOutEverything
    copyWriteToRead
    @cycle_num += 1
  end
end

#wbStagevoid

This method returns an undefined value.

wbStage: Register Write Back If writing to registers (add, sub, or load), then write the given value to the register.

Raises:

  • (ArgumentError)

    expected 1 or 0 for memToReg



298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'pipeline_simulation.rb', line 298

def wbStage
  return if @MemWbRegister.read[:control].is_a? String
  regWrite = @MemWbRegister.read[:control][:regWrite]
  memToReg = @MemWbRegister.read[:control][:memToReg]
  if regWrite == 1
    if memToReg == 1
      @regs.content[@MemWbRegister.read[:writeRegNum]] = @MemWbRegister.read[:lWDataValue]
    elsif memToReg == 0
      @regs.content[@MemWbRegister.read[:writeRegNum]] = @MemWbRegister.read[:aLUResult]
    else
      raise ArgumentError.new("Expecting 1 or 0 for memToReg. Got #{memToReg}.")
    end
  end
end