first commit

master
Alessandro Mauri 1 year ago
commit 8f27b1a576
  1. 4
      .gitignore
  2. 3
      README.md
  3. 6
      ROADMAP
  4. 283
      build_cross_toolchain.sh
  5. 0
      kernel/-kernel
  6. 4
      kernel/.gitignore
  7. 39
      kernel/Makefile
  8. 190
      kernel/clock.h
  9. 27
      kernel/crt0.S
  10. BIN
      kernel/kernel
  11. 12
      kernel/kernel.c
  12. 10
      kernel/macro.h
  13. 26
      kernel/platform/FU740/clock.c
  14. BIN
      kernel/platform/FU740/doc/FU740_errata_20210205.pdf
  15. BIN
      kernel/platform/FU740/doc/fu740-c000-manual-v1p6.pdf
  16. 62
      kernel/platform/FU740/uart.c
  17. 170
      kernel/platform/FU740/uart.h
  18. 263
      kernel/rv64-sifive_u.ld
  19. 44
      qemu_run.sh

4
.gitignore vendored

@ -0,0 +1,4 @@
**/.cross_build
cross
*.dtb
*.dts

@ -0,0 +1,3 @@
# ROVO
An hobby operating system for RISC-V platforms

@ -0,0 +1,6 @@
[ ] parse the dtb
[ ] Fin the name of the board
[ ] Find out how many cores the cpu has
[ ] Find where the ram starts and how much memory is available
[ ] Find the UART address
[ ] Print the previous information to the serial console

