Skip to content
Snippets Groups Projects
Select Git revision
  • v3 default
  • update_akita
  • solve-TLB-memory-leak
  • generic_lmf
  • v2
  • dir_latency
  • 103-storage-should-be-able-to-set-granularity
  • 104-support-tracing-memory-responses
  • akita_v3
  • 102-limit-tlb-concurrency
  • domained_timing
  • master protected
  • 97-gl0_invalidate_navisim
  • 81-implement-colt-tlb-for-gpu
  • fix_timing_order_issue
  • 86-dram-detailed-modeling
  • coherency-hmg
  • round_robin
  • scheduling-research
  • dram
  • v3.0.0-alpha.10
  • v3.0.0-alpha.9
  • v3.0.0-alpha.8
  • v3.0.0-alpha.7
  • v3.0.0-alpha.6
  • v2.5.0
  • v3.0.0-alpha.5
  • v2.4.2
  • v3.0.0-alpha.4
  • v3.0.0-alpha.3
  • v2.4.1
  • v3.0.0-alpha.1
  • v2.4.0
  • v2.3.2
  • v2.3.1
  • v2.3.0
  • v2.2.0
  • v2.1.2
  • v2.1.1
  • v2.1.0
40 results

memoryagent.go

memoryagent.go 5.23 KiB
// Package acceptancetests provides utility data structure definitions for
// writing memory system acceptance tests.
package acceptancetests

import (
	"encoding/binary"
	"log"
	"math/rand"
	"reflect"

	"gitlab.com/akita/akita"
	"gitlab.com/akita/mem"
)

// A MemAccessAgent is a Component that can help testing the cache and the the
// memory controllers by generating a large number of read and write requests.
type MemAccessAgent struct {
	*akita.TickingComponent

	LowModule  akita.Port
	MaxAddress uint64

	WriteLeft       int
	ReadLeft        int
	KnownMemValue   map[uint64][]uint32
	PendingReadReq  map[string]*mem.ReadReq
	PendingWriteReq map[string]*mem.WriteReq

	ToMem akita.Port
}

func (a *MemAccessAgent) checkReadResult(
	read *mem.ReadReq,
	dataReady *mem.DataReadyRsp,
) {
	found := false
	var i int
	var value uint32
	result := BytesToUint32(dataReady.Data)
	for i, value = range a.KnownMemValue[read.Address] {
		if value == result {
			found = true
			break
		}
	}

	if found {
		a.KnownMemValue[read.Address] = a.KnownMemValue[read.Address][i:]
	} else {
		log.Panicf("Mismatch when read 0x%X", read.Address)
	}
}

func (a *MemAccessAgent) Tick(now akita.VTimeInSec) bool {
	madeProgress := false

	madeProgress = a.processMsgRsp(now) || madeProgress

	if a.ReadLeft == 0 && a.WriteLeft == 0 {
		return madeProgress
	}

	if a.shouldRead() {
		madeProgress = a.doRead(now) || madeProgress
	} else {
		madeProgress = a.doWrite(now) || madeProgress
	}

	return madeProgress
}

func (a *MemAccessAgent) processMsgRsp(now akita.VTimeInSec) bool {
	msg := a.ToMem.Retrieve(now)
	if msg == nil {
		return false
	}

	switch msg := msg.(type) {
	case *mem.WriteDoneRsp:
		// write := a.PendingWriteReq[msg.RespondTo]
		// log.Printf("%.12f, agent, write complete, 0x%X\n", now, write.Address)
		delete(a.PendingWriteReq, msg.RespondTo)
		return true
	case *mem.DataReadyRsp:
		req := a.PendingReadReq[msg.RespondTo]
		delete(a.PendingReadReq, msg.RespondTo)
		// log.Printf("%.12f, agent, read complete, 0x%X, %v\n",
		// now, req.Address, msg.Data)
		a.checkReadResult(req, msg)
		return true
	default:
		log.Panicf("cannot process message of type %s", reflect.TypeOf(msg))
	}

	return false
}

