-
bricked my Red-V board
11/12/2024 at 16:05 • 0 commentsI appear to have bricked my Red-V board.
I can upload the firmware but normally I should see a blue LED flash and this is not happening anymore.I was trying to fix an issue that appears that data was not copied correctly from FLASH MEMORY to .data in RAM. I modified the linker file and I somehow think that overwritten some flash copy function that was originally in the board.
Unless I wrote too many times to flash.
-
Introducing sleep timer
11/06/2024 at 20:28 • 0 commentsWe use MTIME that is a 64 bit counter that runs at a frequency of 32768 Khz
MTIME in intended to count machine time, unlike RTC time which also exists in on the board but is more intended for measuring seconds and used as OS time.
# CLINT memory map ====================================================
.equ MTIME_FREQUENCY, 32768 .equ MTIME_LOW_ADDR, 0x0200bff8 # MTIME LOW address .equ MTIME_HIGH_ADDR, 0x0200bffc # MTIME HIGH address
To measure in, milliseconds we split the high and low count
.equ ms_to_cycles_int, 32 # Integer part of 32.768 cycles per millisecond .equ ms_to_cycles_rem, 768 # Fractional remainder as parts per 1000
We can now create a function where a0 is the time to sleep in milliseconds
.globl _sleep_time _sleep_time: # Save a0 on the stack (assuming it is the delay in milliseconds) PUSH a0 # Convert milliseconds to RTC cycles li t0, 32 # Load the integer part of cycles per millisecond (32) mul t0, t0, a0 # Multiply by the number of milliseconds (a0) li t1, 768 # Load the remainder part (768) mul t1, t1, a0 # Multiply remainder by the number of milliseconds li t2, 1000 # Load the scaling factor (1000) div t1, t1, t2 # Divide to get the fractional part in cycles (as an integer) add t0, t0, t1 # Add both the integer and fractional parts # Load mtime base addresses for the Red-V board li t1, MTIME_LOW_ADDR # Load address of mtime low into t1 li t2, MTIME_HIGH_ADDR # Load address of mtime high into t2 # Get the current mtime (with consistency check) 1: lw t3, 0(t1) # Load mtime low lw t4, 0(t2) # Load mtime high lw t5, 0(t1) # Re-read mtime low for consistency bne t3, t5, 1b # Retry if inconsistent # Set target time add t6, t3, t0 # Set target time (low part) # Poll until mtime reaches the target value 2: lw t3, 0(t1) # Reload current mtime low lw t4, 0(t2) # Reload current mtime high bge t3, t6, 3f # Exit loop if current time >= target (low part) j 2b # Keep looping 3: POP a0 ret
We now create a macro
.macro SLEEP_MS ms PUSH a0 # Load the precomputed cycle delay based on the input milliseconds li a0, \ms jal _sleep_time POP a0 .endm
And can be used like this
LOG_INFO STRING_TIMING_START_MSG SLEEP_MS 3000 LOG_INFO STRING_TIMING_END_MSG
-
Dealing with macro's duplicates
11/02/2024 at 20:44 • 0 commentsThe compiled expects this syntax to prevent duplicate definitions of macro's
.ifndef TRACE_MACROS_S_INCLUDED TRACE_MACROS_S_INCLUDED = 1 ... .endif
Example
.ifndef TRACE_MACROS_S_INCLUDED TRACE_MACROS_S_INCLUDED = 1 .include "./src/include/global_constants.s" .align 2 .section .rodata STRINGS_TRACE_START_MSG: .asciz "[TRACE] " .section .text .align 2 # =============================================================== # TRACE message_ptr # =============================================================== .if TRACE_LEVEL > 0 .macro TRACE message_ptr la a0, \message_ptr jal puts la a0, CRLF jal puts .endm .else .macro TRACE message_ptr # No operation when TRACE is disabled .endm .endif .endif
-
Compiled date-time into the binary code
11/02/2024 at 19:32 • 5 commentsA 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
-
Creating debug/trace and logging macros
10/27/2024 at 17:37 • 0 commentsWe need some kind of debugging/logging/tracing when we build our OS
So we need to define in constants.cs we now defined logging levels
# Debugging Levels ===================================================== # Define constants for debug levels .set DEBUG_LEVEL_RELEASE, 0 .set DEBUG_LEVEL_NORMAL, 1 .set DEBUG_LEVEL_VERBOSE, 2 # Set default DEBUG_LEVEL if not defined .ifndef DEBUG_LEVEL .set DEBUG_LEVEL, DEBUG_LEVEL_NORMAL .endif # Tracing Levels ======================================================= .set TRACE_LEVEL_NO_TRACING, 0 .set TRACE_LEVEL_TRACING, 1 # Set default TRACE_LEVEL if not defined .ifndef TRACE_LEVEL .set TRACE_LEVEL, TRACE_LEVEL_TRACING .endif # Logging Levels ======================================================= .set LOG_LEVEL_NONE, 0 .set LOG_LEVEL_INFO, 1 .set LOG_LEVEL_WARN, 2 .set LOG_LEVEL_ERROR, 3 .set LOG_LEVEL_DEBUG, 4 .set LOG_LEVEL_TRACE, 5 # Set default LOG_LEVEL if not defined .ifndef LOG_LEVEL .set LOG_LEVEL, LOG_LEVEL_INFO .endif
Then we define the debug macro.
(I think we need to do some push and pop of registers in the future but for now this is enough.)
# debug-macros.s .include "./src/include/constants.s" # =============================================================== # DEBUG message_ptr # =============================================================== .if DEBUG_LEVEL > 0 .macro DEBUG message_ptr la a0, STRINGS_DEBUG_START_MSG jal puts la a0, \message_ptr jal puts la a0, CRLF jal puts .endm .else .macro DEBUG message_ptr # No operation when DEBUG is disabled .endm .endif
And we are creating logging macros:
(I think we need to do some push and pop of registers in the future but for now this is enough.)
# ----------------------------------------------------------------------- # Generic LOG macro # ----------------------------------------------------------------------- .macro LOG message_ptr, level # Only log if the current LOG_LEVEL is greater than or equal to the specified level .if LOG_LEVEL >= \level la a0, STRINGS_LOG_START_MSG jal puts la a0, \message_ptr jal puts la a0, CRLF jal puts .endif .endm # =============================================================== # Specific log level macros for convenience # =============================================================== # LOG_INFO message_ptr .macro LOG_INFO message_ptr .if LOG_LEVEL >= LOG_LEVEL_INFO LOG \message_ptr, LOG_LEVEL_INFO .endif .endm # LOG_WARN message_ptr .macro LOG_WARN message_ptr .if LOG_LEVEL >= LOG_LEVEL_WARN LOG \message_ptr, LOG_LEVEL_WARN .endif .endm # LOG_ERROR message_ptr .macro LOG_ERROR message_ptr .if LOG_LEVEL >= LOG_LEVEL_ERROR LOG \message_ptr, LOG_LEVEL_ERROR .endif .endm
Now we can use it like this:
LOG_INFO VERSION_INFO LOG_INFO STRINGS_TEST_START_MSG LOG_INFO SEPARATOR_DOUBLE_LINE LOG_INFO STRINGS_TEST_STRCPY_MSG STRCPY scratch1024, STRING_TEST_MSG LOG_INFO scratch1024
-
Extending makefile for tracing, debug and log levels
10/27/2024 at 17:13 • 0 commentsImproving the make files, we now want to to compile with debug, trace and logging level.
make DEBUG_LEVEL=2 TRACE_LEVEL=1 LOG_LEVEL=5
Complete makefile
# Toolchain 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 # Flags ASFLAGS = -march=rv32g -mabi=ilp32 -static -mcmodel=medany \ -fvisibility=hidden -nostdlib -nostartfiles -I$(INCLUDE_DIR) LDFLAGS = -T sparkfun-red-v.ld # Add flag definitions to ASFLAGS ASFLAGS += -DDEBUG_LEVEL=$(DEBUG_LEVEL) -DTRACE_LEVEL=$(TRACE_LEVEL) -DLOG_LEVEL=$(LOG_LEVEL) # 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)/constants.s \ $(DATA_DIR)/data.s \ $(DATA_DIR)/rodata.s \ $(DATA_DIR)/bss.s \ $(wildcard $(UNIT_TESTS_DIR)/strings-module/*.s) \ $(wildcard $(MODULES_DIR)/debug-module/*.s) \ $(wildcard $(MODULES_DIR)/strings-module/*.s) \ $(wildcard $(MODULES_DIR)/info-module/*.s) \ $(BOOT_DIR)/boot.s \ $(SRC_DIR)/main.s # Object Files OBJ = $(patsubst $(SRC_DIR)/%.s,$(BUILD_DIR)/%.o,$(SRC)) # Targets ELF = $(BUILD_DIR)/main.elf HEX = $(BUILD_DIR)/main.hex .PHONY: all clean # 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 files to .o files $(BUILD_DIR)/%.o: $(SRC_DIR)/%.s @mkdir -p $(dir $@) $(CC) $(ASFLAGS) -c $< -o $@ # Clean up build artifacts clean: rm -rf $(BUILD_DIR)
-
PUSH and POP macro's
10/24/2024 at 18:00 • 0 commentsRISC-V has no push and pop instructions to my surprise, so we need a way to make it easier to push registers and pop again
# macros's # ----------------------------------------------------------------------- # ***PUSH reg # *** example PUSH t0 # ----------------------------------------------------------------------- .macro PUSH reg addi sp, sp, -4 sw \reg, 0(sp) .endm # ----------------------------------------------------------------------- # *** POP reg # *** example POP t0 # ----------------------------------------------------------------------- .macro POP reg lw \reg, 0(sp) addi sp, sp, 4 .endm # ----------------------------------------------------------------------- # *** PUSH2 reg1, reg2 # *** example PUSH2 t0 t1 # ----------------------------------------------------------------------- .macro PUSH2 reg1, reg2 addi sp, sp, -8 sw \reg1, 0(sp) sw \reg2, 4(sp) .endm # ----------------------------------------------------------------------- # *** POP2 reg1, reg2 # *** example POP2 t0 t1 # ----------------------------------------------------------------------- .macro POP2 reg1, reg2 lw \reg2, 4(sp) lw \reg1, 0(sp) addi sp, sp, 8 .endm ...
I am still figuring pout what would be what would be most intuitive to use.
... PUSH2 t0 t1 # do something POP2 t0 t1 # We write the registers in the same order as the push ... # OR ... PUSH2 t0 t1 # do something POP2 t1 t0 # We write the registers in the reverse order as the stack ...
Pop same order as the push For me this is the most intuitive way and you can immediately spot if you made a error when you choose the registers to restore.
... PUSH2 t0 t1 # do something POP2 t0 t1 # We write the registers in the same order as the push ...
We extend the macros so we can for example push and pop all the arguments
# ----------------------------------------------------------------------- # *** PUSH all arguments # ----------------------------------------------------------------------- .macro PUSH_ARG_ALL PUSH8 a0, a1, a2, a3, a4, a5, a6, a7 .endm # ----------------------------------------------------------------------- # *** POP all arguments # ----------------------------------------------------------------------- .macro POP_ARG_ALL POP8 a0, a1, a2, a3, a4, a5, a6, a7 .endm
Example usage.to save all argument registers a0..a7, but we can also do that for temp registers because it has its own macro.
PUSH_ARG_ALL # Your code here POP_ARG_ALL
-
Unit testing the strings macro's
10/24/2024 at 17:45 • 0 commentsTime to define some test data to test if our string macro's actually work as expected.
Unit tests will in the end not be part of the the release code, just for now until we know that the code actually does what we think it should do.
We define some Read-Only data constants.
# rodata.s .align 2 # Read only data .section .rodata VERSION_INFO: .string "SparkFun Red-V bare metal v26b.\n\r" CRLF: .string "\n\r" SEPARATOR_SINGLE_LINE: .string "------------------------------------------------------------\n\r" SEPARATOR_DOUBLE_LINE: .string "============================================================\n\r" .section .rodata.tests # String testing code STRINGS_TEST_START_MSG: .string "Starting strings test.\n\r" STRINGS_TEST_END_MSG: .string "Ended strings test.\n\r" STRINGS_TEST_STRCPY_MSG: .string "STRCPY test = " STRINGS_TEST_STRNCPY_MSG: .string "STRNCPY test = " STRINGS_TEST_FILL_BYTES_MSG: .string "FILL_BYTES test = " STRING_TEST_MSG: .string "rodata line string test data message.\n\r" .byte 0
In the strings-macros.s I also defined a PRINT macro statement that directly sends to the UART
The idea is to have macro's that makes the assembler code more human friendly, we are basically starting to create some kind of language from scratch.
# ----------------------------------------------------------------------- # print message # ----------------------------------------------------------------------- .macro PRINT message_ptr la a0, \message_ptr jal puts .endm
We need a bss.s that defines a memory block (1024 bytes) that we can use as scratch buffer (We do not yet have memory management)
# bss.s .align 2 # Uninitialized data .section .bss scratch1024: .skip 1024,0
We have now a system that can test the string macros from the previous post.
This becomes part of our logging in the future.
# strings-tests.s .align 2 .include "./src/data/data.s" .include "./src/data/rodata.s" .include "./src/data/bss.s" .include "./src/include/string-macros.s" .section .text .globl _strings_tests _strings_tests: PRINT STRINGS_TEST_START_MSG PRINT SEPARATOR_DOUBLE_LINE PRINT STRINGS_TEST_STRCPY_MSG STRCPY scratch1024, STRING_TEST_MSG PRINT scratch1024 PRINT STRINGS_TEST_STRNCPY_MSG STRNCPY scratch1024, STRING_TEST_MSG, 5 PRINT scratch1024 PRINT CRLF PRINT STRINGS_TEST_FILL_BYTES_MSG FILL_BYTES scratch1024, '*', 5 PRINT scratch1024 PRINT CRLF STRNCPY scratch1024, destmsg2, 10 PRINT scratch1024 PRINT CRLF FILL_BYTES scratch1024, '-', 2 PRINT scratch1024 PRINT CRLF STRNCPY scratch1024, destmsg2, 10 PRINT scratch1024 PRINT CRLF FILL_STRING scratch1024, '=', 10 PRINT scratch1024 PRINT CRLF PRINT SEPARATOR_SINGLE_LINE PRINT STRINGS_TEST_END_MSG ret
-
Creating string methods for logging
10/24/2024 at 17:33 • 0 commentsFor debugging we need a way to handle strings, so lets first build C like string functions.
I start with macro's, I wand the ability to create then inline when needed for speed and as a function call.
List contains a small sample of the string macro's.
Still in development I need to think how this will be used in my code.I can give you a clue: In this case chatGPT can create code that helps a lot, but you need to manually check every generated code line. We can optimize later on, for now we need something working.
# string-macros.s # ----------------------------------------------------------------------- # Copy string (ASCIIZ) # ----------------------------------------------------------------------- .macro STRCPY dest_string_ptr, src_string_ptr la a0, \dest_string_ptr # dest_string_ptr la a1, \src_string_ptr # src_string_ptr jal _strcpy .endm # ----------------------------------------------------------------------- # String copy with specific length # ----------------------------------------------------------------------- .macro STRNCPY dest_string_ptr, src_string_ptr, len la a0, \dest_string_ptr # dest_string_ptr la a1, \src_string_ptr # src_string_ptr li a2, \len # Load the specified length into a0 jal _strncpy .endm # ----------------------------------------------------------------------- # FILL BYTES # ----------------------------------------------------------------------- .macro FILL_BYTES dest_ptr, value, length la a0, \dest_ptr # Load destination address into t3 li a1, \value # Load value into t0 li a2, \length # Load length into t1 jal _fill_bytes .endm # ----------------------------------------------------------------------- # FILL BYTES STRING (ASCIIZ) # ----------------------------------------------------------------------- .macro FILL_STRING dest_ptr, value, length li t0, \value # Load the value to be repeated into t0 li t1, \length # Load the length into t1 la t3, \dest_ptr # Load the destination address into t3 fill_string_loop: sb t0, 0(t3) # Store the value into the destination address addi t3, t3, 1 # Increment the destination address addi t1, t1, -1 # Decrement the length bnez t1, fill_string_loop # If length is not zero, repeat the loop sb zero, 0(t3) # Store null character at the end .endm # ----------------------------------------------------------------------- # Get string length (ASCIIZ) # ----------------------------------------------------------------------- .macro STRLEN dest_reg, input_string_ptr la t0, \input_string_ptr li \dest_reg, 0 .loop_strlen: lb t1, 0(t0) beqz t1, .end_strlen # If t1 is zero (null-terminator), exit loop addi \dest_reg, \dest_reg, 1 addi t0, t0, 1 # Move to the next character j .loop_strlen .end_strlen: .endm
Accompanying code
# strings.s .section .text .text .globl _fill_bytes .globl _strcpy .globl _strncpy # ----------------------------------------------------------------------- # FILL BYTES # ----------------------------------------------------------------------- _fill_bytes: addi t0, a0, 0 # Load destination address into t0 addi t1, a1, 0 # Load value into t1 addi t2, a2, 0 # Load length into t2 fill_bytes_loop: sb t1, 0(t0) # Store byte from t0 to the destination address addi t0, t0, 1 # Increment destination address addi t2, t2, -1 # Decrement length bnez t2, fill_bytes_loop # If length is not zero, repeat the loop ret # ----------------------------------------------------------------------- # Copy string (ASCIIZ) # ----------------------------------------------------------------------- _strcpy: addi t0, a0, 0 # Load the destination string pointer to t0 addi t1, a1, 0 # Load the source string pointer to t1 .loop_strcpy: lbu t2, 0(t1) beqz t2, .end_strcpy # If t2 is zero (null-terminator), exit loop sb t2, 0(t0) # Copy the character to the destination addi t0, t0, 1 # Move to the next character in src addi t1, t1, 1 # Move to the next character in dest j .loop_strcpy .end_strcpy: sb zero, 0(t0) # Null-terminate the destination string ret # ----------------------------------------------------------------------- # String copy with specific length # ----------------------------------------------------------------------- _strncpy: addi t0, a0, 0 # Load the destination string pointer to t0 addi t1, a1, 0 # Load the source string pointer to t1 addi t2, a2, 0 # Load the specified length into t2 .loop_strncpy: beqz t2, .end_strncpy # If t2 is zero (length reached), exit loop lb t3, 0(t1) sb t3, 0(t0) # Copy the character to the destination addi t0, t0, 1 # Move to the next character in dest addi t1, t1, 1 # Move to the next character in src addi t2, t2, -1 # Decrement the length j .loop_strncpy .end_strncpy: sb zero, 0(t0) # Null-terminate the destination string ret
-
UART constants
10/23/2024 at 19:07 • 0 commentsPrevious post became too big but these are the constants.s file with the RED-V constants for the UART.
# constants.s .equ UART_REG_TXFIFO, 0 .equ UART_BASE, 0x10013000 .equ UART0_BASE, 0x10013000 .equ UART_TXDATA, UART0_BASE + 0x00 .equ UART_RXDATA, UART0_BASE + 0x04 .equ UART_TXCTRL, UART0_BASE + 0x08 .equ UART_RXCTRL, UART0_BASE + 0x0C .equ UART_IE, UART0_BASE + 0x10 .equ UART_IP, UART0_BASE + 0x14 .equ UART_DIV, UART0_BASE + 0x18 .equ GPIO_BASE, 0x10012000 .equ GPIO_IOF_EN, GPIO_BASE + 0x38 .equ GPIO_IOF_SEL, GPIO_BASE + 0x3C .equ IOF_UART0_RX, (1 << 16) # GPIO16 .equ IOF_UART0_TX, (1 << 17) # GPIO17 .equ IOF_UART0_MASK, IOF_UART0_RX | IOF_UART0_TX