Пример #1
0
func printHeader() {
	logger.Print("")
	logger.Print("################################################")
	logger.Print("#     Superscalar Processor Simulator v1.0     #")
	logger.Print("################################################")
	logger.Print("")
}
Пример #2
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()
		}
	}()
}
Пример #3
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()
		}
	}()
}
Пример #4
0
func (this *Processor) NextCycle() int {
	if this.FinishedClock() {
		logger.Print(" => Program has finished\n")
		return consts.PROGRAM_FINISHED
	}

	this.WaitClock()
	return consts.PROGRAM_RUNNING
}
Пример #5
0
func (this *Processor) Start() {

	logger.CleanBuffer()
	logger.Print("\n Simulation:")
	logger.Print("\n => Starting program...")

	// Launch pipeline units and execute instruction 0x0000
	recoveryChannel := channel.New(1)
	flushFunc := this.StartPipelineUnits(this.Config(), recoveryChannel, 0, 0x0000)

	// Launch clock
	go this.RunClock()

	// Launch recovery
	go this.RunRecovery(recoveryChannel, flushFunc)

	logger.Print(" => Program is running...")
	logger.SetVerboseQuiet(true)
}
Пример #6
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)
				}
			}
		}
	}()
}
Пример #7
0
func TranslateFromFile(filename string, outputFilename string) (string, error) {

	// Read lines from file
	logger.Print(" => Reading assembly file: %s", filename)
	lines, err := utils.ReadLines(filename)
	if err != nil {
		return "", err
	}

	// Create output file
	f, err := os.Create(outputFilename)
	if err != nil {
		return "", err
	}
	defer f.Close()

	// Clean lines, remove labels and get map of labels
	memory, lines, labels := getLinesAndMapLabels(lines)

	// Print pre-filled memory macros
	for _, line := range memory {
		f.WriteString(fmt.Sprintf("%s\n", line))
	}

	// Translate instructions
	instructionSet := set.Init()
	for i, line := range lines {
		instruction, err := instructionSet.GetInstructionFromString(line, uint32(i)*consts.BYTES_PER_WORD, labels)
		if err != nil {
			return "", errors.New(fmt.Sprintf("Failed translating line %d: %s. %s", i, line, err.Error()))
		}
		hex := fmt.Sprintf("%08X", instruction.ToUint32())
		f.WriteString(fmt.Sprintf("%s // 0x%04X => %s\n", hex, i*consts.BYTES_PER_WORD, strings.Replace(line, "\t", " ", -1)))
	}
	logger.Print(" => Output hex file: %s", outputFilename)
	return outputFilename, nil
}
func (this *ReservationStation) Run(commonDataBus channel.Channel, output map[info.CategoryEnum]channel.Channel) {
	// Launch unit as a goroutine
	logger.Print(" => Initializing reservation station unit %d", this.Index())
	this.SetOutput(output)

	go func() {
		for this.reservationStation.isActive {
			this.Processor().Wait(consts.DISPATCH_CYCLES)
			this.reservationStation.instructionsDispatchedStack = 0
		}
	}()

	go this.runScheduler()
	go this.runCommonBusListener(commonDataBus)
}
Пример #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()
	}
}
Пример #10
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()
	}
}
Пример #11
0
func (this *Processor) loadInstructionsMemory(assemblyFileName string) error {

	logger.Print(" => Reading hex file: %s", assemblyFileName)
	lines, err := utils.ReadLines(assemblyFileName)
	if err != nil {
		return err
	}

	address := uint32(0)
	for _, line := range lines {

		// Split hex value and humand readable comment
		parts := strings.Split(line, "//")

		// Save human readable for debugging purposes
		if len(parts) > 1 {
			this.InstructionsMap()[address] = strings.TrimSpace(parts[1])
		}

		// Reach pre-filled memory data lines
		if strings.Contains(line, "@0x") {
			parts := strings.Split(strings.Replace(line, "@0x", "", -1), ":")
			err = this.processPreFilledDataMemoryLine(parts[0], strings.Split(parts[1], " "))
			if err != nil {
				return errors.New(fmt.Sprintf("Failed parsing memory macro. %s", err.Error()))
			}
			continue
		}

		// Save hex value into instructions memory
		bytes, err := hex.DecodeString(strings.TrimSpace(parts[0]))
		if err != nil {
			return errors.New(fmt.Sprintf("Failed parsing instruction (hex) value: %s. %s", parts[0], err.Error()))
		}
		this.InstructionsMemory().Store(address, bytes...)

		// Increment address
		address += uint32(len(bytes))
	}
	for i := address; i < this.InstructionsMemory().Size(); i++ {
		this.InstructionsMemory().Store(i, []byte{consts.ENDING_BYTE}...)
	}
	return nil
}
Пример #12
0
func runProgram(assemblyFilename string, interactive bool, outputFolder string, config *config.Config, maxCycles uint32) error {

	err := os.MkdirAll(outputFolder, 0777)
	if err != nil {
		return err
	}

	// Translate assembly file to hex file
	hexFilename, err := translator.TranslateFromFile(assemblyFilename, filepath.Join(outputFolder, "assembly.hex"))
	if err != nil {
		return err
	}

	// Instanciate processor
	p, err := processor.New(hexFilename, config)
	if err != nil {
		return err
	}

	// Start simulation
	p.Start()

	// Run as many instructions as they are
	result := consts.PROGRAM_RUNNING
	for result == consts.PROGRAM_RUNNING {
		p.PauseClock()
		if interactive {
			for runInteractiveStep(p) {
			}
		}
		// Unpause and execute next cycle
		p.ContinueClock()
		result = p.NextCycle()
		// If max cycles option selected
		if maxCycles > 0 && p.Cycles() >= maxCycles {
			p.PauseClock()
			break
		}
	}

	logger.Print(p.Stats())
	return p.SaveOutputFiles(outputFolder)
}
Пример #13
0
func New(assemblyFileName string, config *config.Config) (*Processor, error) {

	p := &Processor{
		&processor{
			done:                  false,
			instructionsFetched:   []string{},
			instructionsCompleted: []uint32{},
			dataLog:               map[uint32][]LogEvent{},

			branchHistoryTable:    map[uint32]uint32{},
			conditionalBranches:   0,
			unconditionalBranches: 0,
			mispredictedBranches:  0,
			noTakenBranches:       0,
			branchPredictorBits:   0,
			speculativeJumps:      0,

			instructionsMap: map[uint32]string{},
			instructionsSet: set.Init(),
			config:          config,

			programCounter:    0,
			registerMemory:    memory.New(config.RegistersMemorySize()),
			instructionMemory: memory.New(config.InstructionsMemorySize()),
			dataMemory:        memory.New(config.DataMemorySize()),
		},
	}

	logger.Print(config.ToString())

	// Instanciate functional units
	instructionsFinished := func() bool {
		return p.processor.done && p.InstructionsFetchedCounter() == p.InstructionsCompletedCounter() && p.SpeculativeJumps() == 0
	}
	p.processor.clockUnit = clock.New(config.CyclePeriod(), instructionsFinished)

	err := p.loadInstructionsMemory(assemblyFileName)
	if err != nil {
		return p, err
	}
	return p, nil
}
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()
	}
}
Пример #15
0
func runCommand(c *cli.Context) {

	printHeader()
	logger.SetVerboseDebug(c.Bool("verbose"))

	if len(c.Args()) != 1 {
		logger.Error("Expecting <assembly-filename> and got %d parameters", len(c.Args()))
		os.Exit(1)
	}

	assemblyFilename, _ := filepath.Abs(c.Args()[0])
	if _, err := os.Stat(assemblyFilename); os.IsNotExist(err) {
		logger.Error("File %s does not exists", assemblyFilename)
		os.Exit(1)
	}

	outputFolder, _ := filepath.Abs(c.String("output-folder"))
	if outputFolder == "" {
		outputFolder = filepath.Join(filepath.Dir(assemblyFilename), getFileName(assemblyFilename))
	}

	configFilename, _ := filepath.Abs(c.String("config-filename"))
	if configFilename == "" {
		logger.Error("Configuration file not provided, please provide a valid configuration file")
		os.Exit(1)
	}

	cfg, err := config.Load(configFilename)
	if err != nil {
		logger.Error("Failed loading config. %s", err.Error())
		os.Exit(1)
	}
	logger.Print(" => Configuration file: %s", configFilename)

	err = runProgram(assemblyFilename, c.Bool("step-by-step"), outputFolder, cfg, uint32(c.Int("max-cycles")))
	if err != nil {
		logger.Error(err.Error())
		os.Exit(1)
	}
}
Пример #16
0
func (this *Processor) SaveOutputFiles(outputFolder string) error {

	logger.Print("\n Output Files:\n")

	// Save debug buffer
	filename := filepath.Join(outputFolder, "debug.log")
	err := logger.WriteBuffer(filename)
	if err != nil {
		return err
	}
	logger.Print(" => Debug buffer saved at %s", filename)

	// Save memory file
	filename = filepath.Join(outputFolder, "memory.dat")
	err = ioutil.WriteFile(filename, []byte(this.DataMemory().ToString()), 0644)
	if err != nil {
		return err
	}
	logger.Print(" => Data memory saved at %s", filename)

	// Save registers file
	filename = filepath.Join(outputFolder, "registers.dat")
	err = ioutil.WriteFile(filename, []byte(this.RegistersMemory().ToString()), 0644)
	if err != nil {
		return err
	}
	logger.Print(" => Registers memory saved at %s", filename)

	// Save pipeline flow
	filename = filepath.Join(outputFolder, "pipeline.dat")
	err = ioutil.WriteFile(filename, []byte(this.PipelineFlow()), 0644)
	if err != nil {
		return err
	}
	logger.Print(" => Pipeline flow saved at %s", filename)

	// Save stats
	filename = filepath.Join(outputFolder, "output.log")
	err = ioutil.WriteFile(filename, []byte(this.Config().ToString()+this.Stats()), 0644)
	if err != nil {
		return err
	}
	logger.Print(" => Output saved at %s", filename)

	return nil
}
Пример #17
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)
}
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
		}
	}()
}
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()
	}
}