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

Subversion Repositories s6soc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /s6soc
    from Rev 21 to Rev 22
    Reverse comparison

Rev 21 → Rev 22

/trunk/sw/zipos/ziplib.h
0,0 → 1,6
#ifndef ZIPLIB_H
#define ZIPLIB_H
 
void *memset(void *s, int c, unsigned n);
 
#endif
/trunk/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 = .;
}
/trunk/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
/trunk/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
 
 
/trunk/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
 
/trunk/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
/trunk/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
/trunk/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
/trunk/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
/trunk/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);
}
}
 
*/
 
/trunk/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);
}
 
/trunk/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
/trunk/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
/trunk/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
/trunk/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);
}
 
/trunk/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
/trunk/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
}
}
/trunk/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
/trunk/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

powered by: WebSVN 2.1.0

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