func (this *ReorderBuffer) commitRobEntry(robEntry RobEntry, startCycles uint32) { // Commit update opId := robEntry.Operation.Id() if robEntry.Type == RegisterType { // Register renaming (RAT) dest := robEntry.Destination if robEntry.Operation.RenamedDestRegister() != -1 { dest = this.RegisterAliasTable().Entries()[robEntry.Destination].ArchRegister // Release entry from RAT this.RegisterAliasTable().Release(opId) } logger.Collect(" => [RB%d][%03d]: Writing %#08X to %s%d...", this.Index(), opId, robEntry.Value, robEntry.Type, dest) this.Processor().RegistersMemory().StoreUint32(dest*consts.BYTES_PER_WORD, uint32(robEntry.Value)) } else if robEntry.Type == MemoryType { logger.Collect(" => [RB%d][%03d]: Writing %#08X to %s[%#X]...", this.Index(), opId, robEntry.Value, robEntry.Type, robEntry.Destination) this.Processor().DataMemory().StoreUint32(robEntry.Destination, uint32(robEntry.Value)) } else { this.Processor().SetProgramCounter(this.getNextProgramCounter(robEntry, this.Processor().ProgramCounter())) } // Increment program counter this.Processor().IncrementProgramCounter(consts.BYTES_PER_WORD) logger.Collect(" => [RB%d][%03d]: PC = %#04X", this.Index(), opId, this.Processor().ProgramCounter()) // Release ROB entry delete(this.Buffer(), opId) }
func (this *BranchPredictor) GetNextAddress(address uint32, instruction *instruction.Instruction, forceStall bool) (uint32, bool, error) { nextData := this.Processor().InstructionsMemory().Load(address, consts.BYTES_PER_WORD) opId := this.Processor().InstructionsFetchedCounter() - 1 this.Processor().AddSpeculativeJump() // Check if next instruction is valid if this.Processor().ReachedEnd(nextData) { this.waitQueueInstructions() this.processor.Finish() logger.Collect(" => [BP%d][%03d]: Program reached the end", this.Index(), opId) return 0, false, errors.New("Program reached the end") } // If it needs to wait needsWait, predicted := this.needsWait(instruction.Info) if needsWait || forceStall { // Stall until previous instruction finishes logger.Collect(" => [BP%d][%03d]: Branch detected, wait to finish queue (%d out of %d)...", this.Index(), this.Processor().InstructionsFetchedCounter()-1, this.Processor().InstructionsCompletedCounter(), opId) this.waitQueueInstructions() logger.Collect(" => [BP%d][%03d]: Waited for address resolution and got %#04X", this.Index(), opId, this.Processor().ProgramCounter()) return this.Processor().ProgramCounter(), false, nil } else { newAddress := this.guessAddress(address, instruction) if instruction.Info.IsBranch() { logger.Collect(" => [BP%d][%03d]: Predicted address: %#04X", this.Index(), opId, newAddress) } return newAddress, predicted, nil } }
func (this *BranchPredictor) getGuessByAddress(address uint32) bool { state, exists := this.Processor().GetBranchStateByAddress(address) if !exists { logger.Collect(" => [BP0]: No history for address %#04X", address) return false } totalStates := uint32(math.Exp2(float64(this.PredictorBits()))) taken := state >= totalStates/2 logger.Collect(" => [BP0]: Address %#04X, Total States: %d, State: %d ,Taken: %v", address, totalStates, state, taken) return taken }
func (this *ReorderBuffer) waitStallOperationIfFull(op *operation.Operation) { for uint32(len(this.Buffer())) >= this.RobEntries() { lastCompletedOpId := this.Processor().LastOperationIdCompleted() if op.Id() == lastCompletedOpId+1 { logger.Collect(" => [RB%d][%03d]: Writing latest instruction.", this.Index(), op.Id()) return } logger.Collect(" => [RB%d][%03d]: ROB is full, wait for free entries. Current: %d, Max: %d, LastOpId: %d...", this.Index(), op.Id(), len(this.Buffer()), this.RobEntries(), lastCompletedOpId) this.Processor().Wait(1) } }
func (this *Dispatcher) runDispatcherToReservationStation(input channel.Channel, rs *reservationstation.ReservationStation, rat *registeraliastable.RegisterAliasTable, rob *reorderbuffer.ReorderBuffer) { incomingQueue := map[uint32]*operation.Operation{} currentOperationId := this.StartOperationId() // For each operation received to schedule, process it for { value, running := <-input.Channel() if !running || !this.IsActive() { logger.Print(" => Flushing dispatcher unit %d (dispatcher to RS)", this.Index()) return } op := operation.Cast(value) // Add to current operation incomingQueue[op.Id()] = op // Send to incoming channel pending ops (if available) for op, exists := incomingQueue[currentOperationId]; exists; op, exists = incomingQueue[currentOperationId] { // Allocate in ROB if there is spacde, otherwise stall rob.Allocate(op) // Rename register in case of WAR & WAR hazards if this.RegisterAliasTableEntries() > 0 { _, destRegister := rs.GetDestinationDependency(op.Id(), op.Instruction()) if destRegister != -1 { found, _ := rat.AddMap(uint32(destRegister), op.Id()) if !found { // Need to stall for an available RAT entry logger.Collect(" => [DI%d][%03d]: No entry available in RAT. Wait for one...", this.Index(), op.Id()) break } // Rename to physical registers this.renameRegisters(op.Id(), op, rat) } } //Redirect input operations to the required execution unit channels logger.Collect(" => [DI%d][%03d]: Scheduling to RS: %s, %s", this.Index(), op.Id(), op.Instruction().Info.ToString(), op.Instruction().Data.ToString()) rs.Schedule(op) currentOperationId += 1 } input.Release() } }
func (this *BranchPredictor) waitQueueInstructions() { for !this.isInstructionCompleted(this.Processor().InstructionsFetchedCounter() - 1) { logger.Collect(" => [BP%d][%03d]: Branch detected, wait to finish queue (%d out of %d)...", this.Index(), this.Processor().InstructionsFetchedCounter()-1, this.Processor().InstructionsCompletedCounter(), this.Processor().InstructionsFetchedCounter()) this.Processor().Wait(1) // Let execute stage add instruction into InstructionsCompleted queue first and then compare lenghts time.Sleep(this.Processor().Config().CyclePeriod() / 2) } }
func (this Channel) Add(value interface{}) { defer func() { if r := recover(); r != nil { logger.Collect("WARN: Recovered in %v", r) } }() if !this.isClosed { this.locks <- true this.channel <- value } }
func (this *ReservationStation) runCommonBusListener(commonDataBus channel.Channel) { // For each operation executed, feed reservation station to release operands for { value, running := <-commonDataBus.Channel() if !running { this.reservationStation.isActive = false logger.Print(" => Flushing reservation station unit %d (CDB listener)", this.Index()) return } op := operation.Cast(value) commonDataBus.Release() this.Lock() <- true dest, _, _ := this.getComponentsFromInstruction(op.Instruction()) entryIndex := this.getEntryIndexFromOperationId(op.Id()) entryOp := this.Entries()[entryIndex] logger.Collect(" => [RS%d][%03d]: Operation completed, releasing entry %d", this.Index(), op.Id(), entryIndex) if entryIndex != INVALID_INDEX { // Release entry this.Entries()[entryIndex].Busy = false this.Entries()[entryIndex].Free = true // Release entry from reservation station queue this.Input().Release() } // Release destination register (as RAT if enabled) if dest != INVALID_INDEX { logger.Collect(" => [RS%d][%03d]: Register %v resolved", this.Index(), op.Id(), entryOp.Destination) this.releaseOperation(entryOp.Destination) } // Release operands registers for _, operand := range entryOp.Operands { if operand.IsValid() && (operand.Type == MemoryType || len(this.RegisterAliasTable().Entries()) == 0) { logger.Collect(" => [RS%d][%03d]: Register %v resolved", this.Index(), op.Id(), operand) this.releaseOperation(operand) } } <-this.Lock() } }
func (this *Fpu) Process(operation *operation.Operation) (*operation.Operation, error) { instruction := operation.Instruction() outputAddress, err := this.compute(operation, instruction.Info, instruction.Data) if err != nil { return operation, err } logger.Collect(" => [FPU][%03d]: [R%d(%#02X) = %#08X]", operation.Id(), outputAddress, outputAddress*consts.BYTES_PER_WORD, this.Result()) // Persist output data this.Bus().StoreRegister(operation, outputAddress, this.Result()) return operation, nil }
func (this *Branch) Process(operation *operation.Operation) (*operation.Operation, error) { instruction := operation.Instruction() info := instruction.Info operands := instruction.Data switch info.Type { case data.TypeI: registerD := operands.(*data.DataI).RegisterD.ToUint32() op1 := this.Bus().LoadRegister(operation, registerD) registerS := operands.(*data.DataI).RegisterS.ToUint32() op2 := this.Bus().LoadRegister(operation, registerS) logger.Collect(" => [BR][%03d]: [R%d(%#02X) = %#08X ? R%d(%#02X) = %#08X]", operation.Id(), registerD, registerD*consts.BYTES_PER_WORD, op1, registerS, registerS*consts.BYTES_PER_WORD, op2) taken, err := processOperation(op1, op2, info.Opcode) if err != nil { return operation, nil } operation.SetBranchResult(taken) if taken { offsetAddress := ComputeOffsetTypeI(operands) this.Bus().IncrementProgramCounter(operation, offsetAddress) logger.Collect(" => [BR][%03d]: [PC(offset) = 0x%06X", operation.Id(), offsetAddress) } else { // Notify ROB this.Bus().IncrementProgramCounter(operation, 0) } case data.TypeJ: address := ComputeAddressTypeJ(operands) this.Bus().SetProgramCounter(operation, uint32(address-consts.BYTES_PER_WORD)) logger.Collect(" => [BR][%03d]: [Address = %06X]", operation.Id(), address) default: return operation, errors.New(fmt.Sprintf("Invalid data type to process by Branch unit. Type: %d", info.Type)) } return operation, nil }
func (this *Clock) Run() { this.clock.startTime = time.Now() this.clock.ticker = time.NewTicker(this.clock.period) for tickTime := range this.clock.ticker.C { // If event finish equals to true, then finish if this.finishEvent() { logger.Collect(" => Stopping clock...") this.Stop() break } // If paused wait until next tick if !this.clock.paused { // Update clock status this.clock.cycles += 1 this.clock.duration = tickTime.Sub(this.clock.startTime) this.clock.tick <- true logger.Collect("\n-------- Cycle: %04d ------- (%04d ms)", this.clock.cycles, this.DurationMs()) } } }
func (this *ReorderBuffer) commitRobEntries(robEntries []RobEntry) { startCycles := this.Processor().Cycles() // Commit results in order opIds := []uint32{} for _, robEntry := range robEntries { opIds = append(opIds, robEntry.Operation.Id()) logger.Collect(" => [RB%d][%03d]: Commiting operation %d...", this.Index(), robEntry.Operation.Id(), robEntry.Operation.Id()) this.commitRobEntry(robEntry, startCycles) } // Wait and log completion in a go routine go func(opIds []uint32, startCycles uint32) { this.Processor().Wait(consts.WRITEBACK_CYCLES) for _, opId := range opIds { this.Processor().LogEvent(consts.WRITEBACK_EVENT, this.Index(), opId, startCycles) this.Processor().LogInstructionCompleted(opId) } }(opIds, startCycles) }
func (this *Processor) RunRecovery(recoveryChannel channel.Channel, flushFunc func()) { for value := range recoveryChannel.Channel() { op := operation.Cast(value) logger.Collect(" => Recovering at OpId: %d and Address: %#04X", op.Id(), op.Address()) logger.SetVerboseQuiet(true) // Flush pipeline flushFunc() // Clean logs this.RemoveForwardLogs(op.Id() - 1) // Clear speculative jumps this.ClearSpeculativeJumps() // Start pipeline from the recovery address flushFunc = this.StartPipelineUnits(this.Config(), recoveryChannel, op.Id(), op.Address()) // Release value from channel recoveryChannel.Release() } }
func (this *ReservationStation) dispatchOperation(entryIndex EntryIndex, op *operation.Operation) { // Lock a slot in max quantity of instructions dispatcher per cycle this.AddInstructionsFetchedStack() // Release reservation station entry this.Entries()[entryIndex].Busy = true go func() { // Release one entry and send operation to execution, when output channel (execution unit) is free logger.Collect(" => [RS%d][%03d]: Sending entry %d to %s queue...", this.Index(), op.Id(), entryIndex, op.Instruction().Info.Category) // Wait dispatch cycles startCycles := this.Processor().Cycles() this.Processor().Wait(consts.DISPATCH_CYCLES) // Log completion this.Processor().LogEvent(consts.DISPATCH_EVENT, this.Index(), op.Id(), startCycles) // Send data to execution unit in a go routine, when execution unit is available go this.Output()[op.Instruction().Info.Category].Add(op) }() }
func (this *Decoder) decodeInstruction(op *operation.Operation) (*instruction.Instruction, error) { startCycles := this.Processor().Cycles() // Do decode once a data instruction is received instruction, err := this.Processor().InstructionsSet().GetInstructionFromBytes(op.Word()) if err != nil { return nil, errors.New(fmt.Sprintf("Failed decoding instruction %#04X. %s]", op.Word(), err.Error())) } logger.Collect(" => [DE%d][%03d]: %#04X = %s, %s", this.Index(), op.Id(), op.Word(), instruction.Info.ToString(), instruction.Data.ToString()) // Wait cycles of a decode stage this.Processor().Wait(consts.DECODE_CYCLES) // Log event if this.IsActive() { this.Processor().LogEvent(consts.DECODE_EVENT, this.Index(), op.Id(), startCycles) } return instruction, nil }
func (this *Executor) executeOperation(unit IExecutor, event string, op *operation.Operation) (*operation.Operation, error) { startCycles := this.Processor().Cycles() // Do decode once a data instruction is received var err error op, err = unit.Process(op) if err != nil { return op, errors.New(fmt.Sprintf("Failed executing instruction. %s]", err.Error())) } // Wait cycles of a execution stage logger.Collect(" => [%s%d][%03d]: Executing %s, %s", event, this.Index(), op.Id(), op.Instruction().Info.ToString(), op.Instruction().Data.ToString()) for i := uint8(0); i < op.Instruction().Info.Cycles; i++ { this.Processor().Wait(1) } // Log completion if this.IsActive() { this.Processor().LogEvent(event, this.Index(), op.Id(), startCycles) } return op, nil }
func (this *Alu) Process(operation *operation.Operation) (*operation.Operation, error) { instruction := operation.Instruction() // Clean Status this.CleanStatus() outputAddress, err := this.compute(operation, instruction.Info, instruction.Data) if err != nil { return operation, err } logger.Collect(" => [ALU][%03d]: [R%d(%#02X) = %#08X]", operation.Id(), outputAddress, outputAddress*consts.BYTES_PER_WORD, this.Result()) // Set status flags this.SetStatusFlag(this.Result()%2 == 0, consts.FLAG_PARITY) this.SetStatusFlag(this.Result() == 0, consts.FLAG_ZERO) this.SetStatusFlag(getSign(this.Result()), consts.FLAG_SIGN) // Persist output data this.Bus().StoreRegister(operation, outputAddress, this.Result()) return operation, nil }
func (this *ReorderBuffer) checkForMisprediction(targetEntry RobEntry, cachedEntries []RobEntry) (bool, uint32) { op := targetEntry.Operation if !op.Instruction().Info.IsBranch() { return false, 0 } // If operation does not have a predicted address, then return if op.PredictedAddress() == -1 { this.Processor().LogBranchInstruction(op.Address(), op.Instruction().Info.IsConditionalBranch(), false, op.Taken()) return false, 0 } // If predicted address is equal to the computed address, then return computedAddress := this.getNextProgramCounter(targetEntry, this.getCachedProgramCounter(cachedEntries)) failed := computedAddress != uint32(op.PredictedAddress()) if failed { logger.Collect(" => [RB%d][%03d]: Misprediction found, it was predicted: %#04X and computed: %#04X", this.Index(), targetEntry.Operation.Id(), op.PredictedAddress(), computedAddress) } this.Processor().LogBranchInstruction(op.Address(), op.Instruction().Info.IsConditionalBranch(), failed, op.Taken()) return failed, computedAddress }
func (this *LoadStore) Process(operation *operation.Operation) (*operation.Operation, error) { instruction := operation.Instruction() rdAddress := instruction.Data.(*data.DataI).RegisterD.ToUint32() rsAddress := instruction.Data.(*data.DataI).RegisterS.ToUint32() immediate := instruction.Data.(*data.DataI).Immediate.ToUint32() switch instruction.Info.Opcode { case set.OP_LW: rsValue := this.Bus().LoadRegister(operation, rsAddress) value := this.Bus().LoadData(operation, rsValue+immediate) this.Bus().StoreRegister(operation, rdAddress, value) logger.Collect(" => [LS][%03d]: [R%d(%#02X) = MEM(%#02X) = %#08X]", operation.Id(), rdAddress, rdAddress*consts.BYTES_PER_WORD, rsValue+immediate, value) case set.OP_SW: rdValue := this.Bus().LoadRegister(operation, rdAddress) rsValue := this.Bus().LoadRegister(operation, rsAddress) this.Bus().StoreData(operation, rdValue+immediate, rsValue) logger.Collect(" => [LS][%03d]: [MEM(%#02X) = %#08X]", operation.Id(), rdValue+immediate, rsValue) case set.OP_LLI: this.Bus().StoreRegister(operation, rdAddress, immediate) logger.Collect(" => [LS][%03d]: [R%d(%#02X) = %#08X]", operation.Id(), rdAddress, rdAddress*consts.BYTES_PER_WORD, immediate) case set.OP_SLI: rdValue := this.Bus().LoadRegister(operation, rdAddress) this.Bus().StoreData(operation, rdValue, immediate) logger.Collect(" => [LS][%03d]: [MEM(%#02X) = %#08X]", operation.Id(), rdValue, immediate) case set.OP_LUI: this.Bus().StoreRegister(operation, rdAddress, immediate<<16) logger.Collect(" => [LS][%03d]: [R%d(%#02X) = %#08X]", operation.Id(), rdAddress, rdAddress*consts.BYTES_PER_WORD, immediate<<16) case set.OP_SUI: rdValue := this.Bus().LoadRegister(operation, rdAddress) this.Bus().StoreData(operation, rdValue, immediate<<16) logger.Collect(" => [LS][%03d]: [MEM(%#02X) = %#08X]", operation.Id(), rdValue, immediate<<16) default: return operation, errors.New(fmt.Sprintf("Invalid operation to process by Data unit. Opcode: %d", instruction.Info.Opcode)) } return operation, nil }
func (this *Fetcher) fetchInstructions(op *operation.Operation, bytes []byte, input channel.Channel) ([]*operation.Operation, error) { initialAddress := op.Address() totalInstructions := len(bytes) / consts.BYTES_PER_WORD ops := []*operation.Operation{} // Analyze each instruction loaded for i := 0; i < totalInstructions; i += 1 { data := bytes[i*consts.BYTES_PER_WORD : (i+1)*consts.BYTES_PER_WORD] // Check program reach end if this.Processor().ReachedEnd(data) { this.processor.Finish() return ops, nil } // Do fetch once a new address is received msg := fmt.Sprintf(" => [FE%d][%03d]: INS[%#04X] = %#04X", this.Index(), this.Processor().InstructionsFetchedCounter(), op.Address(), data) value, ok := this.Processor().InstructionsMap()[op.Address()] if ok { msg = fmt.Sprintf("%s // %s", msg, strings.TrimSpace(strings.Split(value, "=>")[1])) } logger.Collect(msg) // Log event this.Processor().LogInstructionFetched(op.Address()) // Update data into operation and add to array for post-events op.SetWord([]byte{data[0], data[1], data[2], data[3]}) // Add operation to be sent to decode channel ops = append(ops, op) // Do pre-decode needsWait, instruction := this.BranchPredictor().PreDecodeInstruction(op.Address()) // If is not pipelined than wait instruction to finish if !this.Processor().Config().Pipelined() { go func() { address, _, err := this.BranchPredictor().GetNextAddress(op.Address(), instruction, true) newOp := operation.New(this.Processor().InstructionsFetchedCounter(), address) if err == nil { input.Add(newOp) } }() return ops, nil } // Add next instruction for fetching (as many instructions as it supports per cycle) if needsWait { logger.Collect(" => [FE%d][%03d]: Wait detected, no fetching more instructions this cycle", this.Index(), this.Processor().InstructionsFetchedCounter()-1) // Add next instruction in a go routine as it need to be stalled go func() { address, _, err := this.BranchPredictor().GetNextAddress(op.Address(), instruction, false) newOp := operation.New(this.Processor().InstructionsFetchedCounter(), address) if err == nil { input.Add(newOp) } }() return ops, nil } else { address, predicted, err := this.BranchPredictor().GetNextAddress(op.Address(), instruction, false) // Set current operation added to be decoded the predicted address if predicted { ops[len(ops)-1].SetNextPredictedAddress(address) } // Create new operation object op = operation.New(this.Processor().InstructionsFetchedCounter(), address) // If is the last instruction from the package or the predicted address is outside of the address package if err == nil && (i >= totalInstructions-1 || initialAddress+(uint32(i+1)*consts.BYTES_PER_WORD) != op.Address()) { input.Add(op) return ops, nil } } } return ops, nil }
func (this *Processor) Finish() { logger.Collect(" => Stopping processor...") this.processor.done = true }
func (this *ReservationStation) runScheduler() { // For each operation received to schedule, process it for { value, running := <-this.Input().Channel() if !running { this.reservationStation.isActive = false logger.Print(" => Flushing reservation station unit %d (scheduler)", this.Index()) return } this.Lock() <- true op := operation.Cast(value) // Get next entry free entryIndex := this.getNextIndexFreeEntry() dest, valueOperands, memoryOperands := this.getComponentsFromInstruction(op.Instruction()) // Convert to operand objects ops := []Operand{} for _, register := range memoryOperands { ops = append(ops, newMemoryOp(register)) } for _, register := range valueOperands { ratEntry, ok := this.RegisterAliasTable().GetPhysicalRegister(op.Id()-1, uint32(register)) if ok { ops = append(ops, newRegisterRatOp(register, int32(ratEntry))) } else { ops = append(ops, newRegisterOp(register)) } } // Rat Dest regDestRat := newNilDep() if dest != INVALID_INDEX { if op.RenamedDestRegister() != INVALID_INDEX { regDestRat = newRegisterRatOp(dest, op.RenamedDestRegister()) } else { regDestRat = newRegisterOp(dest) } } dependencies := this.getDependencies(op.Id(), regDestRat, ops) logger.Collect(" => [RS%d][%03d]: Adding op to entry %d [D: %v, O's: %v, V's: %v] ..", this.Index(), op.Id(), entryIndex, regDestRat, ops, dependencies) // Store entry depency into reservation station this.Entries()[entryIndex] = RsEntry{ Operation: op, Destination: regDestRat, Operands: ops, Dependencies: dependencies, Free: false, Busy: false, } // If no waiting dependencies, release and execute if len(dependencies) == 0 { this.dispatchOperation(EntryIndex(entryIndex), op) } <-this.Lock() } }