Esempio n. 1
0
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()
	}
}
Esempio n. 2
0
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
		}
	}()
}