Skip to content

Commit

Permalink
Merge pull request #13 from sifive/elf
Browse files Browse the repository at this point in the history
Move to the ELF toolchain
  • Loading branch information
bsousi5 committed Aug 15, 2018
2 parents ffc29be + 650d1c3 commit 1db9131
Show file tree
Hide file tree
Showing 26 changed files with 287 additions and 165 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
*.mk
/return_pass
/return_fail
*.specs
57 changes: 33 additions & 24 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,39 @@
%.c: mee/machine/@[email protected]
%.S: mee/machine/@[email protected]
%.o: mee/machine/@[email protected]
%.c: riscv__menv__mee.specs
%.S: riscv__menv__mee.specs
%.o: riscv__menv__mee.specs
%.c: riscv__mmachine__@[email protected]
%.S: riscv__mmachine__@[email protected]
%.o: riscv__mmachine__@[email protected]

# Every test depends on the generated linker script.
$(check_PROGRAMS): mee-@[email protected]
$(check_PROGRAMS): riscv__mmachine__@[email protected]

# Every test depends on the freshly-compiled library.
$(check_PROGRAMS): libmee-@[email protected]
$(check_PROGRAMS): libriscv__mmachine__@[email protected]

# Generates a linker script that's more reasonable that whatever GCC's default
# is.
ldsdir = $(libdir)
lds_DATA = mee-@[email protected]
mee-@[email protected]: @LDSCRIPT_GENERATOR@ @[email protected]
lds_DATA = riscv__mmachine__@[email protected]
riscv__mmachine__@[email protected]: @LDSCRIPT_GENERATOR@ @[email protected]
$< --dtb $(filter %.dtb,$^) --linker $@

# Generates a SPEC file that sets a reasonable set of default options for this
# build.
specdir = $(libdir)
spec_DATA =

spec_DATA += riscv__mmachine__@[email protected]
riscv__mmachine__@[email protected]: @SPECS_GENERATOR@ @[email protected]
$< --dtb $(filter %.dtb,$^) --specs $@ --prefix @prefix@ --machine @MACHINE_NAME@

spec_DATA += riscv__menv__mee.specs
riscv__menv__mee.specs: riscv__menv__mee.specs.in
cat $^ > $@

# In order to generate code that's actually compatible with a machine we must
# pass the march and mabi arguments to GCC that coorespond to the hardware.
# This is handled by generating a makefile fragment, including it, and then
Expand All @@ -45,6 +64,7 @@ nobase_include_HEADERS = \
mee/compiler.h \
mee/clock.h \
mee/io.h \
mee/machine.h \
mee/shutdown.h \
mee/tty.h \
mee/uart.h
Expand All @@ -64,14 +84,12 @@ mee/machine/@[email protected]: @MEE_HEADER_GENERATOR@ @[email protected]
# Everything in here is compiled into a single library, which contains all the
# source files in the project. It's named for one specific machine, which GCC
# uses to select the target machine that this MEE implementation points at.
lib_LIBRARIES = libmee-@[email protected]
lib_LIBRARIES = libriscv__mmachine__@[email protected]

libmee_@MACHINE_NAME@_a_CFLAGS = -mmee-machine=@MACHINE_NAME@
libmee_@MACHINE_NAME@_a_CFLAGS += -ffunction-sections -fdata-sections
libmee_@MACHINE_NAME@_a_CFLAGS += -march=$(FRAMEWORK_BOARD_DTS_MARCH) -mabi=$(FRAMEWORK_BOARD_DTS_MABI)
libmee_@MACHINE_NAME@_a_CCASFLAGS = $(libmee_@MACHINE_NAME@_a_CFLAGS) -DASSEMBLY
libriscv__mmachine__@MACHINE_NAME@_a_CFLAGS = -menv=mee -mmachine=@MACHINE_NAME@
libriscv__mmachine__@MACHINE_NAME@_a_CCASFLAGS = -menv=mee -mmachine=@MACHINE_NAME@

libmee_@MACHINE_NAME@_a_SOURCES = \
libriscv__mmachine__@MACHINE_NAME@_a_SOURCES = \
src/drivers/fixed-clock.c \
src/drivers/sifive,fe310-g000,hfrosc.c \
src/drivers/sifive,fe310-g000,hfxosc.c \
Expand All @@ -92,37 +110,28 @@ check_PROGRAMS =
# The simplest possible pair of tests: one that passes and one that fails
check_PROGRAMS += return_pass
return_pass_SOURCES = test/return_pass.c
return_pass_CFLAGS = -mmee-machine=@MACHINE_NAME@
return_pass_CFLAGS += -ffunction-sections -fdata-sections
return_pass_CFLAGS += -march=$(FRAMEWORK_BOARD_DTS_MARCH) -mabi=$(FRAMEWORK_BOARD_DTS_MABI)
return_pass_CFLAGS = -menv=mee -mmachine=@MACHINE_NAME@
return_pass_LDFLAGS = -L. -Wl,--gc-sections -Wl,-Map=return_pass.map

