Skip to content

Commit 353c8c8

Browse files
authored
Merge pull request #58 from sarchlab/codex/shared-memory-banked-ld-st
Codex/shared memory banked ld st
2 parents 5f33240 + 8e9f04b commit 353c8c8

110 files changed

Lines changed: 76404 additions & 2224 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ output/
2323

2424
# logs
2525
*.log
26+
*.json.log
27+
28+
# generated reports and traces
29+
*.report.json
30+
tmp-generated-*
31+
**/collected_reports*/
32+
**/sweep_reports*/
33+
**/sweep_reports_micro*/
34+
latest_collected_reports
35+
36+
# local build artifacts
37+
/main
38+
39+
# Python caches
40+
__pycache__/
41+
*.pyc
2642

2743
.venv/
28-
venv/
44+
venv/

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ go run ./verify/cmd/verify-histogram # outputs histogram_verification_report
4848
## Architecture (How it fits together)
4949
- **Tile/Core** (`core/`): Instruction emulator + cycle-accurate send/recv paths on Akita ports; traces dataflow/memory events. Opcodes implemented in `core/emu.go`, state in `core/core.go`.
5050
- **CGRA device** (`cgra/`, `config/`): Mesh wiring of tiles; configurable memory mode (`simple`, `shared`, `local`) via `config.DeviceBuilder`. Uses Akita direct connections and optional shared memory controllers.
51+
- **Shared SRAM** (`config/`, `core/`): `memory_mode: "shared"` with `shared_memory_model: "banked"` models a shared SRAM scratchpad with per-bank conflict timing for compiler-style blocking `LOAD`/`STORE`; see `docs/shared-memory.md`.
5152
- **Driver** (`api/driver.go`): Maps per-PE kernels, feeds inputs, collects outputs, and ticks the simulation engine. Supports preload/read of per-PE memory.
5253
- **Verification fast path** (`verify/`): Static lint (STRUCT/TIMING) + functional simulator + report generator. Mirrors opcode semantics without timing/backpressure; CLIs in `verify/cmd/`.
5354
- **Testbench** (`test/Zeonica_Testbench` submodule): Reference kernels (AXPY, histogram, etc.) consumed by verify and simulation tests.
@@ -81,4 +82,4 @@ flowchart LR
8182
- If adding opcodes, update `core/emu.go` and `verify/funcsim.go`, plus unit tests in `verify/`.
8283

8384
## License
84-
See `LICENSE` for details.
85+
See `LICENSE` for details.

api/builder.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,28 @@ package api
33
import "github.com/sarchlab/akita/v4/sim"
44

55
type defaultPortFactory struct {
6+
incomingBufCap int
7+
outgoingBufCap int
68
}
79

810
func (f defaultPortFactory) make(c sim.Component, name string) sim.Port {
9-
return sim.NewPort(c, 1, 1, name)
11+
incoming := f.incomingBufCap
12+
if incoming <= 0 {
13+
incoming = 1
14+
}
15+
outgoing := f.outgoingBufCap
16+
if outgoing <= 0 {
17+
outgoing = 1
18+
}
19+
return sim.NewPort(c, incoming, outgoing, name)
1020
}
1121

1222
// DriverBuilder creates a new instance of Driver.
1323
type DriverBuilder struct {
14-
engine sim.Engine
15-
freq sim.Freq
24+
engine sim.Engine
25+
freq sim.Freq
26+
portIncomingBufferCap int
27+
portOutgoingBufferCap int
1628
}
1729

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