func (a *MemAccessAgent) shouldRead() bool {
	if len(a.KnownMemValue) == 0 {
		return false
	}

	if a.ReadLeft == 0 {
		return false
	}

	if a.WriteLeft == 0 {
		return true
	}

	dice := rand.Float64()
	return dice > 0.5
}

func (a *MemAccessAgent) doRead(now akita.VTimeInSec) bool {
	address := a.randomReadAddress()

	if a.isAddressInPendingReq(address) {
		return false
	}

	readReq := mem.ReadReqBuilder{}.
		WithSrc(a.ToMem).
		WithDst(a.LowModule).
		WithAddress(address).
		WithByteSize(4).
		WithPID(1).
		Build()
	readReq.SendTime = now
	err := a.ToMem.Send(readReq)
	if err == nil {
		a.PendingReadReq[readReq.ID] = readReq
		a.ReadLeft--
		// log.Printf("%.12f, agent, read, 0x%X\n", now, address)
		return true
	}
	return false
}

func (a *MemAccessAgent) randomReadAddress() uint64 {
	var addr uint64

	for {
		addr = rand.Uint64() % (a.MaxAddress / 4) * 4
		if _, written := a.KnownMemValue[addr]; written {
			return addr
		}
	}
}

func (a *MemAccessAgent) isAddressInPendingReq(addr uint64) bool {
	return a.isAddressInPendingWrite(addr) || a.isAddressInPendingRead(addr)
}

func (a *MemAccessAgent) isAddressInPendingWrite(addr uint64) bool {
	for _, write := range a.PendingWriteReq {
		if write.Address == addr {
			return true
		}
	}
	return false
}

func (a *MemAccessAgent) isAddressInPendingRead(addr uint64) bool {
	for _, read := range a.PendingReadReq {
		if read.Address == addr {
			return true
		}
	}
	return false
}

func Uint32ToBytes(data uint32) []byte {
	bytes := make([]byte, 4)
	binary.LittleEndian.PutUint32(bytes, data)
	return bytes
}

func BytesToUint32(data []byte) uint32 {
	a := uint32(0)
	a += uint32(data[0])
	a += uint32(data[1]) << 8
	a += uint32(data[2]) << 16
	a += uint32(data[3]) << 24
	return a
}

func (a *MemAccessAgent) doWrite(now akita.VTimeInSec) bool {
	address := rand.Uint64() % (a.MaxAddress / 4) * 4
	data := rand.Uint32()

	if a.isAddressInPendingReq(address) {
		return false
	}

	writeReq := mem.WriteReqBuilder{}.
		WithSrc(a.ToMem).
		WithDst(a.LowModule).
		WithAddress(address).
		WithPID(1).
		WithData(Uint32ToBytes(data)).
		Build()
	writeReq.SendTime = now

	err := a.ToMem.Send(writeReq)
	if err == nil {
		a.WriteLeft--
		a.addKnownValue(address, data)
		a.PendingWriteReq[writeReq.ID] = writeReq
		// log.Printf("%.12f, agent, write, 0x%X, %v\n",
		// now, address, writeReq.Data)
		return true
	}
	return false
}

func (a *MemAccessAgent) addKnownValue(address uint64, data uint32) {
	valueList, exist := a.KnownMemValue[address]
	if !exist {
		valueList = make([]uint32, 0)
		a.KnownMemValue[address] = valueList
	}
	valueList = append(valueList, data)
	a.KnownMemValue[address] = valueList
}

func NewMemAccessAgent(engine akita.Engine) *MemAccessAgent {
	agent := new(MemAccessAgent)
	agent.TickingComponent = akita.NewTickingComponent("Agent", engine, 1*akita.GHz, agent)
	agent.ToMem = akita.NewLimitNumMsgPort(agent, 1, "Agent.ToMem")

	agent.ReadLeft = 10000
	agent.WriteLeft = 10000
	agent.KnownMemValue = make(map[uint64][]uint32)
	agent.PendingWriteReq = make(map[string]*mem.WriteReq)
	agent.PendingReadReq = make(map[string]*mem.ReadReq)

	return agent
}