check_PROGRAMS += return_fail
return_fail_SOURCES = test/return_fail.c
return_fail_CFLAGS = -mmee-machine=@MACHINE_NAME@
return_fail_CFLAGS += -ffunction-sections -fdata-sections
return_fail_CFLAGS += -march=$(FRAMEWORK_BOARD_DTS_MARCH) -mabi=$(FRAMEWORK_BOARD_DTS_MABI)
return_fail_CFLAGS = -menv=mee -mmachine=@MACHINE_NAME@
return_fail_LDFLAGS = -L. -Wl,--gc-sections -Wl,-Map=return_fail.map

# A simple "Hello, World!" program that directly uses the MEE interface to
# print to the serial terminal.
check_PROGRAMS += hello
hello_SOURCES = test/hello.c
hello_CFLAGS = -mmee-machine=@MACHINE_NAME@
hello_CFLAGS += -ffunction-sections -fdata-sections
hello_CFLAGS += -march=$(FRAMEWORK_BOARD_DTS_MARCH) -mabi=$(FRAMEWORK_BOARD_DTS_MABI)
hello_CFLAGS = -menv=mee -mmachine=@MACHINE_NAME@
hello_LDFLAGS = -L. -Wl,--gc-sections -Wl,-Map=hello.map

check_PROGRAMS += pll_set_hz
pll_set_hz_SOURCES = test/pll_set_hz.c
pll_set_hz_CFLAGS = -mmee-machine=@MACHINE_NAME@
pll_set_hz_CFLAGS += -ffunction-sections -fdata-sections
pll_set_hz_CFLAGS += -march=$(FRAMEWORK_BOARD_DTS_MARCH) -mabi=$(FRAMEWORK_BOARD_DTS_MABI)
pll_set_hz_CFLAGS = -menv=mee -mmachine=@MACHINE_NAME@
pll_set_hz_LDFLAGS = -L. -Wl,--gc-sections -Wl,-Map=pll_set_hz.map

# Extra clean targets
clean-local:
-rm -rf @[email protected]
-rm -rf mee/machine/@[email protected] @[email protected] mee-@[email protected]
-rm -rf return_pass.map return_fail.map
-rm -rf hello.map
-rm -rf *.map *.specs
66 changes: 0 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,69 +6,3 @@ This repository provides a MEE that is designed to run on SiFive's
Freedom platform devices. As the MEE runs at machine mode it's not
designed with binary compatibility in mind but is instead tailored to a
specific machine.

## Describing a Target Machine

The MEE is not natively self-hosting and is therefor designed to be
cross compiled for a target machine. This target machine is described
by a device tree file, which is then provided to the MEE's build scripts
in order to parameterize it for the target machine.

## MEE Interface

The whole point of the MEE is to define an interface against which
portable programs can be written. Since this is machine-mode software
the degree of portability is very limited, but nontheless some about of
portability can reasonably be achieved. Specifically, that means

* The existence of devices can be statically probed.
* Devices exist behind a well-defined API that abstracts away their
physical addresses.

This allows portable software to be written against the MEE that will
run on all SiFive systems that are capable of meeting its requirements.

### Internal Symbols vs Public Symbols

Any symbol beginning with `__mee_` is internal to the MEE and must not
be called or defined by user code. Symbols beginning with `mee_` are
public interfaces and can be considered to be a stable API. The best
documentation for most of these symbols are found in the code, but some
is listed below.

As the MEE is desgined for bare-metal development there is no stable ABI.

### Pre-C Environment

The MEE handles entering the C library's start routine, which is defined
by the symbol `_start`. This symbol must be defined by the C library in
order to allow the MEE to do anything meaningful.

The MEE follows the standard RISC-V bootloader ABI when calling
`_start`:

* `a0` contains the hart ID.
* `a1` contains a pointer to the machine description. The MEE always
sets this to NULL, as machines are described staticly.
* `a2` contains a callback that should be called from within the child
environment after it has initialized itself. The MEE always sets this
to NULL, as initialization is performed using constructors.