42+
// WithPortBufferDepth configures driver boundary-port incoming/outgoing capacity.
43+
func (b DriverBuilder) WithPortBufferDepth(incoming, outgoing int) DriverBuilder {
44+
b.portIncomingBufferCap = incoming
45+
b.portOutgoingBufferCap = outgoing
46+
return b
47+
}
48+
3049
// Build create a driver.
3150
func (b DriverBuilder) Build(name string) Driver {
3251
d := &driverImpl{
33-
portFactory: defaultPortFactory{},
52+
portFactory: defaultPortFactory{
53+
incomingBufCap: b.portIncomingBufferCap,
54+
outgoingBufCap: b.portOutgoingBufferCap,
55+
},
3456
}
3557

3658
d.TickingComponent = sim.NewTickingComponent(name, b.engine, b.freq, d)

api/builder_microarch_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package api
2+
3+
import (
4+
"testing"
5+
6+
"github.com/sarchlab/akita/v4/sim"
7+
)
8+
9+
func TestDriverBuilderWithPortBufferDepth(t *testing.T) {
10+
engine := sim.NewSerialEngine()
11+
driver := DriverBuilder{}.
12+
WithEngine(engine).
13+
WithFreq(1*sim.GHz).
14+
WithPortBufferDepth(3, 5).
15+
Build("Driver")
16+
17+
impl, ok := driver.(*driverImpl)
18+
if !ok {
19+
t.Fatalf("expected *driverImpl, got %T", driver)
20+
}
21+
22+
factory, ok := impl.portFactory.(defaultPortFactory)
23+
if !ok {
24+
t.Fatalf("expected defaultPortFactory, got %T", impl.portFactory)
25+
}
26+
if factory.incomingBufCap != 3 || factory.outgoingBufCap != 5 {
27+
t.Fatalf("unexpected driver port caps: in=%d out=%d", factory.incomingBufCap, factory.outgoingBufCap)
28+
}
29+
}

api/driver.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type Driver interface {
4343

4444
//
4545
ReadMemory(x int, y int, addr uint32) uint32
46+
ReadSharedMemory(x int, y int, addr uint32) uint32
4647

4748
// Run will run all the tasks that have been added to the driver.
4849
Run()
@@ -80,6 +81,11 @@ func (d *driverImpl) ReadMemory(x int, y int, addr uint32) uint32 {
8081
return tile.GetMemory(x, y, addr)
8182
}
8283

84+
func (d *driverImpl) ReadSharedMemory(x int, y int, addr uint32) uint32 {
85+
tile := d.device.GetTile(x, y)
86+
return tile.ReadSharedMemory(x, y, addr)
87+
}
88+
8389
// Tick runs the driver for one cycle.
8490
func (d *driverImpl) Tick() (madeProgress bool) {
8591
madeProgress = d.doFeedIn() || madeProgress
@@ -142,7 +148,8 @@ func (d *driverImpl) doOneFeedInTask(task *feedInTask) bool {
142148
err := port.Send(msg)
143149
//fmt.Println(msg)
144150
if err != nil {
145-
panic("CGRA cannot handle the data rate")
151+
// Keep task pending when downstream is temporarily back-pressured.
152+
continue
146153
}
147154

148155
timeValue := float64(d.Engine.CurrentTime() * 1e9)

api/feedin_backpressure_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package api
2+
3+
import (
4+
"testing"
5+
6+
gomock "github.com/golang/mock/gomock"
7+
"github.com/sarchlab/akita/v4/sim"
8+
)
9+
10+
func TestDoOneFeedInTaskBackpressureDoesNotPanic(t *testing.T) {
11+
ctrl := gomock.NewController(t)
12+
defer ctrl.Finish()
13+
14+
engine := sim.NewSerialEngine()
15+
d := &driverImpl{}
16+
d.TickingComponent = sim.NewTickingComponent("Driver", engine, 1*sim.GHz, d)
17+
18+
port := NewMockPort(ctrl)
19+
port.EXPECT().CanSend().Return(true).Times(2)
20+
port.EXPECT().Name().Return("mock-port").AnyTimes()
21+
port.EXPECT().AsRemote().Return(sim.RemotePort("driver-local")).AnyTimes()
22+
port.EXPECT().Send(gomock.Any()).Return(sim.NewSendError()).Times(1)
23+
port.EXPECT().Send(gomock.Any()).Return(nil).Times(1)
24+
25+
task := &feedInTask{
26+
data: []uint32{7},
27+
localPorts: []sim.Port{port},
28+
remotePorts: []sim.RemotePort{sim.RemotePort("device-remote")},
29+
stride: 1,
30+
color: 0,
31+
rounds: 1,
32+
portRounds: []int{0},
33+
}
34+
35+
if progressed := d.doOneFeedInTask(task); progressed {
36+
t.Fatal("expected no progress when Send returns backpressure error")
37+
}
38+
if task.portRounds[0] != 0 {
39+
t.Fatalf("expected round to stay 0 after backpressure, got %d", task.portRounds[0])
40+
}
41+
42+
if progressed := d.doOneFeedInTask(task); !progressed {
43+
t.Fatal("expected progress once backpressure clears")
44+
}
45+
if task.portRounds[0] != 1 {
46+
t.Fatalf("expected round to advance to 1, got %d", task.portRounds[0])
47+
}
48+
}

api/mock_cgra_test.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cgra/cgra.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ type Tile interface {
7777
GetMemory(x int, y int, addr uint32) uint32
7878
WriteMemory(x int, y int, data uint32, baseAddr uint32)
7979
WriteSharedMemory(x int, y int, data []byte, baseAddr uint32)
80+
ReadSharedMemory(x int, y int, addr uint32) uint32
8081
GetTileX() int
8182
GetTileY() int
8283
GetRetVal() uint32

cgra/data.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,26 @@ func (d Data) First() uint32 {
2424
return d.Data[0]
2525
}
2626

27+
// LaneCount returns the number of lanes carried by this value.
28+
func (d Data) LaneCount() int {
29+
return len(d.Data)
30+
}
31+
32+
// IsScalar reports whether the value is a single-lane scalar.
33+
func (d Data) IsScalar() bool {
34+
return len(d.Data) <= 1
35+
}
36+
37+
// Clone returns a deep copy of the value container.
38+
func (d Data) Clone() Data {
39+
if len(d.Data) == 0 {
40+
return Data{Pred: d.Pred}
41+
}
42+
cloned := make([]uint32, len(d.Data))
43+
copy(cloned, d.Data)
44+
return Data{Data: cloned, Pred: d.Pred}
45+
}
46+
2747
// WithPred returns a copy with the given predicate flag.
2848
func (d Data) WithPred(pred bool) Data {
2949
d.Pred = pred
@@ -32,5 +52,10 @@ func (d Data) WithPred(pred bool) Data {
3252

3353
// FromSlice constructs a Data from a slice and optional predicate.
3454
func FromSlice(vals []uint32, pred bool) Data {
35-
return Data{Data: vals, Pred: pred}
55+
if len(vals) == 0 {
56+
return Data{Pred: pred}
57+
}
58+
cloned := make([]uint32, len(vals))
59+
copy(cloned, vals)
60+
return Data{Data: cloned, Pred: pred}
3661
}

0 commit comments

Comments
 (0)