diff --git a/examples/zynq7000/README.md b/examples/zynq7000/README.md index a86c096..b7079f9 100644 --- a/examples/zynq7000/README.md +++ b/examples/zynq7000/README.md @@ -9,4 +9,11 @@ ### ZYNQ SoC workflos + target `firmware`: Compile the firmware running on the ARM core(s) with the `make` toolchain + target `firmsim`: Simulate the firmware with QEMU without PS/PL cosimulation with the `qemu` toolchain -+ target `devtree`: Compile the device tree for a PS/PL cosimulation with QEMU \ No newline at end of file ++ target `devtree`: Compile the device tree for a PS/PL cosimulation with QEMU ++ target `cosim_ps`: PS part of the cosimulation. Must be ran first ++ target `cosim_pl`: PL part of the cosimulation. Must be ran in a separate terminal while the PS part is still running + +### Notes: +Compilation with Xilinx provided gcc and binutils done with `xsc` can be problematic... This is the reason +Questasim is used for the cosimulation. If one would use the free Intel variant of Questasim post synthesis +simulation will not be possible. The attempt to get DPI-C working with xsim is not stopped! \ No newline at end of file diff --git a/examples/zynq7000/SIM/c/cosim.c b/examples/zynq7000/SIM/c/cosim.c new file mode 100644 index 0000000..115d52c --- /dev/null +++ b/examples/zynq7000/SIM/c/cosim.c @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "remote-port-proto.h" +#define UNIX_PREFIX "unix:" + +int fd; +uint8_t buf[4096]; +struct rp_pkt_hdr * hdr = (struct rp_pkt_hdr*)buf; +struct rp_pkt * payload = (struct rp_pkt*)(buf); +int rp_pkt_id = 0; +struct rp_peer_state state; +size_t pkt_size; +FILE * log; + +int still_to_write, still_to_read, datpointer; +uint32_t write_addr, read_addr; + +int start_cosim(char * descr){ + // Open socket + fd = socket(AF_UNIX, SOCK_STREAM, 0); + // Try to connect + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, descr + strlen(UNIX_PREFIX), sizeof addr.sun_path); + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) >= 0){ + // log = fopen("cosim_log.txt", "w"); + return 0; + }else{ + close(fd); + return 1; + } +} + +void end_cosim(){ + close(fd); + fclose(log); +} + +int wait_cosim(uint32_t * retaddr, uint32_t * time){ + // fprintf(log, "-->"); + if(still_to_write){ + *retaddr = write_addr; + // fprintf(log, "\r\nwrite [%08x] @ %d\r\n", *retaddr, *time); + return 1; + }else if(still_to_read){ + *retaddr = read_addr; + // fprintf(log, "\r\nread [%08x] @ %d\r\n", *retaddr, *time); + return 2; + }else{ + int n; + int retval = 0; + // Receive header + do{ + n = recv(fd, hdr, sizeof(struct rp_pkt_hdr), 0); + } while(n<=0); + rp_decode_hdr(hdr); + + // fprintf(log, " . "); + + // Receive payload + if(hdr->len){ + do{ + n = recv(fd, hdr+1, hdr->len, 0); + } while(n<=0); + } + rp_decode_payload(payload); + + // fprintf(log, "%d\r\n", hdr->cmd); + switch(hdr->cmd){ + + case RP_CMD_hello:{ + rp_process_caps(&state, buf+payload->hello.caps.offset, payload->hello.caps.len); + + // Send HELO packet + uint32_t caps[] = { + CAP_BUSACCESS_EXT_BASE + }; + size_t s = rp_encode_hello_caps(rp_pkt_id++, 0, buf, 4, 3, caps, caps, sizeof(caps)/sizeof(uint32_t)); + send(fd, buf, s, 0); + send(fd, caps, sizeof(caps), 0); + // fprintf(log, "hello\r\n"); + } break; + + case RP_CMD_interrupt:{ + *time = (uint32_t) payload->interrupt.timestamp; + // fprintf(log, "interrupt @ %ld\r\n", payload->interrupt.timestamp); + } break; + + case RP_CMD_write:{ + int addr = payload->busaccess_ext_base.addr; + int len = payload->busaccess_ext_base.len; + uint64_t t = payload->busaccess_ext_base.timestamp; + + if(len/4>1){ + // Must be more than one write cycle + still_to_write = len/4-1; + write_addr = addr+4; + datpointer = 0; + }else{ + still_to_write = 0; + } + + retval = 1; + *retaddr = addr; + *time = (uint32_t)t; + // fprintf(log, "write [%08x] @ %d\r\n", *retaddr, *time); + + // Respond to write + struct rp_encode_busaccess_in in = {0}; + rp_encode_busaccess_in_rsp_init(&in, payload); + pkt_size = rp_encode_busaccess(&state, buf, &in); + } break; + + case RP_CMD_read:{ + int len = payload->busaccess_ext_base.len; + int addr = payload->busaccess_ext_base.addr; + uint64_t t = payload->busaccess_ext_base.timestamp; + + if(len/4>1){ + // Must be more than one write cycle + still_to_read = len/4-1; + read_addr = addr+4; + datpointer = 0; + }else{ + still_to_read = 0; + } + + retval = 2; + *retaddr = addr; + *time = (uint32_t)t; + // fprintf(log, "read [%08x] @ %d\r\n", *retaddr, *time); + + // Respond to read + struct rp_encode_busaccess_in in = {0}; + rp_encode_busaccess_in_rsp_init(&in, payload); + pkt_size = rp_encode_busaccess(&state, buf, &in); + } break; + + case RP_CMD_sync:{ + // Respond to sync + struct rp_pkt resp = {0}; + size_t s = rp_encode_sync_resp(rp_pkt_id++, 0, &resp, payload->sync.timestamp); + uint64_t t = payload->sync.timestamp; + *time = (uint32_t)t; + send(fd, &resp, s, 0); + // fprintf(log, "SYNC @ %ld\r\n", payload->sync.timestamp); + } break; + } + return retval; + } +} + +void read_cosim(unsigned int value){ + unsigned int * dat = (unsigned int*)((void*)payload + sizeof(payload->busaccess_ext_base)); + dat[datpointer] = value; + // fprintf(log, " -> %08x\r\n", value); +} + +int write_cosim(){ + unsigned int * dat = (unsigned int*)((void*)payload + sizeof(payload->busaccess_ext_base)); + fprintf(log, " -> %08x\r\n", dat[datpointer]); + // return dat[datpointer]; +} + +void finalize_cosim(uint32_t timestamp){ + if(still_to_write){ + still_to_write--; + write_addr += 4; + datpointer++; + }else if(still_to_read){ + still_to_read--; + read_addr += 4; + datpointer++; + }else{ + payload->busaccess_ext_base.timestamp = (uint64_t) timestamp; + send(fd, buf, pkt_size, 0); + } + // fprintf(log, "finalize @ %d\r\n<--\r\n", timestamp); +} \ No newline at end of file diff --git a/examples/zynq7000/SIM/c/remote-port-proto.c b/examples/zynq7000/SIM/c/remote-port-proto.c new file mode 100644 index 0000000..5388855 --- /dev/null +++ b/examples/zynq7000/SIM/c/remote-port-proto.c @@ -0,0 +1,513 @@ +/* + * Remote-port protocol + * + * Copyright (c) 2013 Xilinx Inc + * Written by Edgar E. Iglesias + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _DEFAULT_SOURCE +# define _DEFAULT_SOURCE +#endif +#include +#include +#include +#include +#include +#include "remote-port-proto.h" + +#undef MIN +#define MIN(x, y) (x < y ? x : y) + +#if defined(__linux__) +# include +#elif defined(__FreeBSD__) || defined(__NetBSD__) +# include +#elif defined(__OpenBSD__) +# include +# define be16toh(x) betoh16(x) +# define be32toh(x) betoh32(x) +# define be64toh(x) betoh64(x) +#elif defined(__WIN32) +/* We assume little endian. */ +# define htobe64(x) _byteswap_uint64(x) +# define htobe32(x) _byteswap_ulong(x) +# define htobe16(x) _byteswap_ushort(x) + +# define be64toh(x) _byteswap_uint64(x) +# define be32toh(x) _byteswap_ulong(x) +# define be16toh(x) _byteswap_ushort(x) +#endif + +/* Fallback for ancient Linux systems. */ +#ifndef htobe64 +# include + +# if __BYTE_ORDER == __LITTLE_ENDIAN +# define htobe64(x) bswap_64(x) +# define htobe32(x) bswap_32(x) +# define htobe16(x) bswap_16(x) + +# define be64toh(x) bswap_64(x) +# define be32toh(x) bswap_32(x) +# define be16toh(x) bswap_16(x) +# else +# define htobe64(x) x +# define htobe32(x) x +# define htobe16(x) x + +# define be64toh(x) x +# define be32toh(x) x +# define be16toh(x) x +# endif +#endif + +static const char *rp_cmd_names[RP_CMD_max + 1] = { + [RP_CMD_nop] = "nop", + [RP_CMD_hello] = "hello", + [RP_CMD_cfg] = "cfg", + [RP_CMD_read] = "read", + [RP_CMD_write] = "write", + [RP_CMD_interrupt] = "interrupt", + [RP_CMD_sync] = "sync", + [RP_CMD_ats_req] = "ats_request", + [RP_CMD_ats_inv] = "ats_invalidation", +}; + +const char *rp_cmd_to_string(enum rp_cmd cmd) +{ + assert(cmd <= RP_CMD_max); + return rp_cmd_names[cmd]; +} + +int rp_decode_hdr(struct rp_pkt *pkt) +{ + int used = 0; + + pkt->hdr.cmd = be32toh(pkt->hdr.cmd); + pkt->hdr.len = be32toh(pkt->hdr.len); + pkt->hdr.id = be32toh(pkt->hdr.id); + pkt->hdr.flags = be32toh(pkt->hdr.flags); + pkt->hdr.dev = be32toh(pkt->hdr.dev); + used += sizeof pkt->hdr; + return used; +} + +int rp_decode_payload(struct rp_pkt *pkt) +{ + int used = 0; + /* Master_id has an odd decoding due to historical reasons. */ + uint64_t master_id; + + switch (pkt->hdr.cmd) { + case RP_CMD_hello: + assert(pkt->hdr.len >= sizeof pkt->hello.version); + pkt->hello.version.major = be16toh(pkt->hello.version.major); + pkt->hello.version.minor = be16toh(pkt->hello.version.minor); + used += sizeof pkt->hello.version; + + if ((pkt->hdr.len - used) >= sizeof pkt->hello.caps) { + void *offset; + int i; + + pkt->hello.caps.offset = be32toh(pkt->hello.caps.offset); + pkt->hello.caps.len = be16toh(pkt->hello.caps.len); + + offset = (char *)pkt + pkt->hello.caps.offset; + for (i = 0; i < pkt->hello.caps.len; i++) { + uint32_t cap; + + /* We don't know if offset is 32bit aligned so use + * memcpy to do the endian conversion. */ + memcpy(&cap, offset + i * sizeof cap, sizeof cap); + cap = be32toh(cap); + memcpy(offset + i * sizeof cap, &cap, sizeof cap); + } + used += sizeof pkt->hello.caps; + } else { + pkt->hello.caps.offset = 0; + pkt->hello.caps.len = 0; + } + + /* Consume everything ignoring additional headers we do not yet + * know about. */ + used = pkt->hdr.len; + break; + case RP_CMD_write: + case RP_CMD_read: + assert(pkt->hdr.len >= sizeof pkt->busaccess - sizeof pkt->hdr); + pkt->busaccess.timestamp = be64toh(pkt->busaccess.timestamp); + pkt->busaccess.addr = be64toh(pkt->busaccess.addr); + pkt->busaccess.master_id = be16toh(pkt->busaccess.master_id); + pkt->busaccess.attributes = be64toh(pkt->busaccess.attributes); + pkt->busaccess.len = be32toh(pkt->busaccess.len); + pkt->busaccess.width = be32toh(pkt->busaccess.width); + pkt->busaccess.stream_width = be32toh(pkt->busaccess.stream_width); + master_id = be16toh(pkt->busaccess.master_id); + + used += sizeof pkt->busaccess - sizeof pkt->hdr; + + if (pkt->busaccess.attributes & RP_BUS_ATTR_EXT_BASE) { + struct rp_pkt_busaccess_ext_base *pext = &pkt->busaccess_ext_base; + + assert(pkt->hdr.len >= sizeof *pext - sizeof pkt->hdr); + master_id |= (uint64_t)be16toh(pext->master_id_31_16) << 16; + master_id |= (uint64_t)be32toh(pext->master_id_63_32) << 32; + pext->data_offset = be32toh(pext->data_offset); + pext->next_offset = be32toh(pext->next_offset); + pext->byte_enable_offset = be32toh(pext->byte_enable_offset); + pext->byte_enable_len = be32toh(pext->byte_enable_len); + + used += sizeof *pext - sizeof pkt->busaccess; + } + pkt->busaccess.master_id = master_id; + break; + case RP_CMD_interrupt: + pkt->interrupt.timestamp = be64toh(pkt->interrupt.timestamp); + pkt->interrupt.vector = be64toh(pkt->interrupt.vector); + pkt->interrupt.line = be32toh(pkt->interrupt.line); + pkt->interrupt.val = pkt->interrupt.val; + used += pkt->hdr.len; + break; + case RP_CMD_sync: + pkt->sync.timestamp = be64toh(pkt->interrupt.timestamp); + used += pkt->hdr.len; + break; + case RP_CMD_ats_req: + case RP_CMD_ats_inv: + pkt->ats.attributes = be64toh(pkt->ats.attributes); + pkt->ats.addr = be64toh(pkt->ats.addr); + pkt->ats.len = be64toh(pkt->ats.len); + pkt->ats.result = be32toh(pkt->ats.result); + break; + default: + break; + } + return used; +} + +void rp_encode_hdr(struct rp_pkt_hdr *hdr, uint32_t cmd, uint32_t id, + uint32_t dev, uint32_t len, uint32_t flags) +{ + hdr->cmd = htobe32(cmd); + hdr->len = htobe32(len); + hdr->id = htobe32(id); + hdr->dev = htobe32(dev); + hdr->flags = htobe32(flags); +} + +size_t rp_encode_hello_caps(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt, + uint16_t version_major, uint16_t version_minor, + uint32_t *caps, uint32_t *caps_out, + uint32_t caps_len) +{ + size_t psize = sizeof *pkt + sizeof caps[0] * caps_len; + unsigned int i; + + rp_encode_hdr(&pkt->hdr, RP_CMD_hello, id, dev, + psize - sizeof pkt->hdr, 0); + pkt->version.major = htobe16(version_major); + pkt->version.minor = htobe16(version_minor); + + /* Feature list is appeneded right after the hello packet. */ + pkt->caps.offset = htobe32(sizeof *pkt); + pkt->caps.len = htobe16(caps_len); + + for (i = 0; i < caps_len; i++) { + uint32_t cap; + + cap = caps[i]; + caps_out[i] = htobe32(cap); + } + return sizeof *pkt; +} + +static void rp_encode_busaccess_common(struct rp_pkt_busaccess *pkt, + int64_t clk, uint16_t master_id, + uint64_t addr, uint64_t attr, uint32_t size, + uint32_t width, uint32_t stream_width) +{ + pkt->timestamp = htobe64(clk); + pkt->master_id = htobe16(master_id); + pkt->addr = htobe64(addr); + pkt->attributes = htobe64(attr); + pkt->len = htobe32(size); + pkt->width = htobe32(width); + pkt->stream_width = htobe32(stream_width); +} + +size_t rp_encode_read(uint32_t id, uint32_t dev, + struct rp_pkt_busaccess *pkt, + int64_t clk, uint16_t master_id, + uint64_t addr, uint64_t attr, uint32_t size, + uint32_t width, uint32_t stream_width) +{ + rp_encode_hdr(&pkt->hdr, RP_CMD_read, id, dev, + sizeof *pkt - sizeof pkt->hdr, 0); + rp_encode_busaccess_common(pkt, clk, master_id, addr, attr, + size, width, stream_width); + return sizeof *pkt; +} + +size_t rp_encode_read_resp(uint32_t id, uint32_t dev, + struct rp_pkt_busaccess *pkt, + int64_t clk, uint16_t master_id, + uint64_t addr, uint64_t attr, uint32_t size, + uint32_t width, uint32_t stream_width) +{ + rp_encode_hdr(&pkt->hdr, RP_CMD_read, id, dev, + sizeof *pkt - sizeof pkt->hdr + size, RP_PKT_FLAGS_response); + rp_encode_busaccess_common(pkt, clk, master_id, addr, attr, + size, width, stream_width); + return sizeof *pkt + size; +} + +size_t rp_encode_write(uint32_t id, uint32_t dev, + struct rp_pkt_busaccess *pkt, + int64_t clk, uint16_t master_id, + uint64_t addr, uint64_t attr, uint32_t size, + uint32_t width, uint32_t stream_width) +{ + rp_encode_hdr(&pkt->hdr, RP_CMD_write, id, dev, + sizeof *pkt - sizeof pkt->hdr + size, 0); + rp_encode_busaccess_common(pkt, clk, master_id, addr, attr, + size, width, stream_width); + return sizeof *pkt; +} + +size_t rp_encode_write_resp(uint32_t id, uint32_t dev, + struct rp_pkt_busaccess *pkt, + int64_t clk, uint16_t master_id, + uint64_t addr, uint64_t attr, uint32_t size, + uint32_t width, uint32_t stream_width) +{ + rp_encode_hdr(&pkt->hdr, RP_CMD_write, id, dev, + sizeof *pkt - sizeof pkt->hdr, RP_PKT_FLAGS_response); + rp_encode_busaccess_common(pkt, clk, master_id, addr, attr, + size, width, stream_width); + return sizeof *pkt; +} + +/* New API for extended header. */ +size_t rp_encode_busaccess(struct rp_peer_state *peer, + struct rp_pkt_busaccess_ext_base *pkt, + struct rp_encode_busaccess_in *in) { + struct rp_pkt_busaccess *pkt_v4_0 = (void *) pkt; + uint32_t hsize = 0; + uint32_t ret_size = 0; + + /* Allocate packet space. */ + if (in->cmd == RP_CMD_write && !(in->flags & RP_PKT_FLAGS_response)) { + hsize = in->size; + } + if (in->cmd == RP_CMD_read && (in->flags & RP_PKT_FLAGS_response)) { + hsize = in->size; + ret_size = in->size; + } + + /* If peer does not support the busaccess base extensions, use the + * old layout. For responses, what matters is if we're responding + * to a packet with the extensions. + */ + if (!peer->caps.busaccess_ext_base && !(in->attr & RP_BUS_ATTR_EXT_BASE)) { + /* Old layout. */ + assert(in->master_id < UINT16_MAX); + + rp_encode_hdr(&pkt->hdr, in->cmd, in->id, in->dev, + sizeof *pkt_v4_0 - sizeof pkt->hdr + hsize, in->flags); + rp_encode_busaccess_common(pkt_v4_0, in->clk, in->master_id, + in->addr, in->attr, + in->size, in->width, in->stream_width); + return sizeof *pkt_v4_0 + ret_size; + } + + /* Encode the extended fields. */ + pkt->master_id_31_16 = htobe16(in->master_id >> 16); + pkt->master_id_63_32 = htobe32(in->master_id >> 32); + + /* We always put data right after the header. */ + pkt->data_offset = htobe32(sizeof *pkt); + pkt->next_offset = 0; + + pkt->byte_enable_offset = htobe32(sizeof *pkt + hsize); + pkt->byte_enable_len = htobe32(in->byte_enable_len); + hsize += in->byte_enable_len; + + rp_encode_hdr(&pkt->hdr, in->cmd, in->id, in->dev, + sizeof *pkt - sizeof pkt->hdr + hsize, in->flags); + rp_encode_busaccess_common(pkt_v4_0, in->clk, in->master_id, in->addr, + in->attr | RP_BUS_ATTR_EXT_BASE, + in->size, in->width, in->stream_width); + + return sizeof *pkt + ret_size; +} + +size_t rp_encode_interrupt_f(uint32_t id, uint32_t dev, + struct rp_pkt_interrupt *pkt, + int64_t clk, + uint32_t line, uint64_t vector, uint8_t val, + uint32_t flags) +{ + rp_encode_hdr(&pkt->hdr, RP_CMD_interrupt, id, dev, + sizeof *pkt - sizeof pkt->hdr, flags); + pkt->timestamp = htobe64(clk); + pkt->vector = htobe64(vector); + pkt->line = htobe32(line); + pkt->val = val; + return sizeof *pkt; +} + +size_t rp_encode_interrupt(uint32_t id, uint32_t dev, + struct rp_pkt_interrupt *pkt, + int64_t clk, + uint32_t line, uint64_t vector, uint8_t val) +{ + return rp_encode_interrupt_f(id, dev, pkt, clk, line, vector, val, 0); +} + +static size_t rp_encode_ats_common(uint32_t cmd, uint32_t id, uint32_t dev, + struct rp_pkt_ats *pkt, + int64_t clk, uint64_t attr, uint64_t addr, + uint64_t len, uint64_t result, uint32_t flags) +{ + rp_encode_hdr(&pkt->hdr, cmd, id, dev, + sizeof *pkt - sizeof pkt->hdr, flags); + pkt->timestamp = htobe64(clk); + pkt->attributes = htobe64(attr); + pkt->addr = htobe64(addr); + pkt->len = htobe64(len); + pkt->result = htobe32(result); + return sizeof *pkt; +} + +size_t rp_encode_ats_req(uint32_t id, uint32_t dev, + struct rp_pkt_ats *pkt, + int64_t clk, uint64_t attr, uint64_t addr, + uint64_t len, uint64_t result, uint32_t flags) +{ + return rp_encode_ats_common(RP_CMD_ats_req, id, dev, + pkt, clk, attr, + addr, len, result, flags); +} + +size_t rp_encode_ats_inv(uint32_t id, uint32_t dev, + struct rp_pkt_ats *pkt, + int64_t clk, uint64_t attr, uint64_t addr, + uint64_t len, uint64_t result, uint32_t flags) +{ + return rp_encode_ats_common(RP_CMD_ats_inv, id, dev, + pkt, clk, attr, + addr, len, result, flags); +} + +static size_t rp_encode_sync_common(uint32_t id, uint32_t dev, + struct rp_pkt_sync *pkt, + int64_t clk, uint32_t flags) +{ + rp_encode_hdr(&pkt->hdr, RP_CMD_sync, id, dev, + sizeof *pkt - sizeof pkt->hdr, flags); + pkt->timestamp = htobe64(clk); + return sizeof *pkt; +} + +size_t rp_encode_sync(uint32_t id, uint32_t dev, + struct rp_pkt_sync *pkt, + int64_t clk) +{ + return rp_encode_sync_common(id, dev, pkt, clk, 0); +} + +size_t rp_encode_sync_resp(uint32_t id, uint32_t dev, + struct rp_pkt_sync *pkt, + int64_t clk) +{ + return rp_encode_sync_common(id, dev, pkt, clk, RP_PKT_FLAGS_response); +} + +void rp_process_caps(struct rp_peer_state *peer, + void *caps, size_t caps_len) +{ + int i; + + assert(peer->caps.busaccess_ext_base == false); + + for (i = 0; i < caps_len; i++) { + uint32_t cap; + + memcpy(&cap, caps + i * sizeof cap, sizeof cap); + + switch (cap) { + case CAP_BUSACCESS_EXT_BASE: + peer->caps.busaccess_ext_base = true; + break; + case CAP_BUSACCESS_EXT_BYTE_EN: + peer->caps.busaccess_ext_byte_en = true; + break; + case CAP_WIRE_POSTED_UPDATES: + peer->caps.wire_posted_updates = true; + break; + case CAP_ATS: + peer->caps.ats = true; + break; + } + } +} + + +void rp_dpkt_alloc(RemotePortDynPkt *dpkt, size_t size) +{ + if (dpkt->size < size) { + char *u8; + dpkt->pkt = realloc(dpkt->pkt, size); + u8 = (void *) dpkt->pkt; + memset(u8 + dpkt->size, 0, size - dpkt->size); + dpkt->size = size; + } +} + +void rp_dpkt_swap(RemotePortDynPkt *a, RemotePortDynPkt *b) +{ + struct rp_pkt *tmp_pkt; + size_t tmp_size; + + tmp_pkt = a->pkt; + tmp_size = a->size; + a->pkt = b->pkt; + a->size = b->size; + b->pkt = tmp_pkt; + b->size = tmp_size; +} + +bool rp_dpkt_is_valid(RemotePortDynPkt *dpkt) +{ + return dpkt->size > 0 && dpkt->pkt->hdr.len; +} + +void rp_dpkt_invalidate(RemotePortDynPkt *dpkt) +{ + assert(rp_dpkt_is_valid(dpkt)); + dpkt->pkt->hdr.len = 0; +} + +inline void rp_dpkt_free(RemotePortDynPkt *dpkt) +{ + dpkt->size = 0; + free(dpkt->pkt); +} diff --git a/examples/zynq7000/SIM/c/remote-port-proto.h b/examples/zynq7000/SIM/c/remote-port-proto.h new file mode 100644 index 0000000..9fe4192 --- /dev/null +++ b/examples/zynq7000/SIM/c/remote-port-proto.h @@ -0,0 +1,534 @@ +/* + * QEMU remote port protocol parts. + * + * Copyright (c) 2013 Xilinx Inc + * Written by Edgar E. Iglesias + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef REMOTE_PORT_PROTO_H__ +#define REMOTE_PORT_PROTO_H__ + +#include +#include +#include +#include + +/* + * Remote-Port (RP) is an inter-simulator protocol. It assumes a reliable + * point to point communcation with the remote simulation environment. + * + * Setup + * In the SETUP phase a mandatory HELLO packet is exchanged with optional + * CFG packets following. HELLO packets are useful to ensure that both + * sides are speaking the same protocol and using compatible versions. + * + * CFG packets are used to negotiate configuration options. At the moment + * these remain unimplemented. + * + * Once the session is up, communication can start through various other + * commands. The list can be found further down this document. + * Commands are carried over RP packets. Every RP packet contains a header + * with length, flags and an ID to track potential responses. + * The header is followed by a packet specific payload. You'll find the + * details of the various commands packet layouts here. Some commands can + * carry data/blobs in their payload. + */ + + +#define RP_VERSION_MAJOR 4 +#define RP_VERSION_MINOR 3 + +#if defined(_WIN32) && defined(__MINGW32__) +/* mingw GCC has a bug with packed attributes. */ +#define PACKED __attribute__ ((gcc_struct, packed)) +#else +#define PACKED __attribute__ ((packed)) +#endif + +/* Could be auto generated. */ +enum rp_cmd { + RP_CMD_nop = 0, + RP_CMD_hello = 1, + RP_CMD_cfg = 2, + RP_CMD_read = 3, + RP_CMD_write = 4, + RP_CMD_interrupt = 5, + RP_CMD_sync = 6, + RP_CMD_ats_req = 7, + RP_CMD_ats_inv = 8, + RP_CMD_max = 8 +}; + +enum { + RP_OPT_quantum = 0, +}; + +struct rp_cfg_state { + uint64_t quantum; +}; + +enum { + RP_PKT_FLAGS_optional = 1 << 0, + RP_PKT_FLAGS_response = 1 << 1, + + /* Posted hint. + * When set this means that the receiver is not required to respond to + * the message. Since it's just a hint, the sender must be prepared to + * drop responses. Note that since flags are echoed back in responses + * a response to a posted packet will be easy to identify early in the + * protocol stack. + */ + RP_PKT_FLAGS_posted = 1 << 2, +}; + +struct rp_pkt_hdr { + uint32_t cmd; + uint32_t len; + uint32_t id; + uint32_t flags; + uint32_t dev; +} PACKED; + +struct rp_pkt_cfg { + struct rp_pkt_hdr hdr; + uint32_t opt; + uint8_t set; +} PACKED; + +struct rp_version { + uint16_t major; + uint16_t minor; +} PACKED; + +struct rp_capabilities { + /* Offset from start of packet. */ + uint32_t offset; + uint16_t len; + uint16_t reserved0; +} PACKED; + +enum { + CAP_BUSACCESS_EXT_BASE = 1, /* New header layout. */ + CAP_BUSACCESS_EXT_BYTE_EN = 2, /* Support for Byte Enables. */ + + /* + * Originally, all interrupt/wire updates over remote-port were posted. + * This turned out to be a bad idea. To fix it without breaking backwards + * compatibility, we add the WIRE Posted updates capability. + * + * If the peer supportes this, it will respect the RP_PKT_FLAGS_posted + * flag. If the peer doesn't support this capability, senders need to + * be aware that the peer will not respond to wire updates regardless + * of the posted header-flag. + */ + CAP_WIRE_POSTED_UPDATES = 3, + + CAP_ATS = 4, /* Address translation services */ +}; + +struct rp_pkt_hello { + struct rp_pkt_hdr hdr; + struct rp_version version; + struct rp_capabilities caps; +} PACKED; + +enum { + /* Remote port responses. */ + RP_RESP_OK = 0x0, + RP_RESP_BUS_GENERIC_ERROR = 0x1, + RP_RESP_ADDR_ERROR = 0x2, + RP_RESP_MAX = 0xF, +}; + +enum { + RP_BUS_ATTR_EOP = (1 << 0), + RP_BUS_ATTR_SECURE = (1 << 1), + RP_BUS_ATTR_EXT_BASE = (1 << 2), + RP_BUS_ATTR_PHYS_ADDR = (1 << 3), + + /* + * Bits [11:8] are allocated for storing transaction response codes. + * These new response codes are backward compatible as existing + * implementations will not set/read these bits. + * For existing implementations, these bits will be zero which is RESP_OKAY. + */ + RP_BUS_RESP_SHIFT = 8, + RP_BUS_RESP_MASK = (RP_RESP_MAX << RP_BUS_RESP_SHIFT), +}; + +struct rp_pkt_busaccess { + struct rp_pkt_hdr hdr; + uint64_t timestamp; + uint64_t attributes; + uint64_t addr; + + /* Length in bytes. */ + uint32_t len; + + /* Width of each beat in bytes. Set to zero for unknown (let the remote + side choose). */ + uint32_t width; + + /* Width of streaming, must be a multiple of width. + addr should repeat itself around this width. Set to same as len + for incremental (normal) accesses. In bytes. */ + uint32_t stream_width; + + /* Implementation specific source or master-id. */ + uint16_t master_id; +} PACKED; + + +/* This is the new extended busaccess packet layout. */ +struct rp_pkt_busaccess_ext_base { + struct rp_pkt_hdr hdr; + uint64_t timestamp; + uint64_t attributes; + uint64_t addr; + + /* Length in bytes. */ + uint32_t len; + + /* Width of each beat in bytes. Set to zero for unknown (let the remote + side choose). */ + uint32_t width; + + /* Width of streaming, must be a multiple of width. + addr should repeat itself around this width. Set to same as len + for incremental (normal) accesses. In bytes. */ + uint32_t stream_width; + + /* Implementation specific source or master-id. */ + uint16_t master_id; + /* ---- End of 4.0 base busaccess. ---- */ + + uint16_t master_id_31_16; /* MasterID bits [31:16]. */ + uint32_t master_id_63_32; /* MasterID bits [63:32]. */ + /* --------------------------------------------------- + * Since hdr is 5 x 32bit, we are now 64bit aligned. */ + + uint32_t data_offset; /* Offset to data from start of pkt. */ + uint32_t next_offset; /* Offset to next extension. 0 if none. */ + + uint32_t byte_enable_offset; + uint32_t byte_enable_len; + + /* ---- End of CAP_BUSACCESS_EXT_BASE. ---- */ + + /* If new features are needed that may always occupy space + * in the header, then add a new capability and extend the + * this area with new fields. + * Will help receivers find data_offset and next offset, + * even those that don't know about extended fields. + */ +} PACKED; + +struct rp_pkt_interrupt { + struct rp_pkt_hdr hdr; + uint64_t timestamp; + uint64_t vector; + uint32_t line; + uint8_t val; +} PACKED; + +struct rp_pkt_sync { + struct rp_pkt_hdr hdr; + uint64_t timestamp; +} PACKED; + +enum { + RP_ATS_ATTR_exec = 1 << 0, + RP_ATS_ATTR_read = 1 << 1, + RP_ATS_ATTR_write = 1 << 2, +}; + +enum { + RP_ATS_RESULT_ok = 0, + RP_ATS_RESULT_error = 1, +}; + +struct rp_pkt_ats { + struct rp_pkt_hdr hdr; + uint64_t timestamp; + uint64_t attributes; + uint64_t addr; + uint64_t len; + uint32_t result; + uint64_t reserved0; + uint64_t reserved1; + uint64_t reserved2; + uint64_t reserved3; +} PACKED; + +struct rp_pkt { + union { + struct rp_pkt_hdr hdr; + struct rp_pkt_hello hello; + struct rp_pkt_busaccess busaccess; + struct rp_pkt_busaccess_ext_base busaccess_ext_base; + struct rp_pkt_interrupt interrupt; + struct rp_pkt_sync sync; + struct rp_pkt_ats ats; + }; +}; + +struct rp_peer_state { + void *opaque; + + struct rp_pkt pkt; + bool hdr_used; + + struct rp_version version; + + struct { + bool busaccess_ext_base; + bool busaccess_ext_byte_en; + bool wire_posted_updates; + bool ats; + } caps; + + /* Used to normalize our clk. */ + int64_t clk_base; + + struct rp_cfg_state local_cfg; + struct rp_cfg_state peer_cfg; +}; + +const char *rp_cmd_to_string(enum rp_cmd cmd); +int rp_decode_hdr(struct rp_pkt *pkt); +int rp_decode_payload(struct rp_pkt *pkt); + +void rp_encode_hdr(struct rp_pkt_hdr *hdr, + uint32_t cmd, uint32_t id, uint32_t dev, uint32_t len, + uint32_t flags); + +/* + * caps is a an array of supported capabilities by the implementor. + * caps_out is the encoded (network byte order) version of the + * same array. It should be sent to the peer after the hello packet. + */ +size_t rp_encode_hello_caps(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt, + uint16_t version_major, uint16_t version_minor, + uint32_t *caps, uint32_t *features_out, + uint32_t features_len); + +/* rp_encode_hello is deprecated in favor of hello_caps. */ +static inline size_t __attribute__ ((deprecated)) +rp_encode_hello(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt, + uint16_t version_major, uint16_t version_minor) { + return rp_encode_hello_caps(id, dev, pkt, version_major, version_minor, + NULL, NULL, 0); +} + +static inline void *__attribute__ ((deprecated)) +rp_busaccess_dataptr(struct rp_pkt_busaccess *pkt) +{ + /* Right after the packet. */ + return pkt + 1; +} + +/* + * rp_busaccess_rx_dataptr + * + * Predicts the dataptr for a packet to be transmitted. + * This should only be used if you are trying to keep + * the entire packet in a linear buffer. + */ +static inline unsigned char * +rp_busaccess_tx_dataptr(struct rp_peer_state *peer, + struct rp_pkt_busaccess_ext_base *pkt) +{ + unsigned char *p = (unsigned char *) pkt; + + if (peer->caps.busaccess_ext_base) { + /* We always put our data right after the header. */ + return p + sizeof *pkt; + } else { + /* Right after the old packet layout. */ + return p + sizeof(struct rp_pkt_busaccess); + } +} + +/* + * rp_busaccess_rx_dataptr + * + * Extracts the dataptr from a received packet. + */ +static inline unsigned char * +rp_busaccess_rx_dataptr(struct rp_peer_state *peer, + struct rp_pkt_busaccess_ext_base *pkt) +{ + unsigned char *p = (unsigned char *) pkt; + + if (pkt->attributes & RP_BUS_ATTR_EXT_BASE) { + return p + pkt->data_offset; + } else { + /* Right after the old packet layout. */ + return p + sizeof(struct rp_pkt_busaccess); + } +} + +static inline unsigned char * +rp_busaccess_byte_en_ptr(struct rp_peer_state *peer, + struct rp_pkt_busaccess_ext_base *pkt) +{ + unsigned char *p = (unsigned char *) pkt; + + if ((pkt->attributes & RP_BUS_ATTR_EXT_BASE) + && pkt->byte_enable_len) { + assert(pkt->byte_enable_offset >= sizeof *pkt); + assert(pkt->byte_enable_offset + pkt->byte_enable_len + <= pkt->hdr.len + sizeof pkt->hdr); + return p + pkt->byte_enable_offset; + } + return NULL; +} + +size_t __attribute__ ((deprecated)) +rp_encode_read(uint32_t id, uint32_t dev, + struct rp_pkt_busaccess *pkt, + int64_t clk, uint16_t master_id, + uint64_t addr, uint64_t attr, uint32_t size, + uint32_t width, uint32_t stream_width); + +size_t __attribute__ ((deprecated)) +rp_encode_read_resp(uint32_t id, uint32_t dev, + struct rp_pkt_busaccess *pkt, + int64_t clk, uint16_t master_id, + uint64_t addr, uint64_t attr, uint32_t size, + uint32_t width, uint32_t stream_width); + +size_t __attribute__ ((deprecated)) +rp_encode_write(uint32_t id, uint32_t dev, + struct rp_pkt_busaccess *pkt, + int64_t clk, uint16_t master_id, + uint64_t addr, uint64_t attr, uint32_t size, + uint32_t width, uint32_t stream_width); + +size_t __attribute__ ((deprecated)) +rp_encode_write_resp(uint32_t id, uint32_t dev, + struct rp_pkt_busaccess *pkt, + int64_t clk, uint16_t master_id, + uint64_t addr, uint64_t attr, uint32_t size, + uint32_t width, uint32_t stream_width); + +struct rp_encode_busaccess_in { + uint32_t cmd; + uint32_t id; + uint32_t flags; + uint32_t dev; + int64_t clk; + uint64_t master_id; + uint64_t addr; + uint64_t attr; + uint32_t size; + uint32_t width; + uint32_t stream_width; + uint32_t byte_enable_len; +}; + +/* Prepare encode_busaccess input parameters for a packet response. */ +static inline void +rp_encode_busaccess_in_rsp_init(struct rp_encode_busaccess_in *in, + struct rp_pkt *pkt) { + memset(in, 0, sizeof *in); + in->cmd = pkt->hdr.cmd; + in->id = pkt->hdr.id; + in->flags = pkt->hdr.flags | RP_PKT_FLAGS_response; + in->dev = pkt->hdr.dev; + /* FIXME: Propagate all master_id fields? */ + in->master_id = pkt->busaccess.master_id; + in->addr = pkt->busaccess.addr; + in->size = pkt->busaccess.len; + in->width = pkt->busaccess.width; + in->stream_width = pkt->busaccess.stream_width; + in->byte_enable_len = 0; +} +size_t rp_encode_busaccess(struct rp_peer_state *peer, + struct rp_pkt_busaccess_ext_base *pkt, + struct rp_encode_busaccess_in *in); + +size_t rp_encode_interrupt_f(uint32_t id, uint32_t dev, + struct rp_pkt_interrupt *pkt, + int64_t clk, + uint32_t line, uint64_t vector, uint8_t val, + uint32_t flags); + +size_t rp_encode_interrupt(uint32_t id, uint32_t dev, + struct rp_pkt_interrupt *pkt, + int64_t clk, + uint32_t line, uint64_t vector, uint8_t val); + +size_t rp_encode_sync(uint32_t id, uint32_t dev, + struct rp_pkt_sync *pkt, + int64_t clk); + +size_t rp_encode_sync_resp(uint32_t id, uint32_t dev, + struct rp_pkt_sync *pkt, + int64_t clk); + +size_t rp_encode_ats_req(uint32_t id, uint32_t dev, + struct rp_pkt_ats *pkt, + int64_t clk, uint64_t attr, uint64_t addr, + uint64_t size, uint64_t result, uint32_t flags); + +size_t rp_encode_ats_inv(uint32_t id, uint32_t dev, + struct rp_pkt_ats *pkt, + int64_t clk, uint64_t attr, uint64_t addr, + uint64_t size, uint64_t result, uint32_t flags); + +void rp_process_caps(struct rp_peer_state *peer, + void *caps, size_t caps_len); + +/* Dynamically resizable remote port pkt. */ + +typedef struct RemotePortDynPkt { + struct rp_pkt *pkt; + size_t size; +} RemotePortDynPkt; + +/* + * Make sure dpkt is allocated and has enough room. + */ + +void rp_dpkt_alloc(RemotePortDynPkt *dpkt, size_t size); + +void rp_dpkt_swap(RemotePortDynPkt *a, RemotePortDynPkt *b); + +/* + * Check if the dpkt is valid. Used for debugging purposes. + */ + +bool rp_dpkt_is_valid(RemotePortDynPkt *dpkt); + +/* + * Invalidate the dpkt. Used for debugging purposes. + */ + +void rp_dpkt_invalidate(RemotePortDynPkt *dpkt); + +void rp_dpkt_free(RemotePortDynPkt *dpkt); + +static inline int rp_get_busaccess_response(struct rp_pkt *pkt) +{ + return (pkt->busaccess_ext_base.attributes & RP_BUS_RESP_MASK) >> + RP_BUS_RESP_SHIFT; +} +#endif diff --git a/examples/zynq7000/SIM/tb_cosim.sv b/examples/zynq7000/SIM/tb_cosim.sv new file mode 100644 index 0000000..5dad042 --- /dev/null +++ b/examples/zynq7000/SIM/tb_cosim.sv @@ -0,0 +1,115 @@ +`timescale 1ns / 1ps + +module tb_cosim; + import "DPI-C" function int start_cosim(string path); + import "DPI-C" function void end_cosim(); + import "DPI-C" function int wait_cosim(output int addr, output int t); + import "DPI-C" function void finalize_cosim(int t); + import "DPI-C" function void read_cosim(input int value); + import "DPI-C" function int write_cosim(); + + // Cosimulation variables + integer cosim_ret; + reg unsigned [31:0] cosim_address; + reg unsigned [31:0] cosim_data; + integer timestamp; + integer curtime; + integer started; + + reg ACLK; + reg ARESETN; + + wire [1:0] LED; + + initial begin + ACLK = 1'b0; + ARESETN = 0'b0; + end + + always #5 ACLK = !ACLK; + + initial begin + $display("Starting testbench"); + // Start co-simulation + if (start_cosim("unix:/tmp/qemu-rport-_cosim@0")>0) begin + $display("ERROR: Could not start co-simulation. Stop simulation"); + $stop; + end + $display("Co-simulation started"); + + ARESETN = 1'b0; + repeat(2)@(posedge ACLK); + ARESETN = 1'b1; + repeat(2)@(posedge ACLK); + + // Main loop + cosim_ret = 0; + while (cosim_ret>=0) begin + cosim_ret = wait_cosim(cosim_address, timestamp); + + // Check for end of simulation + if (cosim_address=='h7ffffffc) begin + break; + end + + // Check for start of simulation + if (cosim_address=='h7ffffff8) begin + curtime = timestamp; + cosim_ret = 0; + started = 1; + end + + // Check for pause of simulation + if (cosim_address=='h7ffffff4) begin + started = 0; + finalize_cosim(curtime); + end + + if (started==0) begin + continue; + end + + while(curtimeerror.log | tee run.log", shell=True, cwd=build_dir, - stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + stdin=subprocess.DEVNULL) subprocesses.append(p) while p.poll() is None: time.sleep(1) @@ -36,5 +36,6 @@ def do(config, target, log, subprocesses, prefix='.'): log(" - copy logs") shutil.copy(f'{build_dir}/run.log', f'{out_dir}/run.log') + shutil.copy(f'{build_dir}/error.log', f'{out_dir}/error.log') return 0 \ No newline at end of file diff --git a/remotesyn/toolchains/questa.py b/remotesyn/toolchains/questa.py new file mode 100644 index 0000000..2019f55 --- /dev/null +++ b/remotesyn/toolchains/questa.py @@ -0,0 +1,71 @@ +from importlib.metadata import files +import shutil +import os +import time +import subprocess + +def do(config, target, log, subprocesses, prefix='.'): + # shutil.rmtree(config.get('project', 'build_dir', fallback='build'), True) + + log("Starting simulation") + + log(" - parsing options") + toplevel = config.get(f'target.{target}', 'toplevel', fallback='toplevel') + runtime = config.get(f'target.{target}', 'runtime', fallback='100 ns') + files_vhdl = config.get(f'target.{target}', 'files_vhdl', fallback='').split() + files_verilog = config.get(f'target.{target}', 'files_verilog', fallback='').split() + files_sysverilog = config.get(f'target.{target}', 'files_sysverilog', fallback='').split() + files_c = config.get(f'target.{target}', 'files_c', fallback='').split() + files_other = config.get(f'target.{target}', 'files_other', fallback='').split() + build_dir = config.get(f'project', 'build_dir', fallback='build') + out_dir = config.get(f'project', 'out_dir', fallback='out') + + prefix = f'{os.getcwd()}/{prefix}' + build_dir = f'{prefix}/{build_dir}' + out_dir = f'{prefix}/{out_dir}/{target}' + + log(" - creating output directories") + os.makedirs(build_dir, exist_ok=True) + os.makedirs(out_dir, exist_ok=True) + + files_c_wp = {f'{prefix}/{f}' for f in files_c} + + log(" - writing compile file") + with open(f'{build_dir}/do.sh', 'w') as f: + f.write(f'vlib work\nvmap work work\n') + for s in files_vhdl: + f.write(f"vcom {prefix}/{s}\n") + for s in files_verilog: + f.write(f"vlog {prefix}/{s}\n") + for s in files_sysverilog: + f.write(f"vlog -sv {prefix}/{s}\n") + f.write(f"gcc -g -fPIC -shared -Bsymbolic -o import.so {' '.join(files_c_wp)}\n") + extra = '' + if len(files_c_wp)>0: + extra = '-sv_lib import' + f.write(f"vsim -c -do do.do {extra} {toplevel}") + + log(" - writing do file") + with open(f'{build_dir}/do.do', 'w') as f: + f.write("vcd file out.vcd\n vcd add *\nrun -all\nquit\n"); + + log(" - run vsim") + p = subprocess.Popen(f"bash ./do.sh 2>&1 | tee do.log", + shell=True, cwd=build_dir, + stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocesses.append(p) + while p.poll() is None: + time.sleep(1) + res = p.returncode + + log(" - copy logs") + shutil.copy(f'{build_dir}/do.log', f'{out_dir}/do.log') + + if res!=0: + log("ERROR: vsim returned with", res) + return res + + log(" - copy output files") + shutil.copy(f'{build_dir}/out.vcd', f'{out_dir}/out.vcd') + + return 0 \ No newline at end of file diff --git a/remotesyn/toolchains/xsim.py b/remotesyn/toolchains/xsim.py index e1a757c..c2126ff 100644 --- a/remotesyn/toolchains/xsim.py +++ b/remotesyn/toolchains/xsim.py @@ -16,6 +16,7 @@ def do(config, target, log, subprocesses, prefix='.'): files_verilog = config.get(f'target.{target}', 'files_verilog', fallback='').split() files_sysverilog = config.get(f'target.{target}', 'files_sysverilog', fallback='').split() files_xci = config.get(f'target.{target}', 'files_xci', fallback='').split() + files_c = config.get(f'target.{target}', 'files_c', fallback='').split() files_other = config.get(f'target.{target}', 'files_other', fallback='').split() build_dir = config.get(f'project', 'build_dir', fallback='build') out_dir = config.get(f'project', 'out_dir', fallback='out') @@ -48,7 +49,9 @@ def do(config, target, log, subprocesses, prefix='.'): for s in files_other: f.write(f"add_files -norecurse -scan_for_includes \"{prefix}/{s}\"\n") f.write(f"import_files -norecurse \"{prefix}/{s}\"\n") - # TODO C files for VPI + for s in files_c: + f.write(f"add_files -norecurse -scan_for_includes \"{prefix}/{s}\"\n") + f.write(f"import_files -norecurse \"{prefix}/{s}\"\n") f.write(f"set_property top {toplevel} [get_filesets sim_1]\n") f.write("set_property top_lib xil_defaultlib [get_filesets sim_1]\n") @@ -106,7 +109,7 @@ def do(config, target, log, subprocesses, prefix='.'): log(" - compile") - p = subprocess.Popen(f'bash compile.sh', + p = subprocess.Popen(f'bash compile.sh 2>&1 | tee comp.log', shell=True, cwd=f'{build_dir}/sim/sim.sim/sim_1/behav/xsim', stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) subprocesses.append(p) @@ -115,7 +118,7 @@ def do(config, target, log, subprocesses, prefix='.'): res = p.returncode log(" - copy logs") - shutil.copy(f'{build_dir}/sim/sim.sim/sim_1/behav/xsim/compile.log', f'{out_dir}/compile.log') + shutil.copy(f'{build_dir}/sim/sim.sim/sim_1/behav/xsim/comp.log', f'{out_dir}/compile.log') if res!=0: log("ERROR: compile returned with", res) @@ -123,7 +126,7 @@ def do(config, target, log, subprocesses, prefix='.'): log(" - elaborate") - p = subprocess.Popen(f'bash elaborate.sh', + p = subprocess.Popen(f'bash elaborate.sh 2>&1 | tee elab.log', shell=True, cwd=f'{build_dir}/sim/sim.sim/sim_1/behav/xsim', stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) subprocesses.append(p) @@ -132,7 +135,7 @@ def do(config, target, log, subprocesses, prefix='.'): res = p.returncode log(" - copy logs") - shutil.copy(f'{build_dir}/sim/sim.sim/sim_1/behav/xsim/elaborate.log', f'{out_dir}/elaborate.log') + shutil.copy(f'{build_dir}/sim/sim.sim/sim_1/behav/xsim/elab.log', f'{out_dir}/elaborate.log') if res!=0: log("ERROR: elaborate returned with", res) @@ -144,7 +147,7 @@ def do(config, target, log, subprocesses, prefix='.'): log(" - simulate") - p = subprocess.Popen(f'bash simulate.sh', + p = subprocess.Popen(f'bash simulate.sh 2>&1 | tee simulate.log', shell=True, cwd=f'{build_dir}/sim/sim.sim/sim_1/behav/xsim', stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) subprocesses.append(p) diff --git a/scripts/rmbuild b/scripts/rmbuild index 93bd36d..7401f78 100644 --- a/scripts/rmbuild +++ b/scripts/rmbuild @@ -161,14 +161,14 @@ if __name__=="__main__": signal.signal(signal.SIGALRM, sighandler) try: - # Send project identification - cmd(b'id' + struct.pack('>q', hash(host_key.get_base64())), channel) - # Send config - cmd(b'cf' + sstr(json.dumps({s:dict(config.items(s)) for s in config.sections()})), channel) - for target in targets: if stopped: break + + # Send project identification + cmd(b'id' + struct.pack('>q', hash(host_key.get_base64())), channel) + # Send config + cmd(b'cf' + sstr(json.dumps({s:dict(config.items(s)) for s in config.sections()})), channel) print("Target", target)