Close

Compiled date-time into the binary code

A project log for Sparkfun RED-V bare metal

I wanted to learn RISC-V, not on an emulator, but on a real bare metal RISC-V processor. Even though Sparkfun RED-V is no longer produced.

olaf-baeyensOlaf Baeyens 11/02/2024 at 19:325 Comments

A simple idea of to include the build date and time as information turned out to be a a struggle to get the toolchain and project files correct.

We needed to change the extension from ".s" to capital ".S" which goes through the C++ preprocess. Small s is pure assembler.

We now take the date-time of the build.

# -----------------------------------------------------------------------
# Build Date and Time
# -----------------------------------------------------------------------
# Get the current build date and time
BUILD_DATE := $(shell TZ=$(shell date +"%Z") date +"%Y-%m-%d_%H:%M:%S")

 We add these to the flags.

# -----------------------------------------------------------------------
# Compiler and Linker Flags
# -----------------------------------------------------------------------
ASFLAGS = -march=rv32g -mabi=ilp32 -static -mcmodel=medany \
          -fvisibility=hidden -nostdlib -nostartfiles -I$(INCLUDE_DIR)
ASFLAGS += -DDEBUG_LEVEL=$(DEBUG_LEVEL) -DTRACE_LEVEL=$(TRACE_LEVEL) -DLOG_LEVEL=$(LOG_LEVEL)
ASFLAGS += -DBUILD_DATE=\"$(BUILD_DATE)\"
ASFLAGS += -I$(INCLUDE_DIR)

I have an rodata.S file that will be C++ preprocessed from src to build, and later we link that generated build file into the compiled end result and ignore the one found in the src

# -----------------------------------------------------------------------
# Rule to Generate Build-Specific rodata.S
# -----------------------------------------------------------------------
$(BUILD_DIR)/data/rodata.S: $(DATA_DIR)/rodata.S
    @mkdir -p $(dir $@)          # Ensure the output directory exists
    @echo "Generating rodata.S with build date: $(BUILD_DATE)"
    sed 's/BUILD_DATE_PLACEHOLDER/$(BUILD_DATE)/' $< > $@ 

 Compilation should now take ".s" and ".S" files

# -----------------------------------------------------------------------
# Source Files
# -----------------------------------------------------------------------
GLOBAL_CONSTANTS_FILE = $(INCLUDE_DIR)/global_constants.S

