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

Subversion Repositories s6soc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /s6soc/trunk/sw/zipos
    from Rev 44 to Rev 45
    Reverse comparison

Rev 44 → Rev 45

/Makefile
42,49 → 42,56
AS := $(CROSS)as
OBJDUMP := $(CROSS)objdump
OBJDIR := obj-zip
DEVSRCSR:= display.c keypad.c rtcsim.c
DEVSRCSR:= display.c keypad.c rtcsim.c txfns.c
DEVSRCS := $(addprefix ../dev/,$(DEVSRCSR))
DEVOBJS := $(addprefix $(OBJDIR)/,$(subst .c,.o,$(DEVSRCSR)))
SOURCES := bootloader.c kernel.c ksetup.c syspipe.c pipesetup.c taskp.c doorbell.c zipsys.c # ziplib.c
SOURCES := bootloader.c kernel.c ksetup.c syspipe.c pipesetup.c taskp.c doorbell.c zipsys.c string.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 -DZIPOS -Wall -Wextra -nostdlib -fno-builtin
CFLAGS := -O3 -DZIPOS -Wall -Wextra -nostdlib -fno-builtin
# CFLAGS := -Wall -Wextra -nostdlib -fno-builtin
LDFLAGS := -T cmodram.ld -Wl,-Map,$(OBJDIR)/doorbell.map
CFLAGS := -I. -I../dev -Os -DZIPOS -Wall -Wextra -nostdlib -fno-builtin -Wa,-nocis
LDFLAGS := -T cmodram.ld -Wl,-Map,$(OBJDIR)/doorbell.map -nostdlib
 
all: $(OBJDIR)/ doorbell
all: doorbell
$(OBJDIR)/:
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi"
$(mk-objdir)
 
%.o: $(OBJDIR)/%.o
$(CC) $(CFLAGS) -c $< -o $@
 
$(OBJDIR)/%.o: %.c
$(mk-objdir)
$(CC) $(CFLAGS) -c $< -o $@
 
$(OBJDIR)/%.o: ../dev/%.c
$(mk-objdir)
$(CC) $(CFLAGS) -c $< -o $@
 
$(OBJDIR)/%.txt: $(OBJDIR)/%.o
$(OBJDUMP) -d $^ -o $@
$(OBJDUMP) -Dr $^ > $@
 
$(OBJDIR)/%.s: %.c
$(mk-objdir)
$(CC) -S $(CFLAGS) -c $< -o $@
 
$(OBJDIR)/%.s: ../dev/%.c
$(mk-objdir)
$(CC) -S $(CFLAGS) -c $< -o $@
 
$(OBJDIR)/resetdump.o: resetdump.s
$(AS) $^ -o $@
$(mk-objdir)
$(AS) -nocis $^ -o $@
 
doorbell: $(OBJECTS) cmodram.ld
$(CC) $(LDFLAGS) $(OBJECTS) -o $@
 
$(OBJDIR)/doorbell.txt: doorbell
doorbell.txt: doorbell
$(OBJDUMP) -d $^ > $@
 
define mk-objdir
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi"
endef
 
define build-depends
@echo "Building dependency file(s)"
@$(CC) $(CPPFLAGS) -MM $(SOURCES) $(DEVSRCS) > $(OBJDIR)/xdepends.txt
93,7 → 100,8
endef
 
.PHONY: depends
depends: $(OBJDIR)/ tags
depends: tags
$(mk-objdir)
$(build-depends)
 
tags: $(SOURCES) $(HEADERS)
/board.h
59,9 → 59,8
#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_TIMER 0x010
//#define INT_WATCHDOG 0x020 // Catching a watchdog/reset interrupt makes no sense
#define INT_UARTRX 0x040
#define INT_UARTTX 0x080
#define INT_KEYPAD 0x100
71,6 → 70,8
#define INT_ENABLEV(IN) (INT_ENABLE|((IN)<<16))
#define INT_DISABLEV(IN) ((IN)<<16)
#define INT_CLEAR(IN) (IN)
#define INT_CLEARPIC 0x7fff7fff
#define INT_DALLPIC 0x7fff0000
 
// Clocks per second, for use with the timer
#define TM_ONE_SECOND 80000000
77,50 → 78,46
#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;
int io_pic;
unsigned *io_buserr;
int io_timer, io_watchdog;
unsigned io_pwm_audio;
unsigned io_spio; // aka keypad, buttons, and keyboard
unsigned io_gpio;
unsigned io_uart;
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;
#define WBSCOPE_NO_RESET 0x80000000
#define WBSCOPE_MANUAL WBSCOPE_TRIGGER
//
#define WBSCOPE_STOPPED 0x40000000
#define WBSCOPE_TRIGGERED 0x20000000
#define WBSCOPE_PRIMED 0x10000000
#define WBSCOPE_TRIGGER (0x08000000|WBSCOPE_NO_RESET)
#define WBSCOPE_DISABLED 0x04000000
#define WBSCOPE_DISABLE 0x04000000 // Disable the scope trigger
#define WBSCOPE_RZERO 0x02000000 // Unused,true if ptd at begning
#define WBSCOPE_LGLEN(A) ((A>>20)&0x01f)
#define WBSCOPE_LENGTH(A) (1<<(WBSCOPE_LGLEN(A)))
 
typedef struct {
volatile unsigned c_clock, c_timer, c_stopwatch, c_alarm;
} RTCCLOCK;
typedef struct WBSCOPE_S {
unsigned s_ctrl, s_data;
} WBSCOPE;
 
#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 IOADDR 0x000400
#define SCOPEADDR 0x000800
// #define FCTLADDR 0x000c00 // Flash control, depends upon write capability
#define RAMADDR 0x004000
#define RAMSZ (RAMADDR)
#define FLASHADDR 0x1000000
#define RESET_ADDR 0x1200000
#define FLASHSZ (FLASHADDR)
 
static volatile IOSPACE *const _sys = (IOSPACE *)IOADDR;
static volatile WBSCOPE *const _scope = (WBSCOPE *)SCOPEADDR;
 
#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)))
/bootloader.c
38,22 → 38,24
//
//
#include "board.h"
#include "bootloader.h"
 
// These values will be filled in by the linker. They are unknown at compile
// time.
extern int load_image_start, load_image_end, bss_image_end;
 
