OpenCores
URL https://opencores.org/ocsvn/s6soc/s6soc/trunk

Subversion Repositories s6soc

[/] [s6soc/] [trunk/] [sw/] [zipos/] [syspipe.c] - Blame information for rev 27

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 22 dgisselq
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Filename:    syspipe.c
4
//
5
// Project:     CMod S6 System on a Chip, ZipCPU demonstration project
6
//
7
// Purpose:     This "device" handles the primary device level interaction of
8
//              almost all devices on the ZipOS: the pipe.  A pipe, as defined
9
//      here, is an O/S supported FIFO.  Information written to the FIFO will
10
//      be read from the FIFO in the order it was received.  Attempts to read
11
//      from an empty FIFO, or equivalently to write to a full FIFO, will block
12
//      the reading (writing) process until memory is available.
13
//
14
// Creator:     Dan Gisselquist, Ph.D.
15
//              Gisselquist Technology, LLC
16
//
17
////////////////////////////////////////////////////////////////////////////////
18
//
19
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
20
//
21
// This program is free software (firmware): you can redistribute it and/or
22
// modify it under the terms of  the GNU General Public License as published
23
// by the Free Software Foundation, either version 3 of the License, or (at
24
// your option) any later version.
25
//
26
// This program is distributed in the hope that it will be useful, but WITHOUT
27
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
28
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
29
// for more details.
30
//
31
// You should have received a copy of the GNU General Public License along
32
// with this program.  (It's in the $(ROOT)/doc directory, run make with no
33
// target there if the PDF file isn't present.)  If not, see
34
// <http://www.gnu.org/licenses/> for a copy.
35
//
36
// License:     GPL, v3, as defined and found on www.gnu.org,
37
//              http://www.gnu.org/licenses/gpl.html
38
//
39
//
40
////////////////////////////////////////////////////////////////////////////////
41
//
42
//
43
#include "errno.h"
44
#include "board.h"
45
#include "taskp.h"
46
#include "syspipe.h"
47
#include "zipsys.h"
48
#include "ktraps.h"
49
 
50
#ifndef NULL
51
#define NULL    (void *)0
52
#endif
53
 
54
void    kpush_syspipe(SYSPIPE *pipe, int val) {
55
        int     tst = (pipe->m_head+1)&pipe->m_mask;
56
        if (tst != pipe->m_tail) {
57
                pipe->m_buf[pipe->m_head] = val;
58
                pipe->m_head = tst;             // Increment the head pointer
59
                if ((pipe->m_rdtask)&&(pipe->m_rdtask != INTERRUPT_READ_TASK))
60
                        pipe->m_rdtask->state = SCHED_READY;
61
        } else pipe->m_error = 1;
62
}
63
 
64 27 dgisselq
extern  void    pipe_panic(SYSPIPE *p);
65 22 dgisselq
int     kpop_syspipe(SYSPIPE *pipe, int *vl) {
66
        if (pipe->m_head != pipe->m_tail) {
67
                *vl = pipe->m_buf[pipe->m_tail];
68 27 dgisselq
                pipe->m_tail = (pipe->m_tail+1)&pipe->m_mask;
69 22 dgisselq
                if (pipe->m_wrtask)
70
                        pipe->m_wrtask->state = SCHED_READY;
71
                return 0;
72 27 dgisselq
        }
73
        return 1; // Error condition
74 22 dgisselq
}
75
 
76 27 dgisselq
/* Returns how many values are in the pipe
77
 */
78
/* Of course ... if it's not used, why include it?
79 22 dgisselq
int     len_syspipe(SYSPIPE *p) {
80
        return (p->m_head-p->m_tail) & p->m_mask;
81 27 dgisselq
} */
82
/* Returns how many empty spaces are in the pipe
83
 */
