added tinyprintf in the SW

This commit is contained in:
darotsr 2024-09-29 21:30:53 +03:00
parent 5f9e027763
commit e212b7dfed
13 changed files with 1484 additions and 3 deletions

View file

@ -0,0 +1,38 @@
CV_SW_TOOLCHAIN ?= /opt/riscv
RISCV ?= $(CV_SW_TOOLCHAIN)
RISCV_EXE_PREFIX ?= $(RISCV)/bin/riscv32-unknown-elf-
RISCV_GCC = $(RISCV_EXE_PREFIX)gcc
RISCV_AR = $(RISCV_EXE_PREFIX)ar
SRC = crt0.S simple_system_common.c startup.c tinyprintf.c
OBJ = crt0.o simple_system_common.o startup.o tinyprintf.o
LIBCV-VERIF = libcv-verif.a
TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_PRINTF
TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_SPRINTF
CFLAGS ?= -Os -g -static -mabi=ilp32 -march=$(CV_SW_MARCH) $(TINY_PRINTF_FLAGS) -ffunction-sections -fdata-sections -Wall -pedantic
all: $(LIBCV-VERIF)
$(LIBCV-VERIF): $(OBJ)
$(RISCV_AR) rcs $@ $(OBJ)
%.o : %.c
$(RISCV_GCC) $(CFLAGS) -c $< -o $@
%.o : %.S
$(RISCV_GCC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJ) $(LIBCV-VERIF)
vars:
@echo "make bsp variables:"
@echo " CV_SW_TOOLCHAIN = $(CV_SW_TOOLCHAIN)"
@echo " CV_SW_MARCH = $(CV_SW_MARCH)"
@echo " RISCV = $(RISCV)"
@echo " RISCV_EXE_PREFIX = $(RISCV_EXE_PREFIX)"
@echo " RISCV_GCC = $(RISCV_GCC)"
@echo " RISCV_MARCH = $(RISCV_MARCH)"

View file

