#include /* use simple rv64 cores are lanes for now at least, possibly extend simple * model with some cross-lane interface for barriers etc. */ #include /* vector of riscv cores */ #define VEC_NAME lanes #define VEC_TYPE struct component * #include /* vector of requests */ struct req { struct reg reg; bool queued; }; #define VEC_NAME reqs #define VEC_TYPE struct req #include struct simt_riscv64 { struct component component; struct simt_riscv64_conf conf; struct component *imem; struct component *dmem; struct lanes lanes; struct component imem_intf; struct component dmem_intf; struct reqs data_rqs; struct reqs inst_rqs; size_t rr; }; static stat simt_riscv64_ext_send(uint64_t rcv, struct component *intf, struct component *mem, struct packet pkt) { /* do fixups for sending */ if (is_set(&pkt, PACKET_READ)) pkt.mask = ~0ULL; /* smuggle index as receive address */ uint32_t i = (uint32_t)(pkt.from >> 32); pkt.from = rcv | i; return SEND(intf, mem, pkt); } static stat simt_riscv64_handle_response(struct simt_riscv64 *c, struct component *intf, struct reqs *reqs, struct packet pkt) { /* use index smuggled as address to check which request this is a * response to */ uint32_t idx = (uint32_t)pkt.to; struct req *s = reqs_at(reqs, (size_t)idx); /* sanity check response, a bit crude for now */ assert(s); assert(s->reg.pkt.to == pkt.from); assert(s->reg.busy == true); assert( is_set(&pkt, PACKET_DONE)); assert(!is_set(&pkt, PACKET_ERROR)); /* reads are always full width to maximize chance of all lanes hitting * same cacheline. * * Probably not ideal for instructions when not lockstepping...? */ assert(is_set(&pkt, PACKET_READ) ? pkt.mask == ~0ULL : 1); /* use mask to indicate which portion of packet was meant for * this lane */ pkt.mask = s->reg.pkt.mask; /* use original from address */ pkt.to = s->reg.pkt.from; struct component *lane = *lanes_at(&c->lanes, idx); stat ok = SEND(intf, lane, pkt); assert(ok == OK); s->reg.busy = false; /* possibly broadcast response to readers */ if (is_set(&pkt, PACKET_READ)) for (size_t i = 0; i < c->conf.num_lanes; ++i) { struct req *req = reqs_at(reqs, i); if (!req->reg.busy) continue; /* 64-byte aligned */ if (req->reg.pkt.to != pkt.from) continue; /* skip non-reads */ if (!is_set(&req->reg.pkt, PACKET_READ)) continue; struct component *lane = *lanes_at(&c->lanes, i); pkt.mask = req->reg.pkt.mask; pkt.to = req->reg.pkt.from; stat ok = SEND(intf, lane, pkt); /* no reason for core to be blocked */ assert(ok == OK); req->reg.busy = false; } return OK; } static stat simt_riscv64_receive(struct simt_riscv64 *c, size_t rcv, struct component *mem, struct component *intf, struct reqs *reqs, struct component *from, struct packet pkt) { if (mem == from) return simt_riscv64_handle_response(c, intf, reqs, pkt); /* handle send */ int32_t idx = pkt.from >> 32; struct req *req = reqs_at(reqs, idx); if (req->reg.busy) return EBUSY; /* store packet */ req->reg.pkt = pkt; req->reg.busy = true; req->queued = false; /* check if packet can piggyback off of another packet */ if (is_set(&pkt, PACKET_READ)) foreach(reqs, other_req, reqs) { /* skip ourselves */ if (req == other_req) continue; if (!other_req->reg.busy) continue; struct packet *other_pkt = &other_req->reg.pkt; if (!is_set(other_pkt, PACKET_READ)) continue; /* yes, we can piggyback, therefore no need to do anything else */ if (pkt.to == other_pkt->to) return OK; } stat r = simt_riscv64_ext_send(rcv, intf, mem, pkt); if (r == EBUSY) req->queued = true; return r; } static stat simt_riscv64_data_receive(struct component *dmem_intf, struct component *from, struct packet pkt) { struct simt_riscv64 *c = CONTAINER_OF(dmem_intf, struct simt_riscv64, dmem_intf); return simt_riscv64_receive(c, c->conf.data_rcv, c->dmem, &c->dmem_intf, &c->data_rqs, from, pkt ); } static stat simt_riscv64_inst_receive(struct component *imem_intf, struct component *from, struct packet pkt) { struct simt_riscv64 *c = CONTAINER_OF(imem_intf, struct simt_riscv64, imem_intf); return simt_riscv64_receive(c, c->conf.inst_rcv, c->imem, &c->imem_intf, &c->inst_rqs, from, pkt ); } static stat simt_riscv64_clock(struct simt_riscv64 *c) { stat r = OK; /* send out queued stuff */ foreach(reqs, req, &c->data_rqs) { if (!req->queued) continue; r = simt_riscv64_ext_send(c->conf.data_rcv, &c->dmem_intf, c->dmem, req->reg.pkt); if (r == OK) { req->queued = false; continue; } if (r == EBUSY) continue; return r; } foreach(reqs, req, &c->inst_rqs) { if (!req->queued) continue; r = simt_riscv64_ext_send(c->conf.inst_rcv, &c->imem_intf, c->imem, req->reg.pkt); if (r == OK) { req->queued = false; continue; } if (r == EBUSY) continue; return r; } foreach(lanes, core, &c->lanes) { assert(core); if ((r = (*core)->clock(*core)) != OK) return r; } return r; } static void simt_riscv64_destroy(struct simt_riscv64 *c) { foreach(lanes, core, &c->lanes) { if ((*core)->destroy) (*core)->destroy(*core); else free(*core); } lanes_destroy(&c->lanes); reqs_destroy(&c->data_rqs); reqs_destroy(&c->inst_rqs); free(c); } struct component *create_simt_riscv64( struct simt_riscv64_conf conf, struct component *imem, struct component *dmem) { struct simt_riscv64 *new = calloc(1, sizeof(struct simt_riscv64)); if (!new) return NULL; new->component.clock = (clock_callback)simt_riscv64_clock; new->component.destroy = (destroy_callback)simt_riscv64_destroy; new->conf = conf; new->imem = imem; new->dmem = dmem; new->lanes = lanes_create(conf.num_lanes); new->data_rqs = reqs_create(conf.num_lanes); new->inst_rqs = reqs_create(conf.num_lanes); new->imem_intf.receive = (receive_callback)simt_riscv64_inst_receive; new->dmem_intf.receive = (receive_callback)simt_riscv64_data_receive; new->rr = 0; for (uint64_t i = 0; i < conf.num_lanes; ++i) { struct component *core = create_simple_riscv64( /* core ID, should group id also be given? */ i << 32, conf.start_pc, &new->imem_intf, &new->dmem_intf ); lanes_append(&new->lanes, core); struct req empty = { .reg = { .pkt = {}, .busy = false, }, .queued = false }; reqs_append(&new->data_rqs, empty); reqs_append(&new->inst_rqs, empty); } return (struct component *)&new->component; } struct component *simt_riscv64_data_intf(struct component *c) { struct simt_riscv64 *rv64 = (struct simt_riscv64 *)c; return &rv64->dmem_intf; } struct component *simt_riscv64_inst_intf(struct component *c) { struct simt_riscv64 *rv64 = (struct simt_riscv64 *)c; return &rv64->imem_intf; } void simt_riscv64_set_reg(struct component *c, size_t lane, size_t reg, uint64_t val) { struct simt_riscv64 *rv64 = (struct simt_riscv64 *)c; simple_riscv64_set_reg(*lanes_at(&rv64->lanes, lane), reg, val); }