The final review of step 6e, by a fresh reviewer told to break it, and what was ruled: fix them all, test first. Each has a test that failed before the mend. - Two texts from one node could be begun in the wrong order when room for their answers came between the node's look at the first and at the second. Texts whose answers go by the same port are begun in the order they came: (D-MASK), quit.v4. 102 of 700 timings were wrong before. - A word written with PORT! to a node that speaks whole messages, or to a console's port, blocked its writer for ever and cut off every node beyond it. It is error 18 now, whether that node is asleep, running or stuck: fabric.c, node.c. - A node asleep for room on one wire is woken when another of its wires is cut. - A line there was no room for on the lone node's wire was reported as a fault and ended the hosted program. It is Message refused, and the prompt comes back: V4_BOOT_LINE_REFUSED. hosted-check has the case; no bare-metal boot has typed it. - Ruled: text behind held output for the same port is refused though it would fit. MESH.md 7d.4 said otherwise and is corrected. - The removal test also cuts and re-makes a wire, and has an exchange in which output is held at a full wire: 26,814 disturbances in all. - MESH.md and v4/README.md no longer say that a stuck node holds up no other node, that only printing waits, or that no node is left waiting on one that is gone; 7d.6 lists the limits and what the products do that they did not before. make -C v4 test and sanitize at both widths, hosted-check, lint. Three bare-metal boots: logs/20261013-194827 (amd64), -195131 (aarch64), -195518 (riscv64). POST 538 of 538, word_count=317, dict_hash=0xe5a57f3eb72ff94a on all six; the three sessions are the same character for character. The boots were built with the working tree's uncommitted change to kernel/Makefile that adds the ISA to the kernel's version line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
488 lines
19 KiB
C
488 lines
19 KiB
C
/* 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;
|
|
}
|