/* 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; p->cut = 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; p->wire[k].rx = 0; p->wire[k].tx = 0; } } void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity) { unsigned i; f->place = places; f->capacity = capacity; f->queue = 0; f->queue_count = 0; f->queue_free = 0; f->queue_first = 0; f->gone_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_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; } void v4_fabric_queues(v4_fabric *f, v4_wire_queue *pool, unsigned count) { unsigned i; f->queue = pool; f->queue_count = count; f->queue_free = count; f->queue_first = count ? 1 : 0; for (i = 0; i < count; i++) { v4_wire_reset(&pool[i]); pool[i].head = i + 1 < count ? i + 2 : 0; } } /* a queue that is not on a wire; the fabric has been given some, and there is one */ static v4_wire_queue *take_queue(v4_fabric *f) { v4_wire_queue *q = &f->queue[f->queue_first - 1]; f->queue_first = q->head; f->queue_free--; v4_wire_reset(q); return q; } /* a queue comes off a wire: what was waiting on it is let go */ static void give_queue(v4_fabric *f, v4_wire_queue *q) { if (!q) return; v4_wire_reset(q); q->head = f->queue_first; f->queue_first = (unsigned)(q - f->queue) + 1; f->queue_free++; } /* Can the ports a and b be wired: if the fabric has been given queues, are * there two once what those ports are wired to now has been given back? */ static int queues_for(const v4_fabric *f, unsigned a, unsigned pa, unsigned b, unsigned pb) { const v4_wire_queue *seen[4]; unsigned i, k, n = 0, back = 0; if (!f->queue) return 1; seen[n++] = f->place[a].wire[pa].rx; seen[n++] = f->place[a].wire[pa].tx; if (b < f->capacity) { seen[n++] = f->place[b].wire[pb].rx; seen[n++] = f->place[b].wire[pb].tx; } for (i = 0; i < n; i++) { if (!seen[i]) continue; for (k = 0; k < i && seen[k] != seen[i]; k++) { } if (k == i) back++; } return f->queue_free + back >= 2; } /* 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; other->rx = 0; other->tx = 0; /* the same two queues as w's */ f->place[w->node].cut = 1; } } if (w->kind != V4_WIRE_NONE) f->place[a].cut = 1; /* a node asleep for room on another wire is woken (operation 9) */ if (f->queue) { give_queue(f, w->rx); give_queue(f, w->tx); } w->kind = V4_WIRE_NONE; w->device = 0; w->rx = 0; w->tx = 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; if (!queues_for(f, a, pa, b, 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; if (f->queue) { v4_wire_queue *ab = take_queue(f), *ba = take_queue(f); f->place[a].wire[pa].tx = ab; f->place[b].wire[pb].rx = ab; f->place[b].wire[pb].tx = ba; f->place[a].wire[pa].rx = ba; } return 1; } int v4_fabric_wire_kernel(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; /* and no queues: it takes no messages */ 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; if (!queues_for(f, a, pa, f->capacity, 0)) return 0; cut(f, a, pa); f->place[a].wire[pa].kind = V4_WIRE_DEVICE; f->place[a].wire[pa].device = device; if (f->queue) { f->place[a].wire[pa].rx = take_queue(f); /* from the device */ f->place[a].wire[pa].tx = take_queue(f); /* to it */ } 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; } int v4_fabric_device_put(v4_fabric *f, unsigned node, unsigned port, const v4_cell *header7, const v4_cell *text) { const v4_wire *w; if (!there(f, node, port)) return V4_WIRE_NO_ONE; w = &f->place[node].wire[port]; if (w->kind != V4_WIRE_DEVICE || !w->rx) return V4_WIRE_NO_ONE; return v4_wire_put(w->rx, header7, text); } int v4_fabric_device_take(v4_fabric *f, unsigned node, unsigned port, v4_cell *header7, v4_cell *text, unsigned text_cap) { const v4_wire *w; if (!there(f, node, port)) return V4_WIRE_NO_ONE; w = &f->place[node].wire[port]; if (w->kind != V4_WIRE_DEVICE || !w->tx) return V4_WIRE_NO_ONE; v4_wire_first(w->tx); /* the device takes them in the order they were put */ return v4_wire_take(w->tx, header7, text, text_cap); } 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; } /* 1 if a word written to this end of a wire will not be read: the other end * is a device that takes no words, or a node that speaks whole messages -- * one that has asked for a whole-message operation since it was born. */ static int no_reader(const v4_fabric *f, const v4_wire *w) { if (w->kind == V4_WIRE_DEVICE) return w->device->take == 0; if (w->kind == V4_WIRE_NODE && w->node < f->capacity && f->place[w->node].node) return f->place[w->node].node->n.spoke != 0; return 0; } /* 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); } /* ---- whole messages: what a node asked for by storing to Do (MESH.md 7d.3) ---- */ /* 1 if `count` words from `addr` are memory that is not the ports, or the * addresses after them, of this node: they may be copied to and from. */ static int span_ok(const v4_node *n, v4_cell addr, unsigned count) { if (count == 0) return 1; if (count > V4_NODE_WORDS || addr < 0 || (v4_ucell)addr > (v4_ucell)(V4_NODE_WORDS - count)) return 0; if (n->port >= 0 && addr <= n->port + (v4_cell)V4_PORT_HAVE && addr + (v4_cell)count > n->port) return 0; return 1; } /* the queue of messages that come to the node's wire, and that go from it; 0: no one is on it */ static v4_wire_queue *coming(const v4_fabric *f, unsigned id, unsigned port) { return port < V4_PORTS ? f->place[id].wire[port].rx : 0; } static v4_wire_queue *going(const v4_fabric *f, unsigned id, unsigned port) { return port < V4_PORTS ? f->place[id].wire[port].tx : 0; } /* has a message come to any of the node's wires since it last fetched which have one */ static int arrived(const v4_fabric *f, unsigned id) { unsigned k; for (k = 0; k < V4_PORTS; k++) { const v4_wire_queue *q = f->place[id].wire[k].rx; if (q && q->arrived) return 1; } return 0; } /* has a message come off any of the wires that lead from the node since it was last woken by that */ static int freed(const v4_fabric *f, unsigned id) { unsigned k; for (k = 0; k < V4_PORTS; k++) { const v4_wire_queue *q = f->place[id].wire[k].tx; if (q && q->freed) return 1; } return 0; } /* Node `id` is doing an operation. Returns 1 and puts how it went in * *how, or returns 0: it waits (8 and 9 only). Nothing is copied, put or * taken unless the answer is 0 -- done. */ static int operate(v4_fabric *f, unsigned id, v4_cell *how) { v4_node *n = &f->place[id].node->n; v4_wire_queue *rx = coming(f, id, n->wire), *tx = going(f, id, n->wire), *to; v4_cell a = n->wire_a, b = n->wire_b, h[V4_WIRE_HEADER]; unsigned cells; switch (n->do_op) { case 1: /* look */ if (!rx) { *how = V4_WIRE_NO_ONE; break; } if (rx->mark >= rx->used) { *how = V4_WIRE_NO_MESSAGE; break; } if (!span_ok(n, a, V4_WIRE_HEADER)) { *how = V4_WIRE_NOT_A_MESSAGE; break; } *how = v4_wire_look(rx, &n->mem[a]); break; case 2: /* take */ if (!rx) { *how = V4_WIRE_NO_ONE; break; } if (rx->mark >= rx->used || !rx->looked) { *how = V4_WIRE_NO_MESSAGE; break; } /* only the message last looked at (wire.h) */ (void)v4_wire_look(rx, h); cells = v4_wire_cells(h[6]) - V4_WIRE_HEADER; if (!span_ok(n, a, V4_WIRE_HEADER) || !span_ok(n, b, cells)) { *how = V4_WIRE_NOT_A_MESSAGE; break; } *how = v4_wire_take(rx, &n->mem[a], cells ? &n->mem[b] : &n->mem[0], cells); break; case 3: /* move */ if (!rx) { *how = V4_WIRE_NO_ONE; break; } if (rx->mark >= rx->used || !rx->looked) { *how = V4_WIRE_NO_MESSAGE; break; } to = a >= 0 && a < (v4_cell)V4_PORTS ? going(f, id, (unsigned)a) : 0; *how = to ? v4_wire_move(rx, to) : V4_WIRE_NO_ONE; break; case 4: /* drop */ *how = !rx ? V4_WIRE_NO_ONE : rx->looked ? v4_wire_drop(rx) : V4_WIRE_NO_MESSAGE; break; case 5: /* put */ if (!tx) { *how = V4_WIRE_NO_ONE; break; } if (!span_ok(n, a, V4_WIRE_HEADER)) { *how = V4_WIRE_NOT_A_MESSAGE; break; } cells = v4_wire_cells(n->mem[a + 6]); if (cells == 0 || !span_ok(n, b, cells - V4_WIRE_HEADER)) { *how = V4_WIRE_NOT_A_MESSAGE; break; } *how = v4_wire_put(tx, &n->mem[a], cells > V4_WIRE_HEADER ? &n->mem[b] : &n->mem[0]); break; case 6: /* first */ if (rx) v4_wire_first(rx); *how = rx ? V4_WIRE_DONE : V4_WIRE_NO_ONE; break; case 7: /* next */ *how = rx ? v4_wire_next(rx) : V4_WIRE_NO_ONE; break; case 8: /* sleep */ if (!arrived(f, id)) return 0; *how = V4_WIRE_DONE; break; case 9: /* sleep for room */ if (!tx) { *how = V4_WIRE_NO_ONE; break; } if (a < 0 || a > (v4_cell)V4_WIRE_CELLS) { *how = V4_WIRE_NO_ROOM; break; } /* no waiting can make that room */ if (v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { *how = V4_WIRE_DONE; break; } if (!arrived(f, id) && !freed(f, id) && !f->place[id].cut) return 0; f->place[id].cut = 0; for (cells = 0; cells < V4_PORTS; cells++) /* woken: the room that has come on any of its wires is seen */ if (f->place[id].wire[cells].tx) f->place[id].wire[cells].tx->freed = 0; *how = V4_WIRE_NO_ROOM; break; case 10: /* room */ if (!tx) { *how = V4_WIRE_NO_ONE; break; } if (a < 0) { *how = V4_WIRE_NOT_A_MESSAGE; break; } *how = a <= (v4_cell)V4_WIRE_CELLS ? v4_wire_room(tx, (unsigned)a) : V4_WIRE_NO_ROOM; break; default: /* not an operation */ *how = V4_WIRE_NOT_A_MESSAGE; break; } return 1; } unsigned v4_fabric_step(v4_fabric *f) { unsigned i, k, done = 0; /* which of its wires have a message waiting, for each node to fetch */ for (i = 0; i < f->capacity; i++) if (awake(f, i)) { unsigned have = 0; for (k = 0; k < V4_PORTS; k++) { const v4_wire_queue *q = f->place[i].wire[k].rx; if (q && q->used > 0) have |= 1u << k; } v4_node_have(&f->place[i].node->n, have); } /* 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->doing && !(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); /* a node that fetched which wires have a message has seen what had come */ for (i = 0; i < f->capacity; i++) if (awake(f, i) && f->place[i].node->n.have_fetched) { for (k = 0; k < V4_PORTS; k++) if (f->place[i].wire[k].rx) f->place[i].wire[k].rx->arrived = 0; f->place[i].node->n.have_fetched = 0; } /* the operations on the wires, each whole; the two that wait are looked at last, so that * what every other node has put or taken this step has been, whatever their numbers */ for (i = 0; i < f->capacity; i++) if (awake(f, i) && f->place[i].node->n.doing && f->place[i].node->n.do_op != 8 && f->place[i].node->n.do_op != 9) { v4_cell how; (void)operate(f, i, &how); v4_node_done(&f->place[i].node->n, how); done++; } for (i = 0; i < f->capacity; i++) if (awake(f, i) && f->place[i].node->n.doing) { v4_cell how; if (operate(f, i, &how)) { v4_node_done(&f->place[i].node->n, how); done++; } } 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); /* a node blocked at one port that nothing is wired to; or blocked writing a * word to what will not read one (fabric.h, A WORD NO ONE WILL READ) */ 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 || no_reader(f, &f->place[i].wire[n->ask_port]))) || (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); } return done; }