void bootloader(void) {
int len = ((int)&load_image_end) - RAMADDR;
int *flash = &load_image_start;
int *mem = (int *)RAMADDR;
void _bootloader(void) __attribute__ ((section(".boot")));
 
for(int i=0; i<len; i++)
mem[i] = flash[i];
void _bootloader(void) {
int *flash = _kernel_image_start;
int *mem = _blkram;
 
while(mem < _kernel_image_end)
*mem++ = *flash++;
 
// While I'd love to continue and clear to the end of memory, doing
// so will corrupt my stack and perhaps even my return address. Hence
// we only do this much.
for(int i=len; i< ((int)&bss_image_end)-RAMADDR; i++)
mem[i] = 0;
while(mem < _bss_image_end)
*mem++ = 0;
}
 
/cmod.ld
34,18 → 34,38
 
MEMORY
{
blkram (wx) : ORIGIN = 0x002000, LENGTH = 0x001000
flash (rx) : ORIGIN = 0x400000, LENGTH = 0x400000
blkram (wx) : ORIGIN = 0x0004000, LENGTH = 0x0004000
flash (rx) : ORIGIN = 0x1000000, LENGTH = 0x1000000
}
 
_top_of_stack = ORIGIN(blkram) + LENGTH(blkram) - 1;
_flash = ORIGIN(flash);
_blkram = ORIGIN(blkram);
_sdram = 0;
_top_of_stack = ORIGIN(blkram) + LENGTH(blkram) - 4;
_sdram_image_start = 0;
_sdram_image_end = 0;
 
SECTIONS
{
. = 0x0480000;
.rocode 0x0480000 : { *(.start) *(.text)
*(.rodata)
*(.strings) } > flash
.data : { *(.fixdata) *(.data) *(COMMON) *(.bss) } > blkram
_top_of_heap = .;
.rocode 0x1200000 : {
_boot_address = .;
*(.start) *(.boot)
*(.text*)
*(.rodata*)
*(.strings*)
} > flash
_kernel_image_start = . ;
.data : {
*(.kernel*)
*(.fixdata*)
*(.data*)
*(COMMON*)
_kernel_image_end = . ;
}> blkram AT> flash
_blkram_image_end = . ;
.bss : {
*(.bss*)
_bss_image_end = . ;
} > blkram
_top_of_heap = .;
}
/cmodram.ld
40,34 → 40,51
 
MEMORY
{
blkram (wx) : ORIGIN = 0x002000, LENGTH = 0x001000
flash (rx) : ORIGIN = 0x400000, LENGTH = 0x400000
blkram (wx) : ORIGIN = 0x0004000, LENGTH = 0x0004000
flash (rx) : ORIGIN = 0x1000000, LENGTH = 0x1000000
}
 
_top_of_stack = ORIGIN(blkram) + LENGTH(blkram) - 1;
_flash = ORIGIN(flash);
_blkram = ORIGIN(blkram);
_sdram = 0;
_top_of_stack = ORIGIN(blkram) + LENGTH(blkram) - 4;
_sdram_image_start = 0;
_sdram_image_end = 0;
 
SECTIONS
{
. = 0x0480000;
.rocode 0x0480000 : { *(.start)
.rocode 0x1200000 : ALIGN(4) {
_boot_address = .;
*(.start) *(.boot)
obj-zip/bootloader.o(.text)
obj-zip/ksetup.o(.text)
obj-zip/pipesetup.o(.text)
obj-zip/taskp.o(.text)
obj-zip/doorbell.o(.text)
obj-zip/keypad.o(.text)
obj-zip/display.o(.text)
obj-zip/rtcsim.o(.text)
*(.rodata*) *(.strings)
load_image_start = . ;
} > flash
.ramcode : {
obj-zip/kernel.o(.text)
obj-zip/syspipe.o(.text)
obj-zip/keypad.o(.text*)
obj-zip/display.o(.text*)
obj-zip/rtcsim.o(.text*)
obj-zip/doorbell.o(.text*)
*(.rodata*)
*(.strings*)
__rocode_alignment = (. + 3) & ~ 3;
. = __rocode_alignment;
} > flash
_kernel_image_start = . ;
.ramcode : ALIGN_WITH_INPUT {
obj-zip/kernel.o(.text*)
obj-zip/syspipe.o(.text*)
} > blkram AT> flash
.data : { *(.fixdata) *(.data) *(COMMON)
load_image_end = . ;
} > blkram AT> flash
.bss : { *(.bss) bss_image_end = . ; } > blkram
_top_of_heap = .;
.data : ALIGN_WITH_INPUT {
*(.kernel*)
*(.fixdata*)
*(.data*)
*(COMMON*)
_kernel_image_end = . ;
}> blkram AT> flash
_blkram_image_end = . ;
.bss : ALIGN_WITH_INPUT {
*(.bss*)
_bss_image_end = . ;
} > blkram
_top_of_heap = .;
}
/doorbell.c
53,11 → 53,18
#include "ktraps.h"
#include "errno.h"
#include "swint.h"
#include "txfns.h"
 
#include "../dev/display.h"
#include "../dev/rtcsim.h"
#include "../dev/keypad.h"
 
typedef unsigned size_t;
 
size_t strlen(const char *);
char *strcat(char *, const char *);
char *strcpy(char *, const char *);
 
/* Our system will need some pipes to handle ... life. How about these:
*
* rxpipe - read()s from this pipe read from the UART
140,6 → 147,14
*/
#define DOORBELL_TASK doorbell_task_id
 
 
/*
* Just print Hello World every 15 seconds or so. This is really a test of the
* write() and txpipe infrastructure, but not really a valid part of the task.
*
*/
// #define HELLO_TASK hello_task_id
 
#define LAST_TASK last_task_id
 
typedef enum {
157,8 → 172,8
#ifdef MENU_TASK
MENU_TASK,
#endif
#ifdef COMMAND_TASK
COMMAND_TASK,
#ifdef HELLO_TASK
HELLO_TASK,
#endif
LAST_TASK
} TASKNAME;
168,7 → 183,8
doorbell_task(void),
display_task(void),
keypad_task(void),
menu_task(void);
menu_task(void),
hello_task(void);
// idle_task ... is accomplished within the kernel
extern void restore_context(int *), save_context(int *);
extern SYSPIPE *rxpipe, *txpipe, *pwmpipe, *lcdpipe;
179,20 → 195,20
return LAST_TASK;
} void kinit(TASKP *tasklist) {
#ifdef RTCCLOCK_TASK
//
tasklist[RTCCLOCK_TASK] = new_task(16, rtctask);
// Stack = 36 (rtctask) + 4(rtcdatenext)
tasklist[RTCCLOCK_TASK] = new_task(64, rtctask);
#endif
 
#ifdef DOORBELL_TASK
#ifdef DISPLAY_TASK
// 13 + 10 +9(uwrite)+4(uarthex)+2(uartstr)+2(uartchr)
tasklist[DOORBELL_TASK] = new_task(96, doorbell_task);
// Stack = 36 + 36 (uread/write) + 24(memcpy) + 32(uarthex)+8(uartchr)
tasklist[DOORBELL_TASK] = new_task(256, doorbell_task);
// tasklist[DOORBELL_TASK]->fd[FILENO_STDOUT]= kopen((int)lcdpipe,pipedev);
tasklist[DOORBELL_TASK]->fd[FILENO_STDERR]= kopen((int)txpipe, pipedev);
tasklist[DOORBELL_TASK]->fd[FILENO_AUX] = kopen((int)pwmpipe, pipedev);
 
//
tasklist[DISPLAY_TASK] = new_task(32, display_task);
// Stack = 16 + 36(uread/write) + 24(memcpy)
tasklist[DISPLAY_TASK] = new_task(128, display_task);
tasklist[DISPLAY_TASK]->fd[FILENO_STDIN] = kopen((int)lcdpipe,pipedev);
#endif
#endif
199,18 → 215,27
 
 
#ifdef KEYPAD_TASK
// Stack = 7 + 9(uwrite) + 2*4
tasklist[KEYPAD_TASK] = new_task(32, keypad_task);
// Stack = 28 + 36(uwrite) + 24(memcpy) = 88 bytes
tasklist[KEYPAD_TASK] = new_task(128, keypad_task);
tasklist[KEYPAD_TASK]->fd[FILENO_STDOUT] = kopen((int)keypipe,pipedev);
#endif
#ifdef MENU_TASK
// Stack = 18 + 10(showbell/shownow) + 9(uwrite) + 2(menu_readkey)
// + 18 (time_menu/dawn_menu/dusk_menu)
tasklist[MENU_TASK] = new_task(72, menu_task);
// Stack = 76 + 48(showbell/shownow)
// + 36(uwrite)
// + 8(menu_readkey)
// + 24(memcpy)
// +100(time_menu/dawn_menu/dusk_menu)
//
tasklist[MENU_TASK] = new_task(512, menu_task);
// tasklist[MENU_TASK]->fd[FILENO_STDIN] = kopen((int)keypipe,pipedev);
tasklist[MENU_TASK]->fd[FILENO_STDOUT]= kopen((int)lcdpipe,pipedev);
tasklist[MENU_TASK]->fd[FILENO_STDERR]= kopen((int)txpipe, pipedev);
#endif
 
#ifdef HELLO_TASK
tasklist[HELLO_TASK] = new_task(512, hello_task);
tasklist[HELLO_TASK]->fd[FILENO_STDOUT]= kopen((int)txpipe,pipedev);
#endif
}
 
// #define HALF_HOUR_S 1800 // Seconds per half hour
223,60 → 248,58
const unsigned dawn = 0x060000, dusk = 0x180000;
#endif
 
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',' ');
const char basemsg[] = "\e[jTime: xx:xx:xx\e[1;0H ";
const char nighttime[] = "Night time";
const char daylight[] = "Daylight!";
const char dbellstr[] = "Doorbell!";
void shownow(unsigned now) {
char dmsg[40];
strcpy(dmsg, basemsg);
 
dmsg[ 9] = ((now>>20)&0x0f)+'0';
dmsg[10] = ((now>>16)&0x0f)+'0';
//
dmsg[12] = ((now>>12)&0x0f)+'0';
dmsg[13] = ((now>> 8)&0x0f)+'0';
//
dmsg[15] = ((now>> 4)&0x0f)+'0';
dmsg[16] = ((now )&0x0f)+'0';
 
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);
strcat(dmsg, nighttime);
} 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);
strcat(dmsg, daylight);
} write(FILENO_STDOUT, dmsg, strlen(dmsg));
}
 
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);
char dmsg[40];
 