This can be described as a the C function `void _start(long hartid,
unsigned char *dtb, void (*after_init)(void))'. Note that the MEE does
not initialize a C environment and therefor this cannot actually be a C
function -- for example, there may not be a stack.

### Special Sections

Many special sections are utilized by this MEE implementation in order
to allow compact code to be generated while still providing API
guarntees. These sections are all named such that standard linker
scripts will produce the correct answer, albeit at some performance or
code size cost. A list of interesting sections is described below:

* `.text.mee.nop`: Every sub-section in here will consist of exactly the
same C function: `int func(...) { return -1; }`. Functions that look
like this are frequently weakly referenced in order to provide default
implementations of MBI functionality that cannot be implemented on the
given target.
18 changes: 18 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,24 @@ AS_IF([test "x$MAKEATTRIBUTES_GENERATOR" != "xno"],
[AC_MSG_ERROR([Unable to find freedom-makeattributes-generator, either place it in PATH or try the --with-makeattributes-generator argument.])]
)

# Allows users to specify a path to freedom-mee_specs-generator, which
# generates GCC spec files to control the compilation of MEE based targets.
AC_ARG_WITH([mee_specs-generator],
[AS_HELP_STRING([--with-mee_specs-generator=PATH], [Use the given path to freedom-mee_specs-generator])],
[],
[with_mee_specs_generator=check]
)

AC_ARG_VAR(SPECS_GENERATOR, [The absolute path of the freedom-mee_specs-generator])
AS_IF([test "x$with_mee_specs_generator" == "xcheck"],
[AC_PATH_PROG(SPECS_GENERATOR, freedom-mee_specs-generator, [no])],
[SPECS_GENERATOR=$with_mee_specs_generator]
)
AS_IF([test "x$SPECS_GENERATOR" != "xno"],
[AC_SUBST([SPECS_GENERATOR], "$SPECS_GENERATOR")],
[AC_MSG_ERROR([Unable to find freedom-mee_specs-generator, either place it in PATH or try the --with-mee_specs-generator argument.])]
)

# Generates the remainder of the build system.
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
145 changes: 145 additions & 0 deletions doc/mee-spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# MEE Specification

The MEE is designed to provide a source-level compatibility for
bare-metal code between platforms. This specification defines the
publicly available API. The intent is that this API is stable so users
can rely on it not changing, but we're not going to make any guarantees
about it until our 1.0.0 release.

Note that the MEE does not define an ABI -- specifically that means that
binaries will not be compatible between different versions of the MBI,
or between different platforms.

## User API

The core of the MEE is a C API that is designed to allow programmers to
write portable bare-metal embedded code.

Any symbol beginning with `__mee_` is internal to the MEE and must not
be called or defined by user code. Symbols beginning with `mee_` are
public interfaces and can be considered to be a stable API. The best
documentation for most of these symbols are found in the code, but some
is listed below.

### Clock Interface

The clock interface allows for controlling the rate of various clocks in
the system. Clocks are defined by a pointer to a `struct mee_clock`, the
contents of which is implementation defined. Users of the clock
interface must call the functions defined below in order to interact
with a `struct mee_clock *`.

Note that no mechanism for obtaining a pointer to a `struct mee_clock`
has been defined, making it impossible to call any of these functions
without invoking implementation-defined behavior.

#### `long mee_clock_get_rate_hz(const struct mee_clock *clock)`

Returns the clock rate of the given clock, in Hz.

#### `long mee_clock_set_rate_hz(const struct mee_clock *clock, long hz)`

Attempts to set the rate of the given clock to the given value, in Hz.
Returns the rate the given clock was actually set to, which may be
different than the requested rate. There are no hard requirements on
what clock rates can be set, but it's expected that a best effort
approach is taken to match the caller's desired clock rate.

#### `void mee_clock_register_rate_change_callback(struct mee_clock *clk, int (*cb)(struct mee_clock *, void *), void *priv)`

Registers a function (and an associated opaque data block) that will be
called whenever the giver clock's rate has been changed. This function
will be called after the driver-specific clock frequency changing code
has returned, but before the caller of `mee_clock_set_rate_hz()` has
been returned to.

It's not guaranteed that the given clock's rate will have actually
changed every time the given function is called, but it's guaranteed
that the given function will be called every time the given clock's rate
has been changed.

### Power Control Interface

The MEE defines a mechanism to control the power state of a given
machine. The interface is currently quite simple: it's just the
`mee_shutdown()` function.

#### `void mee_shutdown(int code) __attribute__((noreturn))`

Terminates execution of this program, attempting to pass the given code
to whomever may be looking. The code `0` indicates success, while all
other codes indicate failure. The exact mechanism by which execution
terminates is implementation defined, but some examples include:

* Spinning in an infinite loop.
* Printing the return code to standard out and spinning in an infinite
loop.
* Toggling an external I/O to disable the power to the core.
* Poking through a hole to the host that's providing this environment
and indicating success or failure.

### TTY Interface

The MEE provides an terminal interface. This interface is designed to
provide a simple mechanism for getting text-based data outside of the
MEE -- in other words, it's designed to be used to implement C library
functions like `printf()`.

#### `int mee_tty_putc(unsigned char c);`

Writes the given character to the default terminal, returning 0 on
success or -1 on failure.

### UART Interface

The UART interface allows users of the MEE to control

Note that there is no mechanism for obtaining a pointer to a `struct
mee_uart` without invoking implementation-defined behavior, thus making
calling any of these functions impossible.

#### `int mee_uart_init(struct mee_uart *uart)`

Initializes the given UART. This must be called exactly once before any
other function on this UART can be called. It is invalid to initialize
a UART more than once.

#### `int mee_uart_putc(struct mee_uart *uart, unsigned char c)`

Writes the given character to the given UART, returning 0 on success and
-1 on failure.

#### `int mee_uart_getc(struct mee_uart *uart, unsigned char *c)`

Reads a character from the given UART, storing it at the given character
pointer. This returns 0 on success and -1 on failure.

#### `int mee_uart_get_baud_rate(struct mee_uart *uart)`

Obtains the baud rate of the given UART, or `-1` to signify an error.

#### `int mee_uart_set_baud_rate(struct mee_uart *uart, int baud_rate)`

Sets the baud rate of the given UART. Returns 0 on success, or -1 on
failure. Failure to set the baud rate can render the UART unusable
until a subsequent coll to `mee_uart_set_baud_rate()` returns success.

### C Startup Interface

The MEE handles entering the C library's start routine, which is defined
by the symbol `_start`. This symbol must be defined by the C library in
order to allow the MEE to do anything meaningful.

The MEE follows the standard RISC-V bootloader ABI when calling
`_start`:

* `a0` contains the hart ID.
* `a1` contains a pointer to the machine description. The MEE always
sets this to NULL, as machines are described statically.
* `a2` contains a callback that should be called from within the child
environment after it has initialized itself.

This can be described as a the C function `void _start(long hartid,
unsigned char *dtb, void (*after_init)(void))'. Note that the MEE does
not initialize a C environment and therefor this cannot actually be a C
function -- for example, there may not be a stack.
8 changes: 4 additions & 4 deletions mee/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
#ifndef MEE__COMPILER_H
#define MEE__COMPILER_H

