1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
/*
* Copyright (C) 2014 Nuand LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STREAMING_METADATA_H_
#define STREAMING_METADATA_H_
/*
* Metadata layout
* ~~~~~~~~~~~~~~~~~~~~~~~
*
* The FPGA handles data in units of "messages." These messages are
* 1024 or 2048 bytes for USB 2.0 (Hi-Speed) or USB 3.0 (SuperSpeed),
* respectively.
*
* The first 16 bytes of the message form a header, which includes metadata
* for the samples within the message. This header is shown below:
*
* +-----------------+
* 0x00 | Packet length | 2 bytes, Little-endian uint16_t
* +-----------------+
* 0x02 | Packet flags | 1 byte
* +-----------------+
* 0x03 | Packet core ID | 1 byte
* +-----------------+
* 0x04 | Timestamp | 8 bytes, Little-endian uint64_t
* +-----------------+
* 0x0c | Flags | 4 bytes, Little-endian uint32_t
* +-----------------+
*
* The term "buffer" is used to describe a block of of data received from or
* sent to the device. The size of a "buffer" (in bytes) is always a multiple
* of the size of a "message." Said another way, a buffer will always evenly
* divide into multiple messages. Messages are *not* fragmented across
* consecutive buffers.
*
* +-----------------+ <-. <-.
* | header | | |
* +-----------------+ | |
* | | | |
* | samples | | |
* | | | |
* +-----------------+ | <-+---- message
* | header | |
* +-----------------+ |
* | | |
* | samples | |
* | | |
* +-----------------+ |
* | header | |
* +-----------------+ |
* | | |
* | samples | |
* | | |
* +-----------------+ |
* | header | |
* +-----------------+ |
* | | |
* | samples | |
* | | |
* +-----------------+ <-+---------- buffer
*
*
* When intentionally transmitting discontinuous groups of samples (such
* as bursts), it is important that the last two samples within a message
* be (0 + 0j). Otherwise, the DAC will not properly hold its output
* at (0 + 0j) for the duration of the discontinuity.
*/
/* Components of the metadata header */
#define METADATA_RESV_SIZE (sizeof(uint32_t))
#define METADATA_TIMESTAMP_SIZE (sizeof(uint64_t))
#define METADATA_FLAGS_SIZE (sizeof(uint32_t))
#define METADATA_PACKET_LEN_SIZE (sizeof(uint16_t))
#define METADATA_PACKET_CORE_SIZE (sizeof(uint8_t))
#define METADATA_PACKET_FLAGS_SIZE (sizeof(uint8_t))
#define METADATA_RESV_OFFSET 0
#define METADATA_PACKET_LEN_OFFSET 0
#define METADATA_PACKET_FLAGS_OFFSET 2
#define METADATA_PACKET_CORE_OFFSET 3
#define METADATA_TIMESTAMP_OFFSET (METADATA_RESV_SIZE)
#define METADATA_FLAGS_OFFSET \
(METADATA_TIMESTAMP_OFFSET + METADATA_TIMESTAMP_SIZE)
#define METADATA_HEADER_SIZE (METADATA_FLAGS_OFFSET + METADATA_FLAGS_SIZE)
static inline uint64_t metadata_get_timestamp(const uint8_t *header)
{
uint64_t ret;
assert(sizeof(ret) == METADATA_TIMESTAMP_SIZE);
memcpy(&ret, &header[METADATA_TIMESTAMP_OFFSET], METADATA_TIMESTAMP_SIZE);
ret = LE64_TO_HOST(ret);
return ret;
}
static inline uint32_t metadata_get_flags(const uint8_t *header)
{
uint32_t ret;
assert(sizeof(ret) == METADATA_FLAGS_SIZE);
memcpy(&ret, &header[METADATA_FLAGS_OFFSET], METADATA_FLAGS_SIZE);
return LE32_TO_HOST(ret);
}
static inline uint16_t metadata_get_packet_len(const uint8_t *header)
{
uint16_t ret;
assert(sizeof(ret) == METADATA_PACKET_LEN_SIZE);
memcpy(&ret, &header[METADATA_PACKET_LEN_OFFSET], METADATA_PACKET_LEN_SIZE);
return LE16_TO_HOST(ret);
}
static inline uint8_t metadata_get_packet_core(const uint8_t *header)
{
uint8_t ret;
assert(sizeof(ret) == METADATA_PACKET_CORE_SIZE);
memcpy(&ret, &header[METADATA_PACKET_CORE_OFFSET], METADATA_PACKET_CORE_SIZE);
return ret;
}
static inline uint8_t metadata_get_packet_flags(const uint8_t *header)
{
uint8_t ret;
assert(sizeof(ret) == METADATA_PACKET_FLAGS_SIZE);
memcpy(&ret, &header[METADATA_PACKET_FLAGS_OFFSET], METADATA_PACKET_FLAGS_SIZE);
return ret;
}
static inline void metadata_set_packet(uint8_t *header,
uint64_t timestamp,
uint32_t flags,
uint16_t length,
uint8_t core,
uint8_t pkt_flags)
{
timestamp = HOST_TO_LE64(timestamp);
flags = HOST_TO_LE32(flags);
length = HOST_TO_LE16(length);
assert(sizeof(timestamp) == METADATA_TIMESTAMP_SIZE);
assert(sizeof(flags) == METADATA_FLAGS_SIZE);
memset(&header[METADATA_RESV_OFFSET], 0, METADATA_RESV_SIZE);
memcpy(&header[METADATA_PACKET_LEN_OFFSET], &length, METADATA_PACKET_LEN_SIZE);
memcpy(&header[METADATA_PACKET_CORE_OFFSET], &core, METADATA_PACKET_CORE_SIZE);
memcpy(&header[METADATA_PACKET_FLAGS_OFFSET], &pkt_flags, METADATA_PACKET_FLAGS_SIZE);
memcpy(&header[METADATA_TIMESTAMP_OFFSET], ×tamp,
METADATA_TIMESTAMP_SIZE);
memcpy(&header[METADATA_FLAGS_OFFSET], &flags, METADATA_FLAGS_SIZE);
}
static inline void metadata_set(uint8_t *header,
uint64_t timestamp,
uint32_t flags)
{
metadata_set_packet(header, timestamp, flags, 0, 0, 0);
}
#endif
|