@ -0,0 +1,283 @@
#!/bin/sh
BUILD_GCC=true
BUILD_BINUTILS=true
BUILD_GDB=true
START_SHELL=false
PROJECT_ROOT="$(pwd)"
CROSS_ROOT="$PROJECT_ROOT/cross"
PREFIX="$CROSS_ROOT"
CROSS_BUILD_DIR="$PROJECT_ROOT/.cross_build"
TARGET=''
CPUS=1
# Get the target arch
if [ -z "$ARCH" ]; then
ARCH="$1"
shift
if [ -z "$ARCH" ]; then
echo "Must define an arch"
exit 1
fi
fi
case "$ARCH" in
'x86_64-elf'|'x86_64'|'x64'|'amd64')
TARGET='x86_64-elf'
CONFIG_EXTRA_GCC="$CONFIG_EXTRA_GCC --enable-multilib"
;;
'i686-elf'|'i386'|'x32'|'386')
TARGET='i686-elf'
CONFIG_EXTRA_GCC="$CONFIG_EXTRA_GCC --enable-multilib"
;;
'riscv-64-elf'|'rv64'|'riscv64')
TARGET='riscv-64-elf'
;;
*)
echo "arch=$ARCH not implemented"
exit 1
;;
esac
# Get the build target
while [ "${1:-}" != "" ]; do
case "$1" in
"--no-gcc")
BUILD_GCC=false
;;
"--no-binutils")
BUILD_BINUTILS=false
;;
"--no-gdb")
BUILD_GDB=false
;;
"--start-shell")
START_SHELL=true
BUILD_GCC=false
BUILD_BINUTILS=false
BUILD_GDB=false
;;
esac
shift
done
# TODO: add some more options to speed up the build / disable unnecessary features
# https://gitlab.archlinux.org/archlinux/packaging/packages/riscv64-linux-gnu-binutils/-/blob/main/PKGBUILD
CONFIG_EXTRA_BINUTILS="\
--without-zstd \
--with-sysroot \
--enable-languages=c \
--disable-nls \
--disable-werror"
# TODO: add some more options to speed up the build / disable unnecessary features
# https://gitlab.archlinux.org/archlinux/packaging/packages/riscv64-linux-gnu-gcc/-/blob/main/PKGBUILD
CONFIG_EXTRA_GCC="\
--without-zstd \
--disable-nls \
--enable-languages=c \
--without-headers"
CONFIG_EXTRA_GDB="\
--with-sysroot \
--disable-nls \
--disable-werror \
--without-headers \
--enable-languages=c \
--enable-tui=yes \
--with-expat"
#CONFIG_HOST="$(gcc -dumpmachine)"
FTP_MIRROR="https://ftpmirror.gnu.org"
gdb_version='13.1'
gcc_version='12.2.0'
binutils_version='2.37'
gcc_url="$FTP_MIRROR/gcc/gcc-$gcc_version"
binutils_url="$FTP_MIRROR/binutils"
gdb_url="$FTP_MIRROR/gdb"
gdb_build_log=gdb-log-"$(date +%F_%H-%M)".log
gcc_build_log=gcc-log-"$(date +%F_%H-%M)".log
binutils_build_log=binutils-log-"$(date +%F_%H-%M)".log
# check if the user wants a particular version
if [ -n "$GDB_VERSION" ]; then
gdb_version="$GDB_VERSION"
fi
# sanity check
if [ -z "$TARGET" ]; then
echo "Target not specified, how'd you get this far?"
exit 1
fi
# get the right number of cpus
# and some extra fixes
case $(uname) in
'OpenBSD')
CPUS="$(sysctl -n hw.ncpu)"
CPUS=$((CPUS + 1))
CONFIG_EXTRA_GCC="$CONFIG_EXTRA_GCC --with-gmp=/usr/local"
# use GNU make
alias make=gmake
;;
'Linux')
CPUS="$(nproc)"
CPUS=$((CPUS + 1))
;;
*)
;;
esac
gpg_verify() {
keyring="$1"
pkg="$2"
sig="$3"
gpg --verify --keyring "$keyring" "$sig" "$pkg"
return $?
}
download_sources() {
pkg="$1"
sig="$2"
url="$3"
wget -nv -O "$pkg" "$url/$pkg" || return $?
wget -nv -O "$sig" "$url/$sig" || return $?
}
download_and_build() {
pkg_name="$1"
pkg_version="$2"
pkg_config="$3"
pkg_url="$4"
pkg_ark="$pkg_name-$pkg_version.tar.xz"
pkg_sig="$pkg_name-$pkg_version.tar.xz.sig"
# Get the archives and signature if they are not already present
if ! [ -e "$pkg_ark" ] || ! [ -e "$pkg_sig" ]; then
download_sources "$pkg_ark" "$pkg_sig" "$pkg_url" || return 1
fi
# Verify the archive
if ! gpg_verify ./gnu-keyring.gpg "$pkg_ark" "$pkg_sig"; then
echo "Package signature was incorrect, please remove the donwloaded sources and try again"
return 1
fi
# Extract the archive
xz -k -d "$pkg_ark"
tar -xf "$pkg_name-$pkg_version.tar" && rm -f "$pkg_name-$pkg_version.tar"
# FIXME: this forces a comlete rebuild
#if [ -d "build-$pkg_name-$TARGET" ]; then
# rm -rf "build-$pkg_name-$TARGET"
#fi
mkdir -p "build-$pkg_name-$TARGET"
cd "build-$pkg_name-$TARGET" || exit 1
oldpath="$PATH"
export PATH="$PREFIX/bin:$PATH"
# shellcheck disable=SC2086
../"$pkg_name-$pkg_version"/configure \
--target="$TARGET" \
$pkg_config \
--prefix="$PREFIX" || return 1
case "$pkg_name" in
'gcc')
make all-gcc || return 1
make all-target-libgcc || return 1
make install-gcc || return 1
make install-target-libgcc || return 1
;;
'binutils')
make -j$CPUS || return 1
make install || return 1
;;
'gdb')
make all-gdb || return 1
make install-gdb || return 1
;;
esac
export PATH="$oldpath"
cd ..
return 0
}
#set -x
mkdir -p "$PREFIX"
mkdir -p "$CROSS_BUILD_DIR/$TARGET"
cd "$CROSS_BUILD_DIR/$TARGET" || exit 1
# get the gnu keyring, used to verify the archives
if [ $BUILD_GCC = true ] || [ $BUILD_BINUTILS = true ] || [ $BUILD_GDB = true ]; then
if ! [ -e gnu-keyring.gpg ]; then
wget -O gnu-keyring.gpg https://ftp.gnu.org/gnu/gnu-keyring.gpg
fi
fi
if [ $BUILD_BINUTILS = true ]; then
echo "### BUILDING BINUTILS ###"
download_and_build binutils "$binutils_version" "$CONFIG_EXTRA_BINUTILS" \
"$binutils_url" > ../"$binutils_build_log" 2>&1
res=$?
if ! [ $res -eq 0 ]; then
echo "Error building binutils, check $CROSS_BUILD_DIR/$binutils_build_log for more details"
exit 1
fi
fi
if [ $BUILD_GCC = true ]; then
echo "### BUILDING GCC ###"
download_and_build gcc "$gcc_version" "$CONFIG_EXTRA_GCC" \
"$gcc_url" > ../"$gcc_build_log" 2>&1
res=$?
if ! [ $res -eq 0 ]; then
echo "Error building gcc, check $CROSS_BUILD_DIR/$gcc_build_log for more details"
exit 1
fi
fi
if [ $BUILD_GDB = true ]; then
echo "### BUILDING GDB ###"
download_and_build gdb "$gdb_version" "$CONFIG_EXTRA_GDB" \
"$gdb_url" > ../"$gdb_build_log" 2>&1
res=$?
if ! [ $res -eq 0 ]; then
echo "Error building gdb, check $CROSS_BUILD_DIR/$gdb_build_log for more details"
exit 1
fi
fi
cd "$PROJECT_ROOT" || exit 1
if [ $START_SHELL = true ]; then
PATH="$CROSS_ROOT/bin:$PATH"
export PATH TARGET ARCH
echo "### Starting $SHELL with correct PATH and TARGET ###"
exec "$SHELL" || exit 1
fi
# TODO: validate that all things are where they should

