diff --git a/capsules/v4/nucleus-64.f18 b/capsules/v4/nucleus-64.f18 index 6f818911..72c8fb8f 100644 Binary files a/capsules/v4/nucleus-64.f18 and b/capsules/v4/nucleus-64.f18 differ diff --git a/v4/include/v4/node.h b/v4/include/v4/node.h index 6d4108b7..b21770df 100644 --- a/v4/include/v4/node.h +++ b/v4/include/v4/node.h @@ -43,6 +43,8 @@ #define V4_PORTS 8u #endif #define V4_PORT_ANY V4_PORTS /* not a port: "whichever port has a neighbour writing" */ +#define V4_PORT_OFFER (V4_PORTS + 4u) /* not a port: the offer on port 0; the offer on port k is k further (the wait, below) */ +#define V4_PORT_WAIT (2u * V4_PORTS + 4u) /* not a port: the wait */ #ifndef V4_NODE_WORDS #define V4_NODE_WORDS 1024u @@ -112,6 +114,10 @@ typedef struct { unsigned writers; /* bit k: the neighbour on port k is blocked writing to this node */ unsigned readers; /* bit k: the neighbour on port k is blocked reading from this node */ unsigned since_look; /* words taken from this node since it last fetched either of those */ + unsigned offers; /* bit k: a word is offered on port k (the wait: docs/v4.0.0/MESH.md 7c) */ + v4_cell offer[V4_PORTS]; /* the words offered */ + int waiting; /* non-zero: the read it is blocked in is the wait */ + int offer_asked; /* non-zero: `asking` stands for the offer on `ask_port` (v4_node_offer_as_write) */ /* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */ v4_cell dstack_reg; /* its word address, or -1 */ @@ -236,6 +242,34 @@ unsigned v4_node_words_since_look(const v4_node *n); * that is not blocked at a port, or is reading "any port". */ int v4_node_port_gone(v4_node *n, v4_cell code); +/* THE WAIT (docs/v4.0.0/MESH.md 7c). After the addresses above come + * base + V4_PORT_OFFER + k the offer on port k: a store here is the word + * the node offers to write to that port. It is + * remembered and waits for nothing. + * base + V4_PORT_WAIT the wait: a fetch here blocks the node until a + * word comes for it on any port, or one of its + * offers is taken, whichever is first. + * A word that came is fetched as from "any port", and "which port" says + * where from. When an offer was taken the fetch gives 0 and "which port" + * gives V4_PORTS + k: the offer on port k. Either way every offer the node + * had is withdrawn. Whoever connects the ports does the handing over + * (fabric.h); a node in the wait is blocked reading "any port" as far as a + * writer to it can tell. + * + * 1 if the node is blocked in the wait with nothing given yet. */ +int v4_node_in_wait(const v4_node *n); + +/* The offer on port k has been taken: the node is unblocked and will fetch + * 0, from V4_PORTS + k. 0 if it is not in the wait or has no offer there. */ +int v4_node_offer_taken(v4_node *n, unsigned k); + +/* For a host with one node, whose devices always take what is written: if + * the node is in the wait with an offer, it becomes a blocked write of the + * word offered on the lowest such port -- asking, request, ask_port -- and + * v4_node_port_served then completes the wait as v4_node_offer_taken would. + * 1 if it did. */ +int v4_node_offer_as_write(v4_node *n); + /* The neighbour on `port` writes `value`: the node, blocked reading that * port or "any", is unblocked and will fetch it. */ void v4_node_port_give(v4_node *n, unsigned port, v4_cell value); diff --git a/v4/src/fabric.c b/v4/src/fabric.c index cb269156..dc08485b 100644 --- a/v4/src/fabric.c +++ b/v4/src/fabric.c @@ -209,6 +209,36 @@ unsigned v4_fabric_step(v4_fabric *f) for (i = 0; i < f->capacity; i++) if (awake(f, i) && f->place[i].node->n.reading && !f->place[i].node->n.given) done += hand_over_read(f, i); + /* THE WAIT (docs/v4.0.0/MESH.md 7c). A node in the wait that was given a + * word above is no longer in it: a word for a node comes before its own + * offer. Of the rest, each offer is taken by whoever is ready for it: a + * device that takes, or a node blocked reading that port or any -- which + * a node in the wait is, so of two that offer to each other the one at + * the lower place writes and the other's offers are withdrawn. */ + for (i = 0; i < f->capacity; i++) { + v4_node *n; + unsigned k; + if (!awake(f, i)) continue; + n = &f->place[i].node->n; + if (!v4_node_in_wait(n)) continue; + for (k = 0; k < V4_PORTS; k++) { + const v4_wire *w = &f->place[i].wire[k]; + if (!(n->offers & (1u << k))) continue; + if (w->kind == V4_WIRE_DEVICE) { + if (w->device->take && w->device->take(w->device->self, n->offer[k])) { (void)v4_node_offer_taken(n, k); done++; break; } + } else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) { + v4_node *r = &f->place[w->node].node->n; + if (r->reading && !r->given && (r->read_port == w->port || r->read_port == V4_PORT_ANY)) { + v4_cell word = n->offer[k]; + (void)v4_node_offer_taken(n, k); + v4_node_port_give(r, w->port, word); + done++; + break; + } + } + } + } + /* a node blocked on the first word of a message to a node that is not * reading, while a device of its own has something for it */ if (f->interrupt_error != 0) @@ -236,6 +266,11 @@ unsigned v4_fabric_step(v4_fabric *f) if ((n->asking && f->place[i].wire[n->ask_port].kind == V4_WIRE_NONE) || (n->reading && !n->given && n->read_port != V4_PORT_ANY && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE)) done += (unsigned)v4_node_port_gone(n, f->gone_error); + else if (v4_node_in_wait(n)) { /* an offer on a port with nothing on it */ + unsigned k; + for (k = 0; k < V4_PORTS; k++) + if ((n->offers & (1u << k)) && f->place[i].wire[k].kind == V4_WIRE_NONE) { done += (unsigned)v4_node_port_gone(n, f->gone_error); break; } + } } return done; diff --git a/v4/src/node.c b/v4/src/node.c index 665310b6..c929216f 100644 --- a/v4/src/node.c +++ b/v4/src/node.c @@ -93,7 +93,13 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value) n->asking = 1; return; } - if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any" and "which port" are not written to */ + if (port >= (int)V4_PORT_OFFER && port < (int)V4_PORT_WAIT) { /* an offer: remembered until the wait */ + unsigned k = (unsigned)port - V4_PORT_OFFER; + n->offer[k] = value; + n->offers |= 1u << k; + return; + } + if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any", "which port" and the rest are not written to */ } /* The stack registers: a store empties the stack. -1 when not attached. */ if (addr == n->dstack_reg) { v4_dstack_clear(&n->ds); return; } @@ -122,6 +128,14 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell addr) if (port == (int)V4_PORTS + 1) return (v4_cell)n->last_from; if (port == (int)V4_PORTS + 2) { n->since_look = 0; return (v4_cell)n->writers; } if (port == (int)V4_PORTS + 3) { n->since_look = 0; return (v4_cell)n->readers; } + if (port == (int)V4_PORT_WAIT) { + /* the wait: the word that came, or 0 for an offer taken */ + if (!n->given) return 0; + n->given = 0; + n->last_from = n->given_port; + return n->given_value; + } + if (port >= (int)V4_PORT_OFFER) return 0; /* an offer is not read back */ if (port >= 0) { /* the word its neighbour wrote; v4_node_read_ready has said it is there */ if (!n->given) return 0; @@ -180,6 +194,9 @@ void v4_node_port_attach(v4_node *n, v4_cell base) n->writers = 0; n->readers = 0; n->since_look = 0; + n->offers = 0; + n->waiting = 0; + n->offer_asked = 0; } void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers) @@ -190,17 +207,18 @@ void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers) int v4_node_port_index(const v4_node *n, v4_cell addr) { - if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORTS + 3) return -1; + if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORT_WAIT) return -1; return (int)(addr - n->port); } int v4_node_read_ready(v4_node *n, v4_cell addr) { int port = v4_node_port_index(n, addr); - if (port < 0 || port > (int)V4_PORT_ANY) return 1; - if (n->given && (port == (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1; + if (port < 0 || (port > (int)V4_PORT_ANY && port != (int)V4_PORT_WAIT)) return 1; + if (n->given && (port >= (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1; n->reading = 1; - n->read_port = (unsigned)port; + n->waiting = port == (int)V4_PORT_WAIT; /* the wait is a read of any port, with offers standing */ + n->read_port = n->waiting ? V4_PORT_ANY : (unsigned)port; return 0; } @@ -210,6 +228,31 @@ void v4_node_port_give(v4_node *n, unsigned port, v4_cell value) n->given_value = value; n->given_port = port; n->reading = 0; + if (n->waiting) { n->waiting = 0; n->offers = 0; } /* the wait is over: every offer is withdrawn */ +} + +int v4_node_in_wait(const v4_node *n) +{ + return n->waiting && n->reading && !n->given; +} + +int v4_node_offer_taken(v4_node *n, unsigned k) +{ + if (!v4_node_in_wait(n) || k >= V4_PORTS || !(n->offers & (1u << k))) return 0; + v4_node_port_give(n, V4_PORTS + k, 0); + return 1; +} + +int v4_node_offer_as_write(v4_node *n) +{ + unsigned k; + if (!v4_node_in_wait(n) || n->offers == 0 || n->asking) return 0; + for (k = 0; !(n->offers & (1u << k)); k++) { } + n->request = n->offer[k]; + n->ask_port = k; + n->asking = 1; + n->offer_asked = 1; + return 1; } void v4_node_born(v4_node *n, v4_cell base) @@ -223,6 +266,10 @@ void v4_node_port_served(v4_node *n) { n->asking = 0; n->since_look++; + if (n->offer_asked) { /* what was served was an offer: the wait is over */ + n->offer_asked = 0; + v4_node_port_give(n, V4_PORTS + n->ask_port, 0); + } } unsigned v4_node_words_since_look(const v4_node *n) @@ -233,7 +280,8 @@ unsigned v4_node_words_since_look(const v4_node *n) int v4_node_port_gone(v4_node *n, v4_cell code) { if (code == 0 || n->error_reg < 0 || n->fault_vector < 0 || n->stopped) return 0; - if (n->asking) n->asking = 0; + if (n->asking) { n->asking = 0; if (n->offer_asked) { n->offer_asked = 0; n->reading = 0; n->waiting = 0; n->offers = 0; } } + else if (v4_node_in_wait(n) && n->offers != 0) { n->reading = 0; n->waiting = 0; n->offers = 0; } else if (n->reading && !n->given && n->read_port != V4_PORT_ANY) n->reading = 0; else return 0; v4_node_store(n, n->error_reg, code); /* raises it: P is the node's handler now */ diff --git a/v4/tests/host_map.h b/v4/tests/host_map.h index 98e3cca2..5899f0e8 100644 --- a/v4/tests/host_map.h +++ b/v4/tests/host_map.h @@ -73,7 +73,7 @@ #define OWED (BUF0_W - 112) /* those: for each, the node it is owed to and the node the refused message was for */ #define ACL_HOOK (BUF0_W - 6) /* the xt of the access control recheck word, or 0 */ #define LINE_STATUS (BUF0_W - 9) /* how the last line ended: 0 QUIT, 1 completed, 2 an error */ -#define PORT (BUF0_W - 32) /* the node's ports (node.h): V4_PORTS of them, then "any port" and "which port". Port 0 is where its requests go. */ +#define PORT (BUF0_W - 144) /* the node's ports (node.h): V4_PORTS of them, then "any port", "which port", who writes, who reads, an offer for each port, and the wait: 2 * V4_PORTS + 5 cells, between the output buffer and A_FOUND. Port 0 is where its requests go. */ #define CONSOLE (BUF0_W - 16) /* the node what this one prints is sent to; 0: whoever sent the text being served */ #define ROUTE_COUNT (BUF0_W - 17) /* how many entries the table of ways holds */ #define ROUTE_DEFAULT (BUF0_W - 18) /* the port address for a node not in the table, or 0: there is none */ @@ -124,6 +124,8 @@ typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1]; #endif /* the messages waiting are above the dictionary */ typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 1 : -1]; +/* the ports, with the offers and the wait after them, end below the cells that follow */ +typedef char host_map_port_block_fits[(PORT + 2 * (v4_cell)V4_PORTS + 5 <= BUF0_W - 115) ? 1 : -1]; /* How many slots, from slot 0, a branch may sit in on a node this size: those * whose address field reaches every word of it. */ diff --git a/v4/tests/test_fabric.c b/v4/tests/test_fabric.c index 7af08fe6..4872511e 100644 --- a/v4/tests/test_fabric.c +++ b/v4/tests/test_fabric.c @@ -15,7 +15,7 @@ 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 - 16u)) /* port 0 */ +#define PB ((v4_cell)(V4_NODE_WORDS - 32u)) /* port 0: the ports and what follows them end below the top of memory */ #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 */ @@ -45,6 +45,31 @@ static unsigned loaded(unsigned k) /* 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 */ +/* ---- the wait (MESH.md 7c) ---- */ +static void fresh(void) +{ + unsigned k; + v4_fabric_init(&f, places, 4); + for (k = 0; k < 4; k++) v4_node_reset(&pool[k].n); +} +/* offer `word` on port k */ +static void offer(unsigned k, v4_cell word) { LIT(word); LIT(PB + (v4_cell)V4_PORT_OFFER + (v4_cell)k); O(BANG_A); O(STORE_A); } +/* the wait: what it gives goes to `at`, and which port to `at + 1` */ +static void wait_into(v4_cell at) +{ + LIT(PB + (v4_cell)V4_PORT_WAIT); O(BANG_B); O(FETCH_B); LIT(at); O(BANG_A); O(STORE_A); + LIT(PB + (v4_cell)V4_PORTS + 1); O(BANG_B); O(FETCH_B); LIT(at + 1); O(BANG_A); O(STORE_A); +} +static void done_mark(void) { LIT(1); LIT(OUT + 2); O(BANG_A); O(STORE_A); } +static void count_for_ever(void) +{ + v4_cell again; + LIT(OUT); O(BANG_A); + again = v4_asm_label(&as); + O(FETCH_A); LIT(1); O(ADD); O(STORE_A); + v4_asm_branch(&as, V4_OP_JUMP, again); +} + /* a device that says whether it has something pending */ static int *dev_has; static int device_has(void *self) { (void)self; return *dev_has; } @@ -423,6 +448,129 @@ int main(void) 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"); } + /* ---- the wait (MESH.md 7c): a node offers a word and sleeps ---- */ + { + static stream none; + static v4_device sink; + v4_uheat_t clock; + sink.take = stream_take; sink.give = 0; sink.self = &none; + + /* taken by a reader */ + fresh(); + a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever(); + b = loaded(1); 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) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to one that reads"); + (void)settle(300); + CHECK(ND(b)->n.mem[OUT] == 7, "the reader has the word offered: %ld", (long)ND(b)->n.mem[OUT]); + CHECK(ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0, + "the one that offered is told its offer on port 0 was taken, and has none left: %ld from %ld", (long)ND(a)->n.mem[OUT], (long)ND(a)->n.mem[OUT + 1]); + + /* taken by a device */ + fresh(); none.took_n = 0; + a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever(); + CHECK(v4_fabric_wire_device(&f, a, 0, &sink), "the same, wired to a device that takes what is written"); + (void)settle(300); + CHECK(none.took_n == 1 && none.took[0] == 7 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1, "the device has the word, and the node is told"); + + /* a neighbour that never reads */ + fresh(); + a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever(); + b = loaded(1); count_for_ever(); + CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to one that never reads"); + for (i = 0; i < 200; i++) (void)v4_fabric_step(&f); + clock = ND(a)->es.anticlock; + for (i = 0; i < 100; i++) (void)v4_fabric_step(&f); + CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->es.anticlock == clock && ND(a)->n.mem[OUT + 2] == 0, "it is in the wait, and executes nothing there"); + CHECK((ND(b)->n.readers & (1u << 3)) != 0, "and shows to its neighbour as waiting to read"); + + /* withdrawn by a word that comes for it */ + c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever(); + CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, c, 0, a, 2), "a third node writes to it"); + for (i = 0; i < 100; i++) (void)v4_fabric_step(&f); + CHECK(ND(a)->n.mem[OUT] == 9 && ND(a)->n.mem[OUT + 1] == 2 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0, + "it gets that word, from that port, and its offer is withdrawn: %ld from %ld", (long)ND(a)->n.mem[OUT], (long)ND(a)->n.mem[OUT + 1]); + CHECK(!ND(b)->n.given && !ND(c)->n.asking, "the node offered to was given nothing, and the writer was served"); + + /* an offer that could be taken in the step a word comes: the word comes first */ + fresh(); + a = loaded(0); offer(0, 7); wait_into(OUT); offer(0, 7); wait_into(OUT + 3); done_mark(); wait_for_ever(); + 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 + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever(); + CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 0, a, 2), "one offers to a reader while a third writes to it"); + v4_fabric_sleep(&f, a); + for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); + CHECK(ND(b)->n.reading && ND(c)->n.asking, "the reader and the writer are both blocked"); + v4_fabric_sleep(&f, b); v4_fabric_sleep(&f, c); v4_fabric_wake(&f, a); + for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); + CHECK(v4_node_in_wait(&ND(a)->n), "and then the one that offers is in the wait"); + v4_fabric_wake(&f, b); v4_fabric_wake(&f, c); + (void)v4_fabric_step(&f); + CHECK(!v4_node_in_wait(&ND(a)->n) && !ND(c)->n.asking && ND(b)->n.reading && !ND(b)->n.given, "in one step: the word for it comes first, and the reader is given nothing"); + (void)settle(300); + CHECK(ND(a)->n.mem[OUT] == 9 && ND(a)->n.mem[OUT + 1] == 2 && ND(a)->n.mem[OUT + 3] == 0 && ND(a)->n.mem[OUT + 4] == (v4_cell)V4_PORTS + 0 && ND(b)->n.mem[OUT] == 7, + "it offers again, and the reader gets the word once"); + + /* two nodes each offering to the other */ + fresh(); + a = loaded(0); offer(0, 5); wait_into(OUT); done_mark(); wait_for_ever(); + b = loaded(1); offer(3, 6); wait_into(OUT); done_mark(); wait_for_ever(); + CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "two nodes each offer to the other"); + (void)settle(300); + CHECK(ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0, "the one at the lower place has its offer taken"); + CHECK(ND(b)->n.mem[OUT] == 5 && ND(b)->n.mem[OUT + 1] == 3, "the other gets the word, from the port it came by"); + CHECK(ND(a)->n.offers == 0 && ND(b)->n.offers == 0 && ND(a)->n.mem[OUT + 2] == 1 && ND(b)->n.mem[OUT + 2] == 1, "and neither has an offer left"); + + /* three in a ring, each offering to the next */ + fresh(); + a = loaded(0); offer(0, 11); wait_into(OUT); done_mark(); wait_for_ever(); + b = loaded(1); offer(0, 12); wait_into(OUT); done_mark(); wait_for_ever(); + c = loaded(2); offer(0, 13); wait_into(OUT); done_mark(); wait_for_ever(); + CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 1) && v4_fabric_wire(&f, b, 0, c, 1) && v4_fabric_wire(&f, c, 0, a, 1), "three nodes in a ring, each offering to the next"); + CHECK(settle(1000) < 1000, "they come to rest"); + CHECK(ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(b)->n.mem[OUT] == 11 && ND(b)->n.mem[OUT + 1] == 1, "the first's offer was taken by the second"); + CHECK(v4_node_in_wait(&ND(c)->n) && ND(c)->n.offers == 1u && ND(c)->n.mem[OUT + 2] == 0 && !ND(a)->n.given, "the third still offers to the first, which is not reading: nothing was handed over twice"); + + /* a neighbour that is asleep */ + fresh(); + a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever(); + b = loaded(1); 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) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to a reader"); + v4_fabric_sleep(&f, a); + for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); + v4_fabric_sleep(&f, b); v4_fabric_wake(&f, a); + for (i = 0; i < 100; i++) (void)v4_fabric_step(&f); + CHECK(v4_node_in_wait(&ND(a)->n) && ND(b)->n.reading && !ND(b)->n.given, "while the reader is asleep the offer stands"); + v4_fabric_wake(&f, b); + (void)settle(300); + CHECK(ND(b)->n.mem[OUT] == 7 && ND(a)->n.mem[OUT + 2] == 1, "woken, it takes it"); + + /* a port with nothing on it */ + fresh(); + a = loaded(0); offer(5, 7); wait_into(OUT); done_mark(); wait_for_ever(); + CHECK(v4_asm_ok(&as), "a node that offers on a port nothing is wired to"); + for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); + v4_fabric_gone_error(&f, 18); + for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); + CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.faults == 0, "a bare node waits, though the fabric has an error for it"); + v4_node_error_attach(&ND(a)->n, OUT + 20); + v4_node_fault_attach(&ND(a)->n, 900); + (void)v4_fabric_step(&f); + CHECK(!v4_node_in_wait(&ND(a)->n) && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18 && ND(a)->n.offers == 0, + "one that can take an error has it raised, and its offer is withdrawn"); + + /* a host with one node, whose devices always take */ + fresh(); + a = loaded(0); offer(1, 21); offer(3, 23); wait_into(OUT); done_mark(); wait_for_ever(); + for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); + CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.offers == ((1u << 1) | (1u << 3)), "a node offers on two ports and is in the wait"); + CHECK(v4_node_offer_as_write(&ND(a)->n) && ND(a)->n.asking && ND(a)->n.ask_port == 1 && ND(a)->n.request == 21, "its host turns that into a write of the lower port's word"); + v4_node_port_served(&ND(a)->n); + for (i = 0; i < 50; i++) (void)v4_fabric_step(&f); + CHECK(!ND(a)->n.asking && ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 1 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0, + "and when it is served the node is told that offer was taken"); + CHECK(!v4_node_offer_as_write(&ND(a)->n), "a node that is not in the wait is left alone"); + } + printf(" %d checks, %d failures\n", checks, failures); return failures != 0; }