Queues of whole messages on the wires between nodes (v4/wire.h), the six addresses and ten operations a node asks of the fabric (MESH.md 7d.3), and the fabric's pool of queues. Engine only: the nucleus does not use any of it yet. The ports in the host map move to BUF0_W - 140 so that the new addresses are not among the nucleus variables, with a compile-time check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
825 lines
49 KiB
C
825 lines
49 KiB
C
/* test_fabric.c -- nodes that talk: ports that block on a read as well as a
|
|
* write, a node born empty that executes what arrives at its port, and the
|
|
* fabric of nodes and wiring that changes while they run.
|
|
* docs/v4.0.0/MESH.md section 4 and step 1.
|
|
*
|
|
* At the level of opcodes: no word of any vocabulary is defined here.
|
|
*/
|
|
#include "v4/asm.h"
|
|
#include "v4/capsule.h"
|
|
#include "v4/fabric.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
static int failures = 0, checks = 0;
|
|
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
|
|
|
|
/* The memory map is open (D-4); the test chooses where the ports are. */
|
|
#define PB ((v4_cell)(V4_NODE_WORDS - 24u)) /* port 0: the ports, and the addresses after them (node.h), end inside memory */
|
|
#define P_WIRE (PB + (v4_cell)V4_PORT_WIRE)
|
|
#define P_A (PB + (v4_cell)V4_PORT_A)
|
|
#define P_B (PB + (v4_cell)V4_PORT_B)
|
|
#define P_DO (PB + (v4_cell)V4_PORT_DO)
|
|
#define P_HOW (PB + (v4_cell)V4_PORT_HOW)
|
|
#define P_HAVE (PB + (v4_cell)V4_PORT_HAVE)
|
|
#define ANY (PB + (v4_cell)V4_PORT_ANY)
|
|
#define ORG ((v4_cell)16) /* where a test's programme starts */
|
|
#define OUT ((v4_cell)900) /* where it leaves what it found */
|
|
|
|
#define NODES 8
|
|
static v4_fabric_node pool[NODES];
|
|
static v4_place places[4], more_places[NODES];
|
|
static v4_fabric f;
|
|
static v4_asm as;
|
|
|
|
/* the node with that number in the fabric */
|
|
#define ND(id) (v4_fabric_node_at(&f, (id)))
|
|
|
|
#define O(name) v4_asm_op(&as, V4_OP_##name)
|
|
#define LIT(v) v4_asm_lit(&as, (v4_cell)(v))
|
|
|
|
/* A node with a programme already in it, started at ORG: for the tests that
|
|
* are about talking and not about being born. */
|
|
static unsigned loaded(unsigned k)
|
|
{
|
|
int id = v4_fabric_add(&f, &pool[k], PB);
|
|
pool[k].n.p = ORG;
|
|
v4_asm_begin(&as, &pool[k].n, ORG);
|
|
return (unsigned)id;
|
|
}
|
|
|
|
/* the programme ends by waiting at a port nothing is wired to */
|
|
static void wait_for_ever(void) { LIT(PB + 7); O(BANG_B); O(FETCH_B); (void)v4_asm_label(&as); } /* and the last word is written out */
|
|
|
|
/* a device that says whether it has something pending */
|
|
static int *dev_has;
|
|
static int device_has(void *self) { (void)self; return *dev_has; }
|
|
|
|
/* Let time pass until nothing more can happen, or `max` steps. */
|
|
static unsigned settle(unsigned max)
|
|
{
|
|
unsigned steps = 0;
|
|
while (steps < max && v4_fabric_step(&f) != 0) steps++;
|
|
return steps;
|
|
}
|
|
|
|
/* ---- a device: a neighbour that is not a node ---- */
|
|
typedef struct { const v4_cell *words; unsigned count, at; v4_cell took[16]; unsigned took_n; } stream;
|
|
static int stream_give(void *self, v4_cell *value)
|
|
{
|
|
stream *s = (stream *)self;
|
|
if (s->at >= s->count) return 0;
|
|
*value = s->words[s->at++];
|
|
return 1;
|
|
}
|
|
static int stream_take(void *self, v4_cell value)
|
|
{
|
|
stream *s = (stream *)self;
|
|
if (s->took_n >= 16) return 0;
|
|
s->took[s->took_n++] = value;
|
|
return 1;
|
|
}
|
|
|
|
/* for v4_capsule_write: each word of the capsule, into the stream's own list */
|
|
static void collect(void *self, v4_cell word)
|
|
{
|
|
stream *s = (stream *)self;
|
|
((v4_cell *)s->words)[s->count++] = word;
|
|
}
|
|
|
|
/* What a neighbour sends an empty node to put `count` words at `addr` and
|
|
* start there (MESH.md section 5): for the F18, and for this node, code a
|
|
* node executes straight from its port. */
|
|
static unsigned capsule(v4_cell *out, const v4_cell *words, unsigned count, v4_cell addr)
|
|
{
|
|
static const unsigned set_up[V4_SLOT_COUNT] = { V4_OP_FETCH_P, V4_OP_BANG_A, V4_OP_FETCH_P, V4_OP_PUSH, V4_OP_NOP, V4_OP_NOP };
|
|
static const unsigned pump[V4_SLOT_COUNT] = { V4_OP_FETCH_P, V4_OP_STORE_INC, V4_OP_UNEXT, V4_OP_NOP, V4_OP_NOP, V4_OP_NOP };
|
|
static v4_node scratch;
|
|
v4_asm j;
|
|
unsigned at = 0, i;
|
|
|
|
out[at++] = (v4_cell)v4_iword_assemble(set_up, 0); /* @p a! @p push */
|
|
out[at++] = addr;
|
|
out[at++] = (v4_cell)(count - 1);
|
|
out[at++] = (v4_cell)v4_iword_assemble(pump, 0); /* @p !+ unext */
|
|
for (i = 0; i < count; i++) out[at++] = words[i];
|
|
v4_node_reset(&scratch);
|
|
v4_asm_begin(&j, &scratch, 0);
|
|
v4_asm_branch(&j, V4_OP_JUMP, addr); /* jump addr */
|
|
(void)v4_asm_label(&j);
|
|
out[at++] = scratch.mem[0];
|
|
return at;
|
|
}
|
|
|
|
|
|
/* ---- messages on a wire: what the tests below drive a node with ---- */
|
|
|
|
/* A message in a node's memory: seven words at `hdr` and its text at `text`. */
|
|
static void put_msg(v4_node *n, v4_cell hdr, v4_cell text, v4_cell len, v4_cell seed)
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < 7; i++) n->mem[hdr + (v4_cell)i] = seed * 100 + (v4_cell)i;
|
|
n->mem[hdr + 6] = len;
|
|
for (i = 0; len > 0 && len <= 1024 && i < ((unsigned)len + 3u) / 4u; i++) n->mem[text + (v4_cell)i] = seed * 1000 + (v4_cell)i;
|
|
}
|
|
|
|
/* 1 if the message in the node's memory is the one put_msg makes, but for the word `except` of the header */
|
|
static int msg_is_but(const v4_node *n, v4_cell hdr, v4_cell text, v4_cell len, v4_cell seed, unsigned except)
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < 7; i++) {
|
|
v4_cell want = i == 6 ? len : seed * 100 + (v4_cell)i;
|
|
if (i != except && n->mem[hdr + (v4_cell)i] != want) return 0;
|
|
}
|
|
for (i = 0; i < ((unsigned)len + 3u) / 4u; i++) if (n->mem[text + (v4_cell)i] != seed * 1000 + (v4_cell)i) return 0;
|
|
return 1;
|
|
}
|
|
static int msg_is(const v4_node *n, v4_cell hdr, v4_cell text, v4_cell len, v4_cell seed)
|
|
{
|
|
return msg_is_but(n, hdr, text, len, seed, 7);
|
|
}
|
|
|
|
/* A queue filled until `leave` cells are free. */
|
|
static void fill(v4_wire_queue *q, unsigned leave)
|
|
{
|
|
v4_cell h[7] = { 1, 2, 3, 4, 5, 6, 1024 };
|
|
static v4_cell t[V4_WIRE_CELLS];
|
|
while (V4_WIRE_CELLS - q->used >= 263u + leave) (void)v4_wire_put(q, h, t);
|
|
if (V4_WIRE_CELLS - q->used > leave) {
|
|
h[6] = 4 * (v4_cell)(V4_WIRE_CELLS - q->used - leave - 7u);
|
|
(void)v4_wire_put(q, h, t);
|
|
}
|
|
}
|
|
|
|
/* Ask node `id` for an operation from outside, as its own programme would by
|
|
* storing to its wire, A, B and Do; let one step of the engine pass; and give
|
|
* how it went, or -99 if the node is still blocked at it. */
|
|
static v4_cell ask(unsigned id, v4_cell wire, v4_cell a, v4_cell b, v4_cell op)
|
|
{
|
|
v4_node *n = &ND(id)->n;
|
|
v4_node_store(n, P_WIRE, wire);
|
|
v4_node_store(n, P_A, a);
|
|
v4_node_store(n, P_B, b);
|
|
v4_node_store(n, P_DO, op);
|
|
(void)v4_fabric_step(&f);
|
|
return n->doing ? -99 : v4_node_fetch(n, P_HOW);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
unsigned a, b, c, i, steps;
|
|
|
|
printf("v4 fabric tests: V4_CELL_BITS=%d, %u ports to a node\n", V4_CELL_BITS, (unsigned)V4_PORTS);
|
|
|
|
/* ---- a node newly born ---- */
|
|
v4_fabric_init(&f, places, 4);
|
|
CHECK(v4_fabric_node_at(&f, 0) == 0 && v4_fabric_step(&f) == 0, "an empty fabric: no nodes, and nothing happens");
|
|
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
|
CHECK(a == 0 && v4_fabric_node_at(&f, 0) == &pool[0], "a node is born and has a number");
|
|
CHECK(pool[0].n.p == ANY && pool[0].n.ds.depth == 0 && pool[0].n.rs.depth == 0, "its P is at \"any port\" and its stacks are empty");
|
|
for (i = 0; i < V4_NODE_WORDS && pool[0].n.mem[i] == 0; i++) { }
|
|
CHECK(i == V4_NODE_WORDS, "and there is nothing in its memory");
|
|
CHECK(v4_fabric_step(&f) == 0 && pool[0].n.reading && pool[0].n.read_port == V4_PORT_ANY, "it is blocked reading its ports");
|
|
CHECK(v4_fabric_step(&f) == 0 && pool[0].es.anticlock == 0, "and executes nothing while it waits: its anti-clock has not moved");
|
|
CHECK(v4_fabric_remove(&f, a) == &pool[0] && v4_fabric_node_at(&f, 0) == 0, "it is removed, and its storage comes back");
|
|
|
|
/* ---- two nodes exchange words ---- */
|
|
v4_fabric_init(&f, places, 4);
|
|
a = loaded(0); /* writes three words, then reads one */
|
|
LIT(PB + 2); O(BANG_B); LIT(10); O(STORE_B); LIT(20); O(STORE_B); LIT(12); O(STORE_B);
|
|
O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
|
|
wait_for_ever();
|
|
CHECK(v4_asm_ok(&as), "the writer assembles");
|
|
b = loaded(1); /* reads three, writes their sum back */
|
|
LIT(PB + 5); O(BANG_B); O(FETCH_B); O(FETCH_B); O(FETCH_B); O(ADD); O(ADD); O(STORE_B);
|
|
wait_for_ever();
|
|
CHECK(v4_asm_ok(&as), "the reader assembles");
|
|
|
|
steps = settle(1000);
|
|
CHECK(ND(a)->n.asking && ND(a)->n.ask_port == 2 && ND(a)->n.request == 10, "with no wire, the writer is blocked at its first write");
|
|
CHECK(ND(b)->n.reading && ND(b)->n.read_port == 5, "and the reader at its first read");
|
|
CHECK(v4_fabric_wire(&f, a, 2, b, 5), "port 2 of one is wired to port 5 of the other, while they wait");
|
|
steps = settle(1000);
|
|
CHECK(steps < 1000, "then everything that can happen happens, and stops: %u steps", steps);
|
|
CHECK(ND(a)->n.mem[OUT] == 42, "three words went one way and their sum came back: %ld", (long)ND(a)->n.mem[OUT]);
|
|
CHECK(ND(a)->n.reading && ND(b)->n.reading, "and both are waiting again");
|
|
{
|
|
v4_uheat_t ca = ND(a)->es.anticlock, cb = ND(b)->es.anticlock;
|
|
for (i = 0; i < 20; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->es.anticlock == ca && ND(b)->es.anticlock == cb, "a node that is waiting executes nothing, however long");
|
|
}
|
|
|
|
/* ---- an empty node is filled through its port, and runs what it was sent ---- */
|
|
{
|
|
static v4_node src;
|
|
static v4_cell prog[64], sent[96];
|
|
static stream s;
|
|
static v4_device dev;
|
|
unsigned len, count;
|
|
|
|
v4_node_reset(&src); /* the programme, assembled somewhere to be sent */
|
|
v4_asm_begin(&as, &src, ORG);
|
|
LIT(77); LIT(OUT); O(BANG_A); O(STORE_A);
|
|
LIT(PB + 1); O(BANG_B); LIT(99); O(STORE_B);
|
|
wait_for_ever();
|
|
count = (unsigned)(v4_asm_label(&as) - ORG);
|
|
CHECK(v4_asm_ok(&as) && count < 64, "a programme to send: %u words", count);
|
|
for (i = 0; i < count; i++) prog[i] = src.mem[ORG + i];
|
|
len = capsule(sent, prog, count, ORG);
|
|
|
|
/* sent by a device */
|
|
v4_fabric_init(&f, places, 4);
|
|
memset(&s, 0, sizeof s);
|
|
s.words = sent; s.count = len;
|
|
dev.give = stream_give; dev.take = stream_take; dev.self = &s;
|
|
b = (unsigned)v4_fabric_add(&f, &pool[2], PB);
|
|
CHECK(v4_fabric_wire_device(&f, b, 3, &dev), "a device is wired to port 3 of a node newly born");
|
|
steps = settle(5000);
|
|
CHECK(s.at == len, "the node took every word it was sent: %u of %u", s.at, len);
|
|
for (i = 0; i < count && ND(b)->n.mem[ORG + i] == prog[i]; i++) { }
|
|
CHECK(i == count, "the programme is in its memory, where it was sent to");
|
|
CHECK(ND(b)->n.mem[OUT] == 77, "and it ran: it left its mark");
|
|
CHECK(ND(b)->n.asking && ND(b)->n.ask_port == 1 && ND(b)->n.request == 99, "and is now writing to its port 1, which nothing is wired to");
|
|
CHECK(v4_fabric_wire_device(&f, b, 1, &dev) && settle(100) < 100 && s.took_n == 1 && s.took[0] == 99,
|
|
"a device wired there takes what it wrote");
|
|
|
|
/* sent by another node, which holds it in its own memory */
|
|
v4_fabric_init(&f, places, 4);
|
|
a = loaded(0);
|
|
for (i = 0; i < len; i++) ND(a)->n.mem[200 + i] = sent[i];
|
|
LIT(200); O(BANG_A); LIT(len - 1); O(PUSH); LIT(PB + 4); O(BANG_B);
|
|
(void)v4_asm_label(&as);
|
|
O(FETCH_INC); O(STORE_B); O(UNEXT); /* @+ !b unext: each write blocks in the middle of the word */
|
|
wait_for_ever();
|
|
CHECK(v4_asm_ok(&as), "a node that sends what is in its memory");
|
|
b = (unsigned)v4_fabric_add(&f, &pool[2], PB);
|
|
CHECK(v4_fabric_wire(&f, a, 4, b, 0), "is wired to a node newly born");
|
|
steps = settle(5000);
|
|
CHECK(ND(b)->n.mem[OUT] == 77 && ND(b)->n.asking && ND(b)->n.request == 99, "which takes it in and runs it, as from the device");
|
|
CHECK(ND(b)->n.last_from == 0, "it was listening to any port, and knows which it heard from: %u", ND(b)->n.last_from);
|
|
}
|
|
|
|
/* ---- a capsule of F18 code: any node's memory, sent to an empty node (capsule.h) ---- */
|
|
{
|
|
static v4_node src;
|
|
static v4_cell sent[400];
|
|
static stream s;
|
|
static v4_device dev;
|
|
unsigned k;
|
|
|
|
v4_node_reset(&src);
|
|
v4_asm_begin(&as, &src, 40); /* a programme, not at the start of memory */
|
|
LIT(123); LIT(OUT); O(BANG_A); O(STORE_A);
|
|
wait_for_ever();
|
|
CHECK(v4_asm_ok(&as), "a programme for the capsule");
|
|
src.mem[3] = 5; /* and words here and there: the first of memory, */
|
|
src.mem[200] = -1; src.mem[202] = 7; /* two with one zero between them, */
|
|
src.mem[300] = 9; src.mem[310] = 11; /* two with many, */
|
|
src.mem[PB - 1] = 13; /* and the last before the ports */
|
|
|
|
memset(&s, 0, sizeof s);
|
|
s.words = sent;
|
|
dev.give = stream_give; dev.take = stream_take; dev.self = &s;
|
|
s.count = 0;
|
|
s.count = v4_capsule_write(&src, 40, collect, &s);
|
|
CHECK(s.count > 0 && s.count < 400, "the capsule is %u words for a memory of %u", s.count, (unsigned)V4_NODE_WORDS);
|
|
s.at = 0;
|
|
|
|
v4_fabric_init(&f, places, 4);
|
|
b = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
|
CHECK(v4_fabric_wire_device(&f, b, 5, &dev), "it is sent to a node newly born");
|
|
steps = settle(5000);
|
|
CHECK(s.at == s.count, "which takes all of it: %u of %u", s.at, s.count);
|
|
for (k = 0; k < V4_NODE_WORDS && ND(b)->n.mem[k] == (k == (unsigned)OUT ? 123 : src.mem[k]); k++) { }
|
|
CHECK(k == V4_NODE_WORDS, "its memory is then the same, word for word (first difference at %u)", k);
|
|
CHECK(ND(b)->n.mem[OUT] == 123 && ND(b)->n.reading, "and it was started where the capsule says, and ran");
|
|
}
|
|
|
|
/* ---- a word is passed on by a node in between ---- */
|
|
v4_fabric_init(&f, places, 4);
|
|
a = loaded(0);
|
|
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B); LIT(8); O(STORE_B); LIT(9); O(STORE_B);
|
|
wait_for_ever();
|
|
b = loaded(1); /* whatever comes on any port goes out on port 6 */
|
|
LIT(ANY); O(BANG_A); LIT(PB + 6); O(BANG_B);
|
|
{
|
|
v4_cell again = v4_asm_label(&as);
|
|
O(FETCH_A); O(STORE_B);
|
|
v4_asm_branch(&as, V4_OP_JUMP, again);
|
|
}
|
|
c = loaded(2);
|
|
LIT(PB + 1); O(BANG_B); LIT(OUT); O(BANG_A); O(FETCH_B); O(STORE_INC); O(FETCH_B); O(STORE_INC); O(FETCH_B); O(STORE_INC);
|
|
wait_for_ever();
|
|
CHECK(v4_asm_ok(&as), "three nodes in a row assemble");
|
|
CHECK(v4_fabric_wire(&f, a, 0, b, 2) && v4_fabric_wire(&f, b, 6, c, 1), "the first is wired to the second, the second to the third");
|
|
steps = settle(2000);
|
|
CHECK(ND(c)->n.mem[OUT] == 7 && ND(c)->n.mem[OUT + 1] == 8 && ND(c)->n.mem[OUT + 2] == 9,
|
|
"what the first wrote reached the third, in order, through the one in between");
|
|
CHECK(ND(b)->n.last_from == 2, "the one in between heard it on its port 2");
|
|
|
|
/* ---- the wiring is changed while they run ---- */
|
|
v4_fabric_init(&f, places, 4);
|
|
a = loaded(0); /* writes 1 2 3 4 ... to its port 0 for ever */
|
|
LIT(PB + 0); O(BANG_B); LIT(0);
|
|
{
|
|
v4_cell again = v4_asm_label(&as);
|
|
LIT(1); O(ADD); O(DUP); O(STORE_B);
|
|
v4_asm_branch(&as, V4_OP_JUMP, again);
|
|
}
|
|
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
|
|
c = loaded(2); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
|
|
CHECK(v4_asm_ok(&as), "a writer and two readers assemble");
|
|
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "the writer is wired to the first reader");
|
|
(void)settle(500);
|
|
CHECK(ND(b)->n.mem[OUT] == 1 && ND(c)->n.mem[OUT] == 0, "who gets its first word");
|
|
CHECK(v4_fabric_wire(&f, a, 0, c, 3), "the same port is wired to the second reader instead");
|
|
(void)settle(500);
|
|
CHECK(ND(c)->n.mem[OUT] == 2, "who gets its next: %ld", (long)ND(c)->n.mem[OUT]);
|
|
CHECK(ND(a)->n.asking && ND(a)->n.request == 3, "and the writer waits with its third");
|
|
CHECK(v4_fabric_unwire(&f, a, 0) && settle(100) == 0 && ND(a)->n.asking, "with the wire cut it goes on waiting");
|
|
v4_fabric_gone_error(&f, 18);
|
|
CHECK(settle(100) == 0 && ND(a)->n.asking && ND(a)->n.faults == 0, "and still does when the fabric has an error for it: a bare node cannot take one");
|
|
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
|
v4_node_fault_attach(&ND(a)->n, 900);
|
|
CHECK(v4_fabric_step(&f) == 1 && !ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18
|
|
&& ND(a)->n.p == 900 + (v4_cell)V4_FAULT_RAISED && ND(a)->n.mem[OUT + 20] == 18,
|
|
"a node that can take an error is let go, and the error is raised on it: its P is its handler");
|
|
CHECK(!v4_node_port_gone(&ND(c)->n, 18), "a node that is not blocked at a port is not touched");
|
|
|
|
/* ---- asleep, awake, and removed, while looping; and a node born while others run ---- */
|
|
v4_fabric_init(&f, places, 4);
|
|
a = loaded(0); /* counts in OUT for ever */
|
|
LIT(OUT); O(BANG_A);
|
|
{
|
|
v4_cell again = v4_asm_label(&as);
|
|
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
|
|
v4_asm_branch(&as, V4_OP_JUMP, again);
|
|
}
|
|
b = loaded(1); /* and so does this one */
|
|
LIT(OUT); O(BANG_A);
|
|
{
|
|
v4_cell again = v4_asm_label(&as);
|
|
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
|
|
v4_asm_branch(&as, V4_OP_JUMP, again);
|
|
}
|
|
CHECK(v4_asm_ok(&as), "two nodes that never stop assemble");
|
|
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
|
{
|
|
v4_cell ca = ND(a)->n.mem[OUT], cb = ND(b)->n.mem[OUT];
|
|
v4_uheat_t clock = ND(a)->es.anticlock;
|
|
CHECK(ca > 0 && ca == cb, "each has had as much time as the other: %ld and %ld", (long)ca, (long)cb);
|
|
v4_fabric_sleep(&f, a);
|
|
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.mem[OUT] == ca && ND(a)->es.anticlock == clock, "put to sleep in the middle of its loop, one executes nothing");
|
|
CHECK(ND(b)->n.mem[OUT] == 2 * cb, "and the other goes on as before: %ld", (long)ND(b)->n.mem[OUT]);
|
|
v4_fabric_wake(&f, a);
|
|
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.mem[OUT] == 2 * ca, "woken, it goes on from where it was: %ld", (long)ND(a)->n.mem[OUT]);
|
|
CHECK(v4_fabric_remove(&f, a) != 0 && v4_fabric_node_at(&f, a) == 0, "it is removed while it loops");
|
|
cb = ND(b)->n.mem[OUT];
|
|
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(b)->n.mem[OUT] > cb, "and the other goes on");
|
|
c = (unsigned)v4_fabric_add(&f, &pool[3], PB);
|
|
CHECK(c == a && pool[3].n.p == ANY, "a node is born while the other runs, into the place that was freed");
|
|
for (i = 0; i < 10; i++) (void)v4_fabric_step(&f);
|
|
CHECK(pool[3].n.reading && pool[3].es.anticlock == 0, "and waits, empty, while the other runs");
|
|
}
|
|
|
|
/* ---- more room, while they run ---- */
|
|
v4_fabric_init(&f, places, 4);
|
|
a = loaded(0); LIT(PB + 0); O(BANG_B); LIT(5); O(STORE_B); wait_for_ever();
|
|
b = loaded(1); LIT(PB + 0); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
|
|
(void)loaded(2); wait_for_ever();
|
|
(void)loaded(3); wait_for_ever();
|
|
CHECK(v4_fabric_add(&f, &pool[4], PB) == -1, "a fabric with no room left takes no more nodes");
|
|
CHECK(v4_fabric_wire(&f, a, 0, b, 0), "two of its nodes are wired");
|
|
v4_fabric_grow(&f, more_places, NODES);
|
|
CHECK(v4_fabric_add(&f, &pool[4], PB) == 4, "given more room, it takes another");
|
|
(void)settle(500);
|
|
CHECK(ND(b)->n.mem[OUT] == 5, "and the nodes that were there are wired as they were");
|
|
|
|
/* ---- what cannot be wired ---- */
|
|
CHECK(!v4_fabric_wire(&f, a, V4_PORTS, b, 0) && !v4_fabric_wire(&f, a, 0, 7, 0) && !v4_fabric_wire(&f, a, 1, a, 1),
|
|
"a port that is not there, a node that is not there, a port to itself: refused");
|
|
CHECK(v4_fabric_wire(&f, a, 1, a, 2), "but one port of a node may be wired to another of the same node");
|
|
|
|
/* ---- a node looks at its neighbours before it writes (MESH.md 7a) ---- */
|
|
v4_fabric_init(&f, places, 4);
|
|
a = loaded(0); LIT(PB + 0); O(BANG_B); LIT(5); O(STORE_B); wait_for_ever(); /* writes to its port 0 */
|
|
c = loaded(1); LIT(PB + 1); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever(); /* reads its port 1 */
|
|
for (i = 0; i < 10; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.asking && ND(c)->n.reading, "one node is waiting to write and another to read");
|
|
b = loaded(2);
|
|
LIT(PB + (v4_cell)V4_PORTS + 2); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
|
|
LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); LIT(OUT + 1); O(BANG_A); O(STORE_A);
|
|
wait_for_ever();
|
|
CHECK(v4_fabric_wire(&f, b, 2, a, 0) && v4_fabric_wire(&f, b, 4, c, 1), "a third is wired to both");
|
|
{
|
|
static stream none;
|
|
static v4_device sink;
|
|
sink.take = stream_take; sink.give = 0; sink.self = &none;
|
|
CHECK(v4_fabric_wire_device(&f, b, 6, &sink), "and to a device that takes what is written to it");
|
|
}
|
|
for (i = 0; i < 20; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(b)->n.mem[OUT] == (1 << 2), "it sees which of its ports has a neighbour waiting to write to it: %ld", (long)ND(b)->n.mem[OUT]);
|
|
CHECK((ND(b)->n.mem[OUT + 1] & ((1 << V4_PORTS) - 1)) == ((1 << 4) | (1 << 6)), "and which have one waiting to read from it, or a device: %ld", (long)(ND(b)->n.mem[OUT + 1] & ((1 << V4_PORTS) - 1)));
|
|
CHECK((ND(b)->n.mem[OUT + 1] >> V4_PORTS) == ((1 << 2) | (1 << 4) | (1 << 6)), "and, in the bits above those, which of its ports have anything on them at all: %ld", (long)(ND(b)->n.mem[OUT + 1] >> V4_PORTS));
|
|
CHECK(ND(a)->n.asking && ND(a)->n.request == 5 && ND(c)->n.reading && ND(c)->n.mem[OUT] == 0, "looking takes nothing and gives nothing: both wait as they did");
|
|
CHECK(!ND(b)->n.stopped && ND(b)->n.reading, "and the one that looked did not wait to look");
|
|
|
|
/* ---- a node blocked on the first word of a message, while a device of its own has something for it ---- */
|
|
{
|
|
static int has; /* what the device says it has pending */
|
|
static stream none;
|
|
static v4_device dev;
|
|
v4_fabric_init(&f, places, 4);
|
|
for (i = 0; i < 3; i++) v4_node_reset(&pool[i].n);
|
|
a = loaded(0); /* looks about, then writes two words to its port 0 */
|
|
LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); O(DROP);
|
|
LIT(PB + 0); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); wait_for_ever();
|
|
b = loaded(1); /* never reads */
|
|
LIT(OUT); O(BANG_A);
|
|
{
|
|
v4_cell again = v4_asm_label(&as);
|
|
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
|
|
v4_asm_branch(&as, V4_OP_JUMP, again);
|
|
}
|
|
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer that looks first, wired to a node that never reads");
|
|
dev.take = stream_take; dev.give = 0; dev.self = &none; dev.pending = device_has; has = 1; dev_has = &has;
|
|
CHECK(v4_fabric_wire_device(&f, a, 5, &dev), "and to a device, which has something for it");
|
|
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.asking && ND(a)->n.request == 1 && v4_node_words_since_look(&ND(a)->n) == 0, "the writer is blocked on its first word, and waits: the fabric has no error for it");
|
|
v4_fabric_interrupt_error(&f, 21);
|
|
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "nor is a bare node let go when it has: it cannot take an error");
|
|
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
|
v4_node_fault_attach(&ND(a)->n, 900);
|
|
has = 0;
|
|
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "a node that can take one waits all the same while the device has nothing for it");
|
|
has = 1;
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 21 && ND(a)->n.mem[OUT + 20] == 21,
|
|
"when it has, the writer is let go and the error raised on it");
|
|
|
|
/* once the first word has gone, it is not let go: the message must be finished */
|
|
v4_fabric_init(&f, places, 4);
|
|
for (i = 0; i < 3; i++) v4_node_reset(&pool[i].n);
|
|
a = loaded(0);
|
|
LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); O(DROP);
|
|
LIT(PB + 0); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); wait_for_ever();
|
|
b = loaded(1); /* reads one word, and no more */
|
|
LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A);
|
|
{
|
|
v4_cell again = v4_asm_label(&as);
|
|
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
|
|
v4_asm_branch(&as, V4_OP_JUMP, again);
|
|
}
|
|
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire_device(&f, a, 5, &dev), "the same, to a node that reads one word and no more");
|
|
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
|
v4_node_fault_attach(&ND(a)->n, 900);
|
|
v4_fabric_interrupt_error(&f, 21);
|
|
for (i = 0; i < 60; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.asking && ND(a)->n.request == 2 && ND(a)->n.faults == 0, "blocked on its second word with the device still pending, the writer waits");
|
|
}
|
|
|
|
/* ---- messages on the wire (MESH.md 7d): the engine only ---- */
|
|
{
|
|
static v4_wire_queue qpool[8];
|
|
v4_cell h[7], t[V4_WIRE_CELLS], snap[V4_NODE_WORDS];
|
|
v4_wire_queue copy;
|
|
v4_device nothing;
|
|
unsigned w, k;
|
|
memset(¬hing, 0, sizeof nothing);
|
|
|
|
/* a node puts a message on its wire 0 and a neighbour takes it from its wire 3 */
|
|
v4_fabric_init(&f, places, 4);
|
|
v4_fabric_queues(&f, qpool, 8);
|
|
a = loaded(0);
|
|
LIT(P_WIRE); O(BANG_B); LIT(0); O(STORE_B);
|
|
LIT(P_A); O(BANG_B); LIT(100); O(STORE_B);
|
|
LIT(P_B); O(BANG_B); LIT(110); O(STORE_B);
|
|
LIT(P_DO); O(BANG_B); LIT(5); O(STORE_B);
|
|
LIT(P_HOW); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
|
|
wait_for_ever();
|
|
b = loaded(1);
|
|
LIT(P_WIRE); O(BANG_B); LIT(3); O(STORE_B);
|
|
LIT(P_DO); O(BANG_B); LIT(8); O(STORE_B);
|
|
LIT(P_HAVE); O(BANG_B); O(FETCH_B); LIT(OUT + 1); O(BANG_A); O(STORE_A);
|
|
LIT(P_A); O(BANG_B); LIT(200); O(STORE_B);
|
|
LIT(P_B); O(BANG_B); LIT(220); O(STORE_B);
|
|
LIT(P_DO); O(BANG_B); LIT(2); O(STORE_B);
|
|
LIT(P_HOW); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
|
|
wait_for_ever();
|
|
CHECK(v4_asm_ok(&as), "a node that puts and a node that sleeps and takes assemble");
|
|
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "wire 0 of the first is wired to wire 3 of the second");
|
|
ND(a)->n.mem[OUT] = ND(b)->n.mem[OUT] = -7;
|
|
put_msg(&ND(a)->n, 100, 110, 13, 5);
|
|
for (steps = 0; steps < 300 && ND(a)->n.do_op != 5; steps++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.do_op == 5 && !ND(a)->n.doing && ND(a)->n.how == 0 && f.place[a].wire[0].tx->used == 7 + 4,
|
|
"the put was done in the engine step that asked for it: the message is on the wire and the node is not blocked");
|
|
steps = settle(2000);
|
|
CHECK(steps < 2000 && ND(b)->n.do_op == 2 && ND(a)->n.mem[OUT] == 0 && ND(b)->n.mem[OUT] == 0, "how it went is 0 on both: %ld and %ld", (long)ND(a)->n.mem[OUT], (long)ND(b)->n.mem[OUT]);
|
|
CHECK(msg_is(&ND(b)->n, 200, 220, 13, 5), "the seven words and the text taken are what was put");
|
|
CHECK(ND(b)->n.mem[OUT + 1] == (1 << 3), "and while it waited it saw the message on its wire 3: %ld", (long)ND(b)->n.mem[OUT + 1]);
|
|
CHECK(f.place[a].wire[0].tx->used == 0 && f.place[b].wire[3].rx->used == 0, "the wire is empty again");
|
|
|
|
/* a put with nothing on the port is 2, to a wire with no room is 1; neither changes the wire */
|
|
v4_fabric_init(&f, places, 4);
|
|
v4_fabric_queues(&f, qpool, 8);
|
|
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
|
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
|
|
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "a wire for the refusals");
|
|
put_msg(&ND(a)->n, 100, 110, 20, 3);
|
|
CHECK(ask(a, 5, 100, 110, 5) == 2, "a put to a port with nothing on it answers 2");
|
|
CHECK(ask(a, (v4_cell)V4_PORTS, 100, 110, 5) == 2 && ask(a, -1, 100, 110, 5) == 2, "and so does one to a port that is not there");
|
|
fill(f.place[a].wire[0].tx, 0);
|
|
memcpy(©, f.place[a].wire[0].tx, sizeof copy);
|
|
CHECK(ask(a, 0, 100, 110, 5) == 1, "a put to a wire with no room answers 1");
|
|
CHECK(memcmp(©, f.place[a].wire[0].tx, sizeof copy) == 0, "and the wire is exactly as it was");
|
|
v4_wire_reset(f.place[a].wire[0].tx);
|
|
fill(f.place[a].wire[0].tx, 200);
|
|
put_msg(&ND(a)->n, 100, 110, 4 * (201 - 7), 3);
|
|
memcpy(©, f.place[a].wire[0].tx, sizeof copy);
|
|
CHECK(ask(a, 0, 100, 110, 5) == 1 && memcmp(©, f.place[a].wire[0].tx, sizeof copy) == 0, "a message of 201 cells to a wire with 200 free: 1, and nothing put");
|
|
put_msg(&ND(a)->n, 100, 110, 4 * (200 - 7), 3);
|
|
CHECK(ask(a, 0, 100, 110, 5) == 0 && f.place[a].wire[0].tx->used == V4_WIRE_CELLS, "one of 200 cells goes");
|
|
put_msg(&ND(a)->n, 100, 110, 5000, 3);
|
|
v4_wire_reset(f.place[a].wire[0].tx);
|
|
CHECK(ask(a, 0, 100, 110, 5) == 4 && f.place[a].wire[0].tx->used == 0, "a message whose length is not 0 to 1,024 answers 4");
|
|
put_msg(&ND(a)->n, 100, 110, -1, 3);
|
|
CHECK(ask(a, 0, 100, 110, 5) == 4 && f.place[a].wire[0].tx->used == 0, "so does a length of -1");
|
|
CHECK(ask(a, 0, 100, 110, 99) == 4 && ask(a, 0, 100, 110, 0) == 4 && ask(a, 0, 100, 110, 11) == 4, "and an operation that is not one of the ten");
|
|
|
|
/* addresses off the end of memory, or inside the ports: 4, and nothing copied */
|
|
{
|
|
v4_cell bad[7];
|
|
v4_wire_queue before_rx, before_tx;
|
|
unsigned op, i;
|
|
const unsigned ops[3] = { 1, 2, 5 };
|
|
bad[0] = (v4_cell)V4_NODE_WORDS - 3; /* the seven words run off the end */
|
|
bad[1] = (v4_cell)V4_NODE_WORDS;
|
|
bad[2] = -1;
|
|
bad[3] = PB + 2; /* inside the ports */
|
|
bad[4] = PB - 3; /* runs into them */
|
|
bad[5] = PB + (v4_cell)V4_PORT_HAVE; /* the last of them */
|
|
bad[6] = PB + (v4_cell)V4_PORT_HAVE - 6; /* the seven end on it */
|
|
for (op = 0; op < 3; op++)
|
|
for (k = 0; k < 7; k++) {
|
|
unsigned faults = ND(a)->n.faults;
|
|
v4_wire_reset(f.place[a].wire[0].tx);
|
|
v4_wire_reset(f.place[a].wire[0].rx);
|
|
put_msg(&ND(a)->n, 100, 110, 30, 6); /* a message waiting for look and take to find */
|
|
for (i = 0; i < 7; i++) t[i] = ND(a)->n.mem[100 + i];
|
|
for (i = 0; i < 8; i++) t[8 + i] = ND(a)->n.mem[110 + i];
|
|
CHECK(v4_wire_put(f.place[a].wire[0].rx, t, t + 8) == V4_WIRE_DONE, "(a message waits)");
|
|
memcpy(snap, ND(a)->n.mem, sizeof snap);
|
|
memcpy(&before_rx, f.place[a].wire[0].rx, sizeof before_rx);
|
|
memcpy(&before_tx, f.place[a].wire[0].tx, sizeof before_tx);
|
|
CHECK(ask(a, 0, bad[k], 110, ops[op]) == 4, "op %u with its seven words at %ld: 4", ops[op], (long)bad[k]);
|
|
CHECK(memcmp(snap, ND(a)->n.mem, sizeof snap) == 0 && ND(a)->n.faults == faults, "op %u at %ld: nothing was copied, and the node did not fault", ops[op], (long)bad[k]);
|
|
CHECK(memcmp(&before_rx, f.place[a].wire[0].rx, sizeof before_rx) == 0 && memcmp(&before_tx, f.place[a].wire[0].tx, sizeof before_tx) == 0,
|
|
"op %u at %ld: neither wire has changed", ops[op], (long)bad[k]);
|
|
}
|
|
/* a good header and a text address off the end or inside the ports: 4, and the header is not copied either */
|
|
bad[0] = (v4_cell)V4_NODE_WORDS - 10;
|
|
bad[1] = PB + 3;
|
|
bad[2] = -5;
|
|
for (k = 0; k < 3; k++) {
|
|
v4_wire_reset(f.place[a].wire[0].tx);
|
|
v4_wire_reset(f.place[a].wire[0].rx);
|
|
put_msg(&ND(a)->n, 100, 110, 30, 6);
|
|
for (i = 0; i < 7; i++) t[i] = ND(a)->n.mem[100 + i];
|
|
for (i = 0; i < 8; i++) t[8 + i] = ND(a)->n.mem[110 + i];
|
|
(void)v4_wire_put(f.place[a].wire[0].rx, t, t + 8);
|
|
memcpy(snap, ND(a)->n.mem, sizeof snap);
|
|
memcpy(&before_rx, f.place[a].wire[0].rx, sizeof before_rx);
|
|
CHECK(ask(a, 0, 300, bad[k], 2) == 4 && memcmp(snap, ND(a)->n.mem, sizeof snap) == 0 && memcmp(&before_rx, f.place[a].wire[0].rx, sizeof before_rx) == 0,
|
|
"take with its text at %ld: 4, nothing copied (not the seven words either), the message still there", (long)bad[k]);
|
|
CHECK(ask(a, 0, 100, bad[k], 5) == 4 && f.place[a].wire[0].tx->used == 0, "put with its text at %ld: 4, nothing put", (long)bad[k]);
|
|
}
|
|
/* a message with no text needs no address for it */
|
|
put_msg(&ND(a)->n, 100, 110, 0, 6);
|
|
v4_wire_reset(f.place[a].wire[0].tx);
|
|
CHECK(ask(a, 0, 100, -1, 5) == 0 && f.place[a].wire[0].tx->used == 7, "a message of no text is put with any address for its text");
|
|
v4_wire_reset(f.place[a].wire[0].tx);
|
|
}
|
|
|
|
/* the wire, look, take, first, next, drop and the mark */
|
|
v4_fabric_init(&f, places, 4);
|
|
v4_fabric_queues(&f, qpool, 8);
|
|
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
|
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
|
|
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "a wire for looking");
|
|
CHECK(ask(a, 0, 200, 220, 1) == 3 && ask(a, 0, 200, 220, 2) == 3 && ask(a, 0, 200, 220, 4) == 3 && ask(a, 0, 200, 220, 3) == 3 && ask(a, 5, 200, 220, 1) == 2,
|
|
"look, take, drop and move on a wire with no message answer 3, on a port with nothing 2");
|
|
put_msg(&ND(b)->n, 100, 110, 9, 1); CHECK(ask(b, 3, 100, 110, 5) == 0, "three messages are put");
|
|
put_msg(&ND(b)->n, 100, 110, 21, 2); CHECK(ask(b, 3, 100, 110, 5) == 0, "the second");
|
|
put_msg(&ND(b)->n, 100, 110, 4, 3); CHECK(ask(b, 3, 100, 110, 5) == 0, "and the third");
|
|
CHECK(ask(a, 0, 200, 220, 6) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 100 && ND(a)->n.mem[206] == 9, "first, and look: the first message's seven words");
|
|
CHECK(f.place[a].wire[0].rx->used == 10 + 13 + 8, "look took nothing");
|
|
CHECK(ask(a, 0, 200, 220, 7) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 200, "next: the second");
|
|
CHECK(ask(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 21, 2), "take of the second");
|
|
CHECK(ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 300, "the mark is at the third");
|
|
CHECK(ask(a, 0, 200, 220, 6) == 0 && ask(a, 0, 200, 220, 4) == 0 && ask(a, 0, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 300, "first and drop: the first is gone, the third is left");
|
|
CHECK(ask(a, 0, 200, 220, 7) == 3 && ask(a, 0, 200, 220, 1) == 3, "next at the end answers 3 and the mark is at no message");
|
|
CHECK(ask(a, 0, 200, 220, 6) == 0 && ask(a, 0, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 4, 3) && f.place[a].wire[0].rx->used == 0, "and the third is taken");
|
|
|
|
/* "which wires have a message": a bit to a wire; fetching it clears arrived */
|
|
v4_fabric_init(&f, places, 4);
|
|
v4_fabric_queues(&f, qpool, 8);
|
|
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
|
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
|
|
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
|
|
CHECK(v4_fabric_wire(&f, a, 2, b, 0) && v4_fabric_wire(&f, a, 6, c, 1), "a node with two neighbours");
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(v4_node_fetch(&ND(a)->n, P_HAVE) == 0, "with nothing on any wire, no bit");
|
|
put_msg(&ND(b)->n, 100, 110, 3, 1); CHECK(ask(b, 0, 100, 110, 5) == 0, "one neighbour puts");
|
|
put_msg(&ND(c)->n, 100, 110, 3, 1); CHECK(ask(c, 1, 100, 110, 5) == 0, "the other puts");
|
|
CHECK(f.place[a].wire[2].rx->arrived && f.place[a].wire[6].rx->arrived, "a message has arrived on each");
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(v4_node_fetch(&ND(a)->n, P_HAVE) == ((1 << 2) | (1 << 6)), "the wires with a message have each their bit");
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(!f.place[a].wire[2].rx->arrived && !f.place[a].wire[6].rx->arrived, "and fetching it clears what has arrived");
|
|
CHECK(v4_node_fetch(&ND(a)->n, P_HAVE) == ((1 << 2) | (1 << 6)), "though the messages are still there");
|
|
|
|
/* sleep: blocked, and the engine's clock does not move; a put to one of its wires wakes it */
|
|
v4_fabric_init(&f, places, 4);
|
|
v4_fabric_queues(&f, qpool, 8);
|
|
a = loaded(0);
|
|
LIT(P_DO); O(BANG_B); LIT(8); O(STORE_B);
|
|
LIT(1); LIT(OUT); O(BANG_A); O(STORE_A);
|
|
wait_for_ever();
|
|
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
|
|
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 4, b, 2), "a node that sleeps and then leaves its mark");
|
|
(void)settle(100);
|
|
CHECK(ND(a)->n.doing && ND(a)->n.do_op == 8 && ND(a)->n.mem[OUT] == 0, "it is blocked at its sleep");
|
|
{
|
|
v4_uheat_t clock = ND(a)->es.anticlock;
|
|
unsigned moved = 0;
|
|
for (i = 0; i < 500; i++) moved += v4_fabric_step(&f);
|
|
CHECK(moved == 0 && ND(a)->es.anticlock == clock && ND(a)->n.doing && ND(a)->n.mem[OUT] == 0, "for 500 steps nothing happens and its clock does not move");
|
|
put_msg(&ND(b)->n, 100, 110, 3, 1);
|
|
CHECK(ask(b, 2, 100, 110, 5) == 0, "a put to one of its wires");
|
|
(void)settle(100);
|
|
CHECK(!ND(a)->n.doing && ND(a)->n.how == 0 && ND(a)->n.mem[OUT] == 1, "wakes it, and it goes on");
|
|
}
|
|
|
|
/* a node that looks, leaves the message there, fetches "which wires" and sleeps again stays asleep until a second comes */
|
|
v4_fabric_init(&f, places, 4);
|
|
v4_fabric_queues(&f, qpool, 8);
|
|
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
|
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
|
|
CHECK(v4_fabric_wire(&f, a, 1, b, 4), "two nodes");
|
|
put_msg(&ND(b)->n, 100, 110, 3, 1);
|
|
CHECK(ask(b, 4, 100, 110, 5) == 0, "a message comes");
|
|
CHECK(ask(a, 0, 200, 220, 8) == 0, "a sleep with a message come is not a sleep");
|
|
CHECK(ask(a, 1, 200, 220, 6) == 0 && ask(a, 1, 200, 220, 1) == 0 && ND(a)->n.mem[200] == 100, "it looks, and leaves it there");
|
|
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
|
|
v4_node_store(&ND(a)->n, P_DO, 8);
|
|
for (i = 0; i < 500; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.doing && f.place[a].wire[1].rx->used == 8, "it sleeps again, with the message still on the wire, and stays asleep for 500 steps");
|
|
put_msg(&ND(b)->n, 100, 110, 3, 2);
|
|
CHECK(ask(b, 4, 100, 110, 5) == 0, "a second message comes");
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(!ND(a)->n.doing && ND(a)->n.how == 0, "and wakes it");
|
|
|
|
/* sleep for room */
|
|
v4_fabric_init(&f, places, 4);
|
|
v4_fabric_queues(&f, qpool, 8);
|
|
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
|
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
|
|
CHECK(v4_fabric_wire(&f, a, 0, b, 3), "a writer and a reader");
|
|
fill(f.place[a].wire[0].tx, 100);
|
|
CHECK(ask(a, 0, 50, 0, 10) == 0 && ask(a, 0, 100, 0, 10) == 0 && ask(a, 0, 101, 0, 10) == 1 && ask(a, 0, 5000, 0, 10) == 1,
|
|
"room, with 100 cells free: for 50 and for 100, not for 101");
|
|
CHECK(ask(a, 5, 10, 0, 10) == 2 && ask(a, 0, -1, 0, 10) == 4, "room on a port with nothing is 2, for -1 cells 4");
|
|
CHECK(f.place[a].wire[0].tx->used == V4_WIRE_CELLS - 100 && !ND(a)->n.doing, "and it changes nothing");
|
|
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(ask(a, 0, 200, 0, 9) == -99 && ND(a)->n.doing, "sleep for 200 cells with 100 free: blocked");
|
|
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.doing, "and still blocked");
|
|
v4_wire_first(f.place[b].wire[3].rx);
|
|
CHECK(ask(b, 3, 400, 420, 4) == 0, "the reader drops one message (which takes a few cells)");
|
|
CHECK(ND(a)->n.doing || f.place[a].wire[0].tx->used + 200 <= V4_WIRE_CELLS, "woken when the reader has taken enough, not before");
|
|
while (ND(a)->n.doing && f.place[b].wire[3].rx->used > 0) {
|
|
(void)ask(b, 3, 400, 420, 4);
|
|
}
|
|
CHECK(!ND(a)->n.doing && ND(a)->n.how == 0 && f.place[a].wire[0].tx->used + 200 <= V4_WIRE_CELLS, "woken, with how it went 0");
|
|
/* woken by a message coming instead */
|
|
v4_wire_reset(f.place[a].wire[0].tx);
|
|
fill(f.place[a].wire[0].tx, 100);
|
|
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(ask(a, 0, 200, 0, 9) == -99, "asleep for room again");
|
|
put_msg(&ND(b)->n, 100, 110, 3, 1);
|
|
CHECK(ask(b, 3, 100, 110, 5) == 0, "a message comes on the other wire");
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1 && f.place[a].wire[0].tx->used + 200 > V4_WIRE_CELLS, "it wakes, and how it went says 1: no room, but a message");
|
|
|
|
/* move from one of a node's wires to another */
|
|
v4_fabric_init(&f, more_places, 8);
|
|
v4_fabric_queues(&f, qpool, 8);
|
|
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
|
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
|
|
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
|
|
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, a, 1, c, 2), "a node between two");
|
|
put_msg(&ND(b)->n, 100, 110, 21, 4);
|
|
ND(b)->n.mem[103] = 9;
|
|
CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 0, 0, 6) == 0, "a message comes on wire 0");
|
|
CHECK(ask(a, 0, 1, 0, 3) == 0, "and is moved to wire 1");
|
|
CHECK(f.place[a].wire[0].rx->used == 0 && f.place[c].wire[2].rx->used == 7 + 6, "it is off the one and on the other");
|
|
CHECK(ask(c, 2, 200, 220, 6) == 0 && ask(c, 2, 200, 220, 2) == 0 && ND(c)->n.mem[203] == 8 && msg_is_but(&ND(c)->n, 200, 220, 21, 4, 3),
|
|
"and arrives whole, its fourth word one fewer");
|
|
/* the other full: 1, and nothing moved */
|
|
put_msg(&ND(b)->n, 100, 110, 21, 4);
|
|
CHECK(ask(b, 3, 100, 110, 5) == 0 && ask(a, 0, 0, 0, 6) == 0, "another comes");
|
|
fill(f.place[a].wire[1].tx, 0);
|
|
memcpy(©, f.place[a].wire[0].rx, sizeof copy);
|
|
{
|
|
v4_wire_queue copy2;
|
|
memcpy(©2, f.place[a].wire[1].tx, sizeof copy2);
|
|
CHECK(ask(a, 0, 1, 0, 3) == 1, "moved to a wire with no room: 1");
|
|
CHECK(memcmp(©, f.place[a].wire[0].rx, sizeof copy) == 0 && memcmp(©2, f.place[a].wire[1].tx, sizeof copy2) == 0, "and neither wire has changed");
|
|
}
|
|
CHECK(ask(a, 0, 5, 0, 3) == 2 && ask(a, 0, -1, 0, 3) == 2 && ask(a, 5, 1, 0, 3) == 2, "move to a port with nothing, or from one: 2");
|
|
|
|
/* a node removed with messages waiting both ways on two wires */
|
|
v4_fabric_init(&f, more_places, 8);
|
|
v4_fabric_queues(&f, qpool, 4);
|
|
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
|
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
|
|
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
|
|
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 1, b, 5), "a node in the middle with two neighbours");
|
|
put_msg(&ND(a)->n, 100, 110, 21, 1);
|
|
CHECK(ask(a, 0, 100, 110, 5) == 0 && ask(b, 3, 100, 110, 5) == 0, "a message each way on the first wire");
|
|
CHECK(ask(c, 1, 100, 110, 5) == 0 && ask(b, 5, 100, 110, 5) == 0, "and on the second");
|
|
(void)ask(a, 0, 0, 0, 7);
|
|
(void)ask(b, 3, 0, 0, 7);
|
|
{
|
|
v4_wire_queue *q0 = f.place[a].wire[0].tx, *q1 = f.place[a].wire[0].rx, *q2 = f.place[c].wire[1].tx, *q3 = f.place[c].wire[1].rx;
|
|
fill(q0, 0);
|
|
fill(q2, 0);
|
|
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
|
|
(void)v4_node_fetch(&ND(c)->n, P_HAVE);
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(ask(a, 0, 300, 0, 9) == -99 && ask(c, 1, 300, 0, 9) == -99, "both neighbours are asleep, waiting for room on the wire to it");
|
|
CHECK(v4_fabric_remove(&f, b) == &pool[1], "the node in the middle is removed");
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(!ND(a)->n.doing && ND(a)->n.how == 2 && !ND(c)->n.doing && ND(c)->n.how == 2, "each neighbour asleep for room is woken with 2");
|
|
CHECK(q0->used == 0 && q0->mark == q0->used && q1->used == 0 && q1->mark == q1->used && q2->used == 0 && q2->mark == q2->used && q3->used == 0 && q3->mark == q3->used,
|
|
"and what waited on the four queues is let go, their marks at no message");
|
|
CHECK(f.place[a].wire[0].kind == V4_WIRE_NONE && f.place[a].wire[0].rx == 0 && f.place[a].wire[0].tx == 0, "the neighbour's wire is cut, with no queue");
|
|
CHECK(ask(a, 0, 100, 110, 5) == 2 && ask(a, 0, 100, 110, 1) == 2, "a put or a look on it is 2");
|
|
CHECK(v4_fabric_wire(&f, a, 4, c, 4) && v4_fabric_wire(&f, a, 5, c, 5) && v4_fabric_wire(&f, a, 6, c, 6) == 0,
|
|
"the queues went back to the pool: all four, so two wires and not a third");
|
|
}
|
|
|
|
/* a device's wire */
|
|
v4_fabric_init(&f, places, 4);
|
|
v4_fabric_queues(&f, qpool, 8);
|
|
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
|
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
|
|
CHECK(v4_fabric_wire_device(&f, a, 4, ¬hing) && v4_fabric_wire(&f, a, 1, b, 1), "a device on wire 4 of a node, and another node on wire 1");
|
|
put_msg(&ND(a)->n, 100, 110, 17, 8);
|
|
for (w = 0; w < 7; w++) h[w] = ND(a)->n.mem[100 + w];
|
|
for (w = 0; w < 5; w++) t[w] = ND(a)->n.mem[110 + w];
|
|
CHECK(v4_fabric_device_put(&f, a, 4, h, t) == 0, "the device puts a message");
|
|
CHECK(ask(a, 4, 200, 220, 6) == 0 && ask(a, 4, 200, 220, 2) == 0 && msg_is(&ND(a)->n, 200, 220, 17, 8), "and the node takes it, whole");
|
|
put_msg(&ND(a)->n, 100, 110, 3, 9);
|
|
CHECK(ask(a, 4, 100, 110, 5) == 0, "the node puts one");
|
|
memset(h, 0, sizeof h); memset(t, 0, sizeof t);
|
|
CHECK(v4_fabric_device_take(&f, a, 4, h, t, 0) == 4, "the device, with no room for its text, is told 4");
|
|
CHECK(v4_fabric_device_take(&f, a, 4, h, t, V4_WIRE_CELLS) == 0 && h[6] == 3 && h[0] == 900 && h[2] == 902, "and takes it with room");
|
|
CHECK(v4_fabric_device_take(&f, a, 4, h, t, V4_WIRE_CELLS) == 3, "no more: 3");
|
|
CHECK(v4_fabric_device_put(&f, a, 1, h, t) == 2 && v4_fabric_device_take(&f, a, 1, h, t, 1) == 2, "a wire to a node is not a device's: 2");
|
|
CHECK(v4_fabric_device_put(&f, a, 6, h, t) == 2 && v4_fabric_device_take(&f, a, 6, h, t, 1) == 2 && v4_fabric_device_put(&f, 7, 4, h, t) == 2, "nor is a port with nothing, or a node that is not there");
|
|
h[6] = 1025;
|
|
CHECK(v4_fabric_device_put(&f, a, 4, h, t) == 4, "a device cannot put what is not a message");
|
|
h[6] = 1024;
|
|
fill(f.place[a].wire[4].rx, 0);
|
|
CHECK(v4_fabric_device_put(&f, a, 4, h, t) == 1, "nor onto a full wire: 1");
|
|
|
|
/* a wire when the pool has queues for two: a third fails and changes nothing */
|
|
v4_fabric_init(&f, places, 4);
|
|
v4_fabric_queues(&f, qpool, 4);
|
|
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
|
|
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
|
|
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
|
|
CHECK(v4_fabric_wire(&f, a, 0, b, 0) && v4_fabric_wire_device(&f, c, 0, ¬hing), "two wires take the four queues");
|
|
CHECK(v4_fabric_wire(&f, a, 1, c, 1) == 0 && v4_fabric_wire_device(&f, b, 1, ¬hing) == 0, "a third wire, of either kind, fails");
|
|
CHECK(f.place[a].wire[1].kind == V4_WIRE_NONE && f.place[c].wire[1].kind == V4_WIRE_NONE && f.place[b].wire[1].kind == V4_WIRE_NONE,
|
|
"and it is not there");
|
|
CHECK(f.place[a].wire[0].kind == V4_WIRE_NODE && f.place[a].wire[0].node == b && f.place[c].wire[0].kind == V4_WIRE_DEVICE,
|
|
"and what was wired still is");
|
|
CHECK(v4_fabric_wire(&f, a, 0, c, 2) && f.place[b].wire[0].kind == V4_WIRE_NONE && f.place[c].wire[2].node == a,
|
|
"a port that is wired again gives back its queues first, and gets them");
|
|
CHECK(v4_fabric_unwire(&f, a, 0) && v4_fabric_wire(&f, a, 1, b, 1), "and one that is cut gives them back");
|
|
CHECK(ask(a, 0, 100, 110, 5) == 2, "a put on the cut one: 2");
|
|
}
|
|
|
|
printf(" %d checks, %d failures\n", checks, failures);
|
|
return failures != 0;
|
|
}
|