diff options
Diffstat (limited to 'src/mesh/node1d.c')
| -rw-r--r-- | src/mesh/node1d.c | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/src/mesh/node1d.c b/src/mesh/node1d.c new file mode 100644 index 0000000..23ba2ca --- /dev/null +++ b/src/mesh/node1d.c @@ -0,0 +1,307 @@ +#include <gran/mesh/node1d.h> + +#define left_port(n) (n)->ports[(n)->elems + 0] +#define right_port(n) (n)->ports[(n)->elems + 1] + +#define left_in(n) (n)->in[(n)->elems + 0] +#define right_in(n) (n)->in[(n)->elems + 1] + +#define left_out(n) (n)->out[(n)->elems + 0] +#define right_out(n) (n)->out[(n)->elems + 1] + +struct reg { + struct packet pkt; + bool busy; +}; + +struct node1d { + struct component component; + uint16_t cluster; + uint16_t elems; + + uint64_t timestamp; + + struct reg *in; /* countedby[elems + 2] */ + struct reg *out; /* countedby[elems + 2] */ + struct component **ports; /* countedby[elems + 2] */ +}; + +static void node1d_destroy(struct node1d *n) +{ + free(n->in); + free(n->out); + free(n->ports); +} + +static stat reg_busy(struct reg *r, struct packet pkt) +{ + bool busy = r->busy; + if (!busy) { + r->pkt = pkt; + r->busy = true; + } + + return busy ? EBUSY : OK; +} + +static void copy_reg(struct reg *r, struct reg *s) +{ + assert(s->busy); + if (r->busy) + return; + + r->pkt = s->pkt; + r->busy = true; + s->busy = false; +} + +static stat node1d_receive(struct node1d *n, struct component *from, struct packet pkt) +{ + for (int i = 0; i < n->elems + 2; ++i) { + /* add timestamp to packets that originate with us */ + if (i < n->elems) + pkt.timestamp = n->timestamp; + + if (from == n->ports[i]) + return reg_busy(&n->in[i], pkt); + } + + /* shouldn't be possible */ + abort(); + return OK; +} + +static void clock_outputs(struct node1d *n) +{ + for (int i = 0; i < n->elems + 2; ++i) { + if (!n->out[i].busy) + continue; + + stat ret = SEND(n, n->ports[i], n->out[i].pkt); + if (ret == EBUSY) + continue; + + n->out[i].busy = false; + } +} + +static void propagate_left(struct node1d *n, struct reg *a, struct reg *b) +{ + struct reg *sel_a = NULL, *sel_b = NULL; + if (a && a->busy) { + uint16_t cluster = 0; + addr_mesh1d(a->pkt.to, &cluster, NULL, NULL); + + if (cluster > n->cluster) + sel_a = a; + } + + if (b && b->busy) { + uint16_t cluster = 0; + addr_mesh1d(b->pkt.to, &cluster, NULL, NULL); + + if (cluster > n->cluster) + sel_b = b; + } + + if (!sel_a && !sel_b) + return; + + if (sel_a && !sel_b) { + copy_reg(&left_out(n), sel_a); + return; + } + + if (!sel_a && sel_b) { + copy_reg(&left_out(n), sel_b); + return; + } + + /* both available, select older */ + if (sel_a->pkt.timestamp < sel_b->pkt.timestamp) + copy_reg(&left_out(n), sel_a); + else + copy_reg(&left_out(n), sel_b); +} + +static void propagate_right(struct node1d *n, struct reg *a, struct reg *b) +{ + struct reg *sel_a = NULL, *sel_b = NULL; + if (a && a->busy) { + uint16_t cluster = 0; + addr_mesh1d(a->pkt.to, &cluster, NULL, NULL); + + if (cluster < n->cluster) + sel_a = a; + } + + if (b && b->busy) { + uint16_t cluster = 0; + addr_mesh1d(b->pkt.to, &cluster, NULL, NULL); + + if (cluster < n->cluster) + sel_b = b; + } + + if (!sel_a && !sel_b) + return; + + if (sel_a && !sel_b) { + copy_reg(&right_out(n), sel_a); + return; + } + + if (!sel_a && sel_b) { + copy_reg(&right_out(n), sel_b); + return; + } + + /* both available, select older */ + if (sel_a->pkt.timestamp < sel_b->pkt.timestamp) + copy_reg(&right_out(n), sel_a); + else + copy_reg(&right_out(n), sel_b); +} + +static void propagate(struct node1d *n, int elem, struct reg *a, struct reg *b, struct reg *c) +{ + + struct reg *sel_a = NULL, *sel_b = NULL, *sel_c = NULL; + if (a && a->busy) { + uint16_t cluster = 0, element = 0; + addr_mesh1d(a->pkt.to, &cluster, &element, NULL); + + if (cluster == n->cluster && element == elem) + sel_a = a; + } + + if (b && b->busy) { + uint16_t cluster = 0, element = 0; + addr_mesh1d(b->pkt.to, &cluster, &element, NULL); + + if (cluster == n->cluster && element == elem) + sel_b = b; + } + + if (c && c->busy) { + uint16_t cluster = 0, element = 0; + addr_mesh1d(c->pkt.to, &cluster, &element, NULL); + + if (cluster == n->cluster && element == elem) + sel_c = c; + } + + struct reg *sel_0 = NULL, *sel_1 = NULL; + if (sel_a && sel_b) + sel_0 = sel_a->pkt.timestamp < sel_b->pkt.timestamp ? sel_a : sel_b; + else + sel_0 = sel_a ? sel_a : sel_b; + + if (sel_b && sel_c) + sel_1 = sel_b->pkt.timestamp < sel_c->pkt.timestamp ? sel_b : sel_c; + else + sel_1 = sel_b ? sel_b : sel_c; + + struct reg *sel = NULL; + if (sel_0 && sel_1) + sel = sel_0->pkt.timestamp < sel_1->pkt.timestamp ? sel_0 : sel_1; + else + sel = sel_0 ? sel_0 : sel_1; + + if (!sel) + return; + + copy_reg(&n->out[elem], sel); +} + +static stat node1d_clock(struct node1d *n) +{ + n->timestamp++; + + clock_outputs(n); + + /* select oldest packet to process */ + struct reg *r = NULL; + for (int i = 0; i < n->elems; ++i) { + if (!n->in[i].busy) + continue; + + if (!r || r->pkt.timestamp > n->in[i].pkt.timestamp) + r = &n->in[i]; + } + + propagate_left(n, r, &right_in(n)); + propagate_right(n, r, &left_in(n)); + for (int i = 0; i < n->elems; ++i) + propagate(n, i, r, &right_in(n), &left_in(n)); + + return OK; +} + +stat mesh_node1d_connect(struct component *c, struct component *e, uint16_t elem) +{ + struct node1d *n = (struct node1d *)c; + if (elem >= n->elems) + return ENOSUCH; + + if (n->ports[elem]) + return EEXISTS; + + n->ports[elem] = e; + return OK; +} + +stat mesh_node1d_connect_left(struct component *c, struct component *e) +{ + struct node1d *n = (struct node1d *)c; + if (left_port(n)) + return EEXISTS; + + left_port(n) = e; + return OK; +} + +stat mesh_node1d_connect_right(struct component *c, struct component *e) +{ + struct node1d *n = (struct node1d *)c; + if (right_port(n)) + return EEXISTS; + + right_port(n) = e; + return OK; +} + +struct component *create_mesh_node1d(uint16_t cluster, uint16_t elems) +{ + struct node1d *n = (struct node1d *)calloc(1, sizeof(struct node1d)); + if (!n) + return NULL; + + n->in = (struct reg *)calloc(elems + 2, sizeof(struct reg)); + if (!n->in) { + free(n); + return NULL; + } + + n->out = (struct reg *)calloc(elems + 2, sizeof(struct reg)); + if (!n->out) { + free(n->in); + free(n); + return NULL; + } + + n->ports = (struct component **)calloc(elems + 2, sizeof(struct component *)); + if (!n->ports) { + free(n->out); + free(n->in); + free(n); + return NULL; + } + + n->component.destroy = (destroy_callback)node1d_destroy; + n->component.receive = (receive_callback)node1d_receive; + n->component.clock = (clock_callback)node1d_clock; + n->cluster = cluster; + n->elems = elems; + return (struct component *)n; +} |