strcpy(dmsg, basemsg);
 
dmsg[ 9] = ((now>>20)&0x0f)+'0';
dmsg[10] = ((now>>16)&0x0f)+'0';
//
dmsg[12] = ((now>>12)&0x0f)+'0';
dmsg[13] = ((now>> 8)&0x0f)+'0';
//
dmsg[15] = ((now>> 4)&0x0f)+'0';
dmsg[16] = ((now )&0x0f)+'0';
 
strcat(dmsg, dbellstr);
write(FILENO_STDOUT, dmsg, strlen(dmsg));
}
 
void uartchr(char v) {
if (write(FILENO_STDERR, &v, 1) != 1)
write(FILENO_STDERR, "APPLE-PANIC", 11);
write(FILENO_STDERR, "APPLE-PANIC\r\n", 13);
}
 
void uartstr(const char *str) {
int cnt=0;
while(str[cnt])
cnt++;
int cnt;
cnt = strlen(str);
if (cnt != write(FILENO_STDERR, str, cnt))
write(FILENO_STDERR, "PIPE-PANIC", 10);
write(FILENO_STDERR, "PIPE-PANIC\r\n", 12);
}
 
void uarthex(int num) {
295,11 → 318,10
#include "../dev/samples.c"
 
void belllight(unsigned now) {
IOSPACE *sys = (IOSPACE *)IOADDR;
if ((now < dawn)||(now > dusk))
sys->io_spio = 0x088; // Turn our light on
_sys->io_spio = 0x088; // Turn our light on
else
sys->io_spio = 0x80; // Turn light off
_sys->io_spio = 0x80; // Turn light off
}
 
void doorbell_task(void) {
310,13 → 332,12
// 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
_sys->io_spio = 0x080; // Turn our light off
event = wait(INT_BUTTON|SWINT_PPS,-1);
 
#ifndef MENU_TASK
340,15 → 361,15
 
// Check time: should we turn our light on or not?
belllight((volatile unsigned)rtcclock);
const int *sptr = sound_data;
// uartchr('N');
const short *sptr = sound_data;
while(sptr < &sound_data[NSAMPLE_WORDS]) {
int len = &sound_data[NSAMPLE_WORDS]-sptr;
if (len > 256)
len = 256;
if (len > 512)
len = 512;
 
// We will stall here, if the audio FIFO is full
write(FILENO_AUX, sptr, len);
write(FILENO_AUX, sptr,
sizeof(sound_data[0])*len);
sptr += len;
 
// If the user presses the button more than
398,83 → 419,65
#endif
 
#ifdef MENU_TASK
const char menustr[] = "\e[1;0H : ";
 
void entered_menu_str(char *str, unsigned now,int pos) {
//
// Set current time
// xx:xx:xx
//
str[0] = PACK(0x1b, '[', '1',';');
str[1] = PACK('0','H',' ',' ');
str[2] = PACK(' ','x','x',':');
str[3] = PACK('x','x',' ',' ');
//str[3]=PACK('x','x',':','x');
str[4] = PACK(' ','\0','\0','\0');
 
strcpy(str, menustr);
if (pos>0) {
int ch = ((now >> 20)&0x0f)+'0';
str[2] &= ~0x0ff0000;
str[2] |= (ch<<16);
str[9] = ch;
 
if (pos > 1) {
int ch = ((now >> 16)&0x0f)+'0';
str[2] &= ~0x0ff00;
str[2] |= (ch<<8);
ch = ((now >> 16)&0x0f)+'0';
str[10] = ch;
 
if (pos > 2) {
int ch = ((now >> 12)&0x0f)+'0';
str[3] &= ~0xff000000;
str[3] |= (ch<<24);
ch = ((now >> 12)&0x0f)+'0';
str[12] = ch;
 
if (pos > 3) {
int ch = ((now >> 8)&0x0f)+'0';
str[3] &= ~0x0ff0000;
str[3] |= (ch<<16);
str[13] = ch;
 
if (pos > 4) {
int ch = ((now >> 4)&0x0f)+'0';
str[3] &= ~0x0ff00;
str[3] |= ':'<<8;
str[3] &= ~0x0ff;
str[3] |= (ch);
ch = ((now >> 4)&0x0f)+'0';
str[15] = ch;
str[14] = ':';
 
if (pos > 5)
ch = (now&0x0f)+'0';
else
ch = 'x';
str[4] &= ~0x0ff000000;
str[4] |= (ch<<24);
}}}}}
str[16] = ch;
}}}}} str[17] = '\0';
}
 
