A node stores a word to offer on any of its ports and fetches the wait: it is blocked until a word comes for it or one of its offers is taken, and the fabric does the handing over in one step. The port block moves in the nucleus's map to make room for the new addresses. Nothing uses it yet. MESH.md 7c; plan step 6d task 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
278 lines
10 KiB
C
278 lines
10 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;
|
|
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;
|
|
}
|