First version of zynq cosimulation added

Signed-off-by: Joppe Blondel <joppe@blondel.nl>
This commit is contained in:
2022-09-09 17:53:03 +02:00
parent e18a0c1762
commit 8f03d29894
11 changed files with 1497 additions and 14 deletions

View File

@ -9,4 +9,11 @@
### ZYNQ SoC workflos ### ZYNQ SoC workflos
+ target `firmware`: Compile the firmware running on the ARM core(s) with the `make` toolchain + 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 `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 `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!

View 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);
}

View 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);
}

View 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

View 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

View File

@ -3,6 +3,12 @@
#include "uart.h" #include "uart.h"
#include "printf.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(){ void main(){
cpu_disable_interrups(); cpu_disable_interrups();
// Initialize ZYNQ Processing System // Initialize ZYNQ Processing System
@ -10,7 +16,15 @@ void main(){
// Start UART // Start UART
uart_setup(); uart_setup();
*SOS = 1;
printf("Hello World!\n"); printf("Hello World!\n");
for(int i=0; i<32; i++){
printf("i=%d\n", i);
}
*EOS = 1;
for(;;); for(;;);
} }

View File

@ -160,4 +160,43 @@ files_other = SW/devicetree/pcw.dtsi
SW/devicetree/include/dt-bindings/clock/xlnx-versal-clk.h SW/devicetree/include/dt-bindings/clock/xlnx-versal-clk.h
SW/devicetree/include/dt-bindings/power/xlnx-versal-power.h SW/devicetree/include/dt-bindings/power/xlnx-versal-power.h
SW/devicetree/include/dt-bindings/reset/xlnx-versal-resets.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
# ###################################### # ######################################

View File

@ -26,9 +26,9 @@ def do(config, target, log, subprocesses, prefix='.'):
os.makedirs(out_dir, exist_ok=True) os.makedirs(out_dir, exist_ok=True)
log(" - run QEMU, quit r(m)build to stop") 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, shell=True, cwd=build_dir,
stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL) stdin=subprocess.DEVNULL)
subprocesses.append(p) subprocesses.append(p)
while p.poll() is None: while p.poll() is None:
time.sleep(1) time.sleep(1)
@ -36,5 +36,6 @@ def do(config, target, log, subprocesses, prefix='.'):
log(" - copy logs") log(" - copy logs")
shutil.copy(f'{build_dir}/run.log', f'{out_dir}/run.log') 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 return 0

View 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

View File

@ -16,6 +16,7 @@ def do(config, target, log, subprocesses, prefix='.'):
files_verilog = config.get(f'target.{target}', 'files_verilog', 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_sysverilog = config.get(f'target.{target}', 'files_sysverilog', fallback='').split()
files_xci = config.get(f'target.{target}', 'files_xci', 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() files_other = config.get(f'target.{target}', 'files_other', fallback='').split()
build_dir = config.get(f'project', 'build_dir', fallback='build') build_dir = config.get(f'project', 'build_dir', fallback='build')
out_dir = config.get(f'project', 'out_dir', fallback='out') 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: for s in files_other:
f.write(f"add_files -norecurse -scan_for_includes \"{prefix}/{s}\"\n") f.write(f"add_files -norecurse -scan_for_includes \"{prefix}/{s}\"\n")
f.write(f"import_files -norecurse \"{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(f"set_property top {toplevel} [get_filesets sim_1]\n")
f.write("set_property top_lib xil_defaultlib [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") 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', shell=True, cwd=f'{build_dir}/sim/sim.sim/sim_1/behav/xsim',
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocesses.append(p) subprocesses.append(p)
@ -115,7 +118,7 @@ def do(config, target, log, subprocesses, prefix='.'):
res = p.returncode res = p.returncode
log(" - copy logs") 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: if res!=0:
log("ERROR: compile returned with", res) log("ERROR: compile returned with", res)
@ -123,7 +126,7 @@ def do(config, target, log, subprocesses, prefix='.'):
log(" - elaborate") 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', shell=True, cwd=f'{build_dir}/sim/sim.sim/sim_1/behav/xsim',
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocesses.append(p) subprocesses.append(p)
@ -132,7 +135,7 @@ def do(config, target, log, subprocesses, prefix='.'):
res = p.returncode res = p.returncode
log(" - copy logs") 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: if res!=0:
log("ERROR: elaborate returned with", res) log("ERROR: elaborate returned with", res)
@ -144,7 +147,7 @@ def do(config, target, log, subprocesses, prefix='.'):
log(" - simulate") 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', shell=True, cwd=f'{build_dir}/sim/sim.sim/sim_1/behav/xsim',
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocesses.append(p) subprocesses.append(p)

View File

@ -161,14 +161,14 @@ if __name__=="__main__":
signal.signal(signal.SIGALRM, sighandler) signal.signal(signal.SIGALRM, sighandler)
try: 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: for target in targets:
if stopped: if stopped:
break 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) print("Target", target)