Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Picolibc and building RISC-V applications by default #305

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 26 additions & 26 deletions Configuration.mk
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,7 @@ PACKAGE_NAME ?= $(shell basename "$(shell pwd)")
# 2. (Optional) The name to use when creating the output file.
# 3. (Optional) The address to use as the fixed start of flash.
# 4. (Optional) The address to use as the fixed start of RAM.
#
# By default we currently only build the Cortex-M targets. To enable the RISC-V
# targets, set the RISCV variable like so:
#
# $ make RISCV=1
#
# Once the RV32 toolchain distribution stabilizes (as of June 2020 the toolchain
# isn't as easily obtained as we would like), we intend to make the RISC-V
# targets build by default as well.
ifeq ($(RISCV),)
ifneq ($(NORISCV),)
TOCK_TARGETS ?= cortex-m0 cortex-m3 cortex-m4 cortex-m7
else
# Specific addresses useful for the OpenTitan hardware memory map.
Expand Down Expand Up @@ -286,28 +277,35 @@ override WLFLAGS_rv32imac += $(WLFLAGS_rv32)

# Set the system libraries we link against for RISC-V. We support C++ apps by
# default.
override LINK_LIBS_rv32 += \
-lgcc -lstdc++ -lsupc++
ifeq ($(PICOLIBC),)
override LINK_LIBS_rv32 += -lgcc -lstdc++ -lsupc++

override LINK_LIBS_rv32i += $(LINK_LIBS_rv32)
override LINK_LIBS_rv32imc += $(LINK_LIBS_rv32)
override LINK_LIBS_rv32imac += $(LINK_LIBS_rv32)
# Use precompiled libaries we provide to link against.
override LEGACY_LIBS_rv32i += \
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32i/libc.a\
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32i/libm.a

# Use precompiled libaries we provide to link against.
override LEGACY_LIBS_rv32i += \
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32i/libc.a\
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32i/libm.a
override LEGACY_LIBS_rv32im += \
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32im/libc.a\
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32im/libm.a

override LEGACY_LIBS_rv32im += \
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32im/libc.a\
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32im/libm.a
override LEGACY_LIBS_rv32imc += $(LEGACY_LIBS_rv32im)

override LEGACY_LIBS_rv32imc += $(LEGACY_LIBS_rv32im)
override LEGACY_LIBS_rv32imac += \
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32imac/libc.a\
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32imac/libm.a
else
override CFLAGS_rv32i += --specs=picolibc.specs
override CFLAGS_rv32im += --specs=picolibc.specs
override CFLAGS_rv32imc += --specs=picolibc.specs
override CFLAGS_rv32imac += --specs=picolibc.specs

override LEGACY_LIBS_rv32imac += \
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32imac/libc.a\
$(TOCK_USERLAND_BASE_DIR)/newlib/rv32/rv32imac/libm.a
override LINK_LIBS_rv32 += -lgcc -lc -lm
endif

override LINK_LIBS_rv32i += $(LINK_LIBS_rv32)
override LINK_LIBS_rv32imc += $(LINK_LIBS_rv32)
override LINK_LIBS_rv32imac += $(LINK_LIBS_rv32)

################################################################################
##
Expand Down Expand Up @@ -442,6 +440,8 @@ override CPPFLAGS += -Wwrite-strings # # { char* c = "foo"; c[0] = 'b
override CPPFLAGS_gcc += -Wlogical-op # # "suspicious use of logical operators in expressions" (a lint)
override CPPFLAGS_gcc += -Wtrampolines # # attempt to generate a trampoline on the NX stack

override CPPFLAGS_gcc += -Wno-error=sign-compare # Triggers an error inside picolibc

