Close

Introducing sleep timer

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/06/2024 at 20:280 Comments

We 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

Discussions