84 22 dgisselq
int     num_avail_syspipe(SYSPIPE *p) {
85 27 dgisselq
        // if (head+1 == tail)  PIPE is full
86
        //      (mask+tail-tail+1)=mask+1 &mask = 0
87
        // if (head == tail) PIPE is empty
88
        //      (mask+tail-tail)=mask & mask = mask
89
        // if (head == tail+2) PIPE has two within it
90
        //      (mask+tail-tail-2)=mask-2 & mask = mask-2
91
        //
92
        return (p->m_tail-p->m_head-1) & p->m_mask;
93 22 dgisselq
}
94
 
95
// This will be called from a user context.
96
// Another task may write to the pipe during this call.  If the pipe becomes
97
// full, that task will block.
98
//
99
static int      uread_syspipe(TASKP tsk __attribute__((__unused__)), SYSPIPE *p, int *dst, int len) {
100
        int nleft= len, h;
101
        if (len == 0) {
102
                // We'll only get here if we were released from within a 
103
                // writing task.
104
                return p->m_nread;
105
        } else do {
106
                // We have a valid read request, for a new process.  Continue
107
                // 'reading' until we have fulfilled the request.
108
                //
109
                // We can read from head, just not write to it
110
                // As for the tail pointer -- we own it, no one else can touch
111
                // it.
112
                h = ((volatile SYSPIPE *)p)->m_head;
113
                if (h < p->m_tail) {
114
                        // The buffer wraps around the end.  Thus, we first
115
                        // read anything between the tail pointer and the end
116
                        int ln1 = p->m_mask+1 - p->m_tail; // Navail to be read
117
                        ln1 = (ln1 > nleft) ? nleft : ln1;
118
                        if (ln1 > 0) {
119
                                register int *src = &p->m_buf[p->m_tail];
120
                                for(int i=0; i<ln1; i++)
121
                                        *dst++ = *src++;
122
 
123
                                p->m_nread += ln1;
124
                                nleft -= ln1;
125 27 dgisselq
 
126
                                int nt = p->m_tail+ln1;
127
                                if ((unsigned)nt > p->m_mask)
128
                                        nt = 0;
129
                                p->m_tail = nt;
130 22 dgisselq
                        }
131
 
132
                        // nleft is either zero, or tail
133
                        if (nleft & -2)
134
                                exit(nleft);
135
                        else if (p->m_nread & -2)
136
                                exit(p->m_nread);
137
                }
138
 
139
                // Then repeat with the second half of the buffer, from the
140
                // beginning to the head--unless we've exhausted our buffer.
141
                if (nleft > 0) {
142
                        // Still need to do more, wrap around our buffer and
143
                        // restart
144
                        int ln1 = h - p->m_tail;
145
                        ln1 = (ln1 < nleft) ? ln1 : nleft;
146
 
147
                        int *src = &p->m_buf[p->m_tail];
148
                        for(int i=0; i<ln1; i++)
149
                                *dst++ = *src++;
150
 
151
                        p->m_nread += ln1;
152
                        nleft -= ln1;
153 27 dgisselq
                        int nt = p->m_tail+ln1; // nt = new tail value
154
                        if ((unsigned)nt > p->m_mask)
155
                                nt = 0;
156
                        p->m_tail = nt;
157 22 dgisselq
 
158
                        if (nleft & -2)
159
                                exit(nleft);
160
                        else if (p->m_nread & -2)
161
                                exit(p->m_nread);
162
                }
163
 
164
                if (nleft == 0)
165
                        break;
166
 
167
                // We didn't finish our read, check for a blocked writing
168
                // process to copy directly from.  Note that we don't need
169
                // to check the status of the write task--if it is set and
170
                // we are active, then it is blocked and waiting for us to
171
                // complete.  Note also that this is treated as a volatile
172
                // pointer.  It can change from one time through our loop
173
                // to the next.
174
                if (((volatile SYSPIPE *)p)->m_wrtask) {
175
                        int     *src, ln;
176
 
177
                        // If the head changed before the write task blocked,
178
                        // then go around again and copy some more before
179
                        // getting started
180
                        //
181
                        // This should never happen, however.  If a write task
182
                        // gets assigned while a read task exists, it doesn't
183
                        // write its values into the buffer, it just waits.
184
                        // therefore we don't need to check for this.
185
                        //
186
                        // if (p->m_head != h)
187
                        //      continue;
188
 
189
                        ln = nleft;
190
                        if (p->m_wrtask->context[4] < nleft)
191
                                ln = p->m_wrtask->context[4];
192
                        src = (int *)p->m_wrtask->context[3];
193
 
194
                        for(int i=0; i<ln; i++)
195
                                *dst++ = *src++;
196
 
197
                        p->m_nwritten += ln;
198
                        p->m_nread    += ln;
199
 
200
                        nleft -= ln;
201
                        p->m_wrtask->context[4] -= ln;
202
                        p->m_wrtask->context[3]  = (int)src;
203
 
204
                        // We have exhausted the write task.  Release it
205
                        if (p->m_wrtask->context[4] == 0) { // wr_len == 0
206
                                // Release the write task, it has exhausted
207
                                // its buffer
208
                                TASKP   w = p->m_wrtask;
209
                                // Now we allow other tasks to write into our
210
                                // pipe
211
                                p->m_wrtask = 0;
212
                                // And here we actually release the writing
213
                                // task
214
                                w->state = SCHED_READY;
215
                        }
216
                }
217
 
218
                // Realistically, we need to block here 'till more data is
219
                // available.  Need to determine how to do that.  Until then,
220
                // we'll just tell the scheduler to yield.  This will in
221
                // effect create a busy wait--not what we want, but it'll work.
222
                if (nleft > 0) {
223
                        DISABLE_INTS();
224
                        h = ((volatile SYSPIPE *)p)->m_head;
225
                        if (h == p->m_tail)
226
                                wait(0,-1);
227
                        else
228
                                ENABLE_INTS();
229
                }
230
        } while(nleft > 0);
231
 
232
        len = p->m_nread;
233
        p->m_nread = 0;
234
        // Release our ownership of the read end of the pipe
235
        DISABLE_INTS();
236
        p->m_rdtask = NULL;
237
        if (((volatile SYSPIPE *)p)->m_wrtask)
238
                p->m_wrtask->state = SCHED_READY;
239
        ENABLE_INTS();
240
 
241
        // We have accomplished our read
242
        //
243
        return len;
244
}
245
 
