예제 #1
0
func (this *Executor) Run(input map[info.CategoryEnum]channel.Channel, commonDataBus channel.Channel) {
	// Launch each unit as a goroutine
	unit, event := this.getUnitFromCategory(this.Category())
	logger.Print(" => Initializing execution unit (%s) %d", this.Category(), this.Index())
	go func() {
		for {
			value, running := <-input[this.Category()].Channel()
			if !running || !this.IsActive() {
				logger.Print(" => Flushing execution unit (%s) %d", this.Category(), this.Index())
				return
			}
			op := operation.Cast(value)
			for i := uint8(0); i < op.Instruction().Info.Cycles; i++ {
				this.Processor().Wait(1)
			}

			// Iterate instructions received via the channel
			op, _ = this.executeOperation(unit, event, op)
			// Send data to common bus for reservation station feedback
			commonDataBus.Add(op)
			// Release one item from input Channel
			input[this.Category()].Release()
		}
	}()
}
예제 #2
0
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()
	}
}
예제 #3
0
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) 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()
	}
}
예제 #5
0
func (this *Decoder) Run(input, output channel.Channel) {
	// Launch each unit as a goroutine
	logger.Print(" => Initializing decoder unit %d", this.Index())
	go func() {
		for {
			value, running := <-input.Channel()
			if !running || !this.IsActive() {
				logger.Print(" => Flushing decoder unit %d", this.Index())
				return
			}
			op := operation.Cast(value)
			// Iterate instructions received via the channel
			instruction, err := this.decodeInstruction(op)
			if err != nil {
				logger.Error(err.Error())
				break
			}
			// Send data to output
			op.SetInstruction(instruction)
			output.Add(op)
			// Release one item from input Channel
			input.Release()
		}
	}()
}
예제 #6
0
func (this *Dispatcher) Run(input channel.Channel, output map[info.CategoryEnum]channel.Channel, commonDataBus, recoveryBus channel.Channel) {

	// Create register alias table
	rat := registeraliastable.New(this.Index(), this.RegisterAliasTableEntries())

	// Create re-order buffer
	rob := reorderbuffer.New(this.Index(),
		this.Processor(),
		this.StartOperationId(),
		this.ReorderBufferEntries(),
		this.InstructionsWrittenPerCycle(),
		rat)
	commonDataBusROB := channel.New(commonDataBus.Capacity())

	// Create reservation station
	rs := reservationstation.New(this.Index(), this.Processor(),
		this.Registers(),
		this.ReservationStationEntries(),
		this.InstructionsFetchedPerCycle(),
		rat, rob.Bus())
	commonDataBusRS := channel.New(commonDataBus.Capacity())

	// Create storage bus
	this.dispatcher.bus = rob.Bus()

	// Launch each unit as a goroutine
	logger.Print(" => Initializing dispatcher unit %d", this.Index())

	// Start dispatcher of operations to be executed into reservation station
	go this.runDispatcherToReservationStation(input, rs, rat, rob)
	// Start common bus multiplexer to send ack to reservation station and reorder buffer
	go this.runCommonBusMultiplexer(commonDataBus, commonDataBusRS, commonDataBusROB)

	// Run reservation station
	rs.Run(commonDataBusRS, output)
	// Run re-order buffer
	rob.Run(commonDataBusROB, recoveryBus)
}
예제 #7
0
func (this *Fetcher) Run(input, output channel.Channel) {
	logger.Print(" => Initializing fetcher unit %d", this.Index())
	// Launch each unit as a goroutine
	go func() {
		for {
			value, running := <-input.Channel()
			if !running || !this.IsActive() {
				logger.Print(" => Flushing fetcher unit %d", this.Index())
				return
			}
			// Release item from input Channel
			input.Release()

			// Initial operation (address)
			op := operation.Cast(value)

			// Load instructions data from memory
			data := this.Processor().InstructionsMemory().Load(op.Address(), consts.BYTES_PER_WORD*this.InstructionsFetchedPerCycle())

			// Fetch instructions
			startCycles := this.Processor().Cycles()
			operations, err := this.fetchInstructions(op, data, input)
			if err != nil {
				logger.Error(err.Error())
				break
			}

			// Wait cycles of a fetch stage
			this.Processor().Wait(consts.FETCH_CYCLES)

			// After wait cycle, notify decode channel with new operations
			for _, op := range operations {
				if this.IsActive() {
					this.Processor().LogEvent(consts.FETCH_EVENT, this.Index(), op.Id(), startCycles)
					output.Add(op)
				}
			}
		}
	}()
}
예제 #8
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
}
예제 #9
0
func (this *Dispatcher) runCommonBusMultiplexer(input, output1, output2 channel.Channel) {
	// For each result got from execution units in the common data bus send to RS and ROB
	for {
		value, running := <-input.Channel()
		if !running {
			output1.Close()
			output2.Close()
			logger.Print(" => Flushing dispatcher unit %d (CDB Mux)", this.Index())
			return
		}
		output1.Add(value)
		output2.Add(value)
		input.Release()
	}
}
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
		}
	}()
}