URL
https://opencores.org/ocsvn/s6soc/s6soc/trunk
Subversion Repositories s6soc
Compare Revisions
- This comparison shows the changes necessary to convert path
/s6soc/trunk
- from Rev 52 to Rev 53
- ↔ Reverse comparison
Rev 52 → Rev 53
/sw/dev/asmstartup.h
File deleted
/sw/dev/Makefile
45,8 → 45,9
################################################################################ |
## |
## |
# Declare "all" to be the default target |
all: |
PROGRAMS := helloworld doorbell doorbell2 kptest blinky |
PROGRAMS := helloworld doorbell doorbell2 kptest blinky cputest uartecho |
all: $(OBJDIR)/ $(PROGRAMS) |
|
|
61,14 → 62,16
# Not for build, for for building tags and dependency files, we need to know |
# what the sources and headers are |
DEVDRVR:= keypad.c display.c rtcsim.c txfns.c |
SOURCES:= helloworld.c doorbell.c doorbell2.c kptest.c $(DEVDRVR) |
SOURCES:= helloworld.c doorbell.c doorbell2.c kptest.c uartecho.c $(DEVDRVR) |
HEADERS:= board.h |
OBJDRVR := $(addprefix $(OBJDIR)/,$(subst .c,.o,$(DEVDRVR))) |
|
CPPFLAGS := -I../zipos -I. |
DUMPFLAGS := # -fdump-rtl-all |
CFLAGS := $(CPPFLAGS) $(DUMPFLAGS) -O3 -Wall -Wextra -nostdlib -fno-builtin -Wa,-nocis |
CFLAGS := $(CPPFLAGS) $(DUMPFLAGS) -O3 -Wall -Wextra -nostdlib -fno-builtin |
LDFLAGS = -T cmod.ld -Wl,-Map,$(OBJDIR)/$@.map -Wl,--unresolved-symbols=report-all -nostdlib |
STARTUP := resetdump.s |
STARTOBJ:= $(addprefix $(OBJDIR)/,$(subst .s,.o,$(STARTUP))) |
|
$(OBJDIR)/: |
$(mk-objdir) |
78,9 → 81,18
$(OBJDIR)/%.o: %.c |
$(mk-objdir) |
$(CC) $(CFLAGS) -c $< -o $@ |
$(OBJDIR)/cputest.o: cputest.c |
$(mk-objdir) |
$(CC) $(CFLAGS) -Wa,-nocis -c $< -o $@ |
$(OBJDIR)/cputestcis.o: cputest.c |
$(mk-objdir) |
$(CC) $(CFLAGS) -Wa,-cis -c $< -o $@ |
$(OBJDIR)/%.o: ../zipos/%.c |
$(mk-objdir) |
$(CC) $(CFLAGS) -c $< -o $@ |
$(OBJDIR)/%.o: %.s |
$(mk-objdir) |
$(AS) -nocis $< -o $@ |
|
$(OBJDIR)/%.s: %.c |
$(mk-objdir) |
93,29 → 105,38
$(OBJDUMP) -Dr $< > $@ |
|
|
helloworld: $(OBJDIR)/helloworld.o cmod.ld |
$(CC) $(LDFLAGS) $(OBJDIR)/helloworld.o -o $@ |
helloworld: $(OBJDIR)/helloworld.o $(STARTOBJ) cmod.ld |
$(CC) $(LDFLAGS) $(STARTOBJ) $(OBJDIR)/helloworld.o -o $@ |
$(OBJDIR)/helloworld.txt: helloworld |
$(OBJDUMP) -dr $^ > $@ |
|
doorbell: $(OBJDIR)/doorbell.o cmod.ld |
$(CC) $(LDFLAGS) $(OBJDIR)/doorbell.o -o $@ |
uartecho: $(OBJDIR)/uartecho.o $(STARTOBJ) cmod.ld |
$(CC) $(LDFLAGS) $(STARTOBJ) $(OBJDIR)/uartecho.o -o $@ |
|
doorbell2: $(OBJDIR)/doorbell2.o $(OBJDRVR) $(OBJDIR)/string.o cmod.ld |
$(CC) $(LDFLAGS) $(OBJDIR)/doorbell2.o $(OBJDRVR) $(OBJDIR)/string.o -o $@ |
doorbell: $(OBJDIR)/doorbell.o $(STARTOBJ) cmod.ld |
$(CC) $(LDFLAGS) $(STARTOBJ) $(OBJDIR)/doorbell.o -o $@ |
|
doorbell2: $(OBJDIR)/doorbell2.o $(OBJDRVR) $(STARTOBJ) $(OBJDIR)/string.o cmod.ld |
$(CC) $(LDFLAGS) $(STARTOBJ) $(OBJDIR)/doorbell2.o $(OBJDRVR) $(OBJDIR)/string.o -o $@ |
$(OBJDIR)/doorbell2.txt: doorbell2 |
$(OBJDUMP) -dr $^ > $@ |
|
KPSRCS := kptest.c keypad.c txfns.c |
KPOBJS := $(addprefix $(OBJDIR)/,$(subst .c,.o,$(KPSRCS))) |
kptest: $(KPOBJS) cmod.ld |
$(CC) $(LDFLAGS) $(KPOBJS) -o $@ |
kptest: $(KPOBJS) $(STARTOBJ) cmod.ld |
$(CC) $(LDFLAGS) $(STARTOBJ) $(KPOBJS) -o $@ |
$(OBJDIR)/kptest.txt: kptest |
$(OBJDUMP) -dr $^ > $@ |
|
blinky: $(OBJDIR)/blinky.o cmod.ld |
$(CC) $(LDFLAGS) $(OBJDIR)/blinky.o -o $@ |
$(CC) $(LDFLAGS) $(STARTOBJ) $(OBJDIR)/blinky.o -o $@ |
|
cputest: $(OBJDIR)/cputest.o cmod.ld |
$(CC) $(LDFLAGS) $(OBJDIR)/cputest.o -o $@ |
|
cputestcis: $(OBJDIR)/cputestcis.o cmod.ld |
$(CC) $(LDFLAGS) $(OBJDIR)/cputestcis.o -o $@ |
|
define mk-objdir |
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi" |
endef |
/sw/dev/asmstartup.s
0,0 → 1,58
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; |
;; Filename: asmstartup.s |
;; |
;; Project: CMod S6 System on a Chip, ZipCPU demonstration project |
;; |
;; Purpose: A small assembly routine, designed to place the startup code |
;; into the very beginning of the program space (i.e. crt0.s). |
;; This startup code *must* start at the RESET_ADDRESS of the ZipCPU. It |
;; does two things: 1) loads a valid stack pointer, and 2) jumps to the |
;; entry point in the program which (as a result of this startup code) |
;; may be anywhere in the address space. |
;; |
;; Creator: Dan Gisselquist, Ph.D. |
;; 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 |
;; 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,"ax",@progbits |
.global _start |
.type _start,@function |
_start: |
NDUMP |
LDI 255,R0 ; Turn all the LEDs on |
SW R0,(SPIO) |
MOV kernel_exit(PC),uPC |
LDI _top_of_stack,SP |
JSR entry |
NEXIT R0 ; Exit on return if in a simulator |
kernel_exit: |
HALT ; Otherwise just halt the CPU |
BRA kernel_exit ; In case were called from user mode |
|
.set SPIO, 0x0414 |
/sw/dev/blinky.c
38,27 → 38,10
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
// #include "asmstartup.h" |
#include "board.h" |
|
void zip_idle(void); |
|
asm("\t.section\t.start\n" |
"\t.global\t_start\n" |
"\t.type\t_start,@function\n" |
"_start:\n" |
"\tMOV\tkernel_exit(PC),uPC\n" |
"\tLDI 104,R0\n" |
"\tSW R0,0x41c\n" |
"\tLDI 0xff,R0\n" |
"\tSW R0,0x414\n" |
"\tLDI\t_top_of_stack,SP\n" |
"\tJSR\tentry\n" |
"\tNEXIT\tR0\n" |
"kernel_exit:\n" |
"\tBUSY\n" |
"\t.section\t.text"); |
|
void entry(void) { |
const char *msg = "Hello, World!\r\n", *ptr = msg; |
int count = 0; |
/sw/dev/board.h
109,13 → 109,32
#define IOADDR 0x000400 |
#define SCOPEADDR 0x000800 |
// #define FCTLADDR 0x000c00 // Flash control, depends upon write capability |
#define RAMADDR 0x004000 |
#define BKRAM (void *)0x004000 |
#define FLASH (void *)0x1000000 |
#define SDRAM (void *)0 |
#define RAMSZ (RAMADDR) |
#define FLASHADDR 0x1000000 |
#define FLASHSZ (FLASHADDR) |
#define MEMLEN 0x04000 |
#define FLASHLEN 0x1000000 |
#define RESET_ADDR 0x1200000 |
#define FLASHSZ (FLASHADDR) |
|
#define CLOCKFREQHZ 80000000 |
#define CLOCKFREQ_HZ CLOCKFREQHZ |
|
static volatile IOSPACE *const _sys = (IOSPACE *)IOADDR; |
#define _ZIP_HAS_WBUARTRX |
#define _uartrx _sys->io_uart |
#define _ZIP_HAS_LONELY_UART |
#define LONELY_UART |
#define _uart _sys->io_uart |
|
static volatile WBSCOPE *const _scope = (WBSCOPE *)SCOPEADDR; |
|
#define SYSTIMER _sys->io_timer |
#define SYSPIC _sys->io_pic |
|
#define valid_ram_region(PTR,LN) (((int)(PTR)>=RAMADDR)&&((int)(PTR+LN)<RAMADDR+RAMSZ)) |
#define valid_flash_region(PTR,LN) (((int)(PTR)>=FLASHADDR)&&((int)(PTR+LN)<FLASHADDR+FLASHSZ)) |
#define valid_mem_region(PTR,LN) ((valid_ram_region(PTR,LN))||(valid_flash_region(PTR,LN))) |
|
#endif |
/sw/dev/cmod.ld
38,14 → 38,36
flash (rx) : ORIGIN = 0x1000000, LENGTH = 0x1000000 |
} |
|
_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 |
{ |
. = 0x1200000; |
.rocode 0x1200000 : { *(.start) *(.text*) |
*(.rodata*) |
*(.strings*) } > flash |
.data : { *(.fixdata*) *(.data*) *(COMMON*) *(.bss*) } > blkram |
_top_of_heap = .; |
.rocode 0x1200000 : ALIGN(4) { |
_boot_address = .; |
*(.start) *(.boot*) |
*(.text*) |
*(.rodata*) |
*(.strings*) |
__rocode_alignment = (. + 3) & ~ 3; |
. = __rocode_alignment; |
} > flash |
_kernel_image_start = . ; |
.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 = .; |
} |
/sw/dev/cputest.c
0,0 → 1,1473
/////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: cputest.c |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: To test the CPU, it's instructions, cache, and pipeline, to make |
// certain that it works. This includes testing that each of the |
// instructions works, as well as any strange instruction combinations. |
// |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include "../zlib/zipcpu.h" |
#include "board.h" |
|
#ifndef NULL |
#define NULL (void *)0 |
#endif |
|
#define PIC SYSPIC |
#define TIMER SYSTIMER |
|
#define HAVE_SCOPE |
#define SCOPEc _scope->s_ctrl |
#define SCOPE_DELAY 4 |
#define TRIGGER_SCOPE_NOW (WBSCOPE_TRIGGER|SCOPE_DELAY) |
#define PREPARE_SCOPE SCOPE_DELAY |
|
unsigned zip_ucc(void); |
unsigned zip_cc(void); |
void zip_save_context(int *); |
void zip_halt(void); |
|
|
void txchr(char v); |
void txstr(const char *str); |
void txhex(int num); |
void tx4hex(int num); |
|
|
asm("\t.section\t.start\n" |
"\t.global\t_start\n" |
"\t.type\t_start,@function\n" |
"_start:\n" |
"\tCLR\tR0\n" |
"\tCLR\tR1\n" |
"\tCLR\tR2\n" |
"\tCLR\tR3\n" |
"\tCLR\tR4\n" |
"\tCLR\tR5\n" |
"\tCLR\tR6\n" |
"\tCLR\tR7\n" |
"\tCLR\tR8\n" |
"\tCLR\tR9\n" |
"\tCLR\tR10\n" |
"\tCLR\tR11\n" |
"\tCLR\tR12\n" |
"\tLDI\t_top_of_stack,SP\n" |
"\tCLR\tCC\n" |
"\tMOV\tbusy_failure(PC),R0\n" |
"\tBRA\tentry\n" |
"busy_failure:\n" |
"\tBUSY\n" |
"\t.section\t.text"); |
|
#ifdef COUNTER |
#define MARKSTART start_time = COUNTER |
#define MARKSTOP stop_time = COUNTER |
#else |
#ifdef TIMER |
#define MARKSTART start_time = TIMER |
#define MARKSTOP stop_time = TIMER |
#else |
#define MARKSTART |
#define MARKSTOP |
#endif |
#endif |
|
|
extern int run_test(void *pc, void *stack); |
asm("\t.text\n\t.global\trun_test\n" |
"\t.type\trun_test,@function\n" |
"run_test:\n" |
"\tCLR\tR3\n" |
"\tMOV\ttest_return(PC),uR0\n" |
"\tMOV\tR3,uR1\n" |
"\tMOV\tR3,uR2\n" |
"\tMOV\tR3,uR3\n" |
"\tMOV\tR3,uR4\n" |
"\tMOV\tR3,uR5\n" |
"\tMOV\tR3,uR6\n" |
"\tMOV\tR3,uR7\n" |
"\tMOV\tR3,uR8\n" |
"\tMOV\tR3,uR9\n" |
"\tMOV\tR3,uR10\n" |
"\tMOV\tR3,uR11\n" |
"\tMOV\tR3,uR12\n" |
"\tMOV\tR2,uSP\n" // uSP = stack |
"\tMOV\t0x20+R3,uCC\n" // Clear uCC of all but the GIE bit |
"\tMOV\tR1,uPC\n" // uPC = pc |
"\tRTU\n" |
"test_return:\n" |
"\tMOV\tuR1,R1\n" |
"\tAND\t0xffffffdf,CC\n" |
// Works with 5 NOOPS, works with 3 NOOPS, works with 1 NOOP |
"\tJMP\tR0\n"); |
|
extern int idle_test(void); |
asm("\t.text\n\t.global\tidle_test\n" |
"\t.type\tidle_test,@function\n" |
"idle_test:\n" |
"\tCLR\tR1\n" |
"\tMOV\tidle_loop(PC),uR0\n" |
"\tMOV\tR1,uR1\n" |
"\tMOV\tR1,uR2\n" |
"\tMOV\tR1,uR3\n" |
"\tMOV\tR1,uR4\n" |
"\tMOV\tR1,uR5\n" |
"\tMOV\tR1,uR6\n" |
"\tMOV\tR1,uR7\n" |
"\tMOV\tR1,uR8\n" |
"\tMOV\tR1,uR9\n" |
"\tMOV\tR1,uR10\n" |
"\tMOV\tR1,uR11\n" |
"\tMOV\tR1,uR12\n" |
"\tMOV\tR1,uSP\n" |
"\tMOV\t0x20+R1,uCC\n" |
"\tMOV\tidle_loop(PC),uPC\n" |
"\tWAIT\n" |
"\tMOV uPC,R1\n" |
"\tCMP idle_loop(PC),R1\n" |
"\tLDI 0,R1\n" |
"\tLDI.NZ 1,R1\n" |
"\nRETN\n" |
"idle_loop:\n" |
"\tWAIT\n" |
"\tBRA idle_loop\n"); |
|
void break_one(void); |
asm("\t.text\n\t.global\tbreak_one\n" |
"\t.type\tbreak_one,@function\n" |
"break_one:\n" |
"\tLDI\t0,R1\n" |
"\tBREAK\n" |
"\tLDI\t1,R1\n" // Test fails |
"\tJMP\tR0"); |
|
void break_two(void); |
// Can we stop a break before we hit it? |
asm("\t.text\n\t.global\tbreak_two\n" |
"\t.type\tbreak_two,@function\n" |
"break_two:\n" |
"\tLDI\t0,R1\n" |
"\tJMP\tR0\n" |
"\tBREAK\n"); |
|
void break_three(void); |
// Can we jump to a break, and still have the uPC match |
asm("\t.text\n\t.global\tbreak_three\n" |
"\t.type\tbreak_three,@function\n" |
// R1 = 0 by default from calling. This will return as though |
// we had succeeded. |
"break_three:\n" |
"\tBREAK\n"); |
|
void early_branch_test(void); |
asm("\t.text\n\t.global\tearly_branch_test\n" |
"\t.type\tearly_branch_test,@function\n" |
"early_branch_test:\n" |
"\tLDI\t1,R1\n" |
"\tBRA\t_eb_a\n" |
"\tBREAK\n" |
"_eb_a:\n" |
"\tLDI\t2,R1\n" |
"\tBRA\t_eb_b\n" |
"\tNOP\n" |
"\tBREAK\n" |
"_eb_b:\n" |
"\tLDI\t3,R1\n" |
"\tBRA\t_eb_c\n" |
"\tNOP\n" |
"\tNOP\n" |
"\tBREAK\n" |
"_eb_c:\n" |
"\tLDI\t4,R1\n" |
"\tBRA\t_eb_d\n" |
"\tNOP\n" |
"\tNOP\n" |
"\tNOP\n" |
"\tBREAK\n" |
"_eb_d:\n" |
"\tLDI\t5,R1\n" |
"\tBRA\t_eb_e\n" |
"\tNOP\n" |
"\tNOP\n" |
"\tNOP\n" |
"\tNOP\n" |
"\tBREAK\n" |
"_eb_e:\n" |
"\tLDI\t6,R1\n" |
"\tBRA\t_eb_f\n" |
"\tNOP\n" |
"\tNOP\n" |
"\tNOP\n" |
"\tNOP\n" |
"\tNOP\n" |
"\tBREAK\n" |
"_eb_f:\n" |
"\tLDI\t0,R1\n" |
"\tJMP\tR0"); |
|
void trap_test_and(void); |
asm("\t.text\n\t.global\ttrap_test_and\n" |
"\t.type\ttrap_test_and,@function\n" |
"trap_test_and:\n" |
"\tLDI\t0,R1\n" |
"\tAND\t0xffffffdf,CC\n" |
"\tLDI\t1,R1\n" // Test fails |
"\tJMP\tR0"); |
|
void trap_test_clr(void); |
asm("\t.text\n\t.global\ttrap_test_clr\n" |
"\t.type\ttrap_test_clr,@function\n" |
"trap_test_clr:\n" |
"\tLDI\t0,R1\n" |
"\tCLR\tCC\n" |
"\tLDI\t1,R1\n" // Test fails |
"\tJMP\tR0"); |
|
void overflow_test(void); |
asm("\t.text\n\t.global\toverflow_test\n" |
"\t.type\toverflow_test,@function\n" |
"overflow_test:\n" |
"\tLDI\t0,R1\n" |
"\tLDI\t0,R3\n" // Clear our scorecard |
// First test: does adding one to the maximum integer cause an overflow? |
"\tLDI\t-1,R2\n" |
"\tLSR\t1,R2\n" |
"\tADD\t1,R2\n" |
"\tOR.V\t1,R3\n" |
// Second test: does subtracting one to the minimum integer cause an overflow? |
"\tLDI\t0x80000000,R2\n" |
"\tSUB\t1,R2\n" |
"\tOR.V\t2,R3\n" |
// Third test, overflow from LSR |
"\tLDI\t0x80000000,R2\n" |
"\tLSR\t1,R2\n" // Overflows 'cause the sign changes |
"\tOR.V\t4,R3\n" |
// Fourth test, overflow from LSL |
"\tLDI\t0x40000000,R2\n" |
"\tLSL\t1,R2\n" |
"\tOR.V\t8,R3\n" |
// Fifth test, overflow from LSL, negative to positive |
"\tLDI\t0x80000000,R2\n" |
"\tLSL\t1,R2\n" |
"\tOR.V\t16,R3\n" |
// Record our scores |
"\tXOR\t31,R3\n" |
"\tOR\tR3,R1\n" |
// And return the results |
"\tJMP\tR0"); |
|
|
void carry_test(void); |
asm("\t.text\n\t.global\tcarry_test\n" |
"\t.type\tcarry_test,@function\n" |
"carry_test:\n" |
"\tLDI\t0,R1\n" |
"\tLDI\t0,R3\n" |
// First, in adding |
"\tLDI\t-1,R2\n" |
"\tADD\t1,R2\n" |
"\tOR.C\t1,R3\n" |
// Then, in subtraction |
"\tSUB\t1,R2\n" |
"\tOR.C\t2,R3\n" |
// From a right shift |
"\tLDI\t1,R2\n" |
"\tLSR\t1,R2\n" |
"\tOR.C\t4,R3\n" |
"\tLDI\t1,R2\n" |
"\tASR\t1,R2\n" |
"\tOR.C\t8,R3\n" |
// Or from a compare |
"\tLDI\t0,R2\n" |
"\tCMP\t1,R2\n" |
"\tOR.C\t16,R3\n" |
// Set our return and clean up |
"\tXOR\t31,R3\n" |
"\tOR\tR3,R1\n" |
"\tJMP\tR0"); |
|
void loop_test(void); |
asm("\t.text\n\t.global\tloop_test\n" |
"\t.type\tloop_test,@function\n" |
"loop_test:\n" |
"\tLDI\t0,R1\n" |
// Let's try a loop: for(i=0; i<5; i++) |
"\tLDI\t0,R2\n" |
"\tLDI\t0,R3\n" |
"\tCMP\t5,R2\n" |
"\tBGE\tend_for_loop_test\n" |
"for_loop_test:" |
"\tADD\t1,R2\n" |
"\tADD\t1,R3\n" |
"\tCMP\t5,R2\n" |
"\tBLT\tfor_loop_test\n" |
"end_for_loop_test:" |
"\tCMP\t5,R3\n" |
"\tOR.NZ\t1,R1\n" |
// How about a reverse do{} while loop? These are usually cheaper than for() |
// loops. |
"\tLDI\t0,R2\n" |
// What if we use >=? |
"\tLDI\t5,R3\n" |
"bge_loop_test:\n" |
"\tADD\t1,R2\n" |
"\tSUB\t1,R3\n" |
"\tBGE\tbge_loop_test\n" |
"\tCMP\t6,R2\n" |
"\tOR.NZ\t4,R1\n" |
// Once more with the reverse loop, this time storing the loop variable in |
// memory |
"\tSUB\t4,SP\n" |
"\tLDI\t0,R2\n" |
"\tLDI\t4,R3\n" |
"\tSW\tR3,(SP)\n" |
"mem_loop_test:\n" |
"\tADD\t1,R2\n" // Keep track of the number of times loop is executed |
"\tADD\t14,R3\n" |
"\tLW\t(SP),R3\n" |
"\tSUB\t1,R3\n" |
"\tSW\tR3,(SP)\n" |
"\tBGE\tmem_loop_test\n" |
"\tCMP\t5,R2\n" |
"\tOR.NZ\t8,R1\n" |
"\tADD\t4,SP\n" |
// |
"\tJMP\tR0\n"); |
|
// Test whether or not LSL, LSR, and ASR instructions work, together with their |
// carry flags |
void shift_test(void); |
asm("\t.text\n\t.global\tshift_test\n" |
"\t.type\tshift_test,@function\n" |
"shift_test:\n" |
"\tLDI\t0,R1\n" // Bit-field of tests that have failed |
"\tLDI\t0,R3\n" // Bit-field of tests that have worked |
"\tLDI\t0,R4\n" // Upper 16-bits of the same bit field |
"\tLDI\t0,R5\n" // Upper 16-bits of tests that have failed |
// Does shifting right by 32 result in a zero? |
"\tLDI\t-1,R2\n" |
"\tLSR\t32,R2\n" |
"\tOR.Z\t1,R3\n" |
"\tOR.C\t2,R3\n" |
"\tCMP\t0,R2\n" |
"\tOR.Z\t4,R3\n" |
// Does shifting a -1 right arithmetically by 32 result in a -1? |
"\tLDI\t-1,R2\n" |
"\tASR\t32,R2\n" |
"\tOR.LT\t8,R3\n" |
"\tOR.C\t16,R3\n" |
"\tCMP\t-1,R2\n" |
"\tOR.Z\t32,R3\n" |
// Does shifting a -4 right arithmetically by 2 result in a -1? |
"\tLDI\t-4,R2\n" |
"\tASR\t2,R2\n" |
"\tOR.LT\t64,R3\n" |
"\tOR.C\t128,R1\n" |
"\tOR\t128,R3\n" // Artificially declare passing, so as not to fail it |
"\tCMP\t-1,R2\n" |
"\tOR.Z\t256,R3\n" |
// Does one more set the carry flag as desired? |
"\tASR\t1,R2\n" |
"\tOR.LT\t512,R3\n" |
"\tOR.C\t1024,R3\n" |
"\tCMP\t-1,R2\n" |
"\tOR.Z\t2048,R3\n" |
// Does shifting -1 left by 32 result in a zero? |
"\tLDI\t-1,R2\n" |
"\tLSL\t32,R2\n" |
"\tOR.Z\t4096,R3\n" |
"\tOR.C\t8192,R3\n" |
"\tCMP\t0,R2\n" |
"\tOR.Z\t16384,R3\n" |
// How about shifting by zero? |
"\tLDI\t-1,R2\n" |
"\tASR\t0,R2\n" |
"\tOR.C\t32768,R1\n" |
"\tOR\t32768,R3\n" |
"\tCMP\t-1,R2\n" |
"\tOR.Z\t1,R4\n" |
// |
"\tLSR\t0,R2\n" |
"\tLDI\t131072,R5\n" |
"\tOR.C\tR5,R1\n" |
"\tCMP\t-1,R2\n" |
"\tOR.Z\t2,R4\n" |
// |
"\tLSL\t0,R2\n" |
"\tLDI\t524288,R5\n" |
"\tOR.C\tR5,R1\n" |
"\tCMP\t-1,R2\n" |
"\tOR.Z\t4,R4\n" |
// Tally up our results and return |
"\tXOR\t7,R4\n" |
"\tXOR\t65535,R3\n" |
"\tLSL\t16,R4\n" |
"\tOR\tR4,R3\n" |
"\tOR\tR3,R1\n" |
"\tJMP\tR0"); |
|
int sw_brev(int v); |
asm("\t.text\n\t.global\tsw_brev\n" |
"\t.type\tsw_brev,@function\n" |
"sw_brev:\n" |
"\tSUB\t8,SP\n" |
"\tSW\tR2,(SP)\n" |
"\tSW\tR3,4(SP)\n" |
"\tLDI\t-1,R2\n" |
"\tCLR\tR3\n" |
"sw_brev_loop:\n" |
"\tLSL\t1,R3\n" |
"\tLSR\t1,R1\n" |
"\tOR.C\t1,R3\n" |
"\tLSR\t1,R2\n" |
"\tBZ\tsw_brev_endloop\n" |
"\tBRA\tsw_brev_loop\n" |
"sw_brev_endloop:\n" |
"\tMOV\tR3,R1\n" |
"\tLW\t(SP),R2\n" |
"\tLW\t4(SP),R3\n" |
"\tADD\t8,SP\n" |
"\tJMP\tR0"); |
|
void pipeline_stack_test(void); |
asm("\t.text\n\t.global\tpipeline_stack_test\n" |
"\t.type\tpipeline_stack_test,@function\n" |
"pipeline_stack_test:\n" |
"\tSUB\t4,SP\n" |
"\tSW\tR0,(SP)\n" |
"\tLDI\t0,R0\n" |
"\tMOV\t1(R0),R1\n" |
"\tMOV\t1(R1),R2\n" |
"\tMOV\t1(R2),R3\n" |
"\tMOV\t1(R3),R4\n" |
"\tMOV\t1(R4),R5\n" |
"\tMOV\t1(R5),R6\n" |
"\tMOV\t1(R6),R7\n" |
"\tMOV\t1(R7),R8\n" |
"\tMOV\t1(R8),R9\n" |
"\tMOV\t1(R9),R10\n" |
"\tMOV\t1(R10),R11\n" |
"\tMOV\t1(R11),R12\n" |
"\tMOV\tpipeline_stack_test_component_return(PC),R0\n" |
// "\tLJMP\tpipeline_stack_test_component\n" |
"\tBRA\tpipeline_stack_test_component\n" |
"pipeline_stack_test_component_return:\n" |
"\tCMP\t1,R1\n" |
"\tLDI.Z\t0,R1\n" |
"\tCMP\t2,R2\n" |
"\tCMP.Z\t3,R3\n" |
"\tCMP.Z\t4,R4\n" |
"\tCMP.Z\t5,R5\n" |
"\tCMP.Z\t6,R6\n" |
"\tCMP.Z\t7,R7\n" |
"\tCMP.Z\t8,R8\n" |
"\tCMP.Z\t9,R9\n" |
"\tCMP.Z\t10,R10\n" |
"\tCMP.Z\t11,R11\n" |
"\tCMP.Z\t12,R12\n" |
"\tBREV.NZ\t-1,R1\n" |
"\tLW\t(SP),R0\n" |
"\tADD\t4,SP\n" |
"\tJMP\tR0\n" |
); |
|
void pipeline_stack_test_component(void); |
asm("\t.text\n\t.global\tpipeline_stack_test_component\n" |
"\t.type\tpipeline_stack_test_component,@function\n" |
"pipeline_stack_test_component:\n" |
"\tSUB\t52,SP\n" |
"\tSW\tR0,(SP)\n" |
"\tSW\tR1,4(SP)\n" |
"\tSW\tR2,8(SP)\n" |
"\tSW\tR3,12(SP)\n" |
"\tSW\tR4,16(SP)\n" |
"\tSW\tR5,20(SP)\n" |
"\tSW\tR6,24(SP)\n" |
"\tSW\tR7,28(SP)\n" |
"\tSW\tR8,32(SP)\n" |
"\tSW\tR9,36(SP)\n" |
"\tSW\tR10,40(SP)\n" |
"\tSW\tR11,44(SP)\n" |
"\tSW\tR12,48(SP)\n" |
"\tXOR\t-1,R0\n" |
"\tXOR\t-1,R1\n" |
"\tXOR\t-1,R2\n" |
"\tXOR\t-1,R3\n" |
"\tXOR\t-1,R4\n" |
"\tXOR\t-1,R5\n" |
"\tXOR\t-1,R6\n" |
"\tXOR\t-1,R7\n" |
"\tXOR\t-1,R8\n" |
"\tXOR\t-1,R9\n" |
"\tXOR\t-1,R10\n" |
"\tXOR\t-1,R11\n" |
"\tXOR\t-1,R12\n" |
"\tLW\t(SP),R0\n" |
"\tLW\t4(SP),R1\n" |
"\tLW\t8(SP),R2\n" |
"\tLW\t12(SP),R3\n" |
"\tLW\t16(SP),R4\n" |
"\tLW\t20(SP),R5\n" |
"\tLW\t24(SP),R6\n" |
"\tLW\t28(SP),R7\n" |
"\tLW\t32(SP),R8\n" |
"\tLW\t36(SP),R9\n" |
"\tLW\t40(SP),R10\n" |
"\tLW\t44(SP),R11\n" |
"\tLW\t48(SP),R12\n" |
"\tADD\t52,SP\n" |
"\tJMP\tR0\n"); |
|
//mpy_test |
void mpy_test(void); |
asm("\t.text\n\t.global\tmpy_test\n" |
"\t.type\tmpy_test,@function\n" |
"mpy_test:\n" |
"\tCLR\tR1\n" |
// First test: let's count multiples of 137 |
"\tLDI\t137,R2\n" // What we're doing multiples of |
"\tCLR\tR3\n" // Our accumulator via addition |
"\tCLR\tR4\n" // Our index for multiplication |
"mpy_137_test_loop:\n" |
"\tMOV\tR2,R5\n" |
"\tMPY\tR4,R5\n" |
"\tCMP\tR3,R5\n" |
"\tBNZ\tend_mpy_137_test_loop_failed\n" |
// Let's try negative while we are at it |
"\tMOV\tR2,R6\n" |
"\tNEG\tR6\n" |
"\tMPY\tR4,R6\n" |
"\tNEG\tR6\n" |
"\tCMP\tR3,R6\n" |
"\tBNZ\tend_mpy_137_test_loop_failed\n" |
"\tCLR\tR6\n" |
"\tTEST\t0xffff0000,R3\n" |
"\tBNZ\tend_mpy_137_test_loop\n" |
"\tADD\tR2,R3\n" |
"\tADD\t1,R4\n" |
"\tBRA\tmpy_137_test_loop\n" |
"end_mpy_137_test_loop_failed:\n" |
"\tOR\t1,R1\n" |
"end_mpy_137_test_loop:\n" |
// Second test ... whatever that might be |
"\tJMP\tR0\n"); |
|
unsigned soft_mpyuhi(unsigned, unsigned); |
int soft_mpyshi(int,int); |
|
unsigned hard_mpyuhi(unsigned, unsigned); |
asm("\t.text\n\t.global\thard_mpyuhi\n" |
"\t.type\thard_mpyuhi,@function\n" |
"hard_mpyuhi:\n" |
"\tMPYUHI\tR2,R1\n" |
"\tRETN\n"); |
|
int hard_mpyshi(int, int); |
asm("\t.text\n\t.global\thard_mpyshi\n" |
"\t.type\thard_mpyshi,@function\n" |
"hard_mpyshi:\n" |
"\tMPYSHI\tR2,R1\n" |
"\tRETN\n"); |
|
void debugmpy(char *str, int a, int b, int s, int r) { |
#ifdef HAVE_SCOPE |
// Trigger the scope, if it hasn't been triggered yet |
// but ... dont reset it if it has been. |
SCOPEc = TRIGGER_SCOPE_NOW; |
#endif |
txstr("\r\n"); txstr(str); txhex(a); |
txstr(" x "); txhex(b); |
txstr(" = "); txhex(s); |
txstr("(Soft) = "); txhex(r); |
txstr("(Hard)\r\n"); |
} |
|
int mpyhi_test(void) { |
int a = 0xf97e27ab, b = 0; |
|
while(b<0x6fffffff) { |
int r, sr; |
|
sr = soft_mpyuhi(a, b); |
r = hard_mpyuhi(a,b); |
if (r != sr) { |
debugmpy("MPYUHI: ", a,b,sr,r); |
return 1; |
} |
|
sr = soft_mpyshi(a, b); |
r = hard_mpyshi(a,b); |
if (r != sr) { |
debugmpy("MPYSHI: ", a,b,sr,r); |
return 2; |
} |
|
sr = soft_mpyshi(-a, b); |
r = hard_mpyshi(-a,b); |
if (r != sr) { |
debugmpy("MPYSHI-NEG: ", -a,b,sr,r); |
return 3; |
} |
|
b += 0x197e2*7; |
} |
|
return 0; |
} |
|
unsigned soft_mpyuhi(unsigned a, unsigned b) { |
unsigned alo, ahi; |
unsigned rhi, rlhi, rllo; |
|
alo = (a & 0x0ffff); |
ahi = (a>>16)& 0x0ffff; |
|
rhi = 0; |
rlhi = 0; |
rllo = 0; |
|
for(int i=0; i<16; i++) { |
if (b&(1<<i)) { |
unsigned slo, shi, sup; |
slo = (alo << i); |
shi = (ahi << i); |
shi |= (slo>>16) & 0x0ffff; |
slo &= 0x0ffff; |
sup = (shi>>16)&0x0ffff; |
shi &= 0x0ffff; |
|
rhi += sup; |
rlhi += shi; |
rllo += slo; |
|
rlhi += (rllo >> 16)&0x0ffff; |
rllo &= 0x0ffff; |
|
rhi += (rlhi >> 16)&0x0ffff; |
rlhi &= 0x0ffff; |
} |
} |
|
for(int i=16; i<32; i++) { |
if (b&(1<<i)) { |
unsigned slo, shi, sup; |
slo = (alo << (i-16)); |
shi = (ahi << (i-16)); |
shi |= (slo>>16) & 0x0ffff; |
slo &= 0x0ffff; |
sup = (shi>>16)&0x0ffff; |
shi &= 0x0ffff; |
|
rhi += sup << 16; |
rhi += shi; |
rlhi += slo; |
|
rhi += (rlhi >> 16)&0x0ffff; |
rlhi &= 0x0ffff; |
} |
} |
|
return rhi; |
} |
|
int soft_mpyshi(int a, int b) { |
unsigned sgn, r, p; |
|
sgn = ((a^b)>>31)&0x01; |
|
if (a<0) a = -a; |
if (b<0) b = -b; |
|
p = a * b; |
|
// This will only fail if the lower 32-bits of of a*b are 0, |
// at which point our following negation won't capture the carry it |
// needs. |
r = soft_mpyuhi(a, b); |
|
r = (sgn)?(r^-1):r; |
if ((sgn)&&(p==0)) |
r += 1; |
return r; |
} |
|
int div_test(void); |
asm("\t.text\n\t.global\tdiv_test\n" |
"\t.type\tdiv_test,@function\n" |
"div_test:\n" |
"\tLDI\t0x4881a7,R4\n" |
"\tLDI\t0x2d5108b,R2\n" |
"\tLDI\t10,R3\n" |
"\tDIVU\tR3,R2\n" |
"\tCMP\tR4,R2\n" |
"\tLDILO.NZ\t1,R1\n" |
"\tRETN.NZ\n" |
"\tLDI\t0x2d5108b,R2\n" |
"\tDIVU\t10,R2\n" |
"\tCMP\tR4,R2\n" |
"\tLDILO.NZ\t1,R1\n" |
"\tRETN\n"); |
|
//brev_test |
//pipeline_test -- used to be called pipeline memory race conditions |
void pipeline_test(void); |
asm("\t.text\n\t.global\tpipeline_test\n" |
"\t.type\tpipeline_test,@function\n" |
"pipeline_test:\n" |
"\tSUB\t12,SP\n" |
// Test setup |
"\tLDI\t275,R2\n" |
"\tSW\tR2,4(SP)\n" |
"\tMOV\t4(SP),R2\n" |
"\tSW\tR2,(SP)\n" |
"\tCLR\tR2\n" |
// |
"\tMOV\tSP,R2\n" |
"\tLW\t(R2),R2\n" |
"\tLW\t(R2),R2\n" |
"\tCMP\t275,R2\n" |
"\tOR.NZ\t1,R1\n" |
// |
"\tMOV\tSP,R2\n" |
// Here's the test sequence |
"\tLW\t(R2),R3\n" |
"\tLW\t4(R2),R4\n" |
"\tSW\tR4,4(R3)\n" |
// Make sure we clear the load pipeline |
"\tLW\t(R2),R3\n" |
// Load our written value |
"\tLW\t8(R2),R4\n" |
"\tCMP\t275,R4\n" |
"\tOR.NZ\t2,R1\n" |
// |
// |
// Next (once upon a time) failing sequence: |
// LOD -x(R12),R0 |
// LOD y(R0),R0 |
"\tMOV\tSP,R2\n" |
"\tMOV\t4(R2),R3\n" |
"\tSW\tR3,4(R2)\n" |
"\tLDI\t3588,R4\n" // Just some random value |
"\tSW\tR4,8(R2)\n" |
"\tMOV\tR2,R3\n" |
// Here's the test sequence |
"\tLW\t(R2),R3\n" |
"\tLW\t4(R3),R3\n" |
"\tCMP\tR4,R3\n" |
"\tOR.NZ\t4,R1\n" |
// |
"\tADD\t12,SP\n" |
"\tJMP\tR0\n"); |
|
//mempipe_test |
void mempipe_test(void); |
asm("\t.text\n\t.global\tmempipe_test\n" |
"\t.type\tmempipe_test,@function\n" |
"mempipe_test:\n" |
"\tSUB\t16,SP\n" |
"\tSW\tR0,(SP)\n" |
"\tLDI\t0x1000,R11\n" |
// Test #1 ... Let's start by writing a value to memory |
"\tLDI\t-1,R2\n" |
"\tCLR\tR3\n" |
"\tSW\tR2,8(SP)\n" |
"\tLW\t8(SP),R3\n" |
"\tCMP\tR3,R2\n" |
"\tOR.NZ\t1,R1\n" |
// Test #2, reading and then writing a value from memory |
"\tNOOP\n" |
"\tNOOP\n" |
"\tCLR\tR2\n" |
"\tCLR\tR3\n" |
"\tLW\t8(SP),R2\n" // This should load back up our -1 value |
"\tSW\tR2,12(SP)\n" |
// Insist that the pipeline clear |
"\tLW\t8(SP),R2\n" |
// Now let's try loading into R3 |
"\tNOOP\n" |
"\tNOOP\n" |
"\tNOOP\n" |
"\tNOOP\n" |
"\tLW\t12(SP),R3\n" |
"\tCMP\tR3,R2\n" |
"\tOR.NZ\t2,R1\n" |
// |
"\tLW\t(SP),R0\n" |
"\tADD\t16,SP\n" |
"\tJMP\tR0\n"); |
|
//cexec_test |
void cexec_test(void); |
asm("\t.text\n\t.global\tcexec_test\n" |
"\t.type\tcexec_test,@function\n" |
"cexec_test:\n" |
"\tSUB\t4,SP\n" |
"\tSW\tR0,(SP)\n" |
// |
"\tXOR\tR2,R2\n" |
"\tADD.Z\t1,R2\n" |
"\tADD.NZ\t1,R1\n" |
"\tCMP.Z\t0,R2\n" |
"\tOR.Z\t2,R1\n" |
// |
"\tLW\t(SP),R0\n" |
"\tADD\t4,SP\n" |
"\tJMP\tR0\n"); |
|
// Pipeline stalls have been hideous problems for me. The CPU has been modified |
// with special logic to keep stages from stalling. For the most part, this |
// means that ALU and memory results may be accessed either before or as they |
// are written to the register file. This set of code is designed to test |
// whether this bypass logic works. |
// |
//nowaitpipe_test |
void nowaitpipe_test(void); |
asm("\t.text\n\t.global\tnowaitpipe_test\n" |
"\t.type\tnowaitpipe_test,@function\n" |
"nowaitpipe_test:\n" |
"\tSUB\t8,SP\n" |
// |
// Let's start with ALU-ALU testing |
// AA: result->input A |
"\tLDI\t-1,R2\n" |
"\tCLR\tR2\n" |
"\tADD\t1,R2\n" |
"\tCMP\t1,R2\n" |
"\tOR.NZ\t1,R1\n" |
// |
// AA: result -> input B |
"\tCLR\tR2\n" |
"\tCLR\tR3\n" |
"\tADD\t1,R2\n" |
"\tCMP\tR2,R3\n" |
"\tOR.Z\t2,R1\n" |
// AA: result -> input A on condition |
"\tXOR\tR2,R2\n" |
"\tADD.Z\t5,R2\n" |
"\tCMP\t5,R2\n" |
"\tOR.NZ\t4,R1\n" |
// AA: result -> input B on condition |
"\tCLR\tR2\n" |
"\tXOR\tR3,R3\n" |
"\tADD.Z\t5,R2\n" |
"\tCMP\tR2,R3\n" |
"\tOR.Z\t8,R1\n" |
// AA: result->input B plus offset |
"\tCLR\tR2\n" |
"\tXOR\tR3,R3\n" |
"\tADD\t5,R2\n" |
"\tCMP\t-5(R2),R3\n" |
"\tOR.NZ\t16,R1\n" |
// AA: result->input B plus offset on condition |
"\tCLR\tR2\n" |
"\tXOR\tR3,R3\n" |
"\tADD.Z\t5,R2\n" |
"\tCMP\t-5(R2),R3\n" |
"\tOR.NZ\t32,R1\n" |
// |
// Then we need to do the ALU-MEM input testing |
// |
"\tCLR\tR2\n" |
"\tSW\tR2,4(SP)\n" |
"\tLDI\t8352,R2\n" |
"\tLW\t4(SP),R2\n" |
"\tTST\t-1,R2\n" |
"\tOR.NZ\t64,R1\n" |
// Let's try again, this time with something that's not zero |
"\tLDI\t937,R2\n" |
"\tSW\tR2,4(SP)\n" |
"\tNOOP\n" |
"\tLW\t4(SP),R2\n" |
"\tCMP\t938,R2\n" |
"\tOR.GE\t128,R1\n" |
"\tCMP\t936,R2\n" |
"\tOR.LT\t256,R1\n" |
// Mem output->ALU input testing |
// Okay, we just did that as part of our last test |
// Mem output->mem input testing |
"\tLDI\t5328,R2\n" |
"\tLW\t4(SP),R2\n" |
"\tSW\tR2,4(SP)\n" |
"\tLW\t4(SP),R3\n" |
"\tCMP\t937,R3\n" |
"\tOR.NZ\t512,R1\n" |
// |
"\tADD\t8,SP\n" |
"\tJMP\tR0\n"); |
|
//bcmem_test |
void bcmem_test(void); |
asm("\t.text\n\t.global\tbcmem_test\n" |
"\t.type\tbcmem_test,@function\n" |
"bcmem_test:\n" |
"\tSUB\t4,SP\n" |
"\tCLR\tR1\n" |
"\tCLR\tR2\n" |
"\tLDI\t-1,R3\n" |
"\tLDI\t0x13000,R4\n" |
"\tSW\tR2,(SP)\n" |
"\tLW\t(SP),R3\n" |
"\tCMP\tR2,R3\n" |
"\tOR.NZ\t1,R1\n" |
"\tCMP\t0x13000,R4\n" |
"\tBZ\tbcmem_test_cmploc_1\n" |
"\tSW\tR4,(SP)\n" |
"bcmem_test_cmploc_1:\n" |
"\tLW\t(SP),R2\n" |
"\tCMP\tR2,R4\n" |
"\tOR.Z\t2,R1\n" |
"\tCLR\tR2\n" |
"\tCMP\tR2,R4\n" |
"\tBZ\tbcmem_test_cmploc_2\n" |
"\tSW.NZ\tR4,(SP)\n" |
"bcmem_test_cmploc_2:\n" |
"\tLW\t(SP),R2\n" |
"\tCMP\tR2,R4\n" |
"\tOR.NZ\t4,R1\n" |
// |
"\tADD\t4,SP\n" |
"\tJMP\tR0\n"); |
|
// The illegal instruction test. Specifically, illegal instructions cannot be |
// allowed to execute. The PC must, upon completion, point to the illegal |
// instruction that caused the exception. |
// |
// To create our illegal instruction, we assume that the only the three |
// operations without arguments are NOOP, BREAK, LOCK, and so we envision a |
// fourth instruction to create. |
void ill_test(void); |
asm("\t.text\n\t.global\till_test\n" |
"\t.type\till_test,@function\n" |
"ill_test:\n" // 0.111_1.110_11...... |
"\t.int\t0x7ec00000\n" |
"\tJMP\tR0\n"); |
|
// Are sim instructions considered valid? Just hit the illegal instruction |
// so we can report the result |
void sim_test(void); |
asm("\t.text\n\t.global\tsim_test\n" |
"\t.type\tsim_test,@function\n" |
"sim_test:\n" // 0.111_1.111_10...... |
"\t.int\t0x7f800000\n" |
"\tJMP\tR0\n"); |
|
// Are CIS instructions considered valid? Try two compare instructions to |
// see if they are built into our CPU. |
void cis_test(void); |
asm("\t.text\n\t.global\tcis_test\n" |
"\t.type\tcis_test,@function\n" |
"cis_test:\n" // 1.000_0.011._1.101_0.000 ... 1.000_1.011._1.110_0.000 |
"\t.int\t0x83d08be0\n" |
"\tJMP\tR0\n"); |
|
void cmpeq_test(void); |
asm("\t.text\n\t.global\tcmpeq_test\n" |
"\t.type\tcmpeq_test,@function\n" |
"cmpeq_test:\n" |
"\tCMP\tR1,R2\n" |
"\tCMP.Z\tR3,R4\n" |
"\tLDILO.NZ\t1,R1\n" |
"\tJMP\tR0\n"); |
|
void cmpneq_test(void); |
asm("\t.text\n\t.global\tcmpneq_test\n" |
"\t.type\tcmpneq_test,@function\n" |
"cmpneq_test:\n" |
"\tLDI\t1,R4\n" |
"\tCMP\tR1,R2\n" |
"\tCMP.Z\tR3,R4\n" |
"\tLDILO.Z\t1,R0\n" |
"\tJMP\tR0\n"); |
// |
// The CC register has some ... unique requirements associated with it. |
// Particularly, flags are unavailable until after an ALU operation completes, |
// and they can't really be bypassed for the CC register. After writeback, |
// the "new" CC register isn't really available for another clock. Trying to |
// bypass this extra clock can have problems, since ... some bits are fixed, |
// some bits can only be changed by the supervisor, and others can/will change |
// and even have effects--like sending the CPU to supervisor mode or |
// alternatively to sleep. |
// |
// Here, let's see if our pipeline can successfully navigate any of these |
// issues. |
// |
void ccreg_test(void); |
asm("\t.text\n\t.global\tccreg_test\n" |
"\t.type\tccreg_test,@function\n" |
"ccreg_test:\n" |
// First test: If we try to change the fixed bits, will they change |
// because the pipeline tries to bypass the write-back stage |
"\tCLR\tR1\n" // We'll start with an "answer" of success |
"\tMOV\tCC,R2\n" |
"\tMOV\tR2,R4\n" // Keep a copy |
"\tBREV\t0x0ff,R3\n" // Attempt to change the top fixed bits |
"\tXOR\tR3,R2\n" // Arrange for the changes |
"\tMOV\tR2,CC\n" // Set the changes (they won't take) |
"\tMOV\tCC,R5\n" // See if the pipeline makes them take |
"\tXOR\tR4,R5\n" // Let's look for anything that has changed |
"\tOR.NZ\t1,R1\n" // If anything has changed, then we fail the tst |
// |
// Test #2: Can we set the flags? |
"\tMOV\tCC,R6\n" |
"\tOR\t15,R6\n" |
"\tMOV\tR6,CC\n" |
"\tMOV\tCC,R7\n" |
"\tAND\t15,R7\n" |
"\tCMP\t15,R7\n" |
"\tOR.NZ\t2,R1\n" |
// |
// Test #3: How about setting specific flags, and immediately acting |
// on them? |
"\tXOR\t1+R8,R8\n" // Turn off the Z bit |
"\tOR\t1,CC\n" // Turn on the Z bit |
"\tOR.NZ\t4,R1\n" |
// |
// Test #4: Can we load the CC plus a value into a register? |
// I don't think so ... |
"\tJMP\tR0\n"); |
|
// Multiple argument test |
__attribute__((noinline)) |
int multiarg_subroutine(int a, int b, int c, int d, int e, int f, int g) { |
if (a!=0) return 1; |
if (b!=1) return 2; |
if (c!=2) return 4; |
if (d!=3) return 8; |
if (e!=4) return 16; |
if (f!=5) return 32; |
if (g!=6) return 64; |
return 0; |
} |
|
int multiarg_test(void) { |
return multiarg_subroutine(0,1,2,3,4,5,6); |
} |
|
__attribute__((noinline)) |
void wait(unsigned int msk) { |
PIC = 0x7fff0000|msk; |
asm("MOV\tidle_task(PC),uPC\n"); |
PIC = 0x80000000|(msk<<16); |
asm("WAIT\n"); |
PIC = 0; // Turn interrupts back off, lest they confuse the test |
} |
|
asm("\n\t.text\nidle_task:\n\tWAIT\n\tBRA\tidle_task\n"); |
|
__attribute__((noinline)) |
void txchr(char v) { |
if (zip_cc() & CC_GIE) { |
if (PIC & INT_UARTTX) |
PIC = INT_UARTTX; |
while((PIC & INT_UARTTX)==0) |
; |
} else |
wait(INT_UARTTX); |
_uart = v; |
} |
|
void wait_for_uart_idle(void) { |
PIC = INT_UARTTX; |
while((PIC & INT_UARTTX)==0) |
; |
} |
|
__attribute__((noinline)) |
void txstr(const char *str) { |
const char *ptr = str; |
while(*ptr) |
txchr(*ptr++); |
} |
|
__attribute__((noinline)) |
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); |
} |
} |
|
__attribute__((noinline)) |
void tx4hex(int num) { |
if (num & 0xffff0000){ |
txhex(num); |
return; |
} for(int ds=12; ds>=0; ds-=4) { |
int ch; |
ch = (num >> ds)&0x0f; |
if (ch >= 10) |
ch = 'A'+ch-10; |
else |
ch += '0'; |
txchr(ch); |
} |
} |
|
__attribute__((noinline)) |
void txreg(const char *name, int val) { |
txstr(name); // 4 characters |
txstr("0x"); // 2 characters |
txhex(val); // 8 characters |
txstr(" "); // 4 characters |
} |
|
__attribute__((noinline)) |
void save_context(int *context) { |
zip_save_context(context); |
} |
|
__attribute__((noinline)) |
void test_fails(int start_time, int *listno) { |
int context[16], stop_time; |
|
// Trigger the scope, if it hasn't already triggered. Otherwise, |
// if it has triggered, don't clear it. |
#ifdef HAVE_SCOPE |
SCOPEc = TRIGGER_SCOPE_NOW; |
#endif |
|
MARKSTOP; |
save_context(context); |
*listno++ = context[1]; |
*listno++ = context[14]; |
*listno++ = context[15]; |
#ifdef HAVE_COUNTER |
*listno = stop_time; |
#endif |
|
txstr("FAIL!\r\n\r\n"); |
txstr("Between 0x"); txhex(start_time); |
txstr(" and 0x"); txhex(stop_time); txstr("\r\n\r\n"); |
txstr("Core-dump:\r\n"); |
txreg("uR0 : ", context[0]); |
txreg("uR1 : ", context[1]); |
txreg("uR2 : ", context[2]); |
txreg("uR3 : ", context[3]); |
txstr("\r\n"); |
|
txreg("uR4 : ", context[4]); |
txreg("uR5 : ", context[5]); |
txreg("uR6 : ", context[6]); |
txreg("uR7 : ", context[7]); |
txstr("\r\n"); |
|
txreg("uR8 : ", context[8]); |
txreg("uR9 : ", context[9]); |
txreg("uR10: ", context[10]); |
txreg("uR11: ", context[11]); |
txstr("\r\n"); |
|
txreg("uR12: ", context[12]); |
txreg("uSP : ", context[13]); |
txreg("uCC : ", context[14]); |
txreg("uPC : ", context[15]); |
txstr("\r\n\r\n"); |
|
wait_for_uart_idle(); |
asm("NEXIT -1"); |
// While previous versions of cputest.c called zip_busy(), here we |
// reject that notion for the simple reason that zip_busy may not |
// necessarily halt any Verilator simulation. Instead, we try to |
// halt the CPU. |
while(1) |
zip_halt(); |
} |
|
void testid(const char *str) { |
const int WIDTH=32; |
txstr(str); |
const char *ptr = str; |
int i = 0; |
while(0 != *ptr++) |
i++; |
i = WIDTH-i; |
while(i-- > 0) |
txchr(' '); |
} |
|
int testlist[32]; |
|
void entry(void) { |
int context[16]; |
int user_stack[256], *user_stack_ptr = &user_stack[256]; |
int start_time, i; |
int cc_fail, cis_insns = 0; |
|
|
for(i=0; i<32; i++) |
testlist[i] = -1; |
|
#ifdef TIMER |
TIMER = 0x7fffffff; |
#endif |
#ifdef COUNTER |
COUNTER = 0; |
#endif |
#ifdef HAVE_SCOPE |
SCOPEc = PREPARE_SCOPE; |
#endif |
|
// *UART_CTRL = 8333; // 9600 Baud, 8-bit chars, no parity, one stop bit |
// *UART_CTRL = 25; // 9600 Baud, 8-bit chars, no parity, one stop bit |
// |
|
txstr("\r\n"); |
txstr("Running CPU self-test\r\n"); |
txstr("-----------------------------------\r\n"); |
|
int tnum = 0; |
|
// Check whether or not this CPU correctly identifies SIM instructions |
// as illegal instructions |
testid("SIM Instructions"); MARKSTART; |
cc_fail = CC_MMUERR|CC_FPUERR|CC_DIVERR|CC_BUSERR|CC_TRAP|CC_STEP|CC_SLEEP; |
if ((run_test(sim_test, user_stack_ptr))||(zip_ucc()&cc_fail)) |
test_fails(start_time, &testlist[tnum]); |
else if (zip_ucc() & CC_ILL) { |
txstr("Pass\r\n"); testlist[tnum++] = 0; // 0 |
} else |
txstr("Is this a simulator?\r\n"); |
|
testid("CIS Instructions"); MARKSTART; |
cc_fail = CC_MMUERR|CC_FPUERR|CC_DIVERR|CC_BUSERR|CC_STEP|CC_SLEEP; |
if ((run_test(cis_test, user_stack_ptr))||(zip_ucc()&cc_fail)) |
test_fails(start_time, &testlist[tnum]); |
else if (zip_ucc() & CC_ILL) |
txstr("Not supported\r\n"); |
else { |
txstr("Supported\r\n"); |
cis_insns = 1; |
} |
|
// Test break instruction in user mode |
// Make sure the break works as designed |
testid("Break test #1"); MARKSTART; |
|
cc_fail = CC_MMUERR|CC_FPUERR|CC_DIVERR|CC_BUSERR|CC_TRAP|CC_ILL|CC_STEP|CC_SLEEP; |
if ((run_test(break_one, user_stack_ptr))||(zip_ucc()&cc_fail)) |
test_fails(start_time, &testlist[tnum]); |
save_context(context); |
if ((context[15] != (int)break_one+4)||(0==(zip_ucc()&CC_BREAK))) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #1 |
|
|
// Test break instruction in user mode |
// Make sure that a decision on the clock prior won't still cause a |
// break condition |
cc_fail = CC_MMUERR|CC_FPUERR|CC_DIVERR|CC_BUSERR|CC_ILL|CC_BREAK|CC_STEP|CC_SLEEP; |
testid("Break test #2"); MARKSTART; |
if ((run_test(break_two, user_stack_ptr))||(zip_ucc()&cc_fail)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #2 |
|
// Test break instruction in user mode |
// Make sure that a decision on the clock prior won't still cause a |
// break condition |
cc_fail = (CC_FAULT)|CC_MMUERR|CC_TRAP|CC_STEP|CC_SLEEP; |
testid("Break test #3"); MARKSTART; |
run_test(break_three, user_stack_ptr); |
save_context(context); |
if ((context[15] != (int)break_three) // Insist we stop at the break |
||(0==(zip_ucc()&CC_BREAK))//insn,that the break flag is |
||(zip_ucc()&cc_fail)) // set, and no other excpt flags |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #3 |
|
// LJMP test ... not (yet) written |
|
// Test the early branching capability |
// Does it successfully clear whatever else is in the pipeline? |
testid("Early Branch test"); MARKSTART; |
if ((run_test(early_branch_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #4 |
|
// TRAP test |
testid("Trap test/AND"); MARKSTART; |
if ((run_test(trap_test_and, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
if ((zip_ucc() & 0x0200)==0) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #5 |
|
testid("Trap test/CLR"); MARKSTART; |
if ((run_test(trap_test_clr, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
if ((zip_ucc() & 0x0200)==0) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #6 |
|
// Overflow test |
testid("Overflow test"); MARKSTART; |
if ((run_test(overflow_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #7 |
|
// Carry test |
testid("Carry test"); MARKSTART; |
if ((run_test(carry_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #8 |
|
// LOOP_TEST |
testid("Loop test"); MARKSTART; |
if ((run_test(loop_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #9 |
|
// SHIFT_TEST |
testid("Shift test"); MARKSTART; |
if ((run_test(shift_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #10 |
|
// BREV_TEST |
//testid("BREV/stack test"); MARKSTART; |
//if ((run_test(brev_test, user_stack_ptr))||(zip_ucc()&0x01d90)) |
//test_fails(start_time); // #10 |
//txstr("Pass\r\n"); |
|
// PIPELINE_TEST |
testid("Pipeline test"); MARKSTART; |
if ((run_test(pipeline_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #11 |
|
// MEM_PIPELINE_STACK_TEST |
testid("Mem-Pipeline test"); MARKSTART; |
if ((run_test(mempipe_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #12 |
|
// CONDITIONAL EXECUTION test |
testid("Conditional Execution test"); MARKSTART; |
if ((run_test(cexec_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #13 |
|
// NOWAIT pipeline test |
testid("No-waiting pipeline test"); MARKSTART; |
if ((run_test(nowaitpipe_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #14 |
|
// BCMEM test |
testid("Conditional Branching test"); MARKSTART; |
if ((run_test(bcmem_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #15 |
|
// Illegal Instruction test |
testid("Ill Instruction test, NULL PC"); MARKSTART; |
if ((run_test(NULL, user_stack_ptr))||((zip_ucc()^CC_ILL)&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #16 |
|
// Illegal Instruction test |
cc_fail = CC_BUSERR|CC_DIVERR|CC_FPUERR|CC_BREAK|CC_MMUERR; |
testid("Ill Instruction test, two"); MARKSTART; |
if ((run_test(ill_test, user_stack_ptr))||(zip_ucc()&cc_fail)) |
test_fails(start_time, &testlist[tnum]); |
save_context(context); |
if (context[15] != (int)&ill_test) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #17 |
|
// Compare EQuals test |
cc_fail = CC_BUSERR|CC_DIVERR|CC_FPUERR|CC_BREAK|CC_MMUERR|CC_ILL; |
testid("Comparison test, =="); MARKSTART; |
if ((run_test(cmpeq_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #18 |
|
// Compare !EQuals test |
cc_fail = CC_BUSERR|CC_DIVERR|CC_FPUERR|CC_BREAK|CC_MMUERR|CC_ILL; |
testid("Comparison test, !="); MARKSTART; |
if ((run_test(cmpneq_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #19 |
|
// Pipeline memory race condition test |
// DIVIDE test |
|
// CC Register test |
testid("CC Register test"); MARKSTART; |
if ((run_test(ccreg_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #20 |
|
// Multiple argument test |
testid("Multi-Arg test"); MARKSTART; |
if ((run_test(multiarg_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #21 |
|
// MPY_TEST |
testid("Multiply test"); MARKSTART; |
if ((run_test(mpy_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #22 |
|
// MPYxHI_TEST |
testid("Multiply HI-word test"); MARKSTART; |
if ((run_test(mpyhi_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
txstr("Pass\r\n"); testlist[tnum++] = 0; // #23 |
|
// DIV_TEST |
testid("Divide test"); |
if ((zip_cc() & 0x20000000)==0) { |
txstr("No divide unit installed\r\n"); |
} else { MARKSTART; |
if ((run_test(div_test, user_stack_ptr))||(zip_ucc()&CC_EXCEPTION)) |
test_fails(start_time, &testlist[tnum]); |
} txstr("Pass\r\n"); testlist[tnum++] = 0; // #24 |
|
|
txstr("\r\n"); |
txstr("-----------------------------------\r\n"); |
txstr("All tests passed. Halting CPU.\r\n"); |
wait_for_uart_idle(); |
for(int k=0; k<50000; k++) |
asm("NOOP"); |
asm("NEXIT 0"); |
zip_halt(); |
} |
|
// To build this: |
// zip-gcc -O3 -Wall -Wextra -nostdlib -fno-builtin -T xula.ld -Wl,-Map,cputest.map cputest.cpp -o cputest |
// zip-objdump -D cputest > cputest.txt |
// |
/sw/dev/doorbell.c
36,7 → 36,6
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include "asmstartup.h" |
#include "board.h" |
|
#include "samples.c" |
/sw/dev/doorbell2.c
50,7 → 50,6
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include "asmstartup.h" |
#include "board.h" |
#include "rtcsim.h" |
#include "display.h" |
59,8 → 58,6
|
#include "samples.c" |
|
void zip_halt(void); |
|
void build_dpymsg(char *msg, unsigned clkval); |
void build_uartmsg(char *msg, unsigned clkval); |
void showval(int val); |
/sw/dev/helloworld.c
46,7 → 46,6
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include "asmstartup.h" |
#include "board.h" |
|
const char msg[] = "Hello, world!\r\n"; |
55,8 → 54,13
volatile IOSPACE *const sys = (IOSPACE *)IOADDR; |
int ledset = 0; |
|
sys->io_spio = 0x0f0; |
sys->io_pic = 0x07fffffff; // Acknowledge and turn off all ints |
|
// sys->io_watchdog = TM_ONE_SECOND * 2; |
_scope->s_ctrl = 2 | WBSCOPE_NO_RESET; |
|
sys->io_spio = 0x0fc; |
|
/// Turn off timer B |
sys->io_watchdog = 0; |
|
64,6 → 68,7
const char *ptr; |
sys->io_timer = TM_ONE_SECOND; // Ticks per second, 80M |
sys->io_pic = 0x07fffffff; // Acknowledge and turn off all ints |
sys->io_pic = INT_ENABLEV(INT_BUTTON); |
|
ptr = msg; |
while(*ptr) { |
72,6 → 77,7
// Wait while our transmitter is busy |
while((sys->io_pic & INT_UARTTX)==0) |
; |
|
sys->io_uart = iv; // Transmit our character |
sys->io_pic = INT_UARTTX; // Clear the int flag |
} |
93,6 → 99,7
sys->io_spio = ledset&0x0f5; |
else |
sys->io_spio = ledset; |
sys->io_watchdog = 0; |
} |
} |
|
/sw/dev/kptest.c
35,7 → 35,6
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include "asmstartup.h" |
#include "board.h" |
#include "keypad.h" |
#include "txfns.h" |
/sw/dev/resetdump.s
0,0 → 1,491
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;// |
;; |
;; 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 wait for a button press to restart. |
;; sometime before the desired clock, set the watchdog timer. |
;; 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 also 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-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 |
;; 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 |
SW R0,(DBG) |
LDI 0x00000002,R0 |
SW R0,(SCOPE) |
JSR internal_kpanic |
LDI _top_of_stack,SP |
LDI gostr,R1 |
JSR raw_put_string |
LDI 0x7fffffff,R1 |
SW R1,(PIC) |
; ; Turn on the button interrupt--so we can stop on interrupt |
; LDI 0x80010001,R1 |
; SW R1,(PIC) |
;LDI 0xff000002,R1 |
;SW R1,(SCOPE) |
; For running in supervisor mode: |
; JSR entry |
; otherwise .. |
MOV entry(PC),uPC |
MOV SP,uSP |
RTU |
JMP kpanic |
|
.global kpanic |
.type kpanic,@function |
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: |
LW (SPIO),R0 |
TEST 0x010,R0 |
BNZ kpanic_wait_for_button_release |
kpanic_wait_for_button: |
LW (SPIO),R0 |
TEST 0x010,R0 |
BZ kpanic_wait_for_button |
BRA _start |
HALT |
|
internal_kpanic: |
; The original R0 is stored in (DBG) |
SW R1,4(DBG) |
SW R2,8(DBG) |
SW R0,12(DBG) ; Our return address |
|
; Make sure the watchdog is off |
LDI 0,R1 |
SW R1,0x40c |
|
; R0 |
LDI 0,R1 |
LW (DBG),R2 |
JSR uart_put_reg_value |
|
; R1 |
LDI 1,R1 |
LW 4(DBG),R2 |
JSR uart_put_reg_value |
; R2 |
LDI 2,R1 |
LW 8(DBG),R2 |
JSR uart_put_reg_value |
; R3 |
LDI 3,R1 |
MOV R3,R2 |
JSR uart_put_reg_value |
|
; R4 |
LDI 4,R1 |
MOV R4,R2 |
JSR uart_put_reg_value |
|
; R5 |
LDI 5,R1 |
MOV R5,R2 |
JSR uart_put_reg_value |
|
; R6 |
LDI 6,R1 |
MOV R6,R2 |
JSR uart_put_reg_value |
|
; R7 |
LDI 7,R1 |
MOV R7,R2 |
JSR uart_put_reg_value |
|
; R8 |
LDI 8,R1 |
MOV R8,R2 |
JSR uart_put_reg_value |
|
; R9 |
LDI 9,R1 |
MOV R9,R2 |
JSR uart_put_reg_value |
|
; R10 |
LDI 10,R1 |
MOV R10,R2 |
JSR uart_put_reg_value |
|
; R11 |
LDI 11,R1 |
MOV R11,R2 |
JSR uart_put_reg_value |
|
; R12 |
LDI 12,R1 |
MOV R12,R2 |
JSR uart_put_reg_value |
|
; SP |
LDI 13,R1 |
MOV R13,R2 |
JSR uart_put_reg_value |
|
; uR0 |
LDI 16,R1 |
MOV uR0,R2 |
JSR uart_put_reg_value |
|
; uR1 |
LDI 17,R1 |
MOV uR1,R2 |
JSR uart_put_reg_value |
|
LDI 18,R1 |
MOV uR2,R2 |
JSR uart_put_reg_value |
|
LDI 19,R1 |
MOV uR3,R2 |
JSR uart_put_reg_value |
|
LDI 20,R1 |
MOV uR4,R2 |
JSR uart_put_reg_value |
|
LDI 21,R1 |
MOV uR5,R2 |
JSR uart_put_reg_value |
|
LDI 22,R1 |
MOV uR6,R2 |
JSR uart_put_reg_value |
|
LDI 23,R1 |
MOV uR7,R2 |
JSR uart_put_reg_value |
|
LDI 24,R1 |
MOV uR8,R2 |
JSR uart_put_reg_value |
|
LDI 25,R1 |
MOV uR9,R2 |
JSR uart_put_reg_value |
|
LDI 26,R1 |
MOV uR10,R2 |
JSR uart_put_reg_value |
|
LDI 27,R1 |
MOV uR11,R2 |
JSR uart_put_reg_value |
|
LDI 28,R1 |
MOV uR12,R2 |
JSR uart_put_reg_value |
|
; uSP |
LDI 29,R1 |
MOV uSP,R2 |
JSR uart_put_reg_value |
|
; uCC |
LDI 30,R1 |
MOV uCC,R2 |
JSR uart_put_reg_value |
|
; uPC |
LDI 31,R1 |
MOV uPC,R2 |
JSR uart_put_reg_value |
|
;stack_mem_dump: |
; LDI 0,R4 |
; LDI _top_of_stack,R5 |
;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 |
|
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 0x0fff,R6 |
LDI 0x0f8,R7 |
SW R7,(SPIO) |
full_mem_dump_loop: |
MOV R5,R1 |
LW (R5),R2 |
JSR uart_dump_mem_value |
LDI 0x0f2,R7 |
SW R7,(SPIO) |
|
ADD 4,R5 |
SUB 1,R6 |
BGE full_mem_dump_loop |
|
LDI 0x0f5,R7 |
SW 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 |
LW (R7),R2 |
MOV R2,R5 ; R5 will not be changed by a subroutine |
JSR 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 ; 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 4,R7 ; Get the scope data address |
dump_scope_loop: |
MOV R7,R1 |
LW (R7),R2 |
JSR uart_dump_mem_value |
SUB 1,R6 |
BGE dump_scope_loop |
|
dump_buserr: |
; Dump a bus error address, if used |
LDI buserr_header, R1 |
JSR raw_put_string |
LDI BUSERR,R1 |
LW (R1),R2 |
JSR uart_dump_mem_value |
|
end_internal_panic: |
LDI doublenewline,R1 |
JSR raw_put_string |
LDI 0x0ff,R7 |
SW R7,(SPIO) |
LW 12(DBG),PC |
RTN |
|
; R0 is return address |
; R1 is register ID |
; R2 is register value to output |
uart_put_reg_value: |
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 |
JSR raw_put_uart |
LDI '0',R1 |
CMP 10,R2 |
LDILO.GE '1',R1 |
SUB.GE 10,R2 |
JSR raw_put_uart |
MOV R2,R1 |
AND 15,R1 |
JSR get_hex |
JSR raw_put_uart |
LDI ':',R1 |
JSR raw_put_uart |
LW 20(DBG),R2 |
LDI 8,R3 |
uart_put_loop: |
MOV R2,R1 |
LSR 28,R1 |
LSL 4,R2 |
JSR get_hex |
JSR raw_put_uart |
SUB 1,R3 |
BNZ uart_put_loop |
LDI newline, R1 |
JSR raw_put_string |
LW 16(DBG),R0 |
LW 20(DBG),R2 |
LW 24(DBG),R3 |
RTN |
|
uart_dump_mem_value: |
; R0 = return address |
; R1 = Memory address |
; R2 = Memory Value |
; Local: R3 = working value |
SW R0,28(DBG) |
MOV R1,R3 ; R3 = Memory address |
MOV R2,R4 ; R4 = Memory Value |
LDI memopenstr,R1 |
JSR raw_put_string |
; Set up a loop to dump things |
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: |
MOV R3,R1 |
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 memmidstr,R1 |
JSR raw_put_string |
|
; Set up a loop to dump the memory value now |
LDI 8,R2 |
uart_put_mem_value_loop: |
MOV R4,R1 |
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 newline,R1 |
JSR raw_put_string |
; And return from our subroutine |
LW 28(DBG),R0 |
RTN |
|
get_hex: |
ADD 0x30,R1 |
CMP 0x3a,R1 |
ADD.GE 7,R1 ; Add 'A'-'0'-10 |
JMP R0 |
|
; 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 |
SW R2,(PIC) ; Clear the PIC, turn off interrupts, etc. |
raw_put_uart_retest: |
LW (PIC),R2 |
TEST INT_UARTTX,R2 |
BZ raw_put_uart_retest |
SW R1,(UART) |
LW 32(DBG),R2 |
JMP R0 |
|
.section .fixdata |
DBG: |
.int 0,0,0,0,0,0,0,0,0,0 |
|
.set INT_UARTRX, 0x040 |
.set INT_UARTTX, 0x080 |
.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 "]: " |
gostr: |
.string "Go!\r\n" |
|
/sw/dev/uartecho.c
0,0 → 1,58
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: uartecho.c |
// |
// Project: CMod S6 System on a Chip, ZipCPU demonstration project |
// |
// Purpose: To simply test if both parts of the UART work. All this file |
// does is attempt to receive a character from the UART, and if |
// successful, print it back out the UART. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// 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 |
// 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 "board.h" |
|
void entry(void) { |
SYSPIC = INT_CLEARPIC; |
|
while(1) { |
int ch; |
|
SYSPIC = INT_UARTRX; |
while((SYSPIC & INT_UARTRX)==0) |
; |
ch = _uart & 0x0ff; |
|
SYSPIC = INT_UARTTX; |
while((SYSPIC & INT_UARTTX)==0) |
; |
_uart = ch; |
} |
} |