const char timmenu[] = "\e[jSet current time:";
 
void show_time_menu(unsigned when, int posn) {
char dmsg[10];
dmsg[0] = PACK(0x1b,'[','j','S');
dmsg[1] = PACK('e','t',' ','c');
dmsg[2] = PACK('u','r','r','e');
dmsg[3] = PACK('n','t',' ','t');
dmsg[4] = PACK('i','m','e',':');
entered_menu_str(&dmsg[5], when, posn);
write(FILENO_STDOUT, dmsg, 9);
char dmsg[64];
strcpy(dmsg, timmenu);
entered_menu_str(&dmsg[20], when, posn);
write(FILENO_STDOUT, dmsg, strlen(dmsg));
}
 
const char dawnmenu[] = "\e[jSet sunrise: ";
void show_dawn_menu(unsigned when, int posn) {
char dmsg[10];
dmsg[0] = PACK(0x1b,'[','j','S');
dmsg[1] = PACK('e','t',' ','s');
dmsg[2] = PACK('u','n','r','i');
dmsg[3] = PACK('s','e',':','\0');
entered_menu_str(&dmsg[4], when, posn);
write(FILENO_STDOUT, dmsg, 8);
char dmsg[64];
strcpy(dmsg, dawnmenu);
entered_menu_str(&dmsg[16], when, posn);
write(FILENO_STDOUT, dmsg, strlen(dmsg));
}
 
const char duskmenu[] = "\e[;Set sunset: ";
void show_dusk_menu(unsigned when, int posn) {
char dmsg[10];
dmsg[0] = PACK(0x1b,'[','j','S');
dmsg[1] = PACK('e','t',' ','s');
dmsg[2] = PACK('u','n','s','e');
dmsg[3] = PACK('t',':','\0','\0');
entered_menu_str(&dmsg[4], when, posn);
write(FILENO_STDOUT, dmsg, 8);
char dmsg[64];
entered_menu_str(&dmsg[15], when, posn);
write(FILENO_STDOUT, dmsg, strlen(dmsg));
}
 
int menu_readkey(void) {
605,23 → 608,12
} dusk = newdusk;
}
 