@ -0,0 +1,103 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
COMMON_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
COMMON_SRCS = $(wildcard $(COMMON_DIR)/*.c)
INCS := -I$(COMMON_DIR)
TARGET := riscv32
ARCH := rv32g
RISCV_ABI := ilp32
RISCV_WARNINGS += -Wunused-variable -Wall -Wextra -Wno-unused-command-line-argument # -Werror
LLVM_FLAGS ?= --target=$(TARGET) -march=$(ARCH) -menable-experimental-extensions -mabi=$(RISCV_ABI) -mno-relax
RISCV_FLAGS ?= $(LLVM_FLAGS) -mcmodel=medany -O3 -ffast-math -g $(RISCV_WARNINGS)
RISCV_CCFLAGS ?= $(RISCV_FLAGS) -ffunction-sections -fdata-sections -std=gnu99 -nostdlib -nostartfiles
RISCV_CXXFLAGS ?= $(RISCV_FLAGS) -ffunction-sections -fdata-sections
RISCV_LDFLAGS ?= -static -nostdlib
LINKER_SCRIPT ?= $(COMMON_DIR)/link.ld
CFLAGS ?= -march=$(ARCH) -mabi=ilp32 -static -mcmodel=medany -Wall -g -O3\
-fvisibility=hidden -nostdlib -nostartfiles -ffreestanding $(PROGRAM_CFLAGS)
ifdef PROGRAM
PROGRAM_C := $(PROGRAM).c
endif
SRCS = $(COMMON_SRCS) $(PROGRAM_C) $(EXTRA_SRCS)
C_SRCS = $(filter %.c, $(SRCS))
ASM_SRCS = $(filter %.S, $(SRCS))
CC := $(LLVM_TOOLCHAIN)/clang
LD := $(LLVM_TOOLCHAIN)/riscv32-unknown-elf-ld
#LD := $(LLVM_TOOLCHAIN)/ld.lld
#LD := $(GCC_TOOLCHAIN)/riscv32-unknown-elf-gcc
#OBJCOPY := $(LLVM_TOOLCHAIN)/llvm-objcopy
OBJDUMP := $(GCC_TOOLCHAIN)/riscv32-unknown-elf-objdump
#OBJDUMP := $(LLVM_TOOLCHAIN)/llvm-objdump
CRT ?= $(COMMON_DIR)/crt0.S
OBJS := ${C_SRCS:.c=.o} ${ASM_SRCS:.S=.o} ${CRT:.S=.o}
DEPS = $(OBJS:%.o=%.d)
ifdef PROGRAM
OUTFILES := $(PROGRAM).elf
else
OUTFILES := $(OBJS)
endif
all: $(OUTFILES)
ifdef PROGRAM
$(PROGRAM).elf: $(OBJS) $(LINKER_SCRIPT)
# $(LD) $(CFLAGS) -T $(LINKER_SCRIPT) $(OBJS) -o $@ $(LIBS)
$(LD) $(RISCV_LDFLAGS) -Map $@.map -T $(LINKER_SCRIPT) $(OBJS) -o $@ $(LIBS)
$(OBJDUMP) -dh $@ >$@.headers
.PHONY: disassemble
disassemble: $(PROGRAM).dis
endif
%.dis: %.elf
$(OBJDUMP) -fhSD $^ > $@
# Note: this target requires the srecord package to be installed.
# XXX: This could be replaced by objcopy once
# https://sourceware.org/bugzilla/show_bug.cgi?id=19921
# is widely available.
%.vmem: %.bin
srec_cat $^ -binary -offset 0x0000 -byte-swap 4 -o $@ -vmem
%.bin: %.elf
$(OBJCOPY) -O binary $^ $@
%.o: %.c
$(CC) -c $(RISCV_CCFLAGS) $(INCS) -o $@ $<
# Rule to compile C to assembly
%.s: %.c
$(CC) -S $(RISCV_CCFLAGS) $(INCS) -o $@ $<
%.o: %.S
$(CC) -c $(RISCV_CCFLAGS) $(INCS) -o $@ $<
clean:
$(RM) -f $(OBJS) $(DEPS)
rm -f *.bin *.vmem *.elf *.headers
distclean: clean
$(RM) -f $(OUTFILES)

View file

@ -0,0 +1,84 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
COMMON_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
COMMON_SRCS = $(wildcard $(COMMON_DIR)/*.c)
INCS := -I$(COMMON_DIR)
# ARCH = rv32im # to disable compressed instructions
ARCH ?= rv32im_zicsr
ifdef PROGRAM
PROGRAM_C := $(PROGRAM).c
endif
SRCS = $(COMMON_SRCS) $(PROGRAM_C) $(EXTRA_SRCS)
C_SRCS = $(filter %.c, $(SRCS))
ASM_SRCS = $(filter %.S, $(SRCS))
CC = riscv32-unknown-elf-gcc
CROSS_COMPILE = $(patsubst %-gcc,%-,$(CC))
OBJCOPY ?= $(CROSS_COMPILE)objcopy
OBJDUMP ?= $(CROSS_COMPILE)objdump
LINKER_SCRIPT ?= $(COMMON_DIR)/link.ld
CRT ?= $(COMMON_DIR)/crt0.S
TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_PRINTF
TINY_PRINTF_FLAGS += -DTINYPRINTF_DEFINE_TFP_SPRINTF
CFLAGS ?= -march=$(ARCH) -mabi=ilp32 -static -mcmodel=medany -Wall -g -O3\
-fvisibility=hidden -nostdlib -nostartfiles -ffreestanding $(TINY_PRINTF_FLAGS) $(PROGRAM_CFLAGS)
OBJS := ${C_SRCS:.c=.o} ${ASM_SRCS:.S=.o} ${CRT:.S=.o}
DEPS = $(OBJS:%.o=%.d)
ifdef PROGRAM
OUTFILES := $(PROGRAM).elf
else
OUTFILES := $(OBJS)
endif
all: $(OUTFILES)
ifdef PROGRAM
$(PROGRAM).elf: $(OBJS) $(LINKER_SCRIPT)
$(CC) $(CFLAGS) -T $(LINKER_SCRIPT) $(OBJS) -o $@ $(LIBS)
$(OBJDUMP) -dh $@ >$@.headers
.PHONY: disassemble
disassemble: $(PROGRAM).dis
endif
%.dis: %.elf
$(OBJDUMP) -fhSD $^ > $@
# Note: this target requires the srecord package to be installed.
# XXX: This could be replaced by objcopy once
# https://sourceware.org/bugzilla/show_bug.cgi?id=19921
# is widely available.
%.vmem: %.bin
srec_cat $^ -binary -offset 0x0000 -byte-swap 4 -o $@ -vmem
%.bin: %.elf
$(OBJCOPY) -O binary $^ $@
%.o: %.c
$(CC) $(CFLAGS) -MMD -c $(INCS) -o $@ $<
# Rule to compile C to assembly
%.s: %.c
$(CC) $(CFLAGS) -S $(INCS) $< -o $@
%.o: %.S
$(CC) $(CFLAGS) -MMD -c $(INCS) -o $@ $<
clean:
$(RM) -f $(OBJS) $(DEPS)
rm -f *.bin *.vmem *.elf *.headers
distclean: clean
$(RM) -f $(OUTFILES)

View file

@ -0,0 +1,105 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
#include "simple_system_regs.h"
.section .text
default_exc_handler:
jal x0, simple_exc_handler
timer_handler:
jal x0, simple_timer_handler
reset_handler:
/* set all registers to zero */
mv x1, x0
mv x2, x1
mv x3, x1
mv x4, x1
mv x5, x1
mv x6, x1
mv x7, x1
mv x8, x1
mv x9, x1
mv x10, x1
mv x11, x1
mv x12, x1
mv x13, x1
mv x14, x1
mv x15, x1
mv x16, x1
mv x17, x1
mv x18, x1
mv x19, x1
mv x20, x1
mv x21, x1
mv x22, x1
mv x23, x1
mv x24, x1
mv x25, x1
mv x26, x1
mv x27, x1
mv x28, x1
mv x29, x1
mv x30, x1
mv x31, x1
/* stack initilization */
la x2, _stack_start
_start:
.global _start
/* clear BSS */
la x26, _bss_start
la x27, _bss_end
bge x26, x27, zero_loop_end
zero_loop:
sw x0, 0(x26)
addi x26, x26, 4
ble x26, x27, zero_loop
zero_loop_end:
main_entry:
/* jump to startup program entry */
// addi x10, x0, 0
// addi x11, x0, 0
jal x1, startup
/* Halt simulation */
li a1, 0
li a2, 0
li a3, 0
li a4, 0
li a5, 0
li a7, 93
ecall
/* If execution ends up here just put the core to sleep */
sleep_loop:
wfi
j sleep_loop
/* =================================================== [ exceptions ] === */
/* This section has to be down here, since we have to disable rvc for it */
.section .vectors, "ax"
.option norvc;
// All unimplemented interrupts/exceptions go to the default_exc_handler.
.org 0x00
.rept 7
jal x0, default_exc_handler
.endr
jal x0, timer_handler
.rept 23
jal x0, default_exc_handler
.endr
// reset vector
.org 0x80
jal x0, reset_handler

View file

@ -0,0 +1,90 @@
/* Copyright lowRISC contributors.
Licensed under the Apache License, Version 2.0, see LICENSE for details.
SPDX-License-Identifier: Apache-2.0 */
OUTPUT_ARCH(riscv)
MEMORY
{
/* Change this if you'd like different sizes. Arty A7-100(35) has a maximum of 607.5KB(225KB)
BRAM space. Configuration below is for maximum BRAM capacity with Artya A7-35 while letting
CoreMark run (.vmem of 152.8KB).
*/
ram : ORIGIN = 0x00100000, LENGTH = 0x30000 /* 192 kB */
stack : ORIGIN = 0x00130000, LENGTH = 0x8000 /* 32 kB */
}
/* Stack information variables */
_min_stack = 0x2000; /* 8K - minimum stack space to reserve */
_stack_len = LENGTH(stack);
_stack_start = ORIGIN(stack) + LENGTH(stack);
_entry_point = _vectors_start + 0x80;
ENTRY(_entry_point)
/* The tohost address is used by Spike for a magic "stop me now" message. This
is set to equal SIM_CTRL_CTRL (see simple_system_regs.h), which has that
effect in simple_system simulations. Note that it must be 8-byte aligned.
We don't read data back from Spike, so fromhost is set to some dummy value:
we place it just above the top of the stack.
*/
tohost = 0x20008;
fromhost = _stack_start + 0x10;
SECTIONS
{
.vectors :
{
. = ALIGN(4);
_vectors_start = .;
KEEP(*(.vectors))
_vectors_end = .;
} > ram
.text : {
. = ALIGN(4);
*(.text)
*(.text.*)
} > ram
.rodata : {
. = ALIGN(4);
/* Small RO data before large RO data */
*(.srodata)
*(.srodata.*)
*(.rodata);
*(.rodata.*)
} > ram
.data : {
. = ALIGN(4);
/* Small data before large data */
*(.sdata)
*(.sdata.*)
*(.data);
*(.data.*)
} > ram
.bss :
{
. = ALIGN(4);
_bss_start = .;
/* Small BSS before large BSS */
*(.sbss)
*(.sbss.*)
*(.bss)
*(.bss.*)
*(COMMON)
_bss_end = .;
} > ram
/* ensure there is enough room for stack */
.stack (NOLOAD): {
. = ALIGN(4);
. = . + _min_stack ;
. = ALIGN(4);
stack = . ;
_stack = . ;
} > stack
}

View file

@ -0,0 +1,191 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "simple_system_common.h"
#include "tinyprintf.h"
void puthex(uint32_t h) {
int cur_digit;
// Iterate through h taking top 4 bits each time and outputting ASCII of hex
// digit for those 4 bits
for (int i = 0; i < 8; i++) {
cur_digit = h >> 28;
if (cur_digit < 10)
putchar('0' + cur_digit);
else
putchar('A' - 10 + cur_digit);
h <<= 4;
}
}
#define MMADDR_EXIT SIM_CTRL_BASE + SIM_CTRL_CTRL
void sim_halt() { DEV_WRITE(MMADDR_EXIT, 1); }
void pcount_reset() {
asm volatile(
"csrw minstret, x0\n"
"csrw mcycle, x0\n"
"csrw mhpmcounter3, x0\n"
"csrw mhpmcounter4, x0\n"
"csrw mhpmcounter5, x0\n"
"csrw mhpmcounter6, x0\n"
"csrw mhpmcounter7, x0\n"
"csrw mhpmcounter8, x0\n"
"csrw mhpmcounter9, x0\n"
"csrw mhpmcounter10, x0\n"
"csrw mhpmcounter11, x0\n"
"csrw mhpmcounter12, x0\n"
"csrw mhpmcounter13, x0\n"
"csrw mhpmcounter14, x0\n"
"csrw mhpmcounter15, x0\n"
"csrw mhpmcounter16, x0\n"
"csrw mhpmcounter17, x0\n"
"csrw mhpmcounter18, x0\n"
"csrw mhpmcounter19, x0\n"
"csrw mhpmcounter20, x0\n"
"csrw mhpmcounter21, x0\n"
"csrw mhpmcounter22, x0\n"
"csrw mhpmcounter23, x0\n"
"csrw mhpmcounter24, x0\n"
"csrw mhpmcounter25, x0\n"
"csrw mhpmcounter26, x0\n"
"csrw mhpmcounter27, x0\n"
"csrw mhpmcounter28, x0\n"
"csrw mhpmcounter29, x0\n"
"csrw mhpmcounter30, x0\n"
"csrw mhpmcounter31, x0\n"
"csrw minstreth, x0\n"
"csrw mcycleh, x0\n"
"csrw mhpmcounter3h, x0\n"
"csrw mhpmcounter4h, x0\n"
"csrw mhpmcounter5h, x0\n"
"csrw mhpmcounter6h, x0\n"
"csrw mhpmcounter7h, x0\n"
"csrw mhpmcounter8h, x0\n"
"csrw mhpmcounter9h, x0\n"
"csrw mhpmcounter10h, x0\n"
"csrw mhpmcounter11h, x0\n"
"csrw mhpmcounter12h, x0\n"
"csrw mhpmcounter13h, x0\n"
"csrw mhpmcounter14h, x0\n"
"csrw mhpmcounter15h, x0\n"
"csrw mhpmcounter16h, x0\n"
"csrw mhpmcounter17h, x0\n"
"csrw mhpmcounter18h, x0\n"
"csrw mhpmcounter19h, x0\n"
"csrw mhpmcounter20h, x0\n"
"csrw mhpmcounter21h, x0\n"
"csrw mhpmcounter22h, x0\n"
"csrw mhpmcounter23h, x0\n"
"csrw mhpmcounter24h, x0\n"
"csrw mhpmcounter25h, x0\n"
"csrw mhpmcounter26h, x0\n"
"csrw mhpmcounter27h, x0\n"
"csrw mhpmcounter28h, x0\n"
"csrw mhpmcounter29h, x0\n"
"csrw mhpmcounter30h, x0\n"
"csrw mhpmcounter31h, x0\n");
}
unsigned int get_mepc() {
uint32_t result;
__asm__ volatile("csrr %0, mepc;" : "=r"(result));
return result;
}
unsigned int get_mcause() {
uint32_t result;
__asm__ volatile("csrr %0, mcause;" : "=r"(result));
return result;
}
unsigned int get_mtval() {
uint32_t result;
__asm__ volatile("csrr %0, mtval;" : "=r"(result));
return result;
}
void simple_exc_handler(void) {
#if 0
volatile register int a7 asm("a7");
// Check if A7 equals 93
//https://jborza.com/post/2021-05-11-riscv-linux-syscalls/
if (a7 == 93) {
#else
int result;
asm volatile ("mv %0, a7" : "=r"(result));
// Check if A7 equals 93
//https://jborza.com/post/2021-05-11-riscv-linux-syscalls/
if (result == 93) {
#endif
printf("exit()\n");
printf("======\n");
}else{
printf("EXCEPTION!!!\n");
printf("============\n");
printf("MEPC: 0x");
puthex(get_mepc());
printf("\nMCAUSE: 0x");
puthex(get_mcause());
printf("\nMTVAL: 0x");
puthex(get_mtval());
putchar('\n');
}
sim_halt();
while(1);
}
volatile uint64_t time_elapsed;
uint64_t time_increment;
inline static void increment_timecmp(uint64_t time_base) {
uint64_t current_time = timer_read();
current_time += time_base;
timecmp_update(current_time);
}
void timer_enable(uint64_t time_base) {
time_elapsed = 0;
time_increment = time_base;
// Set timer values
increment_timecmp(time_base);
// enable timer interrupt
asm volatile("csrs mie, %0\n" : : "r"(0x80));
// enable global interrupt
asm volatile("csrs mstatus, %0\n" : : "r"(0x8));
}
void timer_disable(void) { asm volatile("csrc mie, %0\n" : : "r"(0x80)); }
uint64_t timer_read(void) {
uint32_t current_timeh;
uint32_t current_time;
// check if time overflowed while reading and try again
do {
current_timeh = DEV_READ(TIMER_BASE + TIMER_MTIMEH, 0);
current_time = DEV_READ(TIMER_BASE + TIMER_MTIME, 0);
} while (current_timeh != DEV_READ(TIMER_BASE + TIMER_MTIMEH, 0));
uint64_t final_time = ((uint64_t)current_timeh << 32) | current_time;
return final_time;
}
void timecmp_update(uint64_t new_time) {
DEV_WRITE(TIMER_BASE + TIMER_MTIMECMP, -1);
DEV_WRITE(TIMER_BASE + TIMER_MTIMECMPH, new_time >> 32);
DEV_WRITE(TIMER_BASE + TIMER_MTIMECMP, new_time);
}
uint64_t get_elapsed_time(void) { return time_elapsed; }
void simple_timer_handler(void) __attribute__((interrupt));
void simple_timer_handler(void) {
increment_timecmp(time_increment);
time_elapsed++;
}

View file

@ -0,0 +1,116 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#ifndef SIMPLE_SYSTEM_COMMON_H__
#include <stdint.h>
#include "simple_system_regs.h"
#define DEV_WRITE(addr, val) (*((volatile uint32_t *)(addr)) = val)
#define DEV_READ(addr, val) (*((volatile uint32_t *)(addr)))
#define PCOUNT_READ(name, dst) asm volatile("csrr %0, " #name ";" : "=r"(dst))
extern void _putcf (void * unused, char c);
/**
* Writes character to simulator out log. Signature matches c stdlib function
* of the same name.
*
* @param c Character to output
* @returns Character output (never fails so no EOF ever returned)
*/
int putchar(char c);
/**
* Writes string to simulator out log. Signature matches c stdlib function of
* the same name.
*
* @param str String to output
* @returns 0 always (never fails so no error)
*/
int puts(const char *str);
/**
* Writes ASCII hex representation of number to simulator out log.
*
* @param h Number to output in hex
*/
void puthex(uint32_t h);
/**
* Immediately halts the simulation
*/
void sim_halt();
/**
* Enables/disables performance counters. This effects mcycle and minstret as
* well as the mhpmcounterN counters.
*
* @param enable if non-zero enables, otherwise disables
*/
static inline void pcount_enable(int enable) {
// Note cycle is disabled with everything else
unsigned int inhibit_val = enable ? 0x0 : 0xFFFFFFFF;
// CSR 0x320 was called `mucounteren` in the privileged spec v1.9.1, it was
// then dropped in v1.10, and then re-added in v1.11 with the name
// `mcountinhibit`. Unfortunately, the version of binutils we use only allows
// the old name, and LLVM only supports the new name (though this is changed
// on trunk to support both), so we use the numeric value here for maximum
// compatibility.
asm volatile("csrw 0x320, %0\n" : : "r"(inhibit_val));
}
/**
* Resets all performance counters. This effects mcycle and minstret as well
* as the mhpmcounterN counters.
*/
void pcount_reset();
/**
* Enables timer interrupt
*
* @param time_base Number of time ticks to count before interrupt
*/
void timer_enable(uint64_t time_base);
/**
* Returns current mtime value
*/
uint64_t timer_read(void);
/**
* Set a new timer value
*
* @param new_time New value for time
*/
void timecmp_update(uint64_t new_time);
/**
* Disables timer interrupt
*/
void timer_disable(void);
/**
* Returns current global time value
*/
uint64_t get_elapsed_time(void);
/**
* Enables/disables the instruction cache. This has no effect on Ibex
* configurations that do not have an instruction cache and in particular is
* safe to execute on those configurations.
*
* @param enable if non-zero enables, otherwise disables
*/
static inline void icache_enable(int enable) {
if (enable) {
// Set icache enable bit in CPUCTRLSTS
asm volatile("csrs 0x7c0, 1");
} else {
// Clear icache enable bit in CPUCTRLSTS
asm volatile("csrc 0x7c0, 1");
}
}
#endif

View file

@ -0,0 +1,18 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#ifndef SIMPLE_SYSTEM_REGS_H__
#define SIMPLE_SYSTEM_REGS_H__
#define SIM_CTRL_BASE 0x20000
#define SIM_CTRL_OUT 0x0
#define SIM_CTRL_CTRL 0x8
#define TIMER_BASE 0x30000
#define TIMER_MTIME 0x0
#define TIMER_MTIMEH 0x4
#define TIMER_MTIMECMP 0x8
#define TIMER_MTIMECMPH 0xC
#endif // SIMPLE_SYSTEM_REGS_H__

View file

@ -0,0 +1,28 @@
#define TINYPRINTF_DEFINE_TFP_PRINTF 1
#define TINYPRINTF_DEFINE_TFP_SPRINTF 1
#include "tinyprintf.h"
#include "simple_system_common.h"
extern int main(int argc, char *argv[]);
void _putcf (void * unused, char c) {
(int)unused;
DEV_WRITE(SIM_CTRL_BASE + SIM_CTRL_OUT, (unsigned char)c);
}
int putchar(char c){
_putcf (0, c);
return 1;
}
void startup(){
init_printf(0, _putcf);
main(0, 0);
}

View file

@ -0,0 +1,521 @@
/*
File: tinyprintf.c
Copyright (C) 2004 Kustaa Nyholm
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "tinyprintf.h"
/*
* Configuration
*/
/* Enable long int support */
#define PRINTF_LONG_SUPPORT
/* Enable long long int support (implies long int support) */
//#define PRINTF_LONG_LONG_SUPPORT
/* Enable %z (size_t) support */
#define PRINTF_SIZE_T_SUPPORT
/*
* Configuration adjustments
*/
#ifdef PRINTF_SIZE_T_SUPPORT
#include <sys/types.h>
#endif
#ifdef PRINTF_LONG_LONG_SUPPORT
# define PRINTF_LONG_SUPPORT
#endif
/* __SIZEOF_<type>__ defined at least by gcc */
#ifdef __SIZEOF_POINTER__
# define SIZEOF_POINTER __SIZEOF_POINTER__
#endif
#ifdef __SIZEOF_LONG_LONG__
# define SIZEOF_LONG_LONG __SIZEOF_LONG_LONG__
#endif
#ifdef __SIZEOF_LONG__
# define SIZEOF_LONG __SIZEOF_LONG__
#endif
#ifdef __SIZEOF_INT__
# define SIZEOF_INT __SIZEOF_INT__
#endif
#ifdef __GNUC__
# define _TFP_GCC_NO_INLINE_ __attribute__ ((noinline))
#else
# define _TFP_GCC_NO_INLINE_
#endif
/*
* Implementation
*/
struct param {
char lz:1; /**< Leading zeros */
char alt:1; /**< alternate form */
char uc:1; /**< Upper case (for base16 only) */
char align_left:1; /**< 0 == align right (default), 1 == align left */
unsigned int width; /**< field width */
char sign; /**< The sign to display (if any) */
unsigned int base; /**< number base (e.g.: 8, 10, 16) */
char *bf; /**< Buffer to output */
};
#ifdef PRINTF_LONG_LONG_SUPPORT
static void _TFP_GCC_NO_INLINE_ ulli2a(
unsigned long long int num, struct param *p)
{
int n = 0;
unsigned long long int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void lli2a(long long int num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
ulli2a(num, p);
}
#endif
#ifdef PRINTF_LONG_SUPPORT
static void uli2a(unsigned long int num, struct param *p)
{
int n = 0;
unsigned long int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void li2a(long num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
uli2a(num, p);
}
#endif
static void ui2a(unsigned int num, struct param *p)
{
int n = 0;
unsigned int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void i2a(int num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
ui2a(num, p);
}
static int a2d(char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
else
return -1;
}
static char a2u(char ch, const char **src, int base, unsigned int *nump)
{
const char *p = *src;
unsigned int num = 0;
int digit;
while ((digit = a2d(ch)) >= 0) {
if (digit > base)
break;
num = num * base + digit;
ch = *p++;
}
*src = p;
*nump = num;
return ch;
}
static void putchw(void *putp, putcf putf, struct param *p)
{
char ch;
int n = p->width;
char *bf = p->bf;
/* Number of filling characters */
while (*bf++ && n > 0)
n--;
if (p->sign)
n--;
if (p->alt && p->base == 16)
n -= 2;
else if (p->alt && p->base == 8)
n--;
/* Fill with space to align to the right, before alternate or sign */
if (!p->lz && !p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
/* print sign */
if (p->sign)
putf(putp, p->sign);
/* Alternate */
if (p->alt && p->base == 16) {
putf(putp, '0');
putf(putp, (p->uc ? 'X' : 'x'));
} else if (p->alt && p->base == 8) {
putf(putp, '0');
}
/* Fill with zeros, after alternate or sign */
if (p->lz) {
while (n-- > 0)
putf(putp, '0');
}
/* Put actual buffer */
bf = p->bf;
while ((ch = *bf++))
putf(putp, ch);
/* Fill with space to align to the left, after string */
if (!p->lz && p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
}
void tfp_format(void *putp, putcf putf, const char *fmt, va_list va)
{
struct param p;
#ifdef PRINTF_LONG_SUPPORT
char bf[23]; /* long = 64b on some architectures */
#else
char bf[12]; /* int = 32b on some architectures */
#endif
char ch;
p.bf = bf;
while ((ch = *(fmt++))) {
if (ch != '%') {
putf(putp, ch);
} else {
#ifdef PRINTF_LONG_SUPPORT
char lng = 0; /* 1 for long, 2 for long long */
#endif
/* Init parameter struct */
p.lz = 0;
p.alt = 0;
p.width = 0;
p.align_left = 0;
p.sign = 0;
/* Flags */
while ((ch = *(fmt++))) {
switch (ch) {
case '-':
p.align_left = 1;
continue;
case '0':
p.lz = 1;
continue;
case '#':
p.alt = 1;
continue;
default:
break;
}
break;
}
/* Width */
if (ch >= '0' && ch <= '9') {
ch = a2u(ch, &fmt, 10, &(p.width));
}
/* We accept 'x.y' format but don't support it completely:
* we ignore the 'y' digit => this ignores 0-fill
* size and makes it == width (ie. 'x') */
if (ch == '.') {
p.lz = 1; /* zero-padding */
/* ignore actual 0-fill size: */
do {
ch = *(fmt++);
} while ((ch >= '0') && (ch <= '9'));
}
#ifdef PRINTF_SIZE_T_SUPPORT
# ifdef PRINTF_LONG_SUPPORT
if (ch == 'z') {
ch = *(fmt++);
if (sizeof(size_t) == sizeof(unsigned long int))
lng = 1;
# ifdef PRINTF_LONG_LONG_SUPPORT
else if (sizeof(size_t) == sizeof(unsigned long long int))
lng = 2;
# endif
} else
# endif
#endif
#ifdef PRINTF_LONG_SUPPORT
if (ch == 'l') {
ch = *(fmt++);
lng = 1;
#ifdef PRINTF_LONG_LONG_SUPPORT
if (ch == 'l') {
ch = *(fmt++);
lng = 2;
}
#endif
}
#endif
switch (ch) {
case 0:
goto abort;
case 'u':
p.base = 10;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
ulli2a(va_arg(va, unsigned long long int), &p);
else
#endif
if (1 == lng)
uli2a(va_arg(va, unsigned long int), &p);
else
#endif
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'd':
case 'i':
p.base = 10;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
lli2a(va_arg(va, long long int), &p);
else
#endif
if (1 == lng)
li2a(va_arg(va, long int), &p);
else
#endif
i2a(va_arg(va, int), &p);
putchw(putp, putf, &p);
break;
#ifdef SIZEOF_POINTER
case 'p':
p.alt = 1;
# if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT
lng = 0;
# elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG
lng = 1;
# elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG
lng = 2;
# endif
#endif
case 'x':
case 'X':
p.base = 16;
p.uc = (ch == 'X')?1:0;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
ulli2a(va_arg(va, unsigned long long int), &p);
else
#endif
if (1 == lng)
uli2a(va_arg(va, unsigned long int), &p);
else
#endif
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'o':
p.base = 8;
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'c':
putf(putp, (char)(va_arg(va, int)));
break;
case 's':
p.bf = va_arg(va, char *);
putchw(putp, putf, &p);
p.bf = bf;
break;
case '%':
putf(putp, ch);
default:
break;
}
}
}
abort:;
}
#if TINYPRINTF_DEFINE_TFP_PRINTF
static putcf stdout_putf;
static void *stdout_putp;
void init_printf(void *putp, putcf putf)
{
stdout_putf = putf;
stdout_putp = putp;
}
void tfp_printf(char *fmt, ...)
{
va_list va;
va_start(va, fmt);
tfp_format(stdout_putp, stdout_putf, fmt, va);
va_end(va);
}
#endif
#if TINYPRINTF_DEFINE_TFP_SPRINTF
struct _vsnprintf_putcf_data
{
size_t dest_capacity;
char *dest;
size_t num_chars;
};
static void _vsnprintf_putcf(void *p, char c)
{
struct _vsnprintf_putcf_data *data = (struct _vsnprintf_putcf_data*)p;
if (data->num_chars < data->dest_capacity)
data->dest[data->num_chars] = c;
data->num_chars ++;
}
int tfp_vsnprintf(char *str, size_t size, const char *format, va_list ap)
{
struct _vsnprintf_putcf_data data;
if (size < 1)
return 0;
data.dest = str;
data.dest_capacity = size-1;
data.num_chars = 0;
tfp_format(&data, _vsnprintf_putcf, format, ap);
if (data.num_chars < data.dest_capacity)
data.dest[data.num_chars] = '\0';
else
data.dest[data.dest_capacity] = '\0';
return data.num_chars;
}
int tfp_snprintf(char *str, size_t size, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = tfp_vsnprintf(str, size, format, ap);
va_end(ap);
return retval;
}
struct _vsprintf_putcf_data
{
char *dest;
size_t num_chars;
};
static void _vsprintf_putcf(void *p, char c)
{
struct _vsprintf_putcf_data *data = (struct _vsprintf_putcf_data*)p;
data->dest[data->num_chars++] = c;
}
int tfp_vsprintf(char *str, const char *format, va_list ap)
{
struct _vsprintf_putcf_data data;
data.dest = str;
data.num_chars = 0;
tfp_format(&data, _vsprintf_putcf, format, ap);
data.dest[data.num_chars] = '\0';
return data.num_chars;
}
int tfp_sprintf(char *str, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = tfp_vsprintf(str, format, ap);
va_end(ap);
return retval;
}
#endif

View file

@ -0,0 +1,186 @@
/*
File: tinyprintf.h
Copyright (C) 2004 Kustaa Nyholm
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
This library is really just two files: 'tinyprintf.h' and 'tinyprintf.c'.
They provide a simple and small (+400 loc) printf functionality to
be used in embedded systems.
I've found them so useful in debugging that I do not bother with a
debugger at all.
They are distributed in source form, so to use them, just compile them
into your project.
Two printf variants are provided: printf and the 'sprintf' family of
functions ('snprintf', 'sprintf', 'vsnprintf', 'vsprintf').
The formats supported by this implementation are:
'c' 'd' 'i' 'o' 'p' 'u' 's' 'x' 'X'.
Zero padding and field width are also supported.
If the library is compiled with 'PRINTF_SUPPORT_LONG' defined, then
the long specifier is also supported. Note that this will pull in some
long math routines (pun intended!) and thus make your executable
noticeably longer. Likewise with 'PRINTF_LONG_LONG_SUPPORT' for the
long long specifier, and with 'PRINTF_SIZE_T_SUPPORT' for the size_t
specifier.
The memory footprint of course depends on the target CPU, compiler and
compiler options, but a rough guesstimate (based on a H8S target) is about
1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space.
Not too bad. Your mileage may vary. By hacking the source code you can
get rid of some hundred bytes, I'm sure, but personally I feel the balance of
functionality and flexibility versus code size is close to optimal for
many embedded systems.
To use the printf, you need to supply your own character output function,
something like :
void putc ( void* p, char c)
{
while (!SERIAL_PORT_EMPTY) ;
SERIAL_PORT_TX_REGISTER = c;
}
Before you can call printf, you need to initialize it to use your
character output function with something like:
init_printf(NULL,putc);
Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc',
the NULL (or any pointer) you pass into the 'init_printf' will eventually be
passed to your 'putc' routine. This allows you to pass some storage space (or
anything really) to the character output function, if necessary.
This is not often needed but it was implemented like that because it made
implementing the sprintf function so neat (look at the source code).
The code is re-entrant, except for the 'init_printf' function, so it is safe
to call it from interrupts too, although this may result in mixed output.
If you rely on re-entrancy, take care that your 'putc' function is re-entrant!
The printf and sprintf functions are actually macros that translate to
'tfp_printf' and 'tfp_sprintf' when 'TINYPRINTF_OVERRIDE_LIBC' is set
(default). Setting it to 0 makes it possible to use them along with
'stdio.h' printf's in a single source file. When
'TINYPRINTF_OVERRIDE_LIBC' is set, please note that printf/sprintf are
not function-like macros, so if you have variables or struct members
with these names, things will explode in your face. Without variadic
macros this is the best we can do to wrap these function. If it is a
problem, just give up the macros and use the functions directly, or
rename them.
It is also possible to avoid defining tfp_printf and/or tfp_sprintf by
clearing 'TINYPRINTF_DEFINE_TFP_PRINTF' and/or
'TINYPRINTF_DEFINE_TFP_SPRINTF' to 0. This allows for example to
export only tfp_format, which is at the core of all the other
functions.
For further details see source code.
regs Kusti, 23.10.2004
*/
#ifndef __TFP_PRINTF__
#define __TFP_PRINTF__
#include <stdarg.h>
/* Global configuration */
/* Set this to 0 if you do not want to provide tfp_printf */
#ifndef TINYPRINTF_DEFINE_TFP_PRINTF
# define TINYPRINTF_DEFINE_TFP_PRINTF 1
#endif
/* Set this to 0 if you do not want to provide
tfp_sprintf/snprintf/vsprintf/vsnprintf */
#ifndef TINYPRINTF_DEFINE_TFP_SPRINTF
# define TINYPRINTF_DEFINE_TFP_SPRINTF 1
#endif
/* Set this to 0 if you do not want tfp_printf and
tfp_{vsn,sn,vs,s}printf to be also available as
printf/{vsn,sn,vs,s}printf */
#ifndef TINYPRINTF_OVERRIDE_LIBC
# define TINYPRINTF_OVERRIDE_LIBC 1
#endif
/* Optional external types dependencies */
#if TINYPRINTF_DEFINE_TFP_SPRINTF
# include <sys/types.h> /* size_t */
#endif
/* Declarations */
#ifdef __GNUC__
# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx) \
__attribute__((format (printf, fmt_idx, arg1_idx)))
#else
# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx)
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*putcf) (void *, char);
/*
'tfp_format' really is the central function for all tinyprintf. For
each output character after formatting, the 'putf' callback is
called with 2 args:
- an arbitrary void* 'putp' param defined by the user and
passed unmodified from 'tfp_format',
- the character.
The 'tfp_printf' and 'tfp_sprintf' functions simply define their own
callback and pass to it the right 'putp' it is expecting.
*/
void tfp_format(void *putp, putcf putf, const char *fmt, va_list va);
#if TINYPRINTF_DEFINE_TFP_SPRINTF
int tfp_vsnprintf(char *str, size_t size, const char *fmt, va_list ap);
int tfp_snprintf(char *str, size_t size, const char *fmt, ...) \
_TFP_SPECIFY_PRINTF_FMT(3, 4);
int tfp_vsprintf(char *str, const char *fmt, va_list ap);
int tfp_sprintf(char *str, const char *fmt, ...) \
_TFP_SPECIFY_PRINTF_FMT(2, 3);
# if TINYPRINTF_OVERRIDE_LIBC
# define vsnprintf tfp_vsnprintf
# define snprintf tfp_snprintf
# define vsprintf tfp_vsprintf
# define sprintf tfp_sprintf
# endif
#endif
#if TINYPRINTF_DEFINE_TFP_PRINTF
void init_printf(void *putp, putcf putf);
void tfp_printf(char *fmt, ...) _TFP_SPECIFY_PRINTF_FMT(1, 2);
# if TINYPRINTF_OVERRIDE_LIBC
# define printf tfp_printf
# endif
#endif
#ifdef __cplusplus
}
#endif
#endif

View file

@ -13,8 +13,8 @@ EXTRA_SRCS :=
# Check the COMPILER variable passed via the command line
ifeq ($(COMPILER), llvm)
include $(PROGRAM_DIR)/../common/common-llvm.mk
include $(PROGRAM_DIR)/../bsp/common-llvm.mk
else
include $(PROGRAM_DIR)/../common/common.mk
include $(PROGRAM_DIR)/../bsp/common.mk
endif

View file

@ -14,12 +14,13 @@
#include "simple_system_common.h"
#include "tinyprintf.h"
int main(int argc, char **argv) {
puts("Hello test instr\n");
printf("Hello test instr\n");
unsigned int enc_instr64 = 0xBEAD0000; // Example value
unsigned int dummy = 0xBADCAFE;