4
kernel/.gitignore vendored

@ -0,0 +1,4 @@
**/*.o
*.img
*.elf
compile_commands.json

@ -0,0 +1,39 @@
CC = $(TARGET)-gcc
LD = $(TARGET)-ld
AS = $(TARGET)-gcc
KERN_WARNINGS = -Wall -Wextra -Wpedantic -Wuninitialized -Wunused-result
KERN_FLAGS = -ffreestanding -nostartfiles -nostdlib -nodefaultlibs --std=gnu2x
MACHINE_FLAGS = -march=rv64gc -mabi=lp64 -mcmodel=medany
PATH_FLAGS = -I./
CFLAGS = ${MACHINE_FLAGS} -g -O0 ${KERN_WARNINGS} ${KERN_FLAGS} ${PATH_FLAGS}
LDFLAGS = -m elf64lriscv --gc-sections -T rv64-sifive_u.ld
ASFLAGS = ${CFLAGS}
.SUFFIXES: .c .o .S
.c.o:
${CC} ${CFLAGS} -o $@ -c $<
.S.o:
${AS} ${ASFLAGS} -o $@ -c $<
kernel.elf: kernel.o crt0.o platform/FU740/uart.o platform/FU740/clock.o
${LD} ${LDFLAGS} -o kernel.elf kernel.o crt0.o platform/FU740/uart.o platform/FU740/clock.o
kernel.o: kernel.c
platform/FU740/uart.o: platform/FU740/uart.c
platform/FU740/clock.o: platform/FU740/clock.c
crt0.o: crt0.S
.PHONY: all test clean
all: kernel.img
test:
clean:
rm -f kernel.elf kernel.o crt0.o platform/FU740/uart.o platform/FU740/clock.o

@ -0,0 +1,190 @@
#ifndef _PLATFORM_CLOCK_H
#define _PLATFORM_CLOCK_H
#include <stdint.h>
#include <macro.h>
/* Clocking and reset is managed by the PRCI (Power Reset Clocking Interrupt)
* block (Figure 24) [77]
*
* FU740-C000 generates all internal clocks from 26 MHz hfclk driven from an
* external oscillator (HFCLKIN) or crystal (HFOSCIN) input, selected by input
* HFXSEL. All harts operate in a single clock domain (coreclk) supplied by
* either corepll or dvfscorepll, which can be selected using the corepllsel
* register. These PLLs step 26 MHz hfclk up to higher frequencies.
* The recommended frequency of coreclk is 1.0GHz, however operation at up to
* 1.5GHz is possible.
* tlclk is a divided version of the coreclk and generates the clock for the L2
* cache.
* The hfpclkpll generates the clock for peripherals such as SPI, UART, GPIO,
* I2C, and PWM.
* dvfs_core_pll enables the user to change the CPU frequency without dropping
* down to the lower frequency hfclk.
* The DDR, Ethernet and PCIe Subsystems operate asynchronously.
* The PRCI contains two dedicated PLLs used to step 26 MHz hfclk up to the DDR
* and Ethernet operating frequencies.
* The PCIe Subsystem contains its own clock generation.
* The PRCI contains memory-mapped registers that control the clock selection
* and configuration of the PLLs.
* On power-on, the default PRCI register settings start the harts running
* directly from hfclk.
* All additional clock management, for instance initializing the DDR PLL or
* stepping the coreclk frequency, is performed through software reads and
* writes to the memory-mapped PRCI control registers.
* The CPU real time clock (rtcclk) runs at 1 MHz and is driven from input pin
* RTCCLKIN. This should be connected to an external oscillator.
* JTAG debug logic runs off of JTAG TCK as described in Chapter 26.
*
* Reset
* The FU740-C000 has two external reset pins.
* PORESET_N is an asynchonous active low power-on reset that should be
* connected to an external power sequencing/supervisory circuit. ERESET_N is
* an asynchonous active low reset that can be connected to a reset button.
* There is internal debounce and stretch logic. The PRCI also contains hardware
* to generate internal synchronous resets for coreclk, tlclk, and hfpclk
* domains and handle reset to and from the debug module. Resets for the DDR,
* Eth- ernet and PCIE Subsystems are performed through software reads and
* writes to memory-mapped PRCI control registers. These registers are outlined
* in Table 34 below.
*/
/* Table 21: PRCI Memory Map
* Offset Name Description
* 0x00 hfxosccfg Crystal Oscillator Configuration and Status
* 0x04 core_pllcfg PLL Configuration and Status
* 0x08 core_plloutdiv PLL Final Divide Configuration
* 0x0C ddr_pllcfg PLL Configuration and Status
* 0x10 ddr_plloutdiv PLL Final Divide Configuration
* 0x1C gemgxl_pllcfg PLL Configuration and Status
* 0x20 gemgxl_plloutdiv PLL Final Divide Configuration
* 0x24 core_clk_sel_reg Select core clock source. 0: coreclkpll 1: external hfclk
* 0x28 devices_reset_n Software controlled resets (active low)
* 0x2C clk_mux_status Current selection of each clock mux
* 0x38 dvfs_core_pllcfg PLL Configuration and Status
* 0x3C dvfs_core_plloutdiv PLL Final Divide Configuration
* 0x40 corepllsel Select which PLL output to use for core clock. 0: corepll 1: dvfscorepll
* 0x50 hfpclk_pllcfg PLL Configuration and Status
* 0x54 hfpclk_plloutdiv PLL Final Divide Configuration
* 0x58 hfpclkpllsel Select source for Periphery Clock (pclk). 0: hfpclkpll 1: external hfclk
* 0x5C hfpclk_div_reg HFPCLK PLL divider value
* 0xE0 prci_plls Indicates presence of each PLL
*/
#define HFCLK_FREQ_HZ 26000000L
#define PRCI_MEMORY_BLOCK 0x10000000L
struct S_PACKED prci_pllcfg {
uint32_t pllr:6; // [RW, 0x1] PLL R Value
uint32_t pllf:9; // [RW, 0x1F] PLL F Value
uint32_t pllq:3; // [RW, 0x3] PLL Q Value
uint32_t pllrange:3; // [RW, 0x0] PLL Range Value
uint32_t reserved0:3;
uint32_t pllbypass:1; // [RW, 0x1] PLL Bypass
uint32_t pllfsebypass:1; // [RW, 0x1] PLL FSE Bypass
uint32_t reserved1:5;
uint32_t plllock:1; // [RO, X] PLL Lock
};
struct S_PACKED prci_plloutdiv {
uint32_t reserved:31;
uint32_t pllcke:1; // [RW, 0x0] PLL Clock Enable
};
struct S_PACKED prci_mem_map {
// Offset 0x0
struct prci_hfxosccfg {
uint32_t reserved:30;
uint32_t hfxoscen:1; // [RW, 0x1] Crystal Oscillator Enable
uint32_t hfxoscrdy:1; // [RO, X] Crystal Oscillator Ready
} hfxosccfg;
// Offset 0x4
struct prci_pllcfg core_pllcfg;
// Offset 0x8
struct { uint32_t reserved; } core_plloutdiv;
// Offset 0xC
struct prci_pllcfg ddr_pllcfg;
// Offset 0x10
struct prci_plloutdiv ddr_plloutdiv;
uint8_t off0[8];
// Offset 0x1C
struct prci_pllcfg gemgxl_pllcfg;
// Offset 0x20
struct prci_plloutdiv gemgxl_plloutdiv;
// Offset 0x24
uint32_t core_clk_sel_reg;
// Offset 0x28
struct prci_devices_reset_n {
uint32_t ddrctrl_reset_n:1; // [RW, 0x0] Active-Low ddrctrl reset
uint32_t ddraxi_reset_n:1; // [RW, 0x0] Active-Low ddraxi reset
uint32_t ddrahb_reset_n:1; // [RW, 0x0] Active-Low ddrahb reset
uint32_t ddrphy_reset_n:1; // [RW, 0x0] Active-Low ddrphy reset
uint32_t pcieaux_reset_n:1; // [RW, 0x0] Active-Low pcieaux reset
uint32_t gemgxl_reset_n:1; // [RW, 0x0] Active-Low gemgxl reset
uint32_t reserved:26;
} devices_reset_n;
// Offset 0x2C
struct prci_clk_mux_status {
uint32_t coreclkpllsel:1; // [RO, X] Current setting of coreclkpllsel mux
uint32_t tlclksel:1; // [RO, X] Current setting of tlclksel mux
uint32_t rtcxsel:1; // [RO, X] Current setting of rtcxsel mux
uint32_t ddrctrlclksel:1; // [RO, X] Current setting of ddrctrlclksel mux
uint32_t ddrphyclksel:1; // [RO, X] Current setting of ddrphyclksel mux
uint32_t reserved0:1; // [RO, X] Current setting of reserved0 mux
uint32_t gemgxlclksel:1; // [RO, X] Current setting of gemgxlclksel mux
uint32_t mainmemclksel:1; // [RO, X] Current setting of mainmemclksel mux
uint32_t reserved:24;
} clk_mux_status;
uint8_t off1[8];
// Offset 0x38
struct prci_pllcfg dvfs_core_pllcfg;
// Offset 0x3C
struct prci_plloutdiv dvfs_core_plloutdiv;
// Offset 0x40
uint32_t corepllsel;
uint8_t off2[12];
// Offset 0x50
struct prci_pllcfg hfpclk_pllcfg;
// Offset 0x54
struct prci_plloutdiv hfpclk_plloutdiv;
// Offset 0x58
uint32_t hfpclkpllsel;
// Offset 0x5C
uint32_t hfpclk_div_reg; // [RW, 0x0] HFPCLK PLL divider value
uint8_t off3[128];
// Offset 0xE0
struct prci_prci_plls {
uint32_t cltxpll:1; // [RO, X] Indicates presence of cltxpll
uint32_t gemgxlpll:1; // [RO, X] Indicates presence of gemgxlpll
uint32_t ddrpll:1; // [RO, X] Indicates presence of ddrpll
uint32_t hfpclkpll:1; // [RO, X] Indicates presence of hfpclkpll
uint32_t dvfscorepll:1; // [RO, X] Indicates presence of dvfscorepll
uint32_t corepll:1; // [RO, X] Indicates presence of corepll
uint32_t reserved:26;
} prci_plls;
};
static_assert((sizeof(struct prci_mem_map) == 0xE4),
"prci_mem_map is not the right size");
#endif