#define MEE_DECLARE_VTABLE(type) \
asm(".weak " #type); \
const struct type type \
#define __MEE_DECLARE_VTABLE(type) \
asm(".weak " #type); \
const struct type type \

#define MEE_GET_FIELD(reg, mask) \
#define __MEE_GET_FIELD(reg, mask) \
(((reg) & (mask)) / ((mask) & ~((mask) << 1)))

static inline long abs(long val)
Expand Down
2 changes: 1 addition & 1 deletion mee/drivers/fixed-clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct __mee_driver_vtable_fixed_clock {
long __mee_driver_fixed_clock_get_rate_hz(const struct mee_clock *gclk);
long __mee_driver_fixed_clock_set_rate_hz(struct mee_clock *gclk, long target_hz);

MEE_DECLARE_VTABLE(__mee_driver_vtable_fixed_clock) = {
__MEE_DECLARE_VTABLE(__mee_driver_vtable_fixed_clock) = {
.clock.get_rate_hz = __mee_driver_fixed_clock_get_rate_hz,
.clock.set_rate_hz = __mee_driver_fixed_clock_set_rate_hz,
};
Expand Down
2 changes: 1 addition & 1 deletion mee/drivers/sifive,fe310-g000,hfrosc.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct __mee_driver_vtable_sifive_fe310_g000_hfrosc {
struct __mee_clock_vtable clock;
};

MEE_DECLARE_VTABLE(__mee_driver_vtable_sifive_fe310_g000_hfrosc) = {
__MEE_DECLARE_VTABLE(__mee_driver_vtable_sifive_fe310_g000_hfrosc) = {
.clock.get_rate_hz = &__mee_driver_sifive_fe310_g000_hfrosc_get_rate_hz,
.clock.set_rate_hz = &__mee_driver_sifive_fe310_g000_hfrosc_set_rate_hz,
};
Expand Down
Loading

0 comments on commit 1db9131

Please sign in to comment.