From 2887165ae2990e7f93038f3220ed74ee429b5244 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Wed, 20 Jul 2016 23:59:02 +0000 Subject: Initial Checkin --- .gitmodules | 2 +- Makefile | 88 +++++++++++++-- README.md | 44 ++++++++ bitfile/README.md | 7 ++ bootrom/bram.mk | 79 +++++++++++++ bootrom/common/bram.ld | 40 +++++++ bootrom/common/mem.awk | 63 +++++++++++ bootrom/demo/Makefile | 5 + bootrom/demo/demo.S | 71 ++++++++++++ bootrom/demo/hello_msg.txt | 37 ++++++ openocd | 2 +- riscv-gnu-toolchain | 2 +- riscv-tests | 2 +- software/demo_gpio/Makefile | 9 ++ software/demo_gpio/demo_gpio.c | 182 ++++++++++++++++++++++++++++++ software/shared/Makefile.shared | 47 ++++++++ software/shared/drivers_sifive/plic.c | 123 ++++++++++++++++++++ software/shared/drivers_sifive/plic.h | 70 ++++++++++++ software/shared/entry.S | 1 + software/shared/gpio.h | 10 ++ software/shared/init.c | 48 ++++++++ software/shared/link.lds | 1 + software/shared/shared.h | 73 ++++++++++++ software/shared/spi.h | 8 ++ software/shared/syscall.c | 206 ++++++++++++++++++++++++++++++++++ software/shared/uart.h | 12 ++ 26 files changed, 1221 insertions(+), 11 deletions(-) create mode 100644 README.md create mode 100644 bitfile/README.md create mode 100644 bootrom/bram.mk create mode 100644 bootrom/common/bram.ld create mode 100644 bootrom/common/mem.awk create mode 100644 bootrom/demo/Makefile create mode 100644 bootrom/demo/demo.S create mode 100644 bootrom/demo/hello_msg.txt create mode 100644 software/demo_gpio/Makefile create mode 100644 software/demo_gpio/demo_gpio.c create mode 100644 software/shared/Makefile.shared create mode 100644 software/shared/drivers_sifive/plic.c create mode 100644 software/shared/drivers_sifive/plic.h create mode 120000 software/shared/entry.S create mode 100644 software/shared/gpio.h create mode 100644 software/shared/init.c create mode 120000 software/shared/link.lds create mode 100644 software/shared/shared.h create mode 100644 software/shared/spi.h create mode 100644 software/shared/syscall.c create mode 100644 software/shared/uart.h diff --git a/.gitmodules b/.gitmodules index 9527506..c870132 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,4 +9,4 @@ url = http://github.com/sifive/riscv-binutils-gdb.git [submodule "riscv-tests"] path = riscv-tests - url = http://github.com/timsifive/riscv-tests + url = http://github.com/riscv/riscv-tests.git diff --git a/Makefile b/Makefile index 9d81d77..ac1f9b6 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,36 @@ srcdir := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) srcdir := $(srcdir:/=) wrkdir := $(CURDIR)/work +############################################################# +# Prints help message +############################################################# + +.PHONY: help +help : + @echo " SiFive Freedom E Software Development Kit " + @echo " Makefile targets:" + @echo "" + @echo " tools:" + @echo " Install compilation & debugging tools" + @echo "" + @echo " fpga [BOOTROM=demo]:" + @echo " Rebuild the image (including Boot SW) to" + @echo " reprogram the FPGA. " + @echo "" + @echo " software [PROGRAM=demo_gpio]:" + @echo " Build a software program to load with the" + @echo " debugger." + @echo "" + @echo " run_debug [PROGRAM=demo_gpio]:" + @echo " Launch the debugging tools to load or" + @echo " debug running programs." + @echo "" + @echo " For more information, visit dev.sifive.com" + +############################################################# +# This section is for tool installation +############################################################# + toolchain_srcdir := $(srcdir)/riscv-gnu-toolchain toolchain32_wrkdir := $(wrkdir)/riscv32-gnu-toolchain toolchain64_wrkdir := $(wrkdir)/riscv64-gnu-toolchain @@ -23,10 +53,11 @@ target32 := riscv32-unknown-linux-gnu .PHONY: all -all: $(hex) +all: tools @echo All done. tools: tools64 tools32 openocd gdb + @echo All Tools Installed tools64: $(toolchain_dest)/bin/$(target64)-gcc tools32: $(toolchain_dest)/bin/$(target32)-gcc @@ -43,7 +74,6 @@ $(toolchain_dest)/bin/$(target32)-gcc: $(toolchain_srcdir) cd $(toolchain32_wrkdir); $(toolchain_srcdir)/configure --prefix=$(toolchain_dest) --with-arch=RV32IMA $(MAKE) -C $(toolchain32_wrkdir) - $(openocd_dest)/bin/openocd: $(openocd_srcdir) mkdir -p $(openocd_wrkdir) cd $(openocd_srcdir); \ @@ -60,11 +90,55 @@ $(gdb_dest)/bin/$(target64)-gdb : $(gdb_srcdir) $(MAKE) -C $(gdb_wrkdir) install +.PHONY: uninstall +uninstall: + rm -rf -- $(toolchain_dest) + +############################################################# +# This Section is for MCS File Generation ( +############################################################# + +BOOTROM ?= demo +BOOTROM_DIR = $(srcdir)/bootrom/$(BOOTROM)/ + +.PHONY: +fpga: + cd $(BOOTROM_DIR);\ + make mcs + +fpga_clean: + cd $(BOOTROM_DIR);\ + make clean + +############################################################# +# This Section is for Software (non-boot) compilation +############################################################# + +PROGRAM ?= demo_gpio +PROGRAM_DIR = $(srcdir)/software/$(PROGRAM) + +.PHONY: software +software: + cd $(PROGRAM_DIR);\ + make + +software_clean: + cd $(PROGRAM_DIR);\ + make clean + +############################################################# +# This Section is for launching the debugger +############################################################# +OPENOCD = $(toolchain_dest)/bin/openocd +OPENOCDARGS += -f $(srcdir)/riscv-tests/debug/targets/m2gl_m2s/openocd.cfg + +GDB = $(toolchain_dest)/bin/riscv64-unknown-elf-gdb +GDBARGS += -ex "target extended-remote localhost:3333" + +run_debug: + $(OPENOCD) $(OPENOCDARGS) & + $(GDB) $(PROGRAM_DIR)/$(PROGRAM) $(GDBARGS) + .PHONY: clean clean: rm -rf -- $(wrkdir) - - -.PHONY: superclean -superclean: - rm -rf -- $(toolchain_dest) diff --git a/README.md b/README.md new file mode 100644 index 0000000..c7aae18 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# README # + +This repository, maintained by SiFive, Inc, makes it easy to get started developing software for the Freedom E RISC-V platform. + +### Contents ### + +* RISC-V Software Toolchain +* RISC-V Debugging Toolchain +* FPGA Toolchain Commands (Specific to the Freedom E300 Arty Development Kit). + +### Setting up the SDK ### + +First, clone this repository: + +``` +git clone --recursive http://github.com/sifive/freedom-e-sdk.git +``` + +Next, build the tools: + +``` +cd freedom-e-sdk +make tools +``` + +To compile a bare-metal RISC-V program: + +``` +cd freedom-e-sdk +make software [PROGRAM=demo_gpio] +``` + +To see additional options: + +``` +cd freedom-e-sdk +make help +``` + +### For More Information ### + +Documentation, Forums, and much more available at + +[dev.sifive.com](http://dev.sifive.com) \ No newline at end of file diff --git a/bitfile/README.md b/bitfile/README.md new file mode 100644 index 0000000..8dbf9c5 --- /dev/null +++ b/bitfile/README.md @@ -0,0 +1,7 @@ +Download the bitfile and associated files from: + +https://dev.sifive.com/develop/freedom-e300-arty-dev-kit/ + +Place them in the freedom-e-sdk/bitfile/ directory. + + diff --git a/bootrom/bram.mk b/bootrom/bram.mk new file mode 100644 index 0000000..2c7875a --- /dev/null +++ b/bootrom/bram.mk @@ -0,0 +1,79 @@ +ifndef _BRAM_MK +_BRAM_MK := # defined + +BRAM_ADDR := 0x1000 + +BASEDIR := $(dir $(lastword $(MAKEFILE_LIST))) +BASEDIR := $(BASEDIR:/=) +DATADIR := $(BASEDIR)/common + +LDSCRIPT := $(DATADIR)/bram.ld +MEMSCRIPT := $(DATADIR)/mem.awk + +BITFILEDIR := $(BASEDIR)/../bitfile +BITFILE_VERSION ?= 0-1 + +BITFILE := $(BITFILEDIR)/freedom-e300-arty-$(BITFILE_VERSION).bit +CFGFILE := $(BITFILEDIR)/FreedomE-$(BITFILE_VERSION).cfg +MEMINFO := $(BITFILEDIR)/freedom-e300-arty-$(BITFILE_VERSION).mmi +BRAM_INST := `cat $(BITFILEDIR)/freedom-e300-arty-$(BITFILE_VERSION)-bootrom.txt` + +XLEN = 32 +BRAM_WIDTH=64 + +all: mcs + +$(BITFILE): + @echo "In order to get Version $(BITFILE_VERSION) of the bitfile:" + @echo "" + @cat $(BITFILEDIR)/README.md + +CC := ../../toolchain/bin/riscv$(XLEN)-unknown-elf-gcc +UPDATEMEM ?= updatemem + +CDEFINES += -DCFG_STRING=\"$(CFGFILE)\" + +CFLAGS ?= -m$(XLEN) -O2 -std=c11 -pedantic -Wall \ + -nostartfiles -fno-common -mcmodel=medany -g \ + $(CDEFINES) + +LDFLAGS ?= -T$(LDSCRIPT) -static -nostdlib +OBJCOPY ?= objcopy +elf: $(PROG).elf +hex: $(PROG).hex +mem: $(PROG).impl.mem +mcs: $(PROG).mcs + +$(PROG).elf: $(SRCS) $(EXTRAS) $(CFGFILE) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(SRCS) + +$(PROG).hex: $(PROG).elf + $(OBJCOPY) -S -O verilog --change-addresses -$(BRAM_ADDR) $< $@ + +# Manually reverse the byte order due to limitations in UpdateMEM. +# Per UG898: "When UpdateMEM inputs data, it takes data from data input +# files in Bit Lane sized chunks from the most right value first to the +# left most. For example, if the first 64 bits of input data are +# 0xB47DDE02826A8419 then the value 0xB4 is the first value to be set +# into a Block RAM." +$(PROG).impl.mem: $(PROG).hex $(MEMSCRIPT) + awk -v WIDTH=$(BRAM_WIDTH) -v REVERSE=1 -f $(MEMSCRIPT) $< > $@ + +.PHONY: $(PROG).mcs +$(PROG).mcs : $(BITFILE) $(PROG).impl.mem + @echo "" + @echo "" + @echo "TO COMPLETE THE BUILD OF $(PROG).mcs, " + @echo "ENTER THE FOLLOWING IN THE VIVADO TCL WINDOW:" + @echo "" + @echo "cd `pwd`" + @echo "" + @echo "exec $(UPDATEMEM) -meminfo $(MEMINFO) -data $(PROG).impl.mem -proc $(BRAM_INST) -bit $(BITFILE) --out $(PROG).bit -force" + @echo "" + @echo "write_cfgmem -format mcs -interface spix4 -size 16 -loadbit {up 0x0 $(PROG).bit} -file $(PROG).mcs -force" + @echo "" + +clean: + rm -f -- $(foreach ext,elf hex impl.mem bit mcs,$(PROG).$(ext)) + +endif # _BRAM_MK diff --git a/bootrom/common/bram.ld b/bootrom/common/bram.ld new file mode 100644 index 0000000..45df67f --- /dev/null +++ b/bootrom/common/bram.ld @@ -0,0 +1,40 @@ +OUTPUT_ARCH("riscv") +ENTRY(_start) + +MEMORY +{ + rom (rx) : ORIGIN = 0x00001000, LENGTH = 4K + ram (rwx) : ORIGIN = 0x80000000, LENGTH = 256M +} + +SECTIONS +{ + PROVIDE(_rom = ORIGIN(rom)); + PROVIDE(_rom_end = _rom + LENGTH(rom)); + PROVIDE(_ram = ORIGIN(ram)); + PROVIDE(_ram_end = _ram + LENGTH(ram)); + + .text : { + PROVIDE(_ftext = .); + *(.text.init) + *(.text .text.* .gnu.linkonce.t.*) + PROVIDE(_etext = .); + } > rom + + .rodata : { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } > rom + + .bss : ALIGN(8) { + PROVIDE(_fbss = .); + *(.bss .bss.* .gnu.linkonce.b.*) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + . = ALIGN(8); + PROVIDE(_ebss = .); + } > ram + + . += 0x1000; + PROVIDE(_sp = NEXT(0x1000)); + + PROVIDE(_end = .); +} diff --git a/bootrom/common/mem.awk b/bootrom/common/mem.awk new file mode 100644 index 0000000..3003b56 --- /dev/null +++ b/bootrom/common/mem.awk @@ -0,0 +1,63 @@ +BEGIN { + RS = ORS = "\r\n" + addr = 0 + buf = "" + limit = (WIDTH > 0 ? WIDTH / 4 : 16) + print "@00000000" +} + +# Portable strtonum() replacement +function atoi(str, x, n, i, b, c) { + if (str ~ /^0[0-7]*$/) { + i = 2 + b = 8 + } else if (str ~ /^0[xX][[:xdigit:]]+$/) { + i = 3 + b = 16 + } else { + return str + } + + x = 0 + n = length(str) + for (; i <= n; i++) { + c = tolower(substr(str, i , 1)) + c = index("123456789abcdef", c) + x = (x * b) + c + } + return x +} + +function out(x) { + addr++ + buf = (REVERSE ? buf x : x buf) + if (length(buf) >= limit) { + print buf + buf = "" + } +} + +function pad(n) { + while (addr < n) { + out("00") + } +} + +match($1, /^@[[:xdigit:]]+/) { + pad(atoi("0x" substr($1, RSTART+1, RLENGTH-1))) + next +} + +{ + for (i = 1; i <= NF; i++) { + out($i) + } +} + +END { + align = limit / 2 + pad(int((addr + align - 1) / align) * align) + if (length(buf) > 0) { + print buf; + } +} diff --git a/bootrom/demo/Makefile b/bootrom/demo/Makefile new file mode 100644 index 0000000..dfe3663 --- /dev/null +++ b/bootrom/demo/Makefile @@ -0,0 +1,5 @@ +PROG := demo +SRCS := demo.S +EXTRAS := hello_msg.txt + +include ../bram.mk diff --git a/bootrom/demo/demo.S b/bootrom/demo/demo.S new file mode 100644 index 0000000..c6f2618 --- /dev/null +++ b/bootrom/demo/demo.S @@ -0,0 +1,71 @@ +#define UART_BASE 0x48000000 +#define SPI_BASE 0x48001000 +#define GPIO_BASE 0x48002000 + +#ifndef CFG_STRING +#error Must define CFG_STRING +#endif + + .globl _start +_start: + j uart_init + nop + nop + .word cfgstr + +uart_init: + // a1 = UART_TX + // a2 = hello_msg_end + // for (a0 = hello_msg_start; a0 != a2; ++ a0){ + // while (a3 = TX_READY == 0); + // a3 = *a0 + // *a1 = a3 + // } + + la a0, hello_msg_start + la a2, hello_msg_end + li a1, UART_BASE + +uart_loop: + lw a3, 4(a1) // Wait until non-zero (uart can send data). + beqz a3, uart_loop + + lb a3, 0(a0) // read the next character in hello_msg + sw a3, 0(a1) // Write the current character. + addi a0, a0, 1 // increment the pointer. + bne a0, a2, uart_loop + +gpio_init: + li a0, GPIO_BASE + li t0, 0xFFFF0000 + sw t0, 4(a0) + +gpio_loop: + // For Red LEDs, increment manually. + li t0, 0x1 + sw t0, (a0) + li t0, 0x2 + sw t0, (a0) + li t0, 0x4 + sw t0, (a0) + li t0, 0x8 + sw t0, (a0) + + // For Blue and Green LEDs, sample the switches & buttons + + lw t0, (a0) + srli t0, t0, 16 + andi t0, t0, 0xFF + slli t0, t0, 4 + sw t0, (a0) + + j gpio_loop + + .section .rodata + +hello_msg_start: + .incbin "hello_msg.txt" +hello_msg_end: + +cfgstr: + .incbin CFG_STRING diff --git a/bootrom/demo/hello_msg.txt b/bootrom/demo/hello_msg.txt new file mode 100644 index 0000000..c3cb3f2 --- /dev/null +++ b/bootrom/demo/hello_msg.txt @@ -0,0 +1,37 @@ + + SIFIVE, INC. + + 5555555555555555555555555 + 5555 5555 + 5555 5555 + 5555 5555 + 5555 5555555555555555555555 + 5555 555555555555555555555555 + 5555 5555 + 5555 5555 + 5555 5555 +5555555555555555555555555555 55555 + 55555 555555555 55555 + 55555 55555 55555 + 55555 5 55555 + 55555 55555 + 55555 55555 + 55555 55555 + 55555 55555 + 55555 55555 + 555555555 + 55555 + 5 + + SiFive RISC-V Coreplex + +Welcome to the Arty FPGA Freedom E Dev Kit. +The red LEDs are blinking very fast and appear +dim. Try pressing the buttons and switches to control +the blue and green LEDs. You can try using the +debugger to step through the boot code. + + + + + diff --git a/openocd b/openocd index 47ef2c1..ebb090c 160000 --- a/openocd +++ b/openocd @@ -1 +1 @@ -Subproject commit 47ef2c144b298d820c6daf5d38e5d4c110cf9f3e +Subproject commit ebb090c01e7d8a0c29bcf86333cd30da28be28b7 diff --git a/riscv-gnu-toolchain b/riscv-gnu-toolchain index 11fb8bb..83665a7 160000 --- a/riscv-gnu-toolchain +++ b/riscv-gnu-toolchain @@ -1 +1 @@ -Subproject commit 11fb8bbdb07e80a5b02c9e8753482fb739152bef +Subproject commit 83665a769ab27547192c86045bd17b14f4f64461 diff --git a/riscv-tests b/riscv-tests index c3b7d47..97cffef 160000 --- a/riscv-tests +++ b/riscv-tests @@ -1 +1 @@ -Subproject commit c3b7d479afd8e5c2bcb7e0632f03b80386196f17 +Subproject commit 97cffefa6ebeb5fdc174fc51c7d642f714daa559 diff --git a/software/demo_gpio/Makefile b/software/demo_gpio/Makefile new file mode 100644 index 0000000..5f201a2 --- /dev/null +++ b/software/demo_gpio/Makefile @@ -0,0 +1,9 @@ +#See LICENSE for license details. + +TARGET=demo_gpio +CFLAGS += -g +CDEFINES += -DUSE_PLIC -DUSE_M_TIME + +SWDIR = ../ + +include $(SWDIR)/shared/Makefile.shared diff --git a/software/demo_gpio/demo_gpio.c b/software/demo_gpio/demo_gpio.c new file mode 100644 index 0000000..3b0b6f5 --- /dev/null +++ b/software/demo_gpio/demo_gpio.c @@ -0,0 +1,182 @@ +// See LICENSE for license details. + +#include + +#include "shared.h" +#include "plic.h" +#include + +void reset_demo (void); + +// Structures for registering different interrupt handlers +// for different parts of the application. +typedef void (*function_ptr_t) (void); + +void no_interrupt_handler (void) {}; + +function_ptr_t g_ext_interrupt_handlers[9]; + +function_ptr_t g_m_timer_interrupt_handler = no_interrupt_handler; + +// Instance data for the PLIC. + +plic_instance_t g_plic; + +// Simple variables for LEDs, buttons, etc. +volatile unsigned int* g_outputs = (unsigned int *) (GPIO_BASE_ADDR + GPIO_OUT_OFFSET); +volatile unsigned int* g_inputs = (unsigned int *) (GPIO_BASE_ADDR + GPIO_IN_OFFSET); +volatile unsigned int* g_tristates = (unsigned int *) (GPIO_BASE_ADDR + GPIO_TRI_OFFSET); + + +/*Entry Point for PLIC Interrupt Handler*/ +void handle_m_ext_interrupt(){ + plic_source int_num = PLIC_claim_interrupt(&g_plic); + if ((int_num >=1 ) && (int_num <= 8)) { + g_ext_interrupt_handlers[int_num](); + } + else { + _exit(1 + (uintptr_t) int_num); + } + PLIC_complete_interrupt(&g_plic, int_num); +} + + +/*Entry Point for Machine Timer Interrupt Handler*/ +void handle_m_time_interrupt(){ + + clear_csr(mie, MIP_MTIP); + + // Reset the timer for 3s in the future. + // This also clears the existing timer interrupt. + + volatile uint64_t * mtime = (uint64_t*) MTIME_ADDR; + volatile uint64_t * mtimecmp = (uint64_t*) MTIMECMP_BASE_ADDR; + uint64_t now = *mtime; + uint64_t then = now + 3 * CLOCK_FREQUENCY / RTC_PRESCALER; + *mtimecmp = then; + + // read the current value of the LEDS and invert them. + uint32_t leds = *g_outputs; + + *g_outputs = (~leds) & ((0xF << RED_LEDS_OFFSET) | + (0xF << GREEN_LEDS_OFFSET) | + (0xF << BLUE_LEDS_OFFSET)); + + + // Re-enable the timer interrupt. + set_csr(mie, MIP_MTIP); + +} + + +/*Entry Point for Machine Timer Interrupt Handler*/ +void handle_m_timer_interrupt(){ + g_m_timer_interrupt_handler(); +} + +const char * instructions_msg = " \ +\n\ + SIFIVE, INC.\n\ +\n\ + 5555555555555555555555555\n\ + 5555 5555\n\ + 5555 5555\n\ + 5555 5555\n\ + 5555 5555555555555555555555\n\ + 5555 555555555555555555555555\n\ + 5555 5555\n\ + 5555 5555\n\ + 5555 5555\n\ +5555555555555555555555555555 55555\n\ + 55555 555555555 55555\n\ + 55555 55555 55555\n\ + 55555 5 55555\n\ + 55555 55555\n\ + 55555 55555\n\ + 55555 55555\n\ + 55555 55555\n\ + 55555 55555\n\ + 555555555\n\ + 55555\n\ + 5\n\ +\n\ + SiFive E-Series Coreplex Demo on Arty Board\n \ + \n\ + "; + +void print_instructions() { + + write (STDOUT_FILENO, instructions_msg, strlen(instructions_msg)); + +} + +void button_0_handler(void) { + + // Rainbow LEDs! + * g_outputs = (0x9 << RED_LEDS_OFFSET) | (0xA << GREEN_LEDS_OFFSET) | (0xC << BLUE_LEDS_OFFSET); + +}; + + +void reset_demo (){ + + // Disable the machine & timer interrupts until setup is done. + + clear_csr(mie, MIP_MEIP); + clear_csr(mie, MIP_MTIP); + + g_ext_interrupt_handlers[INT_DEVICE_BUTTON_0] = button_0_handler; + g_ext_interrupt_handlers[INT_DEVICE_BUTTON_1] = no_interrupt_handler; + g_ext_interrupt_handlers[INT_DEVICE_BUTTON_2] = no_interrupt_handler; + g_ext_interrupt_handlers[INT_DEVICE_BUTTON_3] = no_interrupt_handler; + g_ext_interrupt_handlers[INT_DEVICE_JA_7] = no_interrupt_handler; + g_ext_interrupt_handlers[INT_DEVICE_JA_8] = no_interrupt_handler; + g_ext_interrupt_handlers[INT_DEVICE_JA_9] = no_interrupt_handler; + g_ext_interrupt_handlers[INT_DEVICE_JA_10] = no_interrupt_handler; + + print_instructions(); + + PLIC_enable_interrupt (&g_plic, INT_DEVICE_BUTTON_0); + + // Set the machine timer to go off in 3 seconds. + // The + volatile uint64_t * mtime = (uint64_t*) MTIME_ADDR; + volatile uint64_t * mtimecmp = (uint64_t*) MTIMECMP_BASE_ADDR; + uint64_t now = *mtime; + uint64_t then = now + 3*CLOCK_FREQUENCY / RTC_PRESCALER; + *mtimecmp = then; + + // Enable the Machine-External bit in MIE + set_csr(mie, MIP_MEIP); + + // Enable the Machine-Timer bit in MIE + set_csr(mie, MIP_MTIP); + + // Enable interrupts in general. + set_csr(mstatus, MSTATUS_MIE); + +} + +int main(int argc, char **argv) +{ + // Set up the GPIOs such that the inputs' tristate are set. + // The boot ROM actually does this, but still good. + + * g_tristates = (0xF << BUTTONS_OFFSET) | (0xF << SWITCHES_OFFSET) | (0xF << JA_IN_OFFSET); + + * g_outputs = (0xF << RED_LEDS_OFFSET); + + + /************************************************************************** + * Set up the PLIC + * + *************************************************************************/ + PLIC_init(&g_plic, PLIC_BASE_ADDR, PLIC_NUM_SOURCES, PLIC_NUM_PRIORITIES); + + reset_demo(); + + while (1); + + return 0; + +} diff --git a/software/shared/Makefile.shared b/software/shared/Makefile.shared new file mode 100644 index 0000000..5817826 --- /dev/null +++ b/software/shared/Makefile.shared @@ -0,0 +1,47 @@ +# See LICENSE for license details. + +SHARED_DIR = $(SWDIR)/shared + +ASM_SRCS += $(SHARED_DIR)/entry.S + +C_SRCS += $(SHARED_DIR)/init.c +C_SRCS += $(SHARED_DIR)/syscall.c +C_SRCS += $(SHARED_DIR)/drivers_sifive/plic.c + +LINKER_SCRIPT := $(SHARED_DIR)/link.lds +HEADERS += $(SHARED_DIR)/*.h + +C_SRCS += $(TARGET).c + +INCLUDES += -I . +INCLUDES += -I $(SHARED_DIR) +INCLUDES += -I $(SWDIR)/../riscv-tests/env +INCLUDES += -I$(SHARED_DIR)/drivers_sifive/ + +CC = $(SWDIR)/../toolchain/bin/riscv32-unknown-elf-gcc + +CFLAGS += $(CDEFINES) + +LDFLAGS := -T $(LINKER_SCRIPT) -nostdlib -nostartfiles -lc -lgcc +LDFLAGS += -L$(SHARED_DIR) +LDFLAGS += -L$(SHARED_DIR)/drivers +LDFLAGS += -L$(SHARED_DIR)/hal + +ASM_OBJS := $(patsubst %.S,%.o,$(ASM_SRCS)) +C_OBJS := $(patsubst %.c,%.o,$(C_SRCS)) + +$(TARGET): $(ASM_OBJS) $(C_OBJS) $(LINKER_SCRIPT) + $(CC) $(CFLAGS) $(INCLUDES) $(C_OBJS) $(ASM_OBJS) -o $@ $(LDFLAGS) + +$(ASM_OBJS): %.o: %.S $(HEADERS) + $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< + +$(C_OBJS): %.o: %.c $(HEADERS) + $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< + +all: default + +clean: + rm -f $(TARGET) *.o $(SHARED_DIR)/*.o $(SHARED_DIR)/drivers/*/*.o $(SHARED_DIR)/drivers_sifive/*.o + +.PHONY: clean all default diff --git a/software/shared/drivers_sifive/plic.c b/software/shared/drivers_sifive/plic.c new file mode 100644 index 0000000..ec0c178 --- /dev/null +++ b/software/shared/drivers_sifive/plic.c @@ -0,0 +1,123 @@ +// See LICENSE for license details. + +#include "plic.h" +#include "encoding.h" +#include + + +// Note that there are no assertions or bounds checking on these +// parameter values. + +void volatile_memzero(uint8_t * base, unsigned int size){ + + volatile uint8_t * ptr; + for (ptr = base; ptr < (base + size); ptr++){ + *ptr = 0; + } +} + +void PLIC_init ( + plic_instance_t * this_plic, + uintptr_t base_addr, + uint32_t num_sources, + uint32_t num_priorities + ){ + + this_plic->base_addr = base_addr; + this_plic->num_sources = num_sources; + this_plic->num_priorities = num_priorities; + + // Disable all interrupts (don't assume that these registers are reset). + unsigned long hart_id = read_csr(mhartid); + volatile_memzero((uint8_t*) (this_plic->base_addr + + PLIC_ENABLE_OFFSET + + (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET)), + (num_sources + 8) / 8); + + // Set all priorities to 0 (equal priority -- don't assume that these are reset). + volatile_memzero ((uint8_t *)(this_plic->base_addr + + PLIC_PRIORITY_OFFSET), + (num_sources + 1) << PLIC_PRIORITY_SHIFT_PER_SOURCE); + + // Set the threshold to 0. + volatile plic_threshold* threshold = (plic_threshold*) + (this_plic->base_addr + + PLIC_THRESHOLD_OFFSET + + (hart_id << PLIC_THRESHOLD_SHIFT_PER_TARGET)); + + *threshold = 0; + +} + +void PLIC_set_threshold (plic_instance_t * this_plic, plic_threshold threshold){ + + unsigned long hart_id = read_csr(mhartid); + volatile plic_threshold* threshold_ptr = (plic_threshold*) (this_plic->base_addr + + PLIC_THRESHOLD_OFFSET + + (hart_id << PLIC_THRESHOLD_SHIFT_PER_TARGET)); + + *threshold_ptr = threshold; + +} + + +void PLIC_enable_interrupt (plic_instance_t * this_plic, plic_source source){ + + unsigned long hart_id = read_csr(mhartid); + volatile uint8_t * current_ptr = (volatile uint8_t *)(this_plic->base_addr + + PLIC_ENABLE_OFFSET + + (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET) + + (source >> 3)); + uint8_t current = *current_ptr; + current = current | ( 1 << (source & 0x7)); + *current_ptr = current; + +} + +void PLIC_disable_interrupt (plic_instance_t * this_plic, plic_source source){ + + unsigned long hart_id = read_csr(mhartid); + volatile uint8_t * current_ptr = (volatile uint8_t *) (this_plic->base_addr + + PLIC_ENABLE_OFFSET + + (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET) + + (source >> 3)); + uint8_t current = *current_ptr; + current = current & ~(( 1 << (source & 0x7))); + *current_ptr = current; + +} + +void PLIC_set_priority (plic_instance_t * this_plic, plic_source source, plic_priority priority){ + + if (this_plic->num_priorities > 0) { + volatile plic_priority * priority_ptr = (volatile plic_priority *) + (this_plic->base_addr + + PLIC_PRIORITY_OFFSET + + (source << PLIC_PRIORITY_SHIFT_PER_SOURCE)); + *priority_ptr = priority; + } +} + +plic_source PLIC_claim_interrupt(plic_instance_t * this_plic){ + + unsigned long hart_id = read_csr(mhartid); + + volatile plic_source * claim_addr = (volatile plic_source * ) + (this_plic->base_addr + + PLIC_CLAIM_OFFSET + + (hart_id << PLIC_CLAIM_SHIFT_PER_TARGET)); + + return *claim_addr; + +} + +void PLIC_complete_interrupt(plic_instance_t * this_plic, plic_source source){ + + unsigned long hart_id = read_csr(mhartid); + volatile plic_source * claim_addr = (volatile plic_source *) (this_plic->base_addr + + PLIC_CLAIM_OFFSET + + (hart_id << PLIC_CLAIM_SHIFT_PER_TARGET)); + *claim_addr = source; + +} + diff --git a/software/shared/drivers_sifive/plic.h b/software/shared/drivers_sifive/plic.h new file mode 100644 index 0000000..14118a5 --- /dev/null +++ b/software/shared/drivers_sifive/plic.h @@ -0,0 +1,70 @@ +// See LICENSE for license details. + +#ifndef PLIC_H +#define PLIC_H + +#include +#include +#include +#include + + +#include "encoding.h" + +// 32 bits per source +#define PLIC_PRIORITY_OFFSET 0x0000UL +#define PLIC_PRIORITY_SHIFT_PER_SOURCE 2 +// 1 bit per source (1 address) +#define PLIC_PENDING_OFFSET 0x1000UL +#define PLIC_PENDING_SHIFT_PER_SOURCE 0 + +//0x80 per target +#define PLIC_ENABLE_OFFSET 0x2000UL +#define PLIC_ENABLE_SHIFT_PER_TARGET 7 + + +#define PLIC_THRESHOLD_OFFSET 0x200000UL +#define PLIC_CLAIM_OFFSET 0x200004UL +#define PLIC_THRESHOLD_SHIFT_PER_TARGET 12 +#define PLIC_CLAIM_SHIFT_PER_TARGET 12 + +#define PLIC_MAX_SOURCE 1023 +#define PLIC_SOURCE_MASK 0x3FF + +#define PLIC_MAX_TARGET 15871 +#define PLIC_TARGET_MASK 0x3FFF + + +typedef struct __plic_instance_t +{ + uintptr_t base_addr; + + uint32_t num_sources; + uint32_t num_priorities; + +} plic_instance_t; + +typedef uint32_t plic_source; +typedef uint32_t plic_priority; +typedef uint32_t plic_threshold; + +void PLIC_init ( + plic_instance_t * this_plic, + uintptr_t base_addr, + uint32_t num_sources, + uint32_t num_priorities + ); + +void PLIC_enable_interrupt (plic_instance_t * this_plic, plic_source source); + +void PLIC_disable_interrupt (plic_instance_t * this_plic, plic_source source); + +void PLIC_set_priority (plic_instance_t * this_plic, plic_source source, plic_priority priority); + +plic_source PLIC_claim_interrupt(plic_instance_t * this_plic); + +void PLIC_complete_interrupt(plic_instance_t * this_plic, plic_source source); + +void PLIC_set_threshold(plic_instance_t * this_plic, plic_threshold threshold); + +#endif diff --git a/software/shared/entry.S b/software/shared/entry.S new file mode 120000 index 0000000..11f412b --- /dev/null +++ b/software/shared/entry.S @@ -0,0 +1 @@ +../../riscv-tests/debug/programs/entry.S \ No newline at end of file diff --git a/software/shared/gpio.h b/software/shared/gpio.h new file mode 100644 index 0000000..ed48255 --- /dev/null +++ b/software/shared/gpio.h @@ -0,0 +1,10 @@ +// See LICENSE for license details. + +#ifndef SIFIVE_GPIO_H +#define SIFIVE_GPIO_H + +#define GPIO_IN_OFFSET 0 +#define GPIO_OUT_OFFSET 0 +#define GPIO_TRI_OFFSET 4 + +#endif diff --git a/software/shared/init.c b/software/shared/init.c new file mode 100644 index 0000000..ab790b2 --- /dev/null +++ b/software/shared/init.c @@ -0,0 +1,48 @@ +// See LICENSE for license details. + +#include +#include +#include +#include + +#include "encoding.h" +#include "shared.h" + +int main(void); + +#ifdef USE_PLIC +extern void handle_m_ext_interrupt(); +#endif + + +#ifdef USE_M_TIME +extern void handle_m_time_interrupt(); +#endif + + +void _init(void) +{ + + exit(main()); +} + +uintptr_t handle_trap(uintptr_t mcause, uintptr_t epc) +{ + if (0){ +#ifdef USE_PLIC + // External Machine-Level Interrupt from PLIC + }else if ((mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == IRQ_M_EXT)) { + handle_m_ext_interrupt(); +#endif +#ifdef USE_M_TIME + // External Machine-Level Interrupt from PLIC + }else if ((mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == IRQ_M_TIMER)) { + handle_m_time_interrupt(); +#endif + } + else{ + write(1, "trap\n", 5); + _exit(1 + mcause); + } + return epc; +} diff --git a/software/shared/link.lds b/software/shared/link.lds new file mode 120000 index 0000000..013b6cb --- /dev/null +++ b/software/shared/link.lds @@ -0,0 +1 @@ +../../riscv-tests/debug/targets/m2gl_m2s/link.lds \ No newline at end of file diff --git a/software/shared/shared.h b/software/shared/shared.h new file mode 100644 index 0000000..e8f3bf4 --- /dev/null +++ b/software/shared/shared.h @@ -0,0 +1,73 @@ +// See LICENSE for license details. + +#ifndef SIFIVE_SHARED_H +#define SIFIVE_SHARED_H + +#include "uart.h" +#include "gpio.h" +#include "plic.h" + +// Some things missing from the official encoding.h +#define MCAUSE_INT 0x80000000 +#define MCAUSE_CAUSE 0x7FFFFFFF + +/**************************************************************************** + * Platform definitions + *****************************************************************************/ + +#define MTIMECMP_BASE_ADDR 0x44004000UL +#define MTIME_ADDR 0x4400BFF8UL +#define PLIC_BASE_ADDR 0x40000000UL +#define UART_BASE_ADDR 0x48000000UL +#define GPIO_BASE_ADDR 0x48002000UL +#define SPI_BASE_ADDR 0x48001000UL + + +/**************************************************************************** + * Clock Parameters + *****************************************************************************/ + +#define RTC_PRESCALER 100 +#define CLOCK_FREQUENCY 62500000 + +/**************************************************************************** + * GPIO Connections + *****************************************************************************/ + + +// Each of these OFFSETS holds 4 bits. + +#define RED_LEDS_OFFSET 0 +#define GREEN_LEDS_OFFSET 4 +#define BLUE_LEDS_OFFSET 8 +#define JA_OUT_OFFSET 12 // JA Pins 1-4 are outputs. + +#define BUTTONS_OFFSET 16 +#define SWITCHES_OFFSET 20 +#define JA_IN_OFFSET 24 // JA Pins 7-10 are inputs. +// reserved input offset 38 // Tied to zero. + +/**************************************************************************** + * External Interrupts handled by PLIC. + *****************************************************************************/ + +// Interrupt devices +#define INT_DEVICE_BUTTON_0 1 +#define INT_DEVICE_BUTTON_1 2 +#define INT_DEVICE_BUTTON_2 3 +#define INT_DEVICE_BUTTON_3 4 + +#define INT_DEVICE_JA_7 5 +#define INT_DEVICE_JA_8 6 +#define INT_DEVICE_JA_9 7 +#define INT_DEVICE_JA_10 8 + + +// Setting these correctly makes the initialization scripts +// run faster. +#define PLIC_NUM_SOURCES 31 +#define PLIC_NUM_PRIORITIES 0 + +void write_hex(int fd, uint32_t hex); + +#endif diff --git a/software/shared/spi.h b/software/shared/spi.h new file mode 100644 index 0000000..9c974c1 --- /dev/null +++ b/software/shared/spi.h @@ -0,0 +1,8 @@ +// See LICENSE for license details. + +#ifndef SIFIVE_SPI_H +#define SIFIVE_SPI_H + +//TODO + +#endif diff --git a/software/shared/syscall.c b/software/shared/syscall.c new file mode 100644 index 0000000..2e4199f --- /dev/null +++ b/software/shared/syscall.c @@ -0,0 +1,206 @@ +// See LICENSE for license details. + +/* This is an incomplete version of a syscall library, + * which really only supports simple reads and writes over UART. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "encoding.h" + +#include "shared.h" + +volatile uint64_t tohost __attribute__((aligned(64))); +volatile uint64_t fromhost __attribute__((aligned(64))); + +void write_hex(int fd, uint32_t hex){ + uint8_t ii; + uint8_t jj; + char towrite; + write( fd , "0x", 2 ); + for (ii = 8 ; ii > 0; ii--){ + jj = ii-1; + uint8_t digit = ((hex & (0xF << (jj*4))) >> (jj*4)); + towrite = digit < 0xA ? ('0' + digit) : ('A' + (digit - 0xA)); + write( fd, &towrite, 1); + } + +} + + +void _exit(int code) +{ + volatile uint32_t* leds = (uint32_t*) (GPIO_BASE_ADDR + GPIO_OUT_OFFSET); + const char * message = "\nProgam has exited with code:"; + + *leds = (~(code)); + + write(STDERR_FILENO, message, strlen(message)); + write_hex(STDERR_FILENO, code); + + while (1); + +} + +void *sbrk(ptrdiff_t incr) +{ + // extern char _end[]; + // extern char _heap_end[]; + // static char *curbrk = _end; + + // if ((curbrk + incr < _end) || (curbrk + incr > _heap_end)) + return NULL - 1; + + // curbrk += incr; + //return curbrk - incr; +} + +static int stub(int err) +{ + errno = err; + return -1; +} + +int open(const char* name, int flags, int mode) +{ + return stub(ENOENT); +} + +int openat(int dirfd, const char* name, int flags, int mode) +{ + return stub(ENOENT); +} + +int close(int fd) +{ + return stub(EBADF); +} + +int execve(const char* name, char* const argv[], char* const env[]) +{ + return stub(ENOMEM); +} + +int fork() +{ + return stub(EAGAIN); +} + +int fstat(int fd, struct stat *st) +{ + if (isatty(fd)) { + st->st_mode = S_IFCHR; + return 0; + } + + return stub(EBADF); +} + +int getpid() +{ + return 1; +} + +int isatty(int fd) +{ + if (fd == STDOUT_FILENO || fd == STDERR_FILENO) + return 1; + + errno = EBADF; + return 0; +} + +int kill(int pid, int sig) +{ + return stub(EINVAL); +} + +int link(const char *old_name, const char *new_name) +{ + return stub(EMLINK); +} + +off_t lseek(int fd, off_t ptr, int dir) +{ + if (isatty(fd)) + return 0; + + return stub(EBADF); +} + +ssize_t read(int fd, void* ptr, size_t len) +{ + int i; + uint8_t * current = (uint8_t*) ptr; + volatile uint8_t * uart_rx = (uint8_t*) (UART_BASE_ADDR + UART_RX_OFFSET); + volatile uint32_t * uart_rx_cnt = (uint32_t*) (UART_BASE_ADDR + UART_RX_COUNT_OFFSET); + + ssize_t result = 0; + + if (isatty(fd)) { + + for (current = (uint8_t*) ptr; + (current < ((uint8_t*) ptr) + len) && (*uart_rx_cnt > 0); + current ++){ + *current = *uart_rx; + result ++; + } + return result; + + } + + return stub(EBADF); +} + +int stat(const char* file, struct stat* st) +{ + return stub(EACCES); +} + +clock_t times(struct tms* buf) +{ + return stub(EACCES); +} + +int unlink(const char* name) +{ + return stub(ENOENT); +} + +int wait(int* status) +{ + return stub(ECHILD); +} + +ssize_t write(int fd, const void* ptr, size_t len) +{ + + const uint8_t * current = (const char*) ptr; + volatile uint32_t * uart_tx_count = (uint32_t*) (UART_BASE_ADDR + UART_TX_COUNT_OFFSET); + volatile uint8_t * uart_tx = (uint8_t*) (UART_BASE_ADDR + UART_TX_OFFSET); + + size_t jj; + if (isatty(fd)) { + + for (jj = 0; jj < len; jj++){ + + while ((*uart_tx_count) < 1); + *uart_tx = current[jj]; + + if (current[jj] == '\n'){ + while ((*uart_tx_count) < 1); + *uart_tx = '\r'; + } + } + return len; + } + return stub(EBADF); +} diff --git a/software/shared/uart.h b/software/shared/uart.h new file mode 100644 index 0000000..e97ef8b --- /dev/null +++ b/software/shared/uart.h @@ -0,0 +1,12 @@ +// See LICENSE for license details. + +#ifndef SIFIVE_UART_H +#define SIFIVE_UART_H + +#define UART_RX_OFFSET 0 +#define UART_TX_OFFSET 0 +#define UART_TX_COUNT_OFFSET 0x4 +#define UART_RX_COUNT_OFFSET 0x8 +#define UART_DIVIDER_OFFSET 0xC + +#endif -- cgit v1.2.1-18-gbd029