@ -0,0 +1,27 @@
# C runtime, modified from
# https://twilco.github.io/riscv-from-scratch/2019/04/27/riscv-from-scratch-2.html#stop--hammertime-runtime
# in the init section which is "allocatable" and "executable"
.section .init, "ax"
# entry point for the kernel
.global _start
_start:
.cfi_startproc
.cfi_undefined ra
# initialize the global pointer register
.option push
.option norelax
la gp, __global_pointer$
.option pop
# initialize the stack pointer and frame pointer registers
# FIXME: when putting the stack at the end of RAM with system memory >2G
# it fails to link with the error: relocation truncated to fit:
# R_RISCV_HI20 against '__stack_top'
la sp, __stack_top
add s0, sp, zero
# jump to main
jal zero, main
.cfi_endproc
.end

Binary file not shown.

@ -0,0 +1,12 @@
#include "macro.h"
#include "platform/FU740/uart.h"
int F_NAKED main(void)
{
(void)uart_init(UART0, DEFAULT_BAUD, 0);
static char str[] = "Hello RISC-V!\n";
uart_tx_buf(UART0, str, sizeof(str));
while(1) asm volatile("nop"); // we should not be here
}

@ -0,0 +1,10 @@
#ifndef _COMMON_MACROS_H
#define _COMMON_MACROS_H
#define F_NAKED __attribute__((noreturn))
#define F_INTERRUPT __attribute__((interrupt))
#define S_PACKED __attribute__((packed))
#define static_assert _Static_assert
#endif

