First version of zynq cosimulation added
Signed-off-by: Joppe Blondel <joppe@blondel.nl>
This commit is contained in:
534
examples/zynq7000/SIM/c/remote-port-proto.h
Normal file
534
examples/zynq7000/SIM/c/remote-port-proto.h
Normal file
@ -0,0 +1,534 @@
|
||||
/*
|
||||
* QEMU remote port protocol parts.
|
||||
*
|
||||
* Copyright (c) 2013 Xilinx Inc
|
||||
* Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef REMOTE_PORT_PROTO_H__
|
||||
#define REMOTE_PORT_PROTO_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* Remote-Port (RP) is an inter-simulator protocol. It assumes a reliable
|
||||
* point to point communcation with the remote simulation environment.
|
||||
*
|
||||
* Setup
|
||||
* In the SETUP phase a mandatory HELLO packet is exchanged with optional
|
||||
* CFG packets following. HELLO packets are useful to ensure that both
|
||||
* sides are speaking the same protocol and using compatible versions.
|
||||
*
|
||||
* CFG packets are used to negotiate configuration options. At the moment
|
||||
* these remain unimplemented.
|
||||
*
|
||||
* Once the session is up, communication can start through various other
|
||||
* commands. The list can be found further down this document.
|
||||
* Commands are carried over RP packets. Every RP packet contains a header
|
||||
* with length, flags and an ID to track potential responses.
|
||||
* The header is followed by a packet specific payload. You'll find the
|
||||
* details of the various commands packet layouts here. Some commands can
|
||||
* carry data/blobs in their payload.
|
||||
*/
|
||||
|
||||
|
||||
#define RP_VERSION_MAJOR 4
|
||||
#define RP_VERSION_MINOR 3
|
||||
|
||||
#if defined(_WIN32) && defined(__MINGW32__)
|
||||
/* mingw GCC has a bug with packed attributes. */
|
||||
#define PACKED __attribute__ ((gcc_struct, packed))
|
||||
#else
|
||||
#define PACKED __attribute__ ((packed))
|
||||
#endif
|
||||
|
||||
/* Could be auto generated. */
|
||||
enum rp_cmd {
|
||||
RP_CMD_nop = 0,
|
||||
RP_CMD_hello = 1,
|
||||
RP_CMD_cfg = 2,
|
||||
RP_CMD_read = 3,
|
||||
RP_CMD_write = 4,
|
||||
RP_CMD_interrupt = 5,
|
||||
RP_CMD_sync = 6,
|
||||
RP_CMD_ats_req = 7,
|
||||
RP_CMD_ats_inv = 8,
|
||||
RP_CMD_max = 8
|
||||
};
|
||||
|
||||
enum {
|
||||
RP_OPT_quantum = 0,
|
||||
};
|
||||
|
||||
struct rp_cfg_state {
|
||||
uint64_t quantum;
|
||||
};
|
||||
|
||||
enum {
|
||||
RP_PKT_FLAGS_optional = 1 << 0,
|
||||
RP_PKT_FLAGS_response = 1 << 1,
|
||||
|
||||
/* Posted hint.
|
||||
* When set this means that the receiver is not required to respond to
|
||||
* the message. Since it's just a hint, the sender must be prepared to
|
||||
* drop responses. Note that since flags are echoed back in responses
|
||||
* a response to a posted packet will be easy to identify early in the
|
||||
* protocol stack.
|
||||
*/
|
||||
RP_PKT_FLAGS_posted = 1 << 2,
|
||||
};
|
||||
|
||||
struct rp_pkt_hdr {
|
||||
uint32_t cmd;
|
||||
uint32_t len;
|
||||
uint32_t id;
|
||||
uint32_t flags;
|
||||
uint32_t dev;
|
||||
} PACKED;
|
||||
|
||||
struct rp_pkt_cfg {
|
||||
struct rp_pkt_hdr hdr;
|
||||
uint32_t opt;
|
||||
uint8_t set;
|
||||
} PACKED;
|
||||
|
||||
struct rp_version {
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
} PACKED;
|
||||
|
||||
struct rp_capabilities {
|
||||
/* Offset from start of packet. */
|
||||
uint32_t offset;
|
||||
uint16_t len;
|
||||
uint16_t reserved0;
|
||||
} PACKED;
|
||||
|
||||
enum {
|
||||
CAP_BUSACCESS_EXT_BASE = 1, /* New header layout. */
|
||||
CAP_BUSACCESS_EXT_BYTE_EN = 2, /* Support for Byte Enables. */
|
||||
|
||||
/*
|
||||
* Originally, all interrupt/wire updates over remote-port were posted.
|
||||
* This turned out to be a bad idea. To fix it without breaking backwards
|
||||
* compatibility, we add the WIRE Posted updates capability.
|
||||
*
|
||||
* If the peer supportes this, it will respect the RP_PKT_FLAGS_posted
|
||||
* flag. If the peer doesn't support this capability, senders need to
|
||||
* be aware that the peer will not respond to wire updates regardless
|
||||
* of the posted header-flag.
|
||||
*/
|
||||
CAP_WIRE_POSTED_UPDATES = 3,
|
||||
|
||||
CAP_ATS = 4, /* Address translation services */
|
||||
};
|
||||
|
||||
struct rp_pkt_hello {
|
||||
struct rp_pkt_hdr hdr;
|
||||
struct rp_version version;
|
||||
struct rp_capabilities caps;
|
||||
} PACKED;
|
||||
|
||||
enum {
|
||||
/* Remote port responses. */
|
||||
RP_RESP_OK = 0x0,
|
||||
RP_RESP_BUS_GENERIC_ERROR = 0x1,
|
||||
RP_RESP_ADDR_ERROR = 0x2,
|
||||
RP_RESP_MAX = 0xF,
|
||||
};
|
||||
|
||||
enum {
|
||||
RP_BUS_ATTR_EOP = (1 << 0),
|
||||
RP_BUS_ATTR_SECURE = (1 << 1),
|
||||
RP_BUS_ATTR_EXT_BASE = (1 << 2),
|
||||
RP_BUS_ATTR_PHYS_ADDR = (1 << 3),
|
||||
|
||||
/*
|
||||
* Bits [11:8] are allocated for storing transaction response codes.
|
||||
* These new response codes are backward compatible as existing
|
||||
* implementations will not set/read these bits.
|
||||
* For existing implementations, these bits will be zero which is RESP_OKAY.
|
||||
*/
|
||||
RP_BUS_RESP_SHIFT = 8,
|
||||
RP_BUS_RESP_MASK = (RP_RESP_MAX << RP_BUS_RESP_SHIFT),
|
||||
};
|
||||
|
||||
struct rp_pkt_busaccess {
|
||||
struct rp_pkt_hdr hdr;
|
||||
uint64_t timestamp;
|
||||
uint64_t attributes;
|
||||
uint64_t addr;
|
||||
|
||||
/* Length in bytes. */
|
||||
uint32_t len;
|
||||
|
||||
/* Width of each beat in bytes. Set to zero for unknown (let the remote
|
||||
side choose). */
|
||||
uint32_t width;
|
||||
|
||||
/* Width of streaming, must be a multiple of width.
|
||||
addr should repeat itself around this width. Set to same as len
|
||||
for incremental (normal) accesses. In bytes. */
|
||||
uint32_t stream_width;
|
||||
|
||||
/* Implementation specific source or master-id. */
|
||||
uint16_t master_id;
|
||||
} PACKED;
|
||||
|
||||
|
||||
/* This is the new extended busaccess packet layout. */
|
||||
struct rp_pkt_busaccess_ext_base {
|
||||
struct rp_pkt_hdr hdr;
|
||||
uint64_t timestamp;
|
||||
uint64_t attributes;
|
||||
uint64_t addr;
|
||||
|
||||
/* Length in bytes. */
|
||||
uint32_t len;
|
||||
|
||||
/* Width of each beat in bytes. Set to zero for unknown (let the remote
|
||||
side choose). */
|
||||
uint32_t width;
|
||||
|
||||
/* Width of streaming, must be a multiple of width.
|
||||
addr should repeat itself around this width. Set to same as len
|
||||
for incremental (normal) accesses. In bytes. */
|
||||
uint32_t stream_width;
|
||||
|
||||
/* Implementation specific source or master-id. */
|
||||
uint16_t master_id;
|
||||
/* ---- End of 4.0 base busaccess. ---- */
|
||||
|
||||
uint16_t master_id_31_16; /* MasterID bits [31:16]. */
|
||||
uint32_t master_id_63_32; /* MasterID bits [63:32]. */
|
||||
/* ---------------------------------------------------
|
||||
* Since hdr is 5 x 32bit, we are now 64bit aligned. */
|
||||
|
||||
uint32_t data_offset; /* Offset to data from start of pkt. */
|
||||
uint32_t next_offset; /* Offset to next extension. 0 if none. */
|
||||
|
||||
uint32_t byte_enable_offset;
|
||||
uint32_t byte_enable_len;
|
||||
|
||||
/* ---- End of CAP_BUSACCESS_EXT_BASE. ---- */
|
||||
|
||||
/* If new features are needed that may always occupy space
|
||||
* in the header, then add a new capability and extend the
|
||||
* this area with new fields.
|
||||
* Will help receivers find data_offset and next offset,
|
||||
* even those that don't know about extended fields.
|
||||
*/
|
||||
} PACKED;
|
||||
|
||||
struct rp_pkt_interrupt {
|
||||
struct rp_pkt_hdr hdr;
|
||||
uint64_t timestamp;
|
||||
uint64_t vector;
|
||||
uint32_t line;
|
||||
uint8_t val;
|
||||
} PACKED;
|
||||
|
||||
struct rp_pkt_sync {
|
||||
struct rp_pkt_hdr hdr;
|
||||
uint64_t timestamp;
|
||||
} PACKED;
|
||||
|
||||
enum {
|
||||
RP_ATS_ATTR_exec = 1 << 0,
|
||||
RP_ATS_ATTR_read = 1 << 1,
|
||||
RP_ATS_ATTR_write = 1 << 2,
|
||||
};
|
||||
|
||||
enum {
|
||||
RP_ATS_RESULT_ok = 0,
|
||||
RP_ATS_RESULT_error = 1,
|
||||
};
|
||||
|
||||
struct rp_pkt_ats {
|
||||
struct rp_pkt_hdr hdr;
|
||||
uint64_t timestamp;
|
||||
uint64_t attributes;
|
||||
uint64_t addr;
|
||||
uint64_t len;
|
||||
uint32_t result;
|
||||
uint64_t reserved0;
|
||||
uint64_t reserved1;
|
||||
uint64_t reserved2;
|
||||
uint64_t reserved3;
|
||||
} PACKED;
|
||||
|
||||
struct rp_pkt {
|
||||
union {
|
||||
struct rp_pkt_hdr hdr;
|
||||
struct rp_pkt_hello hello;
|
||||
struct rp_pkt_busaccess busaccess;
|
||||
struct rp_pkt_busaccess_ext_base busaccess_ext_base;
|
||||
struct rp_pkt_interrupt interrupt;
|
||||
struct rp_pkt_sync sync;
|
||||
struct rp_pkt_ats ats;
|
||||
};
|
||||
};
|
||||
|
||||
struct rp_peer_state {
|
||||
void *opaque;
|
||||
|
||||
struct rp_pkt pkt;
|
||||
bool hdr_used;
|
||||
|
||||
struct rp_version version;
|
||||
|
||||
struct {
|
||||
bool busaccess_ext_base;
|
||||
bool busaccess_ext_byte_en;
|
||||
bool wire_posted_updates;
|
||||
bool ats;
|
||||
} caps;
|
||||
|
||||
/* Used to normalize our clk. */
|
||||
int64_t clk_base;
|
||||
|
||||
struct rp_cfg_state local_cfg;
|
||||
struct rp_cfg_state peer_cfg;
|
||||
};
|
||||
|
||||
const char *rp_cmd_to_string(enum rp_cmd cmd);
|
||||
int rp_decode_hdr(struct rp_pkt *pkt);
|
||||
int rp_decode_payload(struct rp_pkt *pkt);
|
||||
|
||||
void rp_encode_hdr(struct rp_pkt_hdr *hdr,
|
||||
uint32_t cmd, uint32_t id, uint32_t dev, uint32_t len,
|
||||
uint32_t flags);
|
||||
|
||||
/*
|
||||
* caps is a an array of supported capabilities by the implementor.
|
||||
* caps_out is the encoded (network byte order) version of the
|
||||
* same array. It should be sent to the peer after the hello packet.
|
||||
*/
|
||||
size_t rp_encode_hello_caps(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt,
|
||||
uint16_t version_major, uint16_t version_minor,
|
||||
uint32_t *caps, uint32_t *features_out,
|
||||
uint32_t features_len);
|
||||
|
||||
/* rp_encode_hello is deprecated in favor of hello_caps. */
|
||||
static inline size_t __attribute__ ((deprecated))
|
||||
rp_encode_hello(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt,
|
||||
uint16_t version_major, uint16_t version_minor) {
|
||||
return rp_encode_hello_caps(id, dev, pkt, version_major, version_minor,
|
||||
NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static inline void *__attribute__ ((deprecated))
|
||||
rp_busaccess_dataptr(struct rp_pkt_busaccess *pkt)
|
||||
{
|
||||
/* Right after the packet. */
|
||||
return pkt + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* rp_busaccess_rx_dataptr
|
||||
*
|
||||
* Predicts the dataptr for a packet to be transmitted.
|
||||
* This should only be used if you are trying to keep
|
||||
* the entire packet in a linear buffer.
|
||||
*/
|
||||
static inline unsigned char *
|
||||
rp_busaccess_tx_dataptr(struct rp_peer_state *peer,
|
||||
struct rp_pkt_busaccess_ext_base *pkt)
|
||||
{
|
||||
unsigned char *p = (unsigned char *) pkt;
|
||||
|
||||
if (peer->caps.busaccess_ext_base) {
|
||||
/* We always put our data right after the header. */
|
||||
return p + sizeof *pkt;
|
||||
} else {
|
||||
/* Right after the old packet layout. */
|
||||
return p + sizeof(struct rp_pkt_busaccess);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* rp_busaccess_rx_dataptr
|
||||
*
|
||||
* Extracts the dataptr from a received packet.
|
||||
*/
|
||||
static inline unsigned char *
|
||||
rp_busaccess_rx_dataptr(struct rp_peer_state *peer,
|
||||
struct rp_pkt_busaccess_ext_base *pkt)
|
||||
{
|
||||
unsigned char *p = (unsigned char *) pkt;
|
||||
|
||||
if (pkt->attributes & RP_BUS_ATTR_EXT_BASE) {
|
||||
return p + pkt->data_offset;
|
||||
} else {
|
||||
/* Right after the old packet layout. */
|
||||
return p + sizeof(struct rp_pkt_busaccess);
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned char *
|
||||
rp_busaccess_byte_en_ptr(struct rp_peer_state *peer,
|
||||
struct rp_pkt_busaccess_ext_base *pkt)
|
||||
{
|
||||
unsigned char *p = (unsigned char *) pkt;
|
||||
|
||||
if ((pkt->attributes & RP_BUS_ATTR_EXT_BASE)
|
||||
&& pkt->byte_enable_len) {
|
||||
assert(pkt->byte_enable_offset >= sizeof *pkt);
|
||||
assert(pkt->byte_enable_offset + pkt->byte_enable_len
|
||||
<= pkt->hdr.len + sizeof pkt->hdr);
|
||||
return p + pkt->byte_enable_offset;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t __attribute__ ((deprecated))
|
||||
rp_encode_read(uint32_t id, uint32_t dev,
|
||||
struct rp_pkt_busaccess *pkt,
|
||||
int64_t clk, uint16_t master_id,
|
||||
uint64_t addr, uint64_t attr, uint32_t size,
|
||||
uint32_t width, uint32_t stream_width);
|
||||
|
||||
size_t __attribute__ ((deprecated))
|
||||
rp_encode_read_resp(uint32_t id, uint32_t dev,
|
||||
struct rp_pkt_busaccess *pkt,
|
||||
int64_t clk, uint16_t master_id,
|
||||
uint64_t addr, uint64_t attr, uint32_t size,
|
||||
uint32_t width, uint32_t stream_width);
|
||||
|
||||
size_t __attribute__ ((deprecated))
|
||||
rp_encode_write(uint32_t id, uint32_t dev,
|
||||
struct rp_pkt_busaccess *pkt,
|
||||
int64_t clk, uint16_t master_id,
|
||||
uint64_t addr, uint64_t attr, uint32_t size,
|
||||
uint32_t width, uint32_t stream_width);
|
||||
|
||||
size_t __attribute__ ((deprecated))
|
||||
rp_encode_write_resp(uint32_t id, uint32_t dev,
|
||||
struct rp_pkt_busaccess *pkt,
|
||||
int64_t clk, uint16_t master_id,
|
||||
uint64_t addr, uint64_t attr, uint32_t size,
|
||||
uint32_t width, uint32_t stream_width);
|
||||
|
||||
struct rp_encode_busaccess_in {
|
||||
uint32_t cmd;
|
||||
uint32_t id;
|
||||
uint32_t flags;
|
||||
uint32_t dev;
|
||||
int64_t clk;
|
||||
uint64_t master_id;
|
||||
uint64_t addr;
|
||||
uint64_t attr;
|
||||
uint32_t size;
|
||||
uint32_t width;
|
||||
uint32_t stream_width;
|
||||
uint32_t byte_enable_len;
|
||||
};
|
||||
|
||||
/* Prepare encode_busaccess input parameters for a packet response. */
|
||||
static inline void
|
||||
rp_encode_busaccess_in_rsp_init(struct rp_encode_busaccess_in *in,
|
||||
struct rp_pkt *pkt) {
|
||||
memset(in, 0, sizeof *in);
|
||||
in->cmd = pkt->hdr.cmd;
|
||||
in->id = pkt->hdr.id;
|
||||
in->flags = pkt->hdr.flags | RP_PKT_FLAGS_response;
|
||||
in->dev = pkt->hdr.dev;
|
||||
/* FIXME: Propagate all master_id fields? */
|
||||
in->master_id = pkt->busaccess.master_id;
|
||||
in->addr = pkt->busaccess.addr;
|
||||
in->size = pkt->busaccess.len;
|
||||
in->width = pkt->busaccess.width;
|
||||
in->stream_width = pkt->busaccess.stream_width;
|
||||
in->byte_enable_len = 0;
|
||||
}
|
||||
size_t rp_encode_busaccess(struct rp_peer_state *peer,
|
||||
struct rp_pkt_busaccess_ext_base *pkt,
|
||||
struct rp_encode_busaccess_in *in);
|
||||
|
||||
size_t rp_encode_interrupt_f(uint32_t id, uint32_t dev,
|
||||
struct rp_pkt_interrupt *pkt,
|
||||
int64_t clk,
|
||||
uint32_t line, uint64_t vector, uint8_t val,
|
||||
uint32_t flags);
|
||||
|
||||
size_t rp_encode_interrupt(uint32_t id, uint32_t dev,
|
||||
struct rp_pkt_interrupt *pkt,
|
||||
int64_t clk,
|
||||
uint32_t line, uint64_t vector, uint8_t val);
|
||||
|
||||
size_t rp_encode_sync(uint32_t id, uint32_t dev,
|
||||
struct rp_pkt_sync *pkt,
|
||||
int64_t clk);
|
||||
|
||||
size_t rp_encode_sync_resp(uint32_t id, uint32_t dev,
|
||||
struct rp_pkt_sync *pkt,
|
||||
int64_t clk);
|
||||
|
||||
size_t rp_encode_ats_req(uint32_t id, uint32_t dev,
|
||||
struct rp_pkt_ats *pkt,
|
||||
int64_t clk, uint64_t attr, uint64_t addr,
|
||||
uint64_t size, uint64_t result, uint32_t flags);
|
||||
|
||||
size_t rp_encode_ats_inv(uint32_t id, uint32_t dev,
|
||||
struct rp_pkt_ats *pkt,
|
||||
int64_t clk, uint64_t attr, uint64_t addr,
|
||||
uint64_t size, uint64_t result, uint32_t flags);
|
||||
|
||||
void rp_process_caps(struct rp_peer_state *peer,
|
||||
void *caps, size_t caps_len);
|
||||
|
||||
/* Dynamically resizable remote port pkt. */
|
||||
|
||||
typedef struct RemotePortDynPkt {
|
||||
struct rp_pkt *pkt;
|
||||
size_t size;
|
||||
} RemotePortDynPkt;
|
||||
|
||||
/*
|
||||
* Make sure dpkt is allocated and has enough room.
|
||||
*/
|
||||
|
||||
void rp_dpkt_alloc(RemotePortDynPkt *dpkt, size_t size);
|
||||
|
||||
void rp_dpkt_swap(RemotePortDynPkt *a, RemotePortDynPkt *b);
|
||||
|
||||
/*
|
||||
* Check if the dpkt is valid. Used for debugging purposes.
|
||||
*/
|
||||
|
||||
bool rp_dpkt_is_valid(RemotePortDynPkt *dpkt);
|
||||
|
||||
/*
|
||||
* Invalidate the dpkt. Used for debugging purposes.
|
||||
*/
|
||||
|
||||
void rp_dpkt_invalidate(RemotePortDynPkt *dpkt);
|
||||
|
||||
void rp_dpkt_free(RemotePortDynPkt *dpkt);
|
||||
|
||||
static inline int rp_get_busaccess_response(struct rp_pkt *pkt)
|
||||
{
|
||||
return (pkt->busaccess_ext_base.attributes & RP_BUS_RESP_MASK) >>
|
||||
RP_BUS_RESP_SHIFT;
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user