- The result of every take is looked at: a wire cut between the look and the take leaves nothing acted on (the text before run again, a refusal counted, a wait ended by an old answer, a way forgotten). - A node with no room to pass on what another printed, or its word of how text ended, leaves it on the wire it came by and moves it when there is room; what is behind it for the same port waits too. Sleep for room (9) is woken by room on any of the node's wires (engine; MESH.md 7d.3). - The lone node's host keeps no queue for the kernel's port: a message to it is error 18. The step limit is counted while the node waits for room. - A text waiting for room to print is not interrupted by console text; AWAIT is, as before. (BREAK?) is gone. - A GONE is believed only by the centre's port; unawaited answers let go are counted; nothing past a message's last character goes on a wire; AWAIT sets (AWAIT-FROM) only once the stack has been tried. - test_host_depth.c: ten paths, each run works or ends in one named stack error; it fails with (ROOM) or (ROOM-SERVE) taken out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
911 lines
54 KiB
C
911 lines
54 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");
|
|
|
|
/* a message put in the same engine step as the node's fetch of "which wires" is not lost: the next sleep does not block */
|
|
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), "two nodes for the same-step put");
|
|
(void)v4_node_fetch(&ND(a)->n, P_HAVE); /* the fetch is seen in the step's execute phase ... */
|
|
put_msg(&ND(b)->n, 100, 110, 3, 1);
|
|
v4_node_store(&ND(b)->n, P_WIRE, 3);
|
|
v4_node_store(&ND(b)->n, P_A, 100);
|
|
v4_node_store(&ND(b)->n, P_B, 110);
|
|
v4_node_store(&ND(b)->n, P_DO, 5); /* ... and the put is done in the same step's operations */
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(!ND(b)->n.doing && ND(b)->n.how == 0 && f.place[a].wire[0].rx->used == 8 && f.place[a].wire[0].rx->arrived != 0,
|
|
"the put and the fetch were in one step: the message is on the wire and counts as arrived");
|
|
CHECK(ask(a, 0, 0, 0, 8) == 0, "so the sleep that follows does not block");
|
|
|
|
/* 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");
|
|
CHECK(ask(a, 0, V4_WIRE_CELLS + 1, 0, 9) == 1 && ask(a, 0, 100000, 0, 9) == 1 && ask(a, 0, -1, 0, 9) == 1 && !ND(a)->n.doing,
|
|
"sleep for more room than the wire can ever have answers 1 at once, not blocked");
|
|
(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 the front message (which is 263 cells)");
|
|
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");
|
|
/* a small message in front: dropping it frees little, and the sleeper wants more than that leaves */
|
|
v4_wire_reset(f.place[a].wire[0].tx);
|
|
v4_wire_reset(f.place[b].wire[3].rx);
|
|
{
|
|
v4_cell hh[7] = { 1, 2, 3, 4, 5, 6, 12 };
|
|
static v4_cell tt[V4_WIRE_CELLS];
|
|
(void)v4_wire_put(f.place[a].wire[0].tx, hh, tt); /* 10 cells */
|
|
hh[6] = 1024;
|
|
for (i = 0; i < 3; i++) (void)v4_wire_put(f.place[a].wire[0].tx, hh, tt);
|
|
hh[6] = 4 * (35 - 7);
|
|
(void)v4_wire_put(f.place[a].wire[0].tx, hh, tt); /* 35 cells: 190 are free */
|
|
}
|
|
CHECK(V4_WIRE_CELLS - f.place[a].wire[0].tx->used == 190, "a wire with 190 free and a small message in front");
|
|
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(ask(a, 0, 250, 0, 9) == -99, "asleep for 250");
|
|
v4_wire_first(f.place[b].wire[3].rx);
|
|
CHECK(ask(b, 3, 400, 420, 4) == 0 && V4_WIRE_CELLS - f.place[a].wire[0].tx->used == 200, "the reader drops the small one: 200 are free");
|
|
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "the sleeper, which wants 250, is woken, and told 1: a wire of its own has gained room, and not enough");
|
|
CHECK(ask(a, 0, 250, 0, 9) == -99, "it asks again, and sleeps");
|
|
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.doing, "and sleeps on while nothing changes");
|
|
CHECK(ask(b, 3, 400, 420, 4) == 0 && !ND(a)->n.doing && ND(a)->n.how == 0, "the next take gives it the room: 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");
|
|
|
|
/* SLEEP FOR ROOM IS WOKEN BY ROOM ON ANY OF THE NODE'S WIRES (MESH.md 7d.3): a node may be waiting to
|
|
* put on more than one, and names only one */
|
|
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 with two wires");
|
|
fill(f.place[a].wire[0].tx, 0);
|
|
fill(f.place[a].wire[1].tx, 0);
|
|
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(ask(a, 0, 100, 0, 9) == -99, "both full: asleep for room on wire 0");
|
|
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.doing, "and it sleeps on while nothing changes");
|
|
v4_wire_first(f.place[c].wire[2].rx);
|
|
CHECK(ask(c, 2, 400, 420, 4) == 0, "the reader of wire 1 drops a message");
|
|
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1 && f.place[a].wire[0].tx->used == V4_WIRE_CELLS, "it wakes, told 1: the wire it named has no room, but something has changed");
|
|
CHECK(ask(a, 0, 100, 0, 10) == 1 && ask(a, 1, 100, 0, 10) == 0, "it asks which: wire 1 has the room");
|
|
CHECK(ask(a, 0, 100, 0, 9) == -99, "asleep for room on wire 0 again: that room on wire 1 was seen");
|
|
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
|
CHECK(ND(a)->n.doing, "and it is not woken twice for it");
|
|
/* the room comes in the very step the node goes to sleep: the reader's take is done before the sleep is looked at */
|
|
v4_node_store(&ND(c)->n, P_WIRE, 2); v4_node_store(&ND(c)->n, P_DO, 4);
|
|
ND(a)->n.doing = 0;
|
|
v4_node_store(&ND(a)->n, P_WIRE, 0); v4_node_store(&ND(a)->n, P_A, 100); v4_node_store(&ND(a)->n, P_DO, 9);
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(!ND(c)->n.doing && !ND(a)->n.doing && ND(a)->n.how == 1, "room that comes in the step the node goes to sleep wakes it: no wake-up is lost");
|
|
/* a device's take is a take */
|
|
{
|
|
static const v4_device dev = { 0, 0, 0, 0 };
|
|
v4_cell hh[7], tt[V4_WIRE_CELLS / 4];
|
|
CHECK(v4_fabric_wire_device(&f, a, 4, &dev), "a device on a third wire");
|
|
fill(f.place[a].wire[4].tx, 0);
|
|
CHECK(ask(a, 0, 100, 0, 9) == -99, "asleep for room on wire 0");
|
|
CHECK(v4_fabric_device_take(&f, a, 4, hh, tt, V4_WIRE_CELLS / 4) == V4_WIRE_DONE, "the device takes a message between steps");
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "and that wakes it");
|
|
}
|
|
/* a plain sleep is not woken by room */
|
|
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
|
|
(void)v4_fabric_step(&f);
|
|
CHECK(ask(a, 0, 0, 0, 8) == -99, "asleep, for a message");
|
|
CHECK(ask(c, 2, 400, 420, 4) == 0 && ND(a)->n.doing, "room on a wire does not wake a node that sleeps for a message only");
|
|
|
|
/* 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;
|
|
}
|