/* fabric.c -- nodes, wiring, and time passing for all of them. See fabric.h. * * Nothing here uses the C library: the bare-metal kernel links this file. */ #include "v4/fabric.h" static void clear_place(v4_place *p) { unsigned k; p->node = 0; p->asleep = 0; for (k = 0; k < V4_PORTS; k++) { p->wire[k].kind = V4_WIRE_NONE; p->wire[k].node = 0; p->wire[k].port = 0; p->wire[k].device = 0; } } void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity) { unsigned i; f->place = places; f->capacity = capacity; f->gone_error = 0; f->interrupt_error = 0; for (i = 0; i < capacity; i++) clear_place(&places[i]); } void v4_fabric_gone_error(v4_fabric *f, v4_cell code) { f->gone_error = code; } void v4_fabric_interrupt_error(v4_fabric *f, v4_cell code) { f->interrupt_error = code; } void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity) { unsigned i; if (capacity < f->capacity) return; for (i = 0; i < f->capacity; i++) places[i] = f->place[i]; for (; i < capacity; i++) clear_place(&places[i]); f->place = places; f->capacity = capacity; } int v4_fabric_add(v4_fabric *f, v4_fabric_node *storage, v4_cell port_base) { unsigned i; for (i = 0; i < f->capacity && f->place[i].node; i++) { } if (i == f->capacity || !storage) return -1; clear_place(&f->place[i]); f->place[i].node = storage; v4_node_born(&storage->n, port_base); v4_exec_reset(&storage->es); v4_heat_reset(&storage->h); return (int)i; } v4_fabric_node *v4_fabric_node_at(const v4_fabric *f, unsigned id) { return id < f->capacity ? f->place[id].node : 0; } static int there(const v4_fabric *f, unsigned id, unsigned port) { return id < f->capacity && f->place[id].node && port < V4_PORTS; } /* cut what is on a port, at both ends */ static void cut(v4_fabric *f, unsigned a, unsigned pa) { v4_wire *w = &f->place[a].wire[pa]; if (w->kind == V4_WIRE_NODE && w->node < f->capacity && w->port < V4_PORTS) { v4_wire *other = &f->place[w->node].wire[w->port]; if (other->kind == V4_WIRE_NODE && other->node == a && other->port == pa) { other->kind = V4_WIRE_NONE; other->device = 0; } } w->kind = V4_WIRE_NONE; w->device = 0; } int v4_fabric_wire(v4_fabric *f, unsigned a, unsigned pa, unsigned b, unsigned pb) { if (!there(f, a, pa) || !there(f, b, pb) || (a == b && pa == pb)) return 0; cut(f, a, pa); cut(f, b, pb); f->place[a].wire[pa].kind = V4_WIRE_NODE; f->place[a].wire[pa].node = b; f->place[a].wire[pa].port = pb; f->place[b].wire[pb].kind = V4_WIRE_NODE; f->place[b].wire[pb].node = a; f->place[b].wire[pb].port = pa; return 1; } int v4_fabric_wire_device(v4_fabric *f, unsigned a, unsigned pa, const v4_device *device) { if (!there(f, a, pa) || !device) return 0; cut(f, a, pa); f->place[a].wire[pa].kind = V4_WIRE_DEVICE; f->place[a].wire[pa].device = device; return 1; } int v4_fabric_unwire(v4_fabric *f, unsigned a, unsigned pa) { if (!there(f, a, pa)) return 0; cut(f, a, pa); return 1; } v4_fabric_node *v4_fabric_remove(v4_fabric *f, unsigned id) { v4_fabric_node *storage; unsigned k; if (id >= f->capacity || !f->place[id].node) return 0; for (k = 0; k < V4_PORTS; k++) cut(f, id, k); storage = f->place[id].node; clear_place(&f->place[id]); return storage; } void v4_fabric_sleep(v4_fabric *f, unsigned id) { if (id < f->capacity && f->place[id].node) f->place[id].asleep = 1; } void v4_fabric_wake(v4_fabric *f, unsigned id) { if (id < f->capacity && f->place[id].node) f->place[id].asleep = 0; } static int awake(const v4_fabric *f, unsigned id) { return id < f->capacity && f->place[id].node && !f->place[id].asleep; } /* What node `a` wrote to one of its ports, handed to whatever is there. */ static unsigned hand_over_write(v4_fabric *f, unsigned a) { v4_node *n = &f->place[a].node->n; const v4_wire *w = &f->place[a].wire[n->ask_port]; if (w->kind == V4_WIRE_DEVICE) { if (w->device->take && w->device->take(w->device->self, n->request)) { v4_node_port_served(n); return 1; } } else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) { v4_node *r = &f->place[w->node].node->n; if (r->reading && !r->given && (r->read_port == w->port || r->read_port == V4_PORT_ANY)) { v4_node_port_give(r, w->port, n->request); v4_node_port_served(n); return 1; } } return 0; } /* Node `a` is reading: a device on that port, or on any of them, is asked. */ static unsigned hand_over_read(v4_fabric *f, unsigned a) { v4_node *n = &f->place[a].node->n; unsigned first = n->read_port == V4_PORT_ANY ? 0 : n->read_port; unsigned last = n->read_port == V4_PORT_ANY ? V4_PORTS - 1 : n->read_port; unsigned k; v4_cell value; for (k = first; k <= last; k++) { const v4_wire *w = &f->place[a].wire[k]; if (w->kind == V4_WIRE_DEVICE && w->device->give && w->device->give(w->device->self, &value)) { v4_node_port_give(n, k, value); return 1; } } return 0; } /* What node `a` can see of its neighbours before it executes: who is * blocked writing to it, and who would take a word from it. A device is * always ready to be written to; what it has to give, the node finds when * it reads. */ static void look_about(v4_fabric *f, unsigned a) { unsigned k, writers = 0, readers = 0; for (k = 0; k < V4_PORTS; k++) { const v4_wire *w = &f->place[a].wire[k]; if (w->kind != V4_WIRE_NONE) readers |= 1u << (V4_PORTS + k); /* there is something on this port */ if (w->kind == V4_WIRE_DEVICE) { if (w->device->take) readers |= 1u << k; } else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) { const v4_node *o = &f->place[w->node].node->n; if (o->asking && o->ask_port == w->port) writers |= 1u << k; if (o->reading && !o->given && (o->read_port == w->port || o->read_port == V4_PORT_ANY)) readers |= 1u << k; } } v4_node_port_status(&f->place[a].node->n, writers, readers); } unsigned v4_fabric_step(v4_fabric *f) { unsigned i, done = 0; /* a node that is blocked fetches nothing, and is looked about for when * it is next to execute */ for (i = 0; i < f->capacity; i++) if (awake(f, i)) { const v4_node *n = &f->place[i].node->n; if (!n->stopped && !n->asking && !(n->reading && !n->given)) look_about(f, i); } for (i = 0; i < f->capacity; i++) if (awake(f, i)) done += v4_exec_step_word(&f->place[i].node->n, &f->place[i].node->es, &f->place[i].node->h); for (i = 0; i < f->capacity; i++) if (awake(f, i) && f->place[i].node->n.asking) done += hand_over_write(f, i); for (i = 0; i < f->capacity; i++) if (awake(f, i) && f->place[i].node->n.reading && !f->place[i].node->n.given) done += hand_over_read(f, i); /* THE WAIT (docs/v4.0.0/MESH.md 7c). A node in the wait that was given a * word above is no longer in it: a word for a node comes before its own * offer. Of the rest, each offer is taken by whoever is ready for it: a * device that takes, or a node blocked reading that port or any -- which * a node in the wait is, so of two that offer to each other the one at * the lower place writes and the other's offers are withdrawn. */ for (i = 0; i < f->capacity; i++) { v4_node *n; unsigned k; if (!awake(f, i)) continue; n = &f->place[i].node->n; if (!v4_node_in_wait(n)) continue; for (k = 0; k < V4_PORTS; k++) { const v4_wire *w = &f->place[i].wire[k]; if (!(n->offers & (1u << k))) continue; if (w->kind == V4_WIRE_DEVICE) { if (w->device->take && w->device->take(w->device->self, n->offer[k])) { (void)v4_node_offer_taken(n, k); done++; break; } } else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) { v4_node *r = &f->place[w->node].node->n; if (r->reading && !r->given && (r->read_port == w->port || r->read_port == V4_PORT_ANY)) { v4_cell word = n->offer[k]; (void)v4_node_offer_taken(n, k); v4_node_port_give(r, w->port, word); done++; break; } } } } /* a node blocked on the first word of a message to a node that is not * reading, while a device of its own has something for it */ if (f->interrupt_error != 0) for (i = 0; i < f->capacity; i++) { v4_node *n; unsigned k; if (!awake(f, i)) continue; n = &f->place[i].node->n; if (!n->asking || f->place[i].wire[n->ask_port].kind != V4_WIRE_NODE || v4_node_words_since_look(n) != 0) continue; for (k = 0; k < V4_PORTS; k++) { const v4_wire *w = &f->place[i].wire[k]; if (w->kind == V4_WIRE_DEVICE && w->device->pending && w->device->pending(w->device->self)) { done += (unsigned)v4_node_port_gone(n, f->interrupt_error); break; } } } /* a node blocked at one port that nothing is wired to */ if (f->gone_error != 0) for (i = 0; i < f->capacity; i++) { v4_node *n; if (!awake(f, i)) continue; n = &f->place[i].node->n; if ((n->asking && f->place[i].wire[n->ask_port].kind == V4_WIRE_NONE) || (n->reading && !n->given && n->read_port != V4_PORT_ANY && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE)) done += (unsigned)v4_node_port_gone(n, f->gone_error); else if (v4_node_in_wait(n)) { /* an offer on a port with nothing on it */ unsigned k; for (k = 0; k < V4_PORTS; k++) if ((n->offers & (1u << k)) && f->place[i].wire[k].kind == V4_WIRE_NONE) { done += (unsigned)v4_node_port_gone(n, f->gone_error); break; } } } return done; }