func (this *Processor) StartPipelineUnits(config *config.Config, recoveryChannel channel.Channel, operationId, address uint32) func() { // Initialize channels addressChannel := channel.New(config.InstructionsFetchedPerCycle()) // Branch Predictor -> Fetch instructionChannel := channel.New(config.InstructionsQueue()) // Fetch -> Decode operationChannel := channel.New(config.InstructionsDecodedQueue()) // Decode -> Dispatch executionChannels := map[info.CategoryEnum]channel.Channel{ // Dispatch -> Execute info.Aritmetic: channel.New(config.AluUnits()), info.LoadStore: channel.New(config.LoadStoreUnits()), info.Control: channel.New(config.BranchUnits()), info.FloatingPoint: channel.New(config.FpuUnits()), } commonDataBusChannel := channel.New(channel.INFINITE) // Execute -> Dispatcher (RS & ROB) closeUnitsHandlers := []func(){} ///////////////////////////////////////////////////////////////////////////// // Run stage units in parallel // // - Units are executed as go routines https://blog.golang.org/pipelines) // ///////////////////////////////////////////////////////////////////////////// // ---------- Fetch ------------ // fe := fetcher.New(uint32(0), this, config.InstructionsFetchedPerCycle(), config.BranchPredictorType()) fe.Run(addressChannel, instructionChannel) closeUnitsHandlers = append(closeUnitsHandlers, fe.Close) // ---------- Decode ------------ // for index := uint32(0); index < config.DecoderUnits(); index++ { de := decoder.New(index, this) de.Run(instructionChannel, operationChannel) closeUnitsHandlers = append(closeUnitsHandlers, de.Close) } // ----- Dispatch / RS / ROB ---- // di := dispatcher.New(uint32(0), this, operationId, config.TotalRegisters(), config.ReservationStationEntries(), config.ReorderBufferEntries(), config.InstructionsDispatchedPerCycle(), config.InstructionsWrittenPerCycle(), config.RegisterAliasTableEntries()) di.Run(operationChannel, executionChannels, commonDataBusChannel, recoveryChannel) closeUnitsHandlers = append(closeUnitsHandlers, di.Close) // ------- Execute (Alu) -------- // for index := uint32(0); index < config.AluUnits(); index++ { ex := executor.New(index, this, di.Bus(), info.Aritmetic) ex.Run(executionChannels, commonDataBusChannel) closeUnitsHandlers = append(closeUnitsHandlers, ex.Close) } // ---- Execute (Load Store) ---- // for index := uint32(0); index < config.LoadStoreUnits(); index++ { ex := executor.New(index, this, di.Bus(), info.LoadStore) ex.Run(executionChannels, commonDataBusChannel) closeUnitsHandlers = append(closeUnitsHandlers, ex.Close) } // ------ Execute (Branch) ------ // for index := uint32(0); index < config.BranchUnits(); index++ { ex := executor.New(index, this, di.Bus(), info.Control) ex.Run(executionChannels, commonDataBusChannel) closeUnitsHandlers = append(closeUnitsHandlers, ex.Close) } // ------- Execute (FPU) -------- // for index := uint32(0); index < config.FpuUnits(); index++ { ex := executor.New(index, this, di.Bus(), info.FloatingPoint) ex.Run(executionChannels, commonDataBusChannel) closeUnitsHandlers = append(closeUnitsHandlers, ex.Close) } // Set instruction for fetching to start pipeline go addressChannel.Add(operation.New(operationId, address)) // Return flush function for all channels return func() { // Close units & channels for _, closeUnitHandler := range closeUnitsHandlers { closeUnitHandler() } // Close channels addressChannel.Close() instructionChannel.Close() operationChannel.Close() executionChannels[info.Aritmetic].Close() executionChannels[info.LoadStore].Close() executionChannels[info.Control].Close() executionChannels[info.FloatingPoint].Close() commonDataBusChannel.Close() } }
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 *ReorderBuffer) Run(commonDataBus channel.Channel, recoveryBus channel.Channel) { // Launch unit as a goroutine logger.Print(" => Initializing re-order buffer unit %d", this.Index()) opId := this.StartOperationId() misprediction := false clockAllowed := this.Processor().Cycles() forceClose := false go func() { for { _, running := <-commonDataBus.Channel() if !running || misprediction { forceClose = true logger.Print(" => Flushing re-order buffer unit %d", this.Index()) return } commonDataBus.Release() } }() go func() { for { if forceClose { return } if this.Processor().Cycles() < clockAllowed { this.Processor().Wait(1) continue } // Commit in order, if missing an operation, wait for it computedAddress := uint32(0) robEntries := []RobEntry{} for robEntry, exists := this.Buffer()[opId]; exists; robEntry, exists = this.Buffer()[opId] { if uint32(len(robEntries)) >= this.InstructionsWrittenPerCycle() { break } // Ensure we can write results the next cycle result was written into ROB if this.Processor().Cycles() > robEntry.Cycle+1 { // Check for misprediction misprediction, computedAddress = this.checkForMisprediction(this.Buffer()[opId], robEntries) // Decrement speculative jumps this.Processor().DecrementSpeculativeJump() // Add to queue for commit robEntries = append(robEntries, robEntry) opId += 1 // If misprediction, do not process more rob entries if misprediction { break } } } this.commitRobEntries(robEntries) if misprediction { this.Processor().Wait(consts.WRITEBACK_CYCLES) recoveryBus.Add(operation.New(opId, computedAddress)) } clockAllowed = this.Processor().Cycles() + 1 } }() }