URL
https://opencores.org/ocsvn/s6soc/s6soc/trunk
Subversion Repositories s6soc
Compare Revisions
- This comparison shows the changes necessary to convert path
/s6soc/trunk
- from Rev 21 to Rev 22
- ↔ Reverse comparison
Rev 21 → Rev 22
/sw/zipos/ziplib.h
0,0 → 1,6
#ifndef ZIPLIB_H |
#define ZIPLIB_H |
|
void *memset(void *s, int c, unsigned n); |
|
#endif |
/sw/zipos/cmod.ld
0,0 → 1,51
/******************************************************************************* |
* |
* Filename: cmod.ld |
* |
* Project: Cmod S6 ZipCPU demonstration |
* |
* Purpose: This script provides a description of the Cmod S6 Zip CPU |
* build for the purposes of where to place memory when linking. |
* |
* Creator: Dan Gisselquist, Ph.D. |
* Gisselquist Technology, LLC |
* |
******************************************************************************** |
* |
* Copyright (C) 2016, Gisselquist Technology, LLC |
* |
* This program is free software (firmware): you can redistribute it and/or |
* modify it under the terms of the GNU General Public License as published |
* by the Free Software Foundation, either version 3 of the License, or (at |
* your option) any later version. |
* |
* This program is distributed in the hope that it will be useful, but WITHOUT |
* ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
* for more details. |
* |
* License: GPL, v3, as defined and found on www.gnu.org, |
* http://www.gnu.org/licenses/gpl.html |
* |
* |
*******************************************************************************/ |
|
ENTRY(_start) |
|
MEMORY |
{ |
blkram (wx) : ORIGIN = 0x002000, LENGTH = 0x001000 |
flash (rx) : ORIGIN = 0x400000, LENGTH = 0x400000 |
} |
|
_top_of_stack = ORIGIN(blkram) + LENGTH(blkram) - 1; |
|
SECTIONS |
{ |
. = 0x0480000; |
.rocode 0x0480000 : { *(.start) *(.text) |
*(.rodata) |
*(.strings) } > flash |
.data : { *(.fixdata) *(.data) *(COMMON) *(.bss) } > blkram |
_top_of_heap = .; |
} |
/sw/zipos/syspipe.h
0,0 → 1,99
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: syspipe.h |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: This "device" handles the primary device level interaction of |
// almost all devices on the ZipOS: the pipe. A pipe, as defined |
// here, is an O/S supported FIFO. Information written to the FIFO will |
// be read from the FIFO in the order it was received. Attempts to read |
// from an empty FIFO, or equivalently to write to a full FIFO, will block |
// the reading (writing) process until memory is available. |
// |
// In general, each PIPE has three interface functions for reading and |
// another three for writing, together with one for creating an empty pipe. |
// The read functions are: |
// |
// kpop_syspipe |
// Attempts to read one value from the pipe in an interrupt |
// context. It is not allowed to fail. An empty pipe |
// simply becomes a return value of '1'. |
// |
// kread_syspipe |
// This is where the user trap read() ends up. When a user |
// tries to read from a pipe, kread_syspipe checks that the |
// users request is valid, then transfers control to a |
// user task level (interrupts enabled) read function. |
// |
// uread_syspipe |
// The user read task. This task is invoked from the user |
// trap, calling kread_syspipe at the kernel mode, which |
// then transitions the read to user mode using |
// uread_syspipe. |
// |
// Writing has it's analogous functions: |
// kpush_syspipe |
// Attempts to write a value from an interrupt context |
// to the pipe. Primarily used when reading from a |
// UART driven device. |
// kwrite_syspipe |
// uwrite_syspipie |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#ifndef SYSPIPE_H |
#define SYSPIPE_H |
|
extern void *sys_malloc(int sz); |
|
typedef struct { |
unsigned int m_mask; |
int m_head, m_tail; |
volatile TASKP m_rdtask, m_wrtask; |
unsigned int m_nread, m_nwritten; |
int m_error; |
|
int m_buf[1]; |
} SYSPIPE; |
|
SYSPIPE *new_syspipe(const unsigned int len); |
extern void kread_syspipe( TASKP tsk, int dev, int *dst, int len); |
extern void kwrite_syspipe(TASKP tsk, int dev, int *src, int len); |
void kpush_syspipe(SYSPIPE *p, int val); |
int kpop_syspipe(SYSPIPE *p, int *val); |
extern int num_avail_syspipe(SYSPIPE *p); |
extern int len_syspipe(SYSPIPE *p); |
|
#define INTERRUPT_READ_TASK ((TASKP)((unsigned)-1)) |
// #define INTERRUPT_WRITE_TASK ((TASKP)((unsigned)-1)) |
|
#endif |
/sw/zipos/zipsys.c
0,0 → 1,86
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: zipsystem.c |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: Implements some ZipCPU specific functions. Specifically, these |
// are the system call trap (which just switches to supervisor |
// mode), and the two context switching functions. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
|
#include "zipsys.h" |
|
// Implement a save_context function. This really boils into a long series of |
// instructions within the compiler. For this reason, it makes more sense |
// for it to be a function call rather than an inline function--although |
// zip_save_context could be either. Of course, the difficult part of placing |
// it in line is that the CPU may not realize the context changes between one |
// invocation of save_context and the corresponding restore_context function... |
void save_context(int *c) { |
zip_save_context(c); |
} |
|
void restore_context(int *c) { |
zip_restore_context(c); |
} |
|
#ifdef C_SYSCALL |
/* While the following system call *should* be identical to the assembly |
* equivalent beneath it, the dependency is actually dependent upon any |
* optimizations within the compiler. If the compiler is not optimized, |
* then it may try to create a stack frame, store id, a, b, and c, on the |
* stack frame, call the system call, clear the stack frame and return. |
* |
* The problem with this is that system traps may believe that they can replace |
* the system call with a goto. In that case, there is no knowledge of the |
* stack frame that needs to be unwound. Hence, we need to make certain that |
* the system call does not create a stack frame, and thus use the assembly |
* form beneath here. |
*/ |
int syscall(const int id, const int a, const int b, const int c) { |
zip_syscall(); |
} |
#else |
/* By making this into an assembly language equivalent, we can be specific about |
* what we are expecting. That way the kernel can just set the PC address and |
* the system call may believe that it was called like any ordinary subroutine. |
*/ |
asm("\t.section\t.text\n" |
"\t.global\tsyscall\n" |
"syscall:\n" |
"\tCLR\tCC\n" |
"\tJMP\tR0\n" |
); |
#endif |
|
|
/sw/zipos/doorbell.c
0,0 → 1,340
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: doorbell.c |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include "zipsys.h" |
#include "board.h" |
#include "ksched.h" |
#include "kfildes.h" |
#include "taskp.h" |
#include "syspipe.h" |
#include "ktraps.h" |
#include "errno.h" |
#include "swint.h" |
|
#include "../dev/display.h" |
#include "../dev/rtcsim.h" |
|
/* Our system will need some pipes to handle ... life. How about these: |
* |
* rxpipe - read()s from this pipe read from the UART |
* Interrupt fed |
* txpipe - write()s to this pipe write to the UART |
* Interrupt consumed |
* keypipe - read()s from this pipe return values read by the keypad |
* lcdpipe - write()s to this pipe write to the LCD display SPI port |
* pwmpipe - write()s to this pipe will send values to the audio port |
* Interrupt consumed |
* cmdpipe - written to by the user command task, read by the display task |
* used to communicate menu status |
* |
*/ |
|
/* We'll need some tasks as well: |
* User command task |
* Handles user interaction |
* Reads from pipe--either the keypad or the UARTRX pipe |
* (Might be two such tasks in the system, one for each.) |
* Sets clock upon request |
* Reads from a pipe (rxpipe or keypipe), Writes to the txpipe pipe |
* Doorbell task |
* Maintains system time on the clock : TIME: HH:MM:SS |
* Maintains system status on display : Light is (dis/en)abled |
* Transitions when the doorbell is rung to: (fixed time line) |
* : DOORBELL!! |
* When the doorbell is clear, returns to the original task. |
* --- |
* Waits on events, writes to the lcdpipe and pwmpipe. |
* Reads from a command pipe, so that it can handle any user menu's |
* Command pipe. This, though, is tricky. It requires |
* a task that can be interrupted by either an event or a |
* pipe. Blocking is going to be more tricky ... |
* Keypad task |
* Normally, you might think this should be an interrupt task. |
* But, it needs state in order to have timeouts and to debounce |
* the input pin. So ... let's leave this as a task. |
* --- |
* Waits on events(keypad/timer), writes to the keypipe |
* Display task |
* The display does *not* need to be written to at an interrupt |
* level. It really needs to be written to at a task level, so |
* let's make a display task. |
* --- |
* Reads from the lcdpipe |
* Real-time Clock Task |
* Gets called once per second to update the real-time clock |
* and to post those updates as an event to other tasks that might |
* be interested in it. |
* --- |
* Waits on system tasks, uses two semaphores |
*/ |
|
|
/* |
* Read the keypad, write the results to an output pipe |
*/ |
// #define KEYPAD_TASK keypad_task_id |
/* |
* Maintain a realtime clock |
*/ |
#define RTCCLOCK_TASK rtccclock_task_id |
/* |
* Read from an incoming pipe, write results to the SPI port controlling the |
* display. |
*/ |
#define DISPLAY_TASK display_task_id |
|
/* |
* Wait for a button press, and then based upon the clock set a light |
*/ |
#define DOORBELL_TASK doorbell_task_id |
|
/* |
* Interract with any user commands, such as setting the clock, setting |
* nighttime (when the lights turn on) or setting daytime when only the |
* doorbell rings. |
*/ |
// #define COMMAND_TASK command_task_id |
#define LAST_TASK last_task_id |
|
typedef enum { |
#ifdef RTCCLOCK_TASK |
RTCCLOCK_TASK, |
#endif |
#ifdef DOORBELL_TASK |
#ifdef DISPLAY_TASK |
DOORBELL_TASK, DISPLAY_TASK, |
#endif |
#endif |
#ifdef KEYPAD_TASK |
KEYPAD_TASK, |
#endif |
#ifdef COMMAND_TASK |
COMMAND_TASK, |
#endif |
LAST_TASK |
} TASKNAME; |
|
|
void rtctask(void), |
doorbell_task(void), |
display_task(void), |
keypad_task(void), |
command_task(void); |
// idle_task ... is accomplished within the kernel |
extern void restore_context(int *), save_context(int *); |
extern SYSPIPE *rxpipe, *txpipe, *pwmpipe, *lcdpipe; |
SYSPIPE *midpipe; |
extern KDEVICE *pipedev; |
|
int kntasks(void) { |
return LAST_TASK; |
} void kinit(TASKP *tasklist) { |
#ifdef RTCCLOCK_TASK |
// |
tasklist[RTCCLOCK_TASK] = new_task(16, rtctask); |
#endif |
|
#ifdef DOORBELL_TASK |
#ifdef DISPLAY_TASK |
// |
tasklist[DOORBELL_TASK] = new_task(64, doorbell_task); |
tasklist[DOORBELL_TASK]->fd[FILENO_STDOUT] = sys_malloc(sizeof(KFILDES)); |
tasklist[DOORBELL_TASK]->fd[FILENO_STDOUT]->id = (int)lcdpipe; |
tasklist[DOORBELL_TASK]->fd[FILENO_STDOUT]->dev= pipedev; |
tasklist[DOORBELL_TASK]->fd[FILENO_AUX] = sys_malloc(sizeof(KFILDES)); |
tasklist[DOORBELL_TASK]->fd[FILENO_AUX]->id = (int)pwmpipe; |
tasklist[DOORBELL_TASK]->fd[FILENO_AUX]->dev= pipedev; |
|
// |
tasklist[DISPLAY_TASK] = new_task(32, display_task); |
tasklist[DISPLAY_TASK]->fd[FILENO_STDIN] = sys_malloc(sizeof(KFILDES)); |
tasklist[DISPLAY_TASK]->fd[FILENO_STDIN]->id = (int)lcdpipe; |
tasklist[DISPLAY_TASK]->fd[FILENO_STDIN]->dev= pipedev; |
#endif |
#endif |
|
|
#ifdef KEYPAD_TASK |
tasklist[KEYPAD_TASK] = new_task(16, keypad_task); |
tasklist[KEYPAD_TASK]->fd[FILENO_STDOUT] = sys_malloc(sizeof(KFILDES)); |
tasklist[NMEA_TASK]->fd[FILENO_STDOUT]->id = (int)keypipe; |
tasklist[NMEA_TASK]->fd[FILENO_STDOUT]->dev= pipedev; |
#endif |
} |
|
#ifdef DOORBELL_TASK |
// #define HALF_HOUR_S 1800 // Seconds per half hour |
// #define HALF_HOUR_S 180 // Seconds per three minutes--for test |
#define HALF_HOUR_S 30 // 3 Mins is to long, here's 3 seconds |
|
#include "../dev/samples.c" |
|
const unsigned dawn = 0x060000, dusk = 0x180000; |
|
void shownow(unsigned now) { // Uses 10 stack slots + 8 for write() |
char dmsg[9]; |
dmsg[0] = PACK(0x1b,'[','j','T'); |
dmsg[1] = PACK('i','m','e',':'); |
dmsg[2] = PACK(' ',((now>>20)&0x3)+'0', |
((now>>16)&0xf)+'0',':'); |
dmsg[3] = PACK( ((now>>12)&0xf)+'0', |
((now>> 8)&0xf)+'0', |
':', |
((now>> 4)&0xf)+'0'); |
dmsg[4] = PACK( ((now )&0xf)+'0', |
0x1b, '[', '1'); |
dmsg[5] = PACK(';','0','H',' '); |
if ((now < dawn)||(now > dusk)) { |
dmsg[6] = PACK('N','i','g','h'); |
dmsg[7] = PACK('t',' ','t','i'); |
dmsg[8] = PACK('m','e',0,0); |
} else { |
dmsg[6] = PACK('D','a','y','l'); |
dmsg[7] = PACK('i','g','h','t'); |
dmsg[8] = PACK('!',' ',0,0); |
} write(FILENO_STDOUT, dmsg, 9); |
} |
|
void showbell(unsigned now) { // Uses 10 stack slots + 8 for write() |
char dmsg[9]; |
dmsg[0] = PACK(0x1b,'[','j','T'); |
dmsg[1] = PACK('i','m','e',':'); |
dmsg[2] = PACK(' ',((now>>20)&0x3)+'0', |
((now>>16)&0xf)+'0',':'); |
dmsg[3] = PACK( ((now>>12)&0xf)+'0', |
((now>> 8)&0xf)+'0', |
':', |
((now>> 4)&0xf)+'0'); |
dmsg[4] = PACK( ((now )&0xf)+'0', |
0x1b, '[', '1'); |
dmsg[5] = PACK(';','0','H',' '); |
dmsg[6] = PACK('D','o','o','r'); |
dmsg[7] = PACK('b','e','l','l'); |
dmsg[8] = PACK('!',' ',0,0); |
write(FILENO_STDOUT, dmsg, 9); |
} |
|
void belllight(unsigned now) { |
IOSPACE *sys = (IOSPACE *)IOADDR; |
if ((now < dawn)||(now > dusk)) |
sys->io_spio = 0x088; // Turn our light on |
else |
sys->io_spio = 0x80; // Turn light off |
} |
|
void doorbell_task(void) { |
// Controls LED 0x08 |
|
// Start by initializing the display to GT Gisselquist\nTechnology |
// write(KFD_STDOUT, disp_build_backslash,sizeof(disp_build_backslash)); |
// write(KFD_STDOUT, disp_build_gtlogo, sizeof(disp_build_gtlogo)); |
// write(KFD_STDOUT, disp_reset_data, sizeof(disp_reset_data)); |
// write(KFD_STDOUT, disp_gtech_data, sizeof(disp_gtech_data)); |
|
IOSPACE *sys = (IOSPACE *)IOADDR; |
|
while(1) { |
int event; |
// Initial state: doorbell is not ringing. In this state, we |
// can wait forever for an event |
sys->io_spio = 0x080; // Turn our light off |
event = wait(INT_BUTTON|SWINT_PPS,-1); |
unsigned when = rtcclock; |
if (event & INT_BUTTON) |
showbell(when); |
else if (event & SWINT_PPS) |
shownow(when); |
|
while(event & INT_BUTTON) { |
// Next state, the button has been pressed, the |
// doorbell is ringing |
|
// Seconds records the number of seconds since the |
// button was last pressed. |
int seconds = 0; |
|
// Check time: should we turn our light on or not? |
belllight(rtcclock); |
const int *sptr = sound_data; |
sys->io_uart = 'N'; |
while(sptr < &sound_data[NSAMPLE_WORDS]) { |
int len = &sound_data[NSAMPLE_WORDS]-sptr; |
if (len > 256) |
len = 256; |
|
// We stall here, if the audio FIFO is full |
write(FILENO_AUX, sptr, len); |
sptr += len; |
// If the user presses the button more than |
// once, we start the sound over as well as |
// our light counter. |
event = wait(INT_BUTTON|SWINT_PPS, 0); |
if (event&INT_BUTTON) { |
if (sptr > &sound_data[2048]) { |
sptr = sound_data; |
seconds = 0; |
when = (volatile unsigned)rtcclock; |
showbell(when); |
} |
} else if (event&SWINT_PPS) { |
seconds++; |
belllight(rtcclock); |
showbell(when); |
} |
} |
|
sys->io_uart = 'D'; |
|
// Next state: the doorbell is no longer ringing, but |
// we have yet to return to normal--the light is still |
// on. |
while((seconds < HALF_HOUR_S)&& |
(((event=wait(INT_BUTTON|SWINT_PPS,-1))&INT_BUTTON)==0)) { |
seconds++; |
belllight(rtcclock); |
showbell(when); |
} |
if (event&INT_BUTTON) { |
when = (volatile unsigned)rtcclock; |
showbell(when); |
sys->io_uart = 'S'; |
} |
} |
} |
} |
#endif |
|
/sw/zipos/ksched.h
0,0 → 1,47
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: ksched.h |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: Defines a series of processor states to be used by the |
// scheduler when determining what tasks are available to be run |
// at any given time. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#ifndef KSCHED_H |
#define KSCHED_H |
|
typedef enum { |
SCHED_READY, SCHED_WAITING, SCHED_ERR, SCHED_EXIT |
} KSCHED_STATE; |
|
#endif |
/sw/zipos/zipsys.h
0,0 → 1,91
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: zipsys.h |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: Defines particular definitions specific to the ZipCPU. |
// Originally designed to be specific to the ZipSystem, this has |
// been adjusted to just the CPU, since the CMod S6 doesn't have the full |
// ZipSystem implemented. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#ifndef ZIPSYS_H |
#define ZIPSYS_H |
|
#include "board.h" |
|
#define CC_Z 0x0001 |
#define CC_C 0x0002 |
#define CC_N 0x0004 |
#define CC_V 0x0008 |
#define CC_SLEEP 0x0010 |
#define CC_GIE 0x0020 |
#define CC_STEP 0x0040 |
#define CC_BREAK 0x0080 |
#define CC_ILL 0x0100 |
#define CC_TRAPBIT 0x0200 |
#define CC_BUSERR 0x0400 |
#define CC_DIVERR 0x0800 |
#define CC_FPUERR 0x1000 |
#define CC_IPHASE 0x2000 |
|
extern void zip_rtu(void); |
extern void zip_halt(void); |
extern void zip_idle(void); |
extern void zip_syscall(void); |
extern void zip_restore_context(int *); |
extern void zip_save_context(int *); |
extern int zip_bitrev(int v); |
extern unsigned zip_cc(void); |
extern unsigned zip_ucc(void); |
|
extern int _top_of_heap[1]; |
|
extern void save_context(int *); |
extern void restore_context(int *); |
extern int syscall(const int,int,int,int); |
|
#ifndef NULL |
#define NULL ((void *)0) |
#endif |
|
static inline void DISABLE_INTS(void) { |
IOSPACE *sys = (IOSPACE *)IOADDR; |
sys->io_pic = 0; |
} |
|
static inline void ENABLE_INTS(void) { |
IOSPACE *sys = (IOSPACE *)IOADDR; |
sys->io_pic = 0x80000000; |
} |
|
#endif |
/sw/zipos/swint.h
0,0 → 1,54
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: swint.h |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: Defines the "Software interrupts" that may be created on this |
// system. A software interrupt is one that is created by |
// software, but may be waited on like any other interrupt. |
// |
// The biggest reason for having this is realtime clock (RTC) emulation. |
// Somewhere, a hardware interrupt needs to prod the software RTC. Then |
// the software RTC will generate a software interrupt so that anything |
// waiting on the top of the second may now be informed of that fact. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#ifndef SWINT_H |
#define SWINT_H |
|
#define SWINT_PPS 0x008000 |
#define SWINT_TIMEOUT 0x010000 |
#define SWINT_PPD 0x020000 |
#define SWINT_ALARM 0x040000 |
#define SWINT_CLOCK 0x080000 |
|
#endif |
/sw/zipos/ktraps.h
0,0 → 1,112
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: ktraps.h |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#ifndef KTRAPS_H |
#define KTRAPS_H |
|
typedef enum { |
// First two deal with interrupts: |
// syscall(TRAPID_WAIT, intmask, timeout, ?) |
// returns when an interrupt in intmask has taken place |
// may return immediately if such an interrupt has |
// occurred between the last such syscall and this one |
// If timeout is != 0, specifies the maximum amount of |
// time to wait for the event to occurr. |
// If intmask = 0, then the task will wait for a timeout |
// alone. |
// If the task is stalled for another reason, it may wake |
// early from the trap when the timeout is up. |
// syscall(TRAPID_CLEAR, intmask, timeout, ?) |
// Same thing, except this returns immediately after |
// clearing any potentially pending interrupts |
// If the timeout < 0, clears any pending timeout wakeup |
// If the timeout > 0, sets a pending timeout wakeup and |
// returns. |
// If the timeout == 0, does nothing. |
TRAPID_WAIT, TRAPID_CLEAR, TRAPID_POST, |
// Yield: Yields the processor until the next scheduled time slice. |
TRAPID_YIELD, |
TRAPID_READ, TRAPID_WRITE, |
TRAPID_TIME, |
// Return from a kernel system call. This is necessary if ever an |
// exception triggers a syste call. In such cases, it will be |
// impossible to return the caller back to his context in a pristine |
// manner ... without help. |
// TRAPID_KRETURN |
// Semaphore ID's. These allow us to run the rest of the trap |
// stuffs in kernel space |
TRAPID_SEMGET, TRAPID_SEMPUT, TRAPID_SEMNEW, |
// Malloc--since we're using a system level malloc, from a system |
// heap for everything, malloc/free require system calls |
TRAPID_MALLOC, TRAPID_FREE, |
// EXIT -- end a task |
TRAPID_EXIT |
} TRAPID; |
|
extern int syscall(const int id, const int a, const int b, const int c); |
|
static inline int read(int fid, void *buf, int ln) { |
return syscall(TRAPID_READ, fid,(int)buf,ln); |
} static inline int write(int fid, const void *buf, int ln) { |
return syscall(TRAPID_WRITE, fid, (int)buf, ln); |
} |
|
static inline unsigned time(void) { |
return syscall(TRAPID_TIME, 0,0,0); |
} |
|
static inline void yield(void) { |
syscall(TRAPID_YIELD, 0, 0, 0); |
} static inline int wait(unsigned event_mask, int timeout) { |
return syscall(TRAPID_WAIT, (int)event_mask, timeout, 0); |
} static inline void clear(unsigned event) { |
syscall(TRAPID_CLEAR, (int)event, 0, 0); |
} static inline void post(unsigned event) { |
syscall(TRAPID_POST, (int)event, 0, 0); |
} |
|
static inline void *malloc(unsigned nbytes) { |
return (void *)syscall(TRAPID_MALLOC, (int)nbytes, 0, 0); |
} static inline void free(void *buf) { |
syscall(TRAPID_FREE,(int)buf,0,0); |
} |
|
static inline void exit(int status) { |
syscall(TRAPID_EXIT,status,0,0); |
} |
|
#endif |
/sw/zipos/kernel.c
0,0 → 1,552
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: kernel.c |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: If you are looking for a main() program associated with the |
// ZipOS, this is it. This is the main program for the supervisor |
// task. It handles interrupt processing, creating tasks, context swaps, |
// creating tasks, and ... just about everything else a kernel must handle. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
|
#include "zipsys.h" |
#include "board.h" |
#include "ksched.h" |
#include "kfildes.h" |
#include "taskp.h" |
#include "syspipe.h" |
#include "ktraps.h" |
#include "errno.h" |
#include "swint.h" |
|
extern void kpanic(void); |
extern void raw_put_uart(int val); |
|
unsigned int nresets = 0; |
|
extern int kntasks(void); |
extern void kinit(TASKP *tasklist); |
extern void restore_context(int *), save_context(int *); |
SYSPIPE *rxpipe, *txpipe, *keypipe, *lcdpipe, *pwmpipe, *cmdpipe; |
KDEVICE *pipedev, *txdev, *pwmdev; |
void *heap; // = _top_of_heap; // Need to wait on startup to set this |
|
#define CONTEXT_LENGTH 100000 // 1ms |
#define TICKS_PER_SECOND 1000 |
|
void kwrite_audio(TASKP tsk, int dev, int *dst, int len); |
void kwrite_txuart(TASKP tsk, int dev, int *dst, int len); |
int kpost(TASKP *task, unsigned events, int milliseconds); |
TASKP kschedule(int LAST_TASK, TASKP *tasklist, TASKP last); |
|
int LAST_TASK; |
|
void kernel_entry(void) { |
int nheartbeats= 0, tickcount = 0, milliseconds=0, ticks = 0; |
int audiostate = 0; |
TASKP *tasklist, current; |
int *last_context; |
IOSPACE *sys = (IOSPACE *)IOADDR; |
sys->io_spio = 0x0f0; |
sys->io_timb = 0; // Turn off the watchdog timer |
LAST_TASK = kntasks(); |
heap = _top_of_heap; |
|
pipedev = sys_malloc(sizeof(KDEVICE)); |
pipedev->write = (RWFDFUN)kwrite_syspipe; |
pipedev->read = (RWFDFUN)kread_syspipe; |
pipedev->close = NULL; |
|
/* |
txdev = sys_malloc(sizeof(KDEVICE)); |
txdev->write = (RWFDFUN)kwrite_txuart; |
txdev->read = (RWFDFUN)kread_syspipe; |
txdev->close = NULL; |
|
pwmdev = sys_malloc(sizeof(KDEVICE)); |
pwmdev->write = (RWFDFUN)kwrite_audio; |
pwmdev->read = (RWFDFUN)kread_syspipe; |
pwmdev->close = NULL; |
*/ |
|
txdev = pwmdev = pipedev; |
|
rxpipe = new_syspipe(16); //rxpipe->m_wrtask=INTERRUPT_WRITE_TASK; |
txpipe = new_syspipe(16); txpipe->m_rdtask = INTERRUPT_READ_TASK; |
keypipe = new_syspipe(16); |
lcdpipe = new_syspipe(16); |
pwmpipe = new_syspipe(256); pwmpipe->m_rdtask= INTERRUPT_READ_TASK; |
cmdpipe = new_syspipe(16); |
|
tasklist = sys_malloc(sizeof(TASKP)*(1+LAST_TASK)); |
kinit(tasklist); |
tasklist[LAST_TASK] = new_task(2, idle_task); |
|
|
// current = tasklist[0]; |
current = tasklist[LAST_TASK]; |
restore_context(current->context); |
last_context = current->context; |
|
// Turn all interrupts off, acknowledge all at the same time |
sys->io_pic = 0x7fff7fff; |
unsigned enableset = |
INT_ENABLEV(INT_BUTTON) |
|INT_ENABLEV(INT_TIMA) |
// |INT_ENABLEV(INT_UARTRX) |
// |INT_ENABLEV(INT_UARTTX) // Needs to be turned on by driver |
// |INT_ENABLEV(INT_AUDIO // Needs to be turned on by driver) |
// |INT_ENABLEV(INT_GPIO) |
// |INT_ENABLEV(INT_TIMB); |
; |
// Then selectively turn some of them back on |
sys->io_pic = INT_ENABLE | enableset; |
|
sys->io_tima = CONTEXT_LENGTH | TM_REPEAT; |
|
sys->io_spio = 0x0f1; |
if (1) { |
// Reset our wishbone scope for debug later |
SCOPE *scope = (SCOPE *)SCOPEADDR; |
scope->s_control = 12; |
} |
|
if (0) { |
// Wait on a button press before starting |
while((sys->io_spio & 0x0f0)==0) |
; |
sys->io_spio = 0xf3; |
for(int i=0; i<0x40000; i++) |
sys->io_spio = ((i>>14)&2)|0x20; |
sys->io_spio = 0xf7; |
} |
sys->io_pic = 0x7fff|INT_ENABLE; |
|
do { |
int need_resched = 0, context_has_been_saved, pic; |
nheartbeats++; |
|
if (0) { |
int v = 0xe4, p = sys->io_pic; |
if (p < 0) // LED 4 if interrupts enabled |
v &= ~4; |
if(p & 0x8000) // LED 8 if any already active |
v |= 8; |
sys->io_spio = v; |
} |
|
// if (sys->io_timb == 0) |
// sys->io_timb = 1600000000; // CONTEXT_LENGTH; |
// sys->io_timb = (sys->io_tima & 0x7fffffff) + 350; |
// sys->io_spio = 0x0e0; |
zip_rtu(); |
// sys->io_spio = 0xe2; |
// sys->io_timb = CONTEXT_LENGTH; |
|
last_context = current->context; |
context_has_been_saved = 0; |
pic = sys->io_pic; |
|
if (pic & 0x8000) { // If there's an active interrupt |
// Interrupt processing |
sys->io_spio = 0x44; |
|
// First, turn off pending interrupts |
// Although we migt just write 0x7fff7fff to the |
// interrupt controller, how do we know another |
// interrupt hasn't taken place since we read it? |
// Thus we turn off the pending interrupts that we |
// know about. |
pic &= 0x7fff; |
// Acknowledge current ints, and turn off pending ints |
sys->io_pic = INT_DISABLEV(pic)|(INT_CLEAR(pic)); |
if(pic&INT_TIMA) { |
milliseconds++; |
if (++ticks >= TICKS_PER_SECOND) {//(pic & SYSINT_PPS) |
// Toggle the low order LED |
tickcount++; |
ticks = 0; |
sys->io_spio = ((sys->io_spio&1)^1)|0x010; |
pic |= SWINT_CLOCK; |
} |
} |
// |
if (pic&INT_BUTTON) { |
// Need to turn the button interrupt off |
enableset &= ~(INT_ENABLEV(INT_BUTTON)); |
if ((sys->io_spio&0x0f0)==0x030) |
kpanic(); |
} else // We can turn it back on when the button |
// is released |
enableset |= INT_ENABLEV(INT_BUTTON); |
if (pic & INT_UARTRX) { |
int v = sys->io_uart; |
|
if ((v & (~0x7f))==0) { |
kpush_syspipe(rxpipe, v); |
|
// Local Echo |
if (pic & INT_UARTTX) |
sys->io_uart = v; |
} |
} if (pic & INT_UARTTX) { |
if (kpop_syspipe(txpipe, (int *)&sys->io_uart)==0) { |
enableset |= (INT_ENABLEV(INT_UARTTX)); |
sys->io_pic = INT_UARTTX; |
} else { |
if (txpipe->m_wrtask) |
txpipe->m_wrtask->state = SCHED_READY; |
enableset &= ~(INT_ENABLEV(INT_UARTTX)); |
} |
} if (audiostate) { |
if (pic & INT_AUDIO) { |
int v; |
// States: |
// 0 -- not in use |
// 1 -- First sample, buffer empty |
// time to read a new sample |
// 2 -- second sample, to read new |
// 3 -- Need to turn off |
if ((audiostate & 3)==2) { |
sys->io_pwm_audio = (audiostate>>2)&0x0ffff; |
audiostate = 1; |
} else if (kpop_syspipe(pwmpipe, &v)==0) { |
audiostate = (2|(v<<2))&0x03ffff; |
sys->io_pwm_audio = (v>>16)&0x0ffff; |
} else { |
audiostate = 0; |
// Turn the device off |
sys->io_pwm_audio = 0x10000; |
// Turn the interrupts off |
enableset &= ~(INT_ENABLEV(INT_AUDIO)); |
sys->io_spio = 0x020; |
} |
|
// This particular interrupt cannot be cleared |
// until the port has been written to. Hence, |
// now that we've written to the port, we clear |
// it now. |
sys->io_pic = INT_AUDIO; |
}} else { // if (audiostate == 0) |
int v; |
if (kpop_syspipe(pwmpipe, &v)==0) { |
audiostate = (2|(v<<2))&0x03ffff; |
sys->io_pwm_audio = 0x310000 | ((v>>16)&0x0ffff); |
enableset |= (INT_ENABLEV(INT_AUDIO)); |
sys->io_spio = 0x022; |
sys->io_pic = INT_AUDIO; |
} // else sys->io_spio = 0x020; |
} milliseconds = kpost(tasklist, pic, milliseconds); |
|
// Restart interrupts |
enableset &= (~0x0ffff); // Keep the bottom bits off |
sys->io_pic = INT_ENABLE|enableset; |
} else { |
sys->io_pic = INT_ENABLE; // Make sure interrupts are on |
int v; |
|
// Check for the beginning of an audio pipe. If the |
// interrupt is not enabled, we still might need to |
// enable it. |
if ((audiostate==0)&&(kpop_syspipe(pwmpipe, &v)==0)) { |
audiostate = (2|(v<<2))&0x03ffff; |
sys->io_pwm_audio = 0x310000 | ((v>>16)&0x0ffff); |
enableset |= (INT_ENABLEV(INT_AUDIO)); |
sys->io_spio = 0x022; |
} // else sys->io_spio = 0x020; |
|
// Or the beginning of a transmit pipe. |
if (pic & INT_UARTTX) { |
if (kpop_syspipe(txpipe, (int *)&sys->io_uart)==0) { |
enableset |= (INT_ENABLEV(INT_UARTTX)); |
sys->io_pic = INT_UARTTX; |
} else { |
if (txpipe->m_wrtask) |
txpipe->m_wrtask = SCHED_READY; |
enableset &= ~(INT_ENABLEV(INT_UARTTX)); |
} |
} |
|
// What if the interrupt bit for the buttons is off? |
if ((sys->io_spio & 0x0f0)==0) |
enableset |= INT_ENABLEV(INT_BUTTON); |
|
// What if someone left interrupts off? |
// This might happen as part of a wait trap call, such |
// as syspipe() accomplishes within uwrite_syspipe() |
// (We also might've just turned them off ... ooops) |
enableset &= (~0x0ffff); // Keep the bottom bits off |
sys->io_pic = INT_ENABLE | enableset; |
} |
sys->io_spio = 0x40; |
|
int zcc = zip_ucc(); |
if (zcc & CC_TRAPBIT) { |
// sys->io_spio = 0x0ea; |
|
context_has_been_saved = 1; |
save_context(last_context); |
last_context[14] = zcc & (~CC_TRAPBIT); |
// Do trap handling |
switch(last_context[1]) { |
case TRAPID_WAIT: |
{ // The task wishes to wait on an interrupt |
int ilist, timeout; |
ilist = last_context[2]; |
timeout= last_context[3]; |
if (current->pending & ilist) { |
last_context[1] = ilist & current->pending; |
// Clear upon any read |
current->pending &= (~last_context[1]); |
} else { |
current->waitsig = ilist; |
if (timeout != 0) { |
current->state = SCHED_WAITING; |
need_resched = 1; |
if (timeout > 0) { |
current->timeout=milliseconds+timeout; |
current->waitsig |= SWINT_TIMEOUT; |
} |
} |
}} break; |
case TRAPID_CLEAR: |
{ unsigned timeout; |
// The task wishes to clear any pending |
// interrupts, in a likely attempt to create |
// them soon. |
last_context[1] = last_context[2] & current->pending; |
// Clear upon any read |
current->pending &= (~last_context[1]); |
timeout = (unsigned)last_context[2]; |
if (timeout) { |
if ((int)timeout < 0) |
current->pending &= (~SWINT_TIMEOUT); |
else |
current->timeout = milliseconds+timeout; |
}} break; |
case TRAPID_POST: |
kpost(tasklist, last_context[2]&(~0x07fff), |
milliseconds); |
break; |
case TRAPID_YIELD: |
need_resched = 1; |
break; |
case TRAPID_READ: |
{ |
KFILDES *fd = NULL; |
if ((unsigned)last_context[2] |
< (unsigned)MAX_KFILDES) |
fd = current->fd[last_context[2]]; |
if ((!fd)||(!fd->dev)) |
last_context[1] = -EBADF; |
else |
fd->dev->read(current, fd->id, |
(void *)last_context[3], last_context[4]); |
} break; |
case TRAPID_WRITE: |
{ KFILDES *fd = NULL; |
if ((unsigned)last_context[2] |
< (unsigned)MAX_KFILDES) |
fd = current->fd[last_context[2]]; |
else { kpanic(); zip_halt(); } |
if ((!fd)||(!fd->dev)) |
last_context[1] = -EBADF; |
else |
fd->dev->write(current, fd->id, |
(void *)last_context[3], last_context[4]); |
} break; |
case TRAPID_TIME: |
last_context[1] = tickcount; |
break; |
case TRAPID_MALLOC: |
last_context[1] = (int)sys_malloc(last_context[2]); |
break; |
case TRAPID_FREE: |
// Our current malloc cannot free |
// sys_free(last_context[2]) |
break; |
case TRAPID_EXIT: |
current->state = SCHED_EXIT; |
need_resched = 1; |
kpanic(); |
zip_halt(); |
break; |
default: |
current->state = SCHED_ERR; |
need_resched = 1; |
kpanic(); |
zip_halt(); |
break; |
} |
|
restore_context(last_context); |
} else if (zcc & (CC_BUSERR|CC_DIVERR|CC_FPUERR|CC_ILL)) { |
current->state = SCHED_ERR; |
// current->errno = -EBUS; |
current->errno = (int)sys->io_buserr; |
save_context(last_context); |
context_has_been_saved = 1; |
kpanic(); |
zip_halt(); |
} |
|
if ((need_resched)||(current->state != SCHED_READY) |
||(current == tasklist[LAST_TASK])) |
current = kschedule(LAST_TASK, tasklist, current); |
|
if (current->context != last_context) { |
// Swap contexts |
if (!context_has_been_saved) |
save_context(last_context); |
restore_context(current->context); |
} |
} while(1); |
} |
|
TASKP kschedule(int LAST_TASK, TASKP *tasklist, TASKP last) { |
TASKP current = tasklist[LAST_TASK]; |
int nxtid = 0, i; |
|
// What task were we just running? |
for(i=0; i<=LAST_TASK; i++) { |
if (last == tasklist[i]) { |
// If we found it, then let's run the next one |
nxtid = i+1; |
break; |
} |
} |
|
// Now let's see if we can find the next ready task to run |
for(; nxtid<LAST_TASK; nxtid++) |
if (tasklist[nxtid]->state == SCHED_READY) { |
current=tasklist[nxtid]; |
break; |
} |
// The last task (the idle task) doesn't count |
if (nxtid >= LAST_TASK) { |
nxtid = 0; // Don't automatically run idle task |
for(; nxtid<LAST_TASK; nxtid++) |
if (tasklist[nxtid]->state == SCHED_READY) { |
break; |
} |
// Now we stop at the idle task, if nothing else is ready |
current = tasklist[nxtid]; |
} |
return current; |
} |
|
int kpost(TASKP *tasklist, unsigned events, int milliseconds) { |
int i; |
if (events & INT_TIMA) |
milliseconds++; |
if (milliseconds<0) { |
milliseconds -= 0x80000000; |
for(i=0; i<=LAST_TASK; i++) { |
if(tasklist[i]->timeout) { |
tasklist[i]->timeout -= 0x80000000; |
if (tasklist[i]->timeout==0) |
tasklist[i]->timeout++; |
if ((int)tasklist[i]->timeout < milliseconds) { |
tasklist[i]->pending |= SWINT_TIMEOUT; |
tasklist[i]->timeout = 0; |
} |
} |
} |
} else { |
for(i=0; i<=LAST_TASK; i++) { |
if(tasklist[i]->timeout) { |
if (tasklist[i]->timeout < (unsigned)milliseconds) { |
tasklist[i]->pending |= SWINT_TIMEOUT; |
tasklist[i]->timeout = 0; |
} |
} |
} |
} for(i=0; i<=LAST_TASK; i++) { |
tasklist[i]->pending |= events; |
if ((tasklist[i]->state == SCHED_WAITING) |
&&(tasklist[i]->waitsig&tasklist[i]->pending)) { |
tasklist[i]->state = SCHED_READY; |
tasklist[i]->context[1] = tasklist[i]->waitsig & tasklist[i]->pending; |
tasklist[i]->pending &= (~tasklist[i]->context[1]); |
tasklist[i]->waitsig = 0; |
} |
} return milliseconds; |
} |
|
void kuserexit(int a) { |
syscall(TRAPID_EXIT, a, 0, 0); |
} |
|
void *sys_malloc(int sz) { |
{ |
SCOPE *s = (SCOPE *)SCOPEADDR; |
s->s_control = TRIGGER_SCOPE_NOW | (s->s_control & 0x0ffff); |
} |
|
void *res = heap; |
heap += sz; |
if ((int)heap > ((int)&res)-32) { |
IOSPACE *sys = (IOSPACE *)IOADDR; |
sys->io_spio = 0xf3; |
asm("break 0"); |
} |
return res; |
} |
|
/* |
// This won't work. Once we apply an rtu(), there will be an immediate |
// interrupt before the task has had a chance to fill in a first value. By |
// instead waiting until the task comes back from it's first task swap, perhaps |
// having stalled it's pipe, then we can get our interrupt up and running. |
// Not only that, but at that time we'll have plenty of info to stuff into the |
// interrupt as well. |
void kwrite_audio(TASKP tsk, int dev, int *dst, int len) { |
kwrite_syspipe(tsk,dev,dst,len); |
if (tsk->context[15] == (int)uwrite_syspipe) { |
IOSPACE *io = (IOSPACE *)IOADDR; |
// Turn our audio interrupt on. |
io->pic = INT_ENABLEV(INT_AUDIO); |
} |
} |
|
void kwrite_txuart(TASKP tsk, int dev, int *dst, int len) { |
kwrite_syspipe(tsk,dev,dst,len); |
if (tsk->context[15] == (int)uwrite_syspipe) { |
IOSPACE *io = (IOSPACE *)IOADDR; |
// Turn our UART transmit interrupt on. |
io->pic = INT_ENABLEV(INT_UARTTX); |
} |
} |
|
*/ |
|
/sw/zipos/taskp.c
0,0 → 1,73
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: taskp.c |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: Implements the new_task function to create a new task, as well |
// as the idle_task's entry function and body. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include "ksched.h" |
#include "kfildes.h" |
#include "taskp.h" |
|
extern void *sys_malloc(int sz); |
TASKP new_task(unsigned ln, VENTRYP entry) { |
int i; |
TASKP tsk = (TASKP)sys_malloc(sizeof(struct TASK_S)+ln); |
|
for(i=0; (unsigned)i<sizeof(struct TASK_S)+ln; i++) |
((unsigned int *)tsk)[i] = 0; |
tsk->context[ 0] = (int)((long)(int *)idle_task); |
tsk->context[12] = (int)&tsk->user_data[ln]; // Set the stack pointer |
tsk->context[13] = (int)&tsk->user_data[ln]; // Set the stack pointer |
tsk->context[14] = 0x20; // GIE bit only |
tsk->context[15] = (int)((long)(int *)entry); |
tsk->user_heap = &tsk->user_data[0]; |
tsk->state = SCHED_READY; |
|
return tsk; |
} |
|
// |
// zip_idle is really an assembly language builtin. It translates into the |
// WAIT (i.e. for interrupt) instruction. Even this, though, is a derived |
// instruction. More specifically, the WAIT instruction OR's a constant |
// to the CC register. |
// |
extern void zip_idle(void); |
void idle_task(void) { |
do { |
zip_idle(); |
} while(1); |
} |
|
/sw/zipos/kfildes.h
0,0 → 1,71
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: kfildes.h |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: Defines a "File Descriptor" from the standpoint of the kernel. |
// Inside the kernel, each process has an array of these file |
// descriptors. Each file descriptor contains a pointer to the file |
// itself (typically a SYSPIPE), as well as to a device structure of |
// function pointers that can be used to operator on the file pointer. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#ifndef KFILDES_H |
#define KFILDES_H |
|
struct TASK_S; |
|
#define FILENO_STDIN 0 |
#define FILENO_STDOUT 1 |
#define FILENO_STDERR 2 |
#define FILENO_AUX 3 |
#define MAX_FILDES 4 |
|
typedef void (*RWFDFUN)(struct TASK_S *,int, void *, int); |
|
typedef struct { |
void (*write)(struct TASK_S *tsk, int id, void *buf, int len); |
void (*read)( struct TASK_S *tsk, int id, void *buf, int len); |
void (*close)(struct TASK_S *tsk, int id); |
} KDEVICE; |
|
typedef struct { |
int id; |
KDEVICE *dev; |
} KFILDES; |
|
#ifndef NULL |
#define NULL (void *)0 |
#endif |
|
#endif |
/sw/zipos/taskp.h
0,0 → 1,79
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: taskp.h |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: Defines the components of a task structure, containing all the |
// resources necessary to comprehend a user task: memory, state, |
// interrupt information, etc. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#ifndef TASKP_H |
#define TASKP_H |
|
#include "zipsys.h" |
#include "ksched.h" |
#include "kfildes.h" |
|
#define MAX_KFILDES 4 |
|
typedef struct TASK_S { // Cost: 22+user_data |
KSCHED_STATE state; |
int context[16]; |
KFILDES *fd[MAX_KFILDES]; |
int *user_heap, errno; |
// Interrupt processing. Waitsig is a list of interupts the task |
// wishes to be woken for. Pending is the list it hasn't yet seen. |
int waitsig, pending; |
// Similarly, timeout is when the task wishes to be woken up (if set). |
unsigned timeout; |
int user_data[1]; |
// |
} TASK, *TASKP; |
|
typedef void (*VENTRYP)(void); |
typedef void (*FENTRYP)(int a, int b, int c, int d); |
|
// |
// Create a new task with ln words of memory and entry point entry. |
// |
TASKP new_task(unsigned ln, VENTRYP entry); |
|
// |
// The entry point for the "special" idle task, whose only purpose is to call |
// the idle function as often as possible. (The CPU does nothing in idle mode) |
// Calling this task will prevent the CPU from doing other more useful things, |
// so it is the lowest priority task. |
// |
extern void idle_task(void); |
|
#endif |
/sw/zipos/resetdump.s
0,0 → 1,562
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;// |
;; |
;; Filename: resetdump.s |
;; |
;; Project: CMod S6 System on a Chip, ZipCPU demonstration project |
;; |
;; Purpose: While most of my ZipCPU programs to date have started with a |
;; simple assembly script to load the stack and call the program |
;; entry point, this is different. This is a very complicated startup |
;; script designed to dump all of the internal information to the CPU |
;; to a UART output port. This is on purpose. Indeed, this may be my |
;; only means of debugging the device once it goes bad: |
;; |
;; - To set a breakpoint |
;; at the location desired call kpanic(), the CPU will dump its |
;; variables and restart. |
;; sometime before the desired clock, set the watchdog timer |
;; (currently TIMER_B). When the watchdog expires, |
;; the CPU will restart. Adjusting the watchdog will |
;; adjust how long the CPU waits before restarting, and |
;; may also adjust what instructions you find going on |
;; |
;; - In hardware, you can set the scope. On boot up, this resetdump |
;; startup will dump the value of the scope to the UART. |
;; |
;; Of course, this all depends upon someone listening on the uart. That's |
;; the purpose of the dumpuart.cpp program in the sw/host directory. |
;; That file will capture the dump so it can be examined later. |
;; |
;; Creator: Dan Gisselquist, Ph.D. |
;; Gisselquist Technology, LLC |
;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;// |
;; |
;; Copyright (C) 2015-2016, Gisselquist Technology, LLC |
;; |
;; This program is free software (firmware): you can redistribute it and/or |
;; modify it under the terms of the GNU General Public License as published |
;; by the Free Software Foundation, either version 3 of the License, or (at |
;; your option) any later version. |
;; |
;; This program is distributed in the hope that it will be useful, but WITHOUT |
;; ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
;; for more details. |
;; |
;; You should have received a copy of the GNU General Public License along |
;; with this program. (It's in the $(ROOT)/doc directory, run make with no |
;; target there if the PDF file isn't present.) If not, see |
;; <http://www.gnu.org/licenses/> for a copy. |
;; |
;; License: GPL, v3, as defined and found on www.gnu.org, |
;; http://www.gnu.org/licenses/gpl.html |
;; |
;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;// |
;; |
;; |
.section .start |
.global _start |
.type _start,@function |
_start: |
; Upon reset, we must output our registers to the UART, lest we reset because |
; of a crash |
STO R0,(DBG) |
MOV PC+1,R0 |
BRA internal_kpanic |
LDI _top_of_stack,SP |
BRA kernel_entry |
|
.global kpanic |
.type kpanic,@function |
kpanic: |
STO R0,(DBG) |
STO R1,1(DBG) |
STO R2,2(DBG) |
LDI 'P',R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI 'a',R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI 'n',R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI 'i',R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI 'c',R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI ':',R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI ' ',R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI '\r',R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI '\n',R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LOD 1(DBG),R1 |
LOD 2(DBG),R2 |
MOV PC+1,R0 |
JMP internal_kpanic |
HALT |
|
internal_kpanic: |
STO R1,1(DBG) |
STO R2,2(DBG) |
STO R0,3(DBG) ; Our return address |
|
; R0 |
LDI 0,R1 |
LOD (DBG),R2 |
MOV .Lcall0(PC),R0 |
JMP uart_put_reg_value |
.Lcall0: |
|
; R1 |
LDI 1,R1 |
LOD 1(DBG),R2 |
MOV .Lcall1(PC),R0 |
JMP uart_put_reg_value |
.Lcall1: |
; R2 |
LDI 2,R1 |
LOD 2(DBG),R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
; R3 |
LDI 3,R1 |
MOV R3,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; R4 |
LDI 4,R1 |
MOV R4,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; R5 |
LDI 5,R1 |
MOV R5,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; R6 |
LDI 6,R1 |
MOV R6,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; R7 |
LDI 7,R1 |
MOV R7,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; R8 |
LDI 8,R1 |
MOV R8,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; R9 |
LDI 9,R1 |
MOV R9,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; R10 |
LDI 10,R1 |
MOV R10,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; R11 |
LDI 11,R1 |
MOV R11,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; R12 |
LDI 12,R1 |
MOV R12,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; SP |
LDI 13,R1 |
MOV R13,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; uR0 |
LDI 16,R1 |
MOV uR0,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; uR1 |
LDI 17,R1 |
MOV uR1,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
LDI 18,R1 |
MOV uR2,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
LDI 19,R1 |
MOV uR3,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
LDI 20,R1 |
MOV uR4,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
LDI 21,R1 |
MOV uR5,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
LDI 22,R1 |
MOV uR6,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
LDI 23,R1 |
MOV uR7,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
LDI 24,R1 |
MOV uR8,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
LDI 25,R1 |
MOV uR9,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
LDI 26,R1 |
MOV uR10,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
LDI 27,R1 |
MOV uR11,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
LDI 28,R1 |
MOV uR12,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; uSP |
LDI 29,R1 |
MOV uSP,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; uCC |
LDI 30,R1 |
MOV uCC,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
; uPC |
LDI 31,R1 |
MOV uPC,R2 |
MOV PC+1,R0 |
JMP uart_put_reg_value |
|
;stack_mem_dump: |
;LDI 0,R4 |
;LDI _top_of_stack,R5 |
;stack_mem_dump_loop: |
;MOV R4,R1 |
;LOD (R5),R2 |
;MOV PC+1,R0 |
;JMP uart_put_stack_value |
;ADD 1,R4 |
;SUB 1,R5 |
;CMP 64,R4 |
;BLT stack_mem_dump_loop |
|
; Get prepared for a proper start by setting our stack register |
LDI _top_of_stack,SP |
|
BRA dump_scope |
; BRA end_internal_panic |
|
; Now, do a full dump of all memory--all registers are available to us |
dump_memory: |
LDI RAM,R5 |
LDI 0x1000,R6 |
LDI 0x0f8,R7 |
STO R7,(SPIO) |
full_mem_dump_loop: |
MOV R5,R1 |
LOD (R5),R2 |
MOV PC+1,R0 |
JMP uart_dump_mem_value |
LDI 0x0f2,R7 |
STO R7,(SPIO) |
|
ADD 1,R5 |
SUB 1,R6 |
BGT full_mem_dump_loop |
|
LDI 0x0f5,R7 |
STO R7,(SPIO) |
|
dump_scope: |
; Finally, do a full dump of the scope--if it had triggered |
; First, dump the scope control word |
LDI SCOPE,R7 ; R7 = Debugging scope address |
MOV R7,R1 |
LOD (R7),R2 |
MOV R2,R5 ; R5 will not be changed by a subroutine |
MOV PC+1,R0 |
BRA uart_dump_mem_value |
; Then test whether or not the scope has stopped |
LDI 0x40000000,R1 |
TEST R1,R5 |
; If not, start our kernel. |
BZ dump_buserr |
; Otherwise, calculate the size of the scope |
LSR 20,R5 |
AND 0x1f,R5 |
LDI 1,R6 |
LSL R5,R6 |
; And start dumping |
ADD 1,R7 ; Get the scope data address |
dump_scope_loop: |
MOV R7,R1 |
LOD (R7),R2 |
MOV PC+1,R0 |
BRA uart_dump_mem_value |
SUB 1,R6 |
BGT dump_scope_loop |
|
dump_buserr: |
; Dump a bus error address, if used |
LDI 'B',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI 'u',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI 's',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI 'E',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI 'r',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI 'r',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI ':',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI ' ',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI BUSERR,R1 |
LOD (R1),R2 |
MOV PC+1,R0 |
BRA uart_dump_mem_value |
|
end_internal_panic: |
LDI '\r',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI '\n',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI '\r',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI '\n',R1 |
MOV PC+1,R0 |
BRA raw_put_uart |
LDI 0x0ff,R7 |
STO R7,(SPIO) |
LOD 3(DBG),PC |
JMP R0 |
|
; R0 is return address |
; R1 is register ID |
; R2 is register to output |
uart_put_reg_value: |
STO R0,4(DBG) |
STO R2,5(DBG) |
STO R3,6(DBG) |
MOV R1,R2 |
LDI 'u',R1 |
CMP 16,R2 |
LDILO.LT 's',R1 |
SUB.GE 16,R2 |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI '0',R1 |
CMP 10,R2 |
LDILO.GE '1',R1 |
SUB.GE 10,R2 |
MOV PC+1,R0 |
JMP raw_put_uart |
MOV R2,R1 |
AND 15,R1 |
MOV PC+1,R0 |
JMP get_hex |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI 58,R1 ; A ':' |
MOV PC+1,R0 |
JMP raw_put_uart |
LOD 5(DBG),R2 |
LDI 8,R3 |
uart_put_loop: |
ROL 4,R2 |
MOV R2,R1 |
AND 15,R1 |
MOV PC+1,R0 |
JMP get_hex |
MOV PC+1,R0 |
JMP raw_put_uart |
SUB 1,R3 |
BNZ uart_put_loop |
LDI '\r',R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI '\n',R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LOD 4(DBG),R0 |
LOD 5(DBG),R2 |
LOD 6(DBG),R3 |
JMP R0 |
|
uart_dump_mem_value: |
; R0 = return address |
; R1 = Memory address |
; R2 = Memory Value |
; Local: R3 = working value |
STO R0,7(DBG) |
MOV R1,R3 ; R3 = Memory address |
MOV R2,R4 ; R4 = Memory Value |
LDI 77,R1 ; 77 = 'M' |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI 91,R1 ; 91 = '[' |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI 48,R1 ; A '0' |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI 120,R1 ; An 'x' |
MOV PC+1,R0 |
JMP raw_put_uart |
; Set up a loop to dump things |
ROL 16,R3 ; Ignore the first 16 bits |
LDI 4,R2 ; We're going to do four hex digits here |
; |
uart_put_mem_address_loop: |
ROL 4,R3 |
MOV R3,R1 |
AND 15,R1 |
MOV PC+1,R0 |
JMP get_hex |
MOV PC+1,R0 |
JMP raw_put_uart |
SUB 1,R2 |
BNZ uart_put_mem_address_loop |
; Put some transition characters |
LDI 93,R1 ; 93 = ']' |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI 58, R1 ; A semicolon |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI 32, R1 ; A space |
MOV PC+1,R0 |
JMP raw_put_uart |
|
; Set up a loop to dump the memory value now |
LDI 8,R2 |
uart_put_mem_value_loop: |
ROL 4,R4 |
MOV R4,R1 |
AND 15,R1 |
MOV PC+1,R0 |
JMP get_hex |
MOV PC+1,R0 |
JMP raw_put_uart |
SUB 1,R2 |
BNZ uart_put_mem_value_loop |
; Clear the line |
LDI '\r', R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
LDI '\n', R1 |
MOV PC+1,R0 |
JMP raw_put_uart |
; And return from our subroutine |
LOD 7(DBG),R0 |
JMP R0 |
|
get_hex: |
ADD 0x30,R1 |
CMP 0x39,R1 |
ADD.GT 7,R1 ; Add 'A'-'0'-10 |
JMP R0 |
|
raw_put_uart: ; R0 is return address, R1 is value to transmit |
STO R2,8(DBG) |
LDI INT_UARTTX,R2 |
STO R2,(PIC) ; Clear the PIC, turn off interrupts, etc. |
raw_put_uart_retest: |
LOD (PIC),R2 |
TEST INT_UARTTX,R2 |
BZ raw_put_uart_retest |
STO R1,(UART) |
LOD 8(DBG),R2 |
JMP R0 |
|
.section .fixdata |
DBG: |
.byte 0,0,0,0,0,0,0,0,0 |
|
.set INT_UARTRX, 0x040 |
.set INT_UARTTX, 0x080 |
.set PIC, 0x0100 |
.set BUSERR, 0x0101 |
.set TMRA, 0x0102 |
.set TMRB, 0x0103 |
.set PWM, 0x0104 |
.set SPIO, 0x0105 |
.set GPIO, 0x0106 |
.set UART, 0x0107 |
.set VERSION, 0x0108 |
.set SCOPE, 0x0200 |
.set SCOPED, 0x0201 |
.set CLOCK, 0x0800 |
.set CONFIG, 0x0400 |
.set TIMER, 0x0801 |
.set STOPWATCH,0x802 |
.set ALARM, 0x0803 |
.set RAM, 0x2000 |
/sw/zipos/ziplib.c
0,0 → 1,9
#include "ziplib.h" |
|
void *memset(void *s, int c, unsigned n) { |
int *p = s; |
do { |
*p++ = c; |
} while(n-- > 0); |
} |
|
/sw/zipos/errno.h
0,0 → 1,51
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: errno.h |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: Encodes a series of error numbers for the ZipOS. These are |
// loosely based upon the Linux error codes, but they are by no |
// means complete. Still ... they are complete enough for what is here. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#ifndef ZIPOS_ERRNO_BASE_H |
#define ZIPOS_ERRNO_BASE_H |
|
#define EIO 5 |
#define EBADF 9 |
#define EFAULT 14 |
#define EBUSY 16 |
// |
#define EHEAP 50 // Heap overflow error |
#define EBUS 51 // Bus (memory) error |
|
#endif |
/sw/zipos/syspipe.c
0,0 → 1,515
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: syspipe.c |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: This "device" handles the primary device level interaction of |
// almost all devices on the ZipOS: the pipe. A pipe, as defined |
// here, is an O/S supported FIFO. Information written to the FIFO will |
// be read from the FIFO in the order it was received. Attempts to read |
// from an empty FIFO, or equivalently to write to a full FIFO, will block |
// the reading (writing) process until memory is available. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include "errno.h" |
#include "board.h" |
#include "taskp.h" |
#include "syspipe.h" |
#include "zipsys.h" |
#include "ktraps.h" |
|
#ifndef NULL |
#define NULL (void *)0 |
#endif |
|
static void clear_syspipe(SYSPIPE *p) { |
p->m_head = 0; |
p->m_tail = 0; |
p->m_error = 0; |
|
for(int i=0; i<=(int)p->m_mask; i++) |
p->m_buf[i] = 0; |
|
if ((p->m_rdtask)&&(p->m_rdtask != INTERRUPT_READ_TASK)) { |
p->m_rdtask->context[1] = p->m_nread; |
if (p->m_nread == 0) |
p->m_rdtask->errno = -EFAULT; |
p->m_rdtask->state = SCHED_READY; |
} else if (p->m_wrtask) { |
p->m_wrtask->context[1] = p->m_nwritten; |
if (p->m_nwritten == 0) |
p->m_wrtask->errno = -EFAULT; |
p->m_wrtask->state = SCHED_READY; |
} |
|
if (p->m_rdtask != INTERRUPT_READ_TASK) |
p->m_rdtask = 0; |
p->m_wrtask = 0; |
p->m_nread = 0; |
p->m_nwritten = 0; |
} |
|
void kpush_syspipe(SYSPIPE *pipe, int val) { |
int tst = (pipe->m_head+1)&pipe->m_mask; |
if (tst != pipe->m_tail) { |
pipe->m_buf[pipe->m_head] = val; |
pipe->m_head = tst; // Increment the head pointer |
if ((pipe->m_rdtask)&&(pipe->m_rdtask != INTERRUPT_READ_TASK)) |
pipe->m_rdtask->state = SCHED_READY; |
} else pipe->m_error = 1; |
} |
|
void txchr(char v) { |
volatile IOSPACE *sys = (IOSPACE *)IOADDR; |
if (v < 10) |
return; |
v &= 0x0ff; |
sys->io_pic = INT_UARTTX; |
while((sys->io_pic&INT_UARTTX)==0) |
; |
sys->io_uart = v; |
} |
|
void txstr(const char *str) { |
const char *ptr = str; |
while(*ptr) { |
txchr(*ptr++); |
} |
} |
|
void txhex(int num) { |
for(int ds=28; ds>=0; ds-=4) { |
int ch; |
ch = (num>>ds)&0x0f; |
if (ch >= 10) |
ch = 'A'+ch-10; |
else |
ch += '0'; |
txchr(ch); |
} txstr("\r\n"); |
} |
|
void pipe_panic(SYSPIPE *pipe) { |
extern void kpanic(void); |
volatile IOSPACE *sys = (IOSPACE *)IOADDR; |
|
sys->io_spio = 0x0fa; |
|
txstr("SYSPIPE PANIC!\r\n"); |
txstr("ADDR: "); txhex((int)pipe); |
txstr("MASK: "); txhex(pipe->m_mask); |
txstr("HEAD: "); txhex(pipe->m_head); |
txstr("TAIL: "); txhex(pipe->m_tail); |
kpanic(); |
} |
|
int kpop_syspipe(SYSPIPE *pipe, int *vl) { |
if (pipe->m_head != pipe->m_tail) { |
*vl = pipe->m_buf[pipe->m_tail]; |
pipe->m_tail++; |
if ((unsigned)pipe->m_tail > pipe->m_mask) |
pipe->m_tail = 0; |
if (pipe->m_wrtask) |
pipe->m_wrtask->state = SCHED_READY; |
return 0; |
} return 1; // Error condition |
} |
|
SYSPIPE *new_syspipe(const unsigned int len) { |
unsigned msk; |
|
for(msk=2; msk<len; msk<<=1) |
; |
SYSPIPE *pipe = sys_malloc(sizeof(SYSPIPE)-1+msk); |
pipe->m_mask = msk-1; |
pipe->m_rdtask = pipe->m_wrtask = 0; |
clear_syspipe(pipe); |
return pipe; |
} |
|
int len_syspipe(SYSPIPE *p) { |
return (p->m_head-p->m_tail) & p->m_mask; |
} |
int num_avail_syspipe(SYSPIPE *p) { |
return (p->m_mask + p->m_tail-p->m_head) & p->m_mask; |
} |
|
// This will be called from a user context. |
// Another task may write to the pipe during this call. If the pipe becomes |
// full, that task will block. |
// |
static int uread_syspipe(TASKP tsk __attribute__((__unused__)), SYSPIPE *p, int *dst, int len) { |
int nleft= len, h; |
if (len == 0) { |
// We'll only get here if we were released from within a |
// writing task. |
return p->m_nread; |
} else do { |
// We have a valid read request, for a new process. Continue |
// 'reading' until we have fulfilled the request. |
// |
// We can read from head, just not write to it |
// As for the tail pointer -- we own it, no one else can touch |
// it. |
h = ((volatile SYSPIPE *)p)->m_head; |
if (h < p->m_tail) { |
// The buffer wraps around the end. Thus, we first |
// read anything between the tail pointer and the end |
int ln1 = p->m_mask+1 - p->m_tail; // Navail to be read |
ln1 = (ln1 > nleft) ? nleft : ln1; |
if (ln1 > 0) { |
register int *src = &p->m_buf[p->m_tail]; |
for(int i=0; i<ln1; i++) |
*dst++ = *src++; |
|
p->m_nread += ln1; |
nleft -= ln1; |
p->m_tail += ln1; |
if ((unsigned)p->m_tail > p->m_mask) |
p->m_tail = 0; |
} |
|
// nleft is either zero, or tail |
if (nleft & -2) |
exit(nleft); |
else if (p->m_nread & -2) |
exit(p->m_nread); |
} |
|
// Then repeat with the second half of the buffer, from the |
// beginning to the head--unless we've exhausted our buffer. |
if (nleft > 0) { |
// Still need to do more, wrap around our buffer and |
// restart |
int ln1 = h - p->m_tail; |
ln1 = (ln1 < nleft) ? ln1 : nleft; |
|
int *src = &p->m_buf[p->m_tail]; |
for(int i=0; i<ln1; i++) |
*dst++ = *src++; |
|
p->m_nread += ln1; |
nleft -= ln1; |
p->m_tail += ln1; |
if (p->m_tail == (int)p->m_mask+1) |
p->m_tail = 0; |
|
if (nleft & -2) |
exit(nleft); |
else if (p->m_nread & -2) |
exit(p->m_nread); |
} |
|
if (nleft == 0) |
break; |
|
// We didn't finish our read, check for a blocked writing |
// process to copy directly from. Note that we don't need |
// to check the status of the write task--if it is set and |
// we are active, then it is blocked and waiting for us to |
// complete. Note also that this is treated as a volatile |
// pointer. It can change from one time through our loop |
// to the next. |
if (((volatile SYSPIPE *)p)->m_wrtask) { |
int *src, ln; |
|
// If the head changed before the write task blocked, |
// then go around again and copy some more before |
// getting started |
// |
// This should never happen, however. If a write task |
// gets assigned while a read task exists, it doesn't |
// write its values into the buffer, it just waits. |
// therefore we don't need to check for this. |
// |
// if (p->m_head != h) |
// continue; |
|
ln = nleft; |
if (p->m_wrtask->context[4] < nleft) |
ln = p->m_wrtask->context[4]; |
src = (int *)p->m_wrtask->context[3]; |
|
for(int i=0; i<ln; i++) |
*dst++ = *src++; |
|
p->m_nwritten += ln; |
p->m_nread += ln; |
|
nleft -= ln; |
p->m_wrtask->context[4] -= ln; |
p->m_wrtask->context[3] = (int)src; |
|
// We have exhausted the write task. Release it |
if (p->m_wrtask->context[4] == 0) { // wr_len == 0 |
// Release the write task, it has exhausted |
// its buffer |
TASKP w = p->m_wrtask; |
// Now we allow other tasks to write into our |
// pipe |
p->m_wrtask = 0; |
// And here we actually release the writing |
// task |
w->state = SCHED_READY; |
} |
} |
|
// Realistically, we need to block here 'till more data is |
// available. Need to determine how to do that. Until then, |
// we'll just tell the scheduler to yield. This will in |
// effect create a busy wait--not what we want, but it'll work. |
if (nleft > 0) { |
DISABLE_INTS(); |
h = ((volatile SYSPIPE *)p)->m_head; |
if (h == p->m_tail) |
wait(0,-1); |
else |
ENABLE_INTS(); |
} |
} while(nleft > 0); |
|
len = p->m_nread; |
p->m_nread = 0; |
// Release our ownership of the read end of the pipe |
DISABLE_INTS(); |
p->m_rdtask = NULL; |
if (((volatile SYSPIPE *)p)->m_wrtask) |
p->m_wrtask->state = SCHED_READY; |
ENABLE_INTS(); |
|
// We have accomplished our read |
// |
return len; |
} |
|
static int uwrite_syspipe(TASKP tsk __attribute__((__unused__)), SYSPIPE *p, int *src, int len) { |
int nleft = len; |
|
// The kernel guarantees, before we come into here, that we have a |
// valid write request. |
do { |
// We try to fill this request without going through the pipes |
// memory at all. Hence, if there is a read task that is |
// waiting/suspended, waiting on a write (this must've happened |
// since we started)--write directly into the read buffer first. |
|
// If there is a read task blocked, the pipe must be empty |
TASKP rdtask = ((volatile SYSPIPE *)p)->m_rdtask; |
if (rdtask == INTERRUPT_READ_TASK) { |
// We need to copy everything to the buffer |
} else if (rdtask) { |
// #warning "The previous code should have worked" |
// if (((unsigned)rdtask+1) & -2) |
int ln = nleft; |
if (ln > p->m_rdtask->context[4]) |
ln = p->m_rdtask->context[4]; |
int *dst = (int *)p->m_rdtask->context[3]; |
for(int i=0; i<ln; i++) |
*dst++ = *src++; |
p->m_nread += ln; |
p->m_rdtask->context[3]+= ln; |
p->m_rdtask->context[4]-= ln; |
nleft -= ln; |
p->m_nwritten += ln; |
|
// Realistically, we always need to wake up the reader |
// at this point. Either 1) we exhausted the readers |
// buffer, or 2) we exhausted our own and the reader |
// needs to take over. Here, we only handle the first |
// case, leaving the rest for later. |
if (p->m_rdtask->context[4] == 0) { |
TASKP r = p->m_rdtask; |
// Detach the reader task |
p->m_rdtask = 0; |
// Wake up the reader |
r->state = SCHED_READY; |
} |
|
// While it might appear that we might close our loop |
// here, that's not quite the case. It may be that the |
// pipe is read from an interrupt context. In that |
// case, there will never be any reader tasks, but we |
// will still need to loop. |
|
// Now that we've filled any existing reader task, we |
// check whether or not we fit into the buffer. The |
// rule is: don't write into the buffer unless |
// everything will fit. Why? Well, if you have to |
// block anyway, why not see if you can't avoid a |
// double copy? |
if (nleft == 0) |
break; |
} |
|
// Copy whatever we have into the pipe's buffer |
if ((nleft <= num_avail_syspipe(p))||(rdtask == INTERRUPT_READ_TASK)) { |
// Either there is no immediate reader task, or |
// the reader has been exhausted, but we've go |
// more to write. |
// |
// Note that we no longer need to check what |
// will fit into the pipe. We know the entire |
// rest of our buffer will fit. |
|
{ // Write into the first half of the pipe |
int ln = p->m_mask+1-p->m_head; |
int *dst = &p->m_buf[p->m_head]; |
if (ln > nleft) ln = nleft; |
|
for(int i=0; i<ln; i++) |
*dst++ = *src++; |
|
p->m_head += ln; |
nleft -= ln; |
p->m_nwritten += ln; |
if (p->m_head > (int)p->m_mask) |
p->m_head = 0; |
} |
|
// Write into the rest of the pipe |
if (nleft > 0) { |
int ln = num_avail_syspipe(p); |
if (nleft < ln) |
ln = nleft; |
int *dst = &p->m_buf[p->m_head]; |
|
for(int i=0; i<ln; i++) |
*dst++ = *src++; |
|
p->m_head += ln; |
p->m_nwritten += ln; |
nleft -= ln; |
} |
} |
|
if (nleft > 0) { |
if (rdtask == INTERRUPT_READ_TASK) { |
DISABLE_INTS(); |
if (num_avail_syspipe(p)==0) |
wait(0,-1); |
else ENABLE_INTS(); |
} else { |
DISABLE_INTS(); |
if (!((volatile SYSPIPE *)p)->m_rdtask) |
wait(0,-1); // Should really be a wait |
// on JIFFIES and if JIFFIES expired |
// (i.e. write timeout) then break; |
else ENABLE_INTS(); |
} |
} |
} while(nleft > 0); |
|
int nw= p->m_nwritten; |
p->m_wrtask = 0; |
return nw; |
} |
|
// This will be called from a kernel (interrupt) context |
void kread_syspipe(TASKP tsk, int dev, int *dst, int len) { |
SYSPIPE *p = (SYSPIPE *)dev; |
if (p->m_rdtask != NULL) { |
// If the pipe already has a read task, then we fail |
tsk->context[1] = -EBUSY; |
zip_halt(); |
} else if (p->m_error) { |
// If there's been an overrun, let the reader know on the |
// next read--i.e. this one. Also, clear the error condition |
// so that the following read will succeed. |
tsk->context[1] = -EIO; |
p->m_tail = p->m_head; |
p->m_error = 0; |
} else if (len <= 0) { |
tsk->context[1] = -EFAULT; |
zip_halt(); |
} else if (!valid_ram_region(dst, len)) { |
// Bad parameters |
tsk->context[1] = -EFAULT; |
zip_halt(); |
} else { |
// Take ownership of the read end of the pipe |
p->m_rdtask = tsk; |
p->m_nread = 0; |
tsk->context[1] = (int)tsk; |
tsk->context[2] = (int)p; |
// These are already set, else we'd set them again |
// tsk->context[3] = (int)dst; |
// tsk->context[4] = len; |
tsk->context[15] = (int)uread_syspipe; |
|
// If there is already a write task, make sure it is awake |
if (p->m_wrtask) { |
tsk->state = SCHED_WAITING; |
p->m_wrtask->state = SCHED_READY; |
} else if (p->m_head == p->m_tail) |
// If the pipe is empty, block the read task |
tsk->state = SCHED_WAITING; |
|
// On return, this will bring us back to user space, inside our |
// user space version of the read system call |
} |
} |
|
void kwrite_syspipe(TASKP tsk, int dev, int *src, int len) { |
SYSPIPE *p = (SYSPIPE *)dev; |
if (p->m_wrtask != NULL) { |
// If the pipe already has a write task, then we fail |
tsk->context[1] = -EBUSY; |
} else if (len <= 0) { |
tsk->context[1] = -EFAULT; |
} else if (!valid_mem_region(src, len)) { |
// Bad parameters |
tsk->context[1] = -EFAULT; |
zip_halt(); |
} else { |
// Take ownership of the write end of the pipe |
p->m_wrtask = tsk; |
p->m_nwritten = 0; |
tsk->context[1] = (int)tsk; |
tsk->context[2] = (int)p; |
// These are already set, else we'd set them again |
// tsk->context[3] = (int)src; |
// tsk->context[4] = len; |
tsk->context[15] = (int)uwrite_syspipe; |
|
// If a reader task currently exists, then block until that |
// task either finishes or releases us |
if ((p->m_rdtask)&&(p->m_rdtask != INTERRUPT_READ_TASK)) { |
tsk->state = SCHED_WAITING; |
p->m_rdtask->state = SCHED_READY; |
} else if (((p->m_head+1)&p->m_mask) == (unsigned)p->m_tail) |
// If the pipe is empty, block until there's data |
tsk->state = SCHED_WAITING; |
|
// On return, this will bring us back to user space, in our |
// user space write call |
} |
} |
/sw/zipos/board.h
0,0 → 1,128
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: board.h |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: To define the interfaces to the peripherals on the board, as |
// given by the ZipCPU's view of the board. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#ifndef BOARD_H |
#define BOARD_H |
|
// GPIO PINS |
// first the outputs ... |
#define GPO_SDA 0x000001 |
#define GPO_SCL 0x000002 |
#define GPO_MOSI 0x000004 |
#define GPO_SCK 0x000008 |
#define GPO_SS 0x000010 |
// then the inputs. |
#define GPI_SDA 0x010000 |
#define GPI_SCL 0x020000 |
#define GPI_MISO 0x040000 |
|
#define GPOSETV(PINS) ((PINS)|((PINS)<<16)) |
#define GPOCLRV(PINS) ((PINS)<<16) |
|
// Interrupts |
#define INT_ENABLE 0x80000000 |
#define INT_BUTTON 0x001 |
#define INT_BUSERR 0x002 // Kind of useless, a buserr will kill us anyway |
#define INT_SCOPE 0x004 |
#define INT_RTC 0x008 // May not be available, due to lack of space |
#define INT_TIMA 0x010 |
#define INT_TIMB 0x020 |
#define INT_UARTRX 0x040 |
#define INT_UARTTX 0x080 |
#define INT_KEYPAD 0x100 |
#define INT_AUDIO 0x200 |
#define INT_GPIO 0x400 |
// #define INT_FLASH 0x800 // Not available due to lack of space |
#define INT_ENABLEV(IN) (INT_ENABLE|((IN)<<16)) |
#define INT_DISABLEV(IN) ((IN)<<16) |
#define INT_CLEAR(IN) (IN) |
|
// Clocks per second, for use with the timer |
#define TM_ONE_SECOND 80000000 |
#define TM_REPEAT 0x80000000 |
|
typedef struct { |
volatile int io_pic; |
volatile unsigned *io_buserr; |
volatile int io_tima, io_timb; |
volatile unsigned io_pwm_audio; |
volatile unsigned io_spio; // aka keypad, buttons, and keyboard |
volatile unsigned io_gpio; |
volatile unsigned io_uart; |
volatile unsigned io_version; |
} IOSPACE; |
|
// Wishbone scope control |
#define TRIGGER_SCOPE_NOW 0x88000000 |
#define SCOPE_IS_STOPPED 0x40000000 |
#define DISABLE_TRIGGER 0x84000000 |
typedef struct { |
volatile unsigned s_control, s_data; |
} SCOPE; |
|
typedef struct { |
volatile unsigned f_crc, f_far_maj, f_far_min, f_fdri, |
f_fdro, f_cmd, f_ctl, f_mask, |
f_stat, f_lout, f_cor1, f_cor2, |
f_pwrdn, f_flr, f_idcode, f_cwdt, |
f_hcopt, f_csbo, f_gen1, f_gen2, |
f_gen3, f_gen4, f_gen5, f_mode, |
f_gwe, f_mfwr, f_cclk, f_seu, f_exp, f_rdbk, |
f_bootsts, f_eye, f_cbc; |
} FPGACONFIG; |
|
typedef struct { |
volatile unsigned c_clock, c_timer, c_stopwatch, c_alarm; |
} RTCCLOCK; |
|
#define IOADDR 0x000100 |
#define SCOPEADDR 0x000200 |
// #define FCTLADDR 0x000300 // Flash control, depends upon write capability |
#define CONFIGADDR 0x000400 |
// #define RTCADDR 0x000800 // Disabled for lack of space on device |
#define RAMADDR 0x002000 |
#define RAMSZ 0x001000 |
#define FLASHADDR 0x400000 |
#define RESET_ADDR 0x480000 |
#define FLASHSZ 0x400000 |
|
#define valid_ram_region(PTR,LN) (((int)(PTR)>=RAMADDR)&&((int)(PTR+LN)<RAMADDR+RAMSZ)) |
#define valid_flash_region(PTR,LN) (((int)(PTR)>=FLASHADDR)&&((int)(PTR+LN)<FLASHADDR+FLASHSZ)) |
#define valid_mem_region(PTR,LN) ((valid_ram_region(PTR,LN))||(valid_flash_region(PTR,LN))) |
|
#endif |
/sw/zipos/Makefile
0,0 → 1,104
################################################################################ |
## |
## Filename: Makefile |
## |
## Project: CMod S6 System on a Chip, ZipCPU demonstration project |
## |
## Purpose: To coordinate the build of a small ZipOS. |
## |
## Creator: Dan Gisselquist, Ph.D. |
## Gisselquist Technology, LLC |
## |
################################################################################ |
## |
## Copyright (C) 2015-2016, Gisselquist Technology, LLC |
## |
## This program is free software (firmware): you can redistribute it and/or |
## modify it under the terms of the GNU General Public License as published |
## by the Free Software Foundation, either version 3 of the License, or (at |
## your option) any later version. |
## |
## This program is distributed in the hope that it will be useful, but WITHOUT |
## ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
## for more details. |
## |
## You should have received a copy of the GNU General Public License along |
## with this program. (It's in the $(ROOT)/doc directory, run make with no |
## target there if the PDF file isn't present.) If not, see |
## <http:##www.gnu.org/licenses/> for a copy. |
## |
## License: GPL, v3, as defined and found on www.gnu.org, |
## http://www.gnu.org/licenses/gpl.html |
## |
## |
################################################################################ |
## |
## |
all: |
|
CROSS := zip- |
CC := $(CROSS)gcc |
AS := $(CROSS)as |
OBJDUMP := $(CROSS)objdump |
OBJDIR := obj-zip |
DEVSRCSR:= display.c keypad.c rtcsim.c |
DEVSRCS := $(addprefix ../dev/,$(DEVSRCSR)) |
DEVOBJS := $(addprefix $(OBJDIR)/,$(subst .c,.o,$(DEVSRCSR))) |
SOURCES := kernel.c syspipe.c taskp.c doorbell.c zipsys.c # ziplib.c |
OBJECTS := $(addprefix $(OBJDIR)/,$(subst .c,.o,$(SOURCES))) $(DEVOBJS) $(OBJDIR)/resetdump.o |
HEADERS := $(wildcard *.h) $(subst .c,.h,$(DEVSRCS)) |
# CFLAGS := -O3 -fdump-tree-all -Wall -Wextra -nostdlib -fno-builtin |
# CFLAGS := -O3 -fdump-rtl-all -Wall -Wextra -nostdlib -fno-builtin |
CFLAGS := -O3 -DZIPOS -Wall -Wextra -nostdlib -fno-builtin |
# CFLAGS := -Wall -Wextra -nostdlib -fno-builtin |
LDFLAGS := -T cmod.ld -Wl,-Map,$(OBJDIR)/doorbell.map |
|
all: $(OBJDIR)/ doorbell |
$(OBJDIR)/: |
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi" |
|
%.o: $(OBJDIR)/%.o |
$(CC) $(CFLAGS) -c $< -o $@ |
|
$(OBJDIR)/%.o: %.c |
$(CC) $(CFLAGS) -c $< -o $@ |
|
$(OBJDIR)/%.o: ../dev/%.c |
$(CC) $(CFLAGS) -c $< -o $@ |
|
$(OBJDIR)/%.txt: $(OBJDIR)/%.o |
$(OBJDUMP) -d $^ -o $@ |
|
$(OBJDIR)/%.s: %.c |
$(CC) -S $(CFLAGS) -c $< -o $@ |
|
$(OBJDIR)/resetdump.o: resetdump.s |
$(AS) $^ -o $@ |
|
doorbell: $(OBJECTS) cmod.ld |
$(CC) $(LDFLAGS) $(OBJECTS) -o $@ |
|
$(OBJDIR)/doorbell.txt: doorbell |
$(OBJDUMP) -d $^ > $@ |
|
define build-depends |
@echo "Building dependency file(s)" |
@$(CC) $(CPPFLAGS) -MM $(SOURCES) $(DEVSRCS) > $(OBJDIR)/xdepends.txt |
@sed -e 's/^.*.o: /$(OBJDIR)\/&/' < $(OBJDIR)/xdepends.txt > $(OBJDIR)/depends.txt |
@rm $(OBJDIR)/xdepends.txt |
endef |
|
.PHONY: depends |
depends: $(OBJDIR)/ tags |
$(build-depends) |
|
tags: $(SOURCES) $(HEADERS) |
@echo "Generating tags" |
@ctags $(SOURCES) $(DEVSRCS) $(HEADERS) |
|
.PHONY: clean |
clean: |
rm -rf $(OBJDIR)/ doorbell |
|
-include $(OBJDIR)/depends.txt |