#CPPFLAGS += -Wabi -Wabi-tag # inter-compiler abi issues
#CPPFLAGS += -Waggregate-return # warn if things return struct's
#CPPFLAGS += -Wcast-align # { char *c; int *i = (int*) c}, 1 byte -> 4 byte align
Expand Down
228 changes: 100 additions & 128 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,161 +33,133 @@ Prerequisites
$ cd libtock-c
```

1. The main requirement to build the C applications in this repository is having
cross compilers for embedded targets. You will need an `arm-none-eabi`
toolchain for Cortex-M targets.
1. There are two requirements for building libtock-c applications.

**MacOS**:
```
$ brew tap ARMmbed/homebrew-formulae && brew update && brew install arm-none-eabi-gcc
```
1. Cross compiler for embedded targets

**Ubuntu (18.04LTS or later)**:
```
$ sudo apt install gcc-arm-none-eabi
```
**MacOS**:

**Arch**:
```
$ sudo pacman -Syu arm-none-eabi-gcc arm-none-eabi-newlib
```
```shell
$ brew tap ARMmbed/homebrew-formulae && brew update && brew install arm-none-eabi-gcc
$ brew tap riscv/riscv && brew update && brew install riscv-gnu-toolchain
```

**Fedora**:
```
$ sudo dnf install arm-none-eabi-newlib arm-none-eabi-gcc-cs
```
**Ubuntu (21.10 or later) or Debian (11 or later)**:

2. Optional: libtock-c also includes support for building for ***RISC-V
targets***. These are not included by default since obtaining the toolchain
can be difficult (as of July 2022). You will need a RISC-V toolchain that
supports rv32 targets (64 bit toolchains support rv32 if compiled with
multilib support). Some toolchains that can work:
```shell
$ sudo apt install gcc-arm-none-eabi
$ sudo apt install gcc-riscv64-unknown-elf
```

- riscv64-none-elf
- riscv32-none-elf
- riscv64-elf
- riscv64-unknown-elf
- riscv32-unknown-elf
**Arch**:

To actually build for the RISC-V targets, add `RISCV=1` to the make command:
```shell
$ sudo pacman -Syu arm-none-eabi-gcc
$ sudo pacman -Syu riscv64-elf-gcc
```

$ make RISCV=1
**Fedora**:

**MacOS**:
```
$ brew tap riscv/riscv && brew update && brew install riscv-gnu-toolchain
```
NOTE: Fedora currently doesn't have RISC-V support

**Ubuntu (21.10 or later)**:
```
$ sudo apt install gcc-riscv64-unknown-elf picolibc-riscv64-unknown-elf
```
```shell
$ sudo dnf install arm-none-eabi-gcc-cs
```

**Ubuntu (21.04 or earlier)**:
**Other distros**:

Unfortunately, older Ubuntu does not provide a package for RISC-V libc. We
have created a .deb file you can use to install a suitable libc based on
newlib:
```
$ wget http://cs.virginia.edu/~bjc8c/archive/newlib_3.3.0-1_amd64.deb
$ sudo dpkg -i newlib_3.3.0-1_amd64.deb
```
If your distro doesn't provide a RISC-V toolchain you can build one yourself
or just disable RISC-V support by running

If you would rather compile your own newlib-based libc, follow the steps
below. Section [newlib-nano](newlib-nano) describes some extra config options
to build a size optimised newlib.
```
# Download newlib 3.3 from https://sourceware.org/newlib/
$ wget ftp://sourceware.org/pub/newlib/newlib-3.3.0.tar.gz
$ tar -xvf newlib-3.3.0.tar.gz
$ cd newlib-3.3.0
# Disable stdlib for building
$ export CFLAGS=-nostdlib
# Run configure
$ ./configure --disable-newlib-supplied-syscalls --with-gnu-ld --with-newlib --enable-languages=c --target=riscv64-unknown-elf --host=x86 --disable-multi-lib --prefix /usr
# Build and then install
$ make -j8
$ sudo make install
```

Alternatively, you may use a pre-compiled toolchain that we created with
Crosstool-NG.
```
$ wget http://cs.virginia.edu/~bjc8c/archive/gcc-riscv64-unknown-elf-8.3.0-ubuntu.zip
$ unzip gcc-riscv64-unknown-elf-8.3.0-ubuntu.zip
# add gcc-riscv64-unknown-elf-8.3.0-ubuntu/bin to your `$PATH` variable.
```
```shell
NORISCV=1 make
```

**Arch**:
```
$ sudo pacman -Syu riscv64-elf-gcc riscv32-elf-newlib arm-none-eabi-newlib riscv64-elf-newlib
```
1. libc for embedded targets

**Fedora**:
**Ubuntu (21.10 or later) or Debian (11 or later)**:

**dnf** does not contain the `riscv-gnu-toolchain`, an alternative is to
compile from source. Start with some of the tools we need to compile the
source.
```
$ sudo dnf install make automake gcc gcc-c++ kernel-devel texinfo expat expat-devel
$ sudo dnf group install "Development Tools" "C Development Tools and Libraries"
```
Get `riscv-gnu-toolchain`, [summarised instructions as stated
here](https://github.com/riscv-collab/riscv-gnu-toolchain/blob/master/README.md)
```
$ git clone https://github.com/riscv/riscv-gnu-toolchain
$ cd riscv-gnu-toolchain/
```
**Note: add /opt/riscv/bin to your PATH**, then,
```
$ ./configure --prefix=/opt/riscv --enable-multilib
```
`--enable-multilib` ensures that "the multilib compiler will have the prefix
riscv64-unknown-elf- or riscv64-unknown-linux-gnu- but will be able to target
both 32-bit and 64-bit systems."
```
$ sudo make [might need elevated privileges writing to `/opt/`]
```
additionally, with
```
$ sudo make linux
```
you can also build `riscv64-unknown-linux-gnu`, which can be useful with tock
where `riscv64-unknown-linux-gnu-objcopy` is used.
Use picolibc instead of newlib

After the the source has been compiled and copied to `/opt/riscv` and
`/opt/riscv/bin`has appended to the PATH, the toolchain is ready to be used.
```shell
$ sudo apt install picolibc-riscv64-unknown-elf
```

**Arch**:

**newlib-nano**:
```shell
$ sudo pacman -Syu riscv32-elf-newlib
```

newlib can require a large amount of memory, especially for printing.
If this is a concern you can instead use a more size optimised version.
As of August 2020 there are a few options for this.
**Fedora**:

- See if the version of newlib from your distro already has the flags below
enabled. If it does it's already size optimsed.
- See if your distro pacakges a newlib-nano (Debian does this) that will
already include the flags below.
- See if your distro packages picolibc, which is a optimised fork of newlib.
- You can compile newlib with these extra flags:
```shell
$ sudo dnf install arm-none-eabi-newlib
```
--enable-newlib-reent-small \
--disable-newlib-fvwrite-in-streamio \
--disable-newlib-fseek-optimization \
--disable-newlib-wide-orient \
--enable-newlib-nano-malloc \
--disable-newlib-unbuf-stream-opt \
--enable-lite-exit \
--enable-newlib-global-atexit \
--enable-newlib-nano-formatted-io

*newlib-nano*

newlib can require a large amount of memory, especially for printing.
If this is a concern you can instead use a more size optimised version.
As of August 2020 there are a few options for this.

- See if the version of newlib from your distro already has the flags below
enabled. If it does it's already size optimsed.
- See if your distro packages picolibc, which is a optimised fork of newlib.
- You can compile newlib with these extra flags:
```shell
--enable-newlib-reent-small \
--disable-newlib-fvwrite-in-streamio \
--disable-newlib-fseek-optimization \
--disable-newlib-wide-orient \
--enable-newlib-nano-malloc \
--disable-newlib-unbuf-stream-opt \
--enable-lite-exit \
--enable-newlib-global-atexit \
--enable-newlib-nano-formatted-io
```

If you would rather compile your own newlib-based libc, follow the steps
below.

```shell
# Download newlib 4.1 from https://sourceware.org/newlib/
$ wget ftp://sourceware.org/pub/newlib/newlib-4.1.0.tar.gz
$ tar -xvf newlib-4.1.0.tar.gz
$ cd newlib-4.1.0
# Disable stdlib for building
$ export CFLAGS=-nostdlib
# Run configure
$ ./configure --disable-newlib-supplied-syscalls --with-gnu-ld --with-newlib --enable-languages=c --target=riscv64-unknown-elf --host=x86 --disable-multi-lib --prefix /usr
# Build and then install
$ make -j8
$ sudo make install
```

3. Optional: libtock-c also includes support for building RISC-V targets with
1. Optional: Newlib is a commonly used embedded libc. Although it does have a
few limitations. Mostly in that the print implementation can be
(too large)[https://keithp.com/picolibc/picolibc-2021-notes.pdf] and the
license can be (too restrictive)[https://github.com/tock/libtock-c/issues/309].
Newlib for RISC-V also isn't pre-packaged for Debian systems.

If you would prefer to use picolibc instead of newlib for RISC-V you can
follow this step. If you are happy using newlib or aren't building for
RISC-V you can skip this step. picolibc isn't support on ARM platforms
at the moment.

If using picolibc then specify the `PICOLIBC` variable.

```shell
$ make PICOLIBC=1
```

1. Optional: libtock-c also includes support for building RISC-V targets with
the LLVM clang compiler. If you have a compatible clang toolchain, you can
add `CLANG=1` to the make command to use clang instead of the default GCC.

$ make RISCV=1 CLANG=1
```shell
$ make CLANG=1
```

This support is only included for RISC-V targets as Cortex-M targets require
the FDPIC support only present in GCC.
Expand Down
32 changes: 32 additions & 0 deletions libtock/console.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
Expand Down Expand Up @@ -150,3 +151,34 @@ int getnstr_abort(void) {
syscall_return_t com = command(DRIVER_NUM_CONSOLE, 3, 0, 0);
return tock_command_return_novalue_to_returncode(com);
}

/*
* If running with picolibc let's deine the `__iob` array.
* This is used for stdin/stdout/stderr
* https://github.com/picolibc/picolibc/blob/main/doc/os.md#stdinstdoutstderr
*/
#ifdef _FDEV_SETUP_RW
alistair23 marked this conversation as resolved.
Show resolved Hide resolved

static int picolibc_putc(char c, FILE *file)
{
(void) file; /* Not used in this function */
putnstr(&c, 1); /* Defined by underlying system */
return c;
}

static int picolibc_getc(FILE *file)
{
unsigned char c;
(void) file; /* Not used in this function */
c = getch(); /* Defined by underlying system */
return c;
}

static FILE __stdio = FDEV_SETUP_STREAM(picolibc_putc,
picolibc_getc,
NULL,
_FDEV_SETUP_RW);

FILE *const __iob[3] = { &__stdio, &__stdio, &__stdio };

#endif
1 change: 1 addition & 0 deletions libtock/udp.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <sys/types.h>

#include "tock.h"
#include <unistd.h>

#ifdef __cplusplus
extern "C" {
Expand Down
3 changes: 3 additions & 0 deletions userland_generic.ld
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ SECTIONS {
KEEP(*(.sbss*)) /* for RISC-V */
*(COMMON)
. = ALIGN(4);
__heap_start = .;
. = __heap_start + APP_HEAP_SIZE;
__heap_end = .;
} > SRAM

/* End of flash. */
Expand Down