├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── env └── qemu-sifive_u │ ├── crt.s │ └── default.lds ├── examples └── hello │ ├── hello.c │ └── rules.mk └── libfemto ├── arch └── riscv │ └── start.c ├── include └── stdio.h └── std └── putchar.c /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, The Regents of the University of California (Regents). 2 | Copyright (c) 2018-2019, The libfemto authors 3 | All Rights Reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. Neither the name of the Regents nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 17 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 18 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS 19 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | 21 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED 24 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE 25 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CROSS_COMPILE ?= riscv64-unknown-elf- 2 | 3 | AR = $(CROSS_COMPILE)ar 4 | 5 | CFLAGS = -mcmodel=medany -ffunction-sections -fdata-sections 6 | LDFLAGS = -nostartfiles -nostdlib -nostdinc -static -lgcc \ 7 | -Wl,--nmagic -Wl,--gc-sections 8 | INCLUDES = -Ienv/common 9 | 10 | libfemto_dirs = libfemto/std libfemto/drivers libfemto/arch/riscv 11 | libfemto_src = $(sort $(foreach d,$(libfemto_dirs),$(wildcard $(d)/*.c))) 12 | libfemto_asm = $(sort $(foreach d,$(libfemto_dirs),$(wildcard $(d)/*.s))) 13 | libfemto_objs = $(patsubst %.s,%.o,$(libfemto_asm)) \ 14 | $(patsubst %.c,%.o,$(libfemto_src)) 15 | 16 | # 17 | # Compiler configurations and target environment definitions 18 | # 19 | 20 | subdirs = examples 21 | 22 | libs = libfemto 23 | 24 | configs = rv64imac 25 | 26 | CC_rv32imac = $(CROSS_COMPILE)gcc 27 | CFLAGS_rv32imac = -Os -march=rv32imac -mabi=ilp32 -Ienv/common/rv32 28 | LDFLAGS_rv32imac = 29 | 30 | CC_rv64imac = $(CROSS_COMPILE)gcc 31 | CFLAGS_rv64imac = -Os -march=rv64imac -mabi=lp64 -Ienv/common/rv64 32 | LDFLAGS_rv64imac = 33 | 34 | targets = rv64imac:qemu-sifive_u 35 | 36 | # 37 | # make rules 38 | # 39 | 40 | all: all_programs 41 | 42 | clean: 43 | rm -fr build 44 | 45 | backup: clean 46 | tar czf ../$(shell basename $(shell pwd)).tar.gz . 47 | 48 | # 49 | # To view commands use: make V=1 50 | # 51 | 52 | ifdef V 53 | cmd = @mkdir -p $2 ; echo "$3"; $3 54 | else 55 | cmd = @echo "$1"; mkdir -p $2 ; $3 56 | endif 57 | 58 | # 59 | # Build system functions to generate pattern rules for all configs 60 | # 61 | 62 | define pattern = 63 | build/obj/$(2)/%.o: %.$(3) 64 | $(call cmd,$(1).$(2) $$@,$$(@D),$(CC_$(2)) $(CFLAGS_$(2)) $(CFLAGS) \ 65 | $$(INCLUDES) -c $$^ -o $$@) 66 | endef 67 | 68 | $(foreach c,$(configs),$(eval $(call pattern,CC,$(c),c))) 69 | $(foreach c,$(configs),$(eval $(call pattern,AS,$(c),s))) 70 | 71 | # 72 | # Build system functions to generate library rules for all configs 73 | # 74 | 75 | define archive = 76 | build/lib/$(2)/$(3).a: $(addprefix build/obj/$(2)/,$($(3)_objs)) 77 | $(call cmd,$(1).$(2) $$@,$$(@D),$(AR) cr $$@ $$^) 78 | LIBS_$(2) += build/lib/$(2)/$(3).a 79 | endef 80 | 81 | define lib = 82 | $(foreach c,$(configs),$(eval $(call archive,AR,$(c),$(1)))) 83 | INCLUDES += -I$(1)/include 84 | endef 85 | 86 | $(foreach l,$(libs),$(eval $(call lib,$(l)))) 87 | 88 | # 89 | # Build system functions to generate build rules for all subdirs 90 | # 91 | 92 | sub_makes := $(foreach dir,$(subdirs),$(wildcard ${dir}/*/rules.mk)) 93 | $(foreach makefile,$(sub_makes),$(eval include $(makefile))) 94 | sub_dirs := $(foreach m,$(sub_makes),$(m:/rules.mk=)) 95 | module_name = $(lastword $(subst /, ,$(1))) 96 | module_objs = $(addprefix build/obj/$(3)/,$(addprefix $(2)/,$($(1)_objs))) 97 | config_arch = $(word 1,$(subst :, ,$(1))) 98 | config_env = $(word 2,$(subst :, ,$(1))) 99 | 100 | define rule = 101 | build/bin/$(3)/$(4)/$(1): \ 102 | build/obj/$(3)/env/$(4)/crt.o $(2) $$(LIBS_$(3)) 103 | $$(call cmd,LD.$(3) $$@,$$(@D),$(CC_$(3)) $(CFLAGS_$(3)) $$(CFLAGS) \ 104 | $$(LDFLAGS_$(3)) $$(LDFLAGS) -T env/$(4)/default.lds $$^ -o $$@) 105 | endef 106 | 107 | define module = 108 | program_names += $(foreach cfg,$(targets),build/bin/$(call \ 109 | config_arch,$(cfg))/$(call config_env,$(cfg))/$(1)) 110 | 111 | $(foreach cfg,$(targets),$(eval $(call rule,$(1),$(call \ 112 | module_objs,$(1),$(2),$(call config_arch,$(cfg))),$(call \ 113 | config_arch,$(cfg)),$(call config_env,$(cfg))))) 114 | endef 115 | 116 | $(foreach d,$(sub_dirs),$(eval $(call module,$(call module_name,$(d)),$(d)))) 117 | 118 | all_programs: $(program_names) 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bare metal RISC-V hello world in C 2 | 3 | This is a copy of the 4 | [riscv-probe](https://github.com/michaeljclark/riscv-probe) repository stripped 5 | from everything but the 64-bit hello world program for the `sifive_u` QEMU 6 | machine. 7 | 8 | The directory structure is kept. This makes this repository a good starting 9 | point to dive into riscv-probe. In particular we keep the `crt.s` and `start.c` 10 | files although they are almost empty. They are used in the original repository 11 | to initialize libfemto. Here libfemto just provides a `puts` function. 12 | 13 | Having such a stripped down hello world program written in C was necessary for 14 | me to learn how to create a [bare metal RISC-V assembly hello 15 | world](https://github.com/noteed/riscv-hello-asm) version because I couldn't 16 | find a complete example program in assembly only that was writing to the UART. 17 | 18 | 19 | ## Building 20 | 21 | Assuming the default prefix for the GNU toolchain is correct, and the toolchain 22 | is in the `$PATH`, running `make` produce our `hello` program. Refer to the 23 | original repository otherwise. 24 | 25 | ``` 26 | $ make 27 | AS.rv64imac build/obj/rv64imac/env/qemu-sifive_u/crt.o 28 | CC.rv64imac build/obj/rv64imac/examples/hello/hello.o 29 | CC.rv64imac build/obj/rv64imac/libfemto/arch/riscv/start.o 30 | CC.rv64imac build/obj/rv64imac/libfemto/std/putchar.o 31 | AR.rv64imac build/lib/rv64imac/libfemto.a 32 | LD.rv64imac build/bin/rv64imac/qemu-sifive_u/hello 33 | ``` 34 | 35 | The result is a 64-bit RISC-V binary. 36 | 37 | ``` 38 | $ file build/bin/rv64imac/qemu-sifive_u/hello 39 | build/bin/rv64imac/qemu-sifive_u/hello: ELF 64-bit LSB executable, UCB RISC-V, 40 | version 1 (SYSV), statically linked, not stripped 41 | ``` 42 | 43 | 44 | ## Running 45 | 46 | Run it with: 47 | 48 | ``` 49 | $ qemu-system-riscv64 -nographic -machine sifive_u \ 50 | -kernel build/bin/rv64imac/qemu-sifive_u/hello 51 | Hello. 52 | QEMU: Terminated 53 | ``` 54 | 55 | Note: the program enters an infinite loop after producing the `Hello.` text. 56 | Type `ctrl-a x` to stop QEMU. 57 | 58 | 59 | ## Assembly 60 | 61 | To dissamble the program: 62 | 63 | ``` 64 | $ riscv64-unknown-elf-objdump -d build/bin/rv64imac/qemu-sifive_u/hello 65 | build/bin/rv64imac/qemu-sifive_u/hello: file format elf64-littleriscv 66 | 67 | Disassembly of section .text: 68 | 69 | 0000000080000000 <_start>: 70 | 80000000: 0040006f j 80000004
71 | 72 | 0000000080000004
: 73 | 80000004: 1141 addi sp,sp,-16 74 | 80000006: e022 sd s0,0(sp) 75 | 80000008: e406 sd ra,8(sp) 76 | 8000000a: 00000417 auipc s0,0x0 77 | 8000000e: 02e40413 addi s0,s0,46 # 80000038 78 | 80000012: 00044503 lbu a0,0(s0) 79 | 80000016: e111 bnez a0,8000001a 80 | 80000018: a001 j 80000018 81 | 8000001a: 0405 addi s0,s0,1 82 | 8000001c: 006000ef jal ra,80000022 83 | 80000020: bfcd j 80000012 84 | 85 | 0000000080000022 : 86 | 80000022: 100137b7 lui a5,0x10013 87 | 80000026: 4398 lw a4,0(a5) 88 | 80000028: 02071693 slli a3,a4,0x20 89 | 8000002c: fe06cde3 bltz a3,80000026 90 | 80000030: 0ff57513 andi a0,a0,255 91 | 80000034: c388 sw a0,0(a5) 92 | 80000036: 8082 ret 93 | ``` 94 | 95 | I used the above listing to guide modifications to the 96 | [`hello-world-abs.S`](https://github.com/rv8-io/rv8/blob/master/src/test/hello-world-abs.S) 97 | program provided in rv8 to finally create a [pure assembly hello 98 | world](https://github.com/noteed/riscv-hello-asm). 99 | -------------------------------------------------------------------------------- /env/qemu-sifive_u/crt.s: -------------------------------------------------------------------------------- 1 | # See LICENSE for license details. 2 | 3 | .section .text.init,"ax",@progbits 4 | .globl _start 5 | 6 | _start: 7 | j main 8 | -------------------------------------------------------------------------------- /env/qemu-sifive_u/default.lds: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH( "riscv" ) 2 | 3 | ENTRY( _start ) 4 | 5 | MEMORY 6 | { 7 | ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M 8 | } 9 | 10 | PHDRS 11 | { 12 | text PT_LOAD; 13 | data PT_LOAD; 14 | bss PT_LOAD; 15 | } 16 | 17 | SECTIONS 18 | { 19 | .text : { 20 | PROVIDE(_text_start = .); 21 | *(.text.init) *(.text .text.*) 22 | PROVIDE(_text_end = .); 23 | } >ram AT>ram :text 24 | 25 | .rodata : { 26 | PROVIDE(_rodata_start = .); 27 | *(.rodata .rodata.*) 28 | PROVIDE(_rodata_end = .); 29 | } >ram AT>ram :text 30 | 31 | .data : { 32 | . = ALIGN(4096); 33 | PROVIDE(_data_start = .); 34 | *(.sdata .sdata.*) *(.data .data.*) 35 | PROVIDE(_data_end = .); 36 | } >ram AT>ram :data 37 | 38 | .bss :{ 39 | PROVIDE(_bss_start = .); 40 | *(.sbss .sbss.*) *(.bss .bss.*) 41 | PROVIDE(_bss_end = .); 42 | } >ram AT>ram :bss 43 | 44 | PROVIDE(_memory_start = ORIGIN(ram)); 45 | PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram)); 46 | } 47 | -------------------------------------------------------------------------------- /examples/hello/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void main() 4 | { 5 | const char *s = "Hello.\n"; 6 | while (*s) putchar(*s++); 7 | while(1); 8 | } 9 | -------------------------------------------------------------------------------- /examples/hello/rules.mk: -------------------------------------------------------------------------------- 1 | hello_objs = hello.o 2 | -------------------------------------------------------------------------------- /libfemto/arch/riscv/start.c: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | void main(); 4 | 5 | void libfemto_start_main() 6 | { 7 | main(); 8 | } 9 | -------------------------------------------------------------------------------- /libfemto/include/stdio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | int putchar(int); 8 | 9 | #ifdef __cplusplus 10 | } 11 | #endif 12 | -------------------------------------------------------------------------------- /libfemto/std/putchar.c: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #include 4 | 5 | enum { 6 | /* UART Registers */ 7 | UART_REG_TXFIFO = 0, 8 | }; 9 | 10 | static volatile int *uart = (int *)(void *)0x10013000; 11 | 12 | int putchar(int ch) 13 | { 14 | while (uart[UART_REG_TXFIFO] < 0); 15 | return uart[UART_REG_TXFIFO] = ch & 0xff; 16 | } 17 | --------------------------------------------------------------------------------