Skip to content

Non-Pipelined MIPS Simulator, which can be extended to Pipelined design

License

Notifications You must be signed in to change notification settings

AspiringPianist/MIPS_Simulator_Python

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Course Name: EG 212, Computer Architecture - Processor Design

Prakrititz Borah IMT

Unnath Chittimalla IMT

Mannat Kaur Bagga IMT

26 February 2024

Abstract
Our Python-based MIPS simulator provides a platform for simulating the execution of MIPS binary code
generated by external assemblers such as MARS. The simulator includes a processor and a compiler that
can handle binary code, allowing users to write, compile, and execute MIPS programs within the simulated
environment.

Introduction

Our simulator mimics the MIPS architecture using key components like the Program Counter (PC), Instruc-

tion Memory (IM), Register File (RF), Data Memory (DM), Arithmetic Logic Unit (ALU), and Control Unit

(CU). Together, they recreate the behavior of a basic MIPS processor.

We’ve created a user-friendly MIPS simulation environment where users can work with MIPS binary code.

They can input code from external assemblers, and our simulator runs it using the processor model.

We’ve included sample programs in the simulator, covering arithmetic, control flow, memory access, and

data manipulation tasks. These programs showcase MIPS architecture in action and can be run smoothly within

the simulator.

Our simulator supports the simulation of MIPS binary code, allowing users to experiment with code gen-

erated by external tools. This feature enables users to explore MIPS programming concepts in a controlled

setting.

Figure 1: MIPS processor

Processor Implementation

The MIPS simulator is built with different parts that are separate but work together. The main part, known

as the central processing unit (CPU), manages how instructions are carried out by coordinating with other

important components:

  • The Program Counter (PC) keeps track of which instruction is being worked on.
  • The Instruction Memory (IM) stores all the instructions to be executed by the processor.
  • The Register File (RF) contains several registers where data and temporary results are stored during

program execution.

  • The Data Memory (DM) serves as the computer’s main memory, storing data.
  • The Arithmetic Logic Unit (ALU) performs mathematical and logical operations on data.
  • The Control Unit (CU) reads instructions from the Instruction Memory and directs the other components

accordingly.

By having these components work together, our MIPS simulator functions like a real MIPS processor. It

allows users to write, compile, and run MIPS programs in a simulated environment.

Program Counter (PC)

The Program Counter (PC) keeps track of the memory address of the current instruction being executed

and controls the flow of instructions. It is responsible for updating the address of the next instruction based on

the current program flow.

Here’s an implementation of thePCclass:

class PC:

def __init__(self , value):
self.value = value
self.pcSrc = 0
self.jump = 0
def update(self , address , branchAddress):
if not self.jump:
if (self.pcSrc != 0):
print(’pc is branching ’)
self.value = self.value + 4 + branchAddress
print(’pc is now’, self.value)
else:
self.value += 4
else:
self.value = address << 2

Code Fig: 1: PC class Implementation

In the above implementation, thePCclass maintains the current value of the program counter and provides

a method to update it based on branching conditions and control signals.

Memory and Instruction Access

In MIPS architecture, memory is byte-addressable, meaning that each byte in memory has a unique address.

This allows individual bytes to be accessed and manipulated independently.

Instructions in MIPS are typically stored as 32-bit words. Each word consists of 4 bytes. This byte-

addressable nature of memory facilitates precise control over data and instructions at the byte level, enabling

efficient memory management and access within the architecture.

Instruction Memory (IM)

The instruction memory (IM) in our simulator works with byte-addressable memory for storing instructions.

Here’s how it operates:

1 class IM: 2 def init(self): 3 # Initialize instruction memory array with 0s 4 self.mem = [0] * 0xffc 5 print(’Initialized Instruction Memory ’) 6 self.RD = 0 7 8 def RDPort(self , A): 9 # Read 4 bytes (32 bits) from memory to form an instruction 10 self.RD = int( 11 num_to_8bit_binary(self.mem[A + 3]) + 12 num_to_8bit_binary(self.mem[A + 2]) + 13 num_to_8bit_binary(self.mem[A + 1]) + num_to_8bit_binary(self.mem[A]), 14 2) 15 print(’Instruction fetched from memory: ’, self.RD) 16 return

Code Fig: 2: Instruction Memory Access

The memory is organized so that each element in thememarray represents a byte of memory. When fetching

instructions, we access 4 bytes (32 bits) at a time to form a single instruction word. This allows us to effectively

access and process instructions within the MIPS architecture.

By utilizing byte-addressable memory and accessing instructions as 32-bit words, our simulator accurately

represents the memory organization and instruction access in MIPS architecture.

Register File

The Register File initializes control signals and registers, including the stack pointer.

1 class RF:
2 def __init__(self):
3 self.WE3 = 0
4 self.RD1 = 0
5 self.RD2 = 0
6 self.file = [0] * 32
7 self.file [29] = 16380 # stack pointer location in DM
8 self.RegDst = 0
9 self.MemtoReg = 0

