tpu.c (16080B)
1#include "tpu.h" 2#include "util.h" 3 4#include <ctype.h> 5#include <assert.h> 6#include <stdio.h> 7#include <stdbool.h> 8#include <stddef.h> 9#include <string.h> 10 11const char *dir_reprs[] = { 12 "LEFT", "RIGHT", "UP", "DOWN" 13}; 14 15const char *mode_reprs[] = { 16 "IDLE", "RUN", "READ", "WRITE" 17}; 18 19const char *inst_reprs[] = { 20 "NOP", "MOV", "SWP", "SAV", "ADD", "SUB", 21 "NEG", "JMP", "JEZ", "JNZ", "JGZ", "JLZ", "JRO" 22}; 23 24const char *op_reprs[] = { 25 "ACC", "NIL", "LEFT", "RIGHT", "UP", "DOWN", 26 "ANY", "LAST", "LIT", "<NAME>" 27}; 28 29static enum tpu_port_dir 30op_to_dir(enum tpu_inst_op_type op) 31{ 32 return op - OP_LEFT; 33} 34 35static enum tpu_port_dir 36opposite_dir(enum tpu_port_dir dir) 37{ 38 switch (dir) { 39 case DIR_UP: 40 return DIR_DOWN; 41 case DIR_DOWN: 42 return DIR_UP; 43 case DIR_LEFT: 44 return DIR_RIGHT; 45 case DIR_RIGHT: 46 return DIR_LEFT; 47 } 48} 49 50static uint32_t 51djb_hash(const char *str) 52{ 53 uint32_t hash; 54 const char *c; 55 56 hash = 5381; 57 for (c = str; *c; c++) 58 hash = 33 * hash + (uint8_t) tolower(*c); 59 60 return hash; 61} 62 63void 64label_map_init(struct label_map *map) 65{ 66 memset(map, 0, sizeof(struct label_map)); 67} 68 69void 70label_map_deinit(struct label_map *map) 71{ 72 struct label_map_link *link, *next; 73 size_t i; 74 75 for (i = 0; i < LABEL_MAP_BUCKETS; i++) { 76 link = map->buckets[i]; 77 while (link) { 78 next = link->next; 79 free(link); 80 link = next; 81 } 82 } 83 84 for (i = 0; i < TPU_MAX_INST_CNT; i++) 85 free(map->labels[i].str); 86} 87 88static struct label_map_link ** 89label_map_link_pos(struct label_map *map, const char *name) 90{ 91 struct label_map_link **link; 92 93 link = &map->buckets[djb_hash(name) % LABEL_MAP_BUCKETS]; 94 while (*link && strcasecmp(map->labels[(*link)->pc].str, name)) 95 link = &(*link)->next; 96 97 return link; 98} 99 100bool 101label_map_add(struct label_map *map, const char *name, int pc, bool prefix) 102{ 103 struct label_map_link **pos, *link; 104 105 pos = label_map_link_pos(map, name); 106 if (*pos) return false; 107 108 if (map->labels[pc].str) return false; 109 map->labels[pc].str = strdup(name); 110 if (!map->labels[pc].str) die("strdup:"); 111 map->labels[pc].prefix = prefix; 112 113 *pos = link = malloc(sizeof(struct label_map_link)); 114 if (!link) die("malloc:"); 115 link->pc = pc; 116 link->next = NULL; 117 118 return true; 119} 120 121int 122label_map_get(struct label_map *map, const char *name) 123{ 124 struct label_map_link **link; 125 126 link = label_map_link_pos(map, name); 127 if (!*link) return -1; 128 129 return (*link)->pc; 130} 131 132void 133tpu_port_init(struct tpu_port *port, struct tpu *tpu) 134{ 135 port->tpu = tpu; 136 port->attached = false; 137 port->dst_port = NULL; 138 port->type = PORT_BIDI; 139 port->reset_in = false; 140 port->writing = false; 141 port->avail = false; 142 port->in = -1; 143 port->writing = false; 144 port->out = -1; 145 port->io = false; 146} 147 148void 149tpu_port_deinit(struct tpu_port *port) 150{ 151 /* empty */ 152} 153 154void 155tpu_port_update(struct tpu_port *port) 156{ 157 struct tpu *tpu; 158 159 if (!port->attached) return; 160 161 if (port->writing && !port->dst_port->avail) { 162 port->dst_port->in = port->out; 163 port->dst_port->avail = true; 164 } 165 166 if (port->reset_in && (!port->tpu || !port->tpu->idle)) { 167 port->avail = false; 168 port->dst_port->writing = false; 169 if ((tpu = port->dst_port->tpu)) { 170 /* hacky: switch mode to RUN after receive */ 171 assert(tpu->mode == MODE_WRITE); 172 tpu->mode = MODE_RUN; 173 tpu->idle = false; 174 if (++tpu->pc >= tpu->inst_cnt) tpu->pc = 0; 175 } 176 port->reset_in = false; 177 } 178} 179 180void 181tpu_io_port_init(struct tpu_io_port *io_port, char c, enum tpu_port_dir dir, 182 enum tpu_port_type type, int x, int y) 183{ 184 tpu_port_init(&io_port->port, NULL); 185 io_port->port.io = io_port; 186 io_port->io_step = 0; 187 io_port->type = type; 188 io_port->file = NULL; 189 io_port->dir = dir; 190 io_port->c = c; 191 io_port->x = x; 192 io_port->y = y; 193} 194 195void 196tpu_io_port_deinit(struct tpu_io_port *io_port) 197{ 198 tpu_port_deinit(&io_port->port); 199 if (io_port->file) fclose(io_port->file); 200} 201 202void 203tpu_init(struct tpu *tpu) 204{ 205 size_t i; 206 207 tpu->disabled = false; 208 209 tpu->mode = MODE_IDLE; 210 tpu->idle = false; 211 212 tpu->x = 0; 213 tpu->y = 0; 214 215 tpu->steps = 0; 216 tpu->idle_steps = 0; 217 218 tpu->pc = 0; 219 tpu->acc = 0; 220 tpu->bak = 0; 221 tpu->inst_cnt = 0; 222 tpu->rows = 0; 223 label_map_init(&tpu->label_map); 224 225 tpu->last = -1; 226 tpu->io_port = -1; 227 for (i = 0; i < 4; i++) 228 tpu_port_init(&tpu->ports[i], tpu); 229} 230 231void 232tpu_deinit(struct tpu *tpu) 233{ 234 int i; 235 236 for (i = 0; i < tpu->inst_cnt; i++) { 237 if (tpu->insts[i].opcnt >= 1 238 && tpu->insts[i].ops[0].type == OP_LABEL) 239 free(tpu->insts[i].ops[0].val.label); 240 } 241 label_map_deinit(&tpu->label_map); 242} 243 244struct tpu_inst * 245tpu_current_inst(struct tpu *tpu) 246{ 247 if (tpu->pc >= tpu->inst_cnt) 248 return NULL; 249 return &tpu->insts[tpu->pc]; 250} 251 252void 253tpu_init_ports(struct tpu *tpu, struct tpu_map *map) 254{ 255 struct tpu *neighbor; 256 enum tpu_port_dir odir; 257 int x, y, i; 258 259 for (i = 0; i < 4; i++) { 260 if (tpu->ports[i].attached) continue; 261 if (tpu->disabled) continue; 262 263 switch (i) { 264 case DIR_LEFT: 265 x = tpu->x - 1; 266 y = tpu->y; 267 break; 268 case DIR_RIGHT: 269 x = tpu->x + 1; 270 y = tpu->y; 271 break; 272 case DIR_UP: 273 x = tpu->x; 274 y = tpu->y - 1; 275 break; 276 case DIR_DOWN: 277 x = tpu->x; 278 y = tpu->y + 1; 279 break; 280 } 281 282 neighbor = tpu_map_get(map, x, y); 283 if (neighbor && !neighbor->disabled) { 284 odir = opposite_dir((enum tpu_port_dir) i); 285 tpu->ports[i].attached = true; 286 tpu->ports[i].dst_port = &neighbor->ports[odir]; 287 neighbor->ports[odir].attached = true; 288 neighbor->ports[odir].dst_port = &tpu->ports[i]; 289 } 290 } 291} 292 293void 294tpu_update(struct tpu *tpu) 295{ 296 struct tpu_port *port; 297 int i; 298 299 for (i = 0; i < 4; i++) { 300 port = &tpu->ports[i]; 301 tpu_port_update(port); 302 } 303} 304 305bool 306tpu_set_inst(struct tpu *tpu, int pc, enum tpu_inst_type inst_type, 307 unsigned opcnt, struct tpu_inst_op op1, struct tpu_inst_op op2) 308{ 309 struct tpu_inst *inst; 310 311 inst = &tpu->insts[pc]; 312 inst->type = inst_type; 313 inst->opcnt = opcnt; 314 inst->ops[0] = op1; 315 inst->ops[1] = op2; 316 317 switch (inst->type) { 318 case INST_NOP: case INST_SAV: 319 case INST_SWP: case INST_NEG: 320 if (inst->opcnt != 0) return false; 321 break; 322 case INST_ADD: case INST_SUB: 323 if (inst->opcnt != 1) return false; 324 break; 325 case INST_JRO: 326 if (inst->opcnt != 1) return false; 327 if (inst->ops[0].type != OP_LIT) return false; 328 break; 329 case INST_JMP: case INST_JEZ: case INST_JNZ: 330 case INST_JGZ: case INST_JLZ: 331 if (inst->opcnt != 1) return false; 332 if (inst->ops[0].type != OP_LABEL) return false; 333 break; 334 case INST_MOV: 335 if (inst->opcnt != 2) return false; 336 if (inst->ops[0].type == OP_LABEL) return false; 337 if (inst->ops[1].type == OP_LABEL) return false; 338 if (inst->ops[1].type == OP_LIT) return false; 339 break; 340 } 341 342 return true; 343} 344 345struct tpu_inst * 346tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst_type, 347 unsigned opcnt, struct tpu_inst_op op1, struct tpu_inst_op op2) 348{ 349 if (tpu->inst_cnt >= TPU_MAX_INST_CNT) 350 die("tpu_add_inst: tpu X%i Y%i, >= max %i instructions", 351 tpu->x, tpu->y, TPU_MAX_INST_CNT); 352 if (!tpu_set_inst(tpu, (uint8_t) tpu->inst_cnt, 353 inst_type, opcnt, op1, op2)) 354 return false; 355 return &tpu->insts[tpu->inst_cnt++]; 356} 357 358/* tpu can only read values if there is a writer attached and offering 359 * a value -- this value is cleared on successful read */ 360 361static bool 362tpu_port_read(struct tpu *tpu, enum tpu_port_dir dir, int *lit) 363{ 364 struct tpu_port *port = &tpu->ports[dir]; 365 366 if (!port->avail) 367 return false; 368 369 *lit = port->in; 370 port->reset_in = true; 371 372 return true; 373} 374 375/* tpu can write value to ->out immediately but only finish the instruction 376 * once the written value is read and cleared by the reader */ 377 378static bool 379tpu_port_write(struct tpu *tpu, enum tpu_port_dir dir, int lit) 380{ 381 struct tpu_port *port = &tpu->ports[dir]; 382 383 if (port->writing) 384 return false; 385 386 port->writing = true; 387 port->out = lit; 388 389 return true; 390} 391 392static void 393tpu_jmp_label(struct tpu *tpu, const char *label) 394{ 395 tpu->pc = label_map_get(&tpu->label_map, label); 396 if (tpu->pc < 0 || tpu->pc >= tpu->inst_cnt) abort(); 397} 398 399int 400tpu_exec_get(struct tpu *tpu, struct tpu_inst_op *op, int *lit) 401{ 402 int i; 403 404 switch (op->type) { 405 case OP_ACC: 406 *lit = tpu->acc; 407 return true; 408 case OP_NIL: 409 *lit = 0; 410 return true; 411 case OP_LEFT: case OP_RIGHT: case OP_UP: case OP_DOWN: 412 return tpu_port_read(tpu, op_to_dir(op->type), lit); 413 case OP_ANY: 414 for (i = 0; i < 4; i++) { 415 if (tpu_port_read(tpu, (enum tpu_port_dir) i, lit)) { 416 tpu->last = i; 417 return true; 418 } 419 } 420 return false; 421 case OP_LAST: 422 if (tpu->last < 0) return false; 423 return tpu_port_read(tpu, (enum tpu_port_dir) tpu->last, lit); 424 case OP_LIT: 425 *lit = op->val.lit; 426 return true; 427 case OP_LABEL: 428 abort(); 429 } 430} 431 432bool 433tpu_exec_put(struct tpu *tpu, struct tpu_inst_op *op, int lit) 434{ 435 int i; 436 437 switch (op->type) { 438 case OP_ACC: 439 tpu->acc = lit; 440 return true; 441 case OP_NIL: 442 return true; 443 case OP_LEFT: case OP_RIGHT: case OP_UP: case OP_DOWN: 444 return tpu_port_write(tpu, op_to_dir(op->type), lit); 445 case OP_ANY: 446 for (i = 0; i < 4; i++) { 447 if (tpu_port_write(tpu, (enum tpu_port_dir) i, lit)) { 448 tpu->last = i; 449 return true; 450 } 451 } 452 return false; 453 case OP_LAST: 454 if (tpu->last < 0) return false; 455 return tpu_port_write(tpu, (enum tpu_port_dir) tpu->last, lit); 456 case OP_LABEL: 457 case OP_LIT: 458 abort(); 459 } 460} 461 462enum tpu_mode 463tpu_exec(struct tpu *tpu, struct tpu_inst *inst) 464{ 465 int lit; 466 int val; 467 468 tpu->idle = false; 469 switch (inst->type) { 470 case INST_NOP: 471 tpu->pc += 1; 472 return MODE_RUN; 473 case INST_MOV: 474 if (tpu->mode == MODE_WRITE) { 475 tpu->idle = true; 476 return MODE_WRITE; 477 } 478 if (!tpu_exec_get(tpu, &inst->ops[0], &lit)) { 479 tpu->idle = true; 480 return MODE_READ; 481 } 482 if (!tpu_exec_put(tpu, &inst->ops[1], lit)) { 483 tpu->idle = true; 484 return MODE_WRITE; 485 } 486 /* if MOV to port, set WRITE tentatively and fix on reset_in */ 487 if (inst->ops[1].type != OP_ACC && inst->ops[1].type != OP_NIL) 488 return MODE_WRITE; 489 tpu->pc += 1; 490 return MODE_RUN; 491 case INST_SWP: 492 lit = tpu->acc; 493 tpu->acc = tpu->bak; 494 tpu->bak = lit; 495 tpu->pc += 1; 496 return MODE_RUN; 497 case INST_SAV: 498 tpu->bak = tpu->acc; 499 tpu->pc += 1; 500 return MODE_RUN; 501 case INST_ADD: 502 if (!tpu_exec_get(tpu, &inst->ops[0], &val)) { 503 tpu->idle = true; 504 return MODE_READ; 505 } 506 tpu->acc = MIN(MAX(tpu->acc + val, -999), 999); 507 tpu->pc += 1; 508 return MODE_RUN; 509 case INST_SUB: 510 if (!tpu_exec_get(tpu, &inst->ops[0], &val)) { 511 tpu->idle = true; 512 return MODE_READ; 513 } 514 tpu->acc = MIN(MAX(tpu->acc - val, -999), 999); 515 tpu->pc += 1; 516 return MODE_RUN; 517 case INST_NEG: 518 tpu->acc = -tpu->acc; 519 tpu->pc += 1; 520 return MODE_RUN; 521 case INST_JMP: 522 tpu_jmp_label(tpu, inst->ops[0].val.label); 523 return MODE_RUN; 524 case INST_JEZ: 525 if (tpu->acc == 0) 526 tpu_jmp_label(tpu, inst->ops[0].val.label); 527 else 528 tpu->pc += 1; 529 return MODE_RUN; 530 case INST_JNZ: 531 if (tpu->acc != 0) 532 tpu_jmp_label(tpu, inst->ops[0].val.label); 533 else 534 tpu->pc += 1; 535 return MODE_RUN; 536 case INST_JGZ: 537 if (tpu->acc > 0) 538 tpu_jmp_label(tpu, inst->ops[0].val.label); 539 else 540 tpu->pc += 1; 541 return MODE_RUN; 542 case INST_JLZ: 543 if (tpu->acc < 0) 544 tpu_jmp_label(tpu, inst->ops[0].val.label); 545 else 546 tpu->pc += 1; 547 return MODE_RUN; 548 case INST_JRO: 549 tpu->pc += inst->ops[0].val.lit; 550 if (tpu->pc < 0) tpu->pc = 0; 551 if (tpu->pc >= tpu->inst_cnt) 552 tpu->pc = (int) tpu->inst_cnt - 1; 553 return MODE_RUN; 554 default: 555 abort(); 556 } 557} 558 559void 560tpu_step(struct tpu *tpu) 561{ 562 int prev_mode; 563 564 prev_mode = tpu->mode; 565 if (tpu->pc < tpu->inst_cnt) { 566 tpu->mode = tpu_exec(tpu, &tpu->insts[tpu->pc]); 567 if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0; 568 } else { 569 tpu->mode = MODE_IDLE; 570 tpu->idle = true; 571 } 572} 573 574void 575tpu_map_init(struct tpu_map *map) 576{ 577 memset(map->buckets, 0, sizeof(void *) * TPU_MAP_BUCKETS); 578} 579 580void 581tpu_map_deinit(struct tpu_map *map) 582{ 583 struct tpu_map_link *link, *next; 584 size_t i; 585 586 for (i = 0; i < TPU_MAP_BUCKETS; i++) { 587 link = map->buckets[i]; 588 while (link) { 589 next = link->next; 590 tpu_deinit(link->tpu); 591 free(link->tpu); 592 free(link); 593 link = next; 594 } 595 } 596} 597 598static struct tpu_map_link ** 599tpu_map_link_pos(struct tpu_map *map, int x, int y) 600{ 601 struct tpu_map_link **link; 602 size_t i; 603 604 i = (size_t) (x + y) % TPU_MAP_BUCKETS; 605 link = &map->buckets[i]; 606 while (*link && !((*link)->x == x && (*link)->y == y)) 607 link = &(*link)->next; 608 609 return link; 610} 611 612bool 613tpu_map_add(struct tpu_map *map, struct tpu *tpu) 614{ 615 struct tpu_map_link **pos, *link; 616 617 pos = tpu_map_link_pos(map, tpu->x, tpu->y); 618 if (*pos) return false; 619 *pos = link = malloc(sizeof(struct tpu_map_link)); 620 if (!link) die("malloc:"); 621 link->tpu = tpu; 622 link->x = tpu->x; 623 link->y = tpu->y; 624 link->next = NULL; 625 return true; 626} 627 628struct tpu * 629tpu_map_get(struct tpu_map *map, int x, int y) 630{ 631 struct tpu_map_link **link; 632 633 if (x < 0 || y < 0) return NULL; 634 635 link = tpu_map_link_pos(map, x, y); 636 if (!*link) return NULL; 637 638 return (*link)->tpu; 639} 640 641void 642tis_init(struct tis *tis) 643{ 644 tpu_map_init(&tis->tpu_map); 645 memset(&tis->in_ports, 0, TIS_MAX_IO_PORTS * sizeof(void *)); 646 memset(&tis->out_ports, 0, TIS_MAX_IO_PORTS * sizeof(void *)); 647 tis->steps = 0; 648 tis->show_stats = false; 649 tis->asm_filepath = NULL; 650} 651 652void 653tis_deinit(struct tis *tis) 654{ 655 int i; 656 657 tpu_map_deinit(&tis->tpu_map); 658 for (i = 0; i < TIS_MAX_IO_PORTS; i++) { 659 if (tis->in_ports[i]) { 660 tpu_io_port_deinit(tis->in_ports[i]); 661 free(tis->in_ports[i]); 662 } 663 if (tis->out_ports[i]) { 664 tpu_io_port_deinit(tis->out_ports[i]); 665 free(tis->out_ports[i]); 666 } 667 } 668 free(tis->asm_filepath); 669} 670 671bool 672tis_step(struct tis *tis) 673{ 674 struct tpu_map_link *link; 675 bool running; 676 size_t i; 677 678 for (i = 0; i < TPU_MAP_BUCKETS; i++) { 679 link = tis->tpu_map.buckets[i]; 680 for (; link; link = link->next) 681 tpu_step(link->tpu); 682 } 683 684 tis_communicate(tis); 685 686 for (i = 0; i < TPU_MAP_BUCKETS; i++) { 687 link = tis->tpu_map.buckets[i]; 688 for (; link; link = link->next) 689 tpu_update(link->tpu); 690 } 691 692 for (i = 0; i < TIS_MAX_IO_PORTS; i++) { 693 if (tis->in_ports[i]) 694 tpu_port_update(&tis->in_ports[i]->port); 695 if (tis->out_ports[i]) 696 tpu_port_update(&tis->out_ports[i]->port); 697 } 698 699 running = false; 700 for (i = 0; i < TPU_MAP_BUCKETS; i++) { 701 link = tis->tpu_map.buckets[i]; 702 for (; link; link = link->next) { 703 running |= !link->tpu->idle; 704 if (link->tpu->pc < link->tpu->inst_cnt) { 705 link->tpu->steps += 1; 706 if (link->tpu->idle) 707 link->tpu->idle_steps += 1; 708 } 709 } 710 } 711 712 tis->steps += 1; 713 714 return running; 715} 716 717void 718tis_communicate(struct tis *tis) 719{ 720 struct tpu_io_port *io_port; 721 char buf[16], *s; 722 int val, i; 723 724 for (i = 0; i < TIS_MAX_IO_PORTS; i++) { 725 io_port = tis->in_ports[i]; 726 if (!io_port) continue; 727 if (!io_port->port.attached || io_port->port.writing) 728 continue; 729 if (!io_port->file) continue; 730 while ((s = fgets(buf, sizeof(buf), io_port->file))) { 731 if ((s = strchr(buf, '\n'))) *s = '\0'; 732 if (!*buf) continue; 733 val = (int) strtol(buf, &s, 10); 734 if (!s || *s != '\0') 735 die("communicate: invalid input '%s'", buf); 736 io_port->port.out = val; 737 io_port->port.writing = true; 738 io_port->io_step = tis->steps; 739 break; 740 } 741 } 742 743 for (i = 0; i < TIS_MAX_IO_PORTS; i++) { 744 io_port = tis->out_ports[i]; 745 if (!io_port) continue; 746 if (!io_port->port.attached || !io_port->port.avail) 747 continue; 748 if (io_port->file) 749 fprintf(io_port->file, "%i\n", io_port->port.in); 750 io_port->port.reset_in = true; 751 tpu_port_update(&io_port->port); 752 io_port->io_step = tis->steps; 753 } 754} 755 756struct tis_stats 757tis_gen_stats(struct tis *tis) 758{ 759 struct tis_stats stats; 760 struct tpu_map_link *link; 761 size_t idle_steps; 762 size_t all_steps; 763 int i; 764 765 stats.steps = 0; 766 stats.nodes = 0; 767 stats.insts = 0; 768 stats.idle = 0; 769 770 for (i = 0; i < TIS_MAX_IO_PORTS; i++) { 771 if (tis->out_ports[i] && tis->out_ports[i]->io_step > 0) { 772 stats.steps = MAX(stats.steps, tis->out_ports[i]->io_step + 1); 773 } 774 } 775 776 all_steps = 0; 777 idle_steps = 0; 778 for (i = 0; i < TPU_MAP_BUCKETS; i++) { 779 link = tis->tpu_map.buckets[i]; 780 for (; link; link = link->next) { 781 stats.insts += link->tpu->inst_cnt; 782 idle_steps += link->tpu->idle_steps; 783 all_steps += link->tpu->steps; 784 stats.nodes += (link->tpu->inst_cnt > 0); 785 } 786 } 787 788 if (!stats.steps) stats.steps = all_steps; 789 790 if (all_steps == 0) 791 stats.idle = 0; 792 else 793 stats.idle = (float) idle_steps / (float) all_steps; 794 795 return stats; 796}