246 27 dgisselq
static int      uwrite_syspipe(TASKP tsk __attribute__((__unused__)),
247
                SYSPIPE *p, int *src, int len) {
248 22 dgisselq
        int nleft = len;
249
 
250
        // The kernel guarantees, before we come into here, that we have a 
251
        // valid write request.  
252
        do {
253
                // We try to fill this request without going through the pipes
254
                // memory at all.  Hence, if there is a read task that is
255
                // waiting/suspended, waiting on a write (this must've happened
256
                // since we started)--write directly into the read buffer first.
257
 
258
                // If there is a read task blocked, the pipe must be empty
259
                TASKP rdtask = ((volatile SYSPIPE *)p)->m_rdtask;
260
                if (rdtask == INTERRUPT_READ_TASK) {
261
                        // We need to copy everything to the buffer
262
                } else if (rdtask) {
263
                        int ln = nleft;
264
                        if (ln > p->m_rdtask->context[4])
265
                                ln = p->m_rdtask->context[4];
266
                        int *dst = (int *)p->m_rdtask->context[3];
267
                        for(int i=0; i<ln; i++)
268
                                *dst++ = *src++;
269
                        p->m_nread += ln;
270
                        p->m_rdtask->context[3]+= ln;
271
                        p->m_rdtask->context[4]-= ln;
272
                        nleft -= ln;
273
                        p->m_nwritten += ln;
274
 
275
                        // Realistically, we always need to wake up the reader
276
                        // at this point.  Either 1) we exhausted the readers
277
                        // buffer, or 2) we exhausted our own and the reader
278
                        // needs to take over.  Here, we only handle the first
279
                        // case, leaving the rest for later.
280
                        if (p->m_rdtask->context[4] == 0) {
281
                                TASKP   r = p->m_rdtask;
282
                                // Detach the reader task
283
                                p->m_rdtask = 0;
284
                                // Wake up the reader
285
                                r->state = SCHED_READY;
286
                        }
287
 
288
                        // While it might appear that we might close our loop
289
                        // here, that's not quite the case.  It may be that the
290
                        // pipe is read from an interrupt context.  In that
291
                        // case, there will never be any reader tasks, but we
292
                        // will still need to loop.
293
 
294
                        // Now that we've filled any existing reader task, we
295
                        // check whether or not we fit into the buffer.  The
296
                        // rule is: don't write into the buffer unless
297
                        // everything will fit.  Why?  Well, if you have to
298
                        // block anyway, why not see if you can't avoid a
299
                        // double copy?
300
                        if (nleft == 0)
301
                                break;
302
                }
303
 
304
                // Copy whatever we have into the pipe's buffer
305 27 dgisselq
                int     navail = num_avail_syspipe(p);
306
                if ((nleft <= navail)
307
                        ||((rdtask == INTERRUPT_READ_TASK)&&(navail>0))) {
308 22 dgisselq
                        // Either there is no immediate reader task, or
309
                        // the reader has been exhausted, but we've go
310
                        // more to write.
311
                        //
312
                        // Note that we no longer need to check what
313
                        // will fit into the pipe.  We know the entire
314
                        // rest of our buffer will fit.
315
 
316
                        { // Write into the first half of the pipe
317 27 dgisselq
                        // Be careful not to change head until all is written
318
                        // so that it remains consistent under interrupt
319
                        // conditions.
320 22 dgisselq
                                int ln = p->m_mask+1-p->m_head;
321
                                int *dst = &p->m_buf[p->m_head];
322
                                if (ln > nleft) ln = nleft;
323 27 dgisselq
                                if (ln > navail) ln = navail;
324 22 dgisselq
 
325
                                for(int i=0; i<ln; i++)
326
                                        *dst++ = *src++;
327
 
328 27 dgisselq
                                p->m_head = (p->m_head+ln)&p->m_mask;
329 22 dgisselq
                                nleft -= ln;
330
                                p->m_nwritten += ln;
331 27 dgisselq
                                navail -= ln;
332 22 dgisselq
                        }
333
 
334 27 dgisselq
                        /*
335 22 dgisselq
                        // Write into the rest of the pipe
336 27 dgisselq
                        if ((0 == p->m_head)&&(nleft>0)&&(navail>0)) {
337
                                int ln = navail;
338 22 dgisselq
                                if (nleft < ln)
339
                                        ln = nleft;
340
                                int *dst = &p->m_buf[p->m_head];
341
 
342
                                for(int i=0; i<ln; i++)
343
                                        *dst++ = *src++;
344
 
345
                                p->m_head += ln;
346
                                p->m_nwritten += ln;
347
                                nleft -= ln;
348 27 dgisselq
                        }*/
349 22 dgisselq
                }
350
 
351 27 dgisselq
                if ((nleft > 0)&&(navail == 0)) {
352 22 dgisselq
                        if (rdtask == INTERRUPT_READ_TASK) {
353
                                DISABLE_INTS();
354 27 dgisselq
                                if (0==num_avail_syspipe(p))
355 22 dgisselq
                                        wait(0,-1);
356
                                else ENABLE_INTS();
357
                        } else {
358
                                DISABLE_INTS();
359
                                if (!((volatile SYSPIPE *)p)->m_rdtask)
360
                                        wait(0,-1); // Should really be a wait
361
                                        // on JIFFIES and if JIFFIES expired
362
                                        // (i.e. write timeout) then break;
363
                                else ENABLE_INTS();
364
                        }
365
                }
366
        } while(nleft > 0);
367
 
368
        int     nw= p->m_nwritten;
369
        p->m_wrtask = 0;
370
        return nw;
371
}
372
 