SRC = $(GLOBAL_CONSTANTS_FILE) \
      $(INCLUDE_DIR)/global_constants.S \
      $(DATA_DIR)/data.S \
      $(BUILD_DIR)/data/rodata.S \
      $(DATA_DIR)/bss.S \
      $(wildcard $(UNIT_TESTS_DIR)/strings-module/*.[sS]) \
      $(wildcard $(MODULES_DIR)/debug-module/*.[sS]) \
      $(wildcard $(MODULES_DIR)/timing-module/*.[sS]) \
      $(wildcard $(MODULES_DIR)/strings-module/*.[sS]) \
      $(wildcard $(MODULES_DIR)/info-module/*.[sS]) \
      $(BOOT_DIR)/boot.S \
      $(SRC_DIR)/main.S

Output now send to the UART

ATE0--> Send Flag error: #255 #255 #255 #255 AT+BLEINIT=0--> Send Flag error: #255 #255 #255 #255 AT+CWMODE=0--> Send Flag error: #255 #255 #255 #255 
[LOG] SparkFun Red-V bare metal v1.0.
[LOG] Build Date: 2024-11-01_16:49:21
[LOG] 

The rodata.S
Note the BUILD_DATE_PLACEHOLDER  inside the string as defined in the make file

# rodata.S
.align 2
.section .rodata

.globl VERSION_INFO
VERSION_INFO:
    .asciz "SparkFun Red-V bare metal v1.0." 

.globl BUILD_DATE_STR
BUILD_DATE_STR:
    .asciz "Build Date: BUILD_DATE_PLACEHOLDER"

 Complete update make file

# Makefile for building assembly code for the SparkFun Red-V development board

# -----------------------------------------------------------------------
# Toolchain Configuration
# -----------------------------------------------------------------------
TOOLCHAIN_PREFIX = riscv64-unknown-elf-
CC       = $(TOOLCHAIN_PREFIX)gcc
OBJCOPY  = $(TOOLCHAIN_PREFIX)objcopy

# -----------------------------------------------------------------------
# Default Flag Values (can be overridden from the command line)
# -----------------------------------------------------------------------
DEBUG_LEVEL ?= 2
TRACE_LEVEL ?= 1
LOG_LEVEL   ?= 5

# -----------------------------------------------------------------------
# Build Date and Time
# -----------------------------------------------------------------------
# Get the current build date and time
BUILD_DATE := $(shell TZ=$(shell date +"%Z") date +"%Y-%m-%d_%H:%M:%S")

# -----------------------------------------------------------------------
# Compiler and Linker Flags
# -----------------------------------------------------------------------
ASFLAGS = -march=rv32g -mabi=ilp32 -static -mcmodel=medany \
          -fvisibility=hidden -nostdlib -nostartfiles -I$(INCLUDE_DIR)
ASFLAGS += -DDEBUG_LEVEL=$(DEBUG_LEVEL) -DTRACE_LEVEL=$(TRACE_LEVEL) -DLOG_LEVEL=$(LOG_LEVEL)
ASFLAGS += -DBUILD_DATE=\"$(BUILD_DATE)\"
ASFLAGS += -I$(INCLUDE_DIR)

# Remove dependency generation flags
# ASFLAGS += -MD -MP

LDFLAGS = -T sparkfun-red-v.ld

# -----------------------------------------------------------------------
# Directories
# -----------------------------------------------------------------------
SRC_DIR          = src
BOOT_DIR         = $(SRC_DIR)/boot
INCLUDE_DIR      = $(SRC_DIR)/include
DATA_DIR         = $(SRC_DIR)/data
UNIT_TESTS_DIR   = $(SRC_DIR)/unit-tests
MODULES_DIR      = $(SRC_DIR)/modules
BUILD_DIR        = build

# -----------------------------------------------------------------------
# Source Files
# -----------------------------------------------------------------------
GLOBAL_CONSTANTS_FILE = $(INCLUDE_DIR)/global_constants.S

SRC = $(GLOBAL_CONSTANTS_FILE) \
      $(INCLUDE_DIR)/global_constants.S \
      $(DATA_DIR)/data.S \
      $(BUILD_DIR)/data/rodata.S \
      $(DATA_DIR)/bss.S \
      $(wildcard $(UNIT_TESTS_DIR)/strings-module/*.[sS]) \
      $(wildcard $(MODULES_DIR)/debug-module/*.[sS]) \
      $(wildcard $(MODULES_DIR)/timing-module/*.[sS]) \
      $(wildcard $(MODULES_DIR)/strings-module/*.[sS]) \
      $(wildcard $(MODULES_DIR)/info-module/*.[sS]) \
      $(BOOT_DIR)/boot.S \
      $(SRC_DIR)/main.S

# -----------------------------------------------------------------------
# Object Files
# -----------------------------------------------------------------------
OBJ = $(patsubst $(SRC_DIR)/%.[sS],$(BUILD_DIR)/%.o,$(SRC))

# -----------------------------------------------------------------------
# Targets
# -----------------------------------------------------------------------
ELF = $(BUILD_DIR)/main.elf
HEX = $(BUILD_DIR)/main.hex

.PHONY: all clean rebuild

# -----------------------------------------------------------------------
# Default Target
# -----------------------------------------------------------------------
all: $(BUILD_DIR) $(HEX)

# -----------------------------------------------------------------------
# Create Build Directory
# -----------------------------------------------------------------------
$(BUILD_DIR):
    @mkdir -p $(BUILD_DIR)

# -----------------------------------------------------------------------
# Rule to Build HEX File from ELF
# -----------------------------------------------------------------------
$(HEX): $(ELF)
    $(OBJCOPY) -O ihex $< $@

# -----------------------------------------------------------------------
# Rule to Build ELF from Object Files
# -----------------------------------------------------------------------
$(ELF): $(OBJ)
    $(CC) $(ASFLAGS) $(LDFLAGS) $^ -o $@

# -----------------------------------------------------------------------
# Rule to Compile .s and .S Files to .o Files
# -----------------------------------------------------------------------
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.[sS]
    @mkdir -p $(dir $@)
    $(CC) $(ASFLAGS) -c $< -o $@

# -----------------------------------------------------------------------
# Rule to Generate Build-Specific rodata.S
# -----------------------------------------------------------------------
$(BUILD_DIR)/data/rodata.S: $(DATA_DIR)/rodata.S
    @mkdir -p $(dir $@)          # Ensure the output directory exists
    @echo "Generating rodata.S with build date: $(BUILD_DATE)"
    sed 's/BUILD_DATE_PLACEHOLDER/$(BUILD_DATE)/' $< > $@ 

# Add similar dependency rules for other object files if necessary

# -----------------------------------------------------------------------
# Build Time File to Force Rebuild of Files that Depend on BUILD_DATE
# -----------------------------------------------------------------------
BUILD_TIME_FILE := $(BUILD_DIR)/build_time

.PHONY: $(BUILD_TIME_FILE)

$(BUILD_TIME_FILE):
    @echo $(BUILD_DATE) > $(BUILD_TIME_FILE)

# -----------------------------------------------------------------------
# Clean Up Build Artifacts
# -----------------------------------------------------------------------
clean:
    rm -rf $(BUILD_DIR)

# -----------------------------------------------------------------------
# Rebuild Target
# -----------------------------------------------------------------------
rebuild: clean all

Discussions

Olaf Baeyens wrote 11/03/2024 at 10:11 point

Thank you for your feedback.

The idea is that I want to avoid any usage of C, I want to write everything in as much as assembler as possible. Not constrained by C language.

What I forgot in the blog post is how I would use it:

.globl BUILD_DATE_STR
BUILD_DATE_STR:   .asciz "Build Date: BUILD_DATE_PLACEHOLDER"


I tried your suggestion __DATE__  but the compiler complaints with an error.

---

build/data/rodata.S: Assembler messages:
build/data/rodata.S:6: Error: junk at end of line, first unrecognized character is `_'
make: *** [Makefile:100: build/main.elf] Error 1

---

  Are you sure? yes | no

Ken Yap wrote 11/03/2024 at 12:32 point

But the point is that .S files go through cpp and those macros are predefined in cpp.

  Are you sure? yes | no

Olaf Baeyens wrote 11/04/2024 at 20:17 point

I can't seem to compile it, but in my project I have no cpp reference, no standard library whatsoever. Just bare metal.

  Are you sure? yes | no

Ken Yap wrote 11/04/2024 at 20:24 point

cpp stands for C PreProcessor which handles #defines, #includes and so forth, and is part of the gcc toolchain and is automatically invoked by gcc. But it's not limited to preprocessing C and can also preprocess other languages. You'd have to look at the cpp documentation to see how to invoke it (safely) on assembler. On my system man cpp works.

  Are you sure? yes | no

Ken Yap wrote 11/02/2024 at 20:43 point

Couldn't  you use the predefined macros __DATE__ and __TIME__ in your rodata source?

  Are you sure? yes | no