Reading from Register File:These methods read data from specified registers.

1 def RD1Port(self , A1):
2 self.RD1 = self.file[A1]
3
4 def RD2Port(self , A2):
5 self.RD2 = self.file[A2]

Writing to Register File:This method writes data to specified registers based on control signals.

1 def WD3Port(self , rt , rd , ALUResult , RD): 2 if (self.MemtoReg == 0): 3 value = ALUResult 4 elif (self.MemtoReg == 1): 5 value = RD 6 if (self.WE3): 7 if (self.RegDst == 0): 8 self.file[rt] = value 9 elif (self.RegDst == 1): 10 self.file[rd] = value

Code Fig: 3: Pseudo code of our implementation

Execute and Writeback

In this part, we take a closer look at how the processor works when it runs programs and handles data in

its memory. We’ll focus on two key parts: the Control Unit (CU) and the Arithmetic Logic Unit (ALU). These

parts are like the brain and muscles of the processor, working together to process instructions and manage

information

Control Unit (CU)

The Control Unit (CU) manages control signals and sets them based on the opcode and function code of

the instruction being executed.

1 class CU: 2 def init(self , RF , DM , ALU , PC): 3 self.ALUControl = 0 4 self.RF = RF 5 self.DM = DM 6 self.ALU = ALU 7 self.PC = PC 8 self.branch = 0 9 10 def set_signals(self , opcode , funct): 11 #set signal to different select lines 12 ...

Code Fig: 4: Pseudo-code for Control Unit

Figure 2: ALU control signals

The ALU decoder, as portrayed in Figure 2, plays

a crucial role in interpreting standardized control sig-

nals. Its function ensures the adherence of any binary

code to the MIPS architecture standards, thereby val-

idating the integrity and compatibility of the instruc-

tions.

The tabular representation provided Figure 3

serves to elucidate the intricacies of the control unit

signals. This comprehensive breakdown facilitates a

deeper understanding of the control mechanisms gov-

erning the processor’s operations.

Figure 3: Control Unit signals

The compiler, along with the memory initializa-

tion, is a crucial step in preparing the IAS computer

for code execution. The provided Python script uti-

lizes thewritecodefunction to load machine code

fromMachine.outinto memory. Additionally, specific memory locations are initialized to set the stage for

program execution.

Arithmetic Logic Unit (ALU)

The ALU, short for Arithmetic Logic Unit, is a fundamental component of the processor responsible for

performing arithmetic and logical operations on data.

1 class ALU: 2 def init(self): 3 self.ALUResult = 0 4 self.Zero = 0 5 self.Control = 0 6 self.ALUSrc = 0 7 self.srcA = 0 8 self.srcB = 0 9 10 def calculate(self , srcA , rtVal , imm): 11 # Source Decision 12 self.srcA = srcA 13 if (self.ALUSrc == 0): 14 self.srcB = rtVal 15 elif (self.ALUSrc == 1): 16 self.srcB = imm 17 else: 18 self.srcB = 0 19 self.Zero = self.srcA - self.srcB 20 21 # ALU operation 22 if self.Control == -2: 23 self.ALUResult = self.srcA * self.srcB 24 if self.Control == 0b010: 25 self.ALUResult = self.srcA + self.srcB 26 elif self.Control == 0b110: 27 self.ALUResult = self.srcA - self.srcB 28 elif self.Control == 0b101: 29 self.ALUResult = self.srcA * self.srcB 30 elif self.Control == 0b000: 31 self.ALUResult = self.srcA & self.srcB 32 elif self.Control == 0b001: 33 self.ALUResult = self.srcA | self.srcB 34 elif self.Control == 0b111: 35 if self.srcA < self.srcB: 36 self.ALUResult = 0b 37 else: 38 self.ALUResult = 0b 39 else: 40 pass

Code Fig: 5: Pseudo-code for ALU

The ALU operates based on control signals and performs various arithmetic and logical operations on

input data. It handles operations such as addition, subtraction, multiplication, bitwise AND, bitwise OR, and

comparison.

For instance, in the provided code snippet:

  • IfControlrepresents addition (0b010), the ALU addssrcAandsrcB.
  • IfControlrepresents subtraction (0b110), the ALU subtractssrcBfromsrcA.

The ALU’s versatility and efficiency make it a vital component of modern processors, enabling them to

perform complex computations and logical operations swiftly and accurately.

MIPS Implementation

This section will focus on the three programs that was written in MIPS assembly code : An array sum

program, descending bubble sort and finally, a recursive factorial code that utilizes stack pointers. Note to be

taken that ’mul’ was implemented as an R-type instruction as MARS supports it without any pseudo instruction

(according to MARS, it has a valid 32-bit instruction)Also, MARS memory was configured to have .text

from address 0(using the Memory Configuration option in the ’Settings’)

Array Sum Program

We present an implementation of an array sum program in both C and MIPS assembly language. The

