From fecb86f6093c1e8aed6ab05c29c5af9d0cb93157 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Tue, 24 Sep 2024 16:51:51 +0300 Subject: initial work towards message passing interface + As they are currently implemented, grid *may* get stuck if two nodes try to send to eachother at the same time, I should probably add in some kind of input buffer as well --- include/gran/bus/simple_bus.h | 2 +- include/gran/component.h | 62 ++------- include/gran/cpu/riscv/simple_riscv64.h | 2 +- include/gran/grid/node.h | 19 +++ include/gran/packet.h | 223 ++++++++++++++++++++++++++---- include/gran/snoop.h | 23 --- src/clock_domain.c | 17 +-- src/components/bus/simple_bus.c | 182 ++++++++---------------- src/components/cpu/riscv/simple_riscv64.c | 209 ++++++++++++++++------------ src/components/grid/node.c | 91 +++++++----- src/components/grid/router.c | 72 ++++++---- src/components/mem/simple_mem.c | 53 ++++--- src/components/uart/simple_uart.c | 42 ++++-- src/packet.c | 92 ------------ src/snoop.c | 42 ------ tests/simple_grid/sim.c | 3 +- tests/simple_mem/no_bus.c | 1 + tests/simple_mem/traffic_gen.c | 64 +++++---- 18 files changed, 616 insertions(+), 583 deletions(-) delete mode 100644 include/gran/snoop.h delete mode 100644 src/packet.c delete mode 100644 src/snoop.c diff --git a/include/gran/bus/simple_bus.h b/include/gran/bus/simple_bus.h index 62dd7d2..eb1e821 100644 --- a/include/gran/bus/simple_bus.h +++ b/include/gran/bus/simple_bus.h @@ -4,7 +4,7 @@ #ifndef GRAN_SIMPLE_BUS_H #define GRAN_SIMPLE_BUS_H -#include +#include struct component *create_simple_bus(); stat simple_bus_add(struct component *bus, struct component *component, diff --git a/include/gran/component.h b/include/gran/component.h index 4ef94ad..289d0a6 100644 --- a/include/gran/component.h +++ b/include/gran/component.h @@ -12,36 +12,20 @@ #include #include -#include struct component; -typedef stat (*read_callback)(struct component *, struct packet *pkt); -typedef stat (*write_callback)(struct component *, struct packet *pkt); -typedef stat (*swap_callback)(struct component *, struct packet *pkt); - -typedef stat (*snoop_callback)(struct component *, struct snoop *snoop); -typedef stat (*ctrl_callback)(struct component *, struct packet *pkt); - -typedef stat (*irq_callback)(struct component *, int); +typedef stat (*receive_callback)(struct component *to, struct component *from, struct packet pkt); typedef stat (*clock_callback)(struct component *); typedef stat (*stat_callback)(struct component *, FILE *); typedef stat (*dts_callback)(struct component *, FILE *); - typedef void (*destroy_callback)(struct component *); struct component { /* optional */ char *name; - read_callback read; - write_callback write; - swap_callback swap; - - snoop_callback snoop; - ctrl_callback ctrl; - - irq_callback irq; + receive_callback receive; clock_callback clock; dts_callback dts; @@ -50,48 +34,20 @@ struct component { destroy_callback destroy; }; -static inline stat write(struct component *component, struct packet *pkt) +static inline stat send(struct component *from, struct component *to, struct packet pkt) { - assert(packet_type(pkt) == PACKET_WRITE); - if (!component->write) { + if (!to->receive) { /* printf formatted asserts would maybe be preferable? */ error( - "tried writing %p:%" PRIuPTR " but it doesn't support writing", - component->name, packet_addr(pkt)); - return ENOSUCH; - } - - packet_set_state(pkt, PACKET_SENT); - return component->write(component, pkt); -} - -static inline stat read(struct component *component, struct packet *pkt) -{ - assert(packet_type(pkt) == PACKET_READ); - if (!component->read) { - error( - "tried reading %p:%" PRIuPTR " but it doesn't support reading", - component->name, packet_addr(pkt)); - return ENOSUCH; - } - - packet_set_state(pkt, PACKET_SENT); - return component->read(component, pkt); -} - -static inline stat swap(struct component *component, struct packet *pkt) -{ - assert(packet_type(pkt) == PACKET_SWAP); - if (!component->swap) { - error( - "tried swapping %p:%" PRIuPTR " but it doesn't support swapping", - component->name, packet_addr(pkt)); + "%s:%lx tried sending to %s:%lx", + from->name, pkt.from, + to->name, pkt.to); return ENOSUCH; } - packet_set_state(pkt, PACKET_SENT); - return component->swap(component, pkt); + return to->receive(to, from, pkt); } +#define SEND(x, y, z) send((struct component *)(x), (struct component *)(y), z) static inline void destroy(struct component *component) { diff --git a/include/gran/cpu/riscv/simple_riscv64.h b/include/gran/cpu/riscv/simple_riscv64.h index 8f66013..fe6e686 100644 --- a/include/gran/cpu/riscv/simple_riscv64.h +++ b/include/gran/cpu/riscv/simple_riscv64.h @@ -6,7 +6,7 @@ #include -struct component *create_simple_riscv64(uint32_t start_pc, +struct component *create_simple_riscv64(uint64_t rcv, uint32_t start_pc, struct component *imem, struct component *dmem); diff --git a/include/gran/grid/node.h b/include/gran/grid/node.h index 637ac91..16116d2 100644 --- a/include/gran/grid/node.h +++ b/include/gran/grid/node.h @@ -11,4 +11,23 @@ stat grid_node_connect(struct component *node, struct component *up, struct component *down, struct component *lower, struct component *ascend); +static inline uint64_t grid_addr(uint8_t u, uint8_t v, uint8_t x, uint8_t y, uint32_t off) +{ + return off + | ((uint64_t)x << 32) + | ((uint64_t)y << 40) + | ((uint64_t)u << 48) + | ((uint64_t)v << 56) + ; +} + +static inline void addr_grid(uint64_t addr, uint32_t *off, uint8_t *x, uint8_t *y, uint8_t *u, uint8_t *v) +{ + if (off) *off = addr & 0xffffffff; + if (x) *x = (addr >> 32) & 0xff; + if (y) *y = (addr >> 40) & 0xff; + if (u) *u = (addr >> 48) & 0xff; + if (v) *v = (addr >> 56) & 0xff; +} + #endif /* GRAN_GRID_NODE_H */ diff --git a/include/gran/packet.h b/include/gran/packet.h index ab07b56..5f8bd9c 100644 --- a/include/gran/packet.h +++ b/include/gran/packet.h @@ -4,44 +4,215 @@ #ifndef GRAN_PACKET_H #define GRAN_PACKET_H +#include #include #include +#include +#include -enum packet_state { - PACKET_INIT, - PACKET_FAILED, - PACKET_SENT, - PACKET_WAITING, - PACKET_DONE, +enum packet_flags { + PACKET_READ = (1 << 0), + PACKET_WRITE = (1 << 1), + PACKET_ATOMIC = (1 << 2), + PACKET_ERROR = (1 << 3), + PACKET_DONE = (1 << 4) }; -enum packet_type { - PACKET_READ, - PACKET_WRITE, - PACKET_SWAP, - PACKET_SNOOP, - PACKET_CTRL, +struct packet { + uint64_t from; + uint64_t to; + uint64_t mask; + uint8_t data[64]; + enum packet_flags flags; }; -struct packet; +static inline struct packet response(struct packet pkt) +{ + uint64_t from = pkt.from; + uint64_t to = pkt.to; + pkt.from = to; + pkt.to = from; + return pkt; +} -struct packet *create_packet(enum packet_type type, uintptr_t addr, - size_t size); +static inline void set_flags(struct packet *pkt, enum packet_flags flags) +{ + pkt->flags |= flags; +} -struct packet *create_packet_with(enum packet_type type, uintptr_t addr, - size_t size, void *data); +static inline void clear_flags(struct packet *pkt, enum packet_flags flags) +{ + pkt->flags &= ~flags; +} -struct packet *reuse_packet(struct packet *pkt, enum packet_type type, - uintptr_t addr, size_t size, void *data); +static inline bool is_set(struct packet *pkt, enum packet_flags flags) +{ + return pkt->flags & flags; +} -enum packet_type packet_type(struct packet *pkt); -enum packet_state packet_state(struct packet *pkt); -void packet_set_state(struct packet *pkt, enum packet_state state); +static inline uint64_t packet_align(uint64_t addr) +{ + return addr & ~(64 - 1); +} -size_t packet_size(struct packet *pkt); -uint64_t packet_addr(struct packet *pkt); -void *packet_data(struct packet *pkt); +static inline uint64_t packet_mask(uint64_t addr, uint64_t size) +{ + assert(size <= 64); + uint64_t mask = size == 64 ? ((uint64_t)-1) : (((uint64_t)1 << size) - 1); + uint64_t aligned = packet_align(addr); + uint64_t diff = addr - aligned; -void destroy_packet(struct packet *pkt); + assert((diff == 0) || ((mask >> (64 - diff)) == 0)); + mask <<= diff; + return mask; +} + +static inline uint64_t packet_convsize(struct packet *pkt) +{ + int start = __builtin_ffsll(pkt->mask); + if (start == 0) + return 0; + + switch ((pkt->mask >> (start - 1)) & 0xff) { + case 0x01: return 1; + case 0x03: return 2; + case 0x0f: return 4; + case 0xff: return 8; + default: abort(); + } + + return 0; +} + +static inline uint64_t packet_convto(struct packet *pkt) +{ + int start = __builtin_ffsll(pkt->mask); + if (start == 0) + return pkt->to; + + return pkt->to + (start - 1); +} + +static inline uint64_t packet_convfrom(struct packet *pkt) +{ + int start = __builtin_ffsll(pkt->mask); + if (start == 0) + return pkt->from; + + return pkt->from + (start - 1); +} + +static inline int64_t packet_convi8(struct packet *pkt) +{ + int start = __builtin_ffsll(pkt->mask); + assert(start != 0); + + return pkt->data[start - 1]; +} + +static inline uint8_t packet_convu8(struct packet *pkt) +{ + int start = __builtin_ffsll(pkt->mask); + assert(start != 0); + + return pkt->data[start - 1]; +} + +static inline int16_t packet_convi16(struct packet *pkt) +{ + int start = __builtin_ffsll(pkt->mask); + assert(start != 0); + + int16_t res = 0; + memcpy(&res, pkt->data + (start - 1), 2); + return res; +} + +static inline uint16_t packet_convu16(struct packet *pkt) +{ + int start = __builtin_ffsll(pkt->mask); + assert(start != 0); + + uint16_t res = 0; + memcpy(&res, pkt->data + (start - 1), 2); + return res; +} + +static inline int32_t packet_convi32(struct packet *pkt) +{ + int start = __builtin_ffsll(pkt->mask); + assert(start != 0); + + int32_t res = 0; + memcpy(&res, pkt->data + (start - 1), 4); + return res; +} + +static inline uint32_t packet_convu32(struct packet *pkt) +{ + int start = __builtin_ffsll(pkt->mask); + assert(start != 0); + + uint32_t res = 0; + memcpy(&res, pkt->data + (start - 1), 4); + return res; +} + +static inline int64_t packet_convi64(struct packet *pkt) +{ + int start = __builtin_ffsll(pkt->mask); + assert(start != 0); + + int64_t res = 0; + memcpy(&res, pkt->data + (start - 1), 8); + return res; +} + +static inline uint64_t packet_convu64(struct packet *pkt) +{ + int start = __builtin_ffsll(pkt->mask); + assert(start != 0); + + uint64_t res = 0; + memcpy(&res, pkt->data + (start - 1), 8); + return res; +} + +static inline struct packet create_packet(uint64_t from, uint64_t to, uint64_t size, void *data, enum packet_flags flags) +{ + assert(size <= 64); + assert(packet_align(from) == from); + + uint64_t aligned = packet_align(to); + uint64_t mask = packet_mask(to, size); + struct packet pkt = (struct packet){ + .from = from, + .to = aligned, + .mask = mask, + .data = {}, + .flags = flags + }; + + if (is_set(&pkt, PACKET_WRITE)) + memcpy(pkt.data + (to - aligned), data, size); + + return pkt; +} + +static inline void checked_copyto(struct packet *pkt, uint8_t *data) +{ + for (size_t i = 0; i < 64; ++i) { + if (pkt->mask & ((uint64_t)1 << i)) + pkt->data[i] = data[i]; + } +} + +static inline void checked_copyfrom(struct packet *pkt, uint8_t *data) +{ + for (size_t i = 0; i < 64; ++i) { + if (pkt->mask & ((uint64_t)1 << i)) + data[i] = pkt->data[i]; + } +} #endif /* GRAN_PACKET_H */ diff --git a/include/gran/snoop.h b/include/gran/snoop.h deleted file mode 100644 index 5254984..0000000 --- a/include/gran/snoop.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: copyleft-next-0.3.1 */ -/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ - -#ifndef GRAN_SNOOP_H -#define GRAN_SNOOP_H - -#include - -enum snoop_state { - SNOOP_UNANSWERED, - SNOOP_ANSWERED, -}; - -struct snoop; - -struct snoop *create_snoop(uintptr_t addr, size_t size); -void destroy_snoop(struct snoop *snoop); - -enum snoop_state snoop_state(struct snoop *snoop); -uintptr_t snoop_addr(struct snoop *snoop); -size_t snoop_size(struct snoop *snoop); - -#endif /* GRAN_SNOOP_H */ diff --git a/src/clock_domain.c b/src/clock_domain.c index 6610d8f..74f0f03 100644 --- a/src/clock_domain.c +++ b/src/clock_domain.c @@ -20,8 +20,6 @@ struct clock_domain { tick period; stat ret; - - mtx_t mtx; }; void advance_clock(struct clock_domain *clk) @@ -41,7 +39,6 @@ struct clock_domain *create_clock_domain(tick period) return NULL; clk->period = period; - mtx_init(&clk->mtx, mtx_plain); clk->components = vec_create(sizeof(struct component *)); return clk; @@ -62,20 +59,12 @@ stat clock_domain_add(struct clock_domain *clk, struct component *component) return OK; } -static void update_ret(struct clock_domain *clk, stat ret) -{ - if (ret) { - mtx_lock(&clk->mtx); - clk->ret = ret; - mtx_unlock(&clk->mtx); - } -} - static void clocked_component_tick(struct clock_domain *clk, struct component *component) { - stat ret = component->clock(component); - update_ret(clk, ret); + stat r = component->clock(component); + if (r) + clk->ret = r; } stat clock_domain_tick(struct clock_domain *clk) diff --git a/src/components/bus/simple_bus.c b/src/components/bus/simple_bus.c index 218e930..1932419 100644 --- a/src/components/bus/simple_bus.c +++ b/src/components/bus/simple_bus.c @@ -1,154 +1,76 @@ /* SPDX-License-Identifier: copyleft-next-0.3.1 */ /* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ - -#include -#include -#include - -#include -#include +#include #include +#include -struct mem_region { - uintptr_t addr; - size_t size; +struct bus_region { + uint64_t addr; + uint64_t size; struct component *component; - struct mem_region *next; }; struct simple_bus { struct component component; - mtx_t lock; + struct vec regions; - struct mem_region *mem_regions; + struct component *send; + struct packet pkt; + bool busy; }; -static struct mem_region *find_mem_region(struct simple_bus *bus, - uintptr_t addr) +static struct bus_region *find_bus_region(struct simple_bus *bus, uint64_t addr) { - if (!bus->mem_regions) - return NULL; - - struct mem_region *cur = bus->mem_regions; - while (cur) { - /* address is within memory region */ - if (addr >= cur->addr && addr < cur->addr + cur->size) - return cur; - - cur = cur->next; + foreach_vec(i, bus->regions) { + struct bus_region *r = vec_at(&bus->regions, i); + if (addr >= r->addr && addr < (r->addr + r->size)) + return r; } return NULL; } -static stat add_mem_region(struct simple_bus *bus, struct mem_region *new) +static stat simple_bus_clock(struct simple_bus *bus) { - if (!bus->mem_regions) { - bus->mem_regions = new; - return OK; - } + if (bus->busy) { + stat r = SEND(bus, bus->send, bus->pkt); + if (r == EBUSY) + return OK; - struct mem_region *found = NULL; - if ((found = find_mem_region(bus, new->addr))) { - error("%s overlaps with %s at %" PRIuPTR, - new->component->name, - found->component->name, - new->addr - ); - return EEXISTS; + bus->busy = false; + return r; } - new->next = bus->mem_regions; - bus->mem_regions = new; return OK; } -static stat simple_bus_write(struct simple_bus *bus, struct packet *pkt) +static stat simple_bus_receive(struct simple_bus *bus, struct component *from, struct packet pkt) { - /* only one device can drive the bus at one time */ - if (mtx_trylock(&bus->lock) != thrd_success) + if (bus->busy) return EBUSY; - struct mem_region *mem_region = find_mem_region(bus, packet_addr(pkt)); - if (!mem_region) { - warn("nothing to write on bus %s at %" PRIuPTR, - bus->component.name, packet_addr(pkt)); - return EBUS; - } - - stat ret = write(mem_region->component, pkt); - - mtx_unlock(&bus->lock); - return ret; -} + bus->busy = true; -static stat simple_bus_read(struct simple_bus *bus, struct packet *pkt) -{ - /* only one device can drive the bus at one time */ - if (mtx_trylock(&bus->lock) != thrd_success) - return EBUSY; - - struct mem_region *mem_region = find_mem_region(bus, packet_addr(pkt)); - if (!mem_region) { - warn("nothing to read on bus %s at %" PRIuPTR, - bus->component.name, packet_addr(pkt)); - return EBUS; - } + struct bus_region *region = find_bus_region(bus, pkt.to); + if (!region) { + warn("illegal address on bus %s at %" PRIuPTR, + bus->component.name, pkt.to); - stat ret = read(mem_region->component, pkt); - - mtx_unlock(&bus->lock); - return ret; -} - -static stat simple_bus_swap(struct simple_bus *bus, struct packet *pkt) -{ - if (mtx_trylock(&bus->lock) != thrd_success) - return EBUSY; - - struct mem_region *mem_region = find_mem_region(bus, packet_addr(pkt)); - if (!mem_region) { - warn("nothing to swap on bus %s at %" PRIuPTR, - bus->component.name, packet_addr(pkt)); - return EBUS; - } - - stat ret = swap(mem_region->component, pkt); - - mtx_unlock(&bus->lock); - return ret; -} - -static stat simple_bus_snoop(struct simple_bus *bus, struct snoop *snoop) -{ - struct mem_region *cur = bus->mem_regions; - while (cur) { - if (cur->component->snoop) { - stat ret = cur->component->snoop(cur->component, snoop); - if (ret) - return ret; - - if (snoop_state(snoop) == SNOOP_ANSWERED) - return OK; - } - - cur = cur->next; + bus->send = from; + bus->pkt = response(pkt); + set_flags(&bus->pkt, PACKET_ERROR); + return OK; } + bus->send = region->component; + bus->pkt = pkt; return OK; } static void simple_bus_destroy(struct simple_bus *bus) { - struct mem_region *cur = bus->mem_regions, *next = NULL; - if (cur) - do { - next = cur->next; - destroy(cur->component); - free(cur); - } while ((cur = next)); - + vec_destroy(&bus->regions); free(bus); } @@ -158,34 +80,38 @@ struct component *create_simple_bus() if (!bus) return NULL; - bus->component.write = (write_callback)simple_bus_write; - bus->component.read = (read_callback)simple_bus_read; - bus->component.swap = (swap_callback)simple_bus_swap; - bus->component.snoop = (snoop_callback)simple_bus_snoop; - // actually, still not sure about what API I want to use for controls - // bus->component.ctrl = (ctrl_callback)simple_bus_ctrl; + bus->component.receive = (receive_callback)simple_bus_receive; + bus->component.clock = (clock_callback)simple_bus_clock; bus->component.destroy = (destroy_callback)simple_bus_destroy; + bus->regions = vec_create(sizeof(struct bus_region)); - mtx_init(&bus->lock, mtx_plain); return (struct component *)bus; } stat simple_bus_add(struct component *bus, struct component *component, uint64_t addr, uint64_t size) { - struct mem_region *new = calloc(1, sizeof(struct mem_region)); - if (!new) - return ENOMEM; + struct simple_bus *b = (struct simple_bus *)bus; + struct bus_region *found = find_bus_region(b, addr); + if (!found) found = find_bus_region(b, addr + size); - new->addr = addr; - new->size = size; - new->component = component; + if (found) { + error("%s overlaps with %s at %" PRIuPTR, + found->component->name, + component->name, + found->addr + ); - if (add_mem_region((struct simple_bus *)bus, new)) { - free(new); return EEXISTS; } + struct bus_region new = (struct bus_region){ + .component = component, + .addr = addr, + .size = size + }; + + vect_append(struct bus_region, b->regions, &new); return OK; } diff --git a/src/components/cpu/riscv/simple_riscv64.c b/src/components/cpu/riscv/simple_riscv64.c index be96e60..987838e 100644 --- a/src/components/cpu/riscv/simple_riscv64.c +++ b/src/components/cpu/riscv/simple_riscv64.c @@ -9,8 +9,13 @@ #include -struct simple_rv64_ldst { - struct packet *pkt; +enum ldst_state { + LDST_IDLE, LDST_BLOCKED, LDST_SENT, LDST_DONE +}; + +struct ldst { + struct packet pkt; + enum ldst_state state; uint32_t reg; bool u; }; @@ -21,8 +26,10 @@ struct simple_riscv64 { struct component *imem; struct component *dmem; - struct simple_rv64_ldst dls; - struct simple_rv64_ldst ils; + struct ldst dls; + struct ldst ils; + + uint64_t rcv; /* have to be careful with x0 */ uint64_t regs[32]; @@ -396,25 +403,31 @@ static stat load(struct simple_riscv64 *cpu, union rv_insn insn) /* LH/LHU */ case 0b101: u = true; /* fallthrough */ case 0b001: size = 2; break; - /* LW */ + /* LW/LWU */ + case 0b110: u = true; /* fallthrough */ case 0b010: size = 4; break; + /* LD */ case 0b011: size = 8; break; default: error("unknown LOAD width %x", insn.btype.funct3); return ENOSUCH; } - struct packet *pkt = create_packet(PACKET_READ, addr, size); - if (!pkt) - return EMEM; - - cpu->dls = (struct simple_rv64_ldst){pkt, insn.itype.rd, u}; - stat ret = read(cpu->dmem, pkt); - if (ret) - return ret; + struct packet pkt = create_packet(cpu->rcv, + addr, + size, + NULL, + PACKET_READ); + + cpu->dls = (struct ldst){pkt, LDST_SENT, insn.itype.rd, u}; + stat ret = SEND(cpu, cpu->dmem, pkt); + if (ret == EBUSY) { + cpu->dls.state = LDST_BLOCKED; + ret = OK; + } cpu->pc += 4; - return OK; + return ret; } #define STYPE_IMM(insn) \ @@ -428,66 +441,58 @@ static stat store(struct simple_riscv64 *cpu, union rv_insn insn) int64_t addr = base + imm; uint64_t src = get_reg(cpu, insn.stype.rs2); + uint64_t size = 0; switch (insn.stype.funct3) { /* SB */ - case 0b000: { - cpu->dls.pkt = create_packet(PACKET_WRITE, addr, 1); - *(uint8_t *)packet_data(cpu->dls.pkt) = src; - break; - } + case 0b000: size = 1; break; /* SH */ - case 0b001: { - cpu->dls.pkt = create_packet(PACKET_WRITE, addr, 2); - *(uint16_t *)packet_data(cpu->dls.pkt) = src; - break; - } + case 0b001: size = 2; break; /* SW */ - case 0b010: { - cpu->dls.pkt = create_packet(PACKET_WRITE, addr, 4); - *(uint32_t *)packet_data(cpu->dls.pkt) = src; - break; - } + case 0b010: size = 4; break; /* SD */ - case 0b011: { - cpu->dls.pkt = create_packet(PACKET_WRITE, addr, 8); - *(uint64_t *)packet_data(cpu->dls.pkt) = src; - break; - } + case 0b011: size = 8; break; default: error("unknown width of STORE %x", insn.stype.funct3); return ENOSUCH; } + struct packet pkt = create_packet(cpu->rcv, addr, size, &src, PACKET_WRITE); + cpu->dls = (struct ldst){pkt, LDST_SENT, 0, false}; + stat ret = SEND(cpu, cpu->dmem, pkt); + if (ret == EBUSY) { + cpu->dls.state = LDST_BLOCKED; + ret = OK; + } + cpu->pc += 4; - return write(cpu->dmem, cpu->dls.pkt); + return ret; } static void finalize_ld(struct simple_riscv64 *cpu) { - struct simple_rv64_ldst ld = cpu->dls; + struct ldst ld = cpu->dls; uint64_t val = 0; - void *data = packet_data(ld.pkt); - switch (packet_size(ld.pkt)) { + switch (packet_convsize(&ld.pkt)) { case 1: - if (ld.u) val = *(uint8_t *)data; - else val = *(int8_t *)data; + if (ld.u) val = packet_convu8(&ld.pkt); + else val = packet_convi8(&ld.pkt); break; - case 2: if (ld.u) val = *(uint16_t *)data; - else val = *(int16_t *)data; + case 2: if (ld.u) val = packet_convu16(&ld.pkt); + else val = packet_convi16(&ld.pkt); break; - case 4: if (ld.u) val = *(uint32_t *)data; - else val = *(int32_t *)data; + case 4: if (ld.u) val = packet_convi32(&ld.pkt); + else val = packet_convu32(&ld.pkt); break; - case 8: val = *(uint64_t *)data; + case 8: val = packet_convu64(&ld.pkt); break; default: - error("unknown load size %zu", packet_size(ld.pkt)); + error("unknown load size %zu", packet_convsize(&ld.pkt)); } set_reg(cpu, ld.reg, val); @@ -501,58 +506,97 @@ static void finalize_st(struct simple_riscv64 *cpu) static void finalize_dls(struct simple_riscv64 *cpu) { - struct packet *pkt = cpu->dls.pkt; + cpu->dls.state = LDST_IDLE; + struct packet pkt = cpu->dls.pkt; - if (packet_type(pkt) == PACKET_READ) + if (is_set(&pkt, PACKET_READ)) finalize_ld(cpu); - else if (packet_type(pkt) == PACKET_WRITE) + else if (is_set(&pkt, PACKET_WRITE)) finalize_st(cpu); else error("unsupported packet type for simple_riscv64"); - - destroy_packet(pkt); - cpu->dls.pkt = NULL; } static uint32_t finalize_ils(struct simple_riscv64 *cpu) { - uint32_t insn = *(uint32_t *)packet_data(cpu->ils.pkt); - destroy_packet(cpu->ils.pkt); - cpu->ils.pkt = NULL; - return insn; + cpu->ils.state = LDST_IDLE; + return packet_convu32(&cpu->ils.pkt); +} + +static stat simple_riscv64_receive(struct simple_riscv64 *cpu, struct component *from, struct packet pkt) +{ + (void)from; + + if (pkt.to == cpu->rcv) { + cpu->dls.pkt = pkt; + cpu->dls.state = LDST_DONE; + return OK; + } + else if (pkt.to == cpu->rcv + 64) { + cpu->ils.pkt = pkt; + cpu->ils.state = LDST_DONE; + return OK; + } + else { + error("illegal receive on %s", cpu->component.name); + return EBUS; + } } static stat simple_riscv64_clock(struct simple_riscv64 *cpu) { /* there's an active data transfer we should handle */ - if (cpu->dls.pkt) { - assert(packet_state(cpu->dls.pkt) != PACKET_FAILED); + if (cpu->dls.state != LDST_IDLE) { + assert(!is_set(&cpu->dls.pkt, PACKET_ERROR)); + + if (cpu->dls.state == LDST_BLOCKED) { + stat r = SEND(cpu, cpu->dmem, cpu->dls.pkt); + if (r == EBUSY) + return OK; + + cpu->dls.state = LDST_SENT; + return OK; + } + + if (cpu->dls.state == LDST_SENT) + return OK; - if (packet_state(cpu->dls.pkt) == PACKET_DONE) + if (cpu->dls.state == LDST_DONE) { finalize_dls(cpu); - else /* wait for data */ + cpu->dls.state = LDST_IDLE; + } + else return OK; } - if (!cpu->ils.pkt) { - cpu->ils.pkt = - create_packet(PACKET_READ, cpu->pc, sizeof(uint32_t)); - if (!cpu->ils.pkt) - return EMEM; + if (cpu->ils.state == LDST_BLOCKED) { + stat ret = SEND(cpu, cpu->imem, cpu->ils.pkt); + if (ret == EBUSY) + return OK; - stat ret = read(cpu->imem, cpu->ils.pkt); - if (ret) - return ret; + return OK; } uint32_t insn = 0; - if (cpu->ils.pkt) { - assert(packet_state(cpu->ils.pkt) != PACKET_FAILED); + if (cpu->ils.state == LDST_DONE) { + assert(!is_set(&cpu->dls.pkt, PACKET_ERROR)); - if (packet_state(cpu->ils.pkt) == PACKET_DONE) - insn = finalize_ils(cpu); - else /* wait for instruction */ - return OK; + insn = finalize_ils(cpu); + cpu->ils.state = LDST_IDLE; + } + + if (cpu->ils.state == LDST_IDLE) { + cpu->ils.pkt = create_packet(cpu->rcv + 64, + cpu->pc, + sizeof(uint32_t), + NULL, + PACKET_READ); + cpu->ils.state = LDST_SENT; + stat ret = SEND(cpu, cpu->imem, cpu->ils.pkt); + if (ret == EBUSY) { + cpu->ils.state = LDST_BLOCKED; + return ret; + } } // for now assume little endian emulated and host cpu @@ -586,23 +630,12 @@ static stat simple_riscv64_clock(struct simple_riscv64 *cpu) static void simple_riscv64_destroy(struct simple_riscv64 *cpu) { - /* oh yeah, will have to think about the name stuff, - * i.e. how and where to free it, and where to assign it */ destroy(cpu->imem); - - if (cpu->imem != cpu->dmem) - destroy(cpu->dmem); - - if (cpu->ils.pkt) - destroy_packet(cpu->ils.pkt); - - if (cpu->dls.pkt) - destroy_packet(cpu->dls.pkt); - + destroy(cpu->dmem); free(cpu); } -struct component *create_simple_riscv64(uint32_t start_pc, +struct component *create_simple_riscv64(uint64_t rcv, uint32_t start_pc, struct component *imem, struct component *dmem) { @@ -610,10 +643,12 @@ struct component *create_simple_riscv64(uint32_t start_pc, if (!new) return NULL; + new->component.receive = (receive_callback)simple_riscv64_receive; new->component.clock = (clock_callback)simple_riscv64_clock; new->component.destroy = (destroy_callback)simple_riscv64_destroy; new->pc = start_pc; + new->rcv = rcv; new->imem = imem; new->dmem = dmem; return (struct component *)new; diff --git a/src/components/grid/node.c b/src/components/grid/node.c index 0aed768..57ef7ed 100644 --- a/src/components/grid/node.c +++ b/src/components/grid/node.c @@ -5,73 +5,101 @@ * Each node should have a router beneath it, just to simplify my life. A router * is basically a bus with a fallback ascension path. */ +#include + #include struct grid_node { struct component component; uint8_t u, v, x, y; struct component *left, *right, *up, *down, *ascend, *lower; + + struct component *send; + struct packet pkt; + bool busy; }; -typedef read_callback callback; +static stat grid_clock(struct grid_node *grid) +{ + if (!grid->busy) + return OK; + + stat r = SEND(grid, grid->send, grid->pkt); + if (r == EBUSY) + return OK; + + grid->busy = false; + return r; +} -static stat grid_route(struct grid_node *grid, struct packet *pkt, callback op) +static stat grid_receive(struct grid_node *grid, struct component *from, struct packet pkt) { - uint64_t addr = packet_addr(pkt); - uint8_t u = (addr >> 56) & 0xff; - uint8_t v = (addr >> 48) & 0xff; - uint8_t x = (addr >> 40) & 0xff; - uint8_t y = (addr >> 32) & 0xff; + if (grid->busy) + return EBUSY; - if (grid->u == u && grid->v == v && grid->x == x && grid->y == y) - return op(grid->lower, pkt); + uint8_t u; + uint8_t v; + uint8_t x; + uint8_t y; + uint64_t addr = pkt.to; + addr_grid(addr, NULL, &x, &y, &u, &v); + + grid->busy = true; + grid->pkt = pkt; + + if (grid->u == u && grid->v == v && grid->x == x && grid->y == y) { + if (!grid->lower) + goto nosuch; + + grid->send = grid->lower; + return OK; + } if (grid->u != u || grid->v != v) { if (!grid->ascend) - return EBUS; + goto nosuch; - return op(grid->ascend, pkt); + grid->send = grid->ascend; + return OK; } if (y < grid->y) { if (!grid->down) - return EBUS; + goto nosuch; - return op(grid->down, pkt); + grid->send = grid->down; + return OK; } if (y > grid->y) { if (!grid->up) - return EBUS; + goto nosuch; - return op(grid->up, pkt); + grid->send = grid->up; + return OK; } if (x < grid->x) { if (!grid->left) - return EBUS; + goto nosuch; - return op(grid->left, pkt); + grid->send = grid->left; + return OK; } if (x > grid->x) { if (!grid->right) return EBUS; - return op(grid->right, pkt); + grid->send = grid->right; + return OK; } - return EBUS; -} - -static stat grid_write(struct grid_node *node, struct packet *pkt) -{ - return grid_route(node, pkt, write); -} - -static stat grid_read(struct grid_node *node, struct packet *pkt) -{ - return grid_route(node, pkt, read); +nosuch: + grid->send = from; + grid->pkt = response(pkt); + set_flags(&grid->pkt, PACKET_ERROR); + return OK; } struct component *create_grid_node(uint8_t u, uint8_t v, uint8_t x, uint8_t y) @@ -80,13 +108,12 @@ struct component *create_grid_node(uint8_t u, uint8_t v, uint8_t x, uint8_t y) if (!node) return NULL; + node->component.receive = (receive_callback)grid_receive; + node->component.clock = (clock_callback)grid_clock; node->u = u; node->v = v; node->x = x; node->y = y; - node->component.write = (write_callback)grid_write; - node->component.read = (read_callback)grid_read; - return (struct component *)node; } diff --git a/src/components/grid/router.c b/src/components/grid/router.c index bdb05c8..63c4011 100644 --- a/src/components/grid/router.c +++ b/src/components/grid/router.c @@ -1,3 +1,6 @@ +#include + +#include #include #include @@ -12,6 +15,10 @@ struct node_router { struct component *ascend; struct vec regions; uint8_t u, v, x, y; + + struct component *send; + struct packet pkt; + bool busy; }; static struct router_region *find_region(struct node_router *router, uint32_t addr) @@ -26,40 +33,55 @@ static struct router_region *find_region(struct node_router *router, uint32_t ad return NULL; } -static stat router_write(struct node_router *router, struct packet *pkt) +static stat router_clock(struct node_router *router) { - uint64_t addr = packet_addr(pkt); - uint8_t u = (addr >> 56) & 0xff; - uint8_t v = (addr >> 48) & 0xff; - uint8_t x = (addr >> 40) & 0xff; - uint8_t y = (addr >> 32) & 0xff; + if (!router->busy) + return OK; - if (router->u != u || router->v != v || router->x != x || router->y != y) - return write(router->ascend, pkt); + stat r = SEND(router, router->send, router->pkt); + if (r == EBUSY) + return OK; - struct router_region *region = find_region(router, addr); - if (!region) - return write(router->ascend, pkt); - - return write(region->component, pkt); + router->busy = false; + return OK; } -static stat router_read(struct node_router *router, struct packet *pkt) +static stat router_receive(struct node_router *router, struct component *from, struct packet pkt) { - uint64_t addr = packet_addr(pkt); - uint8_t u = (addr >> 56) & 0xff; - uint8_t v = (addr >> 48) & 0xff; - uint8_t x = (addr >> 40) & 0xff; - uint8_t y = (addr >> 32) & 0xff; + if (router->busy) + return EBUSY; + + router->busy = true; - if (router->u != u || router->v != v || router->x != x || router->y != y) - return read(router->ascend, pkt); + uint64_t addr = pkt.to; + uint8_t u; + uint8_t v; + uint8_t x; + uint8_t y; + addr_grid(addr, NULL, &x, &y, &u, &v); + + router->pkt = pkt; + + if (router->u != u || router->v != v || router->x != x || router->y != y) { + if (!router->ascend) + goto nosuch; + + router->send = router->ascend; + return OK; + } struct router_region *region = find_region(router, addr); if (!region) - return read(router->ascend, pkt); + goto nosuch; - return read(region->component, pkt); + router->send = region->component; + return OK; + +nosuch: + router->send = from; + router->pkt = response(pkt); + set_flags(&router->pkt, PACKET_ERROR); + return OK; } struct component *create_node_router(uint8_t u, uint8_t v, uint8_t x, uint8_t y) @@ -73,8 +95,8 @@ struct component *create_node_router(uint8_t u, uint8_t v, uint8_t x, uint8_t y) router->x = x; router->y = y; - router->component.write = (write_callback)router_write; - router->component.read = (read_callback)router_read; + router->component.receive = (receive_callback)router_receive; + router->component.clock = (clock_callback)router_clock; router->regions = vec_create(sizeof(struct router_region)); return (struct component *)router; diff --git a/src/components/mem/simple_mem.c b/src/components/mem/simple_mem.c index bb9dd40..e435ea5 100644 --- a/src/components/mem/simple_mem.c +++ b/src/components/mem/simple_mem.c @@ -3,38 +3,57 @@ #include #include +#include #include struct simple_mem { struct component component; + struct component *send; + struct packet pkt; + bool busy; + size_t size; - char buf[]; + uint8_t buf[]; }; -static stat simple_mem_write(struct simple_mem *mem, struct packet *pkt) +static stat simple_mem_clock(struct simple_mem *mem) { - uintptr_t offset = packet_addr(pkt) % mem->size; - if (offset + packet_size(pkt) > mem->size) { - error("write outside memory"); - return ESIZE; - } + if (mem->busy) + return OK; + + stat r = SEND(mem, mem->send, mem->pkt); + if (r == EBUSY) + return OK; - memcpy(mem->buf + offset, packet_data(pkt), packet_size(pkt)); - packet_set_state(pkt, PACKET_DONE); + mem->busy = false; return OK; } -static stat simple_mem_read(struct simple_mem *mem, struct packet *pkt) +static stat simple_mem_receive(struct simple_mem *mem, struct component *from, struct packet pkt) { - uintptr_t offset = packet_addr(pkt) % mem->size; - if (offset + packet_size(pkt) > mem->size) { + if (mem->busy) + return EBUSY; + + mem->send = from; + + uint64_t offset = pkt.to % mem->size; + if (offset >= mem->size) { error("read outside memory"); - return ESIZE; + mem->pkt = response(pkt); + set_flags(&mem->pkt, PACKET_ERROR); + return OK; } - memcpy(packet_data(pkt), mem->buf + offset, packet_size(pkt)); - packet_set_state(pkt, PACKET_DONE); + if (is_set(&pkt, PACKET_READ)) + checked_copyto(&pkt, mem->buf + offset); + else if (is_set(&pkt, PACKET_WRITE)) + checked_copyfrom(&pkt, mem->buf + offset); + else + abort(); + + mem->pkt = response(pkt); + set_flags(&mem->pkt, PACKET_DONE); return OK; } @@ -45,8 +64,8 @@ struct component *create_simple_mem(size_t size) return NULL; new->size = size; - new->component.write = (write_callback)simple_mem_write; - new->component.read = (read_callback)simple_mem_read; + new->component.receive = (receive_callback)simple_mem_receive; + new->component.clock = (clock_callback)simple_mem_clock; return (struct component *)new; } diff --git a/src/components/uart/simple_uart.c b/src/components/uart/simple_uart.c index 5c97ccd..1528a70 100644 --- a/src/components/uart/simple_uart.c +++ b/src/components/uart/simple_uart.c @@ -3,17 +3,42 @@ struct simple_uart { struct component component; + + struct component *send; + struct packet pkt; + bool busy; }; -static stat simple_uart_write(struct simple_uart *uart, struct packet *pkt) +static stat simple_uart_clock(struct simple_uart *uart) { - (void)uart; - size_t size = packet_size(pkt); - if (size != 1) - return EBUS; + if (!uart->busy) + return OK; + + stat r = SEND(uart, uart->send, uart->pkt); + if (r == EBUSY) + return OK; + + uart->busy = false; + return OK; +} + +static stat simple_uart_receive(struct simple_uart *uart, struct component *from, struct packet pkt) +{ + if (uart->busy) + return EBUSY; + + uart->busy = true; + uart->send = from; + uart->pkt = response(pkt); + + size_t size = packet_convsize(&pkt); + if (size != 1) { + set_flags(&uart->pkt, PACKET_ERROR); + return OK; + } - putchar(*(uint8_t *)packet_data(pkt)); - packet_set_state(pkt, PACKET_DONE); + putchar(packet_convu8(&pkt)); + set_flags(&uart->pkt, PACKET_DONE); return OK; } @@ -23,6 +48,7 @@ struct component *create_simple_uart() if (!uart) return NULL; - uart->component.write = (write_callback)simple_uart_write; + uart->component.receive = (receive_callback)simple_uart_receive; + uart->component.clock = (clock_callback)simple_uart_clock; return (struct component *)uart; } diff --git a/src/packet.c b/src/packet.c deleted file mode 100644 index 9442307..0000000 --- a/src/packet.c +++ /dev/null @@ -1,92 +0,0 @@ -/* SPDX-License-Identifier: copyleft-next-0.3.1 */ -/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ - -#include -#include -#include - -struct packet { - enum packet_state state; - enum packet_type type; - - uintptr_t addr; - size_t size; - void *data; -}; - - -struct packet *create_packet(enum packet_type type, uintptr_t addr, size_t size) -{ - struct packet *pkt = malloc(sizeof(struct packet) + size); - if (!pkt) - return NULL; - - pkt->state = PACKET_INIT; - pkt->type = type; - pkt->addr = addr; - pkt->size = size; - pkt->data = pkt + 1; - return pkt; -} - -struct packet *create_packet_with(enum packet_type type, uintptr_t addr, - size_t size, void *data) -{ - struct packet *pkt = malloc(sizeof(struct packet)); - if (!pkt) - return NULL; - - pkt->state = PACKET_INIT; - pkt->type = type; - pkt->addr = addr; - pkt->size = size; - pkt->data = data; - return pkt; -} - -struct packet *reuse_packet(struct packet *pkt, enum packet_type type, - uintptr_t addr, size_t size, void *data) -{ - assert(pkt->state == PACKET_DONE); - pkt->state = PACKET_INIT; - pkt->type = type; - pkt->addr = addr; - pkt->size = size; - pkt->data = data; - return pkt; -} - -enum packet_type packet_type(struct packet *pkt) -{ - return pkt->type; -} - -enum packet_state packet_state(struct packet *pkt) -{ - return pkt->state; -} - -void packet_set_state(struct packet *pkt, enum packet_state state) -{ - pkt->state = state; -} - -size_t packet_size(struct packet *pkt) -{ - return pkt->size; -} - -uintptr_t packet_addr(struct packet *pkt) -{ - return pkt->addr; -} - -void *packet_data(struct packet *pkt) -{ - return pkt->data; -} - -void destroy_packet(struct packet *pkt) -{ - free(pkt); -} diff --git a/src/snoop.c b/src/snoop.c deleted file mode 100644 index 23970a8..0000000 --- a/src/snoop.c +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: copyleft-next-0.3.1 */ -/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ - -#include - -#include - -struct snoop { - enum snoop_state state; - uintptr_t addr; - size_t size; -}; - -struct snoop *create_snoop(uintptr_t addr, size_t size) -{ - struct snoop *snoop = calloc(1, sizeof(struct snoop)); - - snoop->state = SNOOP_UNANSWERED; - snoop->addr = addr; - snoop->size = size; - return snoop; -} - -enum snoop_state snoop_state(struct snoop *snoop) -{ - return snoop->state; -} - -uintptr_t snoop_addr(struct snoop *snoop) -{ - return snoop->addr; -} - -size_t snoop_size(struct snoop *snoop) -{ - return snoop->size; -} - -void destroy_snoop(struct snoop *snoop) -{ - free(snoop); -} diff --git a/tests/simple_grid/sim.c b/tests/simple_grid/sim.c index e0c485b..f11be21 100644 --- a/tests/simple_grid/sim.c +++ b/tests/simple_grid/sim.c @@ -67,7 +67,8 @@ static stat build_grid(struct clock_domain *clk, uint8_t x, uint8_t y) struct component *router = create_node_router(0, 0, i, j); node_router_add(router, dmem, 4096, 4096); - struct component *rv64 = create_simple_riscv64(0, imem, router); + uint64_t rcv = grid_addr(0, 0, i, j, 16384); + struct component *rv64 = create_simple_riscv64(rcv, 0, imem, router); simple_riscv64_set_reg(rv64, 10, i); /* a0 */ simple_riscv64_set_reg(rv64, 11, j); /* a1 */ diff --git a/tests/simple_mem/no_bus.c b/tests/simple_mem/no_bus.c index 8c67e1e..f4622c8 100644 --- a/tests/simple_mem/no_bus.c +++ b/tests/simple_mem/no_bus.c @@ -17,6 +17,7 @@ int main() struct clock_domain *clk = create_clock_domain(NS(1)); clock_domain_add(clk, traffic_gen); + clock_domain_add(clk, simple_mem); struct gran_root *root = create_root(); root_add_clock(root, clk); diff --git a/tests/simple_mem/traffic_gen.c b/tests/simple_mem/traffic_gen.c index 192e2c4..a4d5064 100644 --- a/tests/simple_mem/traffic_gen.c +++ b/tests/simple_mem/traffic_gen.c @@ -12,53 +12,61 @@ struct traffic_gen { struct component component; struct component *stress; - struct packet *pkt; + struct packet pkt; - uintptr_t addr; - uintptr_t end; + uint64_t rcv; + uint64_t addr; + uint64_t end; size_t counter; }; +static stat traffic_gen_receive(struct traffic_gen *tg, struct component *from, struct packet pkt) +{ + (void)from; + tg->pkt = pkt; + return OK; +} + static stat traffic_gen_clock(struct traffic_gen *tg) { if (tg->addr == tg->end) return DONE; uint8_t c = 0; - if (tg->pkt) { - assert(packet_state(tg->pkt) != PACKET_FAILED); - /* wait for packet */ - if (packet_state(tg->pkt) != PACKET_DONE) - return OK; - } - switch (tg->counter) { case 0: c = 13; - tg->pkt = create_packet(PACKET_WRITE, tg->addr, sizeof(c)); - *(uint8_t *)packet_data(tg->pkt) = c; - assert(write(tg->stress, tg->pkt) == OK); + tg->pkt = create_packet(tg->rcv, tg->addr, sizeof(c), &c, PACKET_WRITE); + assert(SEND(tg, tg->stress, tg->pkt) == OK); tg->counter++; break; case 1: - /* destroy write packet now that the write is done */ - destroy_packet(tg->pkt); - tg->pkt = NULL; + /* wait for write to finish */ + if (!is_set(&tg->pkt, PACKET_DONE)) + break; + tg->counter++; break; case 2: - tg->pkt = create_packet(PACKET_READ, tg->addr, sizeof(c)); - assert(read(tg->stress, tg->pkt) == OK); + tg->pkt = create_packet(0, tg->addr, sizeof(c), NULL, PACKET_READ); + assert(SEND(tg, tg->stress, tg->pkt) == OK); tg->counter++; break; case 3: - c = *(uint8_t *)packet_data(tg->pkt); - destroy_packet(tg->pkt); - tg->pkt = NULL; + /* wait for read to finish */ + if (!is_set(&tg->pkt, PACKET_DONE)) + break; + + tg->counter++; + break; + + case 4: + c = packet_convu8(&tg->pkt); + assert(c == 13); tg->counter = 0; tg->addr++; break; @@ -67,17 +75,7 @@ static stat traffic_gen_clock(struct traffic_gen *tg) return OK; } -void traffic_gen_destroy(struct traffic_gen *tg) -{ - if (tg->pkt) - destroy_packet(tg->pkt); - - destroy(tg->stress); - free(tg); -} - -struct component *create_traffic_gen(struct component *stress, uintptr_t start, - size_t size) +struct component *create_traffic_gen(struct component *stress, uintptr_t start, size_t size) { struct traffic_gen *new = calloc(1, sizeof(struct traffic_gen)); if (!new) @@ -88,7 +86,7 @@ struct component *create_traffic_gen(struct component *stress, uintptr_t start, new->end = start + size; new->counter = 0; + new->component.receive = (receive_callback)traffic_gen_receive; new->component.clock = (clock_callback)traffic_gen_clock; - new->component.destroy = (destroy_callback)traffic_gen_destroy; return (struct component *)new; } -- cgit v1.3