master
c 566 lines 13.7 KB
Raw
1 /*
2 * Inter-Thread Communication Unit emulation.
3 *
4 * Copyright (c) 2016 Imagination Technologies
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "qemu/osdep.h"
21 #include "qemu/units.h"
22 #include "qemu/log.h"
23 #include "qemu/module.h"
24 #include "qapi/error.h"
25 #include "hw/core/cpu.h"
26 #include "hw/misc/mips_itu.h"
27 #include "hw/core/qdev-properties.h"
28 #include "accel/tcg/cpu-loop.h"
29 #include "target/mips/cpu.h"
30
31 #define ITC_TAG_ADDRSPACE_SZ (ITC_ADDRESSMAP_NUM * 8)
32 /* Initialize as 4kB area to fit all 32 cells with default 128B grain.
33 Storage may be resized by the software. */
34 #define ITC_STORAGE_ADDRSPACE_SZ 0x1000
35
36 #define ITC_FIFO_NUM_MAX 16
37 #define ITC_SEMAPH_NUM_MAX 16
38 #define ITC_AM1_NUMENTRIES_OFS 20
39
40 #define ITC_CELL_PV_MAX_VAL 0xFFFF
41
42 #define ITC_CELL_TAG_FIFO_DEPTH 28
43 #define ITC_CELL_TAG_FIFO_PTR 18
44 #define ITC_CELL_TAG_FIFO 17
45 #define ITC_CELL_TAG_T 16
46 #define ITC_CELL_TAG_F 1
47 #define ITC_CELL_TAG_E 0
48
49 #define ITC_AM0_BASE_ADDRESS_MASK 0xFFFFFC00ULL
50 #define ITC_AM0_EN_MASK 0x1
51
52 #define ITC_AM1_ADDR_MASK_MASK 0x1FC00
53 #define ITC_AM1_ENTRY_GRAIN_MASK 0x7
54
55 typedef enum ITCView {
56 ITCVIEW_BYPASS = 0,
57 ITCVIEW_CONTROL = 1,
58 ITCVIEW_EF_SYNC = 2,
59 ITCVIEW_EF_TRY = 3,
60 ITCVIEW_PV_SYNC = 4,
61 ITCVIEW_PV_TRY = 5,
62 ITCVIEW_PV_ICR0 = 15,
63 } ITCView;
64
65 #define ITC_ICR0_CELL_NUM 16
66 #define ITC_ICR0_BLK_GRAIN 8
67 #define ITC_ICR0_BLK_GRAIN_MASK 0x7
68 #define ITC_ICR0_ERR_AXI 2
69 #define ITC_ICR0_ERR_PARITY 1
70 #define ITC_ICR0_ERR_EXEC 0
71
72 MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu)
73 {
74 return &itu->tag_io;
75 }
76
77 static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size)
78 {
79 MIPSITUState *tag = (MIPSITUState *)opaque;
80 uint64_t index = addr >> 3;
81
82 if (index >= ITC_ADDRESSMAP_NUM) {
83 qemu_log_mask(LOG_GUEST_ERROR, "Read 0x%" PRIx64 "\n", addr);
84 return 0;
85 }
86
87 return tag->ITCAddressMap[index];
88 }
89
90 static void itc_reconfigure(MIPSITUState *tag)
91 {
92 uint64_t *am = &tag->ITCAddressMap[0];
93 MemoryRegion *mr = &tag->storage_io;
94 hwaddr address = am[0] & ITC_AM0_BASE_ADDRESS_MASK;
95 uint64_t size = (1 * KiB) + (am[1] & ITC_AM1_ADDR_MASK_MASK);
96 bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0;
97
98 memory_region_transaction_begin();
99 if (!(size & (size - 1))) {
100 memory_region_set_size(mr, size);
101 }
102 memory_region_set_address(mr, address);
103 memory_region_set_enabled(mr, is_enabled);
104 memory_region_transaction_commit();
105 }
106
107 static void itc_tag_write(void *opaque, hwaddr addr,
108 uint64_t data, unsigned size)
109 {
110 MIPSITUState *tag = (MIPSITUState *)opaque;
111 uint64_t *am = &tag->ITCAddressMap[0];
112 uint64_t am_old, mask;
113 uint64_t index = addr >> 3;
114
115 switch (index) {
116 case 0:
117 mask = ITC_AM0_BASE_ADDRESS_MASK | ITC_AM0_EN_MASK;
118 break;
119 case 1:
120 mask = ITC_AM1_ADDR_MASK_MASK | ITC_AM1_ENTRY_GRAIN_MASK;
121 break;
122 default:
123 qemu_log_mask(LOG_GUEST_ERROR, "Bad write 0x%" PRIx64 "\n", addr);
124 return;
125 }
126
127 am_old = am[index];
128 am[index] = (data & mask) | (am_old & ~mask);
129 if (am_old != am[index]) {
130 itc_reconfigure(tag);
131 }
132 }
133
134 static const MemoryRegionOps itc_tag_ops = {
135 .read = itc_tag_read,
136 .write = itc_tag_write,
137 .impl = {
138 .max_access_size = 8,
139 },
140 .endianness = DEVICE_NATIVE_ENDIAN,
141 };
142
143 static inline uint32_t get_num_cells(MIPSITUState *s)
144 {
145 return s->num_fifo + s->num_semaphores;
146 }
147
148 static inline ITCView get_itc_view(hwaddr addr)
149 {
150 return (addr >> 3) & 0xf;
151 }
152
153 static inline int get_cell_stride_shift(const MIPSITUState *s)
154 {
155 /* Minimum interval (for EntryGain = 0) is 128 B */
156 return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK);
157 }
158
159 static inline ITCStorageCell *get_cell(MIPSITUState *s,
160 hwaddr addr)
161 {
162 uint32_t cell_idx = addr >> get_cell_stride_shift(s);
163 uint32_t num_cells = get_num_cells(s);
164
165 if (cell_idx >= num_cells) {
166 cell_idx = num_cells - 1;
167 }
168
169 return &s->cell[cell_idx];
170 }
171
172 static void wake_blocked_threads(ITCStorageCell *c)
173 {
174 CPUState *cs;
175 CPU_FOREACH(cs) {
176 if (cs->halted && (c->blocked_threads & (1ULL << cs->cpu_index))) {
177 cpu_interrupt(cs, CPU_INTERRUPT_WAKE);
178 }
179 }
180 c->blocked_threads = 0;
181 }
182
183 static G_NORETURN
184 void block_thread_and_exit(ITCStorageCell *c)
185 {
186 c->blocked_threads |= 1ULL << current_cpu->cpu_index;
187 current_cpu->halted = 1;
188 current_cpu->exception_index = EXCP_HLT;
189 cpu_loop_exit_restore(current_cpu, current_cpu->mem_io_pc);
190 }
191
192 /* ITC Bypass View */
193
194 static inline uint64_t view_bypass_read(ITCStorageCell *c)
195 {
196 if (c->tag.FIFO) {
197 return c->data[c->fifo_out];
198 } else {
199 return c->data[0];
200 }
201 }
202
203 static inline void view_bypass_write(ITCStorageCell *c, uint64_t val)
204 {
205 if (c->tag.FIFO && (c->tag.FIFOPtr > 0)) {
206 int idx = (c->fifo_out + c->tag.FIFOPtr - 1) % ITC_CELL_DEPTH;
207 c->data[idx] = val;
208 }
209
210 /* ignore a write to the semaphore cell */
211 }
212
213 /* ITC Control View */
214
215 static inline uint64_t view_control_read(ITCStorageCell *c)
216 {
217 return ((uint64_t)c->tag.FIFODepth << ITC_CELL_TAG_FIFO_DEPTH) |
218 (c->tag.FIFOPtr << ITC_CELL_TAG_FIFO_PTR) |
219 (c->tag.FIFO << ITC_CELL_TAG_FIFO) |
220 (c->tag.T << ITC_CELL_TAG_T) |
221 (c->tag.E << ITC_CELL_TAG_E) |
222 (c->tag.F << ITC_CELL_TAG_F);
223 }
224
225 static inline void view_control_write(ITCStorageCell *c, uint64_t val)
226 {
227 c->tag.T = (val >> ITC_CELL_TAG_T) & 1;
228 c->tag.E = (val >> ITC_CELL_TAG_E) & 1;
229 c->tag.F = (val >> ITC_CELL_TAG_F) & 1;
230
231 if (c->tag.E) {
232 c->tag.FIFOPtr = 0;
233 }
234 }
235
236 /* ITC Empty/Full View */
237
238 static uint64_t view_ef_common_read(ITCStorageCell *c, bool blocking)
239 {
240 uint64_t ret = 0;
241
242 if (!c->tag.FIFO) {
243 return 0;
244 }
245
246 c->tag.F = 0;
247
248 if (blocking && c->tag.E) {
249 block_thread_and_exit(c);
250 }
251
252 if (c->blocked_threads) {
253 wake_blocked_threads(c);
254 }
255
256 if (c->tag.FIFOPtr > 0) {
257 ret = c->data[c->fifo_out];
258 c->fifo_out = (c->fifo_out + 1) % ITC_CELL_DEPTH;
259 c->tag.FIFOPtr--;
260 }
261
262 if (c->tag.FIFOPtr == 0) {
263 c->tag.E = 1;
264 }
265
266 return ret;
267 }
268
269 static uint64_t view_ef_sync_read(ITCStorageCell *c)
270 {
271 return view_ef_common_read(c, true);
272 }
273
274 static uint64_t view_ef_try_read(ITCStorageCell *c)
275 {
276 return view_ef_common_read(c, false);
277 }
278
279 static inline void view_ef_common_write(ITCStorageCell *c, uint64_t val,
280 bool blocking)
281 {
282 if (!c->tag.FIFO) {
283 return;
284 }
285
286 c->tag.E = 0;
287
288 if (blocking && c->tag.F) {
289 block_thread_and_exit(c);
290 }
291
292 if (c->blocked_threads) {
293 wake_blocked_threads(c);
294 }
295
296 if (c->tag.FIFOPtr < ITC_CELL_DEPTH) {
297 int idx = (c->fifo_out + c->tag.FIFOPtr) % ITC_CELL_DEPTH;
298 c->data[idx] = val;
299 c->tag.FIFOPtr++;
300 }
301
302 if (c->tag.FIFOPtr == ITC_CELL_DEPTH) {
303 c->tag.F = 1;
304 }
305 }
306
307 static void view_ef_sync_write(ITCStorageCell *c, uint64_t val)
308 {
309 view_ef_common_write(c, val, true);
310 }
311
312 static void view_ef_try_write(ITCStorageCell *c, uint64_t val)
313 {
314 view_ef_common_write(c, val, false);
315 }
316
317 /* ITC P/V View */
318
319 static uint64_t view_pv_common_read(ITCStorageCell *c, bool blocking)
320 {
321 uint64_t ret = c->data[0];
322
323 if (c->tag.FIFO) {
324 return 0;
325 }
326
327 if (c->data[0] > 0) {
328 c->data[0]--;
329 } else if (blocking) {
330 block_thread_and_exit(c);
331 }
332
333 return ret;
334 }
335
336 static uint64_t view_pv_sync_read(ITCStorageCell *c)
337 {
338 return view_pv_common_read(c, true);
339 }
340
341 static uint64_t view_pv_try_read(ITCStorageCell *c)
342 {
343 return view_pv_common_read(c, false);
344 }
345
346 static inline void view_pv_common_write(ITCStorageCell *c)
347 {
348 if (c->tag.FIFO) {
349 return;
350 }
351
352 if (c->data[0] < ITC_CELL_PV_MAX_VAL) {
353 c->data[0]++;
354 }
355
356 if (c->blocked_threads) {
357 wake_blocked_threads(c);
358 }
359 }
360
361 static void view_pv_sync_write(ITCStorageCell *c)
362 {
363 view_pv_common_write(c);
364 }
365
366 static void view_pv_try_write(ITCStorageCell *c)
367 {
368 view_pv_common_write(c);
369 }
370
371 static void raise_exception(int excp)
372 {
373 current_cpu->exception_index = excp;
374 cpu_loop_exit(current_cpu);
375 }
376
377 static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size)
378 {
379 MIPSITUState *s = (MIPSITUState *)opaque;
380 ITCStorageCell *cell = get_cell(s, addr);
381 ITCView view = get_itc_view(addr);
382 uint64_t ret = -1;
383
384 switch (size) {
385 case 1:
386 case 2:
387 s->icr0 |= 1 << ITC_ICR0_ERR_AXI;
388 raise_exception(EXCP_DBE);
389 return 0;
390 }
391
392 switch (view) {
393 case ITCVIEW_BYPASS:
394 ret = view_bypass_read(cell);
395 break;
396 case ITCVIEW_CONTROL:
397 ret = view_control_read(cell);
398 break;
399 case ITCVIEW_EF_SYNC:
400 ret = view_ef_sync_read(cell);
401 break;
402 case ITCVIEW_EF_TRY:
403 ret = view_ef_try_read(cell);
404 break;
405 case ITCVIEW_PV_SYNC:
406 ret = view_pv_sync_read(cell);
407 break;
408 case ITCVIEW_PV_TRY:
409 ret = view_pv_try_read(cell);
410 break;
411 case ITCVIEW_PV_ICR0:
412 ret = s->icr0;
413 break;
414 default:
415 qemu_log_mask(LOG_GUEST_ERROR,
416 "itc_storage_read: Bad ITC View %d\n", (int)view);
417 break;
418 }
419
420 return ret;
421 }
422
423 static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data,
424 unsigned size)
425 {
426 MIPSITUState *s = (MIPSITUState *)opaque;
427 ITCStorageCell *cell = get_cell(s, addr);
428 ITCView view = get_itc_view(addr);
429
430 switch (size) {
431 case 1:
432 case 2:
433 s->icr0 |= 1 << ITC_ICR0_ERR_AXI;
434 raise_exception(EXCP_DBE);
435 return;
436 }
437
438 switch (view) {
439 case ITCVIEW_BYPASS:
440 view_bypass_write(cell, data);
441 break;
442 case ITCVIEW_CONTROL:
443 view_control_write(cell, data);
444 break;
445 case ITCVIEW_EF_SYNC:
446 view_ef_sync_write(cell, data);
447 break;
448 case ITCVIEW_EF_TRY:
449 view_ef_try_write(cell, data);
450 break;
451 case ITCVIEW_PV_SYNC:
452 view_pv_sync_write(cell);
453 break;
454 case ITCVIEW_PV_TRY:
455 view_pv_try_write(cell);
456 break;
457 case ITCVIEW_PV_ICR0:
458 if (data & 0x7) {
459 /* clear ERROR bits */
460 s->icr0 &= ~(data & 0x7);
461 }
462 /* set BLK_GRAIN */
463 s->icr0 &= ~0x700;
464 s->icr0 |= data & 0x700;
465 break;
466 default:
467 qemu_log_mask(LOG_GUEST_ERROR,
468 "itc_storage_write: Bad ITC View %d\n", (int)view);
469 break;
470 }
471
472 }
473
474 static const MemoryRegionOps itc_storage_ops = {
475 .read = itc_storage_read,
476 .write = itc_storage_write,
477 .endianness = DEVICE_NATIVE_ENDIAN,
478 };
479
480 static void itc_reset_cells(MIPSITUState *s)
481 {
482 int i;
483
484 memset(s->cell, 0, get_num_cells(s) * sizeof(s->cell[0]));
485
486 for (i = 0; i < s->num_fifo; i++) {
487 s->cell[i].tag.E = 1;
488 s->cell[i].tag.FIFO = 1;
489 s->cell[i].tag.FIFODepth = ITC_CELL_DEPTH_SHIFT;
490 }
491 }
492
493 static void mips_itu_init(Object *obj)
494 {
495 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
496 MIPSITUState *s = MIPS_ITU(obj);
497
498 memory_region_init_io(&s->storage_io, OBJECT(s), &itc_storage_ops, s,
499 "mips-itc-storage", ITC_STORAGE_ADDRSPACE_SZ);
500 sysbus_init_mmio(sbd, &s->storage_io);
501
502 memory_region_init_io(&s->tag_io, OBJECT(s), &itc_tag_ops, s,
503 "mips-itc-tag", ITC_TAG_ADDRSPACE_SZ);
504 }
505
506 static void mips_itu_realize(DeviceState *dev, Error **errp)
507 {
508 MIPSITUState *s = MIPS_ITU(dev);
509
510 if (s->num_fifo > ITC_FIFO_NUM_MAX) {
511 error_setg(errp, "Exceed maximum number of FIFO cells: %d",
512 s->num_fifo);
513 return;
514 }
515 if (s->num_semaphores > ITC_SEMAPH_NUM_MAX) {
516 error_setg(errp, "Exceed maximum number of Semaphore cells: %d",
517 s->num_semaphores);
518 return;
519 }
520
521 s->cell = g_new(ITCStorageCell, get_num_cells(s));
522 }
523
524 static void mips_itu_reset(DeviceState *dev)
525 {
526 MIPSITUState *s = MIPS_ITU(dev);
527
528 s->ITCAddressMap[0] = 0;
529 s->ITCAddressMap[1] =
530 ((ITC_STORAGE_ADDRSPACE_SZ - 1) & ITC_AM1_ADDR_MASK_MASK) |
531 (get_num_cells(s) << ITC_AM1_NUMENTRIES_OFS);
532 itc_reconfigure(s);
533
534 itc_reset_cells(s);
535 }
536
537 static const Property mips_itu_properties[] = {
538 DEFINE_PROP_UINT32("num-fifo", MIPSITUState, num_fifo,
539 ITC_FIFO_NUM_MAX),
540 DEFINE_PROP_UINT32("num-semaphores", MIPSITUState, num_semaphores,
541 ITC_SEMAPH_NUM_MAX),
542 };
543
544 static void mips_itu_class_init(ObjectClass *klass, const void *data)
545 {
546 DeviceClass *dc = DEVICE_CLASS(klass);
547
548 device_class_set_props(dc, mips_itu_properties);
549 dc->realize = mips_itu_realize;
550 device_class_set_legacy_reset(dc, mips_itu_reset);
551 }
552
553 static const TypeInfo mips_itu_info = {
554 .name = TYPE_MIPS_ITU,
555 .parent = TYPE_SYS_BUS_DEVICE,
556 .instance_size = sizeof(MIPSITUState),
557 .instance_init = mips_itu_init,
558 .class_init = mips_itu_class_init,
559 };
560
561 static void mips_itu_register_types(void)
562 {
563 type_register_static(&mips_itu_info);
564 }
565
566 type_init(mips_itu_register_types)