diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ada0dd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/launch.json +.DS_Store diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3f35833 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/alabarjasteh/mips-simulator + +go 1.16 diff --git a/main.go b/main.go new file mode 100644 index 0000000..f973d4e --- /dev/null +++ b/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + + "github.com/alabarjasteh/mips-simulator/mips" +) + +type Instruction int64 + +func main() { + mem := mips.NewMemory() + cpu := mips.NewCPU(mem) + + for { + _, instData := cpu.Fetch() + instruction, err := cpu.Decode(instData) + if err != nil { + fmt.Printf("error: %v", err) + return + } + err = cpu.Execute(instruction) + if err != nil { + fmt.Printf("error: %v", err) + return + } + } +} + +// func fetchBurst(pc *int, mem *memory.Mem, reg *register.IfDec) { +// inst, err := mem.FetchInstruction(*pc) +// if err != nil { +// log.Fatal(err) +// } +// reg.IR = inst +// log.Printf("IR : %v", reg.IR) +// *pc += 4 +// reg.NPC = *pc +// } + +// func decodeBurst(rf *register.File, ifDec *register.IfDec, decEx *register.DecEx) { +// r1, r2 := getReadingRegisters(rf, ifDec.IR) +// imm := extractImmediate(ifDec.IR) +// decEx.IR = ifDec.IR +// decEx.NPC = ifDec.NPC +// decEx.R1 = r1 +// decEx.R2 = r2 +// decEx.Imm = imm +// } + +// func executeBurst() { + +// } + +// func memoryBurst() {} +// func writebackBurst() {} diff --git a/memory/state1.txt b/memory/state1.txt deleted file mode 100644 index 3a795b1..0000000 --- a/memory/state1.txt +++ /dev/null @@ -1,7 +0,0 @@ -00000000000000000000000000000001 -00000000000000000000000000000010 -00000000000000000000000000000011 -00000000000000000000000000000100 -00000000000000000000000000000101 -00000000000000000000000000000110 -00000000000000000000000000000111 \ No newline at end of file diff --git a/memory_state1.txt b/memory_state1.txt new file mode 100644 index 0000000..8eca69d --- /dev/null +++ b/memory_state1.txt @@ -0,0 +1,6 @@ +10001100000000010000000000010000 +10001100000000100000000000010100 +00000000001000100001100000100000 +10101100000000110000000000011000 +00000000000000000000000000000001 +00000000000000000000000000000011 \ No newline at end of file diff --git a/memory/instructions1.txt b/memory_state2.txt similarity index 58% rename from memory/instructions1.txt rename to memory_state2.txt index 76c0f00..2b323e5 100644 --- a/memory/instructions1.txt +++ b/memory_state2.txt @@ -8,3 +8,10 @@ 00100000001000100000000000000001 00101000001000100000000000000001 00110100001000100000000000000001 +00000000000000000000000000000001 +00000000000000000000000000000010 +00000000000000000000000000000011 +00000000000000000000000000000100 +00000000000000000000000000000101 +00000000000000000000000000000110 +00000000000000000000000000000111 \ No newline at end of file diff --git a/mips/cpu.go b/mips/cpu.go new file mode 100644 index 0000000..7b1529e --- /dev/null +++ b/mips/cpu.go @@ -0,0 +1,85 @@ +package mips + +import ( + "errors" + "fmt" +) + +type CPU struct { + PC int + Memory Memory + RegFile [32]int32 +} + +func NewCPU(mem Memory) *CPU { + return &CPU{ + PC: 0, + RegFile: [32]int32{}, + Memory: mem, + } +} + +func (cpu *CPU) Fetch() (npc int, instData int) { + ins := cpu.Memory[cpu.PC] + fmt.Printf("instruction: %b\n", ins) + cpu.PC += 4 + npc = cpu.PC + return npc, ins +} + +func (cpu *CPU) Decode(insData int) (*Instruction, error) { + fmt.Printf("inst: %b\n", insData) + opcode := insData >> 26 + fmt.Printf("opcode: %b\n", opcode) + opcodeType, err := getOpcodeType(opcode) + if err != nil { + return nil, err + } + + ins := &Instruction{} + ins.Opcode = opcode + ins.OpcodeType = opcodeType + + switch opcodeType { + case OpcodeTypeR: + insTypeR := &InstructionTypeR{} + insTypeR.FuncCode = insData & 0b111111 + insTypeR.DestinationRegister = (insData >> 11) & 0b11111 + insTypeR.TargetRegister = (insData >> 16) & 0b11111 + insTypeR.SourceRegister = (insData >> 21) & 0b11111 + f, ok := FunctionTypeRMap[insTypeR.FuncCode] + if !ok { + return nil, errors.New("unsupported funcCode") + } + insTypeR.Function = f + + ins.TypeR = insTypeR + case OpcodeTypeI: + insTypeI := &InstructionTypeI{} + insTypeI.Immediate = insData & 0xFFFF + insTypeI.TargetRegister = (insData >> 16) & 0b11111 + insTypeI.SourceRegister = (insData >> 21) & 0b11111 + f, ok := FunctionTypeIMap[opcode] + if !ok { + return nil, errors.New("unsupported opcode") + } + insTypeI.Function = f + + ins.TypeI = insTypeI + } + + return ins, nil +} + +func (cpu *CPU) Execute(ins *Instruction) error { + var err error + switch ins.OpcodeType { + case OpcodeTypeR: + r := ins.TypeR + err = r.Function(cpu, r.SourceRegister, r.TargetRegister, r.DestinationRegister) + case OpcodeTypeI: + i := ins.TypeI + err = i.Function(cpu, i.SourceRegister, i.TargetRegister, i.Immediate) + } + return err +} diff --git a/mips/function.go b/mips/function.go new file mode 100644 index 0000000..10042aa --- /dev/null +++ b/mips/function.go @@ -0,0 +1,84 @@ +package mips + +import ( + "errors" + "fmt" +) + +// R Type Instructions +type FunctionTypeR func(cpu *CPU, rs int, rt int, rd int) error + +func Add(cpu *CPU, rs int, rt int, rd int) error { + + cpu.RegFile[rd] = cpu.RegFile[rs] + cpu.RegFile[rt] + + return nil +} + +func And(cpu *CPU, rs int, rt int, rd int) error { + fmt.Printf("(\"and\" not implemented)\n") + return errors.New("not implemented: and") +} + +func Slt(cpu *CPU, rs int, rt int, rd int) error { + fmt.Printf("(\"slt\" not implemented)\n") + return errors.New("not implemented: slt") +} + +func Sub(cpu *CPU, rs int, rt int, rd int) error { + fmt.Printf("(\"sub\" not implemented)\n") + return errors.New("not implemented: sub") +} + +func Or(cpu *CPU, rs int, rt int, rd int) error { + + cpu.RegFile[rd] = cpu.RegFile[rs] | cpu.RegFile[rt] + + return nil +} + +// I Type Instruction +type FunctionTypeI func(cpu *CPU, rs int, rt int, imm int) error + +func Addi(cpu *CPU, rs int, rt int, imm int) error { + + cpu.RegFile[rt] = cpu.RegFile[rs] + int32(int16(imm)) + + return nil +} + +func Andi(cpu *CPU, rs int, rt int, imm int) error { + fmt.Printf("(\"andi\" not implemented)\n") + return errors.New("not implemented: andi") +} + +func Lw(cpu *CPU, rs int, rt int, imm int) error { + + value := int32(cpu.Memory[int(int16(imm))+int(cpu.RegFile[rs])]) + cpu.RegFile[rt] = value + + return nil +} + +func Ori(cpu *CPU, rs int, rt int, imm int) error { + fmt.Printf("(\"ori\" not implemented)\n") + return errors.New("not implemented: ori") +} + +func Slti(cpu *CPU, rs int, rt int, imm int) error { + + if cpu.RegFile[rs] < int32(int16(imm)) { + cpu.RegFile[rt] = 1 + } else { + cpu.RegFile[rt] = 0 + } + + return nil +} + +func Sw(cpu *CPU, rs int, rt int, imm int) error { + + cpu.Memory[int(int16(imm))+int(cpu.RegFile[rs])] = int(cpu.RegFile[rt]) + + return nil +} diff --git a/mips/instruction.go b/mips/instruction.go new file mode 100644 index 0000000..f61af09 --- /dev/null +++ b/mips/instruction.go @@ -0,0 +1,42 @@ +package mips + +var FunctionTypeRMap = map[int]FunctionTypeR{ + 0b100000: Add, + 0b100010: Sub, + 0b100101: Or, + 0b100100: And, + 0b101010: Slt, +} + +var FunctionTypeIMap = map[int]FunctionTypeI{ + 0b101011: Sw, + 0b100011: Lw, + 0b001000: Addi, + 0b001010: Slti, + 0b001100: Andi, + 0b001101: Ori, +} + +type Instruction struct { + Opcode int + OpcodeType OpcodeType + TypeR *InstructionTypeR + TypeI *InstructionTypeI +} + +type InstructionTypeR struct { + SourceRegister int + TargetRegister int + DestinationRegister int + FuncCode int + + Function func(cpu *CPU, rs int, rt int, rd int) error +} + +type InstructionTypeI struct { + SourceRegister int + TargetRegister int + Immediate int + + Function func(cpu *CPU, rs int, rt int, imm int) error +} diff --git a/mips/memory.go b/mips/memory.go new file mode 100644 index 0000000..a3a98e4 --- /dev/null +++ b/mips/memory.go @@ -0,0 +1,40 @@ +package mips + +import ( + "bufio" + "fmt" + "log" + "os" + "strconv" +) + +type Memory map[int]int // map[PC]Data + +func NewMemory() Memory { + mem := Memory{} + mem.loadMemoryFromFile() + return mem +} + +func (mem Memory) loadMemoryFromFile() { + file, err := os.Open("./memory_state1.txt") + if err != nil { + log.Fatalf("failed to open") + + } + defer file.Close() + scanner := bufio.NewScanner(file) + i := 0 + for scanner.Scan() { + val, err := strconv.ParseInt(scanner.Text(), 2, 64) + if err != nil { + log.Fatal("cannot parse memory state from file") + } + mem[i] = int(val) + fmt.Printf("mem: %b\n", mem[i]) + i += 4 + } + if err := scanner.Err(); err != nil { + log.Fatal(err) + } +} diff --git a/mips/opcode.go b/mips/opcode.go new file mode 100644 index 0000000..b7e4292 --- /dev/null +++ b/mips/opcode.go @@ -0,0 +1,23 @@ +package mips + +import "errors" + +type OpcodeType byte + +var ErrInvalidOpcode = errors.New("invalid opcode") + +const ( + OpcodeTypeInvalid OpcodeType = iota + OpcodeTypeR + OpcodeTypeI +) + +func getOpcodeType(opcode int) (OpcodeType, error) { + if opcode == 0 { + return OpcodeTypeR, nil + } + if _, ok := FunctionTypeIMap[opcode]; ok { + return OpcodeTypeI, nil + } + return OpcodeTypeInvalid, ErrInvalidOpcode +}