-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsoftswitch_main.cpp
More file actions
302 lines (248 loc) · 12.1 KB
/
Copy pathsoftswitch_main.cpp
File metadata and controls
302 lines (248 loc) · 12.1 KB
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#include "tinsel_api.hpp"
#include "hostMsg.hpp"
#include "softswitch.hpp"
#include "softswitch_hostmessaging.hpp"
//#include <cstdio>
#include <cstdarg>
#ifdef SOFTSWITCH_ENABLE_PROFILE
#include "softswitch_perfmon.hpp"
#else
// typedef unsigned long size_t;
#endif
//! Initialise data-structures (e.g. RTS)
extern "C" void softswitch_init(PThreadContext *ctxt);
//! Do some idle work. Return false if there is nothing more to do
extern "C" bool softswitch_onIdle(PThreadContext *ctxt);
//! Deal with the incoming message
extern "C" void softswitch_onReceive(PThreadContext *ctxt, const void *message);
//! Prepare a message on the given buffer
/*! \param numTargets Receives the number of addresses to send the packet to
\param pTargets If numTargets>0, this will be pointed at an array with numTargets elements
\retval Size of message in bytes
*/
extern "C" unsigned softswitch_onSend(PThreadContext *ctxt, void *message, uint32_t &numTargets, const address_t *&pTargets, uint8_t *isApp);
//! Complete a hardware step
extern "C" void softswitch_on_hardware_idle(PThreadContext *ctxt);
extern "C" void *memset( void *dest, int ch, size_t count );
//! functions used to manage the hostmessaging buffer
extern "C" uint32_t hostMsgBufferSize();
extern "C" uint32_t hostMsgBufferSpace();
extern "C" void hostMsgBufferPush(hostMsg *msg);
static PThreadContext *softswitch_getContext()
{
if(tinsel_myId() < softswitch_pthread_count){
return softswitch_pthread_contexts + tinsel_myId();
}else{
return 0;
}
}
#ifdef SOFTSWITCH_ENABLE_INTRA_THREAD_SEND
const int enableIntraThreadSend=1;
#else
const int enableIntraThreadSend=0;
#endif
#ifdef SOFTSWITCH_ENABLE_SEND_OVER_RECV
const int enableSendOverRecv=1;
#else
const int enableSendOverRecv=0;
#endif
extern "C" void softswitch_main()
{
PThreadContext *ctxt=softswitch_getContext();
if(ctxt==0 || ctxt->numDevices==0){
while(1){
tinsel_mboxIdle(false);
}
}
unsigned thisThreadId=tinsel_myId();
// Create a software buffer for the host messages
hostMsg hostMsgBuffer[HOSTBUFFER_SIZE];
ctxt->hostBuffer = hostMsgBuffer;
ctxt->hbuf_head = 0; // head of the host message buffer
ctxt->hbuf_tail = 0; // tail of the host message buffer
softswitch_softswitch_log(1, "softswitch_main()");
softswitch_init(ctxt);
// We'll hold onto this one
volatile void *sendBuffer=tinsel_mboxSendSlot();
// If a send is in progress, this will be non-null
const address_t *currSendAddressList=0;
uint32_t currSendTodo=0;
uint32_t currSize=0;
uint8_t isApp=0; // is 1 if we are sending on an application pin
softswitch_softswitch_log(2, "start loop");
#ifdef SOFTSWITCH_ENABLE_PROFILE
ctxt->thread_cycles_tstart = tinsel_CycleCount();
bool start_perfmon = false;
unsigned perfmon_flush_rate_cnt = 1;
const unsigned perfmon_flush_rate = SOFTSWITCH_ENABLE_PROFILE;
#endif
while(1) {
softswitch_softswitch_log(3, "Loop top");
// true if there is enough space in the host buffer to store the handlers
// if this is false then draining the hostbuffer is a priority
bool adequateHostBufferSpace = (hostMsgBufferSpace() >= MAX_HOST_PER_HANDLER);
// true if there are NO items in the host buffer
bool hostBufferEmpty = (hostMsgBufferSize() == 0);
// Only want to send if either:
// - we are currently sending message,
// - or at least one device wants to send one
// - or we need to send to the host
bool wantToSend = (currSendTodo>0) || softswitch_IsRTSReady(ctxt) || !hostBufferEmpty;
softswitch_softswitch_log(3, "wantToSend=%d", wantToSend?1:0);
// Run idle if:
// - There is nothing to receive
// - we aren't able to send or we don't want to send
#ifdef SOFTSWITCH_ENABLE_PROFILE
uint32_t idle_start = tinsel_CycleCount();
#endif
if( (!tinsel_mboxCanRecv()) && (!wantToSend || !tinsel_mboxCanSend()) ){
if(!softswitch_onIdle(ctxt)) {
#ifdef SOFTSWITCH_ENABLE_PROFILE
if(start_perfmon) {
ctxt->idle_cycles += deltaCycles(idle_start, tinsel_CycleCount());
if(perfmon_flush_rate_cnt < perfmon_flush_rate) {
perfmon_flush_rate_cnt = perfmon_flush_rate_cnt + 1;
} else {
ctxt->thread_cycles = deltaCycles(ctxt->thread_cycles_tstart, tinsel_CycleCount());
volatile uint32_t thread_start = tinsel_CycleCount();
ctxt->thread_cycles_tstart = thread_start;
// check if there is space in the host buffer
if(!hostMsgBufferSpace()) { // if not flush an element of the buffer over UART
hostMessageSlowPopSend(); // clear space
}
softswitch_flush_perfmon(); // add the perfmon data to host message buffer
ctxt->perfmon_cycles = deltaCycles(thread_start, tinsel_CycleCount());
perfmon_flush_rate_cnt = 1;
}
}
start_perfmon = true;
#endif
}else{
continue;
}
}
uint32_t wakeupFlags = wantToSend ? (tinsel_CAN_RECV|tinsel_CAN_SEND) : tinsel_CAN_RECV;
softswitch_softswitch_log(4, "waiting for send=%d, recv=%d", wakeupFlags&tinsel_CAN_SEND, (wakeupFlags&tinsel_CAN_RECV)?1:0);
#ifdef SOFTSWITCH_ENABLE_PROFILE
uint32_t blocked_start = tinsel_CycleCount();
#endif
#if SOFTSWITCH_ENABLE_HARDWARE_IDLE
bool doHardwareIdle=false;
if(!wantToSend){
for(unsigned i=0; i<ctxt->numDevices; i++){
assert(ctxt->devices[i].rtsFlags==0);
}
bool vote=false;
doHardwareIdle=tinsel_mboxIdle(vote);
}else{
tinsel_mboxWaitUntil( (tinsel_WakeupCond) (tinsel_CAN_RECV|tinsel_CAN_SEND) );
}
#else
tinsel_mboxWaitUntil( (tinsel_WakeupCond) wakeupFlags );
#endif
#ifdef SOFTSWITCH_ENABLE_PROFILE
if(wantToSend) {
ctxt->send_blocked_cycles += deltaCycles(blocked_start, tinsel_CycleCount());
} else {
ctxt->recv_blocked_cycles += deltaCycles(blocked_start, tinsel_CycleCount());
}
#endif
#if SOFTSWITCH_ENABLE_HARDWARE_IDLE
if(doHardwareIdle){
softswitch_on_hardware_idle(ctxt);
continue;
}
#endif
bool doRecv=false;
bool doSend=false;
while(!adequateHostBufferSpace) { //Drain the host buffer over UART to prevent deadlock
hostMessageSlowPopSend();
adequateHostBufferSpace = (hostMsgBufferSpace() >= MAX_HOST_PER_HANDLER);
}
if(enableSendOverRecv){
doSend=tinsel_mboxCanSend() && wantToSend;
if(!doSend){
assert(tinsel_mboxCanRecv());
doRecv=true;
}
}else{
// Default. Drain before fill.
doRecv=tinsel_mboxCanRecv() && adequateHostBufferSpace;
if(!doRecv){
assert(tinsel_mboxCanSend() && wantToSend);
doSend=true;
}
}
if(doRecv){
softswitch_softswitch_log(2, "receive branch");
assert(tinsel_mboxCanRecv());
/*! We always receive messages, even while a send is in progress (currSendTodo > 0) */
volatile void* recvBuffer;
softswitch_softswitch_log(4, "calling mboxRecv");
recvBuffer=tinsel_mboxRecv(); // Take the buffer from receive pool
assert(recvBuffer);
softswitch_onReceive(ctxt, (const void *)recvBuffer); // Decode and dispatch
softswitch_softswitch_log(4, "giving buffer back");
tinsel_mboxFree(recvBuffer); // Put it back in receive pool
}
if(doSend){
softswitch_softswitch_log(3, "send branch");
assert(wantToSend); // Only come here if we have something to do
assert(tinsel_mboxCanSend()); // Only reason we could have got here
// first clear any host messages
if(!hostBufferEmpty) {
hostMessageBufferPopSend();
} else {
/* Either we have to finish sending a previous message to more
addresses, or we get the chance to send a new message.
or we need to send a message to the host */
if(currSendTodo==0){
softswitch_softswitch_log(3, "preparing new message");
// Prepare a new packet to send
currSize=softswitch_onSend(ctxt, (void*)sendBuffer, currSendTodo, currSendAddressList, &isApp);
ctxt->currentSize = currSize; // update the current size in the thread context
}else{
// We still have more addresses to deliver the last message to
softswitch_softswitch_log(3, "forwarding current message");
}
if(currSendTodo>0){
//assert(currSendAddressList);
softswitch_softswitch_log(3, "sending to thread %x, device %u, pin %u", currSendAddressList->thread, currSendAddressList->device, currSendAddressList->pin);
// Update the target address (including the device and pin)
static_assert(sizeof(address_t)==8);
if(isApp) { // we are sending an application message there is no destination
//*(uint64_t*)&((packet_header_t*)sendBuffer)->dest = tinselHostId();
} else { // we are sending a regular message
// This wierdness is to avoid the compiler turning it into a call to memcpy
*(uint64_t*)&((packet_header_t*)sendBuffer)->dest = *(uint64_t*)currSendAddressList;
}
if(enableIntraThreadSend && currSendAddressList->thread==thisThreadId && adequateHostBufferSpace){
// Deliver on this thread without waiting
softswitch_onReceive(ctxt, (const void *)sendBuffer); // Decode and dispatch
}else{
// Send to the relevant thread
// TODO: Shouldn't there be something like mboxForward as part of
// the API, which only takes the address?
softswitch_softswitch_log(3, "setting length to %u", currSize);
tinsel_mboxSetLen(currSize);
if(isApp) { // an application message
softswitch_softswitch_log(3, "doing application send");
tinsel_mboxSetLen(64);
tinsel_mboxSend(tinselHostId(), sendBuffer);
currSendTodo = 0;
} else { // not an application message
softswitch_softswitch_log(3, "doing send");
tinsel_mboxSend(currSendAddressList->thread, sendBuffer);
}
}
if(!isApp) { // we are only sending to multiple addresses if it is not an application message
// Move onto next address for next time
currSendTodo--; // If this reaches zero, we are done with the message
currSendAddressList++;
}
}
}
} //doSend
softswitch_softswitch_log(3, "loop bottom");
}
}