Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions api/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,28 @@ package api
import "github.com/sarchlab/akita/v4/sim"

type defaultPortFactory struct {
incomingBufCap int
outgoingBufCap int
}

func (f defaultPortFactory) make(c sim.Component, name string) sim.Port {
return sim.NewPort(c, 1, 1, name)
incoming := f.incomingBufCap
if incoming <= 0 {
incoming = 1
}
outgoing := f.outgoingBufCap
if outgoing <= 0 {
outgoing = 1
}
return sim.NewPort(c, incoming, outgoing, name)
}

// DriverBuilder creates a new instance of Driver.
type DriverBuilder struct {
engine sim.Engine
freq sim.Freq
engine sim.Engine
freq sim.Freq
portIncomingBufferCap int
portOutgoingBufferCap int
}

// WithEngine sets the engine.
Expand All @@ -27,10 +39,20 @@ func (b DriverBuilder) WithFreq(freq sim.Freq) DriverBuilder {
return b
}

// WithPortBufferDepth configures driver boundary-port incoming/outgoing capacity.
func (b DriverBuilder) WithPortBufferDepth(incoming, outgoing int) DriverBuilder {
b.portIncomingBufferCap = incoming
b.portOutgoingBufferCap = outgoing
return b
}

// Build create a driver.
func (b DriverBuilder) Build(name string) Driver {
d := &driverImpl{
portFactory: defaultPortFactory{},
portFactory: defaultPortFactory{
incomingBufCap: b.portIncomingBufferCap,
outgoingBufCap: b.portOutgoingBufferCap,
},
}

d.TickingComponent = sim.NewTickingComponent(name, b.engine, b.freq, d)
Expand Down
29 changes: 29 additions & 0 deletions api/builder_microarch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package api

import (
"testing"

"github.com/sarchlab/akita/v4/sim"
)

func TestDriverBuilderWithPortBufferDepth(t *testing.T) {
engine := sim.NewSerialEngine()
driver := DriverBuilder{}.
WithEngine(engine).
WithFreq(1*sim.GHz).
WithPortBufferDepth(3, 5).
Build("Driver")

impl, ok := driver.(*driverImpl)
if !ok {
t.Fatalf("expected *driverImpl, got %T", driver)
}

factory, ok := impl.portFactory.(defaultPortFactory)
if !ok {
t.Fatalf("expected defaultPortFactory, got %T", impl.portFactory)
}
if factory.incomingBufCap != 3 || factory.outgoingBufCap != 5 {
t.Fatalf("unexpected driver port caps: in=%d out=%d", factory.incomingBufCap, factory.outgoingBufCap)
}
}
47 changes: 29 additions & 18 deletions api/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,23 @@ func (d *driverImpl) doOneFeedInTask(task *feedInTask) bool {
err := port.Send(msg)
//fmt.Println(msg)
if err != nil {
panic("CGRA cannot handle the data rate")
// Keep task pending when downstream is temporarily back-pressured.
continue
}

core.Trace("DataFlow",
"Behavior", "FeedIn",
slog.Float64("Time", float64(d.Engine.CurrentTime()*1e9)),
"Data", task.data[dataIndex],
"Color", task.color,
"From", port.Name(),
"To", task.remotePorts[i],
)
timeValue := float64(d.Engine.CurrentTime() * 1e9)
if core.TraceEnabled() {
core.Trace("DataFlow",
"Behavior", "FeedIn",
slog.Float64("Time", timeValue),
"Data", task.data[dataIndex],
"Color", task.color,
"From", port.Name(),
"To", task.remotePorts[i],
)
} else {
core.ObserveDataFlow("FeedIn", timeValue, port.Name(), string(task.remotePorts[i]), "", "")
}
task.portRounds[i]++
madeProgress = true
}
Expand Down Expand Up @@ -202,15 +208,20 @@ func (d *driverImpl) doOneCollectTask(task *collectTask) bool {
}
task.data[dataIndex] = msg.Data.First()

core.Trace("DataFlow",
"Behavior", "Collect",
slog.Float64("Time", float64(d.Engine.CurrentTime()*1e9)),
"Data", msg.Data.First(),
"Pred", msg.Data.Pred,
"Color", task.color,
"From", task.ports[i].Name(),
"To", "None",
)
timeValue := float64(d.Engine.CurrentTime() * 1e9)
if core.TraceEnabled() {
core.Trace("DataFlow",
"Behavior", "Collect",
slog.Float64("Time", timeValue),
"Data", msg.Data.First(),
"Pred", msg.Data.Pred,
"Color", task.color,
"From", task.ports[i].Name(),
"To", "None",
)
} else {
core.ObserveDataFlow("Collect", timeValue, task.ports[i].Name(), "None", "", "")
}

task.portRounds[i]++
madeProgress = true
Expand Down
48 changes: 48 additions & 0 deletions api/feedin_backpressure_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package api

import (
"testing"

gomock "github.com/golang/mock/gomock"
"github.com/sarchlab/akita/v4/sim"
)

func TestDoOneFeedInTaskBackpressureDoesNotPanic(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

engine := sim.NewSerialEngine()
d := &driverImpl{}
d.TickingComponent = sim.NewTickingComponent("Driver", engine, 1*sim.GHz, d)

port := NewMockPort(ctrl)
port.EXPECT().CanSend().Return(true).Times(2)
port.EXPECT().Name().Return("mock-port").AnyTimes()
port.EXPECT().AsRemote().Return(sim.RemotePort("driver-local")).AnyTimes()
port.EXPECT().Send(gomock.Any()).Return(sim.NewSendError()).Times(1)
port.EXPECT().Send(gomock.Any()).Return(nil).Times(1)

task := &feedInTask{
data: []uint32{7},
localPorts: []sim.Port{port},
remotePorts: []sim.RemotePort{sim.RemotePort("device-remote")},
stride: 1,
color: 0,
rounds: 1,
portRounds: []int{0},
}

if progressed := d.doOneFeedInTask(task); progressed {
t.Fatal("expected no progress when Send returns backpressure error")
}
if task.portRounds[0] != 0 {
t.Fatalf("expected round to stay 0 after backpressure, got %d", task.portRounds[0])
}

if progressed := d.doOneFeedInTask(task); !progressed {
t.Fatal("expected progress once backpressure clears")
}
if task.portRounds[0] != 1 {
t.Fatalf("expected round to advance to 1, got %d", task.portRounds[0])
}
}
78 changes: 75 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,19 @@ type DeviceBuilder struct {
freq sim.Freq
monitor *monitoring.Monitor
//portFactory portFactory
width, height int
memoryMode string // simple or shared or local
memoryShare map[[2]int]int //map[[x, y]]GroupID
width, height int
memoryMode string // simple or shared or local
memoryShare map[[2]int]int //map[[x, y]]GroupID
executionPolicy string
strictMaxSlip int64
strictFailOnViolation bool
corePortIncomingCap int
corePortOutgoingCap int
enableFIFOModel bool
enableQueueWatches bool
queueWatches []core.QueueWatchSpec
numRegisters int
localMemoryWords int
}

// type portFactory interface {
Expand Down Expand Up @@ -74,6 +84,60 @@ func (d DeviceBuilder) WithMemoryShare(share map[[2]int]int) DeviceBuilder {
return d
}

// WithExecutionPolicy sets core execution policy.
func (d DeviceBuilder) WithExecutionPolicy(policy string) DeviceBuilder {
d.executionPolicy = policy
return d
}

// WithStrictTimingConfig sets strict timing replay controls.
func (d DeviceBuilder) WithStrictTimingConfig(maxSlip int64, failOnViolation bool) DeviceBuilder {
d.strictMaxSlip = maxSlip
d.strictFailOnViolation = failOnViolation
return d
}

// WithCorePortBufferDepth sets core port incoming/outgoing capacities.
func (d DeviceBuilder) WithCorePortBufferDepth(incoming, outgoing int) DeviceBuilder {
d.corePortIncomingCap = incoming
d.corePortOutgoingCap = outgoing
return d
}

// WithEnableFIFOModel toggles FIFO-based core execution model.
func (d DeviceBuilder) WithEnableFIFOModel(enabled bool) DeviceBuilder {
d.enableFIFOModel = enabled
return d
}

// WithEnableQueueWatches toggles optional queue-occupancy instrumentation.
func (d DeviceBuilder) WithEnableQueueWatches(enabled bool) DeviceBuilder {
d.enableQueueWatches = enabled
return d
}

// WithQueueWatches sets optional queue watch definitions for all cores.
func (d DeviceBuilder) WithQueueWatches(queueWatches []core.QueueWatchSpec) DeviceBuilder {
if len(queueWatches) == 0 {
d.queueWatches = nil
return d
}
d.queueWatches = append([]core.QueueWatchSpec(nil), queueWatches...)
return d
}

// WithRegisterCount sets register-file size per core.
func (d DeviceBuilder) WithRegisterCount(num int) DeviceBuilder {
d.numRegisters = num
return d
}

// WithLocalMemoryWords sets local memory size (in words) per core.
func (d DeviceBuilder) WithLocalMemoryWords(words int) DeviceBuilder {
d.localMemoryWords = words
return d
}

// Build creates a CGRA device.
func (d DeviceBuilder) Build(name string) cgra.Device {
dev := &device{
Expand Down Expand Up @@ -188,6 +252,14 @@ func (d DeviceBuilder) createTiles(
WithExitAddr(&exit).
WithRetValAddr(&retVal).
WithExitReqAddr(&exitReqTimestamp).
WithExecutionPolicy(d.executionPolicy).
WithStrictTimingConfig(d.strictMaxSlip, d.strictFailOnViolation).
WithPortBufferDepth(d.corePortIncomingCap, d.corePortOutgoingCap).
WithEnableFIFOModel(d.enableFIFOModel).
WithEnableQueueWatches(d.enableQueueWatches).
WithQueueWatches(d.queueWatches).
WithRegisterCount(d.numRegisters).
WithLocalMemoryWords(d.localMemoryWords).
Build(coreName)

if d.monitor != nil {
Expand Down
35 changes: 35 additions & 0 deletions config/config_microarch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package config

import (
"testing"

"github.com/sarchlab/akita/v4/sim"
)

func TestDeviceBuilderLocalMemoryWordsPropagatesToTile(t *testing.T) {
engine := sim.NewSerialEngine()
dev := DeviceBuilder{}.
WithEngine(engine).
WithFreq(1 * sim.GHz).
WithWidth(1).
WithHeight(1).
WithMemoryMode("simple").
WithLocalMemoryWords(32).
Build("Device")

tile := dev.GetTile(0, 0)
_ = tile.GetMemory(0, 0, 31)

didPanic := false
func() {
defer func() {
if recover() != nil {
didPanic = true
}
}()
_ = tile.GetMemory(0, 0, 32)
}()
if !didPanic {
t.Fatal("expected out-of-range panic at address 32 with local_memory_words=32")
}
}
Loading
Loading