@ -0,0 +1,26 @@
#include <stdint.h>
#include "clock.h"
static struct prci_mem_map *const prci_mm = (struct prci_mem_map *const)PRCI_MEMORY_BLOCK;
// Sets the hfpclkpll register to the appropriate value for the freqency, returns
// -1 on error
// TODO: for higher frequencies use the internal PLLs
int set_hfp_frequency(uint32_t f)
{
// test if the correct register is present
if (!prci_mm->prci_plls.hfpclkpll)
return -1;
// make sure that the clock source is set to external
prci_mm->hfpclkpllsel = 1;
// the external clock source is 26MHz, the formula for the baud rate is:
// f_baud = f_hfclk / (1 + hfpclk_div_reg)
prci_mm->hfpclk_div_reg = (HFCLK_FREQ_HZ/f + 1);
return 0;
}

@ -0,0 +1,62 @@
#include <stdint.h>
#include "uart.h"
static struct uart_memory_map *const uart_mm[] = {
(struct uart_memory_map *const)UART_0_ADDR,
(struct uart_memory_map *const)UART_1_ADDR,
};
// set the baudrate on the chosen uart, if the baud rate is incorrect or invalid
// then set it to the default value, return the set baudrate value
int uart_set_baudrate(uart_id id, int baud)
{
// FIXME: actually set the baud rate, for now leave it as the default
(void)baud;
(void)id;
return 115200;
}
// initialize the uart registers for transmit and receive
// TODO: also initialize interrupts and thesholds
int uart_init(uart_id id, uint32_t baud, int stop_bits)
{
// FIXME: check baud rate error and propagate it
(void)uart_set_baudrate(id, baud);
// change the number of stop bits only if stop_bits > 0
if (stop_bits > 0)
uart_mm[id]->txctrl.nstop = !!stop_bits;
uart_mm[id]->txctrl.txen = 1;
uart_mm[id]->rxctrl.rxen = 1;
return 0;
}
// transmit a byte of data through a uart line
void uart_tx_byte(uart_id id, unsigned char b)
{
uart_mm[id]->txdata.data = b;
}
// busy-wait until the uart buffer is cleared
void uart_tx_full(uart_id id)
{
while (uart_mm[id]->txdata.full);
}
// send a buffer through uart
void uart_tx_buf(uart_id id, unsigned char *buf, unsigned int size)
{
for (unsigned int i = 0; i < size; i++) {
uart_tx_byte(id, buf[i]);
uart_tx_full(id);
}
}