program calculates the sum of elements in an array using a loop structure.

1 .text 2 .globl main 3 4 main: 5 # Array initialization 6 li $t0 , 1 # Load first array element 7 li $t1 , 2 # Load second array element 8 li $t2 , 3 # Load third array element 9 li $t3 , 4 # Load fourth array element 10 li $t4 , 5 # Load fifth array element 11 12 # Calculate sum 13 add $s0 , $t0 , $t1 # Add first and second element 14 add $s0 , $s0 , $t2 # Add third element 15 add $s0 , $s0 , $t3 # Add fourth element 16 add $s0 , $s0 , $t4 # Add fifth element 17 18 # Print sum 19 li $v0 , 1 # Load system call code for printing integer 20 move $a0 , $s0 # Move sum to argument register 21 syscall # Perform system call to print sum 22 23 # Exit program 24 li $v0 , 10 # Load system call code for program exit 25 syscall # Perform system call to exit program

Code Fig: 6: MIPS Assembly Code for Array Sum

Initial data

First array element: 1, Second array element: 2, Third array element: 3, Fourth array element: 4, Fifth

array element: 5

Output Results

Figure 4: Initial Data Memory Figure 5: Output Result

Bubble Sort Program

We present an implementation of a bubble sort program in MIPS assembly language. The program sorts an

array of integers in descending order using the bubble sort algorithm.

1 .text 2 .globl main 3 main: 4 # Load array base address 5 addi $s0 ,$zero , 0x 6 # Load array size (4 times the number of elements in the array) 7 addi $s1 , $zero , 40 8 addi $t0 , $zero , 10 9 sw $t0 , 0($s0) 10 addi $t0 , $zero , 2 11 sw $t0 , 4($s0) 12 13 addi $t0 , $zero , 5 14 sw $t0 , 8($s0) 15 16 ... # Rest of the array initialization 17 jal bubble_sort 18 # Exit program 19 addi $v0 ,$zero , 10 # syscall code for exit 20 syscall 21 22 bubble_sort: 23 addi $t0 , $zero , 0 # i = 0 24 outer_loop: 25 sub $t6 , $s1 , $t0 # t6 = n - i 26 addi $t6 , $t6 , -4 # t6 = n - i - 4 27 beq $t6 , $zero , end_bubble # if n - i == 4, exit outer loop 28 29 addi $t1 , $zero , 0 # j = 0 30 inner_loop: 31 beq $t1 , $t6 , next_outer # if j == n - i - 4, go to next outer iteration 32 33 # Load arr[j] into $t 34 add $t7 , $s0 , $t1 # base address + offset 35 lw $t2 , 0($t7) 36 # Load arr[j+1] into $t 37 addi $t8 , $t1 , 4 # j + 4 38 add $t8 , $s0 , $t8 # base address + offset 39 lw $t3 , 0($t8) 40 41 # Compare arr[j] and arr[j+1] 42 slt $at , $t2 , $t 43 beq $at , $zero , no_swap 44 45 # Swap arr[j] and arr[j+1] 46 sw $t3 , 0($t7) 47 sw $t2 , 0($t8) 48 49 no_swap: 50 addi $t1 , $t1 , 4 # j += 4 (increment by word size) 51 j inner_loop 52 53 next_outer: 54 addi $t0 , $t0 , 4 # i += 4 (increment by word size) 55 j outer_loop 56 57 end_bubble: 58 jr $ra # return to caller

Code Fig: 7: MIPS Assembly Code for Bubble Sort

Initial Data

First array element: 10, Second array element: 2, Third array element: 5, Fourth array element: 4, Fifth

array element: 3, Sixth array element: 6, Seventh array element: 7, Eighth array element: 8, Ninth array

element: 4, Tenth array element: 10

Output Results

Figure 6: Initial Data Memory Figure 7: Output Result

Recursive Factorial

The following MIPS assembly code demonstrates the recursive calculation of factorial.

1 .text 2 addi $t1 , $zero , 1 3 addi $a0 , $zero , 6 4 jal fact 5 add $s0 , $zero , $v 6 addi $v0 , $zero , 10 7 syscall 8 fact: 9 addi $sp , $sp , - 10 sw $ra , 4($sp) 11 sw $a0 , 0($sp) 12 slt $t0 , $a0 , $t 13 beq $t0 , $zero , L 14 addi $v0 , $zero , 1 15 addi $sp , $sp , 8 16 jr $ra 17 L1: 18 addi $a0 , $a0 , - 19 jal fact 20 lw $a0 , 0($sp) 21 lw $ra , 4($sp) 22 addi $sp , $sp , 8 23 mul $v0 , $a0 , $v 24 jr $ra

Code Fig: 8: MIPS Assembly Code for Recursive Factorial

Results

Figure 8: 6 is given as input to factorial function Figure 9: Result 720 can be seen (6! = 720)

About

Non-Pipelined MIPS Simulator, which can be extended to Pipelined design

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors