From b7467156c5692220a0c098faa04d03fce03bad33 Mon Sep 17 00:00:00 2001 From: David Harris Date: Sat, 30 Nov 2024 19:12:01 -0800 Subject: [PATCH 1/2] Hello Wally application now running, can print in spike and wsim via UART. Verilator simulation is broken --- examples/C/common/syscalls.c | 60 ++++++++++++++++++++++++++++++++++-- examples/C/hello/Makefile | 33 ++++++++++++++++++++ examples/C/hello/hello.c | 23 ++++++++++++++ 3 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 examples/C/hello/Makefile create mode 100644 examples/C/hello/hello.c diff --git a/examples/C/common/syscalls.c b/examples/C/common/syscalls.c index 39547b3d0..6f57475bc 100644 --- a/examples/C/common/syscalls.c +++ b/examples/C/common/syscalls.c @@ -15,6 +15,44 @@ extern volatile uint64_t tohost; extern volatile uint64_t fromhost; +///////////////////////////// +// Start of code added for Wally +// Use UART rather than syscall host interface for printing +////////////////////////////// + +#include + +void uartInit(void) { + volatile uint8_t *UART_LCR = (uint8_t*)0x10000003; + *UART_LCR = 0b0000011; // 8-bit characters, 1 stop bit, no parity +} + +void uartSend(char c) { + volatile uint8_t *UART_THR = (uint8_t*)0x10000000; + volatile uint8_t *UART_LSR = (uint8_t*)0x10000005; + + while (!(*UART_LSR & (1<<5))); // wait for THRE (trans hold reg empty) + *UART_THR = c; +} + +char uartReceive(void) { + volatile uint8_t *UART_RBR = (uint8_t*)0x10000000; + volatile uint8_t *UART_LSR = (uint8_t*)0x10000005; + + while (!(*UART_LSR & (1<<0))); // wait for DR (Data Ready) + return *UART_RBR; +} + +char uartPutStr(const char *str) { + while (*str) { + uartSend(*str++); + } +} + +///////////////////////////// +// End of code added for Wally +////////////////////////////// + static uintptr_t syscall(uintptr_t which, uint64_t arg0, uint64_t arg1, uint64_t arg2) { volatile uint64_t magic_mem[8] __attribute__((aligned(64))); @@ -76,7 +114,19 @@ void abort() void printstr(const char* s) { - syscall(SYS_write, 1, (uintptr_t)s, strlen(s)); + // david_harris@hmc.edu 11/30/24 switch to printing via UART rather than syscall + // This works on both Spike and Wally simulations + //syscall(SYS_write, 1, (uintptr_t)s, strlen(s)); + uartPutStr(s); +} + +// Added 30 November 2024 David_Harris@hmc.edu +// The compiler encountering printf with a pure string argument tries to invoke puts +// rather than the usual printf, so puts must be defined. +int puts(const char* s) +{ + printstr(s); + return 0; } void __attribute__((weak)) thread_entry(int cid, int nc) @@ -107,6 +157,7 @@ void _init(int cid, int nc) { init_tls(); thread_entry(cid, nc); + uartInit(); // added 11/30/2024 David_Harris@hmc.edu for printing via UART // only single-threaded programs should ever get here. int ret = main(0, 0); @@ -125,6 +176,11 @@ void _init(int cid, int nc) #undef putchar int putchar(int ch) { + // David_Harris@hmc.edu 11/30/2024 + // Replaced syscall with uartSend + uartSend(ch); + + /* static __thread char buf[64] __attribute__((aligned(64))); static __thread int buflen = 0; @@ -134,7 +190,7 @@ int putchar(int ch) { syscall(SYS_write, 1, (uintptr_t)buf, buflen); buflen = 0; - } + } */ return 0; } diff --git a/examples/C/hello/Makefile b/examples/C/hello/Makefile new file mode 100644 index 000000000..ea1ceb910 --- /dev/null +++ b/examples/C/hello/Makefile @@ -0,0 +1,33 @@ +TARGET = hello + +$(TARGET).objdump: $(TARGET) + riscv64-unknown-elf-objdump -S -D $(TARGET) > $(TARGET).objdump + cp $(TARGET) $(TARGET).elf + +$(TARGET): $(TARGET).c Makefile + riscv64-unknown-elf-gcc -o $(TARGET) -gdwarf-2 -O\ + -march=rv64gc -mabi=lp64d -mcmodel=medany \ + -nostdlib -static -lm -fno-tree-loop-distribute-patterns \ + -T../common/test.ld -I../common \ + $(TARGET).c ../common/crt.S ../common/syscalls.c +# Compiler flags: +# -o $(TARGET) defines the name of the output file +# -g generates debugging symbols for gdb +# -O turns on basic optimization; -O3 turns on heavy optimization; omit for no optimization +# -march=rv64gc -mabi=lp64d =mcmodel=medany generates code for RV64GC with doubles and long/ptrs = 64 bits +# -static forces static linking (no dynamic shared libraries on bare metal) +# -lm links the math library if necessary (when #include math.h) +# -nostdlib avoids inserting standard startup files and default libraries +# because we are using crt.s on bare metal +# -fno-tree-loop-distribute-patterns turns replacing loops with memcpy/memset in the std library +# -T specifies the linker file +# -I specifies the include path (e.g. for util.h) +# The last line defines the C files to compile. +# crt.S is needed as our startup file to initialize the processor +# syscalls.c implements printf through the HTIF for Spike +# other flags from riscv-tests makefiles that don't seem to be important +# -ffast-math -DPREALLOCATE=1 -std=gnu99 \ +# -fno-common -fno-builtin-printf -nostartfiles -lgcc \ + +clean: + rm -f $(TARGET) $(TARGET).objdump diff --git a/examples/C/hello/hello.c b/examples/C/hello/hello.c new file mode 100644 index 000000000..4e1215cfb --- /dev/null +++ b/examples/C/hello/hello.c @@ -0,0 +1,23 @@ +// hello.c +// David_Harris@hmc.edu 30 November 2024 + +// Hello World program illustrating compiled C code printing via the UART +// The Wally team has modified the Berkeley syscalls.c (in examples/C/common) +// to print via UART rather than the syscall interface. This is supported +// on both Spike and Wally. It assumes the PC16550-compatible UART is at +// the default SiFive address of 0x10000000. +// Note that there seem to be some discrepancies between the UART and Spike +// such that using \n\r for new lines works best. + +// compile with make +// simulate with: wsim rv64gc hello.elf --sim verilator + +#include + +int main(void) { + printf("Hello Wally!\n\r"); + for (int i = 0; i < 10; i++) { + printf("%d ", i); + } + printf("\n\r"); +} \ No newline at end of file From a4301babff29b2c3fc12106d70d764b10314b890 Mon Sep 17 00:00:00 2001 From: David Harris Date: Sat, 30 Nov 2024 19:41:13 -0800 Subject: [PATCH 2/2] GPIO example --- .gitignore | 2 + examples/C/gpio/Makefile | 33 ++++++++++++++++ examples/C/gpio/gpio.c | 28 ++++++++++++++ examples/C/gpio/gpiolib.h | 81 +++++++++++++++++++++++++++++++++++++++ examples/C/hello/hello.c | 4 +- 5 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 examples/C/gpio/Makefile create mode 100644 examples/C/gpio/gpio.c create mode 100644 examples/C/gpio/gpiolib.h diff --git a/.gitignore b/.gitignore index 64fbbbf23..9a0f35cc1 100644 --- a/.gitignore +++ b/.gitignore @@ -153,6 +153,8 @@ examples/C/mcmodel/mcmodel_medany examples/C/mcmodel/mcmodel_medlow examples/C/sum/sum examples/C/sum_mixed/sum_mixed +examples/C/hello/hello +examples/C/gpio/gpio examples/asm/sumtest/sumtest examples/asm/example/example examples/asm/trap/trap diff --git a/examples/C/gpio/Makefile b/examples/C/gpio/Makefile new file mode 100644 index 000000000..c33425ac7 --- /dev/null +++ b/examples/C/gpio/Makefile @@ -0,0 +1,33 @@ +TARGET = gpio + +$(TARGET).objdump: $(TARGET) + riscv64-unknown-elf-objdump -S -D $(TARGET) > $(TARGET).objdump + cp $(TARGET) $(TARGET).elf + +$(TARGET): $(TARGET).c Makefile + riscv64-unknown-elf-gcc -o $(TARGET) -gdwarf-2 -O\ + -march=rv64gc -mabi=lp64d -mcmodel=medany \ + -nostdlib -static -lm -fno-tree-loop-distribute-patterns \ + -T../common/test.ld -I../common \ + $(TARGET).c ../common/crt.S ../common/syscalls.c +# Compiler flags: +# -o $(TARGET) defines the name of the output file +# -g generates debugging symbols for gdb +# -O turns on basic optimization; -O3 turns on heavy optimization; omit for no optimization +# -march=rv64gc -mabi=lp64d =mcmodel=medany generates code for RV64GC with doubles and long/ptrs = 64 bits +# -static forces static linking (no dynamic shared libraries on bare metal) +# -lm links the math library if necessary (when #include math.h) +# -nostdlib avoids inserting standard startup files and default libraries +# because we are using crt.s on bare metal +# -fno-tree-loop-distribute-patterns turns replacing loops with memcpy/memset in the std library +# -T specifies the linker file +# -I specifies the include path (e.g. for util.h) +# The last line defines the C files to compile. +# crt.S is needed as our startup file to initialize the processor +# syscalls.c implements printf through the HTIF for Spike +# other flags from riscv-tests makefiles that don't seem to be important +# -ffast-math -DPREALLOCATE=1 -std=gnu99 \ +# -fno-common -fno-builtin-printf -nostartfiles -lgcc \ + +clean: + rm -f $(TARGET) $(TARGET).objdump diff --git a/examples/C/gpio/gpio.c b/examples/C/gpio/gpio.c new file mode 100644 index 000000000..64f3b5a08 --- /dev/null +++ b/examples/C/gpio/gpio.c @@ -0,0 +1,28 @@ +// gpio.c +// David_Harris@hmc.edu 30 November 2024 +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +// General-Purpose I/O (GPIO) example program illustrating compiled C code +// compile with make +// simulate with: wsim rv64gc hello.elf --sim verilator + +#include +#include "gpiolib.h" + +int main(void) { + printf("GPIO Example!\n\r"); + pinMode(0, INPUT); + pinMode(1, OUTPUT); + pinMode(2, OUTPUT); + + for (int i=0; i<10; i++) { + // Read pin 0 and write it to pin 1 + int val = digitalRead(0); + printf("Pin 0: %d\n", val); + digitalWrite(1, val); + + // Toggle pin 2 + printf("Pin 2: %d\n", i%2); + digitalWrite(2, i%2); + } +} diff --git a/examples/C/gpio/gpiolib.h b/examples/C/gpio/gpiolib.h new file mode 100644 index 000000000..819089554 --- /dev/null +++ b/examples/C/gpio/gpiolib.h @@ -0,0 +1,81 @@ +// gpiolib.h +// Basic Arduino-compatible functions for general-purpose input/output + +// Assumes GPIO0_BASE is set to the memory-mapped GPIO address from +// config/rv64gc/config.vh. + +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +#include + +/////////////////////////////////////////////////////////////////////////////// +// Bitfield Structs +/////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + volatile uint32_t input_val; // (GPIO offset 0x00) Pin value + volatile uint32_t input_en; // (GPIO offset 0x04) Pin input enable* + volatile uint32_t output_en; // (GPIO offset 0x08) Pin output enable* + volatile uint32_t output_val; // (GPIO offset 0x0C) Output value + volatile uint32_t pue; // (GPIO offset 0x10) Internal pull-up enable* + volatile uint32_t ds; // (GPIO offset 0x14) Pin drive strength + volatile uint32_t rise_ie; // (GPIO offset 0x18) Rise interrupt enable + volatile uint32_t rise_ip; // (GPIO offset 0x1C) Rise interrupt pending + volatile uint32_t fall_ie; // (GPIO offset 0x20) Fall interrupt enable + volatile uint32_t fall_ip; // (GPIO offset 0x24) Fall interrupt pending + volatile uint32_t high_ie; // (GPIO offset 0x28) High interrupt enable + volatile uint32_t high_ip; // (GPIO offset 0x2C) High interrupt pending + volatile uint32_t low_ie; // (GPIO offset 0x30) Low interrupt enable + volatile uint32_t low_ip; // (GPIO offset 0x34) Low interrupt pending + volatile uint32_t iof_en; // (GPIO offset 0x38) HW-Driven functions enable + volatile uint32_t iof_sel; // (GPIO offset 0x3C) HW-Driven functions selection + volatile uint32_t out_xor; // (GPIO offset 0x40) Output XOR (invert) + // Registers marked with * are asynchronously reset to 0. All others are synchronously reset to 0. +} GPIO; + +/////////////////////////////////////////////////////////////////////////////// +// GPIO Constant Definitions +/////////////////////////////////////////////////////////////////////////////// + +#define GPIO0_BASE (0x10060000U) // GPIO memory-mapped base address + +#define GPIO0 ((GPIO*) GPIO0_BASE) // Set up pointer to struct of type GPIO aligned at the base GPIO0 memory-mapped address + +#define LOW 0 +#define HIGH 1 + +#define INPUT 0 +#define OUTPUT 1 +#define GPIO_IOF0 2 + +/////////////////////////////////////////////////////////////////////////////// +// GPIO Functions +/////////////////////////////////////////////////////////////////////////////// + +void pinMode(int pin, int function) +{ + switch(function) { + case INPUT: + GPIO0->input_en |= (1 << pin); // Sets a pin as an input + break; + case OUTPUT: + GPIO0->output_en |= (1 << pin); // Set pin as an output + GPIO0->iof_en &= ~(1 << pin); + break; + case GPIO_IOF0: + GPIO0->iof_sel &= ~(1 << pin); + GPIO0->iof_en |= (1 << pin); + } +} + +void digitalWrite(int pin, int val) +{ + if (val) GPIO0->output_val |= (1 << pin); + else GPIO0->output_val &= ~(1 << pin); +} + +int digitalRead(int pin) +{ + return (GPIO0->input_val >> pin) & 0x1; +} diff --git a/examples/C/hello/hello.c b/examples/C/hello/hello.c index 4e1215cfb..2a6cfa033 100644 --- a/examples/C/hello/hello.c +++ b/examples/C/hello/hello.c @@ -1,5 +1,6 @@ // hello.c // David_Harris@hmc.edu 30 November 2024 +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 // Hello World program illustrating compiled C code printing via the UART // The Wally team has modified the Berkeley syscalls.c (in examples/C/common) @@ -10,7 +11,8 @@ // such that using \n\r for new lines works best. // compile with make -// simulate with: wsim rv64gc hello.elf --sim verilator +// simulate Wally with: wsim rv64gc hello.elf --sim verilator +// simulate in Spike with: spike hello.elf #include