aboutsummaryrefslogtreecommitdiff
path: root/src/cpu/riscv/simt_riscv64.c
blob: 68e87a58b453d123872cdc5c2e7d63baeeaf3fa5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#include <gran/cpu/riscv/simt_riscv64.h>

/* use simple rv64 cores are lanes for now at least, possibly extend simple
 * model with some cross-lane interface for barriers etc. */
#include <gran/cpu/riscv/simple_riscv64.h>

/* vector of riscv cores */
#define VEC_NAME lanes
#define VEC_TYPE struct component *
#include <conts/vec.h>

/* vector of requests */
struct req {
	struct reg reg;
	bool queued;
};

#define VEC_NAME reqs
#define VEC_TYPE struct req
#include <conts/vec.h>

struct simt_riscv64 {
	struct component component;
	struct simt_riscv64_conf conf;
	struct component *imem;
	struct component *dmem;

	struct lanes lanes;

	struct component imem_intf;
	struct component dmem_intf;

	struct reqs data_rqs;
	struct reqs inst_rqs;

	size_t rr;
};

static stat simt_riscv64_ext_send(uint64_t rcv, struct component *intf, struct component *mem, struct packet pkt)
{
	/* do fixups for sending */
	if (is_set(&pkt, PACKET_READ))
		pkt.mask = ~0ULL;

	/* smuggle index as receive address */
	uint32_t i = (uint32_t)(pkt.from >> 32);
	pkt.from = rcv | i;
	return SEND(intf, mem, pkt);
}

static stat simt_riscv64_handle_response(struct simt_riscv64 *c,
		struct component *intf, struct reqs *reqs,
		struct packet pkt)
{
	/* use index smuggled as address to check which request this is a
	 * response to */
	uint32_t idx = (uint32_t)pkt.to;
	struct req *s = reqs_at(reqs, (size_t)idx);

	/* sanity check response, a bit crude for now */
	assert(s);
	assert(s->reg.pkt.to == pkt.from);
	assert(s->reg.busy == true);
	assert( is_set(&pkt, PACKET_DONE));
	assert(!is_set(&pkt, PACKET_ERROR));

	/* reads are always full width to maximize chance of all lanes hitting
	 * same cacheline.
	 *
	 * Probably not ideal for instructions when not lockstepping...?
	 */
	assert(is_set(&pkt, PACKET_READ) ? pkt.mask == ~0ULL : 1);

	/* use mask to indicate which portion of packet was meant for
	 * this lane */
	pkt.mask = s->reg.pkt.mask;

	/* use original from address */
	pkt.to = s->reg.pkt.from;

	struct component *lane = *lanes_at(&c->lanes, idx);
	stat ok = SEND(intf, lane, pkt);
	assert(ok == OK);

	s->reg.busy = false;

	/* possibly broadcast response to readers */
	if (is_set(&pkt, PACKET_READ))
	for (size_t i = 0; i < c->conf.num_lanes; ++i) {
		struct req *req = reqs_at(reqs, i);
		if (!req->reg.busy)
			continue;

		/* 64-byte aligned */
		if (req->reg.pkt.to != pkt.from)
			continue;

		/* skip non-reads */
		if (!is_set(&req->reg.pkt, PACKET_READ))
			continue;

		struct component *lane = *lanes_at(&c->lanes, i);

		pkt.mask = req->reg.pkt.mask;
		pkt.to = req->reg.pkt.from;

		stat ok = SEND(intf, lane, pkt);
		/* no reason for core to be blocked */
		assert(ok == OK);

		req->reg.busy = false;
	}

	return OK;
}

static stat simt_riscv64_receive(struct simt_riscv64 *c,
		size_t rcv, struct component *mem,
		struct component *intf, struct reqs *reqs,
		struct component *from, struct packet pkt)
{
	if (mem == from)
		return simt_riscv64_handle_response(c, intf, reqs, pkt);

	/* handle send */
	int32_t idx = pkt.from >> 32;
	struct req *req = reqs_at(reqs, idx);
	if (req->reg.busy)
		return EBUSY;

	/* store packet */
	req->reg.pkt = pkt;
	req->reg.busy = true;
	req->queued = false;

