First version of zynq cosimulation added
Signed-off-by: Joppe Blondel <joppe@blondel.nl>
This commit is contained in:
@ -10,3 +10,10 @@
|
||||
+ 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
|
||||
+ 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!
|
186
examples/zynq7000/SIM/c/cosim.c
Normal file
186
examples/zynq7000/SIM/c/cosim.c
Normal file
@ -0,0 +1,186 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/select.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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);
|
||||
}
|
513
examples/zynq7000/SIM/c/remote-port-proto.c
Normal file
513
examples/zynq7000/SIM/c/remote-port-proto.c
Normal file
@ -0,0 +1,513 @@
|
||||
/*
|
||||
* Remote-port protocol
|
||||
*
|
||||
* Copyright (c) 2013 Xilinx Inc
|
||||
* Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include "remote-port-proto.h"
|
||||
|
||||
#undef MIN
|
||||
#define MIN(x, y) (x < y ? x : y)
|
||||
|
||||
#if defined(__linux__)
|
||||
# include <endian.h>
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
# include <sys/endian.h>
|
||||
#elif defined(__OpenBSD__)
|
||||
# include <sys/types.h>
|
||||
# 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 <byteswap.h>
|
||||
|
||||
# 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);
|
||||
}
|
534
examples/zynq7000/SIM/c/remote-port-proto.h
Normal file
534
examples/zynq7000/SIM/c/remote-port-proto.h
Normal file
@ -0,0 +1,534 @@
|
||||
/*
|
||||
* QEMU remote port protocol parts.
|
||||
*
|
||||
* Copyright (c) 2013 Xilinx Inc
|
||||
* Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* 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
|
115
examples/zynq7000/SIM/tb_cosim.sv
Normal file
115
examples/zynq7000/SIM/tb_cosim.sv
Normal file
@ -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(curtime<timestamp) begin
|
||||
@(posedge ACLK);
|
||||
end
|
||||
|
||||
if (cosim_ret==1) begin
|
||||
// WRITE
|
||||
cosim_data = write_cosim();
|
||||
// ADDR = cosim_address;
|
||||
// WRDAT = cosim_data;
|
||||
// WR = 1'b1;
|
||||
@(posedge ACLK);
|
||||
// WR = 1'b0;
|
||||
// while (WRREADY==1'b0) begin
|
||||
// @(posedge ACLK);
|
||||
// end
|
||||
end
|
||||
|
||||
if (cosim_ret==2) begin
|
||||
// READ
|
||||
// ADDR = cosim_address;
|
||||
// RD = 1'b1;
|
||||
@(posedge ACLK);
|
||||
// RD = 1'b0;
|
||||
// while (RDREADY==1'b0) begin
|
||||
// @(posedge ACLK);
|
||||
// end
|
||||
@(negedge ACLK);
|
||||
cosim_data = 'hdeadbeef;
|
||||
read_cosim(cosim_data);
|
||||
end
|
||||
finalize_cosim(curtime);
|
||||
end
|
||||
|
||||
$display("Reached end of simulation. Stop Co-simulation");
|
||||
end_cosim();
|
||||
$stop;
|
||||
|
||||
end
|
||||
|
||||
heartbeat #(100000000, 10000000) i_heartbeat(
|
||||
ACLK, ARESETN, LED
|
||||
);
|
||||
|
||||
endmodule
|
@ -3,6 +3,12 @@
|
||||
#include "uart.h"
|
||||
#include "printf.h"
|
||||
|
||||
// COSIM control
|
||||
volatile unsigned int * PAUSE = (unsigned int*) 0x7ffffff4; // Pause the cosimulation
|
||||
volatile unsigned int * SOS = (unsigned int*) 0x7ffffff8; // (Re)start the cosimulation
|
||||
volatile unsigned int * EOS = (unsigned int*) 0x7ffffffc; // Stop the cosimulation
|
||||
|
||||
|
||||
void main(){
|
||||
cpu_disable_interrups();
|
||||
// Initialize ZYNQ Processing System
|
||||
@ -10,7 +16,15 @@ void main(){
|
||||
// Start UART
|
||||
uart_setup();
|
||||
|
||||
*SOS = 1;
|
||||
|
||||
printf("Hello World!\n");
|
||||
|
||||
for(int i=0; i<32; i++){
|
||||
printf("i=%d\n", i);
|
||||
}
|
||||
|
||||
*EOS = 1;
|
||||
|
||||
for(;;);
|
||||
}
|
@ -161,3 +161,42 @@ files_other = SW/devicetree/pcw.dtsi
|
||||
SW/devicetree/include/dt-bindings/power/xlnx-versal-power.h
|
||||
SW/devicetree/include/dt-bindings/reset/xlnx-versal-resets.h
|
||||
# ######################################
|
||||
|
||||
# ######################################
|
||||
# Hardware-firmware cosimulation PS part
|
||||
[target.cosim_ps]
|
||||
toolchain = qemu
|
||||
|
||||
# Toolchain settings
|
||||
arch = xilinxarm
|
||||
machine = arm-generic-fdt-7series
|
||||
ram = 256M
|
||||
extra_opts = -serial /dev/null -serial mon:stdio -dtb ../OUT/devtree/system.dtb \
|
||||
-machine-path /tmp \
|
||||
-icount 1 -sync-quantum 100000 \
|
||||
-device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4
|
||||
|
||||
# Fileset
|
||||
files_executable = OUT/firmware/app.elf
|
||||
files_other = OUT/devtree/system.dtb
|
||||
# ######################################
|
||||
|
||||
# ######################################
|
||||
# Hardware-firmware cosimulation PL part
|
||||
# Note: currently not using xsim since compilation with xilinx
|
||||
# provided tools are horrible on linux
|
||||
[target.cosim_pl]
|
||||
toolchain = questa
|
||||
|
||||
# Toolchain settings
|
||||
toplevel = tb_cosim
|
||||
vcdlevels = 20
|
||||
runtime = all
|
||||
|
||||
# Fileset
|
||||
files_vhdl = RTL/heartbeat.vhd
|
||||
# files_verilog =
|
||||
files_sysverilog = SIM/tb_cosim.sv
|
||||
files_c = SIM/c/remote-port-proto.c SIM/c/cosim.c
|
||||
files_other = SIM/c/remote-port-proto.h
|
||||
# ######################################
|
@ -26,9 +26,9 @@ def do(config, target, log, subprocesses, prefix='.'):
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
|
||||
log(" - run QEMU, quit r(m)build to stop")
|
||||
p = subprocess.Popen(f"qemu-system-{arch} -machine {machine} -kernel {prefix}/{files_executable} {extra_opts} -m {ram} -nographic | tee run.log",
|
||||
p = subprocess.Popen(f"qemu-system-{arch} -machine {machine} -kernel {prefix}/{files_executable} {extra_opts} -m {ram} -nographic 2>error.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
|
71
remotesyn/toolchains/questa.py
Normal file
71
remotesyn/toolchains/questa.py
Normal file
@ -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
|
@ -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)
|
||||
|
@ -161,15 +161,15 @@ 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)
|
||||
|
||||
toolchain = config.get(f'target.{target}', 'toolchain', fallback='NONE')
|
||||
|
Reference in New Issue
Block a user