First version of zynq cosimulation added
Signed-off-by: Joppe Blondel <joppe@blondel.nl>
This commit is contained in:
@ -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!
|
186
examples/zynq7000/SIM/c/cosim.c
Normal file
186
examples/zynq7000/SIM/c/cosim.c
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "remote-port-proto.h"
|
||||||
|
#define UNIX_PREFIX "unix:"
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
uint8_t buf[4096];
|
||||||
|
struct rp_pkt_hdr * hdr = (struct rp_pkt_hdr*)buf;
|
||||||
|
struct rp_pkt * payload = (struct rp_pkt*)(buf);
|
||||||
|
int rp_pkt_id = 0;
|
||||||
|
struct rp_peer_state state;
|
||||||
|
size_t pkt_size;
|
||||||
|
FILE * log;
|
||||||
|
|
||||||
|
int still_to_write, still_to_read, datpointer;
|
||||||
|
uint32_t write_addr, read_addr;
|
||||||
|
|
||||||
|
int start_cosim(char * descr){
|
||||||
|
// Open socket
|
||||||
|
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
// Try to connect
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strncpy(addr.sun_path, descr + strlen(UNIX_PREFIX), sizeof addr.sun_path);
|
||||||
|
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) >= 0){
|
||||||
|
// log = fopen("cosim_log.txt", "w");
|
||||||
|
return 0;
|
||||||
|
}else{
|
||||||
|
close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void end_cosim(){
|
||||||
|
close(fd);
|
||||||
|
fclose(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
int wait_cosim(uint32_t * retaddr, uint32_t * time){
|
||||||
|
// fprintf(log, "-->");
|
||||||
|
if(still_to_write){
|
||||||
|
*retaddr = write_addr;
|
||||||
|
// fprintf(log, "\r\nwrite [%08x] @ %d\r\n", *retaddr, *time);
|
||||||
|
return 1;
|
||||||
|
}else if(still_to_read){
|
||||||
|
*retaddr = read_addr;
|
||||||
|
// fprintf(log, "\r\nread [%08x] @ %d\r\n", *retaddr, *time);
|
||||||
|
return 2;
|
||||||
|
}else{
|
||||||
|
int n;
|
||||||
|
int retval = 0;
|
||||||
|
// Receive header
|
||||||
|
do{
|
||||||
|
n = recv(fd, hdr, sizeof(struct rp_pkt_hdr), 0);
|
||||||
|
} while(n<=0);
|
||||||
|
rp_decode_hdr(hdr);
|
||||||
|
|
||||||
|
// fprintf(log, " . ");
|
||||||
|
|
||||||
|
// Receive payload
|
||||||
|
if(hdr->len){
|
||||||
|
do{
|
||||||
|
n = recv(fd, hdr+1, hdr->len, 0);
|
||||||
|
} while(n<=0);
|
||||||
|
}
|
||||||
|
rp_decode_payload(payload);
|
||||||
|
|
||||||
|
// fprintf(log, "%d\r\n", hdr->cmd);
|
||||||
|
switch(hdr->cmd){
|
||||||
|
|
||||||
|
case RP_CMD_hello:{
|
||||||
|
rp_process_caps(&state, buf+payload->hello.caps.offset, payload->hello.caps.len);
|
||||||
|
|
||||||
|
// Send HELO packet
|
||||||
|
uint32_t caps[] = {
|
||||||
|
CAP_BUSACCESS_EXT_BASE
|
||||||
|
};
|
||||||
|
size_t s = rp_encode_hello_caps(rp_pkt_id++, 0, buf, 4, 3, caps, caps, sizeof(caps)/sizeof(uint32_t));
|
||||||
|
send(fd, buf, s, 0);
|
||||||
|
send(fd, caps, sizeof(caps), 0);
|
||||||
|
// fprintf(log, "hello\r\n");
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RP_CMD_interrupt:{
|
||||||
|
*time = (uint32_t) payload->interrupt.timestamp;
|
||||||
|
// fprintf(log, "interrupt @ %ld\r\n", payload->interrupt.timestamp);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RP_CMD_write:{
|
||||||
|
int addr = payload->busaccess_ext_base.addr;
|
||||||
|
int len = payload->busaccess_ext_base.len;
|
||||||
|
uint64_t t = payload->busaccess_ext_base.timestamp;
|
||||||
|
|
||||||
|
if(len/4>1){
|
||||||
|
// Must be more than one write cycle
|
||||||
|
still_to_write = len/4-1;
|
||||||
|
write_addr = addr+4;
|
||||||
|
datpointer = 0;
|
||||||
|
}else{
|
||||||
|
still_to_write = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = 1;
|
||||||
|
*retaddr = addr;
|
||||||
|
*time = (uint32_t)t;
|
||||||
|
// fprintf(log, "write [%08x] @ %d\r\n", *retaddr, *time);
|
||||||
|
|
||||||
|
// Respond to write
|
||||||
|
struct rp_encode_busaccess_in in = {0};
|
||||||
|
rp_encode_busaccess_in_rsp_init(&in, payload);
|
||||||
|
pkt_size = rp_encode_busaccess(&state, buf, &in);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RP_CMD_read:{
|
||||||
|
int len = payload->busaccess_ext_base.len;
|
||||||
|
int addr = payload->busaccess_ext_base.addr;
|
||||||
|
uint64_t t = payload->busaccess_ext_base.timestamp;
|
||||||
|
|
||||||
|
if(len/4>1){
|
||||||
|
// Must be more than one write cycle
|
||||||
|
still_to_read = len/4-1;
|
||||||
|
read_addr = addr+4;
|
||||||
|
datpointer = 0;
|
||||||
|
}else{
|
||||||
|
still_to_read = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = 2;
|
||||||
|
*retaddr = addr;
|
||||||
|
*time = (uint32_t)t;
|
||||||
|
// fprintf(log, "read [%08x] @ %d\r\n", *retaddr, *time);
|
||||||
|
|
||||||
|
// Respond to read
|
||||||
|
struct rp_encode_busaccess_in in = {0};
|
||||||
|
rp_encode_busaccess_in_rsp_init(&in, payload);
|
||||||
|
pkt_size = rp_encode_busaccess(&state, buf, &in);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case RP_CMD_sync:{
|
||||||
|
// Respond to sync
|
||||||
|
struct rp_pkt resp = {0};
|
||||||
|
size_t s = rp_encode_sync_resp(rp_pkt_id++, 0, &resp, payload->sync.timestamp);
|
||||||
|
uint64_t t = payload->sync.timestamp;
|
||||||
|
*time = (uint32_t)t;
|
||||||
|
send(fd, &resp, s, 0);
|
||||||
|
// fprintf(log, "SYNC @ %ld\r\n", payload->sync.timestamp);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_cosim(unsigned int value){
|
||||||
|
unsigned int * dat = (unsigned int*)((void*)payload + sizeof(payload->busaccess_ext_base));
|
||||||
|
dat[datpointer] = value;
|
||||||
|
// fprintf(log, " -> %08x\r\n", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int write_cosim(){
|
||||||
|
unsigned int * dat = (unsigned int*)((void*)payload + sizeof(payload->busaccess_ext_base));
|
||||||
|
fprintf(log, " -> %08x\r\n", dat[datpointer]);
|
||||||
|
// return dat[datpointer];
|
||||||
|
}
|
||||||
|
|
||||||
|
void finalize_cosim(uint32_t timestamp){
|
||||||
|
if(still_to_write){
|
||||||
|
still_to_write--;
|
||||||
|
write_addr += 4;
|
||||||
|
datpointer++;
|
||||||
|
}else if(still_to_read){
|
||||||
|
still_to_read--;
|
||||||
|
read_addr += 4;
|
||||||
|
datpointer++;
|
||||||
|
}else{
|
||||||
|
payload->busaccess_ext_base.timestamp = (uint64_t) timestamp;
|
||||||
|
send(fd, buf, pkt_size, 0);
|
||||||
|
}
|
||||||
|
// fprintf(log, "finalize @ %d\r\n<--\r\n", timestamp);
|
||||||
|
}
|
513
examples/zynq7000/SIM/c/remote-port-proto.c
Normal file
513
examples/zynq7000/SIM/c/remote-port-proto.c
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
/*
|
||||||
|
* Remote-port protocol
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Xilinx Inc
|
||||||
|
* Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DEFAULT_SOURCE
|
||||||
|
# define _DEFAULT_SOURCE
|
||||||
|
#endif
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "remote-port-proto.h"
|
||||||
|
|
||||||
|
#undef MIN
|
||||||
|
#define MIN(x, y) (x < y ? x : y)
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
# include <endian.h>
|
||||||
|
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
||||||
|
# include <sys/endian.h>
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
# include <sys/types.h>
|
||||||
|
# define be16toh(x) betoh16(x)
|
||||||
|
# define be32toh(x) betoh32(x)
|
||||||
|
# define be64toh(x) betoh64(x)
|
||||||
|
#elif defined(__WIN32)
|
||||||
|
/* We assume little endian. */
|
||||||
|
# define htobe64(x) _byteswap_uint64(x)
|
||||||
|
# define htobe32(x) _byteswap_ulong(x)
|
||||||
|
# define htobe16(x) _byteswap_ushort(x)
|
||||||
|
|
||||||
|
# define be64toh(x) _byteswap_uint64(x)
|
||||||
|
# define be32toh(x) _byteswap_ulong(x)
|
||||||
|
# define be16toh(x) _byteswap_ushort(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Fallback for ancient Linux systems. */
|
||||||
|
#ifndef htobe64
|
||||||
|
# include <byteswap.h>
|
||||||
|
|
||||||
|
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
|
# define htobe64(x) bswap_64(x)
|
||||||
|
# define htobe32(x) bswap_32(x)
|
||||||
|
# define htobe16(x) bswap_16(x)
|
||||||
|
|
||||||
|
# define be64toh(x) bswap_64(x)
|
||||||
|
# define be32toh(x) bswap_32(x)
|
||||||
|
# define be16toh(x) bswap_16(x)
|
||||||
|
# else
|
||||||
|
# define htobe64(x) x
|
||||||
|
# define htobe32(x) x
|
||||||
|
# define htobe16(x) x
|
||||||
|
|
||||||
|
# define be64toh(x) x
|
||||||
|
# define be32toh(x) x
|
||||||
|
# define be16toh(x) x
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *rp_cmd_names[RP_CMD_max + 1] = {
|
||||||
|
[RP_CMD_nop] = "nop",
|
||||||
|
[RP_CMD_hello] = "hello",
|
||||||
|
[RP_CMD_cfg] = "cfg",
|
||||||
|
[RP_CMD_read] = "read",
|
||||||
|
[RP_CMD_write] = "write",
|
||||||
|
[RP_CMD_interrupt] = "interrupt",
|
||||||
|
[RP_CMD_sync] = "sync",
|
||||||
|
[RP_CMD_ats_req] = "ats_request",
|
||||||
|
[RP_CMD_ats_inv] = "ats_invalidation",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *rp_cmd_to_string(enum rp_cmd cmd)
|
||||||
|
{
|
||||||
|
assert(cmd <= RP_CMD_max);
|
||||||
|
return rp_cmd_names[cmd];
|
||||||
|
}
|
||||||
|
|
||||||
|
int rp_decode_hdr(struct rp_pkt *pkt)
|
||||||
|
{
|
||||||
|
int used = 0;
|
||||||
|
|
||||||
|
pkt->hdr.cmd = be32toh(pkt->hdr.cmd);
|
||||||
|
pkt->hdr.len = be32toh(pkt->hdr.len);
|
||||||
|
pkt->hdr.id = be32toh(pkt->hdr.id);
|
||||||
|
pkt->hdr.flags = be32toh(pkt->hdr.flags);
|
||||||
|
pkt->hdr.dev = be32toh(pkt->hdr.dev);
|
||||||
|
used += sizeof pkt->hdr;
|
||||||
|
return used;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rp_decode_payload(struct rp_pkt *pkt)
|
||||||
|
{
|
||||||
|
int used = 0;
|
||||||
|
/* Master_id has an odd decoding due to historical reasons. */
|
||||||
|
uint64_t master_id;
|
||||||
|
|
||||||
|
switch (pkt->hdr.cmd) {
|
||||||
|
case RP_CMD_hello:
|
||||||
|
assert(pkt->hdr.len >= sizeof pkt->hello.version);
|
||||||
|
pkt->hello.version.major = be16toh(pkt->hello.version.major);
|
||||||
|
pkt->hello.version.minor = be16toh(pkt->hello.version.minor);
|
||||||
|
used += sizeof pkt->hello.version;
|
||||||
|
|
||||||
|
if ((pkt->hdr.len - used) >= sizeof pkt->hello.caps) {
|
||||||
|
void *offset;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pkt->hello.caps.offset = be32toh(pkt->hello.caps.offset);
|
||||||
|
pkt->hello.caps.len = be16toh(pkt->hello.caps.len);
|
||||||
|
|
||||||
|
offset = (char *)pkt + pkt->hello.caps.offset;
|
||||||
|
for (i = 0; i < pkt->hello.caps.len; i++) {
|
||||||
|
uint32_t cap;
|
||||||
|
|
||||||
|
/* We don't know if offset is 32bit aligned so use
|
||||||
|
* memcpy to do the endian conversion. */
|
||||||
|
memcpy(&cap, offset + i * sizeof cap, sizeof cap);
|
||||||
|
cap = be32toh(cap);
|
||||||
|
memcpy(offset + i * sizeof cap, &cap, sizeof cap);
|
||||||
|
}
|
||||||
|
used += sizeof pkt->hello.caps;
|
||||||
|
} else {
|
||||||
|
pkt->hello.caps.offset = 0;
|
||||||
|
pkt->hello.caps.len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Consume everything ignoring additional headers we do not yet
|
||||||
|
* know about. */
|
||||||
|
used = pkt->hdr.len;
|
||||||
|
break;
|
||||||
|
case RP_CMD_write:
|
||||||
|
case RP_CMD_read:
|
||||||
|
assert(pkt->hdr.len >= sizeof pkt->busaccess - sizeof pkt->hdr);
|
||||||
|
pkt->busaccess.timestamp = be64toh(pkt->busaccess.timestamp);
|
||||||
|
pkt->busaccess.addr = be64toh(pkt->busaccess.addr);
|
||||||
|
pkt->busaccess.master_id = be16toh(pkt->busaccess.master_id);
|
||||||
|
pkt->busaccess.attributes = be64toh(pkt->busaccess.attributes);
|
||||||
|
pkt->busaccess.len = be32toh(pkt->busaccess.len);
|
||||||
|
pkt->busaccess.width = be32toh(pkt->busaccess.width);
|
||||||
|
pkt->busaccess.stream_width = be32toh(pkt->busaccess.stream_width);
|
||||||
|
master_id = be16toh(pkt->busaccess.master_id);
|
||||||
|
|
||||||
|
used += sizeof pkt->busaccess - sizeof pkt->hdr;
|
||||||
|
|
||||||
|
if (pkt->busaccess.attributes & RP_BUS_ATTR_EXT_BASE) {
|
||||||
|
struct rp_pkt_busaccess_ext_base *pext = &pkt->busaccess_ext_base;
|
||||||
|
|
||||||
|
assert(pkt->hdr.len >= sizeof *pext - sizeof pkt->hdr);
|
||||||
|
master_id |= (uint64_t)be16toh(pext->master_id_31_16) << 16;
|
||||||
|
master_id |= (uint64_t)be32toh(pext->master_id_63_32) << 32;
|
||||||
|
pext->data_offset = be32toh(pext->data_offset);
|
||||||
|
pext->next_offset = be32toh(pext->next_offset);
|
||||||
|
pext->byte_enable_offset = be32toh(pext->byte_enable_offset);
|
||||||
|
pext->byte_enable_len = be32toh(pext->byte_enable_len);
|
||||||
|
|
||||||
|
used += sizeof *pext - sizeof pkt->busaccess;
|
||||||
|
}
|
||||||
|
pkt->busaccess.master_id = master_id;
|
||||||
|
break;
|
||||||
|
case RP_CMD_interrupt:
|
||||||
|
pkt->interrupt.timestamp = be64toh(pkt->interrupt.timestamp);
|
||||||
|
pkt->interrupt.vector = be64toh(pkt->interrupt.vector);
|
||||||
|
pkt->interrupt.line = be32toh(pkt->interrupt.line);
|
||||||
|
pkt->interrupt.val = pkt->interrupt.val;
|
||||||
|
used += pkt->hdr.len;
|
||||||
|
break;
|
||||||
|
case RP_CMD_sync:
|
||||||
|
pkt->sync.timestamp = be64toh(pkt->interrupt.timestamp);
|
||||||
|
used += pkt->hdr.len;
|
||||||
|
break;
|
||||||
|
case RP_CMD_ats_req:
|
||||||
|
case RP_CMD_ats_inv:
|
||||||
|
pkt->ats.attributes = be64toh(pkt->ats.attributes);
|
||||||
|
pkt->ats.addr = be64toh(pkt->ats.addr);
|
||||||
|
pkt->ats.len = be64toh(pkt->ats.len);
|
||||||
|
pkt->ats.result = be32toh(pkt->ats.result);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return used;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rp_encode_hdr(struct rp_pkt_hdr *hdr, uint32_t cmd, uint32_t id,
|
||||||
|
uint32_t dev, uint32_t len, uint32_t flags)
|
||||||
|
{
|
||||||
|
hdr->cmd = htobe32(cmd);
|
||||||
|
hdr->len = htobe32(len);
|
||||||
|
hdr->id = htobe32(id);
|
||||||
|
hdr->dev = htobe32(dev);
|
||||||
|
hdr->flags = htobe32(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rp_encode_hello_caps(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt,
|
||||||
|
uint16_t version_major, uint16_t version_minor,
|
||||||
|
uint32_t *caps, uint32_t *caps_out,
|
||||||
|
uint32_t caps_len)
|
||||||
|
{
|
||||||
|
size_t psize = sizeof *pkt + sizeof caps[0] * caps_len;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
rp_encode_hdr(&pkt->hdr, RP_CMD_hello, id, dev,
|
||||||
|
psize - sizeof pkt->hdr, 0);
|
||||||
|
pkt->version.major = htobe16(version_major);
|
||||||
|
pkt->version.minor = htobe16(version_minor);
|
||||||
|
|
||||||
|
/* Feature list is appeneded right after the hello packet. */
|
||||||
|
pkt->caps.offset = htobe32(sizeof *pkt);
|
||||||
|
pkt->caps.len = htobe16(caps_len);
|
||||||
|
|
||||||
|
for (i = 0; i < caps_len; i++) {
|
||||||
|
uint32_t cap;
|
||||||
|
|
||||||
|
cap = caps[i];
|
||||||
|
caps_out[i] = htobe32(cap);
|
||||||
|
}
|
||||||
|
return sizeof *pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rp_encode_busaccess_common(struct rp_pkt_busaccess *pkt,
|
||||||
|
int64_t clk, uint16_t master_id,
|
||||||
|
uint64_t addr, uint64_t attr, uint32_t size,
|
||||||
|
uint32_t width, uint32_t stream_width)
|
||||||
|
{
|
||||||
|
pkt->timestamp = htobe64(clk);
|
||||||
|
pkt->master_id = htobe16(master_id);
|
||||||
|
pkt->addr = htobe64(addr);
|
||||||
|
pkt->attributes = htobe64(attr);
|
||||||
|
pkt->len = htobe32(size);
|
||||||
|
pkt->width = htobe32(width);
|
||||||
|
pkt->stream_width = htobe32(stream_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rp_encode_read(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_busaccess *pkt,
|
||||||
|
int64_t clk, uint16_t master_id,
|
||||||
|
uint64_t addr, uint64_t attr, uint32_t size,
|
||||||
|
uint32_t width, uint32_t stream_width)
|
||||||
|
{
|
||||||
|
rp_encode_hdr(&pkt->hdr, RP_CMD_read, id, dev,
|
||||||
|
sizeof *pkt - sizeof pkt->hdr, 0);
|
||||||
|
rp_encode_busaccess_common(pkt, clk, master_id, addr, attr,
|
||||||
|
size, width, stream_width);
|
||||||
|
return sizeof *pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rp_encode_read_resp(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_busaccess *pkt,
|
||||||
|
int64_t clk, uint16_t master_id,
|
||||||
|
uint64_t addr, uint64_t attr, uint32_t size,
|
||||||
|
uint32_t width, uint32_t stream_width)
|
||||||
|
{
|
||||||
|
rp_encode_hdr(&pkt->hdr, RP_CMD_read, id, dev,
|
||||||
|
sizeof *pkt - sizeof pkt->hdr + size, RP_PKT_FLAGS_response);
|
||||||
|
rp_encode_busaccess_common(pkt, clk, master_id, addr, attr,
|
||||||
|
size, width, stream_width);
|
||||||
|
return sizeof *pkt + size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rp_encode_write(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_busaccess *pkt,
|
||||||
|
int64_t clk, uint16_t master_id,
|
||||||
|
uint64_t addr, uint64_t attr, uint32_t size,
|
||||||
|
uint32_t width, uint32_t stream_width)
|
||||||
|
{
|
||||||
|
rp_encode_hdr(&pkt->hdr, RP_CMD_write, id, dev,
|
||||||
|
sizeof *pkt - sizeof pkt->hdr + size, 0);
|
||||||
|
rp_encode_busaccess_common(pkt, clk, master_id, addr, attr,
|
||||||
|
size, width, stream_width);
|
||||||
|
return sizeof *pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rp_encode_write_resp(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_busaccess *pkt,
|
||||||
|
int64_t clk, uint16_t master_id,
|
||||||
|
uint64_t addr, uint64_t attr, uint32_t size,
|
||||||
|
uint32_t width, uint32_t stream_width)
|
||||||
|
{
|
||||||
|
rp_encode_hdr(&pkt->hdr, RP_CMD_write, id, dev,
|
||||||
|
sizeof *pkt - sizeof pkt->hdr, RP_PKT_FLAGS_response);
|
||||||
|
rp_encode_busaccess_common(pkt, clk, master_id, addr, attr,
|
||||||
|
size, width, stream_width);
|
||||||
|
return sizeof *pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* New API for extended header. */
|
||||||
|
size_t rp_encode_busaccess(struct rp_peer_state *peer,
|
||||||
|
struct rp_pkt_busaccess_ext_base *pkt,
|
||||||
|
struct rp_encode_busaccess_in *in) {
|
||||||
|
struct rp_pkt_busaccess *pkt_v4_0 = (void *) pkt;
|
||||||
|
uint32_t hsize = 0;
|
||||||
|
uint32_t ret_size = 0;
|
||||||
|
|
||||||
|
/* Allocate packet space. */
|
||||||
|
if (in->cmd == RP_CMD_write && !(in->flags & RP_PKT_FLAGS_response)) {
|
||||||
|
hsize = in->size;
|
||||||
|
}
|
||||||
|
if (in->cmd == RP_CMD_read && (in->flags & RP_PKT_FLAGS_response)) {
|
||||||
|
hsize = in->size;
|
||||||
|
ret_size = in->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If peer does not support the busaccess base extensions, use the
|
||||||
|
* old layout. For responses, what matters is if we're responding
|
||||||
|
* to a packet with the extensions.
|
||||||
|
*/
|
||||||
|
if (!peer->caps.busaccess_ext_base && !(in->attr & RP_BUS_ATTR_EXT_BASE)) {
|
||||||
|
/* Old layout. */
|
||||||
|
assert(in->master_id < UINT16_MAX);
|
||||||
|
|
||||||
|
rp_encode_hdr(&pkt->hdr, in->cmd, in->id, in->dev,
|
||||||
|
sizeof *pkt_v4_0 - sizeof pkt->hdr + hsize, in->flags);
|
||||||
|
rp_encode_busaccess_common(pkt_v4_0, in->clk, in->master_id,
|
||||||
|
in->addr, in->attr,
|
||||||
|
in->size, in->width, in->stream_width);
|
||||||
|
return sizeof *pkt_v4_0 + ret_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode the extended fields. */
|
||||||
|
pkt->master_id_31_16 = htobe16(in->master_id >> 16);
|
||||||
|
pkt->master_id_63_32 = htobe32(in->master_id >> 32);
|
||||||
|
|
||||||
|
/* We always put data right after the header. */
|
||||||
|
pkt->data_offset = htobe32(sizeof *pkt);
|
||||||
|
pkt->next_offset = 0;
|
||||||
|
|
||||||
|
pkt->byte_enable_offset = htobe32(sizeof *pkt + hsize);
|
||||||
|
pkt->byte_enable_len = htobe32(in->byte_enable_len);
|
||||||
|
hsize += in->byte_enable_len;
|
||||||
|
|
||||||
|
rp_encode_hdr(&pkt->hdr, in->cmd, in->id, in->dev,
|
||||||
|
sizeof *pkt - sizeof pkt->hdr + hsize, in->flags);
|
||||||
|
rp_encode_busaccess_common(pkt_v4_0, in->clk, in->master_id, in->addr,
|
||||||
|
in->attr | RP_BUS_ATTR_EXT_BASE,
|
||||||
|
in->size, in->width, in->stream_width);
|
||||||
|
|
||||||
|
return sizeof *pkt + ret_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rp_encode_interrupt_f(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_interrupt *pkt,
|
||||||
|
int64_t clk,
|
||||||
|
uint32_t line, uint64_t vector, uint8_t val,
|
||||||
|
uint32_t flags)
|
||||||
|
{
|
||||||
|
rp_encode_hdr(&pkt->hdr, RP_CMD_interrupt, id, dev,
|
||||||
|
sizeof *pkt - sizeof pkt->hdr, flags);
|
||||||
|
pkt->timestamp = htobe64(clk);
|
||||||
|
pkt->vector = htobe64(vector);
|
||||||
|
pkt->line = htobe32(line);
|
||||||
|
pkt->val = val;
|
||||||
|
return sizeof *pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rp_encode_interrupt(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_interrupt *pkt,
|
||||||
|
int64_t clk,
|
||||||
|
uint32_t line, uint64_t vector, uint8_t val)
|
||||||
|
{
|
||||||
|
return rp_encode_interrupt_f(id, dev, pkt, clk, line, vector, val, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t rp_encode_ats_common(uint32_t cmd, uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_ats *pkt,
|
||||||
|
int64_t clk, uint64_t attr, uint64_t addr,
|
||||||
|
uint64_t len, uint64_t result, uint32_t flags)
|
||||||
|
{
|
||||||
|
rp_encode_hdr(&pkt->hdr, cmd, id, dev,
|
||||||
|
sizeof *pkt - sizeof pkt->hdr, flags);
|
||||||
|
pkt->timestamp = htobe64(clk);
|
||||||
|
pkt->attributes = htobe64(attr);
|
||||||
|
pkt->addr = htobe64(addr);
|
||||||
|
pkt->len = htobe64(len);
|
||||||
|
pkt->result = htobe32(result);
|
||||||
|
return sizeof *pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rp_encode_ats_req(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_ats *pkt,
|
||||||
|
int64_t clk, uint64_t attr, uint64_t addr,
|
||||||
|
uint64_t len, uint64_t result, uint32_t flags)
|
||||||
|
{
|
||||||
|
return rp_encode_ats_common(RP_CMD_ats_req, id, dev,
|
||||||
|
pkt, clk, attr,
|
||||||
|
addr, len, result, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rp_encode_ats_inv(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_ats *pkt,
|
||||||
|
int64_t clk, uint64_t attr, uint64_t addr,
|
||||||
|
uint64_t len, uint64_t result, uint32_t flags)
|
||||||
|
{
|
||||||
|
return rp_encode_ats_common(RP_CMD_ats_inv, id, dev,
|
||||||
|
pkt, clk, attr,
|
||||||
|
addr, len, result, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t rp_encode_sync_common(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_sync *pkt,
|
||||||
|
int64_t clk, uint32_t flags)
|
||||||
|
{
|
||||||
|
rp_encode_hdr(&pkt->hdr, RP_CMD_sync, id, dev,
|
||||||
|
sizeof *pkt - sizeof pkt->hdr, flags);
|
||||||
|
pkt->timestamp = htobe64(clk);
|
||||||
|
return sizeof *pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rp_encode_sync(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_sync *pkt,
|
||||||
|
int64_t clk)
|
||||||
|
{
|
||||||
|
return rp_encode_sync_common(id, dev, pkt, clk, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rp_encode_sync_resp(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_sync *pkt,
|
||||||
|
int64_t clk)
|
||||||
|
{
|
||||||
|
return rp_encode_sync_common(id, dev, pkt, clk, RP_PKT_FLAGS_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rp_process_caps(struct rp_peer_state *peer,
|
||||||
|
void *caps, size_t caps_len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(peer->caps.busaccess_ext_base == false);
|
||||||
|
|
||||||
|
for (i = 0; i < caps_len; i++) {
|
||||||
|
uint32_t cap;
|
||||||
|
|
||||||
|
memcpy(&cap, caps + i * sizeof cap, sizeof cap);
|
||||||
|
|
||||||
|
switch (cap) {
|
||||||
|
case CAP_BUSACCESS_EXT_BASE:
|
||||||
|
peer->caps.busaccess_ext_base = true;
|
||||||
|
break;
|
||||||
|
case CAP_BUSACCESS_EXT_BYTE_EN:
|
||||||
|
peer->caps.busaccess_ext_byte_en = true;
|
||||||
|
break;
|
||||||
|
case CAP_WIRE_POSTED_UPDATES:
|
||||||
|
peer->caps.wire_posted_updates = true;
|
||||||
|
break;
|
||||||
|
case CAP_ATS:
|
||||||
|
peer->caps.ats = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void rp_dpkt_alloc(RemotePortDynPkt *dpkt, size_t size)
|
||||||
|
{
|
||||||
|
if (dpkt->size < size) {
|
||||||
|
char *u8;
|
||||||
|
dpkt->pkt = realloc(dpkt->pkt, size);
|
||||||
|
u8 = (void *) dpkt->pkt;
|
||||||
|
memset(u8 + dpkt->size, 0, size - dpkt->size);
|
||||||
|
dpkt->size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rp_dpkt_swap(RemotePortDynPkt *a, RemotePortDynPkt *b)
|
||||||
|
{
|
||||||
|
struct rp_pkt *tmp_pkt;
|
||||||
|
size_t tmp_size;
|
||||||
|
|
||||||
|
tmp_pkt = a->pkt;
|
||||||
|
tmp_size = a->size;
|
||||||
|
a->pkt = b->pkt;
|
||||||
|
a->size = b->size;
|
||||||
|
b->pkt = tmp_pkt;
|
||||||
|
b->size = tmp_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rp_dpkt_is_valid(RemotePortDynPkt *dpkt)
|
||||||
|
{
|
||||||
|
return dpkt->size > 0 && dpkt->pkt->hdr.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rp_dpkt_invalidate(RemotePortDynPkt *dpkt)
|
||||||
|
{
|
||||||
|
assert(rp_dpkt_is_valid(dpkt));
|
||||||
|
dpkt->pkt->hdr.len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void rp_dpkt_free(RemotePortDynPkt *dpkt)
|
||||||
|
{
|
||||||
|
dpkt->size = 0;
|
||||||
|
free(dpkt->pkt);
|
||||||
|
}
|
534
examples/zynq7000/SIM/c/remote-port-proto.h
Normal file
534
examples/zynq7000/SIM/c/remote-port-proto.h
Normal file
@ -0,0 +1,534 @@
|
|||||||
|
/*
|
||||||
|
* QEMU remote port protocol parts.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Xilinx Inc
|
||||||
|
* Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#ifndef REMOTE_PORT_PROTO_H__
|
||||||
|
#define REMOTE_PORT_PROTO_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remote-Port (RP) is an inter-simulator protocol. It assumes a reliable
|
||||||
|
* point to point communcation with the remote simulation environment.
|
||||||
|
*
|
||||||
|
* Setup
|
||||||
|
* In the SETUP phase a mandatory HELLO packet is exchanged with optional
|
||||||
|
* CFG packets following. HELLO packets are useful to ensure that both
|
||||||
|
* sides are speaking the same protocol and using compatible versions.
|
||||||
|
*
|
||||||
|
* CFG packets are used to negotiate configuration options. At the moment
|
||||||
|
* these remain unimplemented.
|
||||||
|
*
|
||||||
|
* Once the session is up, communication can start through various other
|
||||||
|
* commands. The list can be found further down this document.
|
||||||
|
* Commands are carried over RP packets. Every RP packet contains a header
|
||||||
|
* with length, flags and an ID to track potential responses.
|
||||||
|
* The header is followed by a packet specific payload. You'll find the
|
||||||
|
* details of the various commands packet layouts here. Some commands can
|
||||||
|
* carry data/blobs in their payload.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#define RP_VERSION_MAJOR 4
|
||||||
|
#define RP_VERSION_MINOR 3
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(__MINGW32__)
|
||||||
|
/* mingw GCC has a bug with packed attributes. */
|
||||||
|
#define PACKED __attribute__ ((gcc_struct, packed))
|
||||||
|
#else
|
||||||
|
#define PACKED __attribute__ ((packed))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Could be auto generated. */
|
||||||
|
enum rp_cmd {
|
||||||
|
RP_CMD_nop = 0,
|
||||||
|
RP_CMD_hello = 1,
|
||||||
|
RP_CMD_cfg = 2,
|
||||||
|
RP_CMD_read = 3,
|
||||||
|
RP_CMD_write = 4,
|
||||||
|
RP_CMD_interrupt = 5,
|
||||||
|
RP_CMD_sync = 6,
|
||||||
|
RP_CMD_ats_req = 7,
|
||||||
|
RP_CMD_ats_inv = 8,
|
||||||
|
RP_CMD_max = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RP_OPT_quantum = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rp_cfg_state {
|
||||||
|
uint64_t quantum;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RP_PKT_FLAGS_optional = 1 << 0,
|
||||||
|
RP_PKT_FLAGS_response = 1 << 1,
|
||||||
|
|
||||||
|
/* Posted hint.
|
||||||
|
* When set this means that the receiver is not required to respond to
|
||||||
|
* the message. Since it's just a hint, the sender must be prepared to
|
||||||
|
* drop responses. Note that since flags are echoed back in responses
|
||||||
|
* a response to a posted packet will be easy to identify early in the
|
||||||
|
* protocol stack.
|
||||||
|
*/
|
||||||
|
RP_PKT_FLAGS_posted = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rp_pkt_hdr {
|
||||||
|
uint32_t cmd;
|
||||||
|
uint32_t len;
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t dev;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
struct rp_pkt_cfg {
|
||||||
|
struct rp_pkt_hdr hdr;
|
||||||
|
uint32_t opt;
|
||||||
|
uint8_t set;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
struct rp_version {
|
||||||
|
uint16_t major;
|
||||||
|
uint16_t minor;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
struct rp_capabilities {
|
||||||
|
/* Offset from start of packet. */
|
||||||
|
uint32_t offset;
|
||||||
|
uint16_t len;
|
||||||
|
uint16_t reserved0;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CAP_BUSACCESS_EXT_BASE = 1, /* New header layout. */
|
||||||
|
CAP_BUSACCESS_EXT_BYTE_EN = 2, /* Support for Byte Enables. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Originally, all interrupt/wire updates over remote-port were posted.
|
||||||
|
* This turned out to be a bad idea. To fix it without breaking backwards
|
||||||
|
* compatibility, we add the WIRE Posted updates capability.
|
||||||
|
*
|
||||||
|
* If the peer supportes this, it will respect the RP_PKT_FLAGS_posted
|
||||||
|
* flag. If the peer doesn't support this capability, senders need to
|
||||||
|
* be aware that the peer will not respond to wire updates regardless
|
||||||
|
* of the posted header-flag.
|
||||||
|
*/
|
||||||
|
CAP_WIRE_POSTED_UPDATES = 3,
|
||||||
|
|
||||||
|
CAP_ATS = 4, /* Address translation services */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rp_pkt_hello {
|
||||||
|
struct rp_pkt_hdr hdr;
|
||||||
|
struct rp_version version;
|
||||||
|
struct rp_capabilities caps;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* Remote port responses. */
|
||||||
|
RP_RESP_OK = 0x0,
|
||||||
|
RP_RESP_BUS_GENERIC_ERROR = 0x1,
|
||||||
|
RP_RESP_ADDR_ERROR = 0x2,
|
||||||
|
RP_RESP_MAX = 0xF,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RP_BUS_ATTR_EOP = (1 << 0),
|
||||||
|
RP_BUS_ATTR_SECURE = (1 << 1),
|
||||||
|
RP_BUS_ATTR_EXT_BASE = (1 << 2),
|
||||||
|
RP_BUS_ATTR_PHYS_ADDR = (1 << 3),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bits [11:8] are allocated for storing transaction response codes.
|
||||||
|
* These new response codes are backward compatible as existing
|
||||||
|
* implementations will not set/read these bits.
|
||||||
|
* For existing implementations, these bits will be zero which is RESP_OKAY.
|
||||||
|
*/
|
||||||
|
RP_BUS_RESP_SHIFT = 8,
|
||||||
|
RP_BUS_RESP_MASK = (RP_RESP_MAX << RP_BUS_RESP_SHIFT),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rp_pkt_busaccess {
|
||||||
|
struct rp_pkt_hdr hdr;
|
||||||
|
uint64_t timestamp;
|
||||||
|
uint64_t attributes;
|
||||||
|
uint64_t addr;
|
||||||
|
|
||||||
|
/* Length in bytes. */
|
||||||
|
uint32_t len;
|
||||||
|
|
||||||
|
/* Width of each beat in bytes. Set to zero for unknown (let the remote
|
||||||
|
side choose). */
|
||||||
|
uint32_t width;
|
||||||
|
|
||||||
|
/* Width of streaming, must be a multiple of width.
|
||||||
|
addr should repeat itself around this width. Set to same as len
|
||||||
|
for incremental (normal) accesses. In bytes. */
|
||||||
|
uint32_t stream_width;
|
||||||
|
|
||||||
|
/* Implementation specific source or master-id. */
|
||||||
|
uint16_t master_id;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
|
||||||
|
/* This is the new extended busaccess packet layout. */
|
||||||
|
struct rp_pkt_busaccess_ext_base {
|
||||||
|
struct rp_pkt_hdr hdr;
|
||||||
|
uint64_t timestamp;
|
||||||
|
uint64_t attributes;
|
||||||
|
uint64_t addr;
|
||||||
|
|
||||||
|
/* Length in bytes. */
|
||||||
|
uint32_t len;
|
||||||
|
|
||||||
|
/* Width of each beat in bytes. Set to zero for unknown (let the remote
|
||||||
|
side choose). */
|
||||||
|
uint32_t width;
|
||||||
|
|
||||||
|
/* Width of streaming, must be a multiple of width.
|
||||||
|
addr should repeat itself around this width. Set to same as len
|
||||||
|
for incremental (normal) accesses. In bytes. */
|
||||||
|
uint32_t stream_width;
|
||||||
|
|
||||||
|
/* Implementation specific source or master-id. */
|
||||||
|
uint16_t master_id;
|
||||||
|
/* ---- End of 4.0 base busaccess. ---- */
|
||||||
|
|
||||||
|
uint16_t master_id_31_16; /* MasterID bits [31:16]. */
|
||||||
|
uint32_t master_id_63_32; /* MasterID bits [63:32]. */
|
||||||
|
/* ---------------------------------------------------
|
||||||
|
* Since hdr is 5 x 32bit, we are now 64bit aligned. */
|
||||||
|
|
||||||
|
uint32_t data_offset; /* Offset to data from start of pkt. */
|
||||||
|
uint32_t next_offset; /* Offset to next extension. 0 if none. */
|
||||||
|
|
||||||
|
uint32_t byte_enable_offset;
|
||||||
|
uint32_t byte_enable_len;
|
||||||
|
|
||||||
|
/* ---- End of CAP_BUSACCESS_EXT_BASE. ---- */
|
||||||
|
|
||||||
|
/* If new features are needed that may always occupy space
|
||||||
|
* in the header, then add a new capability and extend the
|
||||||
|
* this area with new fields.
|
||||||
|
* Will help receivers find data_offset and next offset,
|
||||||
|
* even those that don't know about extended fields.
|
||||||
|
*/
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
struct rp_pkt_interrupt {
|
||||||
|
struct rp_pkt_hdr hdr;
|
||||||
|
uint64_t timestamp;
|
||||||
|
uint64_t vector;
|
||||||
|
uint32_t line;
|
||||||
|
uint8_t val;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
struct rp_pkt_sync {
|
||||||
|
struct rp_pkt_hdr hdr;
|
||||||
|
uint64_t timestamp;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RP_ATS_ATTR_exec = 1 << 0,
|
||||||
|
RP_ATS_ATTR_read = 1 << 1,
|
||||||
|
RP_ATS_ATTR_write = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RP_ATS_RESULT_ok = 0,
|
||||||
|
RP_ATS_RESULT_error = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rp_pkt_ats {
|
||||||
|
struct rp_pkt_hdr hdr;
|
||||||
|
uint64_t timestamp;
|
||||||
|
uint64_t attributes;
|
||||||
|
uint64_t addr;
|
||||||
|
uint64_t len;
|
||||||
|
uint32_t result;
|
||||||
|
uint64_t reserved0;
|
||||||
|
uint64_t reserved1;
|
||||||
|
uint64_t reserved2;
|
||||||
|
uint64_t reserved3;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
struct rp_pkt {
|
||||||
|
union {
|
||||||
|
struct rp_pkt_hdr hdr;
|
||||||
|
struct rp_pkt_hello hello;
|
||||||
|
struct rp_pkt_busaccess busaccess;
|
||||||
|
struct rp_pkt_busaccess_ext_base busaccess_ext_base;
|
||||||
|
struct rp_pkt_interrupt interrupt;
|
||||||
|
struct rp_pkt_sync sync;
|
||||||
|
struct rp_pkt_ats ats;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rp_peer_state {
|
||||||
|
void *opaque;
|
||||||
|
|
||||||
|
struct rp_pkt pkt;
|
||||||
|
bool hdr_used;
|
||||||
|
|
||||||
|
struct rp_version version;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool busaccess_ext_base;
|
||||||
|
bool busaccess_ext_byte_en;
|
||||||
|
bool wire_posted_updates;
|
||||||
|
bool ats;
|
||||||
|
} caps;
|
||||||
|
|
||||||
|
/* Used to normalize our clk. */
|
||||||
|
int64_t clk_base;
|
||||||
|
|
||||||
|
struct rp_cfg_state local_cfg;
|
||||||
|
struct rp_cfg_state peer_cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *rp_cmd_to_string(enum rp_cmd cmd);
|
||||||
|
int rp_decode_hdr(struct rp_pkt *pkt);
|
||||||
|
int rp_decode_payload(struct rp_pkt *pkt);
|
||||||
|
|
||||||
|
void rp_encode_hdr(struct rp_pkt_hdr *hdr,
|
||||||
|
uint32_t cmd, uint32_t id, uint32_t dev, uint32_t len,
|
||||||
|
uint32_t flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* caps is a an array of supported capabilities by the implementor.
|
||||||
|
* caps_out is the encoded (network byte order) version of the
|
||||||
|
* same array. It should be sent to the peer after the hello packet.
|
||||||
|
*/
|
||||||
|
size_t rp_encode_hello_caps(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt,
|
||||||
|
uint16_t version_major, uint16_t version_minor,
|
||||||
|
uint32_t *caps, uint32_t *features_out,
|
||||||
|
uint32_t features_len);
|
||||||
|
|
||||||
|
/* rp_encode_hello is deprecated in favor of hello_caps. */
|
||||||
|
static inline size_t __attribute__ ((deprecated))
|
||||||
|
rp_encode_hello(uint32_t id, uint32_t dev, struct rp_pkt_hello *pkt,
|
||||||
|
uint16_t version_major, uint16_t version_minor) {
|
||||||
|
return rp_encode_hello_caps(id, dev, pkt, version_major, version_minor,
|
||||||
|
NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *__attribute__ ((deprecated))
|
||||||
|
rp_busaccess_dataptr(struct rp_pkt_busaccess *pkt)
|
||||||
|
{
|
||||||
|
/* Right after the packet. */
|
||||||
|
return pkt + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rp_busaccess_rx_dataptr
|
||||||
|
*
|
||||||
|
* Predicts the dataptr for a packet to be transmitted.
|
||||||
|
* This should only be used if you are trying to keep
|
||||||
|
* the entire packet in a linear buffer.
|
||||||
|
*/
|
||||||
|
static inline unsigned char *
|
||||||
|
rp_busaccess_tx_dataptr(struct rp_peer_state *peer,
|
||||||
|
struct rp_pkt_busaccess_ext_base *pkt)
|
||||||
|
{
|
||||||
|
unsigned char *p = (unsigned char *) pkt;
|
||||||
|
|
||||||
|
if (peer->caps.busaccess_ext_base) {
|
||||||
|
/* We always put our data right after the header. */
|
||||||
|
return p + sizeof *pkt;
|
||||||
|
} else {
|
||||||
|
/* Right after the old packet layout. */
|
||||||
|
return p + sizeof(struct rp_pkt_busaccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rp_busaccess_rx_dataptr
|
||||||
|
*
|
||||||
|
* Extracts the dataptr from a received packet.
|
||||||
|
*/
|
||||||
|
static inline unsigned char *
|
||||||
|
rp_busaccess_rx_dataptr(struct rp_peer_state *peer,
|
||||||
|
struct rp_pkt_busaccess_ext_base *pkt)
|
||||||
|
{
|
||||||
|
unsigned char *p = (unsigned char *) pkt;
|
||||||
|
|
||||||
|
if (pkt->attributes & RP_BUS_ATTR_EXT_BASE) {
|
||||||
|
return p + pkt->data_offset;
|
||||||
|
} else {
|
||||||
|
/* Right after the old packet layout. */
|
||||||
|
return p + sizeof(struct rp_pkt_busaccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned char *
|
||||||
|
rp_busaccess_byte_en_ptr(struct rp_peer_state *peer,
|
||||||
|
struct rp_pkt_busaccess_ext_base *pkt)
|
||||||
|
{
|
||||||
|
unsigned char *p = (unsigned char *) pkt;
|
||||||
|
|
||||||
|
if ((pkt->attributes & RP_BUS_ATTR_EXT_BASE)
|
||||||
|
&& pkt->byte_enable_len) {
|
||||||
|
assert(pkt->byte_enable_offset >= sizeof *pkt);
|
||||||
|
assert(pkt->byte_enable_offset + pkt->byte_enable_len
|
||||||
|
<= pkt->hdr.len + sizeof pkt->hdr);
|
||||||
|
return p + pkt->byte_enable_offset;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t __attribute__ ((deprecated))
|
||||||
|
rp_encode_read(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_busaccess *pkt,
|
||||||
|
int64_t clk, uint16_t master_id,
|
||||||
|
uint64_t addr, uint64_t attr, uint32_t size,
|
||||||
|
uint32_t width, uint32_t stream_width);
|
||||||
|
|
||||||
|
size_t __attribute__ ((deprecated))
|
||||||
|
rp_encode_read_resp(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_busaccess *pkt,
|
||||||
|
int64_t clk, uint16_t master_id,
|
||||||
|
uint64_t addr, uint64_t attr, uint32_t size,
|
||||||
|
uint32_t width, uint32_t stream_width);
|
||||||
|
|
||||||
|
size_t __attribute__ ((deprecated))
|
||||||
|
rp_encode_write(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_busaccess *pkt,
|
||||||
|
int64_t clk, uint16_t master_id,
|
||||||
|
uint64_t addr, uint64_t attr, uint32_t size,
|
||||||
|
uint32_t width, uint32_t stream_width);
|
||||||
|
|
||||||
|
size_t __attribute__ ((deprecated))
|
||||||
|
rp_encode_write_resp(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_busaccess *pkt,
|
||||||
|
int64_t clk, uint16_t master_id,
|
||||||
|
uint64_t addr, uint64_t attr, uint32_t size,
|
||||||
|
uint32_t width, uint32_t stream_width);
|
||||||
|
|
||||||
|
struct rp_encode_busaccess_in {
|
||||||
|
uint32_t cmd;
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t dev;
|
||||||
|
int64_t clk;
|
||||||
|
uint64_t master_id;
|
||||||
|
uint64_t addr;
|
||||||
|
uint64_t attr;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t stream_width;
|
||||||
|
uint32_t byte_enable_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Prepare encode_busaccess input parameters for a packet response. */
|
||||||
|
static inline void
|
||||||
|
rp_encode_busaccess_in_rsp_init(struct rp_encode_busaccess_in *in,
|
||||||
|
struct rp_pkt *pkt) {
|
||||||
|
memset(in, 0, sizeof *in);
|
||||||
|
in->cmd = pkt->hdr.cmd;
|
||||||
|
in->id = pkt->hdr.id;
|
||||||
|
in->flags = pkt->hdr.flags | RP_PKT_FLAGS_response;
|
||||||
|
in->dev = pkt->hdr.dev;
|
||||||
|
/* FIXME: Propagate all master_id fields? */
|
||||||
|
in->master_id = pkt->busaccess.master_id;
|
||||||
|
in->addr = pkt->busaccess.addr;
|
||||||
|
in->size = pkt->busaccess.len;
|
||||||
|
in->width = pkt->busaccess.width;
|
||||||
|
in->stream_width = pkt->busaccess.stream_width;
|
||||||
|
in->byte_enable_len = 0;
|
||||||
|
}
|
||||||
|
size_t rp_encode_busaccess(struct rp_peer_state *peer,
|
||||||
|
struct rp_pkt_busaccess_ext_base *pkt,
|
||||||
|
struct rp_encode_busaccess_in *in);
|
||||||
|
|
||||||
|
size_t rp_encode_interrupt_f(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_interrupt *pkt,
|
||||||
|
int64_t clk,
|
||||||
|
uint32_t line, uint64_t vector, uint8_t val,
|
||||||
|
uint32_t flags);
|
||||||
|
|
||||||
|
size_t rp_encode_interrupt(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_interrupt *pkt,
|
||||||
|
int64_t clk,
|
||||||
|
uint32_t line, uint64_t vector, uint8_t val);
|
||||||
|
|
||||||
|
size_t rp_encode_sync(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_sync *pkt,
|
||||||
|
int64_t clk);
|
||||||
|
|
||||||
|
size_t rp_encode_sync_resp(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_sync *pkt,
|
||||||
|
int64_t clk);
|
||||||
|
|
||||||
|
size_t rp_encode_ats_req(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_ats *pkt,
|
||||||
|
int64_t clk, uint64_t attr, uint64_t addr,
|
||||||
|
uint64_t size, uint64_t result, uint32_t flags);
|
||||||
|
|
||||||
|
size_t rp_encode_ats_inv(uint32_t id, uint32_t dev,
|
||||||
|
struct rp_pkt_ats *pkt,
|
||||||
|
int64_t clk, uint64_t attr, uint64_t addr,
|
||||||
|
uint64_t size, uint64_t result, uint32_t flags);
|
||||||
|
|
||||||
|
void rp_process_caps(struct rp_peer_state *peer,
|
||||||
|
void *caps, size_t caps_len);
|
||||||
|
|
||||||
|
/* Dynamically resizable remote port pkt. */
|
||||||
|
|
||||||
|
typedef struct RemotePortDynPkt {
|
||||||
|
struct rp_pkt *pkt;
|
||||||
|
size_t size;
|
||||||
|
} RemotePortDynPkt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure dpkt is allocated and has enough room.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void rp_dpkt_alloc(RemotePortDynPkt *dpkt, size_t size);
|
||||||
|
|
||||||
|
void rp_dpkt_swap(RemotePortDynPkt *a, RemotePortDynPkt *b);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the dpkt is valid. Used for debugging purposes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool rp_dpkt_is_valid(RemotePortDynPkt *dpkt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Invalidate the dpkt. Used for debugging purposes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void rp_dpkt_invalidate(RemotePortDynPkt *dpkt);
|
||||||
|
|
||||||
|
void rp_dpkt_free(RemotePortDynPkt *dpkt);
|
||||||
|
|
||||||
|
static inline int rp_get_busaccess_response(struct rp_pkt *pkt)
|
||||||
|
{
|
||||||
|
return (pkt->busaccess_ext_base.attributes & RP_BUS_RESP_MASK) >>
|
||||||
|
RP_BUS_RESP_SHIFT;
|
||||||
|
}
|
||||||
|
#endif
|
115
examples/zynq7000/SIM/tb_cosim.sv
Normal file
115
examples/zynq7000/SIM/tb_cosim.sv
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
module tb_cosim;
|
||||||
|
import "DPI-C" function int start_cosim(string path);
|
||||||
|
import "DPI-C" function void end_cosim();
|
||||||
|
import "DPI-C" function int wait_cosim(output int addr, output int t);
|
||||||
|
import "DPI-C" function void finalize_cosim(int t);
|
||||||
|
import "DPI-C" function void read_cosim(input int value);
|
||||||
|
import "DPI-C" function int write_cosim();
|
||||||
|
|
||||||
|
// Cosimulation variables
|
||||||
|
integer cosim_ret;
|
||||||
|
reg unsigned [31:0] cosim_address;
|
||||||
|
reg unsigned [31:0] cosim_data;
|
||||||
|
integer timestamp;
|
||||||
|
integer curtime;
|
||||||
|
integer started;
|
||||||
|
|
||||||
|
reg ACLK;
|
||||||
|
reg ARESETN;
|
||||||
|
|
||||||
|
wire [1:0] LED;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
ACLK = 1'b0;
|
||||||
|
ARESETN = 0'b0;
|
||||||
|
end
|
||||||
|
|
||||||
|
always #5 ACLK = !ACLK;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$display("Starting testbench");
|
||||||
|
// Start co-simulation
|
||||||
|
if (start_cosim("unix:/tmp/qemu-rport-_cosim@0")>0) begin
|
||||||
|
$display("ERROR: Could not start co-simulation. Stop simulation");
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
$display("Co-simulation started");
|
||||||
|
|
||||||
|
ARESETN = 1'b0;
|
||||||
|
repeat(2)@(posedge ACLK);
|
||||||
|
ARESETN = 1'b1;
|
||||||
|
repeat(2)@(posedge ACLK);
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
cosim_ret = 0;
|
||||||
|
while (cosim_ret>=0) begin
|
||||||
|
cosim_ret = wait_cosim(cosim_address, timestamp);
|
||||||
|
|
||||||
|
// Check for end of simulation
|
||||||
|
if (cosim_address=='h7ffffffc) begin
|
||||||
|
break;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Check for start of simulation
|
||||||
|
if (cosim_address=='h7ffffff8) begin
|
||||||
|
curtime = timestamp;
|
||||||
|
cosim_ret = 0;
|
||||||
|
started = 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Check for pause of simulation
|
||||||
|
if (cosim_address=='h7ffffff4) begin
|
||||||
|
started = 0;
|
||||||
|
finalize_cosim(curtime);
|
||||||
|
end
|
||||||
|
|
||||||
|
if (started==0) begin
|
||||||
|
continue;
|
||||||
|
end
|
||||||
|
|
||||||
|
while(curtime<timestamp) begin
|
||||||
|
@(posedge ACLK);
|
||||||
|
end
|
||||||
|
|
||||||
|
if (cosim_ret==1) begin
|
||||||
|
// WRITE
|
||||||
|
cosim_data = write_cosim();
|
||||||
|
// ADDR = cosim_address;
|
||||||
|
// WRDAT = cosim_data;
|
||||||
|
// WR = 1'b1;
|
||||||
|
@(posedge ACLK);
|
||||||
|
// WR = 1'b0;
|
||||||
|
// while (WRREADY==1'b0) begin
|
||||||
|
// @(posedge ACLK);
|
||||||
|
// end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (cosim_ret==2) begin
|
||||||
|
// READ
|
||||||
|
// ADDR = cosim_address;
|
||||||
|
// RD = 1'b1;
|
||||||
|
@(posedge ACLK);
|
||||||
|
// RD = 1'b0;
|
||||||
|
// while (RDREADY==1'b0) begin
|
||||||
|
// @(posedge ACLK);
|
||||||
|
// end
|
||||||
|
@(negedge ACLK);
|
||||||
|
cosim_data = 'hdeadbeef;
|
||||||
|
read_cosim(cosim_data);
|
||||||
|
end
|
||||||
|
finalize_cosim(curtime);
|
||||||
|
end
|
||||||
|
|
||||||
|
$display("Reached end of simulation. Stop Co-simulation");
|
||||||
|
end_cosim();
|
||||||
|
$stop;
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
heartbeat #(100000000, 10000000) i_heartbeat(
|
||||||
|
ACLK, ARESETN, LED
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
@ -3,6 +3,12 @@
|
|||||||
#include "uart.h"
|
#include "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(;;);
|
||||||
}
|
}
|
@ -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
|
||||||
# ######################################
|
# ######################################
|
@ -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
|
71
remotesyn/toolchains/questa.py
Normal file
71
remotesyn/toolchains/questa.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
from importlib.metadata import files
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
def do(config, target, log, subprocesses, prefix='.'):
|
||||||
|
# shutil.rmtree(config.get('project', 'build_dir', fallback='build'), True)
|
||||||
|
|
||||||
|
log("Starting simulation")
|
||||||
|
|
||||||
|
log(" - parsing options")
|
||||||
|
toplevel = config.get(f'target.{target}', 'toplevel', fallback='toplevel')
|
||||||
|
runtime = config.get(f'target.{target}', 'runtime', fallback='100 ns')
|
||||||
|
files_vhdl = config.get(f'target.{target}', 'files_vhdl', fallback='').split()
|
||||||
|
files_verilog = config.get(f'target.{target}', 'files_verilog', fallback='').split()
|
||||||
|
files_sysverilog = config.get(f'target.{target}', 'files_sysverilog', fallback='').split()
|
||||||
|
files_c = config.get(f'target.{target}', 'files_c', fallback='').split()
|
||||||
|
files_other = config.get(f'target.{target}', 'files_other', fallback='').split()
|
||||||
|
build_dir = config.get(f'project', 'build_dir', fallback='build')
|
||||||
|
out_dir = config.get(f'project', 'out_dir', fallback='out')
|
||||||
|
|
||||||
|
prefix = f'{os.getcwd()}/{prefix}'
|
||||||
|
build_dir = f'{prefix}/{build_dir}'
|
||||||
|
out_dir = f'{prefix}/{out_dir}/{target}'
|
||||||
|
|
||||||
|
log(" - creating output directories")
|
||||||
|
os.makedirs(build_dir, exist_ok=True)
|
||||||
|
os.makedirs(out_dir, exist_ok=True)
|
||||||
|
|
||||||
|
files_c_wp = {f'{prefix}/{f}' for f in files_c}
|
||||||
|
|
||||||
|
log(" - writing compile file")
|
||||||
|
with open(f'{build_dir}/do.sh', 'w') as f:
|
||||||
|
f.write(f'vlib work\nvmap work work\n')
|
||||||
|
for s in files_vhdl:
|
||||||
|
f.write(f"vcom {prefix}/{s}\n")
|
||||||
|
for s in files_verilog:
|
||||||
|
f.write(f"vlog {prefix}/{s}\n")
|
||||||
|
for s in files_sysverilog:
|
||||||
|
f.write(f"vlog -sv {prefix}/{s}\n")
|
||||||
|
f.write(f"gcc -g -fPIC -shared -Bsymbolic -o import.so {' '.join(files_c_wp)}\n")
|
||||||
|
extra = ''
|
||||||
|
if len(files_c_wp)>0:
|
||||||
|
extra = '-sv_lib import'
|
||||||
|
f.write(f"vsim -c -do do.do {extra} {toplevel}")
|
||||||
|
|
||||||
|
log(" - writing do file")
|
||||||
|
with open(f'{build_dir}/do.do', 'w') as f:
|
||||||
|
f.write("vcd file out.vcd\n vcd add *\nrun -all\nquit\n");
|
||||||
|
|
||||||
|
log(" - run vsim")
|
||||||
|
p = subprocess.Popen(f"bash ./do.sh 2>&1 | tee do.log",
|
||||||
|
shell=True, cwd=build_dir,
|
||||||
|
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
subprocesses.append(p)
|
||||||
|
while p.poll() is None:
|
||||||
|
time.sleep(1)
|
||||||
|
res = p.returncode
|
||||||
|
|
||||||
|
log(" - copy logs")
|
||||||
|
shutil.copy(f'{build_dir}/do.log', f'{out_dir}/do.log')
|
||||||
|
|
||||||
|
if res!=0:
|
||||||
|
log("ERROR: vsim returned with", res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
log(" - copy output files")
|
||||||
|
shutil.copy(f'{build_dir}/out.vcd', f'{out_dir}/out.vcd')
|
||||||
|
|
||||||
|
return 0
|
@ -16,6 +16,7 @@ def do(config, target, log, subprocesses, prefix='.'):
|
|||||||
files_verilog = config.get(f'target.{target}', 'files_verilog', fallback='').split()
|
files_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)
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user