	/* check if packet can piggyback off of another packet */
	if (is_set(&pkt, PACKET_READ))
	foreach(reqs, other_req, reqs) {
		/* skip ourselves */
		if (req == other_req)
			continue;

		if (!other_req->reg.busy)
			continue;

		struct packet *other_pkt = &other_req->reg.pkt;
		if (!is_set(other_pkt, PACKET_READ))
			continue;

		/* yes, we can piggyback, therefore no need to do anything else */
		if (pkt.to == other_pkt->to)
			return OK;
	}

	stat r = simt_riscv64_ext_send(rcv, intf, mem, pkt);
	if (r == EBUSY)
		req->queued = true;

	return r;
}

static stat simt_riscv64_data_receive(struct component *dmem_intf, struct component *from,
			 struct packet pkt)
{
	struct simt_riscv64 *c = CONTAINER_OF(dmem_intf, struct simt_riscv64, dmem_intf);
	return simt_riscv64_receive(c, c->conf.data_rcv,
			c->dmem, &c->dmem_intf, &c->data_rqs,
			from, pkt
	);
}

static stat simt_riscv64_inst_receive(struct component *imem_intf, struct component *from,
		struct packet pkt)
{
	struct simt_riscv64 *c = CONTAINER_OF(imem_intf, struct simt_riscv64, imem_intf);
	return simt_riscv64_receive(c, c->conf.inst_rcv,
			c->imem, &c->imem_intf, &c->inst_rqs,
			from, pkt
	);
}

static stat simt_riscv64_clock(struct simt_riscv64 *c)
{
	stat r = OK;
	/* send out queued stuff */
	foreach(reqs, req, &c->data_rqs) {
		if (!req->queued)
			continue;

		r = simt_riscv64_ext_send(c->conf.data_rcv, &c->dmem_intf, c->dmem, req->reg.pkt);
		if (r == OK) {
			req->queued = false;
			continue;
		}

		if (r == EBUSY)
			continue;

		return r;
	}

	foreach(reqs, req, &c->inst_rqs) {
		if (!req->queued)
			continue;

		r = simt_riscv64_ext_send(c->conf.inst_rcv, &c->imem_intf, c->imem, req->reg.pkt);
		if (r == OK) {
			req->queued = false;
			continue;
		}

		if (r == EBUSY)
			continue;

		return r;
	}


	foreach(lanes, core, &c->lanes) {
		assert(core);

		if ((r = (*core)->clock(*core)) != OK)
			return r;
	}

	return r;
}

struct component *create_simt_riscv64(
		struct simt_riscv64_conf conf,
		struct component *imem,
		struct component *dmem)
{
	struct simt_riscv64 *new = calloc(1, sizeof(struct simt_riscv64));
	if (!new)
		return NULL;

	new->component.clock = (clock_callback)simt_riscv64_clock;

	new->conf = conf;
	new->imem = imem;
	new->dmem = dmem;

	new->lanes = lanes_create(conf.num_lanes);
	new->data_rqs = reqs_create(conf.num_lanes);
	new->inst_rqs = reqs_create(conf.num_lanes);

	new->imem_intf.receive = (receive_callback)simt_riscv64_inst_receive;
	new->dmem_intf.receive = (receive_callback)simt_riscv64_data_receive;

	new->rr = 0;

	for (uint64_t i = 0; i < conf.num_lanes; ++i) {
		struct component *core = create_simple_riscv64(
				/* core ID, should group id also be given? */
				i << 32,
				conf.start_pc,
				&new->imem_intf,
				&new->dmem_intf

		);

		lanes_append(&new->lanes, core);

		struct req empty = {
			.reg = {
				.pkt = {},
				.busy = false,
			},
			.queued = false
		};
		reqs_append(&new->data_rqs, empty);
		reqs_append(&new->inst_rqs, empty);
	}

	return (struct component *)&new->component;
}

struct component *simt_riscv64_data_intf(struct component *c)
{
	struct simt_riscv64 *rv64 = (struct simt_riscv64 *)c;
	return &rv64->dmem_intf;
}

struct component *simt_riscv64_inst_intf(struct component *c)
{
	struct simt_riscv64 *rv64 = (struct simt_riscv64 *)c;
	return &rv64->imem_intf;
}

void simt_riscv64_set_reg(struct component *c, size_t lane, size_t reg, uint64_t val)
{
	struct simt_riscv64 *rv64 = (struct simt_riscv64 *)c;
	simple_riscv64_set_reg(*lanes_at(&rv64->lanes, lane), reg, val);
}