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

@ -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