@ -0,0 +1,170 @@
#ifndef _PLATFORM_UART_H
#define _PLATFORM_UART_H
#include <stdint.h>
#include <macro.h>
// <Description> <[Doc Page]>
// UART Base Addesses [141]
#define UART_0_ADDR 0x10010000L
#define UART_1_ADDR 0x10010000L
// UART Control Register Offsets [142]
#define UART_TXDATA_OFF 0x00 // Transmit data register
#define UART_RXDATA_OFF 0x04 // Receive data register
#define UART_TXCTRL_OFF 0x08 // Transmit control register
#define UART_RXCTRL_OFF 0x0C // Receive control register
#define UART_IE_OFF 0x10 // UART interrupt enable
#define UART_IP_OFF 0x14 // UART interrupt pending
#define UART_DIV_OFF 0x18 // Baud rate divisor
#define UART_NUMBER 2
#define DEFAULT_BAUD 115200
// UART ids
typedef enum {
UART0 = 0,
UART1 = 1,
} uart_id;
/* Transmit Data Register (txdata) [142]
* Writing to the txdata register enqueues the character contained in the data
* field to the transmit FIFO if the FIFO is able to accept new entries. Reading
* from txdata returns the current value of the full flag and zero in the data
* field. The full flag indicates whether the transmit FIFO is able to accept
* new entries; when set, writes to data are ignored. A RISCV amoor.w
* instruction can be used to both read the full status and attempt to enqueue
* data, with a non-zero return value indicating the character was not accepted.
*/
struct S_PACKED uart_txdata_reg {
uint32_t data:8;
uint32_t reserved:23;
uint32_t full:1;
};
static_assert((sizeof(struct uart_txdata_reg) == sizeof(uint32_t)),
"uart_txdata_reg is not the right size");
/* Receive Data Register (rxdata) [143]
* Reading the rxdata register dequeues a character from the receive FIFO and
* returns the value in the data field. The empty flag indicates if the receive
* FIFO was empty; when set, the data field does not contain a valid character.
* Writes to rxdata are ignored.
*/
struct S_PACKED uart_rxdata_reg {
uint32_t data:8;
uint32_t reserved:23;
uint32_t empty:1;
};
static_assert((sizeof(struct uart_rxdata_reg) == sizeof(uint32_t)),
"uart_rxdata_reg is not the right size");
/* Transmit Control Register (txctrl) [143]
* The read-write txctrl register controls the operation of the transmit channel.
* The txen bit con- trols whether the Tx channel is active. When cleared,
* transmission of Tx FIFO contents is suppressed, and the txd pin is driven
* high. The nstop field specifies the number of stop bits: 0 for one stop bit
* and 1 for two stop bits. The txcnt field specifies the threshold at which the
* Tx FIFO watermark interrupt triggers. The txctrl register is reset to 0.
*/
struct S_PACKED uart_txctrl_reg {
uint32_t txen:1;
uint32_t nstop:1;
uint32_t reserved0:14;
uint32_t txcnt:3;
uint32_t reserved1:13;
};
static_assert((sizeof(struct uart_txctrl_reg) == sizeof(uint32_t)),
"uart_txctrl_reg is not the right size");
/* Receive Control Register (rxctrl) [144]
* The read-write rxctrl register controls the operation of the receive channel.
* The rxen bit controls whether the Rx channel is active. When cleared, the
* state of the rxd pin is ignored, and no characters will be enqueued into the
* Rx FIFO. The rxcnt field specifies the threshold at which the Rx FIFO
* watermark interrupt triggers. The rxctrl register is reset to 0. Characters
* are enqueued when a zero (low) start bit is seen.
*/
struct S_PACKED uart_rxctrl_reg {
uint32_t rxen:1;
uint32_t reserved0:15;
uint32_t rxcnt:3;
uint32_t reserved1:13;
};
static_assert((sizeof(struct uart_rxctrl_reg) == sizeof(uint32_t)),
"uart_rxctrl_reg is not the right size");
/* Interrupt Registers (ip and ie) [144]
* The ip register is a read-only register indicating the pending interrupt
* conditions, and the read- write ie register controls which UART interrupts
* are enabled. ie is reset to 0. The txwm condition becomes raised when the
* number of entries in the transmit FIFO is strictly less than the count
* specified by the txcnt field of the txctrl register. The pending bit is
* cleared when sufficient entries have been enqueued to exceed the watermark.
* The rxwm condition becomes raised when the number of entries in the receive
* FIFO is strictly greater than the count specified by the rxcnt field of the
* rxctrl register. The pending bit is cleared when sufficient entries have been
* dequeued to fall below the watermark.
*/
struct S_PACKED uart_ie_reg {
uint32_t txwm:1;
uint32_t rxwm:1;
uint32_t reserved:30;
};
static_assert((sizeof(struct uart_ie_reg) == sizeof(uint32_t)),
"uart_ie_reg is not the right size");
struct S_PACKED uart_ip_reg {
uint32_t txwm:1;
uint32_t rxwm:1;
uint32_t reserved:30;
};
static_assert((sizeof(struct uart_ip_reg) == sizeof(uint32_t)),
"uart_ip_reg is not the right size");
/* Baud Rate Divisor Register (div) [145]
* The read-write, div_width-bit div register specifies the divisor used by baud
* rate generation for both Tx and Rx channels. The relationship between the
* input clock and baud rate is given by the following formula: The input clock
* is the bus clock pclk. The reset value of the register is set to div_init,
* which is tuned to provide a 115200 baud output out of reset given the
* expected frequency of pclk. Table 85 shows divisors for some common core
* clock rates and commonly used baud rates. Note that the table shows the
* divide ratios, which are one greater than the value stored in the div
* register. The receive channel is sampled at 16× the baud rate, and a majority
* vote over 3 neighboring bits is used to determine the received value. For
* this reason, the divisor must be 16 for a receive channel.
*/
struct S_PACKED uart_div_reg {
uint32_t div:16;
uint32_t reserved:16;
};
static_assert((sizeof(struct uart_div_reg) == sizeof(uint32_t)),
"uart_div_reg is not the right size");
struct S_PACKED uart_memory_map {
struct uart_txdata_reg txdata;
struct uart_rxdata_reg rxdata;
struct uart_txctrl_reg txctrl;
struct uart_rxctrl_reg rxctrl;
struct uart_ie_reg ie;
struct uart_ip_reg ip;
struct uart_div_reg div;
};
static_assert((sizeof(struct uart_memory_map) == sizeof(uint32_t[7])),
"uart_memory_map is not the right size");
int uart_set_baudrate(uart_id id, int baud);
int uart_init(uart_id id, uint32_t baud, int stop_bits);
void uart_tx_byte(uart_id id, unsigned char b);
void uart_tx_full(uart_id id);
void uart_tx_buf(uart_id id, unsigned char *buf, unsigned int size);
#endif