const char unknownstr[] = "\e[jUnknown Cmd Key\e[1;0HA/Tm B/Dwn C/Dsk";
void unknown_menu(void) {
// 0123456789ABCDEF
// Unknown Cmd Key
// A/Tm B/Dwn C/Dsk
char dmsg[11];
dmsg[0] = PACK(0x1b,'[','j','U');
dmsg[1] = PACK('n','k','n','o');
dmsg[2] = PACK('w','n',' ','C');
dmsg[3] = PACK('m','d',' ','K');
dmsg[4] = PACK('e','y','\0','\0');
dmsg[5] = PACK(0x1b,'[','1',';');
dmsg[6] = PACK('0','H','A','/');
dmsg[7] = PACK('T','m',' ','B');
dmsg[8] = PACK('/','D','w','n');
dmsg[9] = PACK(' ','C','/','D');
dmsg[10] = PACK('s','k',0,0);
write(FILENO_STDOUT, dmsg, 11);
write(FILENO_STDOUT, unknownstr, strlen(unknownstr));
}
void menu_task(void) {
// Controls LED 0x08
631,10 → 623,8
// 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;
unsigned belltime = 0, when;
 
 
when = (volatile unsigned)rtcclock;
while(1) {
int event;
672,3 → 662,14
}
#endif
 
 
#ifdef HELLO_TASK
static const char *hello_string = "Hello, World!\r\n";
void hello_task(void) {
while(1) {
for(int i=0; i<15; i++)
wait(SWINT_CLOCK, -1);
write(FILENO_STDOUT, hello_string, strlen(hello_string));
}
}
#endif
/kernel.c
72,12 → 72,13
 
int LAST_TASK;
 
extern void txstr(const char *);
 
void kernel_entry(void) {
int nheartbeats= 0, tickcount = 0, milliseconds=0, ticks = 0;
int audiostate = 0, buttonstate = 0;
TASKP *tasklist, current;
int *last_context;
IOSPACE *sys = (IOSPACE *)IOADDR;
 
tasklist = ksetup();
 
87,16 → 88,17
 
unsigned enableset =
INT_ENABLEV(INT_BUTTON)
|INT_ENABLEV(INT_TIMA)
|INT_ENABLEV(INT_TIMER)
// |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 | 0x07fff;
_sys->io_pic = INT_ENABLE | enableset | 0x07fff;
 
txstr("HEAP: "); txhex(heap);
 
do {
int need_resched = 0, context_has_been_saved, pic;
nheartbeats++;
105,11 → 107,11
 
last_context = current->context;
context_has_been_saved = 0;
pic = sys->io_pic;
pic = _sys->io_pic;
 
if (pic & 0x8000) { // If there's an active interrupt
// Interrupt processing
sys->io_spio = 0x44;
_sys->io_spio = 0x44;
 
// First, turn off pending interrupts
// Although we migt just write 0x7fff7fff to the
119,18 → 121,18
// 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) {
_sys->io_pic = INT_DISABLEV(pic)|(INT_CLEAR(pic));
if(pic&INT_TIMER) {
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;
_sys->io_spio = ((_sys->io_spio&1)^1)|0x010;
pic |= SWINT_CLOCK;
}
if (buttonstate)
buttonstate--;
else
else if ((_sys->io_spio & 0x0f0)==0)
enableset |= INT_ENABLEV(INT_BUTTON);
}
//
137,12 → 139,14
if (pic&INT_BUTTON) {
// Need to turn the button interrupt off
enableset &= ~(INT_ENABLEV(INT_BUTTON));
if ((sys->io_spio&0x0f0)==0x030)
if ((_sys->io_spio&0x0f0)==0x030)
kpanic();
buttonstate = 3;
if (buttonstate)
pic &= ~INT_BUTTON;
buttonstate = 50;
}
if (pic & INT_UARTRX) {
int v = sys->io_uart;
int v = _sys->io_uart;
if ((v & (~0x7f))==0) {
kpush_syspipe(rxpipe, v);
149,44 → 153,42
 
// Local Echo
if (pic & INT_UARTTX) {
sys->io_uart = v;
sys->io_pic = INT_UARTTX;
_sys->io_uart = v;
_sys->io_pic = INT_UARTTX;
pic &= ~INT_UARTTX;
}
}
} if (pic & INT_UARTTX) {
int v;
if (kpop_syspipe(txpipe, &v)==0) {
char ch;
if (kpop_syspipe(txpipe, &ch)==0) {
unsigned v = ch;
enableset |= (INT_ENABLEV(INT_UARTTX));
sys->io_uart= v;
sys->io_pic = INT_UARTTX;
_sys->io_uart= v;
_sys->io_pic = INT_UARTTX;
// if (v == 'W')
// sys->io_timb = 5;
// sys->io_watchdog = 5;
// 75k was writing the 'e'
} else
enableset &= ~(INT_ENABLEV(INT_UARTTX));
enableset&= ~(INT_DISABLEV(INT_UARTTX));
} if (audiostate) {
if (pic & INT_AUDIO) {
int v;
unsigned short sample;
 
// 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;
// 1 -- in use
 
if (kpop_short_syspipe(pwmpipe, &sample)==0) {
_sys->io_pwm_audio = sample;
_sys->io_spio = 0x022;
// audiostate = 1;
} else {
audiostate = 0;
// Turn the device off
sys->io_pwm_audio = 0x10000;
_sys->io_pwm_audio = 0x10000;
// Turn the interrupts off
enableset &= ~(INT_ENABLEV(INT_AUDIO));
sys->io_spio = 0x020;
_sys->io_spio = 0x020;
}
 
// This particular interrupt cannot be cleared
195,46 → 197,50
// it now. If it needs retriggering, the port
// will retrigger itself -- despite being
// cleared here.
sys->io_pic = INT_AUDIO;
}} else { // if (audiostate == 0)
int sample;
if (kpop_syspipe(pwmpipe, &sample)==0) {
audiostate = (2|(sample<<2))&0x03ffff;
sys->io_pwm_audio = 0x310000 | ((sample>>16)&0x0ffff);
_sys->io_pic = INT_AUDIO;
}}
/*
else { // if (audiostate == 0)
unsigned short sample;
 
if (kpop_short_syspipe(pwmpipe, &sample)==0) {
audiostate = 1;
_sys->io_pwm_audio = 0x310000 | sample;
enableset |= (INT_ENABLEV(INT_AUDIO));
sys->io_spio = 0x022;
sys->io_pic = 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;
_sys->io_pic = INT_ENABLE|enableset;
} else {
sys->io_pic = INT_ENABLE; // Make sure interrupts are on
int sample;
_sys->io_pic = INT_ENABLE; // Make sure interrupts are on
unsigned short sample;
 
// 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, &sample)==0)) {
audiostate = (2|(sample<<2))&0x03ffff;
sys->io_pwm_audio = 0x310000 | ((sample>>16)&0x0ffff);
sys->io_pic = INT_AUDIO;
 
if ((audiostate==0)&&(kpop_short_syspipe(pwmpipe, &sample)==0)) {
audiostate = 1;
_sys->io_pwm_audio = 0x310000 | (sample);
_sys->io_pic = INT_AUDIO;
enableset |= (INT_ENABLEV(INT_AUDIO));
sys->io_spio = 0x022;
_sys->io_spio = 0x022;
} // else sys->io_spio = 0x020;
 
// Or the beginning of a transmit pipe.
if (pic & INT_UARTTX) {
int v;
if (kpop_syspipe(txpipe, &v)==0) {
char ch;
if (kpop_syspipe(txpipe, &ch)==0) {
unsigned v = ch;
enableset |= (INT_ENABLEV(INT_UARTTX));
sys->io_uart = v;
sys->io_pic = INT_UARTTX;
// if (v == 'W')
// sys->io_timb = 5;
_sys->io_uart = v;
_sys->io_pic = INT_UARTTX;
} else
enableset &= ~(INT_ENABLEV(INT_UARTTX));
}
244,9 → 250,9
// 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_pic = INT_ENABLE | enableset;
}
sys->io_spio = 0x40;
_sys->io_spio = 0x40;
 
int zcc = zip_ucc();
if (zcc & CC_TRAPBIT) {
354,8 → 360,7
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;
current->errno = (int)_sys->io_buserr;
save_context(last_context);
context_has_been_saved = 1;
kpanic();
409,7 → 414,7
 
int kpost(TASKP *tasklist, unsigned events, int milliseconds) {
int i;
if (events & INT_TIMA)
if (events & INT_TIMER)
milliseconds++;
if (milliseconds<0) {
milliseconds -= 0x80000000;
/ksetup.c
46,15 → 46,18
#include "ktraps.h"
#include "errno.h"
#include "swint.h"
#include "txfns.h"
 
typedef unsigned size_t;
 
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
char *heap; // = _top_of_heap; // Need to wait on startup to set this
 
#define CONTEXT_LENGTH 80000 // 1ms
#define CONTEXT_LENGTH (80000-1) // 1ms
 
int LAST_TASK;
 
61,12 → 64,12
__attribute__((cold))
TASKP *ksetup(void) {
TASKP *tasklist;
IOSPACE *sys = (IOSPACE *)IOADDR;
volatile IOSPACE *const sys = _sys;
 
sys->io_spio = 0x0f0;
sys->io_timb = 0; // Turn off the watchdog timer
sys->io_watchdog = 0; // Turn off the watchdog timer
LAST_TASK = kntasks();
heap = _top_of_heap;
heap = (char *)_top_of_heap;
 
pipedev = sys_malloc(sizeof(KDEVICE));
pipedev->write = (RWFDFUN)kwrite_syspipe;
75,26 → 78,25
 
txdev = pwmdev = pipedev;
 
rxpipe = new_syspipe(16); //rxpipe->m_wrtask=INTERRUPT_WRITE_TASK;
txpipe = new_syspipe(8); txpipe->m_rdtask = INTERRUPT_READ_TASK;
keypipe = new_syspipe(16);
lcdpipe = new_syspipe(16);
pwmpipe = new_syspipe(128); pwmpipe->m_rdtask= INTERRUPT_READ_TASK;
cmdpipe = new_syspipe(16);
rxpipe = new_syspipe(64); //rxpipe->m_wrtask=INTERRUPT_WRITE_TASK;
txpipe = new_syspipe(64); txpipe->m_rdtask = INTERRUPT_READ_TASK;
keypipe = new_syspipe(64);
lcdpipe = new_syspipe(64);
pwmpipe = new_syspipe(512); pwmpipe->m_rdtask= INTERRUPT_READ_TASK;
cmdpipe = new_syspipe(64);
 
tasklist = sys_malloc(sizeof(TASKP)*(1+LAST_TASK));
kinit(tasklist);
tasklist[LAST_TASK] = new_task(2, idle_task);
tasklist[LAST_TASK] = new_task(8, idle_task);
 
// Turn all interrupts off, acknowledge all at the same time
sys->io_pic = 0x7fff7fff;
sys->io_pic = INT_CLEARPIC;
 
sys->io_tima = CONTEXT_LENGTH | TM_REPEAT;
sys->io_timer = CONTEXT_LENGTH | TM_REPEAT;
 
{
// Reset our wishbone scope for debug later
SCOPE *scope = (SCOPE *)SCOPEADDR;
scope->s_control = 2;
_scope->s_ctrl = 2;
}
sys->io_spio = 0x0f1;
 
102,15 → 104,13
}
 
void kwait_on_buttonpress(void) {
IOSPACE *sys = (IOSPACE *)IOADDR;
 
// Wait on a button press before starting
while((sys->io_spio & 0x0f0)==0)
while((_sys->io_spio & 0x0f0)==0)
;
sys->io_spio = 0x0f3;
_sys->io_spio = 0x0f3;
for(int i=0; i<40000; i++)
sys->io_spio = ((i>>14)&2)|0x020;
sys->io_spio = 0x0f7;
_sys->io_spio = ((i>>14)&2)|0x020;
_sys->io_spio = 0x0f7;
}
 
// __attribute__((noreturn))
119,19 → 119,9
}
 
__attribute__((malloc))
void *sys_malloc(int sz) {
if (0) {
SCOPE *s = (SCOPE *)SCOPEADDR;
s->s_control = TRIGGER_SCOPE_NOW | (s->s_control & 0x0ffff);
}
 
void *sys_malloc(size_t sz) {
void *res = heap;
heap += sz;
if ((int)heap > ((int)&res)-32) {
IOSPACE *sys = (IOSPACE *)IOADDR;
sys->io_spio = 0xf3;
asm("break 0");
}
heap = heap + ((sz+3)&-4);
return res;
}
 
/ktraps.h
56,7 → 56,8
// 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.
// If the timeout == 0, clears the respective interrupt
// slash event, and does nothing more.
TRAPID_WAIT, TRAPID_CLEAR, TRAPID_POST,
// Yield: Yields the processor until the next scheduled time slice.
TRAPID_YIELD,
63,10 → 64,10
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
// exception triggers a system call. In such cases, it will be
// impossible to return the caller back to his context in a pristine
// manner ... without help.
// TRAPID_KRETURN
// 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,
/pipesetup.c
44,6 → 44,7
#include "syspipe.h"
#include "zipsys.h"
#include "ktraps.h"
#include "txfns.h"
 
#ifndef NULL
#define NULL (void *)0
77,43 → 78,10
p->m_nwritten = 0;
}
 
// These routines really belong elsewhere in a uartdump.c file or some such.
// However, until placed there, they'll stay put here for a bit longer.
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;
_sys->io_spio = 0x0fa;
txstr("SYSPIPE PANIC!\r\n");
txstr("ADDR: "); txhex((int)pipe);
126,7 → 94,7
SYSPIPE *new_syspipe(const unsigned int len) {
unsigned msk;
 
for(msk=2; msk<len; msk<<=1)
for(msk=16; msk<len; msk<<=1)
;
SYSPIPE *pipe = sys_malloc(sizeof(SYSPIPE)-1+msk);
pipe->m_mask = msk-1;
/resetdump.s
32,7 → 32,7
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;//
;;
;; Copyright (C) 2015-2016, Gisselquist Technology, LLC
;; Copyright (C) 2015-2017, 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
62,56 → 62,29
_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
SW R0,(DBG)
JSR internal_kpanic
LDI _top_of_stack,SP
LDI kernel_entry,R0
BRA bootloader
BRA _bootloader
 
.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
SW R0,(DBG)
SW R1,4(DBG)
SW R2,8(DBG)
LDI panicstr, R1
JSR raw_put_string
LW 4(DBG),R1
LW 8(DBG),R2
JSR internal_kpanic
kpanic_wait_for_button_release:
LOD (SPIO),R0
LW (SPIO),R0
TEST 0x010,R0
BNZ kpanic_wait_for_button_release
kpanic_wait_for_button:
LOD (SPIO),R0
LW (SPIO),R0
TEST 0x010,R0
BZ kpanic_wait_for_button
BRA _start
118,191 → 91,159
HALT
internal_kpanic:
STO R1,1(DBG)
STO R2,2(DBG)
STO R0,3(DBG) ; Our return address
; The original R0 is stored in (DBG)
SW R1,4(DBG)
SW R2,8(DBG)
SW R0,12(DBG) ; Our return address
 
; R0
LDI 0,R1
LOD (DBG),R2
MOV .Lcall0(PC),R0
JMP uart_put_reg_value
.Lcall0:
LW (DBG),R2
JSR uart_put_reg_value
 
; R1
LDI 1,R1
LOD 1(DBG),R2
MOV .Lcall1(PC),R0
JMP uart_put_reg_value
.Lcall1:
LW 4(DBG),R2
JSR uart_put_reg_value
; R2
LDI 2,R1
LOD 2(DBG),R2
MOV PC+1,R0
JMP uart_put_reg_value
LW 8(DBG),R2
JSR uart_put_reg_value
; R3
LDI 3,R1
MOV R3,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; R4
LDI 4,R1
MOV R4,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; R5
LDI 5,R1
MOV R5,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; R6
LDI 6,R1
MOV R6,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; R7
LDI 7,R1
MOV R7,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; R8
LDI 8,R1
MOV R8,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; R9
LDI 9,R1
MOV R9,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; R10
LDI 10,R1
MOV R10,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; R11
LDI 11,R1
MOV R11,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; R12
LDI 12,R1
MOV R12,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; SP
LDI 13,R1
MOV R13,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; uR0
LDI 16,R1
MOV uR0,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; uR1
LDI 17,R1
MOV uR1,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
LDI 18,R1
MOV uR2,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
LDI 19,R1
MOV uR3,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
LDI 20,R1
MOV uR4,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
LDI 21,R1
MOV uR5,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
LDI 22,R1
MOV uR6,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
LDI 23,R1
MOV uR7,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
LDI 24,R1
MOV uR8,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
LDI 25,R1
MOV uR9,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
LDI 26,R1
MOV uR10,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
LDI 27,R1
MOV uR11,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
LDI 28,R1
MOV uR12,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; uSP
LDI 29,R1
MOV uSP,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; uCC
LDI 30,R1
MOV uCC,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
; uPC
LDI 31,R1
MOV uPC,R2
MOV PC+1,R0
JMP uart_put_reg_value
JSR uart_put_reg_value
 
;stack_mem_dump:
;LDI 0,R4
;LDI _top_of_stack,R5
; 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
; MOV R4,R1
; LW (R5),R2
; JSR uart_put_stack_value
; ADD 4,R4
; SUB 4,R5
; CMP 256,R4
; BLT stack_mem_dump_loop
 
; Get prepared for a proper start by setting our stack register
LDI _top_of_stack,SP
313,23 → 254,22
; Now, do a full dump of all memory--all registers are available to us
dump_memory:
LDI RAM,R5
LDI 0x1000,R6
LDI 0x0fff,R6
LDI 0x0f8,R7
STO R7,(SPIO)
SW R7,(SPIO)
full_mem_dump_loop:
MOV R5,R1
LOD (R5),R2
MOV PC+1,R0
JMP uart_dump_mem_value
LW (R5),R2
JSR uart_dump_mem_value
LDI 0x0f2,R7
STO R7,(SPIO)
SW R7,(SPIO)
 
ADD 1,R5
ADD 4,R5
SUB 1,R6
BGT full_mem_dump_loop
BGE full_mem_dump_loop
 
LDI 0x0f5,R7
STO R7,(SPIO)
SW R7,(SPIO)
 
dump_scope:
; Finally, do a full dump of the scope--if it had triggered
336,10 → 276,9
; First, dump the scope control word
LDI SCOPE,R7 ; R7 = Debugging scope address
MOV R7,R1
LOD (R7),R2
LW (R7),R2
MOV R2,R5 ; R5 will not be changed by a subroutine
MOV PC+1,R0
BRA uart_dump_mem_value
JSR uart_dump_mem_value
; Then test whether or not the scope has stopped
LDI 0x40000000,R1
TEST R1,R5
349,116 → 288,72
LSR 20,R5
AND 0x1f,R5
LDI 1,R6
LSL R5,R6
LSL R5,R6 ; R6 is now the size (number of words) of the scope
SUB 1,R6 ; Now it is one less than the size,2 support the BGE l8r
; And start dumping
ADD 1,R7 ; Get the scope data address
ADD 4,R7 ; Get the scope data address
dump_scope_loop:
MOV R7,R1
LOD (R7),R2
MOV PC+1,R0
BRA uart_dump_mem_value
LW (R7),R2
JSR uart_dump_mem_value
SUB 1,R6
BGT dump_scope_loop
BGE 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_header, R1
JSR raw_put_string
LDI BUSERR,R1
LOD (R1),R2
MOV PC+1,R0
BRA uart_dump_mem_value
LW (R1),R2
JSR 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 doublenewline,R1
JSR raw_put_string
LDI 0x0ff,R7
STO R7,(SPIO)
LOD 3(DBG),PC
SW R7,(SPIO)
LW 12(DBG),PC
JMP R0
 
; R0 is return address
; R1 is register ID
; R2 is register to output
; R2 is register value to output
uart_put_reg_value:
STO R0,4(DBG)
STO R2,5(DBG)
STO R3,6(DBG)
SW R0,16(DBG)
SW R2,20(DBG)
SW R3,24(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
JSR 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
JSR 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
JSR get_hex
JSR raw_put_uart
LDI ':',R1
JSR raw_put_uart
LW 20(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
LSR 28,R1
LSL 4,R2
JSR get_hex
JSR 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
LDI newline, R1
JSR raw_put_string
LW 16(DBG),R0
LW 20(DBG),R2
LW 24(DBG),R3
JMP R0
 
uart_dump_mem_value:
466,107 → 361,109
; R1 = Memory address
; R2 = Memory Value
; Local: R3 = working value
STO R0,7(DBG)
SW R0,28(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
LDI memopenstr,R1
JSR raw_put_string
; Set up a loop to dump things
ROL 16,R3 ; Ignore the first 16 bits
LSL 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
LSR 28,R1
LSL 4,R3
JSR get_hex
JSR 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
LDI memmidstr,R1
JSR raw_put_string
 
; 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
LSR 28,R1
LSL 4,R4
JSR get_hex
JSR 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
LDI newline,R1
JSR raw_put_string
; And return from our subroutine
LOD 7(DBG),R0
LW 28(DBG),R0
JMP R0
 
get_hex:
ADD 0x30,R1
CMP 0x39,R1
ADD.GT 7,R1 ; Add 'A'-'0'-10
CMP 0x3a,R1
ADD.GE 7,R1 ; Add 'A'-'0'-10
JMP R0
 
raw_put_uart: ; R0 is return address, R1 is value to transmit
STO R2,8(DBG)
; R0 is the return address
; R1 is the string address
raw_put_string:
SW R0,36(DBG)
SW R2,40(DBG)
MOV R1,R2
raw_put_string_next:
LB (R2),R1
CMP 0,R1
LW.Z 36(DBG),R0
LW.Z 40(DBG),R2
RTN.Z
ADD 1,R2
MOV raw_put_string_next(PC),R0
BRA raw_put_uart
; R0 is return address,
; R1 is value to transmit
raw_put_uart:
SW R2,32(DBG)
LDI INT_UARTTX,R2
STO R2,(PIC) ; Clear the PIC, turn off interrupts, etc.
SW R2,(PIC) ; Clear the PIC, turn off interrupts, etc.
raw_put_uart_retest:
LOD (PIC),R2
LW (PIC),R2
TEST INT_UARTTX,R2
BZ raw_put_uart_retest
STO R1,(UART)
LOD 8(DBG),R2
SW R1,(UART)
LW 32(DBG),R2
JMP R0
 
.section .fixdata
DBG:
.byte 0,0,0,0,0,0,0,0,0
.int 0,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
.set PIC, 0x0400
.set BUSERR, 0x0404
.set TMRA, 0x0408
.set TMRB, 0x040c
.set PWM, 0x0410
.set SPIO, 0x0414
.set GPIO, 0x0418
.set UART, 0x041c
.set VERSION, 0x0420
.set SCOPE, 0x0800
.set SCOPED, 0x0804
.set RAM, 0x8000
.section .rodata
doublenewline:
.ascii "\r\n"
newline:
.asciz "\r\n"
buserr_header:
.string "BusErr: "
panicstr:
.string "Panic: \r\n"
memopenstr:
.string "M[0x"
memmidstr:
.string "]; "
 
/syspipe.c
46,12 → 46,13
#include "syspipe.h"
#include "zipsys.h"
#include "ktraps.h"
#include "string.h"
 
#ifndef NULL
#define NULL (void *)0
#endif
 
void kpush_syspipe(SYSPIPE *pipe, int val) {
void kpush_syspipe(SYSPIPE *pipe, char val) {
int tst = (pipe->m_head+1)&pipe->m_mask;
if (tst != pipe->m_tail) {
pipe->m_buf[pipe->m_head] = val;
62,8 → 63,15
}
 
extern void pipe_panic(SYSPIPE *p);
int kpop_syspipe(SYSPIPE *pipe, int *vl) {
/*
* kpop_syspipe
*
* Called from an interrupt context to pop one byte off of the syspipe.
*/
int kpop_syspipe(SYSPIPE *pipe, char *vl) {
if (pipe->m_head != pipe->m_tail) {
// The head will *only* equal the tail if the pipe is empty.
// We come in here, therefore, if the pipe is non-empty
*vl = pipe->m_buf[pipe->m_tail];
pipe->m_tail = (pipe->m_tail+1)&pipe->m_mask;
if (pipe->m_wrtask)
73,12 → 81,30
return 1; // Error condition
}
 
/* Returns how many values are in the pipe
/*
* kpop_short_syspipe
*
* This is identical to kpop_syspipe, save that we are pulling a short value
* off of the syspipe (i.e. a pair of chars), not just a single char.
*/
/* Of course ... if it's not used, why include it?
int kpop_short_syspipe(SYSPIPE *pipe, unsigned short *vl) {
if (pipe->m_head == pipe->m_tail)
return 1; // Error condition
 
unsigned short *sptr = (unsigned short *)&pipe->m_buf[pipe->m_tail];
// sv = pipe->m_buf[pipe->m_tail];
*vl = *sptr;;
pipe->m_tail = (pipe->m_tail+2)&pipe->m_mask;
if (pipe->m_wrtask)
pipe->m_wrtask->state = SCHED_READY;
return 0;
}
 
/*
int len_syspipe(SYSPIPE *p) {
return (p->m_head-p->m_tail) & p->m_mask;
} */
 
/* Returns how many empty spaces are in the pipe
*/
int num_avail_syspipe(SYSPIPE *p) {
96,7 → 122,8
// 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) {
static int uread_syspipe(TASKP tsk __attribute__((__unused__)),
SYSPIPE *p, char *dst, int len) {
int nleft= len, h;
if (len == 0) {
// We'll only get here if we were released from within a
116,9 → 143,8
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++;
memcpy(dst, &p->m_buf[p->m_tail], ln1);
dst += ln1;
 
p->m_nread += ln1;
nleft -= ln1;
144,9 → 170,8
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++;
memcpy(dst, &p->m_buf[p->m_tail], ln1);
dst += ln1;
 
p->m_nread += ln1;
nleft -= ln1;
172,7 → 197,8
// pointer. It can change from one time through our loop
// to the next.
if (((volatile SYSPIPE *)p)->m_wrtask) {
int *src, ln;
int ln;
char *src;
 
// If the head changed before the write task blocked,
// then go around again and copy some more before
189,11 → 215,11
ln = nleft;
if (p->m_wrtask->context[4] < nleft)
ln = p->m_wrtask->context[4];
src = (int *)p->m_wrtask->context[3];
src = (char *)p->m_wrtask->context[3];
memcpy(dst, src, ln);
dst += ln;
src += ln;
 
for(int i=0; i<ln; i++)
*dst++ = *src++;
 
p->m_nwritten += ln;
p->m_nread += ln;
 
244,7 → 270,7
}
 
static int uwrite_syspipe(TASKP tsk __attribute__((__unused__)),
SYSPIPE *p, int *src, int len) {
SYSPIPE *p, char *src, int len) {
int nleft = len;
 
// The kernel guarantees, before we come into here, that we have a
263,9 → 289,8
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++;
memcpy((char *)p->m_rdtask->context[3], src, ln);
src += ln;
p->m_nread += ln;
p->m_rdtask->context[3]+= ln;
p->m_rdtask->context[4]-= ln;
318,34 → 343,17
// so that it remains consistent under interrupt
// conditions.
int ln = p->m_mask+1-p->m_head;
int *dst = &p->m_buf[p->m_head];
if (ln > nleft) ln = nleft;
if (ln > navail) ln = navail;
 
for(int i=0; i<ln; i++)
*dst++ = *src++;
memcpy((void *)&p->m_buf[p->m_head], src, ln);
src += ln;
 
p->m_head = (p->m_head+ln)&p->m_mask;
nleft -= ln;
p->m_nwritten += ln;
navail -= ln;
nleft -= ln;
p->m_nwritten += ln;
navail -= ln;
}
 
/*
// Write into the rest of the pipe
if ((0 == p->m_head)&&(nleft>0)&&(navail>0)) {
int ln = navail;
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)&&(navail == 0)) {
371,7 → 379,7
}
 
// This will be called from a kernel (interrupt) context
void kread_syspipe(TASKP tsk, int dev, int *dst, int len) {
void kread_syspipe(TASKP tsk, int dev, char *dst, int len) {
SYSPIPE *p = (SYSPIPE *)dev;
if (p->m_rdtask != NULL) {
// If the pipe already has a read task, then we fail
415,7 → 423,7
}
}
 
void kwrite_syspipe(TASKP tsk, int dev, int *src, int len) {
void kwrite_syspipe(TASKP tsk, int dev, char *src, int len) {
SYSPIPE *p = (SYSPIPE *)dev;
if (p->m_wrtask != NULL) {
// If the pipe already has a write task, then we fail
/syspipe.h
46,7 → 46,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
// Copyright (C) 2015-2017, 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
73,8 → 73,10
#ifndef SYSPIPE_H
#define SYSPIPE_H
 
extern void *sys_malloc(int sz);
typedef unsigned size_t;
 
extern void *sys_malloc(size_t sz);
 
typedef struct {
unsigned int m_mask;
int m_head, m_tail;
83,14 → 85,15
int dummy;
int m_error;
 
int m_buf[1];
char 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 void kread_syspipe( TASKP tsk, int dev, char *dst, int len);
extern void kwrite_syspipe(TASKP tsk, int dev, char *src, int len);
void kpush_syspipe(SYSPIPE *p, char val);
int kpop_syspipe(SYSPIPE *p, char *val);
int kpop_short_syspipe(SYSPIPE *p, unsigned short *val);
extern int num_avail_syspipe(SYSPIPE *p);
extern int len_syspipe(SYSPIPE *p);
 
/taskp.c
39,6 → 39,7
#include "ksched.h"
#include "kfildes.h"
#include "taskp.h"
#include "txfns.h"
 
extern void *sys_malloc(int sz);
TASKP new_task(unsigned ln, VENTRYP entry) {
47,11 → 48,11
 
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[ 0] = (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->context[15] = (int)((int *)entry);
tsk->user_heap = &tsk->user_data[0];
tsk->state = SCHED_READY;
 
/ziplib.c
5,5 → 5,7
do {
*p++ = c;
} while(n-- > 0);
 
return s;
}
 
/zipsys.h
79,13 → 79,11
#endif
 
static inline void DISABLE_INTS(void) {
IOSPACE *sys = (IOSPACE *)IOADDR;
sys->io_pic = 0;
_sys->io_pic = 0;
}
 
static inline void ENABLE_INTS(void) {
IOSPACE *sys = (IOSPACE *)IOADDR;
sys->io_pic = INT_ENABLE;
_sys->io_pic = INT_ENABLE;
}
 
#endif

powered by: WebSVN 2.1.0

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