373
// This will be called from a kernel (interrupt) context
374
void    kread_syspipe(TASKP tsk, int dev, int *dst, int len) {
375
        SYSPIPE *p = (SYSPIPE *)dev;
376
        if (p->m_rdtask != NULL) {
377
                // If the pipe already has a read task, then we fail
378
                tsk->context[1] = -EBUSY;
379
                zip_halt();
380
        } else if (p->m_error) {
381
                // If there's been an overrun, let the reader know on the
382
                // next read--i.e. this one.  Also, clear the error condition
383
                // so that the following read will succeed.
384
                tsk->context[1] = -EIO;
385
                p->m_tail = p->m_head;
386
                p->m_error = 0;
387
        } else if (len <= 0) {
388
                tsk->context[1] = -EFAULT;
389
                zip_halt();
390
        } else if (!valid_ram_region(dst, len)) {
391
                // Bad parameters
392
                tsk->context[1] = -EFAULT;
393
                zip_halt();
394
        } else {
395
                // Take  ownership of the read end of the pipe
396
                p->m_rdtask = tsk;
397
                p->m_nread  = 0;
398
                tsk->context[1] = (int)tsk;
399
                tsk->context[2] = (int)p;
400
                // These are  already set, else we'd set them again
401
                // tsk->context[3] = (int)dst;
402
                // tsk->context[4] = len;
403
                tsk->context[15] = (int)uread_syspipe;
404
 
405
                // If there is already a write task, make sure it is awake
406
                if (p->m_wrtask) {
407
                        tsk->state = SCHED_WAITING;
408
                        p->m_wrtask->state = SCHED_READY;
409
                } else if (p->m_head == p->m_tail)
410
                        // If the pipe is empty, block the read task
411
                        tsk->state = SCHED_WAITING;
412
 
413
                // On return, this will bring us back to user space, inside our
414
                // user space version of the read system call
415
        }
416
}
417
 