@ -0,0 +1,263 @@
OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv")
OUTPUT_ARCH(riscv)
SEARCH_DIR("=/home/ale/Documents/Projects/OS/rovo/cross/riscv-64-elf/lib")
SEARCH_DIR("=/usr/local/lib")
SEARCH_DIR("=/lib")
SEARCH_DIR("=/usr/lib")
MEMORY
{
/* qemu-system-risc64 sifive_u machine */
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 1024M
}
ENTRY(_start)
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x10000));
. = SEGMENT_START("text-segment", 0x10000) + SIZEOF_HEADERS;
/* Provide the stack top positioned at the bottom of RAM */
PROVIDE (__stack_top = ORIGIN(RAM) + LENGTH(RAM));
.interp : { *(.interp) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rela.dyn :
{
*(.rela.init)
*(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
*(.rela.fini)
*(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
*(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
*(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
*(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
*(.rela.ctors)
*(.rela.dtors)
*(.rela.got)
*(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
*(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
*(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
*(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
*(.rela.ifunc)
}
.rela.plt :
{
*(.rela.plt)
PROVIDE_HIDDEN (__rela_iplt_start = .);
*(.rela.iplt)
PROVIDE_HIDDEN (__rela_iplt_end = .);
}
.init :
{
KEEP (*(SORT_NONE(.init)))
}
.plt : { *(.plt) *(.iplt) }
.text :
{
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(SORT(.text.sorted.*))
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf.em. */
*(.gnu.warning)
}
.fini :
{
KEEP (*(SORT_NONE(.fini)))
}
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.sdata2 :
{
*(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
}
.sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) }
/* These sections are generated by the Sun/Oracle C++ compiler. */
.exception_ranges : ONLY_IF_RO { *(.exception_ranges*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gnu_extab : ONLY_IF_RW { *(.gnu_extab) }
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
.exception_ranges : ONLY_IF_RW { *(.exception_ranges*) }
/* Thread Local Storage sections */
.tdata :
{
PROVIDE_HIDDEN (__tdata_start = .);
*(.tdata .tdata.* .gnu.linkonce.td.*)
}
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
}
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
.dynamic : { *(.dynamic) }
. = DATA_SEGMENT_RELRO_END (0, .);
.data :
{
__DATA_BEGIN__ = .;
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
.got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
/* We want the small data sections together, so single-instruction offsets
can access them all, and initialized data all before uninitialized, so
we can shorten the on-disk segment size. */
.sdata :
{
__SDATA_BEGIN__ = .;
*(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*)
*(.sdata .sdata.* .gnu.linkonce.s.*)
}
_edata = .; PROVIDE (edata = .);
. = .;
__bss_start = .;
.sbss :
{
*(.dynsbss)
*(.sbss .sbss.* .gnu.linkonce.sb.*)
*(.scommon)
}
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we do not
pad the .data section. */
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
. = ALIGN(64 / 8);
. = SEGMENT_START("ldata-segment", .);
. = ALIGN(64 / 8);
__BSS_END__ = .;
__global_pointer$ = MIN(__SDATA_BEGIN__ + 0x800,
MAX(__DATA_BEGIN__ + 0x800, __BSS_END__ - 0x800));
_end = .; PROVIDE (end = .);
. = DATA_SEGMENT_END (.);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
.gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1. */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions. */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2. */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2. */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions. */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3. */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
/* DWARF 5. */
.debug_addr 0 : { *(.debug_addr) }
.debug_line_str 0 : { *(.debug_line_str) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_macro 0 : { *(.debug_macro) }
.debug_names 0 : { *(.debug_names) }
.debug_rnglists 0 : { *(.debug_rnglists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.debug_sup 0 : { *(.debug_sup) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}

@ -0,0 +1,44 @@
#!/bin/sh
# https://www.qemu.org/docs/master/system/target-riscv.html#board-specific-documentation
# This is similar to the JH7110 used in the PineTab-V
EMU_MACHINE='sifive_u'
EMU_MEMORY='1G'
EMU_BIOS='none'
EMU_PLATFORM="-machine $EMU_MACHINE -smp 5 -m $EMU_MEMORY"
EMU_OPTIONS="-bios $EMU_BIOS -display none -serial stdio -gdb tcp::1234"
EMU_EXTRA=''
QEMU_BIN='qemu-system-riscv64'
kern_img=''
while [ "${1:-}" != "" ]; do
case "$1" in
"--debug")
EMU_EXTRA='-D'
;;
"--dump-dts"|"--dump")
# shellcheck disable=SC2086
QEMU_CMD="$QEMU_BIN $EMU_PLATFORM $EMU_OPTIONS"
$QEMU_CMD -machine dumpdtb=riscv64-"$EMU_MACHINE".dtb
dtc riscv64-"$EMU_MACHINE".dtb > riscv64-"$EMU_MACHINE".dts
exit 0
;;
*)
kern_img="$1"
;;
esac
shift
done
if ! [ "$kern_img" ]; then
echo "Must provide kernel binary ($kern_img)"
exit 1
fi
QEMU_CMD="$QEMU_BIN $EMU_PLATFORM $EMU_OPTIONS $EMU_EXTRA"
echo "starting $QEMU_CMD"
# shellcheck disable=SC2086
exec $QEMU_CMD -kernel "$kern_img"
Loading…
Cancel
Save