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