418
void    kwrite_syspipe(TASKP tsk, int dev, int *src, int len) {
419
        SYSPIPE *p = (SYSPIPE *)dev;
420
        if (p->m_wrtask != NULL) {
421
                // If the pipe already has a write task, then we fail
422
                tsk->context[1] = -EBUSY;
423
        } else if (len <= 0) {
424
                tsk->context[1] = -EFAULT;
425
        } else if (!valid_mem_region(src, len)) {
426
                // Bad parameters
427
                tsk->context[1] = -EFAULT;
428
                zip_halt();
429
        } else {
430
                // Take  ownership of the write end of the pipe
431
                p->m_wrtask    = tsk;
432
                p->m_nwritten = 0;
433
                tsk->context[1] = (int)tsk;
434
                tsk->context[2] = (int)p;
435
                // These are  already set, else we'd set them again
436
                // tsk->context[3] = (int)src;
437
                // tsk->context[4] = len;
438
                tsk->context[15] = (int)uwrite_syspipe;
439
 
440
                // If a reader task currently exists, then block until that
441
                // task either finishes or releases us
442
                if ((p->m_rdtask)&&(p->m_rdtask != INTERRUPT_READ_TASK)) {
443
                        tsk->state = SCHED_WAITING;
444
                        p->m_rdtask->state = SCHED_READY;
445
                } else if (((p->m_head+1)&p->m_mask) == (unsigned)p->m_tail)
446
                        // If the pipe is empty, block until there's data
447
                        tsk->state = SCHED_WAITING;
448
 
449
                // On return, this will bring us back to user space, in our
450
                // user space write call
451
        }
452
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.