Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a535df957d | |||
| 10a8be72e3 | |||
| 4785d506fc | |||
| 48eccb5e24 | |||
| bd34c7ec1e | |||
| 2b17bb4ff9 | |||
| 032a06fdef | |||
| 8aee0b104c | |||
| 49cabf2077 | |||
| ef1854cfa0 | |||
| a40d2916df | |||
| d589bb8c41 | |||
| f4973842ad | |||
| 62fb0f8849 |
@@ -1,18 +1,18 @@
|
|||||||
*.aux
|
.stem/
|
||||||
*.log
|
|
||||||
*.out
|
|
||||||
*.fls
|
|
||||||
*.fdb_latexmk
|
|
||||||
*.synctex.gz
|
|
||||||
*.pdf
|
|
||||||
build-meta.tex
|
|
||||||
.rv/
|
|
||||||
.nvim/
|
|
||||||
.vim/
|
|
||||||
.build/
|
|
||||||
.gdb_history
|
|
||||||
host-build/
|
host-build/
|
||||||
build/*/prog.bin
|
build/*/prog.bin
|
||||||
build/*/prog.elf
|
build/*/prog.elf
|
||||||
build/*/prog.lst
|
build/*/prog.lst
|
||||||
build/*/prog.map
|
build/*/prog.map
|
||||||
|
build/*/*.o
|
||||||
|
cmake-build-*/
|
||||||
|
compile_commands.json
|
||||||
|
doc/generated/*.aux
|
||||||
|
doc/generated/*.fdb_latexmk
|
||||||
|
doc/generated/*.fls
|
||||||
|
doc/generated/*.log
|
||||||
|
doc/generated/*.out
|
||||||
|
doc/generated/*.pdf
|
||||||
|
doc/generated/*.upa
|
||||||
|
doc/generated/*.upb
|
||||||
|
doc/generated/build-meta.tex
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
if(NOT PICO_SDK_PATH)
|
||||||
|
if(DEFINED ENV{PICO_SDK_PATH})
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Set PICO_SDK_PATH to a Raspberry Pi Pico SDK checkout")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(freertos_heap4_rp2350 C CXX ASM)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
# The card and allocator are C-only. Debian's compact RISC-V toolchain does
|
||||||
|
# not ship libstdc++ headers, so keep the SDK's optional new/delete source out
|
||||||
|
# while preserving the interface targets expected by pico_stdlib.
|
||||||
|
add_library(pico_cxx_options_headers INTERFACE)
|
||||||
|
target_include_directories(pico_cxx_options_headers SYSTEM INTERFACE
|
||||||
|
${PICO_SDK_PATH}/src/rp2_common/pico_cxx_options/include)
|
||||||
|
add_library(pico_cxx_options INTERFACE)
|
||||||
|
target_link_libraries(pico_cxx_options INTERFACE pico_cxx_options_headers)
|
||||||
|
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
if(NOT PICO_PLATFORM STREQUAL "rp2350-riscv")
|
||||||
|
message(FATAL_ERROR "This card targets the RP2350 RISC-V cores")
|
||||||
|
endif()
|
||||||
|
if(NOT HEAP4_UPSTREAM_ROOT)
|
||||||
|
if(DEFINED ENV{HEAP4_UPSTREAM_ROOT})
|
||||||
|
set(HEAP4_UPSTREAM_ROOT $ENV{HEAP4_UPSTREAM_ROOT})
|
||||||
|
else()
|
||||||
|
set(HEAP4_UPSTREAM_ROOT /opt/FreeRTOS-Kernel)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(NOT EXISTS ${HEAP4_UPSTREAM_ROOT}/portable/MemMang/heap_4.c)
|
||||||
|
message(FATAL_ERROR "Pinned FreeRTOS heap_4.c not found at ${HEAP4_UPSTREAM_ROOT}")
|
||||||
|
endif()
|
||||||
|
if(NOT FREERTOS_RP2350_PATH)
|
||||||
|
if(DEFINED ENV{FREERTOS_RP2350_PATH})
|
||||||
|
set(FREERTOS_RP2350_PATH $ENV{FREERTOS_RP2350_PATH})
|
||||||
|
else()
|
||||||
|
set(FREERTOS_RP2350_PATH /opt/FreeRTOS-RP2350)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(NOT EXISTS ${FREERTOS_RP2350_PATH}/portable/ThirdParty/GCC/RP2350_RISC-V/portASM.S)
|
||||||
|
message(FATAL_ERROR "Compatible RP2350 FreeRTOS core/port not found at ${FREERTOS_RP2350_PATH}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(FREERTOS_CONFIG_FILE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||||
|
# Raspberry Pi's port CMake uses FREERTOS_KERNEL_PATH for its matching core.
|
||||||
|
# Keep the stable allocator root separate and add its heap_4.c explicitly.
|
||||||
|
set(FREERTOS_KERNEL_PATH ${FREERTOS_RP2350_PATH})
|
||||||
|
add_subdirectory(
|
||||||
|
${FREERTOS_RP2350_PATH}/portable/ThirdParty/GCC/RP2350_RISC-V
|
||||||
|
${CMAKE_BINARY_DIR}/freertos-kernel)
|
||||||
|
|
||||||
|
set(HEAP4_PROFILE "observe" CACHE STRING "observe, debug or release")
|
||||||
|
set_property(CACHE HEAP4_PROFILE PROPERTY STRINGS observe debug release)
|
||||||
|
set(HEAP4_BINARY_TYPE "no_flash" CACHE STRING "Pico SDK binary type")
|
||||||
|
set_property(CACHE HEAP4_BINARY_TYPE PROPERTY STRINGS no_flash default)
|
||||||
|
|
||||||
|
if(HEAP4_PROFILE STREQUAL "observe")
|
||||||
|
set(HEAP4_COMPILE_OPTIONS
|
||||||
|
-O0 -g3 -fno-omit-frame-pointer -fno-inline
|
||||||
|
-fno-optimize-sibling-calls)
|
||||||
|
elseif(HEAP4_PROFILE STREQUAL "debug")
|
||||||
|
set(HEAP4_COMPILE_OPTIONS
|
||||||
|
-Og -g3 -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
||||||
|
elseif(HEAP4_PROFILE STREQUAL "release")
|
||||||
|
set(HEAP4_COMPILE_OPTIONS -O2)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "HEAP4_PROFILE must be observe, debug or release")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(MODEL_TASKS
|
||||||
|
task01_first_fit_split
|
||||||
|
task02_address_order_coalesce)
|
||||||
|
|
||||||
|
foreach(task IN LISTS MODEL_TASKS)
|
||||||
|
set(task_source src/tasks/${task}.c)
|
||||||
|
add_executable(${task} ${task_source})
|
||||||
|
target_include_directories(${task} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/tasks)
|
||||||
|
set_property(SOURCE ${task_source} APPEND PROPERTY COMPILE_OPTIONS
|
||||||
|
${HEAP4_COMPILE_OPTIONS} -Wall -Wextra -Wpedantic)
|
||||||
|
target_link_libraries(${task} pico_stdlib)
|
||||||
|
pico_enable_stdio_usb(${task} 0)
|
||||||
|
pico_enable_stdio_uart(${task} 0)
|
||||||
|
if(NOT HEAP4_BINARY_TYPE STREQUAL "default")
|
||||||
|
pico_set_binary_type(${task} ${HEAP4_BINARY_TYPE})
|
||||||
|
endif()
|
||||||
|
pico_add_extra_outputs(${task})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
set(heap4_task task03_freertos_heap4)
|
||||||
|
set(heap4_source src/tasks/${heap4_task}.c)
|
||||||
|
add_executable(${heap4_task} ${heap4_source})
|
||||||
|
target_sources(${heap4_task} PRIVATE
|
||||||
|
${HEAP4_UPSTREAM_ROOT}/portable/MemMang/heap_4.c)
|
||||||
|
target_include_directories(${heap4_task} PRIVATE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/include
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/tasks)
|
||||||
|
set_property(SOURCE ${heap4_source} APPEND PROPERTY COMPILE_OPTIONS
|
||||||
|
${HEAP4_COMPILE_OPTIONS} -Wall -Wextra -Wpedantic)
|
||||||
|
target_link_libraries(${heap4_task} pico_stdlib FreeRTOS-Kernel)
|
||||||
|
pico_enable_stdio_usb(${heap4_task} 0)
|
||||||
|
pico_enable_stdio_uart(${heap4_task} 0)
|
||||||
|
if(NOT HEAP4_BINARY_TYPE STREQUAL "default")
|
||||||
|
pico_set_binary_type(${heap4_task} ${HEAP4_BINARY_TYPE})
|
||||||
|
endif()
|
||||||
|
pico_add_extra_outputs(${heap4_task})
|
||||||
@@ -6,11 +6,18 @@ OBJDUMP := $(RISCV_PREFIX)objdump
|
|||||||
SIZE := $(RISCV_PREFIX)size
|
SIZE := $(RISCV_PREFIX)size
|
||||||
HOST_CC ?= cc
|
HOST_CC ?= cc
|
||||||
|
|
||||||
DEBUG ?= 1
|
PROFILE ?= observe
|
||||||
ifeq ($(DEBUG),1)
|
ifeq ($(PROFILE),observe)
|
||||||
|
OPTFLAGS := -O0 -fno-omit-frame-pointer -fno-inline -fno-optimize-sibling-calls
|
||||||
DBGFLAGS := -g3 -ggdb
|
DBGFLAGS := -g3 -ggdb
|
||||||
else
|
else ifeq ($(PROFILE),debug)
|
||||||
|
OPTFLAGS := -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls
|
||||||
|
DBGFLAGS := -g3 -ggdb
|
||||||
|
else ifeq ($(PROFILE),release)
|
||||||
|
OPTFLAGS := -O2
|
||||||
DBGFLAGS :=
|
DBGFLAGS :=
|
||||||
|
else
|
||||||
|
$(error PROFILE must be observe, debug or release)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
RV_ENV_ROOT ?= /opt/rv-env
|
RV_ENV_ROOT ?= /opt/rv-env
|
||||||
@@ -21,36 +28,35 @@ LDSCRIPT ?= $(H3_COMMON)/link_hazard3.ld
|
|||||||
LAB_RUNTIME_DIR ?= $(RV_ENV_ROOT)/vendor/lab-runtime
|
LAB_RUNTIME_DIR ?= $(RV_ENV_ROOT)/vendor/lab-runtime
|
||||||
MEMOPS ?= $(LAB_RUNTIME_DIR)/memops.c
|
MEMOPS ?= $(LAB_RUNTIME_DIR)/memops.c
|
||||||
|
|
||||||
|
LOCAL_FREERTOS := $(abspath ../lab-rv32i-freertos-task-stack-vector/vendor/FreeRTOS-Kernel)
|
||||||
|
FREERTOS_KERNEL_PATH ?= $(if $(wildcard /opt/FreeRTOS-Kernel/include/FreeRTOS.h),/opt/FreeRTOS-Kernel,$(LOCAL_FREERTOS))
|
||||||
|
FREERTOS_PORT := $(FREERTOS_KERNEL_PATH)/portable/GCC/RISC-V
|
||||||
|
HEAP4_SOURCE := $(FREERTOS_KERNEL_PATH)/portable/MemMang/heap_4.c
|
||||||
|
|
||||||
BUILD_ROOT ?= build
|
BUILD_ROOT ?= build
|
||||||
HOST_BUILD_ROOT ?= host-build
|
HOST_BUILD_ROOT ?= host-build
|
||||||
TASKS := \
|
TASKS := \
|
||||||
task01_heap_map \
|
task01_first_fit_split \
|
||||||
task02_free_list \
|
task02_address_order_coalesce \
|
||||||
task03_heap_init \
|
task03_freertos_heap4
|
||||||
task04_alignment \
|
|
||||||
task05_malloc_first_fit \
|
|
||||||
task06_split_block \
|
|
||||||
task07_free_insert \
|
|
||||||
task08_coalescing \
|
|
||||||
task09_config_macros \
|
|
||||||
task10_heap4_demo
|
|
||||||
|
|
||||||
ARCH ?= rv32i_zicsr_zifencei
|
ARCH ?= rv32i_zicsr_zifencei
|
||||||
ABI ?= ilp32
|
ABI ?= ilp32
|
||||||
|
COMMON_FLAGS := -std=c11 -march=$(ARCH) -mabi=$(ABI) -nostdlib -nostartfiles \
|
||||||
COMMON_FLAGS := -std=c99 -march=$(ARCH) -mabi=$(ABI) -nostdlib -nostartfiles -ffreestanding -fno-builtin -fno-stack-protector $(DBGFLAGS) -Wall -Wextra -Isrc/tasks
|
-ffreestanding -fno-builtin -fno-stack-protector -ffunction-sections \
|
||||||
LINK_FLAGS := -Wl,--no-relax -Wl,-T,$(LDSCRIPT)
|
-fdata-sections $(OPTFLAGS) $(DBGFLAGS) -Wall -Wextra -Wpedantic
|
||||||
|
MODEL_CPPFLAGS := -Isrc/tasks
|
||||||
|
KERNEL_CPPFLAGS := -Iinclude/freestanding -Iinclude -Isrc/tasks \
|
||||||
|
-I$(FREERTOS_KERNEL_PATH)/include -I$(FREERTOS_PORT)
|
||||||
|
LINK_FLAGS := -Wl,--no-relax -Wl,--gc-sections -T $(LDSCRIPT)
|
||||||
LIBS := -lgcc
|
LIBS := -lgcc
|
||||||
EMIT_FLAGS := -std=c99 -march=$(ARCH) -mabi=$(ABI) -O -ffreestanding -fno-builtin -fno-stack-protector -nostdlib -nostartfiles -Wall -Wextra -Isrc/tasks
|
HOST_FLAGS := -std=c11 $(OPTFLAGS) $(DBGFLAGS) -Wall -Wextra -Wpedantic \
|
||||||
CFLAGS_STEP := $(COMMON_FLAGS) -O
|
-DHOST_PRINTF=1 -Isrc/tasks
|
||||||
HOST_CFLAGS := -std=c99 -O0 -g -Wall -Wextra -DHOST_PRINTF=1 -Isrc/tasks
|
HOST_HEAP4_FLAGS := $(HOST_FLAGS) -DHEAP4_HOST_ADAPTER=1 -Iinclude/host-shim
|
||||||
|
|
||||||
.PHONY: all tasks host \
|
.PHONY: all tasks host task1 task2 task3 t1 t2 t3 \
|
||||||
task1 task2 task3 task4 task5 task6 task7 task8 task9 task10 \
|
host-task1 host-task2 host-task3 ht1 ht2 ht3 clean check-support check-kernel \
|
||||||
t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 \
|
$(TASKS)
|
||||||
host-task1 host-task2 host-task3 host-task4 host-task5 host-task6 host-task7 host-task8 host-task9 host-task10 \
|
|
||||||
ht1 ht2 ht3 ht4 ht5 ht6 ht7 ht8 ht9 ht10 \
|
|
||||||
clean check-support $(TASKS)
|
|
||||||
|
|
||||||
all: tasks
|
all: tasks
|
||||||
tasks: $(TASKS)
|
tasks: $(TASKS)
|
||||||
@@ -59,54 +65,66 @@ host: $(addprefix $(HOST_BUILD_ROOT)/,$(addsuffix /prog,$(TASKS)))
|
|||||||
task1 t1: $(word 1,$(TASKS))
|
task1 t1: $(word 1,$(TASKS))
|
||||||
task2 t2: $(word 2,$(TASKS))
|
task2 t2: $(word 2,$(TASKS))
|
||||||
task3 t3: $(word 3,$(TASKS))
|
task3 t3: $(word 3,$(TASKS))
|
||||||
task4 t4: $(word 4,$(TASKS))
|
|
||||||
task5 t5: $(word 5,$(TASKS))
|
|
||||||
task6 t6: $(word 6,$(TASKS))
|
|
||||||
task7 t7: $(word 7,$(TASKS))
|
|
||||||
task8 t8: $(word 8,$(TASKS))
|
|
||||||
task9 t9: $(word 9,$(TASKS))
|
|
||||||
task10 t10: $(word 10,$(TASKS))
|
|
||||||
|
|
||||||
host-task1 ht1: $(HOST_BUILD_ROOT)/$(word 1,$(TASKS))/prog
|
host-task1 ht1: $(HOST_BUILD_ROOT)/$(word 1,$(TASKS))/prog
|
||||||
host-task2 ht2: $(HOST_BUILD_ROOT)/$(word 2,$(TASKS))/prog
|
host-task2 ht2: $(HOST_BUILD_ROOT)/$(word 2,$(TASKS))/prog
|
||||||
host-task3 ht3: $(HOST_BUILD_ROOT)/$(word 3,$(TASKS))/prog
|
host-task3 ht3: $(HOST_BUILD_ROOT)/$(word 3,$(TASKS))/prog
|
||||||
host-task4 ht4: $(HOST_BUILD_ROOT)/$(word 4,$(TASKS))/prog
|
|
||||||
host-task5 ht5: $(HOST_BUILD_ROOT)/$(word 5,$(TASKS))/prog
|
|
||||||
host-task6 ht6: $(HOST_BUILD_ROOT)/$(word 6,$(TASKS))/prog
|
|
||||||
host-task7 ht7: $(HOST_BUILD_ROOT)/$(word 7,$(TASKS))/prog
|
|
||||||
host-task8 ht8: $(HOST_BUILD_ROOT)/$(word 8,$(TASKS))/prog
|
|
||||||
host-task9 ht9: $(HOST_BUILD_ROOT)/$(word 9,$(TASKS))/prog
|
|
||||||
host-task10 ht10: $(HOST_BUILD_ROOT)/$(word 10,$(TASKS))/prog
|
|
||||||
|
|
||||||
check-support:
|
check-support:
|
||||||
@test -f "$(H3_INIT)" || { echo "Missing $(H3_INIT). Run inside rv-env or set RV_ENV_ROOT=/path/to/rv32i-hazard3-env" >&2; exit 1; }
|
@test -f "$(H3_INIT)" || { echo "Missing $(H3_INIT). Run inside hazard3-sim." >&2; exit 1; }
|
||||||
@test -f "$(LDSCRIPT)" || { echo "Missing $(LDSCRIPT)." >&2; exit 1; }
|
@test -f "$(LDSCRIPT)" || { echo "Missing $(LDSCRIPT)." >&2; exit 1; }
|
||||||
@test -f "$(MEMOPS)" || { echo "Missing $(MEMOPS)." >&2; exit 1; }
|
@test -f "$(MEMOPS)" || { echo "Missing $(MEMOPS)." >&2; exit 1; }
|
||||||
|
|
||||||
define task_rules
|
check-kernel:
|
||||||
$(1): $(BUILD_ROOT)/$(1)/prog.bin $(BUILD_ROOT)/$(1)/prog.elf $(BUILD_ROOT)/$(1)/prog.lst $(BUILD_ROOT)/$(1)/$(1).s
|
@test -f "$(HEAP4_SOURCE)" || { echo "Missing pinned FreeRTOS heap_4.c at $(HEAP4_SOURCE)." >&2; exit 1; }
|
||||||
|
@grep -F 'FreeRTOS Kernel V11.3.0' "$(FREERTOS_KERNEL_PATH)/include/FreeRTOS.h" >/dev/null || { echo "Expected FreeRTOS Kernel V11.3.0." >&2; exit 1; }
|
||||||
|
|
||||||
$(BUILD_ROOT)/$(1)/prog.elf: crt0.S src/tasks/$(1).c src/tasks/heap4_common.h $(H3_INIT) $(LDSCRIPT) $(MEMOPS) | check-support
|
define model_task_rules
|
||||||
|
$(1): $(BUILD_ROOT)/$(1)/prog.bin $(BUILD_ROOT)/$(1)/prog.elf $(BUILD_ROOT)/$(1)/prog.lst
|
||||||
|
|
||||||
|
$(BUILD_ROOT)/$(1)/prog.elf: crt0.S src/tasks/$(1).c src/tasks/heap4_model.h $(H3_INIT) $(LDSCRIPT) $(MEMOPS) | check-support
|
||||||
mkdir -p $$(@D)
|
mkdir -p $$(@D)
|
||||||
$(CC) $(CFLAGS_STEP) $(LINK_FLAGS) -Wl,-Map,$(BUILD_ROOT)/$(1)/prog.map -o $$@ $(H3_INIT) crt0.S $(MEMOPS) src/tasks/$(1).c $(LIBS)
|
$(CC) $(COMMON_FLAGS) $(MODEL_CPPFLAGS) $(LINK_FLAGS) -Wl,-Map,$(BUILD_ROOT)/$(1)/prog.map -o $$@ $(H3_INIT) crt0.S $(MEMOPS) src/tasks/$(1).c $(LIBS)
|
||||||
$(SIZE) -A -x $$@
|
$(SIZE) -A -x $$@
|
||||||
|
|
||||||
$(BUILD_ROOT)/$(1)/prog.bin: $(BUILD_ROOT)/$(1)/prog.elf
|
$(BUILD_ROOT)/$(1)/prog.bin: $(BUILD_ROOT)/$(1)/prog.elf
|
||||||
$(OBJCOPY) -O binary $$< $$@
|
$(OBJCOPY) -O binary $$< $$@
|
||||||
|
|
||||||
$(BUILD_ROOT)/$(1)/prog.lst: $(BUILD_ROOT)/$(1)/prog.elf
|
$(BUILD_ROOT)/$(1)/prog.lst: $(BUILD_ROOT)/$(1)/prog.elf
|
||||||
$(OBJDUMP) -d -M no-aliases,numeric $$< > $$@
|
$(OBJDUMP) -d -S -M no-aliases,numeric $$< > $$@
|
||||||
|
|
||||||
$(BUILD_ROOT)/$(1)/$(1).s: src/tasks/$(1).c src/tasks/heap4_common.h | check-support
|
$(HOST_BUILD_ROOT)/$(1)/prog: src/tasks/$(1).c src/tasks/heap4_model.h
|
||||||
mkdir -p $$(@D)
|
mkdir -p $$(@D)
|
||||||
$(CC) $(EMIT_FLAGS) -S -o $$@ $$<
|
$(HOST_CC) $(HOST_FLAGS) -o $$@ $$<
|
||||||
|
|
||||||
$(HOST_BUILD_ROOT)/$(1)/prog: src/tasks/$(1).c src/tasks/heap4_common.h
|
|
||||||
mkdir -p $$(@D)
|
|
||||||
$(HOST_CC) $(HOST_CFLAGS) -o $$@ $$<
|
|
||||||
endef
|
endef
|
||||||
|
|
||||||
$(foreach task,$(TASKS),$(eval $(call task_rules,$(task))))
|
$(eval $(call model_task_rules,$(word 1,$(TASKS))))
|
||||||
|
$(eval $(call model_task_rules,$(word 2,$(TASKS))))
|
||||||
|
|
||||||
|
TASK03 := $(word 3,$(TASKS))
|
||||||
|
TASK03_KERNEL_SOURCES := \
|
||||||
|
$(FREERTOS_KERNEL_PATH)/tasks.c \
|
||||||
|
$(FREERTOS_KERNEL_PATH)/list.c \
|
||||||
|
$(HEAP4_SOURCE) \
|
||||||
|
$(FREERTOS_PORT)/port.c \
|
||||||
|
$(FREERTOS_PORT)/portASM.S
|
||||||
|
|
||||||
|
$(TASK03): $(BUILD_ROOT)/$(TASK03)/prog.bin $(BUILD_ROOT)/$(TASK03)/prog.elf $(BUILD_ROOT)/$(TASK03)/prog.lst
|
||||||
|
|
||||||
|
$(BUILD_ROOT)/$(TASK03)/prog.elf: crt0.S src/tasks/$(TASK03).c include/FreeRTOSConfig.h $(TASK03_KERNEL_SOURCES) $(H3_INIT) $(LDSCRIPT) $(MEMOPS) | check-support check-kernel
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(COMMON_FLAGS) $(KERNEL_CPPFLAGS) $(LINK_FLAGS) -Wl,-Map,$(BUILD_ROOT)/$(TASK03)/prog.map -o $@ $(H3_INIT) crt0.S $(MEMOPS) $(TASK03_KERNEL_SOURCES) src/tasks/$(TASK03).c $(LIBS)
|
||||||
|
$(SIZE) -A -x $@
|
||||||
|
|
||||||
|
$(BUILD_ROOT)/$(TASK03)/prog.bin: $(BUILD_ROOT)/$(TASK03)/prog.elf
|
||||||
|
$(OBJCOPY) -O binary $< $@
|
||||||
|
|
||||||
|
$(BUILD_ROOT)/$(TASK03)/prog.lst: $(BUILD_ROOT)/$(TASK03)/prog.elf
|
||||||
|
$(OBJDUMP) -d -S -M no-aliases,numeric $< > $@
|
||||||
|
|
||||||
|
$(HOST_BUILD_ROOT)/$(TASK03)/prog: src/tasks/$(TASK03).c include/host-shim/FreeRTOS.h include/host-shim/task.h $(HEAP4_SOURCE) | check-kernel
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(HOST_CC) $(HOST_HEAP4_FLAGS) -o $@ src/tasks/$(TASK03).c $(HEAP4_SOURCE)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_ROOT) $(HOST_BUILD_ROOT)
|
rm -rf $(BUILD_ROOT) $(HOST_BUILD_ROOT)
|
||||||
|
|||||||
@@ -1,45 +1,130 @@
|
|||||||
# rv32i-freertos / heap4
|
# Karta pracy: FreeRTOS `heap_4`
|
||||||
|
|
||||||
Karta pracy 1/? z serii `rv32i-freertos`.
|
Trzy programy wspierające kartę po K&R 5.4 oraz `structures`:
|
||||||
|
|
||||||
Temat: mechanika alokatora `heap_4.c` z FreeRTOS-a na małych przykładach
|
1. nagłówek, wyrównanie i geometria splitu na arenie 256 B;
|
||||||
uruchamianych w środowisku RV32I.
|
2. lista adresowa + scalanie `A | B | C`;
|
||||||
|
3. niezmodyfikowany FreeRTOS-Kernel V11.3.0 `heap_4.c`.
|
||||||
|
|
||||||
Ta karta jest pomostem po kartach C: używa wskaźników, struktur, listy
|
W lekcji 30-minutowej programy 1–2 są demonstracjami z predykcją. Uczeń
|
||||||
jednokierunkowej, `static`, `typedef`, makr i arytmetyki adresów, ale nie
|
debugguje wyłącznie program 3.
|
||||||
wymaga jeszcze schedulera, tasków ani przerwań.
|
|
||||||
|
|
||||||
Kod w `src/tasks` jest dydaktycznym modelem zachowania `heap_4`, a nie kopią
|
Task 3 linkuje upstream `portable/MemMang/heap_4.c` z commita
|
||||||
pliku upstream FreeRTOS.
|
`9b777ae5c5b8e9e456065a00294d1e5f5f9facf5`.
|
||||||
|
|
||||||
## Taski
|
- AMD64: rzeczywisty algorytm + jednowątkowy adapter sekcji krytycznej.
|
||||||
|
- Hazard3: `tasks.c`, `list.c`, `heap_4.c` i oficjalny port RISC-V.
|
||||||
|
- RP2350: zgodny core+port z forka Raspberry Pi (`4f7299d6ea74`) oraz
|
||||||
|
podstawiony stabilny allocator V11.3.0.
|
||||||
|
|
||||||
- `task01_heap_map.c` - mapa pojęć: sterta, blok, nagłówek bloku.
|
## Bezpośrednia kontynuacja K&R 5.4
|
||||||
- `task02_free_list.c` - lista wolnych bloków.
|
|
||||||
- `task03_heap_init.c` - inicjalizacja sterty i wartownik końca listy.
|
|
||||||
- `task04_alignment.c` - wyrównanie rozmiarów i adresów.
|
|
||||||
- `task05_malloc_first_fit.c` - pierwszy pasujący wolny blok.
|
|
||||||
- `task06_split_block.c` - podział większego bloku.
|
|
||||||
- `task07_free_insert.c` - zwracanie bloku na listę wolnych.
|
|
||||||
- `task08_coalescing.c` - scalanie sąsiednich wolnych bloków.
|
|
||||||
- `task09_config_macros.c` - makra konfiguracyjne i warunkowy `printf`.
|
|
||||||
- `task10_heap4_demo.c` - mini-demonstracja `malloc/free` bez pełnego RTOS-a.
|
|
||||||
|
|
||||||
Hostowe `printf` jest dostępne tylko przez `-DHOST_PRINTF=1`; RV32I zapisuje
|
Lekcja zaczyna się od gotowego projektu z poprzedniej karty:
|
||||||
wyniki w globalnych zmiennych `volatile`.
|
`allocbuf[64]`, `allocp`, `alloc(5)`, `alloc(7)` oraz offsety
|
||||||
|
`0 -> 5 -> 12`. Ten alokator:
|
||||||
|
|
||||||
## Budowanie
|
- nie potrafi zwolnić pierwszego bloku i zachować drugiego;
|
||||||
|
- nie przechowuje rozmiaru ani stanu zwróconego obszaru;
|
||||||
|
- nie zapamiętuje i nie wykorzystuje dziur;
|
||||||
|
- odzyskuje pamięć wyłącznie przez reset całej areny.
|
||||||
|
|
||||||
|
FC01 odpowiada na jedno pytanie projektowe: **co trzeba dodać, aby zwalniać
|
||||||
|
dowolny blok i ponownie wykorzystywać powstałe miejsce?** Odpowiedzią jest
|
||||||
|
mechanika `heap_4`: nagłówek bloku, lista wolnych bloków uporządkowana po
|
||||||
|
adresie, first-fit, split oraz scalanie fizycznych sąsiadów.
|
||||||
|
|
||||||
|
FreeRTOS udostępnia kilka wymiennych implementacji tego samego API i projekt
|
||||||
|
linkuje dokładnie jedną. `heap_1`, `heap_2`, `heap_3` i `heap_5` są w tej
|
||||||
|
karcie tylko jednominutową mapą kontekstu. Cała obserwowana implementacja to
|
||||||
|
niezmodyfikowany [`heap_4.c` z FreeRTOS-Kernel V11.3.0](https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/V11.3.0/portable/MemMang/heap_4.c).
|
||||||
|
|
||||||
|
Lekcja trwa 30 minut. Task 1 i Task 2 są krótkimi demonstracjami z predykcją;
|
||||||
|
Task 3 jest jedynym centralnym przebiegiem ucznia w debuggerze. Obraz dla
|
||||||
|
Pico 2 W musi być zbudowany i wgrany przed zajęciami. `heap_4` jest wyborem
|
||||||
|
linkera projektu, nie sprzętowym mechanizmem RP2350.
|
||||||
|
|
||||||
|
## Jedno wejście: `stemctl`
|
||||||
|
|
||||||
|
Centralny przebieg lekcji, przygotowany przed wejściem uczniów:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
RV_ENV_ROOT=~/dev/workspace/rv/tools/rv32i-hazard3-student-env make tasks
|
stemctl debug rp2350 freertos heap4 3 --device /dev/bus/usb/BBB/DDD
|
||||||
make host
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Praca przez rvctl
|
Pełna weryfikacja repozytorium wykonywana poza 30-minutową lekcją:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl series cards fetch inf heap4
|
stemctl series cards fetch freertos heap4
|
||||||
./rvctl card use inf heap4
|
stemctl test native-amd64 freertos heap4 1
|
||||||
./rvctl tasks list
|
stemctl test hazard3-sim freertos heap4 2
|
||||||
./rvctl debug rv32i 1
|
stemctl debug hazard3-sim freertos heap4 3
|
||||||
|
stemctl test rp2350 freertos heap4 3
|
||||||
|
stemctl deploy rp2350 freertos heap4 3 --device /dev/bus/usb/BBB/DDD
|
||||||
|
stemctl debug rp2350 freertos heap4 3 --device /dev/bus/usb/BBB/DDD
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- `debug rp2350`: roboczy ELF `no_flash` ładowany do SRAM.
|
||||||
|
- `deploy rp2350`: trwały obraz w SPI flash, odczytany przez breakpointy.
|
||||||
|
|
||||||
|
## Kontrakt zadań
|
||||||
|
|
||||||
|
| Task | Sprawdza | Wynik kluczowy |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | alignment, header, split | `wanted + remainder == 256` |
|
||||||
|
| 2 | lista po adresie, coalescing | `2 bloki -> 1 blok`, `96 -> 144 B` |
|
||||||
|
| 3 | prawdziwe API FreeRTOS | free wraca do F0, minimum nie rośnie, OOM = NULL |
|
||||||
|
|
||||||
|
## Debugowanie Task 3
|
||||||
|
|
||||||
|
Automatyczny start: `heap4_reset_checkpoint`. Karta prowadzi po ośmiu
|
||||||
|
powtarzalnych stanach jednego procesu:
|
||||||
|
|
||||||
|
| Event | Symbol | Najważniejszy dowód Hazard3 |
|
||||||
|
|---|---|---|
|
||||||
|
| E01 | `heap4_reset_checkpoint` | `free=largest=4080`, 1 blok |
|
||||||
|
| E02 | `heap4_alloc_a_checkpoint` | A(24), `consumed=48`, `free=4032` |
|
||||||
|
| E03 | `heap4_alloc_b_checkpoint` | B(40), `consumed=64`, `free=3968` |
|
||||||
|
| E04 | `heap4_alloc_c_checkpoint` | C(16), `consumed=32`, `free=3936` |
|
||||||
|
| E05 | `heap4_free_a_checkpoint` | `free=3984`, 2 bloki |
|
||||||
|
| E06 | `heap4_fragmented_checkpoint` | `free=4016`, 2 bloki, `free>largest` |
|
||||||
|
| E07 | `heap4_coalesced_checkpoint` | `free=largest=4080`, 1 blok |
|
||||||
|
| E08 | `task03_debug_checkpoint` | OOM `NULL`, hook=1, asserts=0, PASS=1 |
|
||||||
|
|
||||||
|
`BlockLink_t` ma na RV32 8 B, ale port wymaga wyrównania 16 B, dlatego
|
||||||
|
`xHeapStructSize` wynosi 16 B. To rozróżnienie tłumaczy zużycie 48/64/32 B.
|
||||||
|
|
||||||
|
```gdb
|
||||||
|
p g_fragmented_stats
|
||||||
|
p g_final_stats
|
||||||
|
p g_a_consumed
|
||||||
|
p g_b_consumed
|
||||||
|
p g_c_consumed
|
||||||
|
p xFreeBytesRemaining
|
||||||
|
p xMinimumEverFreeBytesRemaining
|
||||||
|
p xStart
|
||||||
|
p pxEnd
|
||||||
|
x/160bx ucHeap
|
||||||
|
b prvInsertBlockIntoFreeList
|
||||||
|
b pvPortMalloc
|
||||||
|
b vPortFree
|
||||||
|
```
|
||||||
|
|
||||||
|
Scalanie sprawdzamy z replay E06: zatrzymujemy się przed `vPortFree(g_b)`,
|
||||||
|
ustawiamy tymczasowy breakpoint na `prvInsertBlockIntoFreeList` i krokujemy.
|
||||||
|
Upstream V11.3.0 scala najpierw poprzedni/lewy, a potem następny/prawy blok.
|
||||||
|
Pełna macierz kroków diagramów jest w
|
||||||
|
[`doc/hazard3-iteration-plan.md`](doc/hazard3-iteration-plan.md).
|
||||||
|
|
||||||
|
W UI: Termdebug po lewej; źródło i listing po prawej; bash pod Neovimem.
|
||||||
|
`F5` continue, `F9` breakpoint, `F10` next, `F11` step, `Shift-F11`
|
||||||
|
finish, `F8` stepi, `:StudentStack`, `:StudentMemory`, `:StudentLst`.
|
||||||
|
|
||||||
|
## Test repozytorium
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./tests/test_host.sh
|
||||||
|
bash -n tools/card-action.sh
|
||||||
|
./scripts/render_pdf.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Źródło i licencja: [UPSTREAM.md](UPSTREAM.md).
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Upstream contract
|
||||||
|
|
||||||
|
- Project: FreeRTOS-Kernel
|
||||||
|
- Version: V11.3.0
|
||||||
|
- Commit: `9b777ae5c5b8e9e456065a00294d1e5f5f9facf5`
|
||||||
|
- Source used by Task 3: `portable/MemMang/heap_4.c`
|
||||||
|
- License: MIT (as shipped in the pinned upstream checkout)
|
||||||
|
|
||||||
|
RP2350 needs a port that is newer than the V11.3.0 tag:
|
||||||
|
|
||||||
|
- Project: Raspberry Pi fork of FreeRTOS-Kernel
|
||||||
|
- Commit: `4f7299d6ea746b27a9dd19e87af568e34bd65b15`
|
||||||
|
- Files used on RP2350: matching kernel core plus
|
||||||
|
`portable/ThirdParty/GCC/RP2350_RISC-V`
|
||||||
|
- Allocator used on RP2350: still the stable V11.3.0 `heap_4.c` above,
|
||||||
|
compiled instead of the fork's development-branch allocator.
|
||||||
|
|
||||||
|
The canonical containers provide `/opt/FreeRTOS-Kernel`; the RP2350 profile
|
||||||
|
also provides `/opt/FreeRTOS-RP2350`. The card carries no allocator copy.
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
.file "task01_heap_map.c"
|
|
||||||
.option nopic
|
|
||||||
.attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0"
|
|
||||||
.attribute unaligned_access, 0
|
|
||||||
.attribute stack_align, 16
|
|
||||||
.text
|
|
||||||
.align 2
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
li a5,8
|
|
||||||
sw a5,g_header_size,a4
|
|
||||||
sw a5,g_aligned_header_size,a4
|
|
||||||
li a5,1
|
|
||||||
sw a5,g_allocated_bit_set,a4
|
|
||||||
lla a5,g_total_block_bytes
|
|
||||||
li a4,24
|
|
||||||
sw a4,0(a5)
|
|
||||||
lw a5,0(a5)
|
|
||||||
addi a5,a5,-8
|
|
||||||
sw a5,g_user_bytes,a4
|
|
||||||
li a0,0
|
|
||||||
ret
|
|
||||||
.size main, .-main
|
|
||||||
.globl g_total_block_bytes
|
|
||||||
.globl g_user_bytes
|
|
||||||
.globl g_allocated_bit_set
|
|
||||||
.globl g_aligned_header_size
|
|
||||||
.globl g_header_size
|
|
||||||
.section .sbss,"aw",@nobits
|
|
||||||
.align 2
|
|
||||||
.type g_total_block_bytes, @object
|
|
||||||
.size g_total_block_bytes, 4
|
|
||||||
g_total_block_bytes:
|
|
||||||
.zero 4
|
|
||||||
.type g_user_bytes, @object
|
|
||||||
.size g_user_bytes, 4
|
|
||||||
g_user_bytes:
|
|
||||||
.zero 4
|
|
||||||
.type g_allocated_bit_set, @object
|
|
||||||
.size g_allocated_bit_set, 4
|
|
||||||
g_allocated_bit_set:
|
|
||||||
.zero 4
|
|
||||||
.type g_aligned_header_size, @object
|
|
||||||
.size g_aligned_header_size, 4
|
|
||||||
g_aligned_header_size:
|
|
||||||
.zero 4
|
|
||||||
.type g_header_size, @object
|
|
||||||
.size g_header_size, 4
|
|
||||||
g_header_size:
|
|
||||||
.zero 4
|
|
||||||
.ident "GCC: (15.2.0-23) 15.2.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
.file "task02_free_list.c"
|
|
||||||
.option nopic
|
|
||||||
.attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0"
|
|
||||||
.attribute unaligned_access, 0
|
|
||||||
.attribute stack_align, 16
|
|
||||||
.text
|
|
||||||
.align 2
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
addi sp,sp,-32
|
|
||||||
addi a5,sp,16
|
|
||||||
sw a5,24(sp)
|
|
||||||
li a5,32
|
|
||||||
sw a5,28(sp)
|
|
||||||
addi a5,sp,8
|
|
||||||
sw a5,16(sp)
|
|
||||||
li a5,80
|
|
||||||
sw a5,20(sp)
|
|
||||||
sw zero,8(sp)
|
|
||||||
li a5,24
|
|
||||||
sw a5,12(sp)
|
|
||||||
li a4,0
|
|
||||||
add a5,sp,a5
|
|
||||||
.L2:
|
|
||||||
lw a3,4(a5)
|
|
||||||
add a4,a4,a3
|
|
||||||
lw a5,0(a5)
|
|
||||||
bne a5,zero,.L2
|
|
||||||
sw a4,g_free_total,a5
|
|
||||||
li a3,0
|
|
||||||
addi a5,sp,24
|
|
||||||
j .L4
|
|
||||||
.L3:
|
|
||||||
lw a5,0(a5)
|
|
||||||
beq a5,zero,.L10
|
|
||||||
.L4:
|
|
||||||
lw a4,4(a5)
|
|
||||||
bgeu a3,a4,.L3
|
|
||||||
mv a3,a4
|
|
||||||
j .L3
|
|
||||||
.L10:
|
|
||||||
sw a3,g_largest_free,a5
|
|
||||||
addi a5,sp,24
|
|
||||||
li a4,0
|
|
||||||
.L5:
|
|
||||||
addi a4,a4,1
|
|
||||||
lw a5,0(a5)
|
|
||||||
bne a5,zero,.L5
|
|
||||||
sw a4,g_node_count,a5
|
|
||||||
li a0,0
|
|
||||||
addi sp,sp,32
|
|
||||||
jr ra
|
|
||||||
.size main, .-main
|
|
||||||
.globl g_node_count
|
|
||||||
.globl g_largest_free
|
|
||||||
.globl g_free_total
|
|
||||||
.section .sbss,"aw",@nobits
|
|
||||||
.align 2
|
|
||||||
.type g_node_count, @object
|
|
||||||
.size g_node_count, 4
|
|
||||||
g_node_count:
|
|
||||||
.zero 4
|
|
||||||
.type g_largest_free, @object
|
|
||||||
.size g_largest_free, 4
|
|
||||||
g_largest_free:
|
|
||||||
.zero 4
|
|
||||||
.type g_free_total, @object
|
|
||||||
.size g_free_total, 4
|
|
||||||
g_free_total:
|
|
||||||
.zero 4
|
|
||||||
.ident "GCC: (15.2.0-23) 15.2.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
.file "task03_heap_init.c"
|
|
||||||
.option nopic
|
|
||||||
.attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0"
|
|
||||||
.attribute unaligned_access, 0
|
|
||||||
.attribute stack_align, 16
|
|
||||||
.text
|
|
||||||
.align 2
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
lla a2,.LANCHOR0
|
|
||||||
andi a4,a2,7
|
|
||||||
mv a5,a2
|
|
||||||
beq a4,zero,.L2
|
|
||||||
andi a5,a2,-8
|
|
||||||
addi a5,a5,8
|
|
||||||
.L2:
|
|
||||||
lla a3,.LANCHOR0+160
|
|
||||||
sub a3,a3,a5
|
|
||||||
addi a4,a5,-8
|
|
||||||
andi a3,a3,-8
|
|
||||||
add a4,a4,a3
|
|
||||||
sw a4,end_marker,a3
|
|
||||||
sw zero,0(a4)
|
|
||||||
sw zero,4(a4)
|
|
||||||
sw a4,0(a5)
|
|
||||||
sub a3,a4,a5
|
|
||||||
sw a3,4(a5)
|
|
||||||
lla a1,start
|
|
||||||
sw a5,0(a1)
|
|
||||||
sw zero,4(a1)
|
|
||||||
sub a5,a5,a2
|
|
||||||
sw a5,g_aligned_offset,a2
|
|
||||||
sw a3,g_initial_free_size,a5
|
|
||||||
lw a5,0(a4)
|
|
||||||
seqz a5,a5
|
|
||||||
sw a5,g_end_is_last,a4
|
|
||||||
li a0,0
|
|
||||||
ret
|
|
||||||
.size main, .-main
|
|
||||||
.globl g_end_is_last
|
|
||||||
.globl g_aligned_offset
|
|
||||||
.globl g_initial_free_size
|
|
||||||
.bss
|
|
||||||
.align 2
|
|
||||||
.set .LANCHOR0,. + 0
|
|
||||||
.type heap_area, @object
|
|
||||||
.size heap_area, 168
|
|
||||||
heap_area:
|
|
||||||
.zero 168
|
|
||||||
.section .sbss,"aw",@nobits
|
|
||||||
.align 2
|
|
||||||
.type g_end_is_last, @object
|
|
||||||
.size g_end_is_last, 4
|
|
||||||
g_end_is_last:
|
|
||||||
.zero 4
|
|
||||||
.type g_aligned_offset, @object
|
|
||||||
.size g_aligned_offset, 4
|
|
||||||
g_aligned_offset:
|
|
||||||
.zero 4
|
|
||||||
.type g_initial_free_size, @object
|
|
||||||
.size g_initial_free_size, 4
|
|
||||||
g_initial_free_size:
|
|
||||||
.zero 4
|
|
||||||
.type end_marker, @object
|
|
||||||
.size end_marker, 4
|
|
||||||
end_marker:
|
|
||||||
.zero 4
|
|
||||||
.type start, @object
|
|
||||||
.size start, 8
|
|
||||||
start:
|
|
||||||
.zero 8
|
|
||||||
.ident "GCC: (15.2.0-23) 15.2.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
.file "task04_alignment.c"
|
|
||||||
.option nopic
|
|
||||||
.attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0"
|
|
||||||
.attribute unaligned_access, 0
|
|
||||||
.attribute stack_align, 16
|
|
||||||
.text
|
|
||||||
.align 2
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
addi sp,sp,-32
|
|
||||||
li a5,16
|
|
||||||
sw a5,g_request_1,a4
|
|
||||||
li a5,24
|
|
||||||
sw a5,g_request_9,a4
|
|
||||||
li a5,32
|
|
||||||
sw a5,g_request_17,a4
|
|
||||||
li a5,7
|
|
||||||
sw a5,g_address_delta,a4
|
|
||||||
li a0,0
|
|
||||||
addi sp,sp,32
|
|
||||||
jr ra
|
|
||||||
.size main, .-main
|
|
||||||
.globl g_address_delta
|
|
||||||
.globl g_request_17
|
|
||||||
.globl g_request_9
|
|
||||||
.globl g_request_1
|
|
||||||
.section .sbss,"aw",@nobits
|
|
||||||
.align 2
|
|
||||||
.type g_address_delta, @object
|
|
||||||
.size g_address_delta, 4
|
|
||||||
g_address_delta:
|
|
||||||
.zero 4
|
|
||||||
.type g_request_17, @object
|
|
||||||
.size g_request_17, 4
|
|
||||||
g_request_17:
|
|
||||||
.zero 4
|
|
||||||
.type g_request_9, @object
|
|
||||||
.size g_request_9, 4
|
|
||||||
g_request_9:
|
|
||||||
.zero 4
|
|
||||||
.type g_request_1, @object
|
|
||||||
.size g_request_1, 4
|
|
||||||
g_request_1:
|
|
||||||
.zero 4
|
|
||||||
.ident "GCC: (15.2.0-23) 15.2.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
.file "task05_malloc_first_fit.c"
|
|
||||||
.option nopic
|
|
||||||
.attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0"
|
|
||||||
.attribute unaligned_access, 0
|
|
||||||
.attribute stack_align, 16
|
|
||||||
.text
|
|
||||||
.align 2
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
lla a4,.LANCHOR0
|
|
||||||
andi a5,a4,7
|
|
||||||
beq a5,zero,.L2
|
|
||||||
andi a4,a4,-8
|
|
||||||
addi a4,a4,8
|
|
||||||
.L2:
|
|
||||||
mv a5,a4
|
|
||||||
addi a3,a4,120
|
|
||||||
sw a3,end_marker,a2
|
|
||||||
sw zero,120(a4)
|
|
||||||
sw zero,4(a3)
|
|
||||||
sw a3,0(a4)
|
|
||||||
li a2,120
|
|
||||||
sw a2,4(a4)
|
|
||||||
lla a2,start
|
|
||||||
sw a4,0(a2)
|
|
||||||
sw zero,4(a2)
|
|
||||||
li a1,31
|
|
||||||
j .L5
|
|
||||||
.L6:
|
|
||||||
mv a5,a4
|
|
||||||
.L5:
|
|
||||||
lw a4,4(a5)
|
|
||||||
bgtu a4,a1,.L7
|
|
||||||
lw a4,0(a5)
|
|
||||||
mv a2,a5
|
|
||||||
bne a4,a3,.L6
|
|
||||||
li a5,0
|
|
||||||
j .L4
|
|
||||||
.L7:
|
|
||||||
lw a4,0(a5)
|
|
||||||
sw a4,0(a2)
|
|
||||||
sw zero,0(a5)
|
|
||||||
lw a4,4(a5)
|
|
||||||
li a3,-2147483648
|
|
||||||
or a4,a4,a3
|
|
||||||
sw a4,4(a5)
|
|
||||||
addi a5,a5,8
|
|
||||||
.L4:
|
|
||||||
snez a4,a5
|
|
||||||
sw a4,g_allocation_ok,a3
|
|
||||||
lw a5,-4(a5)
|
|
||||||
slli a5,a5,1
|
|
||||||
srli a5,a5,1
|
|
||||||
sw a5,g_allocated_size,a4
|
|
||||||
lw a5,start
|
|
||||||
lw a5,4(a5)
|
|
||||||
sw a5,g_remaining_free,a4
|
|
||||||
li a0,0
|
|
||||||
ret
|
|
||||||
.size main, .-main
|
|
||||||
.globl g_allocation_ok
|
|
||||||
.globl g_remaining_free
|
|
||||||
.globl g_allocated_size
|
|
||||||
.bss
|
|
||||||
.align 2
|
|
||||||
.set .LANCHOR0,. + 0
|
|
||||||
.type heap_area, @object
|
|
||||||
.size heap_area, 136
|
|
||||||
heap_area:
|
|
||||||
.zero 136
|
|
||||||
.section .sbss,"aw",@nobits
|
|
||||||
.align 2
|
|
||||||
.type g_allocation_ok, @object
|
|
||||||
.size g_allocation_ok, 4
|
|
||||||
g_allocation_ok:
|
|
||||||
.zero 4
|
|
||||||
.type g_remaining_free, @object
|
|
||||||
.size g_remaining_free, 4
|
|
||||||
g_remaining_free:
|
|
||||||
.zero 4
|
|
||||||
.type g_allocated_size, @object
|
|
||||||
.size g_allocated_size, 4
|
|
||||||
g_allocated_size:
|
|
||||||
.zero 4
|
|
||||||
.type end_marker, @object
|
|
||||||
.size end_marker, 4
|
|
||||||
end_marker:
|
|
||||||
.zero 4
|
|
||||||
.type start, @object
|
|
||||||
.size start, 8
|
|
||||||
start:
|
|
||||||
.zero 8
|
|
||||||
.ident "GCC: (15.2.0-23) 15.2.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
.file "task06_split_block.c"
|
|
||||||
.option nopic
|
|
||||||
.attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0"
|
|
||||||
.attribute unaligned_access, 0
|
|
||||||
.attribute stack_align, 16
|
|
||||||
.text
|
|
||||||
.align 2
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
lla a4,.LANCHOR0
|
|
||||||
andi a5,a4,7
|
|
||||||
beq a5,zero,.L2
|
|
||||||
andi a4,a4,-8
|
|
||||||
addi a4,a4,8
|
|
||||||
.L2:
|
|
||||||
mv a5,a4
|
|
||||||
addi a3,a4,176
|
|
||||||
sw a3,end_marker,a2
|
|
||||||
sw zero,176(a4)
|
|
||||||
sw zero,4(a3)
|
|
||||||
sw a3,0(a4)
|
|
||||||
li a2,176
|
|
||||||
sw a2,4(a4)
|
|
||||||
lla a2,start
|
|
||||||
sw a4,0(a2)
|
|
||||||
sw zero,4(a2)
|
|
||||||
li a1,39
|
|
||||||
j .L7
|
|
||||||
.L10:
|
|
||||||
mv a5,a4
|
|
||||||
.L7:
|
|
||||||
lw a4,4(a5)
|
|
||||||
bgtu a4,a1,.L13
|
|
||||||
lw a4,0(a5)
|
|
||||||
mv a2,a5
|
|
||||||
bne a4,a3,.L10
|
|
||||||
li a5,0
|
|
||||||
j .L6
|
|
||||||
.L13:
|
|
||||||
addi a4,a4,-40
|
|
||||||
li a1,16
|
|
||||||
bleu a4,a1,.L4
|
|
||||||
sw a4,44(a5)
|
|
||||||
lw a4,0(a5)
|
|
||||||
sw a4,40(a5)
|
|
||||||
addi a4,a5,40
|
|
||||||
sw a4,0(a2)
|
|
||||||
li a4,40
|
|
||||||
sw a4,4(a5)
|
|
||||||
.L5:
|
|
||||||
sw zero,0(a5)
|
|
||||||
lw a4,4(a5)
|
|
||||||
li a2,-2147483648
|
|
||||||
or a4,a4,a2
|
|
||||||
sw a4,4(a5)
|
|
||||||
addi a5,a5,8
|
|
||||||
.L6:
|
|
||||||
lw a5,-4(a5)
|
|
||||||
slli a5,a5,1
|
|
||||||
srli a5,a5,1
|
|
||||||
sw a5,g_allocated_size,a4
|
|
||||||
lw a5,start
|
|
||||||
lw a4,4(a5)
|
|
||||||
sw a4,g_split_free_size,a2
|
|
||||||
beq a5,a3,.L11
|
|
||||||
li a4,0
|
|
||||||
.L9:
|
|
||||||
addi a4,a4,1
|
|
||||||
lw a5,0(a5)
|
|
||||||
bne a5,a3,.L9
|
|
||||||
.L8:
|
|
||||||
sw a4,g_free_nodes,a5
|
|
||||||
li a0,0
|
|
||||||
ret
|
|
||||||
.L4:
|
|
||||||
lw a4,0(a5)
|
|
||||||
sw a4,0(a2)
|
|
||||||
j .L5
|
|
||||||
.L11:
|
|
||||||
li a4,0
|
|
||||||
j .L8
|
|
||||||
.size main, .-main
|
|
||||||
.globl g_free_nodes
|
|
||||||
.globl g_split_free_size
|
|
||||||
.globl g_allocated_size
|
|
||||||
.bss
|
|
||||||
.align 2
|
|
||||||
.set .LANCHOR0,. + 0
|
|
||||||
.type heap_area, @object
|
|
||||||
.size heap_area, 200
|
|
||||||
heap_area:
|
|
||||||
.zero 200
|
|
||||||
.section .sbss,"aw",@nobits
|
|
||||||
.align 2
|
|
||||||
.type g_free_nodes, @object
|
|
||||||
.size g_free_nodes, 4
|
|
||||||
g_free_nodes:
|
|
||||||
.zero 4
|
|
||||||
.type g_split_free_size, @object
|
|
||||||
.size g_split_free_size, 4
|
|
||||||
g_split_free_size:
|
|
||||||
.zero 4
|
|
||||||
.type g_allocated_size, @object
|
|
||||||
.size g_allocated_size, 4
|
|
||||||
g_allocated_size:
|
|
||||||
.zero 4
|
|
||||||
.type end_marker, @object
|
|
||||||
.size end_marker, 4
|
|
||||||
end_marker:
|
|
||||||
.zero 4
|
|
||||||
.type start, @object
|
|
||||||
.size start, 8
|
|
||||||
start:
|
|
||||||
.zero 8
|
|
||||||
.ident "GCC: (15.2.0-23) 15.2.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
.file "task07_free_insert.c"
|
|
||||||
.option nopic
|
|
||||||
.attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0"
|
|
||||||
.attribute unaligned_access, 0
|
|
||||||
.attribute stack_align, 16
|
|
||||||
.text
|
|
||||||
.align 2
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
lla a5,.LANCHOR0
|
|
||||||
lla a4,.LANCHOR0+176
|
|
||||||
sw a4,end_marker,a3
|
|
||||||
sw a5,start,a3
|
|
||||||
lla a3,.LANCHOR0+128
|
|
||||||
sw a3,0(a5)
|
|
||||||
sw a4,128(a5)
|
|
||||||
sw zero,176(a5)
|
|
||||||
li a4,40
|
|
||||||
sw a4,4(a5)
|
|
||||||
li a4,48
|
|
||||||
sw a4,132(a5)
|
|
||||||
sw zero,180(a5)
|
|
||||||
li a4,32
|
|
||||||
sw a4,68(a5)
|
|
||||||
lla a5,start
|
|
||||||
lla a3,.LANCHOR0+64
|
|
||||||
.L2:
|
|
||||||
mv a4,a5
|
|
||||||
lw a5,0(a5)
|
|
||||||
bltu a5,a3,.L2
|
|
||||||
lla a3,.LANCHOR0
|
|
||||||
sw a5,64(a3)
|
|
||||||
lla a5,.LANCHOR0+64
|
|
||||||
sw a5,0(a4)
|
|
||||||
lw a5,start
|
|
||||||
beq a5,a3,.L16
|
|
||||||
sw zero,g_order_ok,a4
|
|
||||||
lla a4,.LANCHOR0+176
|
|
||||||
beq a5,a4,.L5
|
|
||||||
.L9:
|
|
||||||
mv a4,a5
|
|
||||||
li a3,0
|
|
||||||
lla a2,.LANCHOR0+176
|
|
||||||
.L6:
|
|
||||||
addi a3,a3,1
|
|
||||||
lw a4,0(a4)
|
|
||||||
bne a4,a2,.L6
|
|
||||||
sw a3,g_free_count,a4
|
|
||||||
li a4,0
|
|
||||||
lla a2,.LANCHOR0+176
|
|
||||||
.L7:
|
|
||||||
lw a3,4(a5)
|
|
||||||
add a4,a4,a3
|
|
||||||
lw a5,0(a5)
|
|
||||||
bne a5,a2,.L7
|
|
||||||
.L8:
|
|
||||||
sw a4,g_total_free,a5
|
|
||||||
li a0,0
|
|
||||||
ret
|
|
||||||
.L16:
|
|
||||||
lla a3,.LANCHOR0+64
|
|
||||||
lw a2,.LANCHOR0
|
|
||||||
li a4,0
|
|
||||||
beq a2,a3,.L17
|
|
||||||
.L4:
|
|
||||||
sw a4,g_order_ok,a3
|
|
||||||
j .L9
|
|
||||||
.L17:
|
|
||||||
lla a3,.LANCHOR0+128
|
|
||||||
lw a2,.LANCHOR0+64
|
|
||||||
bne a2,a3,.L4
|
|
||||||
lla a3,.LANCHOR0+176
|
|
||||||
lw a4,.LANCHOR0+128
|
|
||||||
sub a4,a4,a3
|
|
||||||
seqz a4,a4
|
|
||||||
j .L4
|
|
||||||
.L5:
|
|
||||||
sw zero,g_free_count,a5
|
|
||||||
li a4,0
|
|
||||||
j .L8
|
|
||||||
.size main, .-main
|
|
||||||
.globl g_total_free
|
|
||||||
.globl g_free_count
|
|
||||||
.globl g_order_ok
|
|
||||||
.bss
|
|
||||||
.align 2
|
|
||||||
.set .LANCHOR0,. + 0
|
|
||||||
.type heap_area, @object
|
|
||||||
.size heap_area, 192
|
|
||||||
heap_area:
|
|
||||||
.zero 192
|
|
||||||
.section .sbss,"aw",@nobits
|
|
||||||
.align 2
|
|
||||||
.type g_total_free, @object
|
|
||||||
.size g_total_free, 4
|
|
||||||
g_total_free:
|
|
||||||
.zero 4
|
|
||||||
.type g_free_count, @object
|
|
||||||
.size g_free_count, 4
|
|
||||||
g_free_count:
|
|
||||||
.zero 4
|
|
||||||
.type g_order_ok, @object
|
|
||||||
.size g_order_ok, 4
|
|
||||||
g_order_ok:
|
|
||||||
.zero 4
|
|
||||||
.type end_marker, @object
|
|
||||||
.size end_marker, 4
|
|
||||||
end_marker:
|
|
||||||
.zero 4
|
|
||||||
.type start, @object
|
|
||||||
.size start, 8
|
|
||||||
start:
|
|
||||||
.zero 8
|
|
||||||
.ident "GCC: (15.2.0-23) 15.2.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
.file "task08_coalescing.c"
|
|
||||||
.option nopic
|
|
||||||
.attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0"
|
|
||||||
.attribute unaligned_access, 0
|
|
||||||
.attribute stack_align, 16
|
|
||||||
.text
|
|
||||||
.align 2
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
lla a5,.LANCHOR0
|
|
||||||
lla a4,.LANCHOR0+160
|
|
||||||
sw a4,end_marker,a3
|
|
||||||
sw a5,start,a3
|
|
||||||
lla a3,.LANCHOR0+96
|
|
||||||
sw a3,0(a5)
|
|
||||||
sw a4,96(a5)
|
|
||||||
sw zero,160(a5)
|
|
||||||
li a4,48
|
|
||||||
sw a4,4(a5)
|
|
||||||
sw a4,52(a5)
|
|
||||||
li a4,64
|
|
||||||
sw a4,100(a5)
|
|
||||||
sw zero,164(a5)
|
|
||||||
lla a5,start
|
|
||||||
lla a3,.LANCHOR0+48
|
|
||||||
.L2:
|
|
||||||
mv a4,a5
|
|
||||||
lw a5,0(a5)
|
|
||||||
bltu a5,a3,.L2
|
|
||||||
lla a3,.LANCHOR0+96
|
|
||||||
beq a5,a3,.L12
|
|
||||||
.L3:
|
|
||||||
sw a5,.LANCHOR0+48,a3
|
|
||||||
lla a5,start
|
|
||||||
lla a3,.LANCHOR0+48
|
|
||||||
beq a4,a5,.L4
|
|
||||||
lw a2,4(a4)
|
|
||||||
slli a5,a2,1
|
|
||||||
srli a5,a5,1
|
|
||||||
add a5,a4,a5
|
|
||||||
beq a5,a3,.L13
|
|
||||||
.L4:
|
|
||||||
sw a3,0(a4)
|
|
||||||
lw a2,start
|
|
||||||
lla a5,.LANCHOR0+160
|
|
||||||
beq a2,a5,.L9
|
|
||||||
mv a5,a2
|
|
||||||
li a4,0
|
|
||||||
lla a3,.LANCHOR0+160
|
|
||||||
.L6:
|
|
||||||
addi a4,a4,1
|
|
||||||
lw a5,0(a5)
|
|
||||||
bne a5,a3,.L6
|
|
||||||
.L5:
|
|
||||||
sw a4,g_free_count,a5
|
|
||||||
lw a5,4(a2)
|
|
||||||
sw a5,g_merged_size,a4
|
|
||||||
lw a5,0(a2)
|
|
||||||
lla a4,.LANCHOR0+160
|
|
||||||
sub a5,a5,a4
|
|
||||||
seqz a5,a5
|
|
||||||
sw a5,g_merged_with_right,a4
|
|
||||||
li a0,0
|
|
||||||
ret
|
|
||||||
.L12:
|
|
||||||
li a5,112
|
|
||||||
sw a5,.LANCHOR0+52,a3
|
|
||||||
lw a5,0(a4)
|
|
||||||
lw a5,0(a5)
|
|
||||||
j .L3
|
|
||||||
.L13:
|
|
||||||
lla a3,.LANCHOR0
|
|
||||||
lw a5,52(a3)
|
|
||||||
add a5,a5,a2
|
|
||||||
sw a5,4(a4)
|
|
||||||
lw a3,48(a3)
|
|
||||||
j .L4
|
|
||||||
.L9:
|
|
||||||
li a4,0
|
|
||||||
j .L5
|
|
||||||
.size main, .-main
|
|
||||||
.globl g_merged_with_right
|
|
||||||
.globl g_merged_size
|
|
||||||
.globl g_free_count
|
|
||||||
.bss
|
|
||||||
.align 2
|
|
||||||
.set .LANCHOR0,. + 0
|
|
||||||
.type heap_area, @object
|
|
||||||
.size heap_area, 192
|
|
||||||
heap_area:
|
|
||||||
.zero 192
|
|
||||||
.section .sbss,"aw",@nobits
|
|
||||||
.align 2
|
|
||||||
.type g_merged_with_right, @object
|
|
||||||
.size g_merged_with_right, 4
|
|
||||||
g_merged_with_right:
|
|
||||||
.zero 4
|
|
||||||
.type g_merged_size, @object
|
|
||||||
.size g_merged_size, 4
|
|
||||||
g_merged_size:
|
|
||||||
.zero 4
|
|
||||||
.type g_free_count, @object
|
|
||||||
.size g_free_count, 4
|
|
||||||
g_free_count:
|
|
||||||
.zero 4
|
|
||||||
.type end_marker, @object
|
|
||||||
.size end_marker, 4
|
|
||||||
end_marker:
|
|
||||||
.zero 4
|
|
||||||
.type start, @object
|
|
||||||
.size start, 8
|
|
||||||
start:
|
|
||||||
.zero 8
|
|
||||||
.ident "GCC: (15.2.0-23) 15.2.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
.file "task09_config_macros.c"
|
|
||||||
.option nopic
|
|
||||||
.attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0"
|
|
||||||
.attribute unaligned_access, 0
|
|
||||||
.attribute stack_align, 16
|
|
||||||
.text
|
|
||||||
.align 2
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
li a5,256
|
|
||||||
sw a5,g_total_heap,a4
|
|
||||||
li a5,248
|
|
||||||
sw a5,g_adjusted_heap,a4
|
|
||||||
li a5,1
|
|
||||||
sw a5,g_dynamic_enabled,a4
|
|
||||||
sw zero,g_trace_is_host_only,a5
|
|
||||||
li a0,0
|
|
||||||
ret
|
|
||||||
.size main, .-main
|
|
||||||
.globl g_trace_is_host_only
|
|
||||||
.globl g_dynamic_enabled
|
|
||||||
.globl g_adjusted_heap
|
|
||||||
.globl g_total_heap
|
|
||||||
.section .sbss,"aw",@nobits
|
|
||||||
.align 2
|
|
||||||
.type g_trace_is_host_only, @object
|
|
||||||
.size g_trace_is_host_only, 4
|
|
||||||
g_trace_is_host_only:
|
|
||||||
.zero 4
|
|
||||||
.type g_dynamic_enabled, @object
|
|
||||||
.size g_dynamic_enabled, 4
|
|
||||||
g_dynamic_enabled:
|
|
||||||
.zero 4
|
|
||||||
.type g_adjusted_heap, @object
|
|
||||||
.size g_adjusted_heap, 4
|
|
||||||
g_adjusted_heap:
|
|
||||||
.zero 4
|
|
||||||
.type g_total_heap, @object
|
|
||||||
.size g_total_heap, 4
|
|
||||||
g_total_heap:
|
|
||||||
.zero 4
|
|
||||||
.ident "GCC: (15.2.0-23) 15.2.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
@@ -1,253 +0,0 @@
|
|||||||
.file "task10_heap4_demo.c"
|
|
||||||
.option nopic
|
|
||||||
.attribute arch, "rv32i2p1_zicsr2p0_zifencei2p0"
|
|
||||||
.attribute unaligned_access, 0
|
|
||||||
.attribute stack_align, 16
|
|
||||||
.text
|
|
||||||
.align 2
|
|
||||||
.type heap_malloc_model, @function
|
|
||||||
heap_malloc_model:
|
|
||||||
beq a0,zero,.L1
|
|
||||||
addi a4,a0,8
|
|
||||||
andi a0,a0,7
|
|
||||||
beq a0,zero,.L3
|
|
||||||
andi a4,a4,-8
|
|
||||||
addi a4,a4,8
|
|
||||||
.L3:
|
|
||||||
lw a0,start
|
|
||||||
lw a3,end_marker
|
|
||||||
beq a0,a3,.L10
|
|
||||||
lla a2,start
|
|
||||||
j .L8
|
|
||||||
.L11:
|
|
||||||
mv a0,a5
|
|
||||||
.L8:
|
|
||||||
lw a5,4(a0)
|
|
||||||
bgeu a5,a4,.L12
|
|
||||||
lw a5,0(a0)
|
|
||||||
mv a2,a0
|
|
||||||
bne a5,a3,.L11
|
|
||||||
li a0,0
|
|
||||||
ret
|
|
||||||
.L12:
|
|
||||||
sub a3,a5,a4
|
|
||||||
li a1,16
|
|
||||||
bleu a3,a1,.L5
|
|
||||||
add a5,a0,a4
|
|
||||||
sw a3,4(a5)
|
|
||||||
lw a3,0(a0)
|
|
||||||
sw a3,0(a5)
|
|
||||||
sw a5,0(a2)
|
|
||||||
sw a4,4(a0)
|
|
||||||
.L6:
|
|
||||||
sw zero,0(a0)
|
|
||||||
lw a5,4(a0)
|
|
||||||
li a3,-2147483648
|
|
||||||
or a5,a5,a3
|
|
||||||
sw a5,4(a0)
|
|
||||||
lla a3,free_bytes_remaining
|
|
||||||
lw a5,0(a3)
|
|
||||||
sub a5,a5,a4
|
|
||||||
sw a5,0(a3)
|
|
||||||
lw a4,minimum_ever_free
|
|
||||||
bgeu a5,a4,.L7
|
|
||||||
sw a5,minimum_ever_free,a4
|
|
||||||
.L7:
|
|
||||||
addi a0,a0,8
|
|
||||||
ret
|
|
||||||
.L5:
|
|
||||||
lw a4,0(a0)
|
|
||||||
sw a4,0(a2)
|
|
||||||
mv a4,a5
|
|
||||||
j .L6
|
|
||||||
.L10:
|
|
||||||
li a0,0
|
|
||||||
.L1:
|
|
||||||
ret
|
|
||||||
.size heap_malloc_model, .-heap_malloc_model
|
|
||||||
.align 2
|
|
||||||
.type heap_free_model, @function
|
|
||||||
heap_free_model:
|
|
||||||
beq a0,zero,.L13
|
|
||||||
lw a5,-4(a0)
|
|
||||||
blt a5,zero,.L19
|
|
||||||
.L13:
|
|
||||||
ret
|
|
||||||
.L19:
|
|
||||||
addi a3,a0,-8
|
|
||||||
slli a5,a5,1
|
|
||||||
srli a2,a5,1
|
|
||||||
sw a2,-4(a0)
|
|
||||||
lla a4,free_bytes_remaining
|
|
||||||
lw a5,0(a4)
|
|
||||||
add a5,a5,a2
|
|
||||||
sw a5,0(a4)
|
|
||||||
lla a5,start
|
|
||||||
.L15:
|
|
||||||
mv a4,a5
|
|
||||||
lw a5,0(a5)
|
|
||||||
bgtu a3,a5,.L15
|
|
||||||
add a1,a3,a2
|
|
||||||
beq a5,a1,.L20
|
|
||||||
.L16:
|
|
||||||
sw a5,-8(a0)
|
|
||||||
lla a5,start
|
|
||||||
beq a4,a5,.L17
|
|
||||||
lw a2,4(a4)
|
|
||||||
slli a5,a2,1
|
|
||||||
srli a5,a5,1
|
|
||||||
add a5,a4,a5
|
|
||||||
beq a3,a5,.L21
|
|
||||||
.L17:
|
|
||||||
sw a3,0(a4)
|
|
||||||
ret
|
|
||||||
.L20:
|
|
||||||
lw a5,4(a5)
|
|
||||||
add a5,a5,a2
|
|
||||||
sw a5,-4(a0)
|
|
||||||
lw a5,0(a4)
|
|
||||||
lw a5,0(a5)
|
|
||||||
j .L16
|
|
||||||
.L21:
|
|
||||||
lw a5,-4(a0)
|
|
||||||
add a5,a5,a2
|
|
||||||
sw a5,4(a4)
|
|
||||||
lw a5,-8(a0)
|
|
||||||
sw a5,0(a4)
|
|
||||||
ret
|
|
||||||
.size heap_free_model, .-heap_free_model
|
|
||||||
.align 2
|
|
||||||
.globl main
|
|
||||||
.type main, @function
|
|
||||||
main:
|
|
||||||
addi sp,sp,-32
|
|
||||||
sw ra,28(sp)
|
|
||||||
sw s0,24(sp)
|
|
||||||
sw s1,20(sp)
|
|
||||||
sw s2,16(sp)
|
|
||||||
sw s3,12(sp)
|
|
||||||
sw s4,8(sp)
|
|
||||||
lla a4,.LANCHOR0
|
|
||||||
andi a3,a4,7
|
|
||||||
mv a5,a4
|
|
||||||
beq a3,zero,.L23
|
|
||||||
andi a4,a4,-8
|
|
||||||
addi a5,a4,8
|
|
||||||
.L23:
|
|
||||||
lla s0,.LANCHOR0+256
|
|
||||||
sub s0,s0,a5
|
|
||||||
andi s0,s0,-8
|
|
||||||
addi a4,a5,-8
|
|
||||||
add s0,s0,a4
|
|
||||||
sw s0,end_marker,a4
|
|
||||||
sw zero,0(s0)
|
|
||||||
sw zero,4(s0)
|
|
||||||
sw s0,0(a5)
|
|
||||||
sub a4,s0,a5
|
|
||||||
sw a4,4(a5)
|
|
||||||
lla s4,start
|
|
||||||
sw a5,0(s4)
|
|
||||||
sw zero,4(s4)
|
|
||||||
sw a4,free_bytes_remaining,a5
|
|
||||||
sw a4,minimum_ever_free,a5
|
|
||||||
li a0,24
|
|
||||||
call heap_malloc_model
|
|
||||||
mv s2,a0
|
|
||||||
li a0,40
|
|
||||||
call heap_malloc_model
|
|
||||||
mv s3,a0
|
|
||||||
mv a0,s2
|
|
||||||
call heap_free_model
|
|
||||||
li a0,16
|
|
||||||
call heap_malloc_model
|
|
||||||
mv s1,a0
|
|
||||||
mv a0,s3
|
|
||||||
call heap_free_model
|
|
||||||
mv a0,s1
|
|
||||||
call heap_free_model
|
|
||||||
snez s2,s2
|
|
||||||
snez s3,s3
|
|
||||||
and s2,s2,s3
|
|
||||||
snez s1,s1
|
|
||||||
and s1,s1,s2
|
|
||||||
sw s1,g_allocations_ok,a5
|
|
||||||
lw a5,0(s4)
|
|
||||||
beq s0,a5,.L24
|
|
||||||
mv a4,a5
|
|
||||||
li a3,0
|
|
||||||
.L25:
|
|
||||||
addi a3,a3,1
|
|
||||||
lw a4,0(a4)
|
|
||||||
bne s0,a4,.L25
|
|
||||||
sw a3,g_final_free_nodes,a2
|
|
||||||
li a3,0
|
|
||||||
.L26:
|
|
||||||
lw a2,4(a5)
|
|
||||||
add a3,a3,a2
|
|
||||||
lw a5,0(a5)
|
|
||||||
bne a5,a4,.L26
|
|
||||||
.L27:
|
|
||||||
sw a3,g_final_free_total,a5
|
|
||||||
lw a5,minimum_ever_free
|
|
||||||
sw a5,g_minimum_ever_free,a4
|
|
||||||
li a0,0
|
|
||||||
lw ra,28(sp)
|
|
||||||
lw s0,24(sp)
|
|
||||||
lw s1,20(sp)
|
|
||||||
lw s2,16(sp)
|
|
||||||
lw s3,12(sp)
|
|
||||||
lw s4,8(sp)
|
|
||||||
addi sp,sp,32
|
|
||||||
jr ra
|
|
||||||
.L24:
|
|
||||||
sw zero,g_final_free_nodes,a5
|
|
||||||
li a3,0
|
|
||||||
j .L27
|
|
||||||
.size main, .-main
|
|
||||||
.globl g_minimum_ever_free
|
|
||||||
.globl g_final_free_total
|
|
||||||
.globl g_final_free_nodes
|
|
||||||
.globl g_allocations_ok
|
|
||||||
.bss
|
|
||||||
.align 2
|
|
||||||
.set .LANCHOR0,. + 0
|
|
||||||
.type heap_area, @object
|
|
||||||
.size heap_area, 264
|
|
||||||
heap_area:
|
|
||||||
.zero 264
|
|
||||||
.section .sbss,"aw",@nobits
|
|
||||||
.align 2
|
|
||||||
.type minimum_ever_free, @object
|
|
||||||
.size minimum_ever_free, 4
|
|
||||||
minimum_ever_free:
|
|
||||||
.zero 4
|
|
||||||
.type free_bytes_remaining, @object
|
|
||||||
.size free_bytes_remaining, 4
|
|
||||||
free_bytes_remaining:
|
|
||||||
.zero 4
|
|
||||||
.type g_minimum_ever_free, @object
|
|
||||||
.size g_minimum_ever_free, 4
|
|
||||||
g_minimum_ever_free:
|
|
||||||
.zero 4
|
|
||||||
.type g_final_free_total, @object
|
|
||||||
.size g_final_free_total, 4
|
|
||||||
g_final_free_total:
|
|
||||||
.zero 4
|
|
||||||
.type g_final_free_nodes, @object
|
|
||||||
.size g_final_free_nodes, 4
|
|
||||||
g_final_free_nodes:
|
|
||||||
.zero 4
|
|
||||||
.type g_allocations_ok, @object
|
|
||||||
.size g_allocations_ok, 4
|
|
||||||
g_allocations_ok:
|
|
||||||
.zero 4
|
|
||||||
.type end_marker, @object
|
|
||||||
.size end_marker, 4
|
|
||||||
end_marker:
|
|
||||||
.zero 4
|
|
||||||
.type start, @object
|
|
||||||
.size start, 8
|
|
||||||
start:
|
|
||||||
.zero 8
|
|
||||||
.ident "GCC: (15.2.0-23) 15.2.0"
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,31 @@
|
|||||||
|
@startuml
|
||||||
|
scale 600 height
|
||||||
|
top to bottom direction
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam defaultFontName Monospace
|
||||||
|
skinparam defaultFontSize 10
|
||||||
|
skinparam rectangleBorderColor #2c7794
|
||||||
|
skinparam rectangleBackgroundColor #edf6fa
|
||||||
|
skinparam ArrowColor #8095a1
|
||||||
|
title A1 CONTEXT — from linear cursor to reusable heap
|
||||||
|
|
||||||
|
rectangle "<<application>>\n01 A / B / C experiment\nrequests payload bytes" as APP
|
||||||
|
rectangle "<<public API>>\n02 pvPortMalloc / vPortFree\nheap statistics" as API
|
||||||
|
rectangle "<<allocator>>\n03 heap_4\nfirst-fit · split · coalesce" as H4
|
||||||
|
rectangle "<<arena>>\n04 ucHeap[4096]\nheaders + payload" as ARENA
|
||||||
|
rectangle "<<target>>\n05 Hazard3 / RV32I\nGDB · registers · memory" as TARGET
|
||||||
|
|
||||||
|
APP -down-> API : request / release
|
||||||
|
API -down-> H4 : direct C call
|
||||||
|
H4 -down-> ARENA : manages
|
||||||
|
ARENA -down-> TARGET : stored in RAM
|
||||||
|
|
||||||
|
note right of H4
|
||||||
|
heap_1: allocate only
|
||||||
|
heap_2: free, no coalesce
|
||||||
|
heap_3: libc wrapper
|
||||||
|
heap_4: free + coalesce
|
||||||
|
heap_5: multiple regions
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="600.3488px" preserveAspectRatio="none" style="width:311px;height:600px;" version="1.1" viewBox="0 0 311 600" width="311.19px" zoomAndPan="magnify"><defs/><g><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="227.6552" x="44.9803" y="9.813">A1 CONTEXT — from linear cursor to reusable heap</text><!--MD5=[cd187d11655e4367e900de538d2f1d0c]
|
||||||
|
entity APP--><rect fill="#EDF6FA" height="55.8673" style="stroke: #2C7794; stroke-width: 1.3769468762104042;" width="121.1713" x="15.6054" y="20.2686"/><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="58.7497" x="24.785" y="39.2613">«application»</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="102.812" x="24.785" y="51.764">01 A / B / C experiment</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="102.812" x="24.785" y="64.2667">requests payload bytes</text><!--MD5=[645d8587175d8f7ad110cd370775931d]
|
||||||
|
entity API--><rect fill="#EDF6FA" height="55.8673" style="stroke: #2C7794; stroke-width: 1.3769468762104042;" width="141.3665" x="5.5078" y="144.9833"/><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="52.324" x="14.6874" y="163.976">«public API»</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="123.0073" x="14.6874" y="176.4787">02 pvPortMalloc / vPortFree</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="65.1755" x="14.6874" y="188.9813">heap statistics</text><!--MD5=[46e692d45a0b60d87b097c4043ce42a2]
|
||||||
|
entity H4--><rect fill="#EDF6FA" height="55.8673" style="stroke: #2C7794; stroke-width: 1.3769468762104042;" width="122.0893" x="15.1464" y="277.6108"/><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="48.6521" x="24.3261" y="296.6035">«allocator»</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="44.9803" x="24.3261" y="309.1062">03 heap_4</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="103.73" x="24.3261" y="321.6088">first-fit · split · coalesce</text><!--MD5=[a55d5013c595d66e4d3c908e0eed28a7]
|
||||||
|
entity ARENA--><rect fill="#EDF6FA" height="55.8673" style="stroke: #2C7794; stroke-width: 1.3769468762104042;" width="100.0581" x="26.162" y="410.2384"/><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="34.8827" x="35.3416" y="429.231">«arena»</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="74.3551" x="35.3416" y="441.7337">04 ucHeap[4096]</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="81.6988" x="35.3416" y="454.2364">headers + payload</text><!--MD5=[aba7613092619c8a5fef3de1c87efe89]
|
||||||
|
entity TARGET--><rect fill="#EDF6FA" height="55.8673" style="stroke: #2C7794; stroke-width: 1.3769468762104042;" width="129.433" x="11.4746" y="534.953"/><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="36.7186" x="20.6542" y="553.9457">«target»</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="84.4527" x="20.6542" y="566.4484">05 Hazard3 / RV32I</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="111.0737" x="20.6542" y="578.951">GDB · registers · memory</text><path d="M169.3645,269.698 L169.3645,301.8726 L137.6029,305.5445 L169.3645,309.2164 L169.3645,341.391 A0,0 0 0 0 169.3645,341.391 L300.6334,341.391 A0,0 0 0 0 300.6334,341.391 L300.6334,278.8776 L291.4538,269.698 L169.3645,269.698 A0,0 0 0 0 169.3645,269.698 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 0.9179645841402695;"/><path d="M291.4538,269.698 L291.4538,278.8776 L300.6334,278.8776 L291.4538,269.698 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 0.9179645841402695;"/><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="93.6324" x="174.8723" y="284.1008">heap_1: allocate only</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="111.9917" x="174.8723" y="296.6035">heap_2: free, no coalesce</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="91.7965" x="174.8723" y="309.1062">heap_3: libc wrapper</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="103.73" x="174.8723" y="321.6088">heap_4: free + coalesce</text><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="109.2378" x="174.8723" y="334.1115">heap_5: multiple regions</text><!--MD5=[ec942d3abfeef98a0155e2f031d4c131]
|
||||||
|
link APP to API--><path d="M76.1911,76.393 C76.1911,94.8441 76.1911,119.3262 76.1911,138.8972 " fill="none" id="APP->API" style="stroke: #8095A1; stroke-width: 0.9179645841402695;"/><polygon fill="#8095A1" points="76.1911,143.4136,79.8629,135.1519,76.1911,138.8238,72.5192,135.1519,76.1911,143.4136" style="stroke: #8095A1; stroke-width: 0.9179645841402695;"/><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="76.1911" x="77.109" y="114.4059">request / release</text><!--MD5=[798ded9ffd9ac635cdedeacba358c97c]
|
||||||
|
link API to H4--><path d="M76.1911,201.2545 C76.1911,221.6609 76.1911,249.6129 76.1911,271.3044 " fill="none" id="API->H4" style="stroke: #8095A1; stroke-width: 0.9179645841402695;"/><polygon fill="#8095A1" points="76.1911,275.8575,79.8629,267.5958,76.1911,271.2677,72.5192,267.5958,76.1911,275.8575" style="stroke: #8095A1; stroke-width: 0.9179645841402695;"/><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="52.324" x="77.109" y="239.1206">direct C call</text><!--MD5=[26d2234bc3ca60bb174d49bba6317b2c]
|
||||||
|
link H4 to ARENA--><path d="M76.1911,333.8821 C76.1911,354.2884 76.1911,382.2404 76.1911,403.9319 " fill="none" id="H4->ARENA" style="stroke: #8095A1; stroke-width: 0.9179645841402695;"/><polygon fill="#8095A1" points="76.1911,408.485,79.8629,400.2234,76.1911,403.8952,72.5192,400.2234,76.1911,408.485" style="stroke: #8095A1; stroke-width: 0.9179645841402695;"/><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="40.3904" x="77.109" y="379.661">manages</text><!--MD5=[057090cc36f41caea0c560281f72f0d5]
|
||||||
|
link ARENA to TARGET--><path d="M76.1911,466.3627 C76.1911,484.8138 76.1911,509.2959 76.1911,528.8669 " fill="none" id="ARENA->TARGET" style="stroke: #8095A1; stroke-width: 0.9179645841402695;"/><polygon fill="#8095A1" points="76.1911,533.3833,79.8629,525.1216,76.1911,528.7935,72.5192,525.1216,76.1911,533.3833" style="stroke: #8095A1; stroke-width: 0.9179645841402695;"/><text fill="#000000" font-family="Monospace" font-size="9.1796" lengthAdjust="spacingAndGlyphs" textLength="61.5036" x="77.109" y="504.3756">stored in RAM</text><!--MD5=[5f927a6771e8d55dac04e3c75dd184e1]
|
||||||
|
@startuml
|
||||||
|
scale 600 height
|
||||||
|
top to bottom direction
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam defaultFontName Monospace
|
||||||
|
skinparam defaultFontSize 10
|
||||||
|
skinparam rectangleBorderColor #2c7794
|
||||||
|
skinparam rectangleBackgroundColor #edf6fa
|
||||||
|
skinparam ArrowColor #8095a1
|
||||||
|
title A1 CONTEXT — from linear cursor to reusable heap
|
||||||
|
|
||||||
|
rectangle "<<application>>\n01 A / B / C experiment\nrequests payload bytes" as APP
|
||||||
|
rectangle "<<public API>>\n02 pvPortMalloc / vPortFree\nheap statistics" as API
|
||||||
|
rectangle "<<allocator>>\n03 heap_4\nfirst-fit · split · coalesce" as H4
|
||||||
|
rectangle "<<arena>>\n04 ucHeap[4096]\nheaders + payload" as ARENA
|
||||||
|
rectangle "<<target>>\n05 Hazard3 / RV32I\nGDB · registers · memory" as TARGET
|
||||||
|
|
||||||
|
APP -down-> API : request / release
|
||||||
|
API -down-> H4 : direct C call
|
||||||
|
H4 -down-> ARENA : manages
|
||||||
|
ARENA -down-> TARGET : stored in RAM
|
||||||
|
|
||||||
|
note right of H4
|
||||||
|
heap_1: allocate only
|
||||||
|
heap_2: free, no coalesce
|
||||||
|
heap_3: libc wrapper
|
||||||
|
heap_4: free + coalesce
|
||||||
|
heap_5: multiple regions
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
|
|
||||||
|
PlantUML version 1.2020.02(Sun Mar 01 11:22:07 CET 2020)
|
||||||
|
(GPL source distribution)
|
||||||
|
Java Runtime: OpenJDK Runtime Environment
|
||||||
|
JVM: OpenJDK 64-Bit Server VM
|
||||||
|
Java Version: 25.0.4-ea+4-1-Debian
|
||||||
|
Operating System: Linux
|
||||||
|
Default Encoding: UTF-8
|
||||||
|
Language: en
|
||||||
|
Country: null
|
||||||
|
--></g></svg>
|
||||||
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,53 @@
|
|||||||
|
@startuml
|
||||||
|
scale 620 height
|
||||||
|
top to bottom direction
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam defaultFontName Monospace
|
||||||
|
skinparam defaultFontSize 10
|
||||||
|
skinparam classBorderColor #2c7794
|
||||||
|
skinparam classBackgroundColor #edf6fa
|
||||||
|
skinparam ArrowColor #8095a1
|
||||||
|
title A2 STRUCTURE — RV32 header, split and two-sided coalescing
|
||||||
|
|
||||||
|
class "01 Block header" as HEADER {
|
||||||
|
next : BlockLink_t*
|
||||||
|
size : size_t
|
||||||
|
raw sizeof = 8 bytes on RV32
|
||||||
|
aligned xHeapStructSize = 16
|
||||||
|
}
|
||||||
|
class "Allocated block" as ALLOC {
|
||||||
|
02 A: header + 24 byte request
|
||||||
|
aligned total = 48 bytes
|
||||||
|
payload begins after header
|
||||||
|
}
|
||||||
|
class "03 Free remainder" as REM {
|
||||||
|
own header
|
||||||
|
free = 4032 bytes after A
|
||||||
|
}
|
||||||
|
|
||||||
|
HEADER *-- ALLOC : precedes payload
|
||||||
|
ALLOC -down-> REM : first-fit + split
|
||||||
|
|
||||||
|
class "04 Address-ordered free list" as LIST {
|
||||||
|
previous < inserted < current
|
||||||
|
A < B < C+tail
|
||||||
|
}
|
||||||
|
class "05 Previous / left neighbour" as LEFT {
|
||||||
|
previous + previous.size == inserted
|
||||||
|
merge previous first
|
||||||
|
}
|
||||||
|
class "06 Next / right neighbour" as RIGHT {
|
||||||
|
merged + merged.size == current
|
||||||
|
merge next second
|
||||||
|
}
|
||||||
|
|
||||||
|
REM -down-> LIST : insert by address
|
||||||
|
LIST -down-> LEFT : heap_4 tests first
|
||||||
|
LEFT -down-> RIGHT : then tests next
|
||||||
|
|
||||||
|
note right of LIST
|
||||||
|
Address order exposes both physical
|
||||||
|
neighbours without scanning the arena.
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="620.2694px" preserveAspectRatio="none" style="width:312px;height:620px;" version="1.1" viewBox="0 0 312 620" width="312.6158px" zoomAndPan="magnify"><defs/><g><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="211.9549" x="52.4571" y="7.5779">A2 STRUCTURE — RV32 header, split and two-sided coalescing</text><!--MD5=[9827fb78ff6e3f59cfadf959dc26af7c]
|
||||||
|
class HEADER--><rect fill="#EDF6FA" height="69.8104" id="HEADER" style="stroke: #2C7794; stroke-width: 1.0633189422886131;" width="107.7497" x="16.3042" y="15.1133"/><ellipse cx="40.1935" cy="25.0376" fill="#ADD1B2" rx="6.3799" ry="6.3799" style="stroke: #2C7794; stroke-width: 0.7088792948590754;"/><path d="M41.1017,27.3747 Q40.858,27.4965 40.5922,27.5519 Q40.3264,27.6184 40.0273,27.6184 Q38.9861,27.6184 38.4323,26.9316 Q37.8896,26.2449 37.8896,24.9379 Q37.8896,23.6309 38.4323,22.9442 Q38.9861,22.2575 40.0273,22.2575 Q40.3264,22.2575 40.5922,22.3239 Q40.8691,22.3793 41.1017,22.5011 L41.1017,23.642 Q40.8359,23.3983 40.5811,23.2876 Q40.3374,23.1768 40.0827,23.1768 Q39.5178,23.1768 39.2298,23.6198 Q38.9418,24.0629 38.9418,24.9379 Q38.9418,25.8129 39.2298,26.256 Q39.5178,26.699 40.0827,26.699 Q40.3374,26.699 40.5811,26.5883 Q40.8359,26.4775 41.1017,26.2338 L41.1017,27.3747 Z "/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="55.2926" x="51.9609" y="27.7881">01 Block header</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="17.0131" x2="123.345" y1="34.9619" y2="34.9619"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="63.0903" x="20.5575" y="45.3753">next : BlockLink_t*</text><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="38.9884" x="20.5575" y="55.0303">size : size_t</text><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="97.8253" x="20.5575" y="64.6852">raw sizeof = 8 bytes on RV32</text><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="99.2431" x="20.5575" y="74.3401">aligned xHeapStructSize = 16</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="17.0131" x2="123.345" y1="79.2527" y2="79.2527"/><!--MD5=[40d637119be40bcfdc4ce848efbdda9e]
|
||||||
|
class ALLOC--><rect fill="#EDF6FA" height="60.1555" id="ALLOC" style="stroke: #2C7794; stroke-width: 1.0633189422886131;" width="114.1296" x="13.1143" y="138.0897"/><ellipse cx="41.6467" cy="148.014" fill="#ADD1B2" rx="6.3799" ry="6.3799" style="stroke: #2C7794; stroke-width: 0.7088792948590754;"/><path d="M42.5549,150.3511 Q42.3112,150.4729 42.0454,150.5283 Q41.7796,150.5948 41.4805,150.5948 Q40.4393,150.5948 39.8855,149.908 Q39.3428,149.2213 39.3428,147.9143 Q39.3428,146.6073 39.8855,145.9206 Q40.4393,145.2339 41.4805,145.2339 Q41.7796,145.2339 42.0454,145.3003 Q42.3223,145.3557 42.5549,145.4775 L42.5549,146.6184 Q42.2891,146.3747 42.0343,146.2639 Q41.7906,146.1532 41.5359,146.1532 Q40.971,146.1532 40.683,146.5962 Q40.395,147.0393 40.395,147.9143 Q40.395,148.7893 40.683,149.2324 Q40.971,149.6754 41.5359,149.6754 Q41.7906,149.6754 42.0343,149.5647 Q42.2891,149.4539 42.5549,149.2102 L42.5549,150.3511 Z "/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="51.7482" x="54.052" y="150.7644">Allocated block</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="13.8231" x2="126.535" y1="157.9383" y2="157.9383"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="105.623" x="17.3675" y="168.3517">02 A: header + 24 byte request</text><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="81.5211" x="17.3675" y="178.0067">aligned total = 48 bytes</text><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="96.4076" x="17.3675" y="187.6616">payload begins after header</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="13.8231" x2="126.535" y1="192.5741" y2="192.5741"/><!--MD5=[71c2adef9be32489055f250a531e7c6f]
|
||||||
|
class REM--><rect fill="#EDF6FA" height="50.5005" id="REM" style="stroke: #2C7794; stroke-width: 1.0633189422886131;" width="92.8632" x="23.7475" y="251.4111"/><ellipse cx="37.4288" cy="261.3354" fill="#ADD1B2" rx="6.3799" ry="6.3799" style="stroke: #2C7794; stroke-width: 0.7088792948590754;"/><path d="M38.3371,263.6725 Q38.0934,263.7944 37.8276,263.8497 Q37.5617,263.9162 37.2627,263.9162 Q36.2215,263.9162 35.6677,263.2295 Q35.125,262.5427 35.125,261.2357 Q35.125,259.9287 35.6677,259.242 Q36.2215,258.5553 37.2627,258.5553 Q37.5617,258.5553 37.8276,258.6218 Q38.1045,258.6771 38.3371,258.799 L38.3371,259.9398 Q38.0712,259.6961 37.8165,259.5854 Q37.5728,259.4746 37.3181,259.4746 Q36.7532,259.4746 36.4652,259.9177 Q36.1772,260.3607 36.1772,261.2357 Q36.1772,262.1108 36.4652,262.5538 Q36.7532,262.9969 37.3181,262.9969 Q37.5728,262.9969 37.8165,262.8861 Q38.0712,262.7753 38.3371,262.5317 L38.3371,263.6725 Z "/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="63.0903" x="46.9278" y="264.0859">03 Free remainder</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="24.4563" x2="115.9018" y1="271.2597" y2="271.2597"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="40.4061" x="28.0007" y="281.6732">own header</text><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="84.3566" x="28.0007" y="291.3281">free = 4032 bytes after A</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="24.4563" x2="115.9018" y1="296.2406" y2="296.2406"/><!--MD5=[b0a0edd67d9eafaaa5639bbe6382adce]
|
||||||
|
class LIST--><rect fill="#EDF6FA" height="50.5005" id="LIST" style="stroke: #2C7794; stroke-width: 1.0633189422886131;" width="114.8384" x="12.7598" y="355.0776"/><ellipse cx="21.9753" cy="365.0019" fill="#ADD1B2" rx="6.3799" ry="6.3799" style="stroke: #2C7794; stroke-width: 0.7088792948590754;"/><path d="M22.8835,367.339 Q22.6398,367.4609 22.374,367.5162 Q22.1082,367.5827 21.8091,367.5827 Q20.7679,367.5827 20.2141,366.896 Q19.6714,366.2093 19.6714,364.9023 Q19.6714,363.5953 20.2141,362.9085 Q20.7679,362.2218 21.8091,362.2218 Q22.1082,362.2218 22.374,362.2883 Q22.6509,362.3436 22.8835,362.4655 L22.8835,363.6063 Q22.6177,363.3627 22.3629,363.2519 Q22.1192,363.1411 21.8645,363.1411 Q21.2996,363.1411 21.0116,363.5842 Q20.7236,364.0272 20.7236,364.9023 Q20.7236,365.7773 21.0116,366.2203 Q21.2996,366.6634 21.8645,366.6634 Q22.1192,366.6634 22.3629,366.5526 Q22.6177,366.4419 22.8835,366.1982 L22.8835,367.339 Z "/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="94.9898" x="30.4818" y="367.7524">04 Address-ordered free list</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="13.4687" x2="126.8894" y1="374.9263" y2="374.9263"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="99.2431" x="17.0131" y="385.3397">previous < inserted < current</text><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="46.0772" x="17.0131" y="394.9946">A < B < C+tail</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="13.4687" x2="126.8894" y1="399.9071" y2="399.9071"/><!--MD5=[007d3a1820cefd008c8dbdad7e6fdec6]
|
||||||
|
class LEFT--><rect fill="#EDF6FA" height="50.5005" id="LEFT" style="stroke: #2C7794; stroke-width: 1.0633189422886131;" width="131.8515" x="4.2533" y="458.7441"/><ellipse cx="21.1246" cy="468.6684" fill="#ADD1B2" rx="6.3799" ry="6.3799" style="stroke: #2C7794; stroke-width: 0.7088792948590754;"/><path d="M22.0329,471.0055 Q21.7892,471.1274 21.5233,471.1828 Q21.2575,471.2492 20.9585,471.2492 Q19.9173,471.2492 19.3635,470.5625 Q18.8207,469.8758 18.8207,468.5688 Q18.8207,467.2618 19.3635,466.575 Q19.9173,465.8883 20.9585,465.8883 Q21.2575,465.8883 21.5233,465.9548 Q21.8003,466.0102 22.0329,466.132 L22.0329,467.2728 Q21.767,467.0292 21.5123,466.9184 Q21.2686,466.8076 21.0138,466.8076 Q20.449,466.8076 20.161,467.2507 Q19.873,467.6937 19.873,468.5688 Q19.873,469.4438 20.161,469.8868 Q20.449,470.3299 21.0138,470.3299 Q21.2686,470.3299 21.5123,470.2191 Q21.767,470.1084 22.0329,469.8647 L22.0329,471.0055 Z "/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="94.9898" x="31.3325" y="471.4189">05 Previous / left neighbour</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="4.9622" x2="135.3959" y1="478.5928" y2="478.5928"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="123.345" x="8.5066" y="489.0062">previous + previous.size == inserted</text><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="68.7613" x="8.5066" y="498.6611">merge previous first</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="4.9622" x2="135.3959" y1="503.5736" y2="503.5736"/><!--MD5=[490fcd32b6546c9bc615869efabd094a]
|
||||||
|
class RIGHT--><rect fill="#EDF6FA" height="50.5005" id="RIGHT" style="stroke: #2C7794; stroke-width: 1.0633189422886131;" width="122.6361" x="8.861" y="562.4106"/><ellipse cx="25.0943" cy="572.335" fill="#ADD1B2" rx="6.3799" ry="6.3799" style="stroke: #2C7794; stroke-width: 0.7088792948590754;"/><path d="M26.0026,574.672 Q25.7589,574.7939 25.4931,574.8493 Q25.2272,574.9157 24.9282,574.9157 Q23.887,574.9157 23.3332,574.229 Q22.7905,573.5423 22.7905,572.2353 Q22.7905,570.9283 23.3332,570.2415 Q23.887,569.5548 24.9282,569.5548 Q25.2272,569.5548 25.4931,569.6213 Q25.77,569.6767 26.0026,569.7985 L26.0026,570.9394 Q25.7367,570.6957 25.482,570.5849 Q25.2383,570.4741 24.9836,570.4741 Q24.4187,570.4741 24.1307,570.9172 Q23.8427,571.3602 23.8427,572.2353 Q23.8427,573.1103 24.1307,573.5533 Q24.4187,573.9964 24.9836,573.9964 Q25.2383,573.9964 25.482,573.8856 Q25.7367,573.7749 26.0026,573.5312 L26.0026,574.672 Z "/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="87.1922" x="35.1604" y="575.0854">06 Next / right neighbour</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="9.5699" x2="130.7882" y1="582.2593" y2="582.2593"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="114.1296" x="13.1143" y="592.6727">merged + merged.size == current</text><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="65.2169" x="13.1143" y="602.3276">merge next second</text><line style="stroke: #2C7794; stroke-width: 1.0633189422886131;" x1="9.5699" x2="130.7882" y1="607.2402" y2="607.2402"/><path d="M152.409,367.1286 L152.409,377.4924 L127.9173,380.3279 L152.409,383.1634 L152.409,393.5272 A0,0 0 0 0 152.409,393.5272 L304.1092,393.5272 A0,0 0 0 0 304.1092,393.5272 L304.1092,374.2174 L297.0204,367.1286 L152.409,367.1286 A0,0 0 0 0 152.409,367.1286 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 0.7088792948590754;"/><path d="M297.0204,367.1286 L297.0204,374.2174 L304.1092,374.2174 L297.0204,367.1286 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 0.7088792948590754;"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="124.7628" x="156.6623" y="378.2509">Address order exposes both physical</text><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="136.8137" x="156.6623" y="387.9058">neighbours without scanning the arena.</text><!--MD5=[fc2657b7de304bb41a5d36ac60748945]
|
||||||
|
reverse link HEADER to ALLOC--><path d="M70.1791,95.245 C70.1791,109.5006 70.1791,124.9329 70.1791,137.8557 " fill="none" id="HEADER<-ALLOC" style="stroke: #8095A1; stroke-width: 0.7088792948590754;"/><polygon fill="#8095A1" points="70.1791,86.1926,67.3435,90.4459,70.1791,94.6992,73.0146,90.4459,70.1791,86.1926" style="stroke: #8095A1; stroke-width: 0.7088792948590754;"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="60.2547" x="70.8879" y="114.4769">precedes payload</text><!--MD5=[bb73b5a7e21afff1a1d0c43dcbd5d11a]
|
||||||
|
link ALLOC to REM--><path d="M70.1791,198.4224 C70.1791,213.4506 70.1791,231.6688 70.1791,246.6687 " fill="none" id="ALLOC->REM" style="stroke: #8095A1; stroke-width: 0.7088792948590754;"/><polygon fill="#8095A1" points="70.1791,250.1422,73.0146,243.7623,70.1791,246.5978,67.3435,243.7623,70.1791,250.1422" style="stroke: #8095A1; stroke-width: 0.7088792948590754;"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="46.0772" x="70.8879" y="227.7983">first-fit + split</text><!--MD5=[32aec76f28e14f88b0d74d9f592c80fb]
|
||||||
|
link REM to LIST--><path d="M70.1791,302.1243 C70.1791,316.5855 70.1791,334.9242 70.1791,350.1297 " fill="none" id="REM->LIST" style="stroke: #8095A1; stroke-width: 0.7088792948590754;"/><polygon fill="#8095A1" points="70.1791,353.667,73.0146,347.287,70.1791,350.1226,67.3435,347.287,70.1791,353.667" style="stroke: #8095A1; stroke-width: 0.7088792948590754;"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="58.837" x="70.8879" y="331.4649">insert by address</text><!--MD5=[cc3605e5f4ba919f564e1d79e1300518]
|
||||||
|
link LIST to LEFT--><path d="M70.1791,405.7909 C70.1791,420.252 70.1791,438.5907 70.1791,453.7962 " fill="none" id="LIST->LEFT" style="stroke: #8095A1; stroke-width: 0.7088792948590754;"/><polygon fill="#8095A1" points="70.1791,457.3335,73.0146,450.9536,70.1791,453.7891,67.3435,450.9536,70.1791,457.3335" style="stroke: #8095A1; stroke-width: 0.7088792948590754;"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="58.837" x="70.8879" y="435.1314">heap_4 tests first</text><!--MD5=[e8df39c6ef374d140d6cfa6fe0b5ac87]
|
||||||
|
link LEFT to RIGHT--><path d="M70.1791,509.4574 C70.1791,523.9185 70.1791,542.2572 70.1791,557.4627 " fill="none" id="LEFT->RIGHT" style="stroke: #8095A1; stroke-width: 0.7088792948590754;"/><polygon fill="#8095A1" points="70.1791,561,73.0146,554.6201,70.1791,557.4556,67.3435,554.6201,70.1791,561" style="stroke: #8095A1; stroke-width: 0.7088792948590754;"/><text fill="#000000" font-family="Monospace" font-size="7.0888" lengthAdjust="spacingAndGlyphs" textLength="51.7482" x="70.8879" y="538.7979">then tests next</text><!--MD5=[d8b34fb36bd2f73cd895320cbfc22e63]
|
||||||
|
@startuml
|
||||||
|
scale 620 height
|
||||||
|
top to bottom direction
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam defaultFontName Monospace
|
||||||
|
skinparam defaultFontSize 10
|
||||||
|
skinparam classBorderColor #2c7794
|
||||||
|
skinparam classBackgroundColor #edf6fa
|
||||||
|
skinparam ArrowColor #8095a1
|
||||||
|
title A2 STRUCTURE — RV32 header, split and two-sided coalescing
|
||||||
|
|
||||||
|
class "01 Block header" as HEADER {
|
||||||
|
next : BlockLink_t*
|
||||||
|
size : size_t
|
||||||
|
raw sizeof = 8 bytes on RV32
|
||||||
|
aligned xHeapStructSize = 16
|
||||||
|
}
|
||||||
|
class "Allocated block" as ALLOC {
|
||||||
|
02 A: header + 24 byte request
|
||||||
|
aligned total = 48 bytes
|
||||||
|
payload begins after header
|
||||||
|
}
|
||||||
|
class "03 Free remainder" as REM {
|
||||||
|
own header
|
||||||
|
free = 4032 bytes after A
|
||||||
|
}
|
||||||
|
|
||||||
|
HEADER *- - ALLOC : precedes payload
|
||||||
|
ALLOC -down-> REM : first-fit + split
|
||||||
|
|
||||||
|
class "04 Address-ordered free list" as LIST {
|
||||||
|
previous < inserted < current
|
||||||
|
A < B < C+tail
|
||||||
|
}
|
||||||
|
class "05 Previous / left neighbour" as LEFT {
|
||||||
|
previous + previous.size == inserted
|
||||||
|
merge previous first
|
||||||
|
}
|
||||||
|
class "06 Next / right neighbour" as RIGHT {
|
||||||
|
merged + merged.size == current
|
||||||
|
merge next second
|
||||||
|
}
|
||||||
|
|
||||||
|
REM -down-> LIST : insert by address
|
||||||
|
LIST -down-> LEFT : heap_4 tests first
|
||||||
|
LEFT -down-> RIGHT : then tests next
|
||||||
|
|
||||||
|
note right of LIST
|
||||||
|
Address order exposes both physical
|
||||||
|
neighbours without scanning the arena.
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
|
|
||||||
|
PlantUML version 1.2020.02(Sun Mar 01 11:22:07 CET 2020)
|
||||||
|
(GPL source distribution)
|
||||||
|
Java Runtime: OpenJDK Runtime Environment
|
||||||
|
JVM: OpenJDK 64-Bit Server VM
|
||||||
|
Java Version: 25.0.4-ea+4-1-Debian
|
||||||
|
Operating System: Linux
|
||||||
|
Default Encoding: UTF-8
|
||||||
|
Language: en
|
||||||
|
Country: null
|
||||||
|
--></g></svg>
|
||||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,29 @@
|
|||||||
|
@startuml
|
||||||
|
scale 820 width
|
||||||
|
top to bottom direction
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam defaultFontName Monospace
|
||||||
|
skinparam defaultFontSize 9
|
||||||
|
skinparam rectangleBorderColor #2c7794
|
||||||
|
skinparam rectangleBackgroundColor #edf6fa
|
||||||
|
skinparam ArrowColor #8095a1
|
||||||
|
title A5 FLOW — eight deterministic Hazard3 replay states
|
||||||
|
|
||||||
|
rectangle "01 E01 RESET\n1 block · free=4080\nraw header=8 · aligned=16" as S1
|
||||||
|
rectangle "02 E02 ALLOC A(24)\nconsumed=48\nfree=4032" as S2
|
||||||
|
rectangle "03 E03 ALLOC B(40)\nconsumed=64\nfree=3968" as S3
|
||||||
|
rectangle "04 E04 ALLOC C(16)\nconsumed=32\nfree=3936" as S4
|
||||||
|
rectangle "05 E05 FREE A\n2 free blocks\nfree=3984" as S5
|
||||||
|
rectangle "06 E06 FREE C\n2 blocks · fragmented\nfree=4016" as S6
|
||||||
|
rectangle "07 E07 FREE B\nmerge left + right\n1 block · free=4080" as S7
|
||||||
|
rectangle "08 E08 OOM / VERIFY\nNULL · hook=1\nasserts=0 · PASS=1" as S8
|
||||||
|
|
||||||
|
S1 -right-> S2 : pvPortMalloc
|
||||||
|
S2 -right-> S3 : split remainder
|
||||||
|
S3 -right-> S4 : split remainder
|
||||||
|
S4 -down-> S5 : vPortFree(A)
|
||||||
|
S5 -left-> S6 : vPortFree(C)
|
||||||
|
S6 -left-> S7 : vPortFree(B)
|
||||||
|
S7 -left-> S8 : oversized request
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="236.6008px" preserveAspectRatio="none" style="width:812px;height:236px;" version="1.1" viewBox="0 0 812 236" width="812.4374px" zoomAndPan="magnify"><defs/><g><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="239.8419" x="290.0791" y="10.3943">A5 FLOW — eight deterministic Hazard3 replay states</text><!--MD5=[d0cae5ff9305becd6f502d4fe4f41481]
|
||||||
|
entity S1--><rect fill="#EDF6FA" height="61.3371" style="stroke: #2C7794; stroke-width: 1.6205533596837944;" width="142.6087" x="6.4822" y="22.3724"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="59.4203" x="17.2859" y="43.5704">01 E01 RESET</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="83.1884" x="17.2859" y="56.8136">1 block · free=4080</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="121.0013" x="17.2859" y="70.0569">raw header=8 · aligned=16</text><!--MD5=[d3395af3100fe8cebf35265100c4415f]
|
||||||
|
entity S2--><rect fill="#EDF6FA" height="61.3371" style="stroke: #2C7794; stroke-width: 1.6205533596837944;" width="110.1976" x="247.4045" y="22.3724"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="88.5903" x="258.2082" y="43.5704">02 E02 ALLOC A(24)</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="63.7418" x="258.2082" y="56.8136">consumed=48</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="45.3755" x="258.2082" y="70.0569">free=4032</text><!--MD5=[6c7192638421eaf134d201765ac5db29]
|
||||||
|
entity S3--><rect fill="#EDF6FA" height="61.3371" style="stroke: #2C7794; stroke-width: 1.6205533596837944;" width="110.1976" x="466.7194" y="22.3724"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="88.5903" x="477.5231" y="43.5704">03 E03 ALLOC B(40)</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="63.7418" x="477.5231" y="56.8136">consumed=64</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="45.3755" x="477.5231" y="70.0569">free=3968</text><!--MD5=[01b905ebe31ca24275b86a9951c478d0]
|
||||||
|
entity S4--><rect fill="#EDF6FA" height="61.3371" style="stroke: #2C7794; stroke-width: 1.6205533596837944;" width="110.1976" x="686.0343" y="22.3724"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="88.5903" x="696.8379" y="43.5704">04 E04 ALLOC C(16)</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="63.7418" x="696.8379" y="56.8136">consumed=32</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="45.3755" x="696.8379" y="70.0569">free=3936</text><!--MD5=[099bd2cf1f4f3a6dbf2f1fde6174f38e]
|
||||||
|
entity S5--><rect fill="#EDF6FA" height="61.3371" style="stroke: #2C7794; stroke-width: 1.6205533596837944;" width="84.2688" x="698.9987" y="163.663"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="62.6614" x="709.8024" y="184.861">05 E05 FREE A</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="56.1792" x="709.8024" y="198.1043">2 free blocks</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="45.3755" x="709.8024" y="211.3475">free=3984</text><!--MD5=[0656c7721b015b9ca8cf2e2c2e8f5aee]
|
||||||
|
entity S6--><rect fill="#EDF6FA" height="61.3371" style="stroke: #2C7794; stroke-width: 1.6205533596837944;" width="118.8406" x="484.0053" y="163.663"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="62.6614" x="494.809" y="184.861">06 E06 FREE C</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="97.2332" x="494.809" y="198.1043">2 blocks · fragmented</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="45.3755" x="494.809" y="211.3475">free=4016</text><!--MD5=[6ba532df225878998c5769132c3837e2]
|
||||||
|
entity S7--><rect fill="#EDF6FA" height="61.3371" style="stroke: #2C7794; stroke-width: 1.6205533596837944;" width="104.7958" x="282.5165" y="163.663"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="62.6614" x="293.3202" y="184.861">07 E07 FREE B</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="78.8669" x="293.3202" y="198.1043">merge left + right</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="83.1884" x="293.3202" y="211.3475">1 block · free=4080</text><!--MD5=[ce611cd767df6ac8a45071985dabb378]
|
||||||
|
entity S8--><rect fill="#EDF6FA" height="61.3371" style="stroke: #2C7794; stroke-width: 1.6205533596837944;" width="115.5995" x="46.996" y="163.663"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="93.9921" x="57.7997" y="184.861">08 E08 OOM / VERIFY</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="65.9025" x="57.7997" y="198.1043">NULL · hook=1</text><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="82.108" x="57.7997" y="211.3475">asserts=0 · PASS=1</text><!--MD5=[15cb110786dd224ca6a510a3d2a24e20]
|
||||||
|
link S1 to S2--><path d="M149.5014,53.044 C178.639,53.044 211.9792,53.044 240.0796,53.044 " fill="none" id="S1->S2" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><polygon fill="#8095A1" points="245.4706,53.044,235.7473,48.7226,240.0688,53.044,235.7473,57.3655,245.4706,53.044" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="58.3399" x="169.0777" y="46.1524">pvPortMalloc</text><!--MD5=[8eac89d31ced3c9003fbd6a8a005c745]
|
||||||
|
link S2 to S3--><path d="M358.1315,53.044 C389.0516,53.044 427.6208,53.044 459.5457,53.044 " fill="none" id="S2->S3" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><polygon fill="#8095A1" points="464.6126,53.044,454.8893,48.7226,459.2108,53.044,454.8893,57.3655,464.6126,53.044" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="69.1436" x="377.5889" y="46.1524">split remainder</text><!--MD5=[82e284a09e4ebade00512bdc77bff32c]
|
||||||
|
link S3 to S4--><path d="M577.4464,53.044 C608.3665,53.044 646.9357,53.044 678.8606,53.044 " fill="none" id="S3->S4" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><polygon fill="#8095A1" points="683.9275,53.044,674.2042,48.7226,678.5257,53.044,674.2042,57.3655,683.9275,53.044" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="69.1436" x="596.9038" y="46.1524">split remainder</text><!--MD5=[0e6b031b378ab8978eaa734a548bdfb6]
|
||||||
|
link S4 to S5--><path d="M741.1331,83.9642 C741.1331,105.2367 741.1331,133.9637 741.1331,156.6298 " fill="none" id="S4->S5" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><polygon fill="#8095A1" points="741.1331,161.8156,745.4545,152.0923,741.1331,156.4137,736.8116,152.0923,741.1331,161.8156" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="57.2596" x="742.2134" y="127.6015">vPortFree(A)</text><!--MD5=[3c9aaad44b43c509ca535c241fdd0151]
|
||||||
|
reverse link S6 to S5--><path d="M610.2356,194.3239 C639.492,194.3239 672.9618,194.3239 698.7286,194.3239 " fill="none" id="S6<-S5" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><polygon fill="#8095A1" points="604.9202,194.3239,614.6435,198.6454,610.322,194.3239,614.6435,190.0024,604.9202,194.3239" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="57.2596" x="622.2925" y="187.4323">vPortFree(C)</text><!--MD5=[5f6ea4567bb0950bff1d27387d49b4c2]
|
||||||
|
reverse link S7 to S6--><path d="M394.7668,194.3239 C422.6403,194.3239 455.7212,194.3239 483.5947,194.3239 " fill="none" id="S7<-S6" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><polygon fill="#8095A1" points="389.4406,194.3239,399.1639,198.6454,394.8424,194.3239,399.1639,190.0024,389.4406,194.3239" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="57.2596" x="407.029" y="187.4323">vPortFree(B)</text><!--MD5=[630b9631f886cf9c8b0d992525b86cf3]
|
||||||
|
reverse link S8 to S7--><path d="M169.8448,194.3239 C205.3025,194.3239 248.6253,194.3239 281.9871,194.3239 " fill="none" id="S8<-S7" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><polygon fill="#8095A1" points="164.497,194.3239,174.2203,198.6454,169.8988,194.3239,174.2203,190.0024,164.497,194.3239" style="stroke: #8095A1; stroke-width: 1.080368906455863;"/><text fill="#000000" font-family="Monospace" font-size="9.7233" lengthAdjust="spacingAndGlyphs" textLength="81.0277" x="182.0422" y="187.4323">oversized request</text><!--MD5=[9f7e06700e17220ce72e8c57ed6bc819]
|
||||||
|
@startuml
|
||||||
|
scale 820 width
|
||||||
|
top to bottom direction
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam defaultFontName Monospace
|
||||||
|
skinparam defaultFontSize 9
|
||||||
|
skinparam rectangleBorderColor #2c7794
|
||||||
|
skinparam rectangleBackgroundColor #edf6fa
|
||||||
|
skinparam ArrowColor #8095a1
|
||||||
|
title A5 FLOW — eight deterministic Hazard3 replay states
|
||||||
|
|
||||||
|
rectangle "01 E01 RESET\n1 block · free=4080\nraw header=8 · aligned=16" as S1
|
||||||
|
rectangle "02 E02 ALLOC A(24)\nconsumed=48\nfree=4032" as S2
|
||||||
|
rectangle "03 E03 ALLOC B(40)\nconsumed=64\nfree=3968" as S3
|
||||||
|
rectangle "04 E04 ALLOC C(16)\nconsumed=32\nfree=3936" as S4
|
||||||
|
rectangle "05 E05 FREE A\n2 free blocks\nfree=3984" as S5
|
||||||
|
rectangle "06 E06 FREE C\n2 blocks · fragmented\nfree=4016" as S6
|
||||||
|
rectangle "07 E07 FREE B\nmerge left + right\n1 block · free=4080" as S7
|
||||||
|
rectangle "08 E08 OOM / VERIFY\nNULL · hook=1\nasserts=0 · PASS=1" as S8
|
||||||
|
|
||||||
|
S1 -right-> S2 : pvPortMalloc
|
||||||
|
S2 -right-> S3 : split remainder
|
||||||
|
S3 -right-> S4 : split remainder
|
||||||
|
S4 -down-> S5 : vPortFree(A)
|
||||||
|
S5 -left-> S6 : vPortFree(C)
|
||||||
|
S6 -left-> S7 : vPortFree(B)
|
||||||
|
S7 -left-> S8 : oversized request
|
||||||
|
@enduml
|
||||||
|
|
||||||
|
PlantUML version 1.2020.02(Sun Mar 01 11:22:07 CET 2020)
|
||||||
|
(GPL source distribution)
|
||||||
|
Java Runtime: OpenJDK Runtime Environment
|
||||||
|
JVM: OpenJDK 64-Bit Server VM
|
||||||
|
Java Version: 25.0.4-ea+4-1-Debian
|
||||||
|
Operating System: Linux
|
||||||
|
Default Encoding: UTF-8
|
||||||
|
Language: en
|
||||||
|
Country: null
|
||||||
|
--></g></svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,29 @@
|
|||||||
|
@startuml
|
||||||
|
scale 600 height
|
||||||
|
top to bottom direction
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam defaultFontName Monospace
|
||||||
|
skinparam defaultFontSize 10
|
||||||
|
skinparam stateBorderColor #2c7794
|
||||||
|
skinparam stateBackgroundColor #edf6fa
|
||||||
|
skinparam ArrowColor #8095a1
|
||||||
|
title A6 STATE — reusable states of the same RV32 arena
|
||||||
|
|
||||||
|
[*] --> Free : 01 E01 one list node
|
||||||
|
Free --> Allocated : 02 E04 A+B+C allocated\ncurrent free = 3936
|
||||||
|
Allocated --> Fragmented : E05 free A\nE06 free C
|
||||||
|
Fragmented : 03 E06 two free blocks
|
||||||
|
Fragmented : sum free > largest block
|
||||||
|
Fragmented --> Coalesced : 04 E07 free B\nmerge previous, then next
|
||||||
|
Coalesced : one free block
|
||||||
|
Coalesced : current = largest = initial
|
||||||
|
Coalesced --> OOMChecked : 05 E08 request > arena
|
||||||
|
OOMChecked : NULL · hook=1 · PASS=1
|
||||||
|
OOMChecked --> [*]
|
||||||
|
|
||||||
|
state "minimum-ever" as MIN
|
||||||
|
Allocated -right-> MIN : records low watermark
|
||||||
|
Fragmented -right-> MIN : does not rise
|
||||||
|
Coalesced -right-> MIN : history remains
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="600.2775px" preserveAspectRatio="none" style="width:321px;height:600px;" version="1.1" viewBox="0 0 321 600" width="321.3164px" zoomAndPan="magnify"><defs/><g><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="184.7569" x="70.8357" y="7.8065">A6 STATE — reusable states of the same RV32 arena</text><ellipse cx="193.155" cy="23.2954" fill="#000000" rx="7.3026" ry="7.3026" style="stroke: none; stroke-width: 0.7302646103274287;"/><rect fill="#EDF6FA" height="36.5132" rx="9.1283" ry="9.1283" style="stroke: #2C7794; stroke-width: 1.0953969154911432;" width="36.5132" x="174.8984" y="85.3679"/><line style="stroke: #2C7794; stroke-width: 1.0953969154911432;" x1="174.8984" x2="211.4116" y1="102.6168" y2="102.6168"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="15.3356" x="185.4872" y="96.8258">Free</text><rect fill="#EDF6FA" height="36.5132" rx="9.1283" ry="9.1283" style="stroke: #2C7794; stroke-width: 1.0953969154911432;" width="47.4672" x="169.4214" y="186.8747"/><line style="stroke: #2C7794; stroke-width: 1.0953969154911432;" x1="169.4214" x2="216.8886" y1="204.1235" y2="204.1235"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="32.8619" x="176.724" y="198.3325">Allocated</text><rect fill="#EDF6FA" height="44.4439" rx="9.1283" ry="9.1283" style="stroke: #2C7794; stroke-width: 1.0953969154911432;" width="99.316" x="93.1087" y="288.3815"/><line style="stroke: #2C7794; stroke-width: 1.0953969154911432;" x1="93.1087" x2="192.4247" y1="305.6303" y2="305.6303"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="42.3553" x="121.5891" y="299.8393">Fragmented</text><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="79.5988" x="96.7601" y="317.0882">03 E06 two free blocks</text><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="84.7107" x="96.7601" y="327.0344">sum free > largest block</text><rect fill="#EDF6FA" height="44.4439" rx="9.1283" ry="9.1283" style="stroke: #2C7794; stroke-width: 1.0953969154911432;" width="102.9673" x="4.3816" y="397.8189"/><line style="stroke: #2C7794; stroke-width: 1.0953969154911432;" x1="4.3816" x2="107.3489" y1="415.0678" y2="415.0678"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="35.783" x="37.9738" y="409.2768">Coalesced</text><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="49.658" x="8.0329" y="426.5256">one free block</text><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="88.362" x="8.0329" y="436.4718">current = largest = initial</text><rect fill="#EDF6FA" height="36.5132" rx="9.1283" ry="9.1283" style="stroke: #2C7794; stroke-width: 1.0953969154911432;" width="96.3949" x="7.6678" y="497.0327"/><line style="stroke: #2C7794; stroke-width: 1.0953969154911432;" x1="7.6678" x2="104.0627" y1="514.2815" y2="514.2815"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="47.4672" x="32.1316" y="508.4905">OOMChecked</text><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="81.7896" x="11.3191" y="525.7394">NULL · hook=1 · PASS=1</text><ellipse cx="55.8652" cy="585.3947" fill="none" rx="7.3026" ry="7.3026" style="stroke: #000000; stroke-width: 0.7302646103274287;"/><ellipse cx="56.2304" cy="585.7598" fill="#000000" rx="4.3816" ry="4.3816" style="stroke: none; stroke-width: 0.7302646103274287;"/><rect fill="#EDF6FA" height="36.5132" rx="9.1283" ry="9.1283" style="stroke: #2C7794; stroke-width: 1.0953969154911432;" width="64.9936" x="188.4083" y="401.7843"/><line style="stroke: #2C7794; stroke-width: 1.0953969154911432;" x1="188.4083" x2="253.4018" y1="419.0331" y2="419.0331"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="50.3883" x="195.7109" y="413.2421">minimum-ever</text><!--MD5=[f16808fa220dde377303612249059854]
|
||||||
|
link *start to Free--><path d="M193.155,30.7514 C193.155,41.647 193.155,63.6352 193.155,80.4898 " fill="none" id="*start->Free" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><polygon fill="#8095A1" points="193.155,83.9877,196.076,77.4153,193.155,80.3364,190.2339,77.4153,193.155,83.9877" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="72.2962" x="193.8853" y="61.0428">01 E01 one list node</text><!--MD5=[19abaf48dc778410378e95e0d1bca006]
|
||||||
|
link Free to Allocated--><path d="M193.155,122.239 C193.155,138.9547 193.155,163.9809 193.155,182.0111 " fill="none" id="Free->Allocated" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><polygon fill="#8095A1" points="193.155,185.5675,196.076,178.9951,193.155,181.9162,190.2339,178.9951,193.155,185.5675" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="83.9804" x="193.8853" y="152.3259">02 E04 A+B+C allocated</text><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="67.9146" x="201.9182" y="162.2721">current free = 3936</text><!--MD5=[f6fa3e1917b73de38ea03346a6903b17]
|
||||||
|
link Allocated to Fragmented--><path d="M184.4941,223.7385 C176.4611,240.3228 164.4191,265.181 155.3346,283.9342 " fill="none" id="Allocated->Fragmented" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><polygon fill="#8095A1" points="153.8083,287.0889,159.2954,282.4391,155.3952,283.8004,154.0338,279.9002,153.8083,287.0889" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="35.783" x="174.6866" y="253.8327">E05 free A</text><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="35.783" x="174.6866" y="263.7789">E06 free C</text><!--MD5=[c73bca393b062d6a86dff80fa900c78c]
|
||||||
|
link Fragmented to Coalesced--><path d="M92.7509,329.6414 C81.6363,335.9582 71.0474,344.1883 63.8982,354.7333 C56.391,365.8114 54.1053,380.4606 53.8278,393.0065 " fill="none" id="Fragmented->Coalesced" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><polygon fill="#8095A1" points="53.8132,396.4241,56.753,389.8601,53.8236,392.7728,50.9109,389.8434,53.8132,396.4241" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="47.4672" x="87.2666" y="363.2701">04 E07 free B</text><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="92.7436" x="64.6284" y="373.2163">merge previous, then next</text><!--MD5=[2b306524d92469efb3bb270d490680cf]
|
||||||
|
link Coalesced to OOMChecked--><path d="M55.8652,442.5257 C55.8652,457.3647 55.8652,476.9796 55.8652,491.9939 " fill="none" id="Coalesced->OOMChecked" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><polygon fill="#8095A1" points="55.8652,495.5941,58.7863,489.0217,55.8652,491.9427,52.9442,489.0217,55.8652,495.5941" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="82.5199" x="56.5955" y="472.7076">05 E08 request > arena</text><!--MD5=[6b8be85e17f6174335a5329fe1414850]
|
||||||
|
link OOMChecked to *end--><path d="M55.8652,533.6993 C55.8652,546.2379 55.8652,562.6835 55.8652,573.4622 " fill="none" id="OOMChecked->*end" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><polygon fill="#8095A1" points="55.8652,576.8725,58.7863,570.3001,55.8652,573.2212,52.9442,570.3001,55.8652,576.8725" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><!--MD5=[b09143a670c8c67aa163b99fca5c6b6b]
|
||||||
|
link Allocated to MIN--><path d="M206.402,223.6581 C210.5791,230.1502 214.7489,237.7595 217.2537,245.2959 C235.6199,300.555 231.8517,317.8477 227.4774,375.911 C226.9589,382.7609 225.995,390.1731 224.958,396.8988 " fill="none" id="Allocated->MIN" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><polygon fill="#8095A1" points="224.403,400.3384,228.3223,394.3078,224.9778,396.7326,222.5531,393.3881,224.403,400.3384" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="81.0594" x="231.8006" y="313.6632">records low watermark</text><!--MD5=[1fa23b8554d20d5dd3f2f8930c0e2df3]
|
||||||
|
link Fragmented to MIN--><path d="M152.2237,332.9422 C158.409,345.9702 167.1649,362.5618 177.0892,375.911 C182.9094,383.7394 190.1317,391.4875 197.0254,398.2279 " fill="none" id="Fragmented->MIN" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><polygon fill="#8095A1" points="199.5813,400.6889,196.8641,394.0296,196.9477,398.1598,192.8176,398.2434,199.5813,400.6889" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="46.0067" x="177.8194" y="368.382">does not rise</text><!--MD5=[87522a40b34d64237ec6603d41372cd8]
|
||||||
|
link Coalesced to MIN--><path d="M107.6337,420.0409 C132.2582,420.0409 161.1402,420.0409 183.5812,420.0409 " fill="none" id="Coalesced->MIN" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><polygon fill="#8095A1" points="187.1595,420.0409,180.5871,417.1198,183.5082,420.0409,180.5871,422.962,187.1595,420.0409" style="stroke: #8095A1; stroke-width: 0.7302646103274287;"/><text fill="#000000" font-family="Monospace" font-size="7.3026" lengthAdjust="spacingAndGlyphs" textLength="54.7698" x="120.4937" y="415.4329">history remains</text><!--MD5=[ca62bd85e5915d2f6639cfa0fa77f8b6]
|
||||||
|
@startuml
|
||||||
|
scale 600 height
|
||||||
|
top to bottom direction
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam defaultFontName Monospace
|
||||||
|
skinparam defaultFontSize 10
|
||||||
|
skinparam stateBorderColor #2c7794
|
||||||
|
skinparam stateBackgroundColor #edf6fa
|
||||||
|
skinparam ArrowColor #8095a1
|
||||||
|
title A6 STATE — reusable states of the same RV32 arena
|
||||||
|
|
||||||
|
[*] - -> Free : 01 E01 one list node
|
||||||
|
Free - -> Allocated : 02 E04 A+B+C allocated\ncurrent free = 3936
|
||||||
|
Allocated - -> Fragmented : E05 free A\nE06 free C
|
||||||
|
Fragmented : 03 E06 two free blocks
|
||||||
|
Fragmented : sum free > largest block
|
||||||
|
Fragmented - -> Coalesced : 04 E07 free B\nmerge previous, then next
|
||||||
|
Coalesced : one free block
|
||||||
|
Coalesced : current = largest = initial
|
||||||
|
Coalesced - -> OOMChecked : 05 E08 request > arena
|
||||||
|
OOMChecked : NULL · hook=1 · PASS=1
|
||||||
|
OOMChecked - -> [*]
|
||||||
|
|
||||||
|
state "minimum-ever" as MIN
|
||||||
|
Allocated -right-> MIN : records low watermark
|
||||||
|
Fragmented -right-> MIN : does not rise
|
||||||
|
Coalesced -right-> MIN : history remains
|
||||||
|
@enduml
|
||||||
|
|
||||||
|
PlantUML version 1.2020.02(Sun Mar 01 11:22:07 CET 2020)
|
||||||
|
(GPL source distribution)
|
||||||
|
Java Runtime: OpenJDK Runtime Environment
|
||||||
|
JVM: OpenJDK 64-Bit Server VM
|
||||||
|
Java Version: 25.0.4-ea+4-1-Debian
|
||||||
|
Operating System: Linux
|
||||||
|
Default Encoding: UTF-8
|
||||||
|
Language: en
|
||||||
|
Country: null
|
||||||
|
--></g></svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,23 @@
|
|||||||
|
@startuml
|
||||||
|
scale 820 width
|
||||||
|
left to right direction
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam defaultFontName Monospace
|
||||||
|
skinparam defaultFontSize 9
|
||||||
|
skinparam rectangleBorderColor #2c7794
|
||||||
|
skinparam rectangleBackgroundColor #edf6fa
|
||||||
|
skinparam ArrowColor #8095a1
|
||||||
|
title A7 RUNTIME — source identity, replay, memory and verdict
|
||||||
|
|
||||||
|
rectangle "<<source>>\n01 task03 C\nA/B/C experiment" as SRC
|
||||||
|
rectangle "<<artifact>>\n02 ELF + image\nheap_4 symbols" as ELF
|
||||||
|
rectangle "<<checkpoint E06>>\n03 fragmented\nblocks=2 · free>largest" as E1
|
||||||
|
rectangle "<<memory>>\n04 ucHeap[4096]\nheaders + payload" as MEM
|
||||||
|
rectangle "<<checkpoint E08>>\n05 verified\nfinal=initial · blocks=1\nOOM=1 · PASS=1" as E2
|
||||||
|
|
||||||
|
SRC --> ELF : compile + link
|
||||||
|
ELF --> E1 : reset / load / replay
|
||||||
|
E1 --> MEM : inspect bytes
|
||||||
|
MEM --> E2 : continue
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="82px" preserveAspectRatio="none" style="width:814px;height:82px;" version="1.1" viewBox="0 0 814 82" width="814.26px" zoomAndPan="magnify"><defs/><g><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="204.18" x="307.91" y="7.8893">A7 RUNTIME — source identity, replay, memory and verdict</text><!--MD5=[183d75e42a7a852d02a7f9ce2cc3343f]
|
||||||
|
entity SRC--><rect fill="#EDF6FA" height="46.5549" style="stroke: #2C7794; stroke-width: 1.23;" width="77.9" x="4.92" y="21.6136"/><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="31.16" x="13.12" y="37.7029">«source»</text><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="38.54" x="13.12" y="47.7545">01 task03 C</text><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="61.5" x="13.12" y="57.8062">A/B/C experiment</text><!--MD5=[4a7b087cd571ae358eb895b9dc7ce0ed]
|
||||||
|
entity ELF--><rect fill="#EDF6FA" height="46.5549" style="stroke: #2C7794; stroke-width: 1.23;" width="71.34" x="179.58" y="21.6136"/><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="31.98" x="187.78" y="37.7029">«artifact»</text><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="50.84" x="187.78" y="47.7545">02 ELF + image</text><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="54.94" x="187.78" y="57.8062">heap_4 symbols</text><!--MD5=[45565a74ca8123df0b8413ce5340d40a]
|
||||||
|
entity E1--><rect fill="#EDF6FA" height="46.5549" style="stroke: #2C7794; stroke-width: 1.23;" width="92.66" x="367.36" y="21.6136"/><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="59.86" x="375.56" y="37.7029">«checkpoint E06»</text><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="51.66" x="375.56" y="47.7545">03 fragmented</text><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="76.26" x="375.56" y="57.8062">blocks=2 · free>largest</text><!--MD5=[5ceda9279414f57783e95166b8d927b7]
|
||||||
|
entity MEM--><rect fill="#EDF6FA" height="46.5549" style="stroke: #2C7794; stroke-width: 1.23;" width="80.36" x="555.96" y="21.6136"/><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="36.9" x="564.16" y="37.7029">«memory»</text><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="58.22" x="564.16" y="47.7545">04 ucHeap[4096]</text><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="63.96" x="564.16" y="57.8062">headers + payload</text><!--MD5=[a3c437a7ee715155cf6a0fec80d7cbaf]
|
||||||
|
entity E2--><rect fill="#EDF6FA" height="56.6065" style="stroke: #2C7794; stroke-width: 1.23;" width="87.74" x="717.5" y="16.587"/><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="59.86" x="725.7" y="32.6763">«checkpoint E08»</text><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="36.08" x="725.7" y="42.7279">05 verified</text><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="71.34" x="725.7" y="52.7796">final=initial · blocks=1</text><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="56.58" x="725.7" y="62.8312">OOM=1 · PASS=1</text><!--MD5=[2e73c4d4efa4a7715d66b06010e70f85]
|
||||||
|
link SRC to ELF--><path d="M83.2054,44.8852 C110.372,44.8852 146.6734,44.8852 174.209,44.8852 " fill="none" id="SRC->ELF" style="stroke: #8095A1; stroke-width: 0.82;"/><polygon fill="#8095A1" points="178.0056,44.8852,170.6256,41.6052,173.9056,44.8852,170.6256,48.1652,178.0056,44.8852" style="stroke: #8095A1; stroke-width: 0.82;"/><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="45.92" x="108.24" y="42.1145">compile + link</text><!--MD5=[98439561a91d683361f6e8599ebb304f]
|
||||||
|
link ELF to E1--><path d="M251.2562,44.8852 C282.0636,44.8852 327.221,44.8852 361.9726,44.8852 " fill="none" id="ELF->E1" style="stroke: #8095A1; stroke-width: 0.82;"/><polygon fill="#8095A1" points="365.9988,44.8852,358.6188,41.6052,361.8988,44.8852,358.6188,48.1652,365.9988,44.8852" style="stroke: #8095A1; stroke-width: 0.82;"/><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="65.6" x="276.34" y="42.1145">reset / load / replay</text><!--MD5=[14b84038a04448f34eb55bd119f2d935]
|
||||||
|
link E1 to MEM--><path d="M460.3726,44.8852 C488.0476,44.8852 523.1518,44.8852 550.6136,44.8852 " fill="none" id="E1->MEM" style="stroke: #8095A1; stroke-width: 0.82;"/><polygon fill="#8095A1" points="554.4102,44.8852,547.0302,41.6052,550.3102,44.8852,547.0302,48.1652,554.4102,44.8852" style="stroke: #8095A1; stroke-width: 0.82;"/><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="45.1" x="485.44" y="42.1145">inspect bytes</text><!--MD5=[28f0a5a321106494a7afb95cbc1e35b0]
|
||||||
|
link MEM to E2--><path d="M636.5578,44.8852 C659.3374,44.8852 688.1276,44.8852 712.2274,44.8852 " fill="none" id="MEM->E2" style="stroke: #8095A1; stroke-width: 0.82;"/><polygon fill="#8095A1" points="716.0568,44.8852,708.6768,41.6052,711.9568,44.8852,708.6768,48.1652,716.0568,44.8852" style="stroke: #8095A1; stroke-width: 0.82;"/><text fill="#000000" font-family="Monospace" font-size="7.38" lengthAdjust="spacingAndGlyphs" textLength="30.34" x="661.74" y="42.1145">continue</text><!--MD5=[d3cc7590ea240485cc9ae283b10caa4d]
|
||||||
|
@startuml
|
||||||
|
scale 820 width
|
||||||
|
left to right direction
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam defaultFontName Monospace
|
||||||
|
skinparam defaultFontSize 9
|
||||||
|
skinparam rectangleBorderColor #2c7794
|
||||||
|
skinparam rectangleBackgroundColor #edf6fa
|
||||||
|
skinparam ArrowColor #8095a1
|
||||||
|
title A7 RUNTIME — source identity, replay, memory and verdict
|
||||||
|
|
||||||
|
rectangle "<<source>>\n01 task03 C\nA/B/C experiment" as SRC
|
||||||
|
rectangle "<<artifact>>\n02 ELF + image\nheap_4 symbols" as ELF
|
||||||
|
rectangle "<<checkpoint E06>>\n03 fragmented\nblocks=2 · free>largest" as E1
|
||||||
|
rectangle "<<memory>>\n04 ucHeap[4096]\nheaders + payload" as MEM
|
||||||
|
rectangle "<<checkpoint E08>>\n05 verified\nfinal=initial · blocks=1\nOOM=1 · PASS=1" as E2
|
||||||
|
|
||||||
|
SRC - -> ELF : compile + link
|
||||||
|
ELF - -> E1 : reset / load / replay
|
||||||
|
E1 - -> MEM : inspect bytes
|
||||||
|
MEM - -> E2 : continue
|
||||||
|
@enduml
|
||||||
|
|
||||||
|
PlantUML version 1.2020.02(Sun Mar 01 11:22:07 CET 2020)
|
||||||
|
(GPL source distribution)
|
||||||
|
Java Runtime: OpenJDK Runtime Environment
|
||||||
|
JVM: OpenJDK 64-Bit Server VM
|
||||||
|
Java Version: 25.0.4-ea+4-1-Debian
|
||||||
|
Operating System: Linux
|
||||||
|
Default Encoding: UTF-8
|
||||||
|
Language: en
|
||||||
|
Country: null
|
||||||
|
--></g></svg>
|
||||||
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,190 @@
|
|||||||
|
# FC01 — FreeRTOS `heap_4`: od kursora do wolnych bloków
|
||||||
|
|
||||||
|
## Decyzja dydaktyczna
|
||||||
|
|
||||||
|
To jest jedna 30-minutowa lekcja i bezpośrednia kontynuacja projektu
|
||||||
|
K&R 5.4 z karty C07. Karta nie uczy pięciu implementacji heap jako pięciu
|
||||||
|
równorzędnych tematów. `heap_1`, `heap_2`, `heap_3` i `heap_5` zajmują łącznie
|
||||||
|
maksymalnie minutę kontekstu. Cała właściwa praca dotyczy `heap_4`.
|
||||||
|
|
||||||
|
Task 1 i Task 2 są krótkimi modelami prowadzonymi przez nauczyciela. Uczeń
|
||||||
|
najpierw zapisuje predykcję, ale nie buduje i nie debugguje tych programów
|
||||||
|
osobno podczas lekcji. Task 3, linkujący upstream `heap_4.c`, jest jedynym
|
||||||
|
centralnym przebiegiem ucznia.
|
||||||
|
|
||||||
|
## Stan wejściowy z K&R 5.4
|
||||||
|
|
||||||
|
Uczeń przychodzi z działającym kodem:
|
||||||
|
|
||||||
|
```text
|
||||||
|
allocbuf[64]
|
||||||
|
allocp
|
||||||
|
allocator_reset()
|
||||||
|
alloc_local(5) -> offset 0
|
||||||
|
alloc_local(7) -> offset 5
|
||||||
|
allocp -> offset 12
|
||||||
|
alloc_local(0) oraz OOM -> NULL bez przesunięcia kursora
|
||||||
|
```
|
||||||
|
|
||||||
|
Nie powtarzamy sposobu działania tego kodu. Zaczynamy od jego granicy:
|
||||||
|
|
||||||
|
> Jak zwolnić obszar 5 B spod offsetu 0, zachować późniejszy obszar 7 B spod
|
||||||
|
> offsetu 5 i ponownie wykorzystać powstałą dziurę?
|
||||||
|
|
||||||
|
W projekcie nie istnieje operacja zwolnienia pojedynczego obszaru. Nawet proste
|
||||||
|
cofnięcie kursora unieważniłoby wszystkie późniejsze przydziały. Brakuje
|
||||||
|
tożsamości bloku, jego rozmiaru, zbioru dziur i mechanizmu ich łączenia.
|
||||||
|
|
||||||
|
## Cel w jednym zdaniu
|
||||||
|
|
||||||
|
Po 30 minutach uczeń potrafi wyjaśnić i pokazać w debuggerze, jak `heap_4`
|
||||||
|
dodaje do liniowej areny nagłówki, wybór first-fit, podział bloku, listę wolnych
|
||||||
|
bloków oraz scalanie sąsiadów, a następnie odróżnić bieżącą ilość wolnej
|
||||||
|
pamięci, największy wolny blok i minimum-ever.
|
||||||
|
|
||||||
|
## Most K&R 5.4 -> `heap_4`
|
||||||
|
|
||||||
|
| K&R 5.4 — stan projektu | Ograniczenie | `heap_4` — nowy mechanizm |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `allocbuf[64]` i jeden `allocp` | istnieje tylko wolny ogon areny | `ucHeap[]` i bloki opisane nagłówkami |
|
||||||
|
| wynik to surowy adres | allocator nie zna później rozmiaru obszaru | `BlockLink_t` przed payloadem przechowuje rozmiar |
|
||||||
|
| kolejne przydziały tylko przesuwają kursor | nie można użyć dziury | lista wolnych bloków i wybór first-fit |
|
||||||
|
| brak zwalniania pojedynczego obszaru | reset odzyskuje wszystko naraz | `vPortFree()` odzyskuje wskazany blok |
|
||||||
|
| brak relacji między dziurami | sąsiednie dziury pozostają rozdzielone | porządek adresowy i coalescing w obie strony |
|
||||||
|
| tylko sukces albo `NULL` | brak informacji o fragmentacji | free, largest, liczba bloków i minimum-ever |
|
||||||
|
|
||||||
|
W realnym `heap_4.c` najwyższy bit `xBlockSize` oznacza blok przydzielony.
|
||||||
|
Uproszczone modele Task 1–2 pokazują geometrię i listę, ale nie odtwarzają tego
|
||||||
|
bitu. To jawna granica modelu.
|
||||||
|
|
||||||
|
## Przygotowanie przed lekcją
|
||||||
|
|
||||||
|
Budowanie, flashowanie i wykrywanie debug probe nie wchodzą do budżetu 30 minut.
|
||||||
|
Przed wejściem uczniów:
|
||||||
|
|
||||||
|
1. zbuduj Task 3 dla `pico2_w` / RP2350 RISC-V;
|
||||||
|
2. wgraj zweryfikowany obraz i otwórz sesję na tym samym ELF;
|
||||||
|
3. ustaw pierwszy breakpoint na `heap4_reset_checkpoint`;
|
||||||
|
4. przygotuj widok `ucHeap`, statystyk E01--E08,
|
||||||
|
`xFreeBytesRemaining` i `xMinimumEverFreeBytesRemaining`;
|
||||||
|
5. przygotuj Hazard3 jako awaryjny zamiennik — podczas lekcji użyj jednej
|
||||||
|
platformy, nigdy obu.
|
||||||
|
|
||||||
|
`heap_4` jest implementacją wybraną i linkowaną przez projekt. Nie jest
|
||||||
|
sprzętowym heapem ani peryferium RP2350.
|
||||||
|
|
||||||
|
## Przebieg 30 minut
|
||||||
|
|
||||||
|
| Czas | Tryb | Działanie i dowód |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 0–3 min | problem | Przypomnij wyłącznie offsety `0 -> 5 -> 12`. Uczeń próbuje zwolnić pierwszy obszar bez utraty drugiego i nazywa brakującą operację. |
|
||||||
|
| 3–4 min | kontekst | Jedno zdanie: FreeRTOS dostarcza wymienne implementacje tego samego API; ta karta i oficjalne przykłady Pico 2 W linkują `heap_4`. |
|
||||||
|
| 4–9 min | Task 1 — demo | Do żądania dodaj nagłówek, wyrównaj rozmiar, wybierz pierwszy pasujący blok i podziel go. Uczeń zapisuje `wanted` i `remainder` przed odsłonięciem wyniku. |
|
||||||
|
| 9–16 min | Task 2 — demo | Dla `A:48 | B:48 | C:48` pokaż listę `A -> C`, wstaw `B` po adresie i sprawdź scalenie w prawo oraz w lewo: `2 bloki / 96 B / largest 48 B -> 1 blok / 144 B / largest 144 B`. |
|
||||||
|
| 16–27 min | Task 3 — RUN | Na przygotowanym celu przejdź przez osiem checkpointów prawdziwego `heap_4.c`: reset, trzy alokacje, free A, free C, free B i kontrolowany OOM. Uczeń wypełnia tabelę relacji. |
|
||||||
|
| 27–30 min | wyjście | Uczeń wymienia trzy dodatki ponad K&R (`header`, `free list`, `coalescing`) i wyjaśnia: suma wolnych bajtów może być większa od największego bloku, a minimum-ever nie rośnie po `free`. |
|
||||||
|
|
||||||
|
## Task 1 — model nagłówka i splitu
|
||||||
|
|
||||||
|
Task 1 nie jest osobnym laboratorium. Na RV32 nagłówek modelu ma 8 B, więc
|
||||||
|
żądanie 13 B daje `wanted=24 B` i `remainder=232 B` z areny 256 B. Na AMD64
|
||||||
|
odpowiednie wartości to 16 B, 32 B i 224 B. Różnica ABI jest tylko pomocą w
|
||||||
|
zrozumieniu `sizeof` i wyrównania; podczas głównego przebiegu obowiązuje RP2350.
|
||||||
|
|
||||||
|
Predykcja ucznia:
|
||||||
|
|
||||||
|
```text
|
||||||
|
free list: [16 B] -> [80 B] -> [160 B]
|
||||||
|
request + header po wyrównaniu: 24 B
|
||||||
|
first-fit wybiera: __________
|
||||||
|
po split: allocated ______ B, remainder ______ B
|
||||||
|
```
|
||||||
|
|
||||||
|
Wykonywalny Task 1 dowodzi obecnie geometrii splitu jednego bloku. Samo
|
||||||
|
przeszukanie dwóch kandydatów jest elementem demonstracji i realnego
|
||||||
|
`pvPortMalloc()`, dlatego nie opisujemy Task 1 jako pełnego testu first-fit.
|
||||||
|
|
||||||
|
## Task 2 — model listy adresowej i coalescingu
|
||||||
|
|
||||||
|
```text
|
||||||
|
pamięć: [ A:48 FREE ][ B:48 USED ][ C:48 FREE ]
|
||||||
|
free list: A --------------------------------> C
|
||||||
|
|
||||||
|
vPortFree(B)
|
||||||
|
1. wstaw B pomiędzy A i C według adresu;
|
||||||
|
2. jeśli end(A) == addr(B), połącz A+B;
|
||||||
|
3. jeśli end(AB) == addr(C), połącz AB+C;
|
||||||
|
|
||||||
|
wynik: [ ABC:144 FREE ]
|
||||||
|
free list: A -> END
|
||||||
|
```
|
||||||
|
|
||||||
|
Uczeń przed odsłonięciem rysuje listę i wpisuje `2 -> 1`, `96 -> 144` oraz
|
||||||
|
`48 -> 144` odpowiednio dla liczby bloków, sumy i largest.
|
||||||
|
|
||||||
|
## Task 3 — centralny przebieg upstream `heap_4.c`
|
||||||
|
|
||||||
|
```text
|
||||||
|
RESET/INIT
|
||||||
|
-> heap4_reset_checkpoint # E01: 4080 B
|
||||||
|
-> ALLOC A(24)
|
||||||
|
-> heap4_alloc_a_checkpoint # E02: 4032 B
|
||||||
|
-> ALLOC B(40)
|
||||||
|
-> heap4_alloc_b_checkpoint # E03: 3968 B
|
||||||
|
-> ALLOC C(16)
|
||||||
|
-> heap4_alloc_c_checkpoint # E04: 3936 B
|
||||||
|
-> FREE A
|
||||||
|
-> heap4_free_a_checkpoint # E05: 3984 B, 2 bloki
|
||||||
|
-> FREE C
|
||||||
|
-> heap4_fragmented_checkpoint # E06: 4016 B, 2 bloki
|
||||||
|
-> FREE B
|
||||||
|
-> heap4_coalesced_checkpoint # E07: 4080 B, 1 blok
|
||||||
|
-> OOM request
|
||||||
|
-> task03_debug_checkpoint # E08: NULL, hook=1, pass=1
|
||||||
|
```
|
||||||
|
|
||||||
|
Na Hazard3 `sizeof(BlockLink_t)=8`, ale `portBYTE_ALIGNMENT=16`, dlatego
|
||||||
|
`xHeapStructSize=16`. Główny replay ma stałe, testowane wartości:
|
||||||
|
|
||||||
|
| Obserwacja | Oczekiwany wynik RV32/Hazard3 |
|
||||||
|
| --- | --- |
|
||||||
|
| E01--E04 | `4080 -> 4032 -> 3968 -> 3936`, zużycie `48/64/32` |
|
||||||
|
| E05--E06 | `3984 -> 4016`, nadal 2 bloki i `largest < total_free` |
|
||||||
|
| E07 | `final_free == initial_free == 4080`, `free_blocks == 1` |
|
||||||
|
| low-water mark | `minimum_ever == 3936` i nie rośnie po free |
|
||||||
|
| OOM | wynik `NULL`, hook `1`, brak uszkodzenia listy |
|
||||||
|
|
||||||
|
Minimalny dowód w GDB:
|
||||||
|
|
||||||
|
```gdb
|
||||||
|
p g_fragmented_stats
|
||||||
|
p g_final_stats
|
||||||
|
p g_a_consumed
|
||||||
|
p g_b_consumed
|
||||||
|
p g_c_consumed
|
||||||
|
p xFreeBytesRemaining
|
||||||
|
p xMinimumEverFreeBytesRemaining
|
||||||
|
p xStart
|
||||||
|
p pxEnd
|
||||||
|
x/96bx ucHeap
|
||||||
|
```
|
||||||
|
|
||||||
|
Szczegółowe mapowanie wszystkich diagramów do kodu, replay i dowodów zawiera
|
||||||
|
[`hazard3-iteration-plan.md`](hazard3-iteration-plan.md).
|
||||||
|
|
||||||
|
## Kryterium zaliczenia
|
||||||
|
|
||||||
|
Uczeń zalicza lekcję, jeżeli:
|
||||||
|
|
||||||
|
- wskaże, dlaczego jeden kursor nie wystarcza do zwolnienia obszaru ze środka;
|
||||||
|
- rozróżni nagłówek allocatora od payloadu i stosu `sp`;
|
||||||
|
- przewidzi przejście wolnej listy `2 -> 1` po zwolnieniu B;
|
||||||
|
- wyjaśni różnicę `total free` kontra `largest free block`;
|
||||||
|
- wyjaśni, dlaczego minimum-ever nie wraca po `free`;
|
||||||
|
- pokaże `aligned=1 oom=1 hooks=1 asserts=0 pass=1` w Task 3.
|
||||||
|
|
||||||
|
## Poza lekcją
|
||||||
|
|
||||||
|
Jako rozszerzenie lub pracę własną pozostają: pełne czytanie `heap_4.c`,
|
||||||
|
uruchomienie Task 1–2 na AMD64 i Hazard3, samodzielny deploy RP2350 oraz
|
||||||
|
porównanie z `heap_1`, `heap_2`, `heap_3` i `heap_5`.
|
||||||
@@ -0,0 +1,340 @@
|
|||||||
|
% Generated from json/card_source.json by tools/render_card.py.
|
||||||
|
% Do not edit manually; update the JSON and regenerate.
|
||||||
|
|
||||||
|
\documentclass[12pt,a4paper]{article}
|
||||||
|
|
||||||
|
\usepackage[T1]{fontenc}
|
||||||
|
\usepackage{lmodern}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage[polish]{babel}
|
||||||
|
\usepackage{csquotes}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
\usepackage{amssymb}
|
||||||
|
\usepackage{xcolor}
|
||||||
|
\usepackage[a4paper,left=2.15cm,right=2.15cm,top=0.5cm,bottom=1cm,headheight=24mm,headsep=3mm,includehead,includefoot]{geometry}
|
||||||
|
\usepackage{eso-pic}
|
||||||
|
\usepackage{marginnote}
|
||||||
|
\usepackage{array}
|
||||||
|
\usepackage{tabularx}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{pdflscape}
|
||||||
|
\usepackage{float}
|
||||||
|
\usepackage{silence}
|
||||||
|
\WarningFilter{soulutf8}{This package is obsolete}
|
||||||
|
\usepackage{pdfcomment}
|
||||||
|
\usepackage{enumitem}
|
||||||
|
\usepackage{needspace}
|
||||||
|
\usepackage{fancyhdr}
|
||||||
|
\usepackage{lastpage}
|
||||||
|
\usepackage{microtype}
|
||||||
|
\usepackage[most]{tcolorbox}
|
||||||
|
\usepackage{qrcode}
|
||||||
|
\IfFileExists{references.bib}{%
|
||||||
|
\usepackage[backend=biber,style=numeric,sorting=none]{biblatex}%
|
||||||
|
\addbibresource{references.bib}%
|
||||||
|
}{}
|
||||||
|
|
||||||
|
\hypersetup{hidelinks}
|
||||||
|
|
||||||
|
\IfFileExists{build-meta.tex}{%
|
||||||
|
\input{build-meta.tex}%
|
||||||
|
}{%
|
||||||
|
\newcommand{\BuildCommit}{lokalna}%
|
||||||
|
}
|
||||||
|
|
||||||
|
\newcommand{\DocumentAuthor}{Mateusz Pabiszczak}
|
||||||
|
\newcommand{\DocumentYear}{2026}
|
||||||
|
\newcommand{\CardSeries}{freertos-c}
|
||||||
|
\newcommand{\CardSeriesTitle}{FreeRTOS C}
|
||||||
|
\newcommand{\CardNumber}{01}
|
||||||
|
\newcommand{\CardCount}{15}
|
||||||
|
\newcommand{\CardSlug}{heap4-mechanics}
|
||||||
|
\newcommand{\CardVersion}{v00.01}
|
||||||
|
\newcommand{\CardStatus}{Gotowa}
|
||||||
|
\newcommand{\DocumentUUID}{87e48095-7793-54e3-9230-4e61d31bd459}
|
||||||
|
|
||||||
|
\newcommand{\EmptyCheck}{\(\square\)}
|
||||||
|
\newcommand{\EscAnswerLines}[1][3]{\par\noindent\dotfill\par\noindent\dotfill\par\noindent\dotfill}
|
||||||
|
\newcommand{\EscWriteRow}[1][2.8em]{\rule{0pt}{#1}}
|
||||||
|
\setlength{\parindent}{0pt}
|
||||||
|
\setlength{\parskip}{0.45em}
|
||||||
|
\setlength{\headheight}{24mm}
|
||||||
|
\setlength{\headsep}{3mm}
|
||||||
|
\setlength{\footskip}{8mm}
|
||||||
|
\setlength{\marginparwidth}{1.5cm}
|
||||||
|
\setlength{\marginparsep}{0.25cm}
|
||||||
|
\renewcommand{\arraystretch}{1.22}
|
||||||
|
% Margin separator lines disabled for layout trial.
|
||||||
|
|
||||||
|
\newcommand{\ESCPageResourceHeader}{%
|
||||||
|
\begingroup%
|
||||||
|
\setlength{\parskip}{0pt}%
|
||||||
|
\setlength{\fboxsep}{0pt}%
|
||||||
|
\setlength{\fboxrule}{0.35pt}%
|
||||||
|
\setlength{\arrayrulewidth}{0.35pt}%
|
||||||
|
\noindent\fbox{%
|
||||||
|
\begin{minipage}[c][23.5mm][c]{\dimexpr\textwidth-2\fboxrule\relax}%
|
||||||
|
\begin{minipage}[c][23mm][c]{24mm}\centering%
|
||||||
|
\href{https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-heap4}{\qrcode[level=L,height=22mm]{https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-heap4}}%
|
||||||
|
\end{minipage}%
|
||||||
|
\vrule width0.35pt%
|
||||||
|
\begin{minipage}[c][23mm][c]{\dimexpr\linewidth-48mm-0.7pt\relax}%
|
||||||
|
\setlength{\tabcolsep}{0pt}%
|
||||||
|
\renewcommand{\arraystretch}{0}%
|
||||||
|
\begin{tabularx}{\linewidth}{@{}p{\dimexpr0.50000000\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.15126050\linewidth-\arrayrulewidth\relax}|X@{}}%
|
||||||
|
\parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries TITLE}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{7.7}{7.9}\selectfont\ttfamily\bfseries heap\_4: From Linear Cursor to Reusable Blocks}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries VER.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.6}{6.8}\selectfont\ttfamily\bfseries v00.01}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries DATETIME}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries 2026-07-20T00:00:00+02:00}\hspace{0.45mm}}\par} \\%
|
||||||
|
\end{tabularx}%
|
||||||
|
\par\nointerlineskip%
|
||||||
|
\hrule height0.35pt%
|
||||||
|
\nointerlineskip%
|
||||||
|
\begin{tabularx}{\linewidth}{@{}p{\dimexpr0.50000000\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.25210084\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.14285714\linewidth-\arrayrulewidth\relax}|X@{}}%
|
||||||
|
\parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries PROJECT}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{7.7}{7.9}\selectfont\ttfamily\bfseries Freestanding C nad FreeRTOS}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries SERIES}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries FreeRTOS C}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries CARD}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries 01/15}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries SHEET}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries \thepage/\pageref{LastPage}}\hspace{0.45mm}}\par} \\%
|
||||||
|
\end{tabularx}%
|
||||||
|
\par\nointerlineskip%
|
||||||
|
\hrule height0.35pt%
|
||||||
|
\nointerlineskip%
|
||||||
|
\begin{tabularx}{\linewidth}{@{}p{\dimexpr0.05882353\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.05042017\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.14705882\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.09243697\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.07563025\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.07563025\linewidth-\arrayrulewidth\relax}|X@{}}%
|
||||||
|
\parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries SUBJ.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries Inf.}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries PROG.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries CORE}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries SCOPE}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries LEVEL}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries POS.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hbox to\linewidth{\hspace{0.35mm}{\fontsize{3.4}{3.75}\selectfont\ttfamily\bfseries GITEA UUID}\hfill{\fontsize{3.4}{3.75}\selectfont\ttfamily 87e48095-7793-54e3-9230-4e61d31bd459}\hspace{0.35mm}}\par\nointerlineskip\hbox to\linewidth{\hspace{0.35mm}{\fontsize{3.4}{3.75}\selectfont\ttfamily\bfseries CARD UUID} {\fontsize{3.4}{3.75}\selectfont\ttfamily aea09ab7-9bfd-5368-8d1b-c8724871c1a7}\hfill{\fontsize{3.4}{3.75}\selectfont\ttfamily\bfseries AUTHOR Mateusz Pabiszczak}\hspace{0.35mm}}} \\%
|
||||||
|
\end{tabularx}%
|
||||||
|
\par\nointerlineskip%
|
||||||
|
\hrule height0.35pt%
|
||||||
|
\nointerlineskip%
|
||||||
|
\begin{tabularx}{\linewidth}{@{}X@{}}%
|
||||||
|
\parbox[c][3.46276mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{4.5}{4.85}\selectfont\ttfamily\bfseries GITEA} {\fontsize{4.5}{4.85}\selectfont\ttfamily\href{https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-heap4}{\nolinkurl{https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-heap4}}}} \\%
|
||||||
|
\end{tabularx}%
|
||||||
|
\par\nointerlineskip%
|
||||||
|
\hrule height0.35pt%
|
||||||
|
\nointerlineskip%
|
||||||
|
\begin{tabularx}{\linewidth}{@{}X@{}}%
|
||||||
|
\parbox[c][3.46276mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{4.5}{4.85}\selectfont\ttfamily\bfseries HTML} {\fontsize{4.5}{4.85}\selectfont\ttfamily\href{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/aea09ab7-9bfd-5368-8d1b-c8724871c1a7}{\nolinkurl{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/aea09ab7-9bfd-5368-8d1b-c8724871c1a7}}}} \\%
|
||||||
|
\end{tabularx}%
|
||||||
|
\end{minipage}%
|
||||||
|
\vrule width0.35pt%
|
||||||
|
\begin{minipage}[c][23mm][c]{24mm}\centering%
|
||||||
|
\href{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/aea09ab7-9bfd-5368-8d1b-c8724871c1a7}{\qrcode[level=L,height=22mm]{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/aea09ab7-9bfd-5368-8d1b-c8724871c1a7}}%
|
||||||
|
\end{minipage}%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\endgroup%
|
||||||
|
}
|
||||||
|
|
||||||
|
\pagestyle{fancy}
|
||||||
|
\fancyhf{}
|
||||||
|
\fancyhead[C]{\ESCPageResourceHeader}
|
||||||
|
\fancyfoot[L]{{\fontsize{6.4}{7.0}\selectfont\ttfamily UUID \DocumentUUID}}
|
||||||
|
\fancyfoot[C]{{\fontsize{6.4}{7.0}\selectfont\ttfamily \DocumentAuthor}}
|
||||||
|
\fancyfoot[R]{{\fontsize{6.4}{7.0}\selectfont\ttfamily STRONA \thepage/\pageref{LastPage}}}
|
||||||
|
\renewcommand{\headrulewidth}{0pt}
|
||||||
|
\renewcommand{\footrulewidth}{0.35pt}
|
||||||
|
|
||||||
|
\newcommand{\ESCMarginTag}[4]{%
|
||||||
|
\hspace*{#1}\textcolor{#2}{#3~#4}%
|
||||||
|
}
|
||||||
|
\newcommand{\ESCSectionBlockStart}{%
|
||||||
|
\Needspace{8\baselineskip}%
|
||||||
|
\par\vspace{0.35em}%
|
||||||
|
\noindent\textcolor{black!28}{\rule{\textwidth}{0.35pt}}%
|
||||||
|
\par\vspace{0.15em}%
|
||||||
|
}
|
||||||
|
\newcommand{\ESCSectionBlockEnd}{%
|
||||||
|
\par\vspace{0.45em}%
|
||||||
|
}
|
||||||
|
\newcommand{\ESCTinyStepSeparator}{%
|
||||||
|
\par\vspace{0.10em}%
|
||||||
|
\noindent{\color{black!18}\leaders\hbox{\rule{0.60em}{0.22pt}\hspace{0.38em}}\hfill\kern0pt}%
|
||||||
|
\par\vspace{0.10em}%
|
||||||
|
}
|
||||||
|
\newtcolorbox{ESCTaskFrame}[1]{enhanced,breakable,arc=0pt,boxrule=0.35pt,colback=white,colframe=black,boxsep=0pt,left=1.4mm,right=1.4mm,top=0.8mm,bottom=0.8mm,colbacktitle=black!7,coltitle=black,title={\ttfamily\bfseries TASK\quad #1}}
|
||||||
|
\newtcolorbox{ESCBlockFrame}[1]{enhanced,breakable,arc=0pt,boxrule=0.35pt,colback=white,colframe=black!55,boxsep=0pt,left=1.0mm,right=1.0mm,top=0.6mm,bottom=0.6mm,colbacktitle=black!4,coltitle=black,title={\ttfamily\bfseries BLOCK\quad #1}}
|
||||||
|
\newtcolorbox{ESCStepFrame}[1]{enhanced,breakable,arc=0pt,boxrule=0.35pt,colback=white,colframe=black!28,boxsep=0pt,left=0.8mm,right=0.8mm,top=0.45mm,bottom=0.45mm,colbacktitle=black!2,coltitle=black,title={\ttfamily STEP\quad #1}}
|
||||||
|
\newtcolorbox{ESCConclusionFrame}{enhanced,breakable,arc=0pt,boxrule=0.45pt,colback=blue!2,colframe=blue!45!black,boxsep=0pt,left=1.2mm,right=1.2mm,top=0.7mm,bottom=0.7mm,colbacktitle=blue!7,coltitle=black,title={\ttfamily\bfseries WNIOSEK}}
|
||||||
|
|
||||||
|
\newcommand{\ESCLearningTreeWidget}{%
|
||||||
|
\reversemarginpar
|
||||||
|
\marginnote[%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\begin{minipage}[t]{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\hspace*{0.28em}\textcolor{orange!85!black}{EK~LOCAL~RTOS}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Uczeń odtwarza osiem checkpointów Hazard3 i odróżnia current free, largest free block oraz\textCR minimum-ever.}}\par%
|
||||||
|
\hspace*{0.54em}\textcolor{blue!70!black}{KW~LOCAL~RTOS}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Łączy kod, diagram i zweryfikowany dowód.}}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
]{}
|
||||||
|
\normalmarginpar
|
||||||
|
|
||||||
|
\marginnote{%
|
||||||
|
\begin{minipage}{\marginparwidth}%
|
||||||
|
\raggedright
|
||||||
|
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||||
|
\hspace*{0.06cm}%
|
||||||
|
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||||
|
\raggedright
|
||||||
|
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||||
|
\vspace{0.08em}%
|
||||||
|
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||||
|
\hspace*{0.28em}\textcolor{green!50!black}{EN~LOCAL~EN~RTOS}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Uczeń wyjaśnia nagłówek, wyrównanie, first-fit, split, adresową listę wolnych bloków i\textCR dwustronne coalescing.}}\par%
|
||||||
|
\hspace*{0.54em}\textcolor{blue!70!black}{KW~LOCAL~KW~RTOS}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Łączy kod, diagram i zweryfikowany dowód.}}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\sloppy
|
||||||
|
|
||||||
|
\vspace{1.0em}
|
||||||
|
\noindent{\Large\bfseries Cel karty}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
Uczeń przechodzi od liniowego kursora z K\&R 5.4 do wielokrotnego użycia tej samej areny: rozpoznaje nagłówek bloku, first-fit, split oraz dwustronne scalanie wolnych bloków w heap\_4.
|
||||||
|
|
||||||
|
\vspace{0.8em}
|
||||||
|
\noindent\textcolor{black!25}{\rule{\textwidth}{0.35pt}}
|
||||||
|
\vspace{0.7em}
|
||||||
|
\noindent{\Large\bfseries Zakres karty}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
Dwa małe modele C przygotowują przewidywanie. Główny replay uruchamia prawdziwe FreeRTOS-Kernel V11.3.0 heap\_4 na RV32I/Hazard3 i dowodzi fragmentacji, pełnego scalenia, minimum-ever oraz kontrolowanego OOM.
|
||||||
|
\vspace{0.55em}
|
||||||
|
\small\begin{tabularx}{\textwidth}{@{}>{\ttfamily\raggedright\arraybackslash}p{1.10cm}>{\ttfamily\raggedright\arraybackslash}p{1.45cm}X>{\raggedright\arraybackslash}p{2.50cm}>{\raggedright\arraybackslash}p{1.75cm}>{\ttfamily\raggedright\arraybackslash}p{1.50cm}@{}}
|
||||||
|
\textbf{Lekcja} & \textbf{Task} & \textbf{Najważniejsza idea} & \textbf{Priorytet} & \textbf{Status} & \textbf{Version} \\ \hline
|
||||||
|
\hline
|
||||||
|
\textbf{\texttt{FC01}} & \textbf{\texttt{Task01}} & \textbf{headers, address-ordered free list, first-fit, split, coalescing and heap statistics} & \textbf{główny} & \textbf{opracowane} & \textbf{\texttt{v00.01}} \\ \hline
|
||||||
|
\end{tabularx}
|
||||||
|
\normalsize
|
||||||
|
|
||||||
|
|
||||||
|
\vspace{0.8em}
|
||||||
|
\noindent\textcolor{black!25}{\rule{\textwidth}{0.35pt}}
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\noindent{\small\bfseries A1 — Od liniowego kursora do odzyskiwalnej areny \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.47121\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a1-context.png}}
|
||||||
|
\caption{A1 CONTEXT — od żądania aplikacji do rzeczywistych bajtów ucHeap. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a1-context}
|
||||||
|
\end{figure}
|
||||||
|
\Needspace{6\baselineskip}
|
||||||
|
\begin{tcolorbox}[enhanced,arc=0pt,boxrule=0.35pt,colback=blue!2,colframe=blue!45!black,boxsep=0pt,left=1.2mm,right=1.2mm,top=0.7mm,bottom=0.7mm,colbacktitle=blue!7,coltitle=black,title={\ttfamily\bfseries INTERPRETACJA}]
|
||||||
|
Aplikacja korzysta z publicznego API, heap\_4 zarządza jedną areną, a Hazard3 pokazuje jej rzeczywisty stan.
|
||||||
|
\end{tcolorbox}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\noindent{\small\bfseries A2 — Nagłówek, payload i lista wolnych bloków \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.47273\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a2-structure.png}}
|
||||||
|
\caption{A2 STRUCTURE — metadane wewnątrz areny i adresowo uporządkowana lista. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a2-structure}
|
||||||
|
\end{figure}
|
||||||
|
\Needspace{6\baselineskip}
|
||||||
|
\begin{tcolorbox}[enhanced,arc=0pt,boxrule=0.35pt,colback=blue!2,colframe=blue!45!black,boxsep=0pt,left=1.2mm,right=1.2mm,top=0.7mm,bottom=0.7mm,colbacktitle=blue!7,coltitle=black,title={\ttfamily\bfseries INTERPRETACJA}]
|
||||||
|
Każdy blok ma nagłówek, a tylko wolne bloki wykorzystują wskaźnik next do budowy listy uporządkowanej adresami.
|
||||||
|
\end{tcolorbox}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\begin{landscape}
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\noindent{\small\bfseries A5 — First-fit, fragmentacja i pełne scalenie \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.86383\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a5-flow.png}}
|
||||||
|
\caption{A5 FLOW — inicjalizacja, A/B/C, fragmentacja, dwustronne scalenie i OOM. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a5-flow}
|
||||||
|
\end{figure}
|
||||||
|
\Needspace{6\baselineskip}
|
||||||
|
\begin{tcolorbox}[enhanced,arc=0pt,boxrule=0.35pt,colback=blue!2,colframe=blue!45!black,boxsep=0pt,left=1.2mm,right=1.2mm,top=0.7mm,bottom=0.7mm,colbacktitle=blue!7,coltitle=black,title={\ttfamily\bfseries INTERPRETACJA}]
|
||||||
|
Jeden przebieg A/B/C przechodzi od pustej areny przez trzy przydziały i dwa wolne obszary do ponownie scalonego bloku.
|
||||||
|
\end{tcolorbox}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
\end{landscape}
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\noindent{\small\bfseries A6 — Stan bloku a stan statystyk \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.48636\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a6-state.png}}
|
||||||
|
\caption{A6 STATE — wolny, przydzielony, rozdzielony, scalony oraz minimum-ever. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a6-state}
|
||||||
|
\end{figure}
|
||||||
|
\Needspace{6\baselineskip}
|
||||||
|
\begin{tcolorbox}[enhanced,arc=0pt,boxrule=0.35pt,colback=blue!2,colframe=blue!45!black,boxsep=0pt,left=1.2mm,right=1.2mm,top=0.7mm,bottom=0.7mm,colbacktitle=blue!7,coltitle=black,title={\ttfamily\bfseries INTERPRETACJA}]
|
||||||
|
Bieżąca liczba wolnych bajtów może rosnąć po free, ale minimum-ever pozostaje historycznym minimum.
|
||||||
|
\end{tcolorbox}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\begin{landscape}
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\noindent{\small\bfseries A7 — Dowód w Hazard3 i ELF \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.86596\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a7-runtime.png}}
|
||||||
|
\caption{A7 RUNTIME — kod, ELF, checkpoint, pamięć i statystyki jednego przebiegu. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a7-runtime}
|
||||||
|
\end{figure}
|
||||||
|
\Needspace{6\baselineskip}
|
||||||
|
\begin{tcolorbox}[enhanced,arc=0pt,boxrule=0.35pt,colback=blue!2,colframe=blue!45!black,boxsep=0pt,left=1.2mm,right=1.2mm,top=0.7mm,bottom=0.7mm,colbacktitle=blue!7,coltitle=black,title={\ttfamily\bfseries INTERPRETACJA}]
|
||||||
|
Ten sam artefakt łączy źródło, symbole ELF, osiem checkpointów, pamięć ucHeap i końcowe asercje.
|
||||||
|
\end{tcolorbox}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
\end{landscape}
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\section{Task01 — mechanika heap\_4}
|
||||||
|
|
||||||
|
\begin{ESCTaskFrame}{TASK01 · Predict and prove reusable heap\_4 blocks}
|
||||||
|
{\scriptsize\ttfamily aea09ab7-9bfd-5368-8d1b-c8724871c1a7}\par
|
||||||
|
\begin{ESCBlockFrame}{A — przewidywanie}
|
||||||
|
Przejdź przez model nagłówka, split i porządek adresowy przed uruchomieniem prawdziwego heap\_4.
|
||||||
|
\begin{ESCStepFrame}{size · Policz rozmiary całych bloków.}
|
||||||
|
Rozróżnij surowy BlockLink\_t (8 bajtów) od xHeapStructSize (16 bajtów), dodaj nagłówek do żądań 24, 40 i 16 bajtów, a następnie wyrównaj wynik do 16.
|
||||||
|
\end{ESCStepFrame}
|
||||||
|
\begin{ESCStepFrame}{list · Narysuj listę po free(A,C).}
|
||||||
|
Zaznacz, dlaczego przydzielony B rozdziela dwa wolne obszary.
|
||||||
|
\end{ESCStepFrame}
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{B — replay Hazard3}
|
||||||
|
Powiąż każdy stan diagramu z deterministycznym checkpointem E01--E08.
|
||||||
|
\begin{ESCStepFrame}{allocate · Sprawdź E01--E04.}
|
||||||
|
Potwierdź wartości 4080, 4032, 3968 i 3936 oraz zużycie 48/64/32 bajtów.
|
||||||
|
\end{ESCStepFrame}
|
||||||
|
\begin{ESCStepFrame}{release · Sprawdź E05--E07.}
|
||||||
|
Porównaj liczbę wolnych bloków i udowodnij scalenie poprzedniego, a potem następnego sąsiada.
|
||||||
|
\end{ESCStepFrame}
|
||||||
|
\begin{ESCStepFrame}{verify · Sprawdź E08.}
|
||||||
|
Udowodnij zachowanie minimum-ever, kontrolowany OOM, brak asercji i PASS=1.
|
||||||
|
\end{ESCStepFrame}
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{Ćwiczenie · Ćwiczenie — ta sama suma, inna użyteczność}
|
||||||
|
Zaproponuj dwa układy wolnych bloków o tej samej sumie bajtów, ale tylko jeden zdolny obsłużyć wskazane duże żądanie. Uzasadnij odpowiedź przez largest-free-block.
|
||||||
|
\par\textbf{Evidence:} Dwa diagramy listy, suma wolnych bajtów, rozmiar największego bloku i wynik pvPortMalloc.
|
||||||
|
\par\textbf{Acceptance:} Uczeń nie utożsamia sumy wolnej pamięci z możliwością pojedynczego dużego przydziału.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\tcblower\textbf{Task acceptance:} Na RV32 BlockLink\_t ma 8 bajtów, lecz 16-bajtowe wyrównanie portu daje xHeapStructSize=16; A/B/C zużywają 48/64/32 bajty; po free(A,C) są dwa wolne bloki, po free(B) jeden; final free = initial free; minimum-ever nie rośnie; OOM zwraca NULL; PASS=1.
|
||||||
|
\end{ESCTaskFrame}
|
||||||
|
\begin{ESCConclusionFrame}
|
||||||
|
heap\_4 odzyskuje pamięć dzięki metadanym w arenie, adresowo uporządkowanej liście i scalaniu sąsiadów. Suma wolnych bajtów nie zastępuje informacji o największym bloku, a minimum-ever jest historią, nie bieżącym stanem.
|
||||||
|
\end{ESCConclusionFrame}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
|
||||||
|
\end{document}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
# FC01 — plan iteracji diagramów w Hazard3
|
||||||
|
|
||||||
|
## Zasada
|
||||||
|
|
||||||
|
Kursor porusza się po diagramie, nie po przypadkowych liniach programu.
|
||||||
|
Każda kotwica ma jeden z dwóch kontraktów:
|
||||||
|
|
||||||
|
- `CODE` — otwiera właściwe źródło lub dowód ELF; nie uruchamia programu;
|
||||||
|
- `RUN` — odtwarza program od czystego resetu do wskazanego checkpointu,
|
||||||
|
ustawia Neovima i udostępnia zestaw wyrażeń GDB.
|
||||||
|
|
||||||
|
Ten sam snapshot może wystąpić w kilku perspektywach. Nie powtarza to
|
||||||
|
eksperymentu: A2 pyta o strukturę, A6 o stan, a A7 o jakość dowodu.
|
||||||
|
|
||||||
|
## A1 CONTEXT — granice odpowiedzialności
|
||||||
|
|
||||||
|
| Step | Tryb | Otwórz | Pytanie kontrolne |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 01 Application | CODE | `main()` | Czy aplikacja dotyka metadanych allocatora? |
|
||||||
|
| 02 FreeRTOS API | CODE | wywołania `pvPortMalloc`, `vPortFree`, stats | Gdzie kończy się publiczny kontrakt? |
|
||||||
|
| 03 heap_4 | CODE | upstream `heap_4.c` | Które funkcje wybierają, dzielą i scalają blok? |
|
||||||
|
| 04 ucHeap | CODE | definicja `ucHeap[4096]` | Gdzie fizycznie leżą nagłówki i payload? |
|
||||||
|
| 05 Hazard3 | CODE | test symboli ELF | Czy debugujemy dokładnie artefakt z karty? |
|
||||||
|
|
||||||
|
A1 nie ma checkpointu: opisuje architekturę, a nie zdarzenie runtime.
|
||||||
|
|
||||||
|
## A2 STRUCTURE — nagłówek, split i scalenie
|
||||||
|
|
||||||
|
| Step | Tryb / replay | Obserwuj | Warunek zaliczenia |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 01 Header | CODE | `BlockLink_t`, `portBYTE_ALIGNMENT` | raw header=8 B, alignment=16 B, `xHeapStructSize`=16 B |
|
||||||
|
| 02 Alignment | RUN E02 | `g_a`, `g_a_consumed` | A jest wyrównane, cały blok zużywa 48 B |
|
||||||
|
| 03 Split | RUN E02 | `g_after_a_stats` | pozostaje 1 wolny blok i 4032 B |
|
||||||
|
| 04 Address order | RUN E06 | `g_a`, `g_b`, `g_c`, stats | adresy rosną, a B rozdziela 2 wolne obszary |
|
||||||
|
| 05 Merge previous | RUN E06 + step | `pxIterator`, `pxBlockToInsert` | poprzedni/lewy blok jest testowany i scalany pierwszy |
|
||||||
|
| 06 Merge next | RUN E06 + step | powiększony blok i `pxNextFreeBlock` | następny/prawy blok jest scalany drugi |
|
||||||
|
|
||||||
|
Dla kroków 05–06 po replay E06:
|
||||||
|
|
||||||
|
```gdb
|
||||||
|
tbreak prvInsertBlockIntoFreeList
|
||||||
|
continue # wykonaj vPortFree(g_b)
|
||||||
|
p pxIterator
|
||||||
|
p pxBlockToInsert
|
||||||
|
next # test poprzedniego/lewego sąsiada
|
||||||
|
next # test następnego/prawego sąsiada
|
||||||
|
```
|
||||||
|
|
||||||
|
## A5 FLOW — pełny przebieg
|
||||||
|
|
||||||
|
| Event | Operacja | Wartości RV32/Hazard3 | Dowód |
|
||||||
|
|---|---|---|---|
|
||||||
|
| E01 | reset/init | free=4080 | 1 blok, largest=4080 |
|
||||||
|
| E02 | alloc A(24) | consumed=48, free=4032 | A != NULL, aligned=1 |
|
||||||
|
| E03 | alloc B(40) | consumed=64, free=3968 | B != NULL, aligned=1 |
|
||||||
|
| E04 | alloc C(16) | consumed=32, free=3936 | C != NULL, aligned=1 |
|
||||||
|
| E05 | free A | free=3984 | 2 wolne bloki |
|
||||||
|
| E06 | free C | free=4016 | 2 bloki, free > largest |
|
||||||
|
| E07 | free B | free=largest=4080 | 1 wolny blok, minimum-ever=3936 |
|
||||||
|
| E08 | oversized alloc | NULL | hook=1, asserts=0, layout=1, PASS=1 |
|
||||||
|
|
||||||
|
Każdy Enter na A5 wykonuje czysty replay do dokładnie jednego eventu. Nie
|
||||||
|
kontynuujemy stanu pozostałego po poprzednim kroku.
|
||||||
|
|
||||||
|
## A6 STATE — znaczenie liczników
|
||||||
|
|
||||||
|
| Step | Replay | Hipoteza |
|
||||||
|
|---|---|---|
|
||||||
|
| 01 Free | E01 | current free i largest są równe |
|
||||||
|
| 02 Allocated | E04 | przydziały zmniejszają current free do 3936 |
|
||||||
|
| 03 Fragmented | E06 | total free może być większe od largest |
|
||||||
|
| 04 Coalesced | E07 | current, largest i initial znów są równe |
|
||||||
|
| 05 Minimum-ever | E08 | minimum-ever pozostaje 3936 mimo odzyskania areny |
|
||||||
|
|
||||||
|
## A7 RUNTIME — jakość dowodu
|
||||||
|
|
||||||
|
| Step | Tryb / replay | Co musi znaleźć się w dowodzie |
|
||||||
|
|---|---|---|
|
||||||
|
| 01 Source | CODE | blob źródła eksperymentu |
|
||||||
|
| 02 ELF | CODE | SHA artefaktu i symbole upstream/checkpointów |
|
||||||
|
| 03 Fragmented | RUN E06 | PC, 2 bloki, free=4016, largest<free |
|
||||||
|
| 04 Memory | RUN E06 | adres `ucHeap`, adresy A/B/C i bajty nagłówków |
|
||||||
|
| 05 Verdict | RUN E08 | final=initial, OOM NULL, hook=1, asserts=0, PASS=1 |
|
||||||
|
|
||||||
|
Snapshot do raportu jest kompletny dopiero wtedy, gdy łączy nazwę eventu,
|
||||||
|
tożsamość ELF, pozycję programu i wartości rozstrzygające hipotezę.
|
||||||
|
|
||||||
|
## Świadomie pominięte perspektywy
|
||||||
|
|
||||||
|
- A3 DISPATCH — allocator nie ma callbacku ani mechanizmu dispatch;
|
||||||
|
- A4 APPLICATION — konkretny eksperyment A/B/C jest pełnym A5;
|
||||||
|
- A8 PATTERNS — first-fit i intrusive address-ordered list są już mierzalne w
|
||||||
|
A2 i A5; osobny diagram powielałby materiał.
|
||||||
|
|
||||||
|
## Definition of done
|
||||||
|
|
||||||
|
1. Hostowe modele i adapter przechodzą `tests/test_host.sh`.
|
||||||
|
2. ELF RV32 zawiera upstream `heap_4` oraz osiem symboli checkpointów.
|
||||||
|
3. Hazard3 RTL kończy Task03 kodem `0`.
|
||||||
|
4. E01–E08 spełniają wyrażenia z `debug_checkpoints`.
|
||||||
|
5. Wszystkie kroki RUN mają `snapshot_ref`; kroki CODE nie udają runtime.
|
||||||
|
6. SVG, HTML i TeX powstają z tego samego `json/card_source.json`.
|
||||||
@@ -1,21 +1,19 @@
|
|||||||
\documentclass[12pt]{article}
|
\documentclass[10pt]{article}
|
||||||
\usepackage[T1]{fontenc}
|
\usepackage[T1]{fontenc}
|
||||||
\usepackage[utf8]{inputenc}
|
\usepackage[utf8]{inputenc}
|
||||||
\usepackage[polish]{babel}
|
\usepackage[polish]{babel}
|
||||||
\usepackage[a4paper,margin=2.0cm]{geometry}
|
\usepackage[a4paper,margin=1.55cm]{geometry}
|
||||||
\usepackage{array}
|
\usepackage{array,tabularx,booktabs}
|
||||||
\usepackage{tabularx}
|
\usepackage{amsmath,amssymb}
|
||||||
\usepackage{xcolor}
|
\usepackage{xcolor,listings}
|
||||||
\usepackage{listings}
|
\usepackage{hyperref,fancyhdr,lastpage}
|
||||||
\usepackage{hyperref}
|
|
||||||
\usepackage{fancyhdr}
|
|
||||||
\usepackage{lastpage}
|
|
||||||
\usepackage{enumitem}
|
\usepackage{enumitem}
|
||||||
|
|
||||||
\definecolor{accent}{HTML}{16324A}
|
\definecolor{accent}{HTML}{16324A}
|
||||||
\definecolor{accentlight}{HTML}{EEF3F7}
|
\definecolor{accentlight}{HTML}{EEF3F7}
|
||||||
\definecolor{rulegray}{HTML}{D7DEE5}
|
\definecolor{rulegray}{HTML}{D7DEE5}
|
||||||
\hypersetup{colorlinks=true,linkcolor=blue,urlcolor=blue,citecolor=blue}
|
\definecolor{success}{HTML}{236B45}
|
||||||
|
\hypersetup{colorlinks=true,linkcolor=accent,urlcolor=blue}
|
||||||
\IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}}
|
\IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}}
|
||||||
\newcommand{\PublisherDomain}{mpabi}
|
\newcommand{\PublisherDomain}{mpabi}
|
||||||
\newcommand{\DocumentAuthor}{M. Pabiszczak}
|
\newcommand{\DocumentAuthor}{M. Pabiszczak}
|
||||||
@@ -23,236 +21,294 @@
|
|||||||
\newcommand{\CardArea}{inf}
|
\newcommand{\CardArea}{inf}
|
||||||
\newcommand{\CardSeries}{rv32i-freertos}
|
\newcommand{\CardSeries}{rv32i-freertos}
|
||||||
\newcommand{\CardNumber}{01}
|
\newcommand{\CardNumber}{01}
|
||||||
\newcommand{\CardCount}{??}
|
\newcommand{\CardCount}{15}
|
||||||
\newcommand{\CardSlug}{heap4}
|
\newcommand{\CardSlug}{heap4}
|
||||||
\newcommand{\CardVersion}{v0.1}
|
\newcommand{\CardVersion}{v0.3}
|
||||||
\newcommand{\DocumentKey}{\PublisherDomain/\CardArea/\CardSeries/\CardNumber/\CardSlug/\CardVersion}
|
\newcommand{\DocumentKey}{\PublisherDomain/\CardArea/\CardSeries/\CardNumber/\CardSlug/\CardVersion}
|
||||||
\newcommand{\DocumentUUID}{4654524c-73f2-46df-83a3-d448ba6d6c5a}
|
\newcommand{\DocumentUUID}{4654524c-73f2-46df-83a3-d448ba6d6c5a}
|
||||||
\renewcommand{\lstlistingname}{Listing}
|
|
||||||
\renewcommand{\lstlistlistingname}{Lista listingów}
|
|
||||||
|
|
||||||
\lstset{basicstyle=\ttfamily\small,columns=fullflexible,keepspaces=true,frame=single,breaklines=true,showstringspaces=false,numbers=left,numberstyle=\tiny\color{accent},stepnumber=1,numbersep=8pt,backgroundcolor=\color{accentlight},rulecolor=\color{rulegray},captionpos=b}
|
|
||||||
\newcommand{\TaskSection}[5]{\section{Task #1: #3}\subsection*{Algorytm i zakresy linii}#4\lstinputlisting[language=C,caption={Task #1: #3},label={lst:task#1-c}]{../src/tasks/#2.c}\subsection*{Co sprawdzić w ASM}#5\lstinputlisting[basicstyle=\ttfamily\scriptsize,caption={ASM dla task #1},label={lst:task#1-asm}]{../build/#2/#2.s}}
|
|
||||||
|
|
||||||
|
\lstset{
|
||||||
|
language=C,basicstyle=\ttfamily\scriptsize,columns=fullflexible,
|
||||||
|
keepspaces=true,frame=single,breaklines=true,showstringspaces=false,
|
||||||
|
numbers=none,backgroundcolor=\color{accentlight},rulecolor=\color{rulegray}
|
||||||
|
}
|
||||||
\pagestyle{fancy}
|
\pagestyle{fancy}
|
||||||
\fancyhf{}
|
\fancyhf{}
|
||||||
\lhead{\textbf{FreeRTOS: heap\_4}}
|
\lhead{\textbf{FC01 · FreeRTOS \texttt{heap\_4}}}
|
||||||
\rhead{\small \DocumentAuthor, \DocumentYear}
|
\rhead{\small 30 minut · Pico 2 W / RP2350}
|
||||||
\lfoot{\scriptsize commit \BuildCommit}
|
\lfoot{\scriptsize commit \BuildCommit}
|
||||||
\cfoot{\scriptsize \thepage/\pageref{LastPage}}
|
\cfoot{\scriptsize \thepage/\pageref{LastPage}}
|
||||||
\rfoot{\scriptsize uuid \DocumentUUID}
|
\rfoot{\scriptsize V11.3.0 / \CardVersion}
|
||||||
\renewcommand{\headrulewidth}{0.4pt}
|
\setlength{\headheight}{14pt}
|
||||||
\setlength{\headheight}{18pt}
|
\setlength{\footskip}{19pt}
|
||||||
\setlength{\footskip}{24pt}
|
\setlist[itemize]{nosep,leftmargin=1.45em}
|
||||||
|
\setlist[enumerate]{nosep,leftmargin=1.65em}
|
||||||
|
\newcommand{\checkline}{\(\square\)\;}
|
||||||
|
\newcommand{\blank}[1]{\rule{#1}{.2pt}}
|
||||||
|
|
||||||
\begin{document}
|
\begin{document}
|
||||||
\sloppy
|
\sloppy
|
||||||
\noindent
|
|
||||||
\begin{tabular}{@{}lp{0.72\textwidth}@{}}
|
|
||||||
Project: & FreeRTOS: \texttt{heap\_4} \\
|
|
||||||
Subject: & Informatyka \\
|
|
||||||
Level: & 1, Zakres Podstawowy, PP \\
|
|
||||||
Topic: & dynamiczna alokacja pamięci, lista wolnych bloków, scalanie bloków \\
|
|
||||||
Revision date: & 02.05.2026 \\
|
|
||||||
Status: & Draft \\
|
|
||||||
Card in series: & \small\texttt{\CardNumber/\CardCount} \\
|
|
||||||
Card version: & \small\texttt{\CardVersion} \\
|
|
||||||
Card key: & \small\texttt{\DocumentKey} \\
|
|
||||||
UUID: & \small\texttt{\DocumentUUID} \\
|
|
||||||
Commit: & \small\texttt{\BuildCommit} \\
|
|
||||||
Scope: & pierwsza karta serii FreeRTOS; model dydaktyczny mechaniki \texttt{heap\_4.c} \\
|
|
||||||
Files used: & \small
|
|
||||||
\begin{tabular}[t]{@{}l@{}}
|
|
||||||
Task01: \texttt{task01\_heap\_map.c} \\
|
|
||||||
Task02: \texttt{task02\_free\_list.c} \\
|
|
||||||
Task03: \texttt{task03\_heap\_init.c} \\
|
|
||||||
Task04: \texttt{task04\_alignment.c} \\
|
|
||||||
Task05: \texttt{task05\_malloc\_first\_fit.c} \\
|
|
||||||
Task06: \texttt{task06\_split\_block.c} \\
|
|
||||||
Task07: \texttt{task07\_free\_insert.c} \\
|
|
||||||
Task08: \texttt{task08\_coalescing.c} \\
|
|
||||||
Task09: \texttt{task09\_config\_macros.c} \\
|
|
||||||
Task10: \texttt{task10\_heap4\_demo.c} \\
|
|
||||||
Common: \texttt{heap4\_common.h} \\
|
|
||||||
Runtime: \texttt{memops.c} \\
|
|
||||||
Startup: \texttt{crt0.S}
|
|
||||||
\end{tabular}
|
|
||||||
\end{tabular}
|
|
||||||
|
|
||||||
\vspace{0.8em}
|
\begin{center}
|
||||||
\noindent\rule{\textwidth}{0.5pt}
|
{\LARGE\bfseries FreeRTOS \texttt{heap\_4}: od kursora do wolnych bloków}\par
|
||||||
|
\vspace{.25em}
|
||||||
|
{\large bezpośrednia kontynuacja projektu K\&R 5.4}\par
|
||||||
|
\end{center}
|
||||||
|
|
||||||
\section{Cel}
|
\noindent\begin{tabularx}{\textwidth}{@{}p{1.45cm}Xp{1.4cm}X@{}}
|
||||||
Ta karta zaczyna serię FreeRTOS od \texttt{heap\_4}, ponieważ ten plik jest
|
\toprule
|
||||||
naturalnym pomostem po kartach z języka C. Wymaga wskaźników, tablic,
|
Karta & FC01 / \CardCount & Czas & 30 minut \\
|
||||||
struktur, listy jednokierunkowej, \texttt{static}, \texttt{typedef}, makr oraz
|
Płytka & Raspberry Pi Pico 2 W & Układ & RP2350, rdzeń RISC-V \\
|
||||||
arytmetyki adresów, ale nie wymaga jeszcze schedulera, przerwań ani
|
Allocator & FreeRTOS-Kernel V11.3.0 & Źródło & upstream \texttt{heap\_4.c} \\
|
||||||
uruchamiania tasków.
|
Wersja & \CardVersion & Data & 19.07.2026 \\
|
||||||
|
\bottomrule
|
||||||
Przykłady są małym modelem edukacyjnym mechaniki \texttt{heap\_4.c}. Nie są
|
|
||||||
kopią pliku upstream FreeRTOS. Na RV32I wyniki są zapisywane w globalnych
|
|
||||||
zmiennych \texttt{volatile}; \texttt{printf} jest dostępny wyłącznie w wariancie
|
|
||||||
hostowym przez flagę \texttt{-DHOST\_PRINTF=1}.
|
|
||||||
|
|
||||||
Listingi C i ASM mają numerowane linie. Opisy w sekcjach tasków odnoszą się
|
|
||||||
do numerów widocznych po lewej stronie listingów C.
|
|
||||||
|
|
||||||
\lstinputlisting[language=C,caption={Wspólny nagłówek dla karty \texttt{heap\_4}},label={lst:common-h}]{../src/tasks/heap4_common.h}
|
|
||||||
|
|
||||||
\subsection{\texttt{static inline} w nagłówku}
|
|
||||||
Helpery z Listingu~\ref{lst:common-h} są zapisane jako \texttt{static inline},
|
|
||||||
bo każdy task jest osobnym programem i dołącza ten sam nagłówek. \texttt{static}
|
|
||||||
daje prywatną kopię funkcji w każdym pliku \texttt{.c}, a \texttt{inline}
|
|
||||||
pozwala kompilatorowi wstawić krótkie obliczenia, takie jak wyrównanie rozmiaru
|
|
||||||
lub sprawdzenie bitu alokacji, bez kosztu zwykłego wywołania funkcji.
|
|
||||||
|
|
||||||
\section{Mapa karty}
|
|
||||||
\begin{tabularx}{\textwidth}{|p{2.1cm}|X|}
|
|
||||||
\hline
|
|
||||||
Task & Temat \\
|
|
||||||
\hline
|
|
||||||
1 & Mapa pojęć: obszar sterty, nagłówek bloku i bit alokacji. \\
|
|
||||||
\hline
|
|
||||||
2 & Lista wolnych bloków jako lista jednokierunkowa. \\
|
|
||||||
\hline
|
|
||||||
3 & Inicjalizacja sterty, pierwszy wolny blok i wartownik końca listy. \\
|
|
||||||
\hline
|
|
||||||
4 & Wyrównanie rozmiarów żądań i adresu początku sterty. \\
|
|
||||||
\hline
|
|
||||||
5 & Pierwszy pasujący blok, czyli zasada \texttt{first fit}. \\
|
|
||||||
\hline
|
|
||||||
6 & Podział dużego bloku na część przydzieloną i pozostały blok wolny. \\
|
|
||||||
\hline
|
|
||||||
7 & Zwracanie bloku i wstawianie go do listy według adresu. \\
|
|
||||||
\hline
|
|
||||||
8 & Scalanie sąsiednich wolnych bloków. \\
|
|
||||||
\hline
|
|
||||||
9 & Makra konfiguracyjne oraz hostowy \texttt{TRACE\_PRINTF}. \\
|
|
||||||
\hline
|
|
||||||
10 & Mini-demonstracja sekwencji \texttt{malloc/free}. \\
|
|
||||||
\hline
|
|
||||||
\end{tabularx}
|
\end{tabularx}
|
||||||
|
|
||||||
\TaskSection{01}{task01_heap_map}{mapa \texttt{heap\_4}}{
|
\section*{Punkt startowy — tego już nie powtarzamy}
|
||||||
\begin{itemize}[leftmargin=1.6em]
|
|
||||||
\item Linie 3--7 definiują zmienne \texttt{volatile}, czyli obserwowalne wyniki taska.
|
|
||||||
\item Linie 11--16 tworzą pojedynczy blok i zapisują w jego polu \texttt{size} rozmiar z bitem alokacji.
|
|
||||||
\item Linie 18--22 rozbijają nagłówek bloku na wartości pomocnicze: rozmiar nagłówka, rozmiar wyrównany, bit zajętości, rozmiar całego bloku i część użytkownika.
|
|
||||||
\item Linie 24--27 wypisują te same wartości tylko w wariancie hostowym; na RV32I makro \texttt{TRACE\_PRINTF} znika.
|
|
||||||
\item Wynik sprawdzaj w \texttt{g\_header\_size}, \texttt{g\_allocated\_bit\_set}, \texttt{g\_total\_block\_bytes} i \texttt{g\_user\_bytes}.
|
|
||||||
\end{itemize}
|
|
||||||
}{
|
|
||||||
W ASM szukaj operacji ustawiania najwyższego bitu rozmiaru oraz zapisów do symboli globalnych \texttt{g\_*}. Dostęp do pól \texttt{block.next} i \texttt{block.size} powinien być zwykłym zapisem pod adres bazowy stosu z przesunięciem.
|
|
||||||
}
|
|
||||||
\TaskSection{02}{task02_free_list}{lista wolnych bloków}{
|
|
||||||
\begin{itemize}[leftmargin=1.6em]
|
|
||||||
\item Linie 7--16 implementują sumowanie rozmiarów bloków na liście.
|
|
||||||
\item Linie 18--28 przechodzą po tej samej liście i zapamiętują największy blok.
|
|
||||||
\item Linie 30--39 liczą liczbę węzłów listy.
|
|
||||||
\item Linie 43--55 budują ręcznie listę \texttt{start -> a -> b -> c -> NULL}.
|
|
||||||
\item Linie 57--59 uruchamiają trzy funkcje pomiarowe i zapisują wyniki w \texttt{volatile}.
|
|
||||||
\end{itemize}
|
|
||||||
}{
|
|
||||||
W ASM zwróć uwagę na pętle po wskaźniku \texttt{block = block->next}. To jest podstawowy idiom przechodzenia po liście jednokierunkowej, widoczny jako ładowanie pola \texttt{next} i skok warunkowy.
|
|
||||||
}
|
|
||||||
\TaskSection{03}{task03_heap_init}{inicjalizacja sterty}{
|
|
||||||
\begin{itemize}[leftmargin=1.6em]
|
|
||||||
\item Linie 3--7 definiują bufor sterty, węzeł startowy listy i wskaźnik końcowego wartownika.
|
|
||||||
\item Linie 20--23 wyrównują początek sterty i obcinają dostępną długość do wielokrotności wyrównania.
|
|
||||||
\item Linie 25--28 ustawiają adres i pola końcowego wartownika.
|
|
||||||
\item Linie 30--34 tworzą pierwszy wolny blok i podpinają go za \texttt{start}.
|
|
||||||
\item Linie 39--43 wykonują inicjalizację i zapisują trzy wartości kontrolne.
|
|
||||||
\end{itemize}
|
|
||||||
}{
|
|
||||||
W ASM szukaj maskowania dolnych bitów adresu i rozmiaru. Warto też porównać zapisy do \texttt{first->next}, \texttt{first->size}, \texttt{start.next} i \texttt{start.size}; każdy z nich jest dostępem do pola struktury.
|
|
||||||
}
|
|
||||||
\TaskSection{04}{task04_alignment}{wyrównanie}{
|
|
||||||
\begin{itemize}[leftmargin=1.6em]
|
|
||||||
\item Linie 3--6 definiują wyniki pokazujące wpływ wyrównania.
|
|
||||||
\item Linie 14--16 liczą rzeczywiste rozmiary bloków dla żądań 1, 9 i 17 bajtów, po dodaniu nagłówka.
|
|
||||||
\item Linie 18--20 biorą celowo nie-wyrównany adres \texttt{\&bytes[1]} i wyliczają przesunięcie do kolejnej granicy wyrównania.
|
|
||||||
\item Linie 22--24 są hostowym wypisem kontrolnym.
|
|
||||||
\end{itemize}
|
|
||||||
}{
|
|
||||||
W ASM znajdź operacje \texttt{and} używane do testowania dolnych bitów oraz dodawanie brakującej liczby bajtów do wyrównania. To jest ten sam mechanizm, którego używa alokator dla rozmiaru bloku i początku sterty.
|
|
||||||
}
|
|
||||||
\TaskSection{05}{task05_malloc_first_fit}{\texttt{first fit}}{
|
|
||||||
\begin{itemize}[leftmargin=1.6em]
|
|
||||||
\item Linie 13--30 inicjalizują stertę z jednym wolnym blokiem oraz wartownikiem końca.
|
|
||||||
\item Linie 38--40 zamieniają żądanie użytkownika na całkowity rozmiar bloku i ustawiają początek przeszukiwania listy.
|
|
||||||
\item Linie 42--51 realizują algorytm \texttt{first fit}: idą po liście do pierwszego bloku o wystarczającym rozmiarze.
|
|
||||||
\item Linie 44--47 zdejmują znaleziony blok z listy wolnych, oznaczają go jako zajęty i zwracają adres danych użytkownika.
|
|
||||||
\item Linie 61--67 wykonują alokację i zapisują, czy się udała, jaki rozmiar dostał blok i ile wolnego miejsca zostało na liście.
|
|
||||||
\end{itemize}
|
|
||||||
}{
|
|
||||||
W ASM obserwuj porównanie \texttt{block->size >= total}, zmianę \texttt{previous->next} oraz dodanie \texttt{sizeof(HeapBlock)} do adresu bloku przed zwróceniem wskaźnika użytkownika.
|
|
||||||
}
|
|
||||||
\TaskSection{06}{task06_split_block}{podział bloku}{
|
|
||||||
\begin{itemize}[leftmargin=1.6em]
|
|
||||||
\item Linie 3--12 definiują rozmiar sterty, minimalny sensowny rozmiar reszty i zmienne wynikowe.
|
|
||||||
\item Linie 14--28 tworzą jeden duży wolny blok.
|
|
||||||
\item Linie 38--44 szukają bloku pasującego do żądania i zapamiętują jego pierwotny rozmiar.
|
|
||||||
\item Linie 45--50 wykonują podział: nowy blok \texttt{remainder} zaczyna się pod adresem \texttt{block + total}.
|
|
||||||
\item Linie 51--56 obsługują przypadek bez podziału, oznaczają blok jako zajęty i zwracają adres użytkownika.
|
|
||||||
\item Linie 65--73 liczą pozostałe wolne bloki, a linie 81--87 zapisują wynik demonstracji.
|
|
||||||
\end{itemize}
|
|
||||||
}{
|
|
||||||
W ASM szukaj arytmetyki adresów dla \texttt{remainder}: adres bloku plus \texttt{total}. To najważniejszy fragment pokazujący, że dzielenie bloku jest tylko przesunięciem wskaźnika i zapisaniem nowego nagłówka.
|
|
||||||
}
|
|
||||||
\TaskSection{07}{task07_free_insert}{\texttt{free} i wstawianie}{
|
|
||||||
\begin{itemize}[leftmargin=1.6em]
|
|
||||||
\item Linie 11--20 wstawiają blok do listy w miejscu wynikającym z porządku adresów.
|
|
||||||
\item Linie 22--32 modelują \texttt{free}: z adresu użytkownika wracają do nagłówka bloku, czyszczą bit alokacji i wywołują wstawianie.
|
|
||||||
\item Linie 34--54 zawierają funkcje kontrolne liczące węzły oraz łączny rozmiar wolnej pamięci.
|
|
||||||
\item Linie 63--76 budują układ \texttt{a}, \texttt{b}, \texttt{c}, gdzie \texttt{b} jest zajętym blokiem między dwoma wolnymi.
|
|
||||||
\item Linie 78--83 zwalniają \texttt{b} i sprawdzają, czy lista ma kolejność \texttt{a -> b -> c -> end}.
|
|
||||||
\end{itemize}
|
|
||||||
}{
|
|
||||||
W ASM zwróć uwagę na porównywanie adresów w pętli z linii 15. To porządkowanie po adresie jest przygotowaniem do scalania bloków w następnym tasku.
|
|
||||||
}
|
|
||||||
\TaskSection{08}{task08_coalescing}{scalanie bloków}{
|
|
||||||
\begin{itemize}[leftmargin=1.6em]
|
|
||||||
\item Linie 11--17 znajdują miejsce w liście, w którym powinien znaleźć się zwracany blok.
|
|
||||||
\item Linie 19--25 sprawdzają sąsiedztwo z prawym blokiem; jeśli adres końca zwracanego bloku jest adresem następnego, rozmiary są łączone.
|
|
||||||
\item Linie 27--32 sprawdzają sąsiedztwo z lewym blokiem i ewentualnie dołączają scalony wynik do lewej strony.
|
|
||||||
\item Linie 35--44 liczą końcową liczbę wolnych bloków.
|
|
||||||
\item Linie 52--65 budują trzy sąsiednie bloki w pamięci, a linia 67 uruchamia scalanie środkowego bloku.
|
|
||||||
\item Linie 69--71 zapisują, czy po scaleniu został jeden blok i jaki ma rozmiar.
|
|
||||||
\end{itemize}
|
|
||||||
}{
|
|
||||||
W ASM szukaj testu \texttt{(uint8\_t *)block + block->size == iterator->next}. To jest sedno scalania: alokator nie porównuje indeksów, tylko rzeczywiste adresy końca i początku bloków.
|
|
||||||
}
|
|
||||||
\TaskSection{09}{task09_config_macros}{makra konfiguracyjne}{
|
|
||||||
\begin{itemize}[leftmargin=1.6em]
|
|
||||||
\item Linie 3--9 definiują wartości domyślne tylko wtedy, gdy nie zostały podane z zewnątrz przez kompilator.
|
|
||||||
\item Linia 11 tworzy wartość pochodną od konfiguracji i wyrównania.
|
|
||||||
\item Linie 20--22 zapisują wartości makr do zmiennych \texttt{volatile}, żeby były widoczne w debugerze.
|
|
||||||
\item Linie 24--28 pokazują kompilację warunkową: host ustawia \texttt{g\_trace\_is\_host\_only} na 1, RV32I na 0.
|
|
||||||
\item Linie 30--32 wypisują wynik tylko w wariancie hostowym.
|
|
||||||
\end{itemize}
|
|
||||||
}{
|
|
||||||
W ASM dla RV32I nie powinno być wywołania \texttt{printf}. Sprawdź też, że makra konfiguracyjne nie są zmiennymi w pamięci, tylko stałymi wstawionymi do kodu przez preprocesor.
|
|
||||||
}
|
|
||||||
\TaskSection{10}{task10_heap4_demo}{mini \texttt{heap\_4}}{
|
|
||||||
\begin{itemize}[leftmargin=1.6em]
|
|
||||||
\item Linie 18--38 to funkcje diagnostyczne przechodzące po liście wolnych bloków.
|
|
||||||
\item Linie 40--62 wstawiają blok i scalają go z prawym oraz lewym sąsiadem.
|
|
||||||
\item Linie 64--88 inicjalizują stertę: wyrównują początek, ustawiają wartownika, pierwszy wolny blok i liczniki wolnej pamięci.
|
|
||||||
\item Linie 90--131 implementują alokację: sprawdzają rozmiar żądania, szukają bloku, dzielą go lub zdejmują w całości z listy, oznaczają jako zajęty i aktualizują minimum wolnej pamięci.
|
|
||||||
\item Linie 133--149 implementują zwalnianie: wracają z adresu użytkownika do nagłówka, sprawdzają bit alokacji, przywracają rozmiar i scalają blok.
|
|
||||||
\item Linie 157--169 wykonują sekwencję \texttt{malloc/free} i zapisują stan końcowy alokatora.
|
|
||||||
\end{itemize}
|
|
||||||
}{
|
|
||||||
W ASM porównaj trzy fragmenty: inicjalizację listy, pętlę szukania wolnego bloku oraz wywołanie \texttt{insert\_block\_merge}. To pokazuje, że pełny model składa się z prostych operacji wskaźnikowych poznanych w poprzednich taskach.
|
|
||||||
}
|
|
||||||
|
|
||||||
\section{Polecenia}
|
Z poprzedniej karty działa projekt:
|
||||||
\begin{enumerate}[leftmargin=1.6em]
|
|
||||||
\item Zbuduj kartę dla RV32I poleceniem \texttt{make tasks}.
|
\begin{lstlisting}
|
||||||
\item Zbuduj wariant hostowy poleceniem \texttt{make host}; wypisywanie działa tylko przez \texttt{HOST\_PRINTF}.
|
allocbuf[64]; allocp = allocbuf;
|
||||||
\item W tasku 6 zmień próg \texttt{MIN\_SPLIT\_SIZE} i sprawdź, kiedy blok nie jest dzielony.
|
alloc_local(5) -> offset 0
|
||||||
\item W tasku 8 zmień rozmiary bloków tak, aby scalanie działało tylko z prawym sąsiadem.
|
alloc_local(7) -> offset 5
|
||||||
\item W tasku 10 dodaj większą alokację, która ma się nie udać, i zapisz status w nowej zmiennej \texttt{volatile}.
|
allocp -> offset 12
|
||||||
|
alloc_local(0) i OOM -> NULL; kursor pozostaje na 12
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\noindent\fcolorbox{accent}{accentlight}{%
|
||||||
|
\begin{minipage}{.94\textwidth}
|
||||||
|
\textbf{Pytanie projektowe.} Jak zwolnić obszar 5 B spod offsetu 0, zachować
|
||||||
|
późniejszy obszar 7 B spod offsetu 5 i ponownie wykorzystać powstałą dziurę?
|
||||||
|
W naszym projekcie nie istnieje \texttt{free}; reset odzyskuje całą arenę.
|
||||||
|
Samo cofnięcie kursora zniszczyłoby również późniejsze przydziały.
|
||||||
|
\end{minipage}}
|
||||||
|
|
||||||
|
\section*{Cel lekcji}
|
||||||
|
|
||||||
|
Potrafię wskazać trzy elementy dodane przez \texttt{heap\_4} do alokatora
|
||||||
|
liniowego --- \textbf{nagłówek bloku, listę wolnych bloków i scalanie} --- oraz
|
||||||
|
potwierdzić ich działanie na prawdziwym \texttt{heap\_4.c}.
|
||||||
|
|
||||||
|
\section*{Plan 30 minut}
|
||||||
|
|
||||||
|
\begin{tabularx}{\textwidth}{@{}p{1.55cm}p{3.0cm}X@{}}
|
||||||
|
\toprule
|
||||||
|
Czas & Tryb & Wynik \\
|
||||||
|
\midrule
|
||||||
|
0--3 & problem K\&R & nazwać, dlaczego jeden kursor nie zwolni obszaru ze środka \\
|
||||||
|
3--4 & kontekst & FreeRTOS linkuje jedną implementację; tutaj pracujemy tylko z \texttt{heap\_4} \\
|
||||||
|
4--9 & Demo 1 & przewidzieć nagłówek, first-fit i split \\
|
||||||
|
9--16 & Demo 2 & przewidzieć listę adresową i scalenie \texttt{2 -> 1} \\
|
||||||
|
16--27 & Task 3 RUN & osiem checkpointów E01--E08 prawdziwego \texttt{heap\_4.c} \\
|
||||||
|
27--30 & wyjście & odróżnić total free, largest free i minimum-ever \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabularx}
|
||||||
|
|
||||||
|
\vfill
|
||||||
|
\noindent\textbf{Jedna minuta kontekstu:} \texttt{heap\_1} przypomina rosnący
|
||||||
|
kursor bez \texttt{free}; \texttt{heap\_2} nie scala; \texttt{heap\_3} używa
|
||||||
|
alokatora biblioteki C; \texttt{heap\_5} rozszerza mechanikę \texttt{heap\_4}
|
||||||
|
na wiele regionów. Nie są to tematy tej lekcji.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section{Most: od K\&R 5.4 do \texttt{heap\_4}}
|
||||||
|
|
||||||
|
\begin{tabularx}{\textwidth}{@{}p{3.5cm}p{4.8cm}X@{}}
|
||||||
|
\toprule
|
||||||
|
K\&R 5.4 — co mamy & Ograniczenie & Co dodaje \texttt{heap\_4} \\
|
||||||
|
\midrule
|
||||||
|
\texttt{allocbuf[64]}, \texttt{allocp} & istnieje tylko wolny ogon areny & arena podzielona na opisane bloki \\
|
||||||
|
wynik to surowy adres & później nie znamy rozmiaru obszaru & nagłówek \texttt{BlockLink\_t} przed payloadem \\
|
||||||
|
przydział przesuwa kursor & nie wykorzystamy dziury & lista wolnych bloków i first-fit \\
|
||||||
|
reset całej areny & brak zwolnienia jednego obszaru & \texttt{vPortFree()} wskazanego bloku \\
|
||||||
|
brak relacji między dziurami & sąsiednie dziury są rozdzielone & porządek adresowy i coalescing \\
|
||||||
|
sukces albo \texttt{NULL} & brak miary fragmentacji & free, largest, block count, minimum-ever \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabularx}
|
||||||
|
|
||||||
|
\subsection*{Nagłówek realnego bloku}
|
||||||
|
|
||||||
|
\begin{lstlisting}
|
||||||
|
typedef struct A_BLOCK_LINK {
|
||||||
|
struct A_BLOCK_LINK *pxNextFreeBlock;
|
||||||
|
size_t xBlockSize; /* MSB oznacza blok przydzielony */
|
||||||
|
} BlockLink_t;
|
||||||
|
|
||||||
|
[ BlockLink_t | payload zwracany przez pvPortMalloc() ]
|
||||||
|
^ metadata ^ adres widziany przez aplikacje
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\textbf{Granica modelu:} Task 1--2 używają uproszczonego nagłówka
|
||||||
|
\texttt{\{next,size\}}. Bit allocated i pełne zabezpieczenia oglądamy dopiero
|
||||||
|
w upstream \texttt{heap\_4.c}.
|
||||||
|
|
||||||
|
\section{Demo 1 — pierwszy pasujący blok i split (5 minut)}
|
||||||
|
|
||||||
|
Na RP2350/RV32 nagłówek modelu ma 8 B. Dla żądania 13 B:
|
||||||
|
|
||||||
|
\[
|
||||||
|
\texttt{wanted}=\operatorname{align8}(8+13)=24\ \mathrm{B}
|
||||||
|
\]
|
||||||
|
|
||||||
|
\begin{lstlisting}
|
||||||
|
free list przed: [ block 16 B ] -> [ block 256 B ] -> END
|
||||||
|
request po naglowku i align8: 24 B
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\textbf{Predykcja — wpisz przed odsłonięciem:}
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item Pierwszy pasujący blok ma \blank{1.5cm} B.
|
||||||
|
\item Blok przydzielony ma \blank{1.5cm} B, a remainder \blank{1.5cm} B.
|
||||||
|
\item Równość zachowania areny: \blank{2cm} + \blank{2cm} = 256 B.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
|
\noindent\textbf{Sprawdzenie po predykcji:}
|
||||||
|
\texttt{16 B} nie mieści żądania, więc first-fit wybiera \texttt{256 B}; split
|
||||||
|
daje \texttt{24 B + 232 B}. Realny \texttt{heap\_4} dzieli blok tylko wtedy,
|
||||||
|
gdy reszta jest większa od minimalnego rozmiaru bloku.
|
||||||
|
|
||||||
|
\noindent\textbf{Nie uruchamiamy osobnego debugowania Task 1.} Model służy do
|
||||||
|
policzenia geometrii; centralny RUN jest w Task 3.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section{Demo 2 — lista adresowa i coalescing (7 minut)}
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\texttt{[ A:48 FREE ][ B:48 USED ][ C:48 FREE ]}
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
\begin{lstlisting}
|
||||||
|
pamiec: A -------- B -------- C
|
||||||
|
free list: A ------------------> C -> END
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\textbf{Predykcja:} po \texttt{vPortFree(B)} narysuj listę i uzupełnij:
|
||||||
|
|
||||||
|
\begin{tabular}{@{}lccc@{}}
|
||||||
|
\toprule
|
||||||
|
Stan & liczba wolnych bloków & suma wolna & największy blok \\
|
||||||
|
\midrule
|
||||||
|
przed zwolnieniem B & \blank{1.2cm} & \blank{1.2cm} & \blank{1.2cm} \\
|
||||||
|
po zwolnieniu B & \blank{1.2cm} & \blank{1.2cm} & \blank{1.2cm} \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
|
||||||
|
\subsection*{Odsłoń po zapisaniu predykcji}
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item Wstaw B między A i C według adresu.
|
||||||
|
\item \texttt{end(B) == address(C)}: połącz B z C.
|
||||||
|
\item \texttt{end(A) == address(BC)}: połącz A z BC.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\begin{lstlisting}
|
||||||
|
przed: A:48 -> C:48 -> END blocks=2 total=96 largest=48
|
||||||
|
po: A:144 -> END blocks=1 total=144 largest=144
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Porządek po adresie sprawia, że fizycznych sąsiadów można sprawdzić podczas
|
||||||
|
jednego wstawienia. Uproszczony Task 2 scala prawą, a następnie lewą stronę.
|
||||||
|
Właściwy \texttt{prvInsertBlockIntoFreeList()} z FreeRTOS V11.3.0 wykonuje
|
||||||
|
te same dwa testy w kolejności: poprzedni/lewy, następnie kolejny/prawy blok.
|
||||||
|
|
||||||
|
\section{Task 3 — jedyny centralny RUN (11 minut)}
|
||||||
|
|
||||||
|
Obraz i sesja muszą być przygotowane przed lekcją. Nie kompilujemy ani nie
|
||||||
|
flashujemy w czasie tych 30 minut.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=bash]
|
||||||
|
# wykonane przed lekcja:
|
||||||
|
stemctl debug rp2350 freertos heap4 3 --device /dev/bus/usb/BBB/DDD
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\begin{lstlisting}
|
||||||
|
RESET/INIT
|
||||||
|
-> heap4_reset_checkpoint # E01: free=4080
|
||||||
|
-> heap4_alloc_a_checkpoint # E02: free=4032, used=48
|
||||||
|
-> heap4_alloc_b_checkpoint # E03: free=3968, used=64
|
||||||
|
-> heap4_alloc_c_checkpoint # E04: free=3936, used=32
|
||||||
|
-> heap4_free_a_checkpoint # E05: free=3984, blocks=2
|
||||||
|
-> heap4_fragmented_checkpoint # E06: free=4016, blocks=2
|
||||||
|
-> heap4_coalesced_checkpoint # E07: free=4080, blocks=1
|
||||||
|
-> task03_debug_checkpoint # E08: OOM, hook, PASS
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\textbf{E01--E04:} sprawdź, że zużycie obejmuje wyrównany nagłówek całego
|
||||||
|
bloku, a nie tylko payload. \textbf{E05--E06:} porównaj sumę wolną z largest.
|
||||||
|
Przed przejściem z E06 do E07 ustaw tymczasowy breakpoint na
|
||||||
|
\texttt{prvInsertBlockIntoFreeList()} i sprawdź oba testy sąsiedztwa.
|
||||||
|
\textbf{E07--E08:} potwierdź odzyskanie areny, minimum-ever, OOM i hook.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section{Dowód na Pico 2 W / RP2350}
|
||||||
|
|
||||||
|
\textbf{Ważne:} \texttt{heap\_4} jest plikiem wybranym i linkowanym przez
|
||||||
|
projekt. Nie jest sprzętowym heapem ani peryferium RP2350. Pico 2 W dostarcza
|
||||||
|
realną pamięć SRAM, w której obserwujemy ten sam algorytm.
|
||||||
|
|
||||||
|
\subsection*{Minimalny widok GDB}
|
||||||
|
|
||||||
|
\begin{lstlisting}
|
||||||
|
p g_fragmented_stats
|
||||||
|
p g_final_stats
|
||||||
|
p g_a_consumed
|
||||||
|
p g_b_consumed
|
||||||
|
p g_c_consumed
|
||||||
|
p xFreeBytesRemaining
|
||||||
|
p xMinimumEverFreeBytesRemaining
|
||||||
|
p xStart
|
||||||
|
p pxEnd
|
||||||
|
x/160bx ucHeap
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\subsection*{Tabela obserwacji}
|
||||||
|
|
||||||
|
\begin{tabularx}{\textwidth}{@{}p{4.2cm}p{3.0cm}X@{}}
|
||||||
|
\toprule
|
||||||
|
Wielkość & Odczyt & Warunek \\
|
||||||
|
\midrule
|
||||||
|
\texttt{initial\_free} & \blank{2.3cm} & punkt odniesienia \\
|
||||||
|
\texttt{after\_allocations} & \blank{2.3cm} & mniejsze niż initial \\
|
||||||
|
E06: free blocks & \blank{2.3cm} & równe 2 \\
|
||||||
|
E06: total / largest & \blank{2.3cm} & largest mniejsze niż total \\
|
||||||
|
E07: final free & \blank{2.3cm} & równe initial \\
|
||||||
|
E07: free blocks & \blank{2.3cm} & równe 1 \\
|
||||||
|
minimum-ever & \blank{2.3cm} & nie rośnie po free \\
|
||||||
|
OOM / hook / asserts / pass & \blank{2.3cm} & \texttt{1 / 1 / 0 / 1} \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabularx}
|
||||||
|
|
||||||
|
\section*{Wyjście — trzy minuty}
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item Dlaczego jeden \texttt{allocp} nie wystarcza do zwolnienia obszaru
|
||||||
|
spod offsetu 0 przy zachowaniu obszaru spod offsetu 5?\\[.35em]
|
||||||
|
\blank{.96\linewidth}
|
||||||
|
\item Jak \texttt{vPortFree()} znajduje rozmiar payloadu, skoro dostaje tylko
|
||||||
|
jego adres?\\[.35em]
|
||||||
|
\blank{.96\linewidth}
|
||||||
|
\item Dlaczego 96 wolnych bajtów w dwóch blokach nie pozwala przydzielić
|
||||||
|
jednego bloku 80 B?\\[.35em]
|
||||||
|
\blank{.96\linewidth}
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\subsection*{Zaliczenie}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \checkline wymieniam: header, free list, coalescing;
|
||||||
|
\item \checkline przewiduję \texttt{2 bloki -> 1 blok};
|
||||||
|
\item \checkline odróżniam payload, metadata allocatora i stos \texttt{sp};
|
||||||
|
\item \checkline odróżniam current free, largest free i minimum-ever;
|
||||||
|
\item \checkline Task 3 kończy się
|
||||||
|
\texttt{aligned=1 oom=1 hooks=1 asserts=0 pass=1}.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\vfill
|
||||||
|
\noindent\textbf{Poza lekcją:} pełne czytanie upstream \texttt{heap\_4.c},
|
||||||
|
osobne uruchomienie Task 1--2 na AMD64/Hazard3, samodzielny deploy RP2350 oraz
|
||||||
|
porównanie z \texttt{heap\_1}, \texttt{heap\_2}, \texttt{heap\_3} i
|
||||||
|
\texttt{heap\_5}.
|
||||||
|
|
||||||
\end{document}
|
\end{document}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
e69aaa6d61af39003186415f3581d519e7a290bd
|
48eccb5e2497b692892c7b24ffa1ed7a6f8480b8
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# FC01 `heap_4` — stan weryfikacji
|
||||||
|
|
||||||
|
## Stan na 2026-07-20
|
||||||
|
|
||||||
|
Karta ma jeden centralny eksperyment na niezmodyfikowanym
|
||||||
|
`heap_4.c` z FreeRTOS-Kernel V11.3.0. Przebieg jest podzielony na osiem
|
||||||
|
deterministycznych checkpointów E01–E08 i działa na RV32I/Hazard3.
|
||||||
|
|
||||||
|
## Potwierdzone na Hazard3
|
||||||
|
|
||||||
|
| Event | Stan | Dowód |
|
||||||
|
|---|---|---|
|
||||||
|
| E01 | arena po inicjalizacji | `free=largest=4080`, 1 blok |
|
||||||
|
| E02 | `alloc A(24)` | zużyto 48, zostało 4032 |
|
||||||
|
| E03 | `alloc B(40)` | zużyto 64, zostało 3968 |
|
||||||
|
| E04 | `alloc C(16)` | zużyto 32, zostało 3936 |
|
||||||
|
| E05 | `free(A)` | 3984, 2 wolne bloki |
|
||||||
|
| E06 | `free(C)` | 4016, 2 bloki, `free > largest` |
|
||||||
|
| E07 | `free(B)` | `free=largest=4080`, 1 blok |
|
||||||
|
| E08 | kontrolowany OOM | `NULL`, hook=1, asserts=0, PASS=1 |
|
||||||
|
|
||||||
|
Na RV32 `sizeof(BlockLink_t) == 8`, ale `portBYTE_ALIGNMENT == 16`, więc
|
||||||
|
prywatne `xHeapStructSize == 16`. To wyjaśnia zużycie 48/64/32 bajtów.
|
||||||
|
|
||||||
|
## Diagramy i iteracja
|
||||||
|
|
||||||
|
- A1 CONTEXT: pięć kroków CODE — granice odpowiedzialności.
|
||||||
|
- A2 STRUCTURE: jeden krok CODE i pięć kroków RUN — nagłówek, split,
|
||||||
|
lista adresowa oraz scalenie lewego i prawego sąsiada.
|
||||||
|
- A5 FLOW: osiem kroków RUN mapowanych 1:1 na E01–E08.
|
||||||
|
- A6 STATE: pięć reprezentatywnych stanów E01, E04, E06, E07 i E08.
|
||||||
|
- A7 RUNTIME: tożsamość źródła/ELF, pamięć `ucHeap` i końcowy werdykt.
|
||||||
|
- A3, A4 i A8 są jawnie pominięte, ponieważ dublowałyby A2/A5.
|
||||||
|
|
||||||
|
Szczegółowa kolejność komend, obserwacji i kryteriów znajduje się w
|
||||||
|
[`hazard3-iteration-plan.md`](hazard3-iteration-plan.md).
|
||||||
|
|
||||||
|
## Wykonane testy
|
||||||
|
|
||||||
|
- testy hostowe Task 1–3;
|
||||||
|
- budowa RV32I i kontrola symboli ELF;
|
||||||
|
- Hazard3 RTL: zakończenie kodem 0 po 27308 cyklach;
|
||||||
|
- osiem checkpointów obecnych w ELF;
|
||||||
|
- walidacja JSON i odwołań `code_ref`/`snapshot_ref`;
|
||||||
|
- kontrola PlantUML, SVG, HTML, TeX i siedmiostronicowego PDF;
|
||||||
|
- TypeScript/React typecheck generatora.
|
||||||
|
|
||||||
|
## Pozostaje poza zakresem tej iteracji
|
||||||
|
|
||||||
|
- próba na fizycznym RP2350/Pico 2 W;
|
||||||
|
- commit i publikacja — wymagają osobnego polecenia.
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
#ifndef FREERTOS_CONFIG_H
|
||||||
|
#define FREERTOS_CONFIG_H
|
||||||
|
|
||||||
|
#define configCPU_CLOCK_HZ ( 150000000UL )
|
||||||
|
#define configTICK_RATE_HZ ( 1000U )
|
||||||
|
#define configMTIME_BASE_ADDRESS ( 0xC0000100UL )
|
||||||
|
#define configMTIMECMP_BASE_ADDRESS ( 0xC0000108UL )
|
||||||
|
|
||||||
|
#define configUSE_PREEMPTION 1
|
||||||
|
#define configUSE_TIME_SLICING 1
|
||||||
|
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
|
||||||
|
#define configUSE_IDLE_HOOK 0
|
||||||
|
#define configUSE_PASSIVE_IDLE_HOOK 0
|
||||||
|
#define configUSE_TICK_HOOK 0
|
||||||
|
#define configMAX_PRIORITIES 4
|
||||||
|
#define configMINIMAL_STACK_SIZE 128
|
||||||
|
#define configMAX_TASK_NAME_LEN 12
|
||||||
|
#define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_32_BITS
|
||||||
|
#define configIDLE_SHOULD_YIELD 1
|
||||||
|
|
||||||
|
#define configNUMBER_OF_CORES 1
|
||||||
|
#define configTICK_CORE 0
|
||||||
|
#define configUSE_CORE_AFFINITY 0
|
||||||
|
#define configRUN_MULTIPLE_PRIORITIES 0
|
||||||
|
#define configSUPPORT_PICO_SYNC_INTEROP 0
|
||||||
|
|
||||||
|
#define configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||||
|
#define configSUPPORT_STATIC_ALLOCATION 0
|
||||||
|
#define configTOTAL_HEAP_SIZE ( 4096U )
|
||||||
|
#define configAPPLICATION_ALLOCATED_HEAP 1
|
||||||
|
#define configUSE_MALLOC_FAILED_HOOK 1
|
||||||
|
#define configHEAP_CLEAR_MEMORY_ON_FREE 0
|
||||||
|
#define configENABLE_HEAP_PROTECTOR 0
|
||||||
|
|
||||||
|
#define configUSE_TIMERS 0
|
||||||
|
#define configUSE_MUTEXES 0
|
||||||
|
#define configUSE_RECURSIVE_MUTEXES 0
|
||||||
|
#define configUSE_COUNTING_SEMAPHORES 0
|
||||||
|
#define configUSE_TASK_NOTIFICATIONS 1
|
||||||
|
#define configUSE_TRACE_FACILITY 1
|
||||||
|
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
|
||||||
|
#define configGENERATE_RUN_TIME_STATS 0
|
||||||
|
#define configUSE_CO_ROUTINES 0
|
||||||
|
#define configUSE_NEWLIB_REENTRANT 0
|
||||||
|
#define configUSE_POSIX_ERRNO 0
|
||||||
|
#define configCHECK_FOR_STACK_OVERFLOW 0
|
||||||
|
|
||||||
|
#define INCLUDE_vTaskDelay 0
|
||||||
|
#define INCLUDE_vTaskDelayUntil 0
|
||||||
|
#define INCLUDE_vTaskDelete 0
|
||||||
|
#define INCLUDE_vTaskSuspend 0
|
||||||
|
#define INCLUDE_xTaskGetSchedulerState 0
|
||||||
|
|
||||||
|
#define configENABLE_FPU 0
|
||||||
|
#define configENABLE_VPU 0
|
||||||
|
#if !defined(PICO_RP2350)
|
||||||
|
#define configISR_STACK_SIZE_WORDS 128
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __ASSEMBLER__
|
||||||
|
void heap4_lab_assert(const char *file, int line);
|
||||||
|
#define configASSERT(condition) \
|
||||||
|
do { if (!(condition)) heap4_lab_assert(__FILE__, __LINE__); } while (0)
|
||||||
|
#else
|
||||||
|
#define configASSERT(condition)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef FREERTOS_RISC_V_CHIP_SPECIFIC_EXTENSIONS_H
|
||||||
|
#define FREERTOS_RISC_V_CHIP_SPECIFIC_EXTENSIONS_H
|
||||||
|
|
||||||
|
/* Hazard3 adds no architectural registers to the base RV32I context. */
|
||||||
|
#define portasmHAS_MTIME 1
|
||||||
|
#define portasmHAS_SIFIVE_CLINT 0
|
||||||
|
#define portasmADDITIONAL_CONTEXT_SIZE 0
|
||||||
|
|
||||||
|
.macro portasmSAVE_ADDITIONAL_REGISTERS
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro portasmRESTORE_ADDITIONAL_REGISTERS
|
||||||
|
.endm
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#ifndef HEAP4_FREESTANDING_STDLIB_H
|
||||||
|
#define HEAP4_FREESTANDING_STDLIB_H
|
||||||
|
#include <stddef.h>
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#ifndef HEAP4_FREESTANDING_STRING_H
|
||||||
|
#define HEAP4_FREESTANDING_STRING_H
|
||||||
|
#include <stddef.h>
|
||||||
|
void *memcpy(void *destination, const void *source, size_t count);
|
||||||
|
void *memset(void *destination, int value, size_t count);
|
||||||
|
size_t strlen(const char *text);
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#ifndef HEAP4_HOST_FREERTOS_H
|
||||||
|
#define HEAP4_HOST_FREERTOS_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef int BaseType_t;
|
||||||
|
typedef uintptr_t portPOINTER_SIZE_TYPE;
|
||||||
|
|
||||||
|
typedef struct xHeapStats {
|
||||||
|
size_t xAvailableHeapSpaceInBytes;
|
||||||
|
size_t xSizeOfLargestFreeBlockInBytes;
|
||||||
|
size_t xSizeOfSmallestFreeBlockInBytes;
|
||||||
|
size_t xNumberOfFreeBlocks;
|
||||||
|
size_t xMinimumEverFreeBytesRemaining;
|
||||||
|
size_t xNumberOfSuccessfulAllocations;
|
||||||
|
size_t xNumberOfSuccessfulFrees;
|
||||||
|
} HeapStats_t;
|
||||||
|
|
||||||
|
#define configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||||
|
#define configTOTAL_HEAP_SIZE ((size_t)4096U)
|
||||||
|
#define configAPPLICATION_ALLOCATED_HEAP 1
|
||||||
|
#define configUSE_MALLOC_FAILED_HOOK 1
|
||||||
|
#define configHEAP_CLEAR_MEMORY_ON_FREE 0
|
||||||
|
#define configENABLE_HEAP_PROTECTOR 0
|
||||||
|
|
||||||
|
#define portBYTE_ALIGNMENT 8U
|
||||||
|
#define portBYTE_ALIGNMENT_MASK (portBYTE_ALIGNMENT - 1U)
|
||||||
|
#define portMAX_DELAY (~(size_t)0U)
|
||||||
|
|
||||||
|
#define PRIVILEGED_DATA
|
||||||
|
#define PRIVILEGED_FUNCTION
|
||||||
|
#define mtCOVERAGE_TEST_MARKER() do { } while (0)
|
||||||
|
#define traceMALLOC(pointer, size) do { (void)(pointer); (void)(size); } while (0)
|
||||||
|
#define traceFREE(pointer, size) do { (void)(pointer); (void)(size); } while (0)
|
||||||
|
#define taskENTER_CRITICAL() do { } while (0)
|
||||||
|
#define taskEXIT_CRITICAL() do { } while (0)
|
||||||
|
|
||||||
|
void heap4_lab_assert(const char *file, int line);
|
||||||
|
void vApplicationMallocFailedHook(void);
|
||||||
|
void *pvPortMalloc(size_t wanted_size);
|
||||||
|
void vPortFree(void *pointer);
|
||||||
|
size_t xPortGetFreeHeapSize(void);
|
||||||
|
size_t xPortGetMinimumEverFreeHeapSize(void);
|
||||||
|
void vPortGetHeapStats(HeapStats_t *stats);
|
||||||
|
void vPortHeapResetState(void);
|
||||||
|
#define configASSERT(condition) \
|
||||||
|
do { if (!(condition)) heap4_lab_assert(__FILE__, __LINE__); } while (0)
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef HEAP4_HOST_TASK_H
|
||||||
|
#define HEAP4_HOST_TASK_H
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
|
||||||
|
void vTaskSuspendAll(void);
|
||||||
|
BaseType_t xTaskResumeAll(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
#ifndef HEAP4_COMMON_H
|
|
||||||
#define HEAP4_COMMON_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef HOST_PRINTF
|
|
||||||
#include <stdio.h>
|
|
||||||
#define TRACE_PRINTF(...) printf(__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define TRACE_PRINTF(...) ((void)0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define HEAP4_ARRAY_LEN(a) ((int)(sizeof(a) / sizeof((a)[0])))
|
|
||||||
#define HEAP4_ALIGNMENT ((size_t)8)
|
|
||||||
#define HEAP4_ALIGNMENT_MASK (HEAP4_ALIGNMENT - 1U)
|
|
||||||
#define HEAP4_ALLOCATED_BIT ((size_t)1U << (sizeof(size_t) * 8U - 1U))
|
|
||||||
|
|
||||||
typedef struct HeapBlock {
|
|
||||||
struct HeapBlock *next;
|
|
||||||
size_t size;
|
|
||||||
} HeapBlock;
|
|
||||||
|
|
||||||
static inline size_t heap4_align_up(size_t value)
|
|
||||||
{
|
|
||||||
if ((value & HEAP4_ALIGNMENT_MASK) != 0U)
|
|
||||||
value += HEAP4_ALIGNMENT - (value & HEAP4_ALIGNMENT_MASK);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uintptr_t heap4_align_address(uintptr_t address)
|
|
||||||
{
|
|
||||||
uintptr_t mask;
|
|
||||||
|
|
||||||
mask = (uintptr_t)HEAP4_ALIGNMENT_MASK;
|
|
||||||
if ((address & mask) != 0U)
|
|
||||||
address += (uintptr_t)HEAP4_ALIGNMENT - (address & mask);
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline size_t heap4_clear_allocated(size_t size)
|
|
||||||
{
|
|
||||||
return size & ~HEAP4_ALLOCATED_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline size_t heap4_mark_allocated(size_t size)
|
|
||||||
{
|
|
||||||
return size | HEAP4_ALLOCATED_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int heap4_is_allocated(size_t size)
|
|
||||||
{
|
|
||||||
return (size & HEAP4_ALLOCATED_BIT) != 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int heap4_blocks_touch(HeapBlock *left, HeapBlock *right)
|
|
||||||
{
|
|
||||||
return (uint8_t *)left + heap4_clear_allocated(left->size) == (uint8_t *)right;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef HEAP4_MODEL_H
|
||||||
|
#define HEAP4_MODEL_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define MODEL_ALIGNMENT ((size_t)8U)
|
||||||
|
|
||||||
|
typedef struct ModelBlock ModelBlock;
|
||||||
|
|
||||||
|
struct ModelBlock {
|
||||||
|
ModelBlock *next;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline size_t model_align_up(size_t value)
|
||||||
|
{
|
||||||
|
return (value + MODEL_ALIGNMENT - 1U) & ~(MODEL_ALIGNMENT - 1U);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HOST_PRINTF)
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#include "heap4_model.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ARENA_BYTES = 256,
|
||||||
|
REQUEST_BYTES = 13,
|
||||||
|
WANTED_BYTES = (sizeof(ModelBlock) + REQUEST_BYTES + 7U) & ~7U
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ModelBlock allocated;
|
||||||
|
uint8_t payload[WANTED_BYTES - sizeof(ModelBlock)];
|
||||||
|
ModelBlock remainder;
|
||||||
|
uint8_t free_payload[ARENA_BYTES - WANTED_BYTES - sizeof(ModelBlock)];
|
||||||
|
} SplitArena;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
uint64_t force_alignment;
|
||||||
|
SplitArena blocks;
|
||||||
|
} AlignedSplitArena;
|
||||||
|
|
||||||
|
AlignedSplitArena g_split_arena;
|
||||||
|
ModelBlock *g_free_head;
|
||||||
|
volatile size_t g_header_bytes;
|
||||||
|
volatile size_t g_wanted_bytes;
|
||||||
|
volatile size_t g_remainder_bytes;
|
||||||
|
volatile size_t g_payload_offset;
|
||||||
|
volatile int g_split_addresses_aligned;
|
||||||
|
volatile int g_task01_pass;
|
||||||
|
|
||||||
|
__attribute__((noinline)) void task01_debug_checkpoint(void)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
SplitArena *arena = &g_split_arena.blocks;
|
||||||
|
uintptr_t base = (uintptr_t)&arena->allocated;
|
||||||
|
uintptr_t remainder = (uintptr_t)&arena->remainder;
|
||||||
|
|
||||||
|
arena->allocated.next = NULL;
|
||||||
|
arena->allocated.size = WANTED_BYTES;
|
||||||
|
arena->remainder.next = NULL;
|
||||||
|
arena->remainder.size = ARENA_BYTES - WANTED_BYTES;
|
||||||
|
arena->payload[0] = (uint8_t)'A';
|
||||||
|
g_free_head = &arena->remainder;
|
||||||
|
|
||||||
|
g_header_bytes = sizeof(ModelBlock);
|
||||||
|
g_wanted_bytes = WANTED_BYTES;
|
||||||
|
g_remainder_bytes = arena->remainder.size;
|
||||||
|
g_payload_offset = (size_t)((uintptr_t)&arena->payload[0] - base);
|
||||||
|
g_split_addresses_aligned =
|
||||||
|
(base % MODEL_ALIGNMENT) == 0U &&
|
||||||
|
(remainder % MODEL_ALIGNMENT) == 0U;
|
||||||
|
g_task01_pass =
|
||||||
|
g_wanted_bytes == model_align_up(sizeof(ModelBlock) + REQUEST_BYTES) &&
|
||||||
|
(size_t)(remainder - base) == g_wanted_bytes &&
|
||||||
|
g_remainder_bytes + g_wanted_bytes == ARENA_BYTES &&
|
||||||
|
g_payload_offset == sizeof(ModelBlock) &&
|
||||||
|
g_split_addresses_aligned;
|
||||||
|
|
||||||
|
task01_debug_checkpoint();
|
||||||
|
|
||||||
|
#if defined(HOST_PRINTF)
|
||||||
|
printf("header=%zu wanted=%zu remainder=%zu payload=%zu aligned=%d pass=%d\n",
|
||||||
|
g_header_bytes, g_wanted_bytes, g_remainder_bytes,
|
||||||
|
g_payload_offset, g_split_addresses_aligned, g_task01_pass);
|
||||||
|
#endif
|
||||||
|
return g_task01_pass ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#include "heap4_common.h"
|
|
||||||
|
|
||||||
volatile size_t g_header_size;
|
|
||||||
volatile size_t g_aligned_header_size;
|
|
||||||
volatile size_t g_allocated_bit_set;
|
|
||||||
volatile size_t g_user_bytes;
|
|
||||||
volatile size_t g_total_block_bytes;
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
HeapBlock block;
|
|
||||||
size_t requested;
|
|
||||||
|
|
||||||
requested = 13U;
|
|
||||||
block.next = 0;
|
|
||||||
block.size = heap4_mark_allocated(heap4_align_up(requested + sizeof(HeapBlock)));
|
|
||||||
|
|
||||||
g_header_size = sizeof(HeapBlock);
|
|
||||||
g_aligned_header_size = heap4_align_up(sizeof(HeapBlock));
|
|
||||||
g_allocated_bit_set = heap4_is_allocated(block.size);
|
|
||||||
g_total_block_bytes = heap4_clear_allocated(block.size);
|
|
||||||
g_user_bytes = g_total_block_bytes - sizeof(HeapBlock);
|
|
||||||
|
|
||||||
TRACE_PRINTF("header=%u aligned=%u allocated=%u total=%u user=%u\n",
|
|
||||||
(unsigned)g_header_size, (unsigned)g_aligned_header_size,
|
|
||||||
(unsigned)g_allocated_bit_set, (unsigned)g_total_block_bytes,
|
|
||||||
(unsigned)g_user_bytes);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
#include "heap4_model.h"
|
||||||
|
|
||||||
|
enum { BLOCK_BYTES = 48 };
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ModelBlock first;
|
||||||
|
uint8_t first_payload[BLOCK_BYTES - sizeof(ModelBlock)];
|
||||||
|
ModelBlock middle;
|
||||||
|
uint8_t middle_payload[BLOCK_BYTES - sizeof(ModelBlock)];
|
||||||
|
ModelBlock last;
|
||||||
|
uint8_t last_payload[BLOCK_BYTES - sizeof(ModelBlock)];
|
||||||
|
} CoalesceArena;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
uint64_t force_alignment;
|
||||||
|
CoalesceArena blocks;
|
||||||
|
} AlignedCoalesceArena;
|
||||||
|
|
||||||
|
AlignedCoalesceArena g_coalesce_arena;
|
||||||
|
ModelBlock *g_free_head;
|
||||||
|
volatile size_t g_before_nodes;
|
||||||
|
volatile size_t g_before_total;
|
||||||
|
volatile size_t g_before_largest;
|
||||||
|
volatile size_t g_after_nodes;
|
||||||
|
volatile size_t g_after_total;
|
||||||
|
volatile size_t g_after_largest;
|
||||||
|
volatile int g_task02_pass;
|
||||||
|
|
||||||
|
static void insert_and_coalesce(ModelBlock *block)
|
||||||
|
{
|
||||||
|
ModelBlock *previous = NULL;
|
||||||
|
ModelBlock *current = g_free_head;
|
||||||
|
|
||||||
|
while (current != NULL && (uintptr_t)current < (uintptr_t)block)
|
||||||
|
{
|
||||||
|
previous = current;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
block->next = current;
|
||||||
|
if (current != NULL &&
|
||||||
|
(uint8_t *)block + block->size == (uint8_t *)current)
|
||||||
|
{
|
||||||
|
block->size += current->size;
|
||||||
|
block->next = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previous != NULL &&
|
||||||
|
(uint8_t *)previous + previous->size == (uint8_t *)block)
|
||||||
|
{
|
||||||
|
previous->size += block->size;
|
||||||
|
previous->next = block->next;
|
||||||
|
}
|
||||||
|
else if (previous != NULL)
|
||||||
|
{
|
||||||
|
previous->next = block;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_free_head = block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void measure(size_t *nodes, size_t *total, size_t *largest)
|
||||||
|
{
|
||||||
|
ModelBlock *block;
|
||||||
|
|
||||||
|
*nodes = 0U;
|
||||||
|
*total = 0U;
|
||||||
|
*largest = 0U;
|
||||||
|
for (block = g_free_head; block != NULL; block = block->next)
|
||||||
|
{
|
||||||
|
(*nodes)++;
|
||||||
|
*total += block->size;
|
||||||
|
if (block->size > *largest)
|
||||||
|
{
|
||||||
|
*largest = block->size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline)) void task02_fragmented_checkpoint(void)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline)) void task02_debug_checkpoint(void)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
CoalesceArena *arena = &g_coalesce_arena.blocks;
|
||||||
|
size_t nodes;
|
||||||
|
size_t total;
|
||||||
|
size_t largest;
|
||||||
|
|
||||||
|
arena->first.size = BLOCK_BYTES;
|
||||||
|
arena->middle.size = BLOCK_BYTES;
|
||||||
|
arena->last.size = BLOCK_BYTES;
|
||||||
|
g_free_head = NULL;
|
||||||
|
|
||||||
|
insert_and_coalesce(&arena->first);
|
||||||
|
insert_and_coalesce(&arena->last);
|
||||||
|
measure(&nodes, &total, &largest);
|
||||||
|
g_before_nodes = nodes;
|
||||||
|
g_before_total = total;
|
||||||
|
g_before_largest = largest;
|
||||||
|
task02_fragmented_checkpoint();
|
||||||
|
|
||||||
|
insert_and_coalesce(&arena->middle);
|
||||||
|
measure(&nodes, &total, &largest);
|
||||||
|
g_after_nodes = nodes;
|
||||||
|
g_after_total = total;
|
||||||
|
g_after_largest = largest;
|
||||||
|
g_task02_pass =
|
||||||
|
g_before_nodes == 2U && g_before_total == 96U &&
|
||||||
|
g_before_largest == 48U && g_after_nodes == 1U &&
|
||||||
|
g_after_total == 144U && g_after_largest == 144U &&
|
||||||
|
g_free_head == &arena->first && g_free_head->next == NULL;
|
||||||
|
|
||||||
|
task02_debug_checkpoint();
|
||||||
|
|
||||||
|
#if defined(HOST_PRINTF)
|
||||||
|
printf("before_nodes=%zu before_total=%zu before_largest=%zu "
|
||||||
|
"after_nodes=%zu after_total=%zu after_largest=%zu pass=%d\n",
|
||||||
|
g_before_nodes, g_before_total, g_before_largest,
|
||||||
|
g_after_nodes, g_after_total, g_after_largest, g_task02_pass);
|
||||||
|
#endif
|
||||||
|
return g_task02_pass ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
#include "heap4_common.h"
|
|
||||||
|
|
||||||
volatile size_t g_free_total;
|
|
||||||
volatile size_t g_largest_free;
|
|
||||||
volatile int g_node_count;
|
|
||||||
|
|
||||||
static size_t list_total(HeapBlock *start)
|
|
||||||
{
|
|
||||||
HeapBlock *block;
|
|
||||||
size_t total;
|
|
||||||
|
|
||||||
total = 0U;
|
|
||||||
for (block = start->next; block != 0; block = block->next)
|
|
||||||
total += block->size;
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t list_largest(HeapBlock *start)
|
|
||||||
{
|
|
||||||
HeapBlock *block;
|
|
||||||
size_t largest;
|
|
||||||
|
|
||||||
largest = 0U;
|
|
||||||
for (block = start->next; block != 0; block = block->next)
|
|
||||||
if (block->size > largest)
|
|
||||||
largest = block->size;
|
|
||||||
return largest;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int list_count(HeapBlock *start)
|
|
||||||
{
|
|
||||||
HeapBlock *block;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
count = 0;
|
|
||||||
for (block = start->next; block != 0; block = block->next)
|
|
||||||
count++;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
HeapBlock start;
|
|
||||||
HeapBlock a;
|
|
||||||
HeapBlock b;
|
|
||||||
HeapBlock c;
|
|
||||||
|
|
||||||
start.next = &a;
|
|
||||||
start.size = 0U;
|
|
||||||
a.next = &b;
|
|
||||||
a.size = 32U;
|
|
||||||
b.next = &c;
|
|
||||||
b.size = 80U;
|
|
||||||
c.next = 0;
|
|
||||||
c.size = 24U;
|
|
||||||
|
|
||||||
g_free_total = list_total(&start);
|
|
||||||
g_largest_free = list_largest(&start);
|
|
||||||
g_node_count = list_count(&start);
|
|
||||||
|
|
||||||
TRACE_PRINTF("free=%u largest=%u count=%d\n",
|
|
||||||
(unsigned)g_free_total, (unsigned)g_largest_free, g_node_count);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
#if defined(HOST_PRINTF)
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint8_t ucHeap[configTOTAL_HEAP_SIZE] __attribute__((aligned(portBYTE_ALIGNMENT)));
|
||||||
|
|
||||||
|
HeapStats_t g_initial_stats;
|
||||||
|
HeapStats_t g_after_a_stats;
|
||||||
|
HeapStats_t g_after_b_stats;
|
||||||
|
HeapStats_t g_after_allocations_stats;
|
||||||
|
HeapStats_t g_after_free_a_stats;
|
||||||
|
HeapStats_t g_fragmented_stats;
|
||||||
|
HeapStats_t g_final_stats;
|
||||||
|
volatile size_t g_initial_free;
|
||||||
|
volatile size_t g_after_a_free;
|
||||||
|
volatile size_t g_after_b_free;
|
||||||
|
volatile size_t g_after_allocations;
|
||||||
|
volatile size_t g_after_free_a;
|
||||||
|
volatile size_t g_fragmented_free;
|
||||||
|
volatile size_t g_final_free;
|
||||||
|
volatile size_t g_minimum_ever_free;
|
||||||
|
volatile size_t g_a_consumed;
|
||||||
|
volatile size_t g_b_consumed;
|
||||||
|
volatile size_t g_c_consumed;
|
||||||
|
void * volatile g_probe;
|
||||||
|
void * volatile g_a;
|
||||||
|
void * volatile g_b;
|
||||||
|
void * volatile g_c;
|
||||||
|
void * volatile g_too_large;
|
||||||
|
volatile int g_a_aligned;
|
||||||
|
volatile int g_b_aligned;
|
||||||
|
volatile int g_c_aligned;
|
||||||
|
volatile int g_allocations_aligned;
|
||||||
|
volatile int g_oom_is_null;
|
||||||
|
volatile int g_malloc_failed_hooks;
|
||||||
|
volatile int g_assert_failures;
|
||||||
|
volatile int g_rv32_layout_expected;
|
||||||
|
volatile int g_task03_pass;
|
||||||
|
|
||||||
|
void vApplicationMallocFailedHook(void)
|
||||||
|
{
|
||||||
|
g_malloc_failed_hooks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void heap4_lab_assert(const char *file, int line)
|
||||||
|
{
|
||||||
|
(void)file;
|
||||||
|
(void)line;
|
||||||
|
g_assert_failures++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HEAP4_HOST_ADAPTER)
|
||||||
|
void vTaskSuspendAll(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseType_t xTaskResumeAll(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__attribute__((noinline)) void heap4_reset_checkpoint(void)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline)) void heap4_alloc_a_checkpoint(void)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline)) void heap4_alloc_b_checkpoint(void)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline)) void heap4_alloc_c_checkpoint(void)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline)) void heap4_free_a_checkpoint(void)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline)) void heap4_fragmented_checkpoint(void)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline)) void heap4_coalesced_checkpoint(void)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline)) void task03_debug_checkpoint(void)
|
||||||
|
{
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
__asm__ volatile("" ::: "memory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
vPortHeapResetState();
|
||||||
|
g_probe = pvPortMalloc(1U);
|
||||||
|
if (g_probe != NULL)
|
||||||
|
{
|
||||||
|
vPortFree(g_probe);
|
||||||
|
}
|
||||||
|
g_initial_free = xPortGetFreeHeapSize();
|
||||||
|
vPortGetHeapStats(&g_initial_stats);
|
||||||
|
heap4_reset_checkpoint();
|
||||||
|
|
||||||
|
g_a = pvPortMalloc(24U);
|
||||||
|
g_after_a_free = xPortGetFreeHeapSize();
|
||||||
|
g_a_consumed = g_initial_free - g_after_a_free;
|
||||||
|
g_a_aligned =
|
||||||
|
g_a != NULL && ((uintptr_t)g_a % portBYTE_ALIGNMENT) == 0U;
|
||||||
|
vPortGetHeapStats(&g_after_a_stats);
|
||||||
|
heap4_alloc_a_checkpoint();
|
||||||
|
|
||||||
|
g_b = pvPortMalloc(40U);
|
||||||
|
g_after_b_free = xPortGetFreeHeapSize();
|
||||||
|
g_b_consumed = g_after_a_free - g_after_b_free;
|
||||||
|
g_b_aligned =
|
||||||
|
g_b != NULL && ((uintptr_t)g_b % portBYTE_ALIGNMENT) == 0U;
|
||||||
|
vPortGetHeapStats(&g_after_b_stats);
|
||||||
|
heap4_alloc_b_checkpoint();
|
||||||
|
|
||||||
|
g_c = pvPortMalloc(16U);
|
||||||
|
g_after_allocations = xPortGetFreeHeapSize();
|
||||||
|
g_c_consumed = g_after_b_free - g_after_allocations;
|
||||||
|
g_c_aligned =
|
||||||
|
g_c != NULL && ((uintptr_t)g_c % portBYTE_ALIGNMENT) == 0U;
|
||||||
|
g_allocations_aligned =
|
||||||
|
g_a_aligned && g_b_aligned && g_c_aligned;
|
||||||
|
vPortGetHeapStats(&g_after_allocations_stats);
|
||||||
|
heap4_alloc_c_checkpoint();
|
||||||
|
|
||||||
|
vPortFree(g_a);
|
||||||
|
g_after_free_a = xPortGetFreeHeapSize();
|
||||||
|
vPortGetHeapStats(&g_after_free_a_stats);
|
||||||
|
heap4_free_a_checkpoint();
|
||||||
|
|
||||||
|
vPortFree(g_c);
|
||||||
|
g_fragmented_free = xPortGetFreeHeapSize();
|
||||||
|
vPortGetHeapStats(&g_fragmented_stats);
|
||||||
|
heap4_fragmented_checkpoint();
|
||||||
|
|
||||||
|
vPortFree(g_b);
|
||||||
|
g_final_free = xPortGetFreeHeapSize();
|
||||||
|
g_minimum_ever_free = xPortGetMinimumEverFreeHeapSize();
|
||||||
|
vPortGetHeapStats(&g_final_stats);
|
||||||
|
heap4_coalesced_checkpoint();
|
||||||
|
|
||||||
|
g_too_large = pvPortMalloc(configTOTAL_HEAP_SIZE * 2U);
|
||||||
|
g_oom_is_null = g_too_large == NULL;
|
||||||
|
#if defined(__riscv) && (__riscv_xlen == 32)
|
||||||
|
g_rv32_layout_expected =
|
||||||
|
portBYTE_ALIGNMENT == 16U &&
|
||||||
|
g_initial_free == 4080U &&
|
||||||
|
g_after_a_free == 4032U && g_a_consumed == 48U &&
|
||||||
|
g_after_b_free == 3968U && g_b_consumed == 64U &&
|
||||||
|
g_after_allocations == 3936U && g_c_consumed == 32U &&
|
||||||
|
g_after_free_a == 3984U &&
|
||||||
|
g_fragmented_free == 4016U &&
|
||||||
|
g_final_free == 4080U;
|
||||||
|
#else
|
||||||
|
g_rv32_layout_expected = 1;
|
||||||
|
#endif
|
||||||
|
g_task03_pass =
|
||||||
|
g_probe != NULL && g_allocations_aligned &&
|
||||||
|
g_rv32_layout_expected &&
|
||||||
|
g_initial_stats.xNumberOfFreeBlocks == 1U &&
|
||||||
|
g_after_a_free < g_initial_free &&
|
||||||
|
g_after_b_free < g_after_a_free &&
|
||||||
|
g_initial_free > g_after_allocations &&
|
||||||
|
g_after_free_a > g_after_allocations &&
|
||||||
|
g_fragmented_free > g_after_free_a &&
|
||||||
|
g_final_free == g_initial_free &&
|
||||||
|
g_minimum_ever_free <= g_after_allocations &&
|
||||||
|
g_after_free_a_stats.xNumberOfFreeBlocks == 2U &&
|
||||||
|
g_fragmented_stats.xNumberOfFreeBlocks == 2U &&
|
||||||
|
g_fragmented_stats.xAvailableHeapSpaceInBytes >
|
||||||
|
g_fragmented_stats.xSizeOfLargestFreeBlockInBytes &&
|
||||||
|
g_final_stats.xNumberOfFreeBlocks == 1U &&
|
||||||
|
g_final_stats.xSizeOfLargestFreeBlockInBytes == g_initial_free &&
|
||||||
|
g_oom_is_null && g_malloc_failed_hooks == 1 &&
|
||||||
|
g_assert_failures == 0;
|
||||||
|
|
||||||
|
task03_debug_checkpoint();
|
||||||
|
|
||||||
|
#if defined(HOST_PRINTF)
|
||||||
|
printf("initial=%zu after_a=%zu after_b=%zu after_c=%zu "
|
||||||
|
"free_a=%zu fragmented=%zu final=%zu min=%zu "
|
||||||
|
"consumed=%zu/%zu/%zu aligned=%d oom=%d hooks=%d "
|
||||||
|
"asserts=%d pass=%d\n",
|
||||||
|
g_initial_free, g_after_a_free, g_after_b_free,
|
||||||
|
g_after_allocations, g_after_free_a, g_fragmented_free,
|
||||||
|
g_final_free, g_minimum_ever_free,
|
||||||
|
g_a_consumed, g_b_consumed, g_c_consumed,
|
||||||
|
g_allocations_aligned, g_oom_is_null,
|
||||||
|
g_malloc_failed_hooks, g_assert_failures, g_task03_pass);
|
||||||
|
#endif
|
||||||
|
return g_task03_pass ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
#include "heap4_common.h"
|
|
||||||
|
|
||||||
#define HEAP_BYTES 160U
|
|
||||||
|
|
||||||
static uint8_t heap_area[HEAP_BYTES + HEAP4_ALIGNMENT];
|
|
||||||
static HeapBlock start;
|
|
||||||
static HeapBlock *end_marker;
|
|
||||||
|
|
||||||
volatile size_t g_initial_free_size;
|
|
||||||
volatile uintptr_t g_aligned_offset;
|
|
||||||
volatile int g_end_is_last;
|
|
||||||
|
|
||||||
static void heap_init(void)
|
|
||||||
{
|
|
||||||
uintptr_t raw;
|
|
||||||
uintptr_t aligned;
|
|
||||||
size_t available;
|
|
||||||
HeapBlock *first;
|
|
||||||
|
|
||||||
raw = (uintptr_t)&heap_area[0];
|
|
||||||
aligned = heap4_align_address(raw);
|
|
||||||
available = HEAP_BYTES - (size_t)(aligned - raw);
|
|
||||||
available &= ~HEAP4_ALIGNMENT_MASK;
|
|
||||||
|
|
||||||
first = (HeapBlock *)aligned;
|
|
||||||
end_marker = (HeapBlock *)(aligned + available - sizeof(HeapBlock));
|
|
||||||
end_marker->next = 0;
|
|
||||||
end_marker->size = 0U;
|
|
||||||
|
|
||||||
first->next = end_marker;
|
|
||||||
first->size = (uint8_t *)end_marker - (uint8_t *)first;
|
|
||||||
|
|
||||||
start.next = first;
|
|
||||||
start.size = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
heap_init();
|
|
||||||
|
|
||||||
g_aligned_offset = (uintptr_t)start.next - (uintptr_t)&heap_area[0];
|
|
||||||
g_initial_free_size = start.next->size;
|
|
||||||
g_end_is_last = (start.next->next == end_marker) && (end_marker->next == 0);
|
|
||||||
|
|
||||||
TRACE_PRINTF("offset=%u free=%u end_last=%d\n",
|
|
||||||
(unsigned)g_aligned_offset, (unsigned)g_initial_free_size,
|
|
||||||
g_end_is_last);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#include "heap4_common.h"
|
|
||||||
|
|
||||||
volatile size_t g_request_1;
|
|
||||||
volatile size_t g_request_9;
|
|
||||||
volatile size_t g_request_17;
|
|
||||||
volatile uintptr_t g_address_delta;
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
uint8_t bytes[32];
|
|
||||||
uintptr_t raw;
|
|
||||||
uintptr_t aligned;
|
|
||||||
|
|
||||||
g_request_1 = heap4_align_up(1U + sizeof(HeapBlock));
|
|
||||||
g_request_9 = heap4_align_up(9U + sizeof(HeapBlock));
|
|
||||||
g_request_17 = heap4_align_up(17U + sizeof(HeapBlock));
|
|
||||||
|
|
||||||
raw = (uintptr_t)&bytes[1];
|
|
||||||
aligned = heap4_align_address(raw);
|
|
||||||
g_address_delta = aligned - raw;
|
|
||||||
|
|
||||||
TRACE_PRINTF("r1=%u r9=%u r17=%u delta=%u\n",
|
|
||||||
(unsigned)g_request_1, (unsigned)g_request_9,
|
|
||||||
(unsigned)g_request_17, (unsigned)g_address_delta);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#include "heap4_common.h"
|
|
||||||
|
|
||||||
#define HEAP_BYTES 128U
|
|
||||||
|
|
||||||
static uint8_t heap_area[HEAP_BYTES + HEAP4_ALIGNMENT];
|
|
||||||
static HeapBlock start;
|
|
||||||
static HeapBlock *end_marker;
|
|
||||||
|
|
||||||
volatile size_t g_allocated_size;
|
|
||||||
volatile size_t g_remaining_free;
|
|
||||||
volatile int g_allocation_ok;
|
|
||||||
|
|
||||||
static void heap_init_one_block(void)
|
|
||||||
{
|
|
||||||
uintptr_t raw;
|
|
||||||
uintptr_t aligned;
|
|
||||||
HeapBlock *first;
|
|
||||||
|
|
||||||
raw = (uintptr_t)&heap_area[0];
|
|
||||||
aligned = heap4_align_address(raw);
|
|
||||||
first = (HeapBlock *)aligned;
|
|
||||||
end_marker = (HeapBlock *)(aligned + 120U);
|
|
||||||
|
|
||||||
end_marker->next = 0;
|
|
||||||
end_marker->size = 0U;
|
|
||||||
first->next = end_marker;
|
|
||||||
first->size = (uint8_t *)end_marker - (uint8_t *)first;
|
|
||||||
start.next = first;
|
|
||||||
start.size = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *heap_malloc_whole_block(size_t wanted)
|
|
||||||
{
|
|
||||||
HeapBlock *previous;
|
|
||||||
HeapBlock *block;
|
|
||||||
size_t total;
|
|
||||||
|
|
||||||
total = heap4_align_up(wanted + sizeof(HeapBlock));
|
|
||||||
previous = &start;
|
|
||||||
block = start.next;
|
|
||||||
|
|
||||||
while (block != end_marker) {
|
|
||||||
if (block->size >= total) {
|
|
||||||
previous->next = block->next;
|
|
||||||
block->next = 0;
|
|
||||||
block->size = heap4_mark_allocated(block->size);
|
|
||||||
return (uint8_t *)block + sizeof(HeapBlock);
|
|
||||||
}
|
|
||||||
previous = block;
|
|
||||||
block = block->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
void *p;
|
|
||||||
HeapBlock *allocated;
|
|
||||||
|
|
||||||
heap_init_one_block();
|
|
||||||
p = heap_malloc_whole_block(24U);
|
|
||||||
allocated = (HeapBlock *)((uint8_t *)p - sizeof(HeapBlock));
|
|
||||||
|
|
||||||
g_allocation_ok = p != 0;
|
|
||||||
g_allocated_size = heap4_clear_allocated(allocated->size);
|
|
||||||
g_remaining_free = start.next->size;
|
|
||||||
|
|
||||||
TRACE_PRINTF("ok=%d allocated=%u remaining=%u\n",
|
|
||||||
g_allocation_ok, (unsigned)g_allocated_size,
|
|
||||||
(unsigned)g_remaining_free);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#include "heap4_common.h"
|
|
||||||
|
|
||||||
#define HEAP_BYTES 192U
|
|
||||||
#define MIN_SPLIT_SIZE ((size_t)(sizeof(HeapBlock) * 2U))
|
|
||||||
|
|
||||||
static uint8_t heap_area[HEAP_BYTES + HEAP4_ALIGNMENT];
|
|
||||||
static HeapBlock start;
|
|
||||||
static HeapBlock *end_marker;
|
|
||||||
|
|
||||||
volatile size_t g_allocated_size;
|
|
||||||
volatile size_t g_split_free_size;
|
|
||||||
volatile int g_free_nodes;
|
|
||||||
|
|
||||||
static void heap_init_one_block(void)
|
|
||||||
{
|
|
||||||
uintptr_t aligned;
|
|
||||||
HeapBlock *first;
|
|
||||||
|
|
||||||
aligned = heap4_align_address((uintptr_t)&heap_area[0]);
|
|
||||||
first = (HeapBlock *)aligned;
|
|
||||||
end_marker = (HeapBlock *)(aligned + 176U);
|
|
||||||
end_marker->next = 0;
|
|
||||||
end_marker->size = 0U;
|
|
||||||
first->next = end_marker;
|
|
||||||
first->size = (uint8_t *)end_marker - (uint8_t *)first;
|
|
||||||
start.next = first;
|
|
||||||
start.size = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *heap_malloc_split(size_t wanted)
|
|
||||||
{
|
|
||||||
HeapBlock *previous;
|
|
||||||
HeapBlock *block;
|
|
||||||
HeapBlock *remainder;
|
|
||||||
size_t total;
|
|
||||||
size_t original;
|
|
||||||
|
|
||||||
total = heap4_align_up(wanted + sizeof(HeapBlock));
|
|
||||||
previous = &start;
|
|
||||||
block = start.next;
|
|
||||||
|
|
||||||
while (block != end_marker) {
|
|
||||||
if (block->size >= total) {
|
|
||||||
original = block->size;
|
|
||||||
if (original - total > MIN_SPLIT_SIZE) {
|
|
||||||
remainder = (HeapBlock *)((uint8_t *)block + total);
|
|
||||||
remainder->size = original - total;
|
|
||||||
remainder->next = block->next;
|
|
||||||
previous->next = remainder;
|
|
||||||
block->size = total;
|
|
||||||
} else {
|
|
||||||
previous->next = block->next;
|
|
||||||
}
|
|
||||||
block->next = 0;
|
|
||||||
block->size = heap4_mark_allocated(block->size);
|
|
||||||
return (uint8_t *)block + sizeof(HeapBlock);
|
|
||||||
}
|
|
||||||
previous = block;
|
|
||||||
block = block->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int count_free_nodes(void)
|
|
||||||
{
|
|
||||||
HeapBlock *block;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
count = 0;
|
|
||||||
for (block = start.next; block != end_marker; block = block->next)
|
|
||||||
count++;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
void *p;
|
|
||||||
HeapBlock *allocated;
|
|
||||||
|
|
||||||
heap_init_one_block();
|
|
||||||
p = heap_malloc_split(32U);
|
|
||||||
allocated = (HeapBlock *)((uint8_t *)p - sizeof(HeapBlock));
|
|
||||||
|
|
||||||
g_allocated_size = heap4_clear_allocated(allocated->size);
|
|
||||||
g_split_free_size = start.next->size;
|
|
||||||
g_free_nodes = count_free_nodes();
|
|
||||||
|
|
||||||
TRACE_PRINTF("allocated=%u split=%u nodes=%d\n",
|
|
||||||
(unsigned)g_allocated_size, (unsigned)g_split_free_size,
|
|
||||||
g_free_nodes);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
#include "heap4_common.h"
|
|
||||||
|
|
||||||
static uint8_t heap_area[192];
|
|
||||||
static HeapBlock start;
|
|
||||||
static HeapBlock *end_marker;
|
|
||||||
|
|
||||||
volatile int g_order_ok;
|
|
||||||
volatile int g_free_count;
|
|
||||||
volatile size_t g_total_free;
|
|
||||||
|
|
||||||
static void insert_block(HeapBlock *block)
|
|
||||||
{
|
|
||||||
HeapBlock *iterator;
|
|
||||||
|
|
||||||
for (iterator = &start; iterator->next < block; iterator = iterator->next)
|
|
||||||
;
|
|
||||||
|
|
||||||
block->next = iterator->next;
|
|
||||||
iterator->next = block;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void heap_free_model(void *p)
|
|
||||||
{
|
|
||||||
HeapBlock *block;
|
|
||||||
|
|
||||||
if (p == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
block = (HeapBlock *)((uint8_t *)p - sizeof(HeapBlock));
|
|
||||||
block->size = heap4_clear_allocated(block->size);
|
|
||||||
insert_block(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int count_free(void)
|
|
||||||
{
|
|
||||||
HeapBlock *block;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
count = 0;
|
|
||||||
for (block = start.next; block != end_marker; block = block->next)
|
|
||||||
count++;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t total_free(void)
|
|
||||||
{
|
|
||||||
HeapBlock *block;
|
|
||||||
size_t total;
|
|
||||||
|
|
||||||
total = 0U;
|
|
||||||
for (block = start.next; block != end_marker; block = block->next)
|
|
||||||
total += block->size;
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
HeapBlock *a;
|
|
||||||
HeapBlock *b;
|
|
||||||
HeapBlock *c;
|
|
||||||
void *payload;
|
|
||||||
|
|
||||||
a = (HeapBlock *)&heap_area[0];
|
|
||||||
b = (HeapBlock *)&heap_area[64];
|
|
||||||
c = (HeapBlock *)&heap_area[128];
|
|
||||||
end_marker = (HeapBlock *)&heap_area[176];
|
|
||||||
|
|
||||||
start.next = a;
|
|
||||||
a->next = c;
|
|
||||||
c->next = end_marker;
|
|
||||||
end_marker->next = 0;
|
|
||||||
|
|
||||||
a->size = 40U;
|
|
||||||
b->size = heap4_mark_allocated(32U);
|
|
||||||
c->size = 48U;
|
|
||||||
end_marker->size = 0U;
|
|
||||||
|
|
||||||
payload = (uint8_t *)b + sizeof(HeapBlock);
|
|
||||||
heap_free_model(payload);
|
|
||||||
|
|
||||||
g_order_ok = start.next == a && a->next == b && b->next == c && c->next == end_marker;
|
|
||||||
g_free_count = count_free();
|
|
||||||
g_total_free = total_free();
|
|
||||||
|
|
||||||
TRACE_PRINTF("order=%d count=%d total=%u\n",
|
|
||||||
g_order_ok, g_free_count, (unsigned)g_total_free);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
#include "heap4_common.h"
|
|
||||||
|
|
||||||
static uint8_t heap_area[192];
|
|
||||||
static HeapBlock start;
|
|
||||||
static HeapBlock *end_marker;
|
|
||||||
|
|
||||||
volatile int g_free_count;
|
|
||||||
volatile size_t g_merged_size;
|
|
||||||
volatile int g_merged_with_right;
|
|
||||||
|
|
||||||
static void insert_block_merge(HeapBlock *block)
|
|
||||||
{
|
|
||||||
HeapBlock *iterator;
|
|
||||||
uint8_t *block_end;
|
|
||||||
|
|
||||||
for (iterator = &start; iterator->next < block; iterator = iterator->next)
|
|
||||||
;
|
|
||||||
|
|
||||||
block_end = (uint8_t *)block + block->size;
|
|
||||||
if (block_end == (uint8_t *)iterator->next) {
|
|
||||||
block->size += iterator->next->size;
|
|
||||||
block->next = iterator->next->next;
|
|
||||||
} else {
|
|
||||||
block->next = iterator->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iterator != &start && heap4_blocks_touch(iterator, block)) {
|
|
||||||
iterator->size += block->size;
|
|
||||||
iterator->next = block->next;
|
|
||||||
} else {
|
|
||||||
iterator->next = block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int count_free(void)
|
|
||||||
{
|
|
||||||
HeapBlock *block;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
count = 0;
|
|
||||||
for (block = start.next; block != end_marker; block = block->next)
|
|
||||||
count++;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
HeapBlock *left;
|
|
||||||
HeapBlock *middle;
|
|
||||||
HeapBlock *right;
|
|
||||||
|
|
||||||
left = (HeapBlock *)&heap_area[0];
|
|
||||||
middle = (HeapBlock *)&heap_area[48];
|
|
||||||
right = (HeapBlock *)&heap_area[96];
|
|
||||||
end_marker = (HeapBlock *)&heap_area[160];
|
|
||||||
|
|
||||||
start.next = left;
|
|
||||||
left->next = right;
|
|
||||||
right->next = end_marker;
|
|
||||||
end_marker->next = 0;
|
|
||||||
|
|
||||||
left->size = 48U;
|
|
||||||
middle->size = 48U;
|
|
||||||
right->size = 64U;
|
|
||||||
end_marker->size = 0U;
|
|
||||||
|
|
||||||
insert_block_merge(middle);
|
|
||||||
|
|
||||||
g_free_count = count_free();
|
|
||||||
g_merged_size = start.next->size;
|
|
||||||
g_merged_with_right = start.next->next == end_marker;
|
|
||||||
|
|
||||||
TRACE_PRINTF("count=%d size=%u merged_right=%d\n",
|
|
||||||
g_free_count, (unsigned)g_merged_size, g_merged_with_right);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#include "heap4_common.h"
|
|
||||||
|
|
||||||
#ifndef CONFIG_TOTAL_HEAP_SIZE
|
|
||||||
#define CONFIG_TOTAL_HEAP_SIZE 256U
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CONFIG_SUPPORT_DYNAMIC_ALLOCATION
|
|
||||||
#define CONFIG_SUPPORT_DYNAMIC_ALLOCATION 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CONFIG_ADJUSTED_HEAP_SIZE (CONFIG_TOTAL_HEAP_SIZE - HEAP4_ALIGNMENT)
|
|
||||||
|
|
||||||
volatile size_t g_total_heap;
|
|
||||||
volatile size_t g_adjusted_heap;
|
|
||||||
volatile int g_dynamic_enabled;
|
|
||||||
volatile int g_trace_is_host_only;
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
g_total_heap = CONFIG_TOTAL_HEAP_SIZE;
|
|
||||||
g_adjusted_heap = CONFIG_ADJUSTED_HEAP_SIZE;
|
|
||||||
g_dynamic_enabled = CONFIG_SUPPORT_DYNAMIC_ALLOCATION;
|
|
||||||
|
|
||||||
#ifdef HOST_PRINTF
|
|
||||||
g_trace_is_host_only = 1;
|
|
||||||
#else
|
|
||||||
g_trace_is_host_only = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TRACE_PRINTF("heap=%u adjusted=%u dynamic=%d host_trace=%d\n",
|
|
||||||
(unsigned)g_total_heap, (unsigned)g_adjusted_heap,
|
|
||||||
g_dynamic_enabled, g_trace_is_host_only);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
#include "heap4_common.h"
|
|
||||||
|
|
||||||
#define HEAP_BYTES 256U
|
|
||||||
#define MIN_SPLIT_SIZE ((size_t)(sizeof(HeapBlock) * 2U))
|
|
||||||
|
|
||||||
static uint8_t heap_area[HEAP_BYTES + HEAP4_ALIGNMENT];
|
|
||||||
static HeapBlock start;
|
|
||||||
static HeapBlock *end_marker;
|
|
||||||
|
|
||||||
volatile int g_allocations_ok;
|
|
||||||
volatile int g_final_free_nodes;
|
|
||||||
volatile size_t g_final_free_total;
|
|
||||||
volatile size_t g_minimum_ever_free;
|
|
||||||
|
|
||||||
static size_t free_bytes_remaining;
|
|
||||||
static size_t minimum_ever_free;
|
|
||||||
|
|
||||||
static size_t list_total(void)
|
|
||||||
{
|
|
||||||
HeapBlock *block;
|
|
||||||
size_t total;
|
|
||||||
|
|
||||||
total = 0U;
|
|
||||||
for (block = start.next; block != end_marker; block = block->next)
|
|
||||||
total += block->size;
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int list_count(void)
|
|
||||||
{
|
|
||||||
HeapBlock *block;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
count = 0;
|
|
||||||
for (block = start.next; block != end_marker; block = block->next)
|
|
||||||
count++;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void insert_block_merge(HeapBlock *block)
|
|
||||||
{
|
|
||||||
HeapBlock *iterator;
|
|
||||||
uint8_t *block_end;
|
|
||||||
|
|
||||||
for (iterator = &start; iterator->next < block; iterator = iterator->next)
|
|
||||||
;
|
|
||||||
|
|
||||||
block_end = (uint8_t *)block + block->size;
|
|
||||||
if (block_end == (uint8_t *)iterator->next) {
|
|
||||||
block->size += iterator->next->size;
|
|
||||||
block->next = iterator->next->next;
|
|
||||||
} else {
|
|
||||||
block->next = iterator->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iterator != &start && heap4_blocks_touch(iterator, block)) {
|
|
||||||
iterator->size += block->size;
|
|
||||||
iterator->next = block->next;
|
|
||||||
} else {
|
|
||||||
iterator->next = block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void heap_init(void)
|
|
||||||
{
|
|
||||||
uintptr_t raw;
|
|
||||||
uintptr_t aligned;
|
|
||||||
size_t available;
|
|
||||||
HeapBlock *first;
|
|
||||||
|
|
||||||
raw = (uintptr_t)&heap_area[0];
|
|
||||||
aligned = heap4_align_address(raw);
|
|
||||||
available = HEAP_BYTES - (size_t)(aligned - raw);
|
|
||||||
available &= ~HEAP4_ALIGNMENT_MASK;
|
|
||||||
|
|
||||||
first = (HeapBlock *)aligned;
|
|
||||||
end_marker = (HeapBlock *)(aligned + available - sizeof(HeapBlock));
|
|
||||||
end_marker->next = 0;
|
|
||||||
end_marker->size = 0U;
|
|
||||||
|
|
||||||
first->next = end_marker;
|
|
||||||
first->size = (uint8_t *)end_marker - (uint8_t *)first;
|
|
||||||
start.next = first;
|
|
||||||
start.size = 0U;
|
|
||||||
|
|
||||||
free_bytes_remaining = first->size;
|
|
||||||
minimum_ever_free = free_bytes_remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *heap_malloc_model(size_t wanted)
|
|
||||||
{
|
|
||||||
HeapBlock *previous;
|
|
||||||
HeapBlock *block;
|
|
||||||
HeapBlock *remainder;
|
|
||||||
size_t total;
|
|
||||||
size_t original;
|
|
||||||
|
|
||||||
if (wanted == 0U)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
total = heap4_align_up(wanted + sizeof(HeapBlock));
|
|
||||||
previous = &start;
|
|
||||||
block = start.next;
|
|
||||||
|
|
||||||
while (block != end_marker) {
|
|
||||||
if (block->size >= total) {
|
|
||||||
original = block->size;
|
|
||||||
if (original - total > MIN_SPLIT_SIZE) {
|
|
||||||
remainder = (HeapBlock *)((uint8_t *)block + total);
|
|
||||||
remainder->size = original - total;
|
|
||||||
remainder->next = block->next;
|
|
||||||
previous->next = remainder;
|
|
||||||
block->size = total;
|
|
||||||
} else {
|
|
||||||
previous->next = block->next;
|
|
||||||
total = original;
|
|
||||||
}
|
|
||||||
|
|
||||||
block->next = 0;
|
|
||||||
block->size = heap4_mark_allocated(block->size);
|
|
||||||
free_bytes_remaining -= total;
|
|
||||||
if (free_bytes_remaining < minimum_ever_free)
|
|
||||||
minimum_ever_free = free_bytes_remaining;
|
|
||||||
return (uint8_t *)block + sizeof(HeapBlock);
|
|
||||||
}
|
|
||||||
previous = block;
|
|
||||||
block = block->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void heap_free_model(void *p)
|
|
||||||
{
|
|
||||||
HeapBlock *block;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
if (p == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
block = (HeapBlock *)((uint8_t *)p - sizeof(HeapBlock));
|
|
||||||
if (!heap4_is_allocated(block->size))
|
|
||||||
return;
|
|
||||||
|
|
||||||
size = heap4_clear_allocated(block->size);
|
|
||||||
block->size = size;
|
|
||||||
free_bytes_remaining += size;
|
|
||||||
insert_block_merge(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
void *a;
|
|
||||||
void *b;
|
|
||||||
void *c;
|
|
||||||
|
|
||||||
heap_init();
|
|
||||||
|
|
||||||
a = heap_malloc_model(24U);
|
|
||||||
b = heap_malloc_model(40U);
|
|
||||||
heap_free_model(a);
|
|
||||||
c = heap_malloc_model(16U);
|
|
||||||
heap_free_model(b);
|
|
||||||
heap_free_model(c);
|
|
||||||
|
|
||||||
g_allocations_ok = a != 0 && b != 0 && c != 0;
|
|
||||||
g_final_free_nodes = list_count();
|
|
||||||
g_final_free_total = list_total();
|
|
||||||
g_minimum_ever_free = minimum_ever_free;
|
|
||||||
|
|
||||||
TRACE_PRINTF("ok=%d nodes=%d free=%u min=%u\n",
|
|
||||||
g_allocations_ok, g_final_free_nodes,
|
|
||||||
(unsigned)g_final_free_total, (unsigned)g_minimum_ever_free);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
schema: 1
|
||||||
|
targets:
|
||||||
|
native:
|
||||||
|
profile: native-amd64
|
||||||
|
actions: [build, test, run, debug]
|
||||||
|
hazard3-baremetal:
|
||||||
|
profile: hazard3-sim
|
||||||
|
actions: [build, test, run, debug]
|
||||||
|
rp2350-rv:
|
||||||
|
profile: rp2350
|
||||||
|
actions: [build, test, deploy, debug]
|
||||||
|
actions:
|
||||||
|
build: [bash, tools/card-action.sh, build]
|
||||||
|
test: [bash, tools/card-action.sh, test]
|
||||||
|
run: [bash, tools/card-action.sh, run]
|
||||||
|
deploy: [bash, tools/card-action.sh, deploy]
|
||||||
|
debug: [bash, tools/card-action.sh, debug]
|
||||||
|
artifacts:
|
||||||
|
directory: .stem/artifacts
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
elf="${1:?usage: check_task03_elf.sh ELF [tool-prefix]}"
|
||||||
|
prefix="${2:-riscv64-unknown-elf-}"
|
||||||
|
nm="${prefix}nm"
|
||||||
|
objdump="${prefix}objdump"
|
||||||
|
|
||||||
|
test -s "$elf"
|
||||||
|
for symbol in pvPortMalloc vPortFree prvInsertBlockIntoFreeList ucHeap \
|
||||||
|
heap4_reset_checkpoint heap4_alloc_a_checkpoint \
|
||||||
|
heap4_alloc_b_checkpoint heap4_alloc_c_checkpoint \
|
||||||
|
heap4_free_a_checkpoint heap4_fragmented_checkpoint \
|
||||||
|
heap4_coalesced_checkpoint task03_debug_checkpoint \
|
||||||
|
g_initial_stats g_after_allocations_stats g_fragmented_stats \
|
||||||
|
g_final_stats g_rv32_layout_expected g_task03_pass; do
|
||||||
|
"$nm" -a "$elf" | grep -E "[[:space:]]${symbol}$" >/dev/null
|
||||||
|
done
|
||||||
|
"$objdump" -d "$elf" | grep -E 'addi[[:space:]]+sp,sp,-' >/dev/null
|
||||||
|
printf 'PASS Task 3 links upstream heap_4 and eight observable checkpoints: %s\n' "$elf"
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
make -C "$root" PROFILE=observe host >/dev/null
|
||||||
|
|
||||||
|
check_output() {
|
||||||
|
local program="$1" expected="$2" actual
|
||||||
|
actual="$($program)"
|
||||||
|
if [[ "$actual" != "$expected" ]]; then
|
||||||
|
printf 'FAIL %s\nexpected: %s\nactual: %s\n' "$program" "$expected" "$actual" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
printf 'PASS %s: %s\n' "$(basename -- "$(dirname -- "$program")")" "$actual"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_output "$root/host-build/task01_first_fit_split/prog" \
|
||||||
|
'header=16 wanted=32 remainder=224 payload=16 aligned=1 pass=1'
|
||||||
|
check_output "$root/host-build/task02_address_order_coalesce/prog" \
|
||||||
|
'before_nodes=2 before_total=96 before_largest=48 after_nodes=1 after_total=144 after_largest=144 pass=1'
|
||||||
|
check_output "$root/host-build/task03_freertos_heap4/prog" \
|
||||||
|
'initial=4080 after_a=4040 after_b=3984 after_c=3952 free_a=3992 fragmented=4024 final=4080 min=3952 consumed=40/56/32 aligned=1 oom=1 hooks=1 asserts=0 pass=1'
|
||||||
@@ -0,0 +1,364 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
action="${1:?action is required}"
|
||||||
|
root="${STEM_REPO:-$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)}"
|
||||||
|
profile="${STEM_PROFILE:-native-amd64}"
|
||||||
|
target="${STEM_TARGET:-native}"
|
||||||
|
selector="${STEM_TASK:-task3}"
|
||||||
|
state="${STEM_STATE_DIR:-$root/.stem/instances/local}"
|
||||||
|
|
||||||
|
tasks=(
|
||||||
|
task01_first_fit_split
|
||||||
|
task02_address_order_coalesce
|
||||||
|
task03_freertos_heap4
|
||||||
|
)
|
||||||
|
|
||||||
|
resolve_task() {
|
||||||
|
local value="${1,,}" digits number stem
|
||||||
|
for stem in "${tasks[@]}"; do
|
||||||
|
if [[ "$value" == "$stem" ]]; then
|
||||||
|
printf '%s\n' "$stem"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$value" =~ ^(task|t)?[-_]?0*([1-3])($|[-_].*) ]]; then
|
||||||
|
digits="${BASH_REMATCH[2]}"
|
||||||
|
number=$((10#$digits))
|
||||||
|
printf '%s\n' "${tasks[number - 1]}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
printf 'Unknown task selector: %s\n' "$1" >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
task_stem="$(resolve_task "$selector")"
|
||||||
|
task_digits="${task_stem#task}"
|
||||||
|
task_digits="${task_digits%%_*}"
|
||||||
|
task_number=$((10#$task_digits))
|
||||||
|
task_key="$(printf 'task%02d' "$task_number")"
|
||||||
|
source_file="$root/src/tasks/$task_stem.c"
|
||||||
|
artifact_dir="$root/.stem/artifacts/$profile/$target/$task_key"
|
||||||
|
mkdir -p "$state" "$artifact_dir"
|
||||||
|
|
||||||
|
freertos_kernel_path() {
|
||||||
|
if [[ -n "${FREERTOS_KERNEL_PATH:-}" ]]; then
|
||||||
|
printf '%s\n' "$FREERTOS_KERNEL_PATH"
|
||||||
|
elif [[ -f /opt/FreeRTOS-Kernel/portable/MemMang/heap_4.c ]]; then
|
||||||
|
printf '%s\n' /opt/FreeRTOS-Kernel
|
||||||
|
else
|
||||||
|
printf '%s\n' "$root/../lab-rv32i-freertos-task-stack-vector/vendor/FreeRTOS-Kernel"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_kernel() {
|
||||||
|
local kernel="$1"
|
||||||
|
test -f "$kernel/portable/MemMang/heap_4.c"
|
||||||
|
grep -F 'FreeRTOS Kernel V11.3.0' "$kernel/include/FreeRTOS.h" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
freertos_rp2350_path() {
|
||||||
|
if [[ -n "${FREERTOS_RP2350_PATH:-}" ]]; then
|
||||||
|
printf '%s\n' "$FREERTOS_RP2350_PATH"
|
||||||
|
else
|
||||||
|
printf '%s\n' /opt/FreeRTOS-RP2350
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_rp2350_kernel() {
|
||||||
|
local kernel="$1"
|
||||||
|
test -f "$kernel/portable/ThirdParty/GCC/RP2350_RISC-V/portASM.S"
|
||||||
|
grep -F '<DEVELOPMENT BRANCH>' "$kernel/include/FreeRTOS.h" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_artifact() {
|
||||||
|
local path="$1"
|
||||||
|
[[ -f "$path" ]] && cp -f "$path" "$artifact_dir/"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_native() {
|
||||||
|
local mode="${1:-normal}" kernel build_dir output cc="${CC:-cc}"
|
||||||
|
local flags=(-std=c11 -O0 -g3 -fno-omit-frame-pointer -fno-inline
|
||||||
|
-fno-optimize-sibling-calls -Wall -Wextra -Wpedantic
|
||||||
|
-DHOST_PRINTF=1 -I"$root/src/tasks")
|
||||||
|
build_dir="$state/build/native/$task_stem"
|
||||||
|
output="$build_dir/prog"
|
||||||
|
if [[ "$mode" == sanitizer ]]; then
|
||||||
|
flags+=(-fsanitize=address,undefined)
|
||||||
|
fi
|
||||||
|
mkdir -p "$build_dir"
|
||||||
|
if [[ "$task_number" -eq 3 ]]; then
|
||||||
|
kernel="$(freertos_kernel_path)"
|
||||||
|
verify_kernel "$kernel"
|
||||||
|
flags+=(-DHEAP4_HOST_ADAPTER=1 -I"$root/include/host-shim")
|
||||||
|
"$cc" "${flags[@]}" "$source_file" \
|
||||||
|
"$kernel/portable/MemMang/heap_4.c" -o "$output"
|
||||||
|
else
|
||||||
|
"$cc" "${flags[@]}" "$source_file" -o "$output"
|
||||||
|
fi
|
||||||
|
objdump -dS "$output" > "$build_dir/prog.lst"
|
||||||
|
cp -f "$output" "$artifact_dir/prog"
|
||||||
|
copy_artifact "$build_dir/prog.lst"
|
||||||
|
printf '%s\n' "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
rv_env_root() {
|
||||||
|
if [[ -n "${RV_ENV_ROOT:-}" ]]; then
|
||||||
|
printf '%s\n' "$RV_ENV_ROOT"
|
||||||
|
elif [[ -f "$root/vendor/Hazard3/test/sim/common/init.S" && \
|
||||||
|
-f "$root/vendor/lab-runtime/memops.c" ]]; then
|
||||||
|
printf '%s\n' "$root"
|
||||||
|
elif [[ -d /opt/rv-env ]]; then
|
||||||
|
printf '%s\n' /opt/rv-env
|
||||||
|
else
|
||||||
|
printf '%s\n' /opt/stem
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
build_hazard3() {
|
||||||
|
local env_root kernel build_dir elf
|
||||||
|
env_root="$(rv_env_root)"
|
||||||
|
kernel="$(freertos_kernel_path)"
|
||||||
|
verify_kernel "$kernel"
|
||||||
|
build_dir="$state/build/hazard3"
|
||||||
|
make -B -C "$root" "task$task_number" \
|
||||||
|
"BUILD_ROOT=$build_dir" "RV_ENV_ROOT=$env_root" \
|
||||||
|
"FREERTOS_KERNEL_PATH=$kernel" PROFILE=observe >&2 || return
|
||||||
|
elf="$build_dir/$task_stem/prog.elf"
|
||||||
|
test -s "$elf"
|
||||||
|
if [[ "$task_number" -eq 3 ]]; then
|
||||||
|
"$root/tests/check_task03_elf.sh" "$elf" >&2
|
||||||
|
fi
|
||||||
|
copy_artifact "$elf"
|
||||||
|
copy_artifact "${elf%.elf}.bin"
|
||||||
|
copy_artifact "${elf%.elf}.lst"
|
||||||
|
copy_artifact "${elf%.elf}.map"
|
||||||
|
printf '%s\n' "$elf"
|
||||||
|
}
|
||||||
|
|
||||||
|
hazard3_tb() {
|
||||||
|
local env_root tb
|
||||||
|
env_root="$(rv_env_root)"
|
||||||
|
tb="$env_root/vendor/Hazard3/test/sim/tb_verilator/tb"
|
||||||
|
if [[ ! -x "$tb" ]]; then
|
||||||
|
make -C "$(dirname -- "$tb")" tb >&2
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$tb"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_rp2350() {
|
||||||
|
local binary_type="${1:-no_flash}" build_kind build_dir elf kernel rp_kernel
|
||||||
|
local cmake_args
|
||||||
|
case "$binary_type" in
|
||||||
|
no_flash) build_kind=ram ;;
|
||||||
|
default) build_kind=flash ;;
|
||||||
|
*) printf 'Unsupported RP2350 binary type: %s\n' "$binary_type" >&2; return 2 ;;
|
||||||
|
esac
|
||||||
|
kernel="$(freertos_kernel_path)"
|
||||||
|
verify_kernel "$kernel"
|
||||||
|
rp_kernel="$(freertos_rp2350_path)"
|
||||||
|
verify_rp2350_kernel "$rp_kernel"
|
||||||
|
build_dir="$state/build/rp2350-rv-$build_kind"
|
||||||
|
cmake_args=(
|
||||||
|
-S "$root" -B "$build_dir"
|
||||||
|
-DPICO_PLATFORM=rp2350-riscv
|
||||||
|
-DPICO_BOARD="${PICO_BOARD:-pico2_w}"
|
||||||
|
-DPICO_GCC_TRIPLE=riscv32-pico-elf
|
||||||
|
-DHEAP4_UPSTREAM_ROOT="$kernel"
|
||||||
|
-DFREERTOS_RP2350_PATH="$rp_kernel"
|
||||||
|
-DHEAP4_PROFILE=observe
|
||||||
|
-DHEAP4_BINARY_TYPE="$binary_type"
|
||||||
|
-DCMAKE_BUILD_TYPE=Debug
|
||||||
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||||
|
)
|
||||||
|
if command -v ninja >/dev/null 2>&1; then
|
||||||
|
cmake_args+=(-G Ninja)
|
||||||
|
else
|
||||||
|
cmake_args+=(-G "Unix Makefiles")
|
||||||
|
fi
|
||||||
|
if [[ -d /opt/picotool/picotool ]]; then
|
||||||
|
cmake_args+=(-Dpicotool_DIR=/opt/picotool/picotool)
|
||||||
|
fi
|
||||||
|
cmake "${cmake_args[@]}" >&2
|
||||||
|
cmake --build "$build_dir" --target "$task_stem" >&2
|
||||||
|
elf="$build_dir/$task_stem.elf"
|
||||||
|
test -s "$elf"
|
||||||
|
riscv64-unknown-elf-objdump -dS -M no-aliases,numeric "$elf" \
|
||||||
|
> "$build_dir/$task_stem.lst"
|
||||||
|
copy_artifact "$elf"
|
||||||
|
copy_artifact "$build_dir/$task_stem.bin"
|
||||||
|
copy_artifact "$build_dir/$task_stem.hex"
|
||||||
|
copy_artifact "$build_dir/$task_stem.uf2"
|
||||||
|
copy_artifact "$build_dir/$task_stem.dis"
|
||||||
|
copy_artifact "$build_dir/$task_stem.lst"
|
||||||
|
copy_artifact "$build_dir/$task_stem.elf.map"
|
||||||
|
printf '%s\n' "$elf"
|
||||||
|
}
|
||||||
|
|
||||||
|
openocd_target="${STEM_OPENOCD_TARGET:-target/rp2350.cfg}"
|
||||||
|
gdb_port="${GDB_PORT:-3333}"
|
||||||
|
gdb_target="${GDB_TARGET:-127.0.0.1:$gdb_port}"
|
||||||
|
openocd_pid=""
|
||||||
|
|
||||||
|
stop_owned_openocd() {
|
||||||
|
if [[ -n "$openocd_pid" ]]; then
|
||||||
|
kill "$openocd_pid" 2>/dev/null || true
|
||||||
|
wait "$openocd_pid" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
start_openocd() {
|
||||||
|
local log="$state/openocd-rp2350.log" attempt
|
||||||
|
if nc -z 127.0.0.1 "$gdb_port" 2>/dev/null; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
openocd -f interface/cmsis-dap.cfg \
|
||||||
|
-c "adapter speed ${STEM_ADAPTER_KHZ:-5000}" \
|
||||||
|
-c 'set USE_CORE { rv0 }' -c 'set USE_SMP 0' \
|
||||||
|
-c "set FLASHSIZE ${STEM_RP2350_FLASH_SIZE:-0x400000}" \
|
||||||
|
-f "$openocd_target" >"$log" 2>&1 &
|
||||||
|
openocd_pid=$!
|
||||||
|
for attempt in $(seq 1 80); do
|
||||||
|
if nc -z 127.0.0.1 "$gdb_port" 2>/dev/null; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if ! kill -0 "$openocd_pid" 2>/dev/null; then
|
||||||
|
sed -n '1,240p' "$log" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
printf 'OpenOCD did not open GDB port %s\n' "$gdb_port" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
flash_rp2350() {
|
||||||
|
local elf="$1" log="$state/openocd-rp2350-flash.log"
|
||||||
|
if nc -z 127.0.0.1 "$gdb_port" 2>/dev/null; then
|
||||||
|
printf 'GDB port %s is busy; stop the current debug session before deploy.\n' "$gdb_port" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! openocd -f interface/cmsis-dap.cfg \
|
||||||
|
-c "adapter speed ${STEM_ADAPTER_KHZ:-5000}" \
|
||||||
|
-c 'set USE_CORE { cm0 rv0 }' -c 'set USE_SMP 0' \
|
||||||
|
-c "set FLASHSIZE ${STEM_RP2350_FLASH_SIZE:-0x400000}" \
|
||||||
|
-f "$openocd_target" -c init \
|
||||||
|
-c 'if {[target_present rp2350.rv0]} { targets rp2350.rv0 } else { targets rp2350.cm0 }' \
|
||||||
|
-c 'reset halt' -c "flash write_image erase $elf" \
|
||||||
|
-c "verify_image $elf" -c 'reset run' -c shutdown >"$log" 2>&1; then
|
||||||
|
sed -n '1,260p' "$log" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
grep -E 'wrote [0-9]+ bytes|verified [0-9]+ bytes' "$log" || true
|
||||||
|
printf 'rp2350-flash-ok image=%s verify=passed architecture=RISC-V\n' "$elf"
|
||||||
|
}
|
||||||
|
|
||||||
|
hardware_test_task03() {
|
||||||
|
local elf="$1" output
|
||||||
|
start_openocd
|
||||||
|
trap stop_owned_openocd EXIT
|
||||||
|
output="$(gdb-multiarch -nx -q -batch "$elf" \
|
||||||
|
-ex 'set architecture riscv:rv32' \
|
||||||
|
-ex "target extended-remote $gdb_target" \
|
||||||
|
-ex 'monitor reset halt' \
|
||||||
|
-ex 'hbreak heap4_fragmented_checkpoint' \
|
||||||
|
-ex 'hbreak task03_debug_checkpoint' \
|
||||||
|
-ex continue \
|
||||||
|
-ex 'printf "fragmented blocks=%lu free=%lu largest=%lu\n", g_fragmented_stats.xNumberOfFreeBlocks, g_fragmented_stats.xAvailableHeapSpaceInBytes, g_fragmented_stats.xSizeOfLargestFreeBlockInBytes' \
|
||||||
|
-ex continue \
|
||||||
|
-ex 'printf "task03 initial=%lu after=%lu final=%lu min=%lu aligned=%d oom=%d hooks=%d asserts=%d pass=%d\n", g_initial_free, g_after_allocations, g_final_free, g_minimum_ever_free, g_allocations_aligned, g_oom_is_null, g_malloc_failed_hooks, g_assert_failures, g_task03_pass' \
|
||||||
|
-ex 'printf "frame sp=0x%08lx fp=0x%08lx pc=0x%08lx align=%lu\n", (unsigned long)$sp, (unsigned long)$s0, (unsigned long)$pc, ((unsigned long)$sp & 15)' \
|
||||||
|
-ex 'x/96bx ucHeap' \
|
||||||
|
-ex 'delete breakpoints' -ex 'monitor halt' -ex disconnect)"
|
||||||
|
printf '%s\n' "$output"
|
||||||
|
grep -E '^fragmented blocks=2 ' <<<"$output" >/dev/null
|
||||||
|
grep -E '^task03 .* aligned=1 oom=1 hooks=1 asserts=0 pass=1$' <<<"$output" >/dev/null
|
||||||
|
grep -E '^frame .* align=0$' <<<"$output" >/dev/null
|
||||||
|
printf 'rp2350-hardware-ok task=task03 heap_4=upstream load=flash\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_debug_view() {
|
||||||
|
export STEM_DEBUG_WINDOW_NAME=heap4
|
||||||
|
case "$task_number" in
|
||||||
|
1)
|
||||||
|
export STEM_DEBUG_INITIAL_BREAK=task01_debug_checkpoint
|
||||||
|
export STEM_DEBUG_MEMORY_EXPR='&g_split_arena' STEM_DEBUG_MEMORY_LENGTH=288
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
export STEM_DEBUG_INITIAL_BREAK=task02_fragmented_checkpoint
|
||||||
|
export STEM_DEBUG_MEMORY_EXPR='&g_coalesce_arena' STEM_DEBUG_MEMORY_LENGTH=160
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
export STEM_DEBUG_INITIAL_BREAK=heap4_reset_checkpoint
|
||||||
|
export STEM_DEBUG_MEMORY_EXPR='ucHeap' STEM_DEBUG_MEMORY_LENGTH=160
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$profile:$target:$action" in
|
||||||
|
native-amd64:native:build)
|
||||||
|
build_native normal >/dev/null
|
||||||
|
;;
|
||||||
|
native-amd64:native:test)
|
||||||
|
output="$(build_native sanitizer)"
|
||||||
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1 "$output"
|
||||||
|
;;
|
||||||
|
native-amd64:native:run)
|
||||||
|
output="$(build_native normal)"
|
||||||
|
exec "$output"
|
||||||
|
;;
|
||||||
|
native-amd64:native:debug)
|
||||||
|
output="$(build_native normal)"
|
||||||
|
export RV_REPO="$root" RV_EDITOR="${STEM_EDITOR:-nvim}"
|
||||||
|
export RV_HOST_ELF="$output" RV_HOST_SRC="$source_file"
|
||||||
|
export RV_HOST_LST="$(dirname -- "$output")/prog.lst"
|
||||||
|
exec /opt/stem/scripts/host-entry.sh debug "task$task_number"
|
||||||
|
;;
|
||||||
|
hazard3-sim:hazard3-baremetal:build)
|
||||||
|
build_hazard3 >/dev/null
|
||||||
|
;;
|
||||||
|
hazard3-sim:hazard3-baremetal:test|hazard3-sim:hazard3-baremetal:run)
|
||||||
|
elf="$(build_hazard3)"
|
||||||
|
tb="$(hazard3_tb)"
|
||||||
|
exec "$tb" --bin "${elf%.elf}.bin" --cpuret
|
||||||
|
;;
|
||||||
|
hazard3-sim:hazard3-baremetal:debug)
|
||||||
|
elf="$(build_hazard3)"
|
||||||
|
configure_debug_view
|
||||||
|
export STEM_DEBUG_BACKEND=hazard3 STEM_DEBUG_ELF="$elf"
|
||||||
|
export STEM_DEBUG_BIN="${elf%.elf}.bin" STEM_DEBUG_SRC="$source_file"
|
||||||
|
export STEM_DEBUG_LST="${elf%.elf}.lst"
|
||||||
|
exec /opt/stem/scripts/card-debug-ui.sh
|
||||||
|
;;
|
||||||
|
rp2350:rp2350-rv:build)
|
||||||
|
build_rp2350 >/dev/null
|
||||||
|
;;
|
||||||
|
rp2350:rp2350-rv:test)
|
||||||
|
elf="$(build_rp2350)"
|
||||||
|
riscv64-unknown-elf-readelf -h "$elf" | grep -F 'Machine:' | grep -F 'RISC-V'
|
||||||
|
if [[ "$task_number" -eq 3 ]]; then
|
||||||
|
"$root/tests/check_task03_elf.sh" "$elf"
|
||||||
|
fi
|
||||||
|
printf 'rp2350-build-ok task=%s binary_type=no_flash\n' "$task_key"
|
||||||
|
;;
|
||||||
|
rp2350:rp2350-rv:deploy)
|
||||||
|
elf="$(build_rp2350 default)"
|
||||||
|
flash_rp2350 "$elf"
|
||||||
|
if [[ "$task_number" -eq 3 ]]; then
|
||||||
|
hardware_test_task03 "$elf"
|
||||||
|
else
|
||||||
|
printf 'rp2350-deploy-ok task=%s load=flash persistent=1\n' "$task_key"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
rp2350:rp2350-rv:debug)
|
||||||
|
elf="$(build_rp2350)"
|
||||||
|
configure_debug_view
|
||||||
|
export STEM_DEBUG_BACKEND=rp2350 STEM_DEBUG_ELF="$elf"
|
||||||
|
export STEM_DEBUG_SRC="$source_file" STEM_DEBUG_LST="${elf%.elf}.lst"
|
||||||
|
exec /opt/stem/scripts/card-debug-ui.sh
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf 'Unsupported profile/target/action: %s/%s/%s\n' "$profile" "$target" "$action" >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_alu #(
|
||||||
|
`include "hazard3_config.vh"
|
||||||
|
,
|
||||||
|
`include "hazard3_width_const.vh"
|
||||||
|
) (
|
||||||
|
input wire [W_ALUOP-1:0] aluop,
|
||||||
|
input wire [6:0] funct7_32b,
|
||||||
|
input wire [2:0] funct3_32b,
|
||||||
|
input wire [W_DATA-1:0] op_a,
|
||||||
|
input wire [W_DATA-1:0] op_b,
|
||||||
|
output reg [W_DATA-1:0] result,
|
||||||
|
output wire cmp
|
||||||
|
);
|
||||||
|
|
||||||
|
`include "hazard3_ops.vh"
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Fiddle around with add/sub, comparisons etc (all related).
|
||||||
|
|
||||||
|
wire sub = !(aluop == ALUOP_ADD || (|EXTENSION_ZBA && aluop == ALUOP_SHXADD));
|
||||||
|
|
||||||
|
wire inv_op_b = sub && !(
|
||||||
|
aluop == ALUOP_AND || aluop == ALUOP_OR || aluop == ALUOP_XOR || aluop == ALUOP_RS2
|
||||||
|
);
|
||||||
|
|
||||||
|
wire [W_DATA-1:0] op_a_shifted =
|
||||||
|
|EXTENSION_ZBA && aluop == ALUOP_SHXADD ? (
|
||||||
|
!funct3_32b[2] ? op_a << 1 :
|
||||||
|
!funct3_32b[1] ? op_a << 2 : op_a << 3
|
||||||
|
) : op_a;
|
||||||
|
|
||||||
|
wire [W_DATA-1:0] op_b_inv = op_b ^ {W_DATA{inv_op_b}};
|
||||||
|
|
||||||
|
wire [W_DATA-1:0] sum = op_a_shifted + op_b_inv + {{W_DATA-1{1'b0}}, sub};
|
||||||
|
wire [W_DATA-1:0] op_xor = op_a ^ op_b;
|
||||||
|
|
||||||
|
wire cmp_is_unsigned = aluop == ALUOP_LTU ||
|
||||||
|
|EXTENSION_ZBB && aluop == ALUOP_MAXU ||
|
||||||
|
|EXTENSION_ZBB && aluop == ALUOP_MINU;
|
||||||
|
|
||||||
|
wire lt = op_a[W_DATA-1] == op_b[W_DATA-1] ? sum[W_DATA-1] :
|
||||||
|
cmp_is_unsigned ? op_b[W_DATA-1] : op_a[W_DATA-1] ;
|
||||||
|
|
||||||
|
assign cmp = aluop == ALUOP_SUB ? |op_xor : lt;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Separate units for shift, ctz etc
|
||||||
|
|
||||||
|
wire [W_DATA-1:0] shift_dout;
|
||||||
|
wire shift_right_nleft =
|
||||||
|
aluop == ALUOP_SRL ||
|
||||||
|
aluop == ALUOP_SRA ||
|
||||||
|
(|EXTENSION_ZBB && aluop == ALUOP_ROR ) ||
|
||||||
|
(|EXTENSION_ZBS && aluop == ALUOP_BEXT ) ||
|
||||||
|
(|EXTENSION_XH3BEXTM && aluop == ALUOP_BEXTM);
|
||||||
|
|
||||||
|
wire shift_arith = aluop == ALUOP_SRA;
|
||||||
|
wire shift_rotate = |EXTENSION_ZBB & (aluop == ALUOP_ROR || aluop == ALUOP_ROL);
|
||||||
|
|
||||||
|
hazard3_shift_barrel #(
|
||||||
|
`include "hazard3_config_inst.vh"
|
||||||
|
) shifter (
|
||||||
|
.din (op_a),
|
||||||
|
.shamt (op_b[4:0]),
|
||||||
|
.right_nleft (shift_right_nleft),
|
||||||
|
.rotate (shift_rotate),
|
||||||
|
.arith (shift_arith),
|
||||||
|
.dout (shift_dout)
|
||||||
|
);
|
||||||
|
|
||||||
|
reg [W_DATA-1:0] op_a_rev;
|
||||||
|
always @ (*) begin: rev_op_a
|
||||||
|
integer i;
|
||||||
|
for (i = 0; i < W_DATA; i = i + 1) begin
|
||||||
|
op_a_rev[i] = op_a[W_DATA - 1 - i];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// "leading" means starting at MSB. This is an LSB-first priority encoder, so
|
||||||
|
// "leading" is reversed and "trailing" is not.
|
||||||
|
wire [W_DATA-1:0] ctz_search_mask = aluop == ALUOP_CLZ ? op_a_rev : op_a;
|
||||||
|
wire [W_SHAMT:0] ctz_clz;
|
||||||
|
|
||||||
|
hazard3_priority_encode #(
|
||||||
|
.W_REQ (W_DATA),
|
||||||
|
.HIGHEST_WINS (0)
|
||||||
|
) ctz_priority_encode (
|
||||||
|
.req (ctz_search_mask),
|
||||||
|
.gnt (ctz_clz[W_SHAMT-1:0])
|
||||||
|
);
|
||||||
|
// Special case: all-zeroes returns XLEN
|
||||||
|
assign ctz_clz[W_SHAMT] = ~|op_a;
|
||||||
|
|
||||||
|
reg [W_SHAMT:0] cpop;
|
||||||
|
always @ (*) begin: cpop_count
|
||||||
|
integer i;
|
||||||
|
cpop = {W_SHAMT+1{1'b0}};
|
||||||
|
for (i = 0; i < W_DATA; i = i + 1) begin
|
||||||
|
cpop = cpop + {{W_SHAMT{1'b0}}, op_a[i]};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
reg [2*W_DATA-1:0] clmul64;
|
||||||
|
|
||||||
|
always @ (*) begin: clmul_mul
|
||||||
|
integer i;
|
||||||
|
clmul64 = {2*W_DATA{1'b0}};
|
||||||
|
for (i = 0; i < W_DATA; i = i + 1) begin
|
||||||
|
clmul64 = clmul64 ^ (({{W_DATA{1'b0}}, op_a} << i) & {2*W_DATA{op_b[i]}});
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// funct3: 1=clmul, 2=clmulr, 3=clmulh, never 0.
|
||||||
|
wire [W_DATA-1:0] clmul =
|
||||||
|
!funct3_32b[1] ? clmul64[31: 0] :
|
||||||
|
!funct3_32b[0] ? clmul64[62:31] : clmul64[63:32];
|
||||||
|
|
||||||
|
reg [W_DATA-1:0] zip;
|
||||||
|
reg [W_DATA-1:0] unzip;
|
||||||
|
always @ (*) begin: do_zip_unzip
|
||||||
|
integer i;
|
||||||
|
for (i = 0; i < W_DATA; i = i + 1) begin
|
||||||
|
zip[i] = op_a[{i[0], i[4:1]}]; // Alternate high/low halves
|
||||||
|
unzip[i] = op_a[{i[3:0], i[4]}]; // All even then all odd
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
reg [W_DATA-1:0] xperm8;
|
||||||
|
always @ (*) begin: do_xperm8
|
||||||
|
integer i;
|
||||||
|
for (i = 0; i < W_DATA; i = i + 8) begin
|
||||||
|
if (|op_b[i + 2 +: 6]) begin
|
||||||
|
xperm8[i +: 8] = 8'h00;
|
||||||
|
end else begin
|
||||||
|
xperm8[i +: 8] = op_a[8 * op_b[i +: 2] +: 8];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
reg [W_DATA-1:0] xperm4;
|
||||||
|
always @ (*) begin: do_xperm4
|
||||||
|
integer i;
|
||||||
|
for (i = 0; i < W_DATA; i = i + 4) begin
|
||||||
|
if (op_b[i + 3]) begin
|
||||||
|
xperm4[i +: 4] = 4'h0;
|
||||||
|
end else begin
|
||||||
|
xperm4[i +: 4] = op_a[4 * op_b[i +: 3] +: 4];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Output mux, with simple operations inline
|
||||||
|
|
||||||
|
// iCE40: We can implement all bitwise ops with 1 LUT4/bit total, since each
|
||||||
|
// result bit uses only two operand bits. Much better than feeding each into
|
||||||
|
// main mux tree. Doesn't matter for big-LUT FPGAs or for implementations with
|
||||||
|
// bitmanip extensions enabled.
|
||||||
|
|
||||||
|
reg [W_DATA-1:0] bitwise;
|
||||||
|
|
||||||
|
always @ (*) begin: bitwise_ops
|
||||||
|
case (aluop[1:0])
|
||||||
|
ALUOP_AND[1:0]: bitwise = op_a & op_b_inv;
|
||||||
|
ALUOP_OR [1:0]: bitwise = op_a | op_b_inv;
|
||||||
|
ALUOP_XOR[1:0]: bitwise = op_a ^ op_b_inv;
|
||||||
|
ALUOP_RS2[1:0]: bitwise = op_b_inv;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
wire [W_DATA-1:0] zbs_mask = {{W_DATA-1{1'b0}}, 1'b1} << op_b[W_SHAMT-1:0];
|
||||||
|
|
||||||
|
always @ (*) begin
|
||||||
|
casez ({|EXTENSION_A, |EXTENSION_ZBA, |EXTENSION_ZBB, |EXTENSION_ZBC,
|
||||||
|
|EXTENSION_ZBS, |EXTENSION_ZBKB, |EXTENSION_ZBKX, |EXTENSION_XH3BEXTM, aluop})
|
||||||
|
// Base ISA
|
||||||
|
{8'bzzzzzzzz, ALUOP_ADD }: result = sum;
|
||||||
|
{8'bzzzzzzzz, ALUOP_SUB }: result = sum;
|
||||||
|
{8'bzzzzzzzz, ALUOP_LT }: result = {{W_DATA-1{1'b0}}, lt};
|
||||||
|
{8'bzzzzzzzz, ALUOP_LTU }: result = {{W_DATA-1{1'b0}}, lt};
|
||||||
|
{8'bzzzzzzzz, ALUOP_SRL }: result = shift_dout;
|
||||||
|
{8'bzzzzzzzz, ALUOP_SRA }: result = shift_dout;
|
||||||
|
{8'bzzzzzzzz, ALUOP_SLL }: result = shift_dout;
|
||||||
|
// A or Zbb (written this way to avoid case overlap)
|
||||||
|
{8'b1zzzzzzz, ALUOP_MAX },
|
||||||
|
{8'b0z1zzzzz, ALUOP_MAX }: result = lt ? op_b : op_a;
|
||||||
|
{8'b1zzzzzzz, ALUOP_MIN },
|
||||||
|
{8'b0z1zzzzz, ALUOP_MIN }: result = lt ? op_a : op_b;
|
||||||
|
{8'b1zzzzzzz, ALUOP_MAXU },
|
||||||
|
{8'b0z1zzzzz, ALUOP_MAXU }: result = lt ? op_b : op_a;
|
||||||
|
{8'b1zzzzzzz, ALUOP_MINU },
|
||||||
|
{8'b0z1zzzzz, ALUOP_MINU }: result = lt ? op_a : op_b;
|
||||||
|
// Zba
|
||||||
|
{8'bz1zzzzzz, ALUOP_SHXADD }: result = sum;
|
||||||
|
// Zbb
|
||||||
|
{8'bzz1zzzzz, ALUOP_ANDN }: result = bitwise;
|
||||||
|
{8'bzz1zzzzz, ALUOP_ORN }: result = bitwise;
|
||||||
|
{8'bzz1zzzzz, ALUOP_XNOR }: result = bitwise;
|
||||||
|
{8'bzz1zzzzz, ALUOP_CLZ }: result = {{W_DATA-W_SHAMT-1{1'b0}}, ctz_clz};
|
||||||
|
{8'bzz1zzzzz, ALUOP_CTZ }: result = {{W_DATA-W_SHAMT-1{1'b0}}, ctz_clz};
|
||||||
|
{8'bzz1zzzzz, ALUOP_CPOP }: result = {{W_DATA-W_SHAMT-1{1'b0}}, cpop};
|
||||||
|
{8'bzz1zzzzz, ALUOP_SEXT_B }: result = {{W_DATA-8{op_a[7]}}, op_a[7:0]};
|
||||||
|
{8'bzz1zzzzz, ALUOP_SEXT_H }: result = {{W_DATA-16{op_a[15]}}, op_a[15:0]};
|
||||||
|
{8'bzz1zzzzz, ALUOP_ZEXT_H }: result = {{W_DATA-16{1'b0}}, op_a[15:0]};
|
||||||
|
{8'bzz1zzzzz, ALUOP_ORC_B }: result = {{8{|op_a[31:24]}}, {8{|op_a[23:16]}}, {8{|op_a[15:8]}}, {8{|op_a[7:0]}}};
|
||||||
|
{8'bzz1zzzzz, ALUOP_REV8 }: result = {op_a[7:0], op_a[15:8], op_a[23:16], op_a[31:24]};
|
||||||
|
{8'bzz1zzzzz, ALUOP_ROL }: result = shift_dout;
|
||||||
|
{8'bzz1zzzzz, ALUOP_ROR }: result = shift_dout;
|
||||||
|
// Zbc
|
||||||
|
{8'bzzz1zzzz, ALUOP_CLMUL }: result = clmul;
|
||||||
|
// Zbs
|
||||||
|
{8'bzzzz1zzz, ALUOP_BCLR }: result = op_a & ~zbs_mask;
|
||||||
|
{8'bzzzz1zzz, ALUOP_BSET }: result = op_a | zbs_mask;
|
||||||
|
{8'bzzzz1zzz, ALUOP_BINV }: result = op_a ^ zbs_mask;
|
||||||
|
{8'bzzzz1zzz, ALUOP_BEXT }: result = {{W_DATA-1{1'b0}}, shift_dout[0]};
|
||||||
|
// Zbkb
|
||||||
|
{8'bzzzzz1zz, ALUOP_PACK }: result = {op_b[15:0], op_a[15:0]};
|
||||||
|
{8'bzzzzz1zz, ALUOP_PACKH }: result = {{W_DATA-16{1'b0}}, op_b[7:0], op_a[7:0]};
|
||||||
|
{8'bzzzzz1zz, ALUOP_BREV8 }: result = {op_a_rev[7:0], op_a_rev[15:8], op_a_rev[23:16], op_a_rev[31:24]};
|
||||||
|
{8'bzzzzz1zz, ALUOP_UNZIP }: result = unzip;
|
||||||
|
{8'bzzzzz1zz, ALUOP_ZIP }: result = zip;
|
||||||
|
// Zbkx
|
||||||
|
{8'bzzzzzz1z, ALUOP_XPERM }: result = funct3_32b[2] ? xperm8 : xperm4;
|
||||||
|
// Xh3bextm
|
||||||
|
{8'bzzzzzzz1, ALUOP_BEXTM }: result = shift_dout & {24'h0, {~(8'hfe << funct7_32b[3:1])}};
|
||||||
|
|
||||||
|
default: result = bitwise;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Properties for base-ISA instructions
|
||||||
|
|
||||||
|
`ifdef HAZARD3_ASSERTIONS
|
||||||
|
`ifndef RISCV_FORMAL
|
||||||
|
// Really we're just interested in the shifts and comparisons, as these are
|
||||||
|
// the nontrivial ones. However, easier to test everything!
|
||||||
|
|
||||||
|
wire clk;
|
||||||
|
always @ (posedge clk) begin
|
||||||
|
case(aluop)
|
||||||
|
default: begin end
|
||||||
|
ALUOP_ADD: assert(result == op_a + op_b);
|
||||||
|
ALUOP_SUB: assert(result == op_a - op_b);
|
||||||
|
ALUOP_LT: assert(result == $signed(op_a) < $signed(op_b));
|
||||||
|
ALUOP_LTU: assert(result == op_a < op_b);
|
||||||
|
ALUOP_AND: assert(result == (op_a & op_b));
|
||||||
|
ALUOP_OR: assert(result == (op_a | op_b));
|
||||||
|
ALUOP_XOR: assert(result == (op_a ^ op_b));
|
||||||
|
ALUOP_SRL: assert(result == op_a >> op_b[4:0]);
|
||||||
|
ALUOP_SRA: assert($signed(result) == $signed(op_a) >>> $signed(op_b[4:0]));
|
||||||
|
ALUOP_SLL: assert(result == op_a << op_b[4:0]);
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
`endif
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// The branch decision path through the ALU is slow because:
|
||||||
|
//
|
||||||
|
// - Sees immediates and PC on its inputs, as well as regs
|
||||||
|
// - Add/sub rather than just add (with complex decode of the sub condition)
|
||||||
|
// - 2 extra mux layers in front of adder if Zba extension is enabled
|
||||||
|
//
|
||||||
|
// So there is sometimes timing benefit to a dedicated branch comparator.
|
||||||
|
|
||||||
|
module hazard3_branchcmp #(
|
||||||
|
`include "hazard3_config.vh"
|
||||||
|
,
|
||||||
|
`include "hazard3_width_const.vh"
|
||||||
|
) (
|
||||||
|
input wire [31:0] cir,
|
||||||
|
input wire [W_DATA-1:0] op_a,
|
||||||
|
input wire [W_DATA-1:0] op_b,
|
||||||
|
output wire cmp
|
||||||
|
);
|
||||||
|
|
||||||
|
`include "hazard3_ops.vh"
|
||||||
|
|
||||||
|
wire [W_DATA-1:0] diff = op_a - op_b;
|
||||||
|
|
||||||
|
// funct3 instruction
|
||||||
|
// ------------------
|
||||||
|
// 000 BEQ
|
||||||
|
// 001 BNE
|
||||||
|
// 100 BLT
|
||||||
|
// 101 BGE
|
||||||
|
// 110 BLTU
|
||||||
|
// 111 BGEU
|
||||||
|
|
||||||
|
wire cmp_is_unsigned = cir[13];
|
||||||
|
|
||||||
|
wire lt = op_a[W_DATA-1] == op_b[W_DATA-1] ? diff[W_DATA-1] :
|
||||||
|
cmp_is_unsigned ? op_b[W_DATA-1] :
|
||||||
|
op_a[W_DATA-1] ;
|
||||||
|
|
||||||
|
assign cmp = cir[14] ? lt : op_a != op_b;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// MUL-only (cfg: MUL_FAST) and MUL/MULH/MULHU/MULHSU (cfg: MUL_FAST &&
|
||||||
|
// MULH_FAST) are handled by different circuits. In either case it's a simple
|
||||||
|
// behavioural multiply, and we rely on inference to get good performance on
|
||||||
|
// FPGA.
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_mul_fast #(
|
||||||
|
`include "hazard3_config.vh"
|
||||||
|
,
|
||||||
|
`include "hazard3_width_const.vh"
|
||||||
|
) (
|
||||||
|
input wire clk,
|
||||||
|
input wire rst_n,
|
||||||
|
|
||||||
|
input wire [W_MULOP-1:0] op,
|
||||||
|
input wire op_vld,
|
||||||
|
input wire [W_DATA-1:0] op_a,
|
||||||
|
input wire [W_DATA-1:0] op_b,
|
||||||
|
|
||||||
|
output wire [W_DATA-1:0] result,
|
||||||
|
output reg result_vld
|
||||||
|
);
|
||||||
|
|
||||||
|
`include "hazard3_ops.vh"
|
||||||
|
|
||||||
|
localparam XLEN = W_DATA;
|
||||||
|
|
||||||
|
//synthesis translate_off
|
||||||
|
generate if (MULH_FAST && !MUL_FAST) begin: err_require_mul_fast_for_mulh
|
||||||
|
initial $fatal("%m: MULH_FAST requires that MUL_FAST is also set.");
|
||||||
|
end endgenerate
|
||||||
|
generate if (MUL_FASTER && !MUL_FAST) begin: err_require_mul_fast_for_faster
|
||||||
|
initial $fatal("%m: MUL_FASTER requires that MUL_FAST is also set.");
|
||||||
|
end endgenerate
|
||||||
|
//synthesis translate_on
|
||||||
|
|
||||||
|
// Latency of 1:
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
result_vld <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
result_vld <= op_vld;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Fast MUL only
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (!MULH_FAST) begin: mul_only
|
||||||
|
|
||||||
|
// This pipestage is folded into the front of the DSP tiles on UP5k. Note the
|
||||||
|
// intention is to register the bypassed core regs at the end of X (since
|
||||||
|
// bypass is quite slow), then perform multiply combinatorially in stage M,
|
||||||
|
// and mux into MW result register.
|
||||||
|
|
||||||
|
reg [XLEN-1:0] op_a_r;
|
||||||
|
reg [XLEN-1:0] op_b_r;
|
||||||
|
|
||||||
|
if (MUL_FASTER) begin: op_passthrough
|
||||||
|
always @ (*) begin
|
||||||
|
op_a_r = op_a;
|
||||||
|
op_b_r = op_b;
|
||||||
|
end
|
||||||
|
end else begin: op_register
|
||||||
|
always @ (posedge clk) begin
|
||||||
|
if (op_vld) begin
|
||||||
|
op_a_r <= op_a;
|
||||||
|
op_b_r <= op_b;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// This should be inferred as 3 DSP tiles on UP5k:
|
||||||
|
//
|
||||||
|
// 1. Register then multiply a[15: 0] and b[15: 0]
|
||||||
|
// 2. Register then multiply a[31:16] and b[15: 0], then directly add output of 1
|
||||||
|
// 3. Register then multiply a[15: 0] and b[31:16], then directly add output of 2
|
||||||
|
//
|
||||||
|
// So there is quite a long path (1x 16-bit multiply, then 2x 16-bit add). On
|
||||||
|
// other platforms you may just end up with a pile of gates.
|
||||||
|
|
||||||
|
`ifndef RISCV_FORMAL_ALTOPS
|
||||||
|
|
||||||
|
assign result = op_a_r * op_b_r;
|
||||||
|
|
||||||
|
`else
|
||||||
|
|
||||||
|
// riscv-formal can use a simpler function, since it's just confirming the
|
||||||
|
// result is correctly hooked up.
|
||||||
|
assign result = result_vld ? (op_a_r + op_b_r) ^ 32'h5876063e : 32'hdeadbeef;
|
||||||
|
|
||||||
|
`endif
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Fast MUL/MULH/MULHU/MULHSU
|
||||||
|
|
||||||
|
end else begin: mul_and_mulh
|
||||||
|
|
||||||
|
reg [XLEN-1:0] op_a_r;
|
||||||
|
reg [XLEN-1:0] op_b_r;
|
||||||
|
reg [W_MULOP-1:0] op_r;
|
||||||
|
|
||||||
|
if (MUL_FASTER) begin: op_passthrough
|
||||||
|
always @ (*) begin
|
||||||
|
op_a_r = op_a;
|
||||||
|
op_b_r = op_b;
|
||||||
|
op_r = op;
|
||||||
|
end
|
||||||
|
end else begin: op_register
|
||||||
|
always @ (posedge clk) begin
|
||||||
|
if (op_vld) begin
|
||||||
|
op_a_r <= op_a;
|
||||||
|
op_b_r <= op_b;
|
||||||
|
op_r <= op;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
wire op_a_signed = op_r == M_OP_MULH || op_r == M_OP_MULHSU;
|
||||||
|
wire op_b_signed = op_r == M_OP_MULH;
|
||||||
|
|
||||||
|
wire [2*XLEN-1:0] op_a_sext = {
|
||||||
|
{XLEN{op_a_r[XLEN - 1] && op_a_signed}},
|
||||||
|
op_a_r
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [2*XLEN-1:0] op_b_sext = {
|
||||||
|
{XLEN{op_b_r[XLEN - 1] && op_b_signed}},
|
||||||
|
op_b_r
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [2*XLEN-1:0] result_full = op_a_sext * op_b_sext;
|
||||||
|
|
||||||
|
`ifndef RISCV_FORMAL_ALTOPS
|
||||||
|
|
||||||
|
assign result = op_r == M_OP_MUL ? result_full[0 +: XLEN] : result_full[XLEN +: XLEN];
|
||||||
|
|
||||||
|
`else
|
||||||
|
|
||||||
|
assign result =
|
||||||
|
op_r == M_OP_MULH ? (op_a_r + op_b_r) ^ 32'hf6583fb7 :
|
||||||
|
op_r == M_OP_MULHSU ? (op_a_r - op_b_r) ^ 32'hecfbe137 :
|
||||||
|
op_r == M_OP_MULHU ? (op_a_r + op_b_r) ^ 32'h949ce5e8 :
|
||||||
|
op_r == M_OP_MUL ? (op_a_r + op_b_r) ^ 32'h5876063e : 32'hdeadbeef;
|
||||||
|
|
||||||
|
`endif
|
||||||
|
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// Combined multiply/divide/modulo circuit. All operations performed at 1 bit
|
||||||
|
// per clock; aiming for minimal resource usage on iCE40 FPGA. Optionally the
|
||||||
|
// circuit can be unrolled for slightly higher performance.
|
||||||
|
//
|
||||||
|
// When op_kill is high, the current calculation halts immediately. op_vld can
|
||||||
|
// be asserted on the same cycle, and the new calculation begins without
|
||||||
|
// delay, regardless of op_rdy. This may be used by the processor on e.g.
|
||||||
|
// mispredict or trap.
|
||||||
|
//
|
||||||
|
// The actual multiply/divide hardware is unsigned. We handle signedness at
|
||||||
|
// input/output.
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_muldiv_seq #(
|
||||||
|
`include "hazard3_config.vh"
|
||||||
|
,
|
||||||
|
`include "hazard3_width_const.vh"
|
||||||
|
) (
|
||||||
|
input wire clk,
|
||||||
|
input wire rst_n,
|
||||||
|
input wire [W_MULOP-1:0] op,
|
||||||
|
input wire op_vld,
|
||||||
|
output wire op_rdy,
|
||||||
|
input wire op_kill,
|
||||||
|
input wire [W_DATA-1:0] op_a,
|
||||||
|
input wire [W_DATA-1:0] op_b,
|
||||||
|
|
||||||
|
output wire [W_DATA-1:0] result_h, // mulh* or rem*
|
||||||
|
output wire [W_DATA-1:0] result_l, // mul or div*
|
||||||
|
output wire result_vld
|
||||||
|
);
|
||||||
|
|
||||||
|
`include "hazard3_ops.vh"
|
||||||
|
|
||||||
|
//synthesis translate_off
|
||||||
|
generate if (|(MULDIV_UNROLL & (MULDIV_UNROLL - 1)) || ~|MULDIV_UNROLL) begin: err_pow2
|
||||||
|
initial $fatal("%m: MULDIV_UNROLL must be a positive power of 2");
|
||||||
|
end endgenerate
|
||||||
|
//synthesis translate_on
|
||||||
|
|
||||||
|
localparam XLEN = W_DATA;
|
||||||
|
parameter W_CTR = $clog2(XLEN + 1);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Operation decode, operand sign adjustment
|
||||||
|
|
||||||
|
// On the first cycle, op_a and op_b go straight through to the accumulator
|
||||||
|
// and the divisor/multiplicand register. They are then adjusted in-place
|
||||||
|
// on the next cycle. This allows the same circuits to be reused for sign
|
||||||
|
// adjustment before output (and helps input timing).
|
||||||
|
|
||||||
|
reg [W_MULOP-1:0] op_r;
|
||||||
|
reg [2*XLEN-1:0] accum;
|
||||||
|
reg [XLEN-1:0] op_b_r;
|
||||||
|
reg op_a_neg_r;
|
||||||
|
reg op_b_neg_r;
|
||||||
|
|
||||||
|
wire op_a_signed =
|
||||||
|
op_r == M_OP_MULH ||
|
||||||
|
op_r == M_OP_MULHSU ||
|
||||||
|
op_r == M_OP_DIV ||
|
||||||
|
op_r == M_OP_REM;
|
||||||
|
|
||||||
|
wire op_b_signed =
|
||||||
|
op_r == M_OP_MULH ||
|
||||||
|
op_r == M_OP_DIV ||
|
||||||
|
op_r == M_OP_REM;
|
||||||
|
|
||||||
|
wire op_a_neg = op_a_signed && accum[XLEN-1];
|
||||||
|
wire op_b_neg = op_b_signed && op_b_r[XLEN-1];
|
||||||
|
|
||||||
|
// Non-divide parts of the circuit should be constant-folded if all the MUL
|
||||||
|
// operations are handled by the fast multiplier
|
||||||
|
|
||||||
|
wire is_div = op_r[2] || (MUL_FAST && MULH_FAST);
|
||||||
|
|
||||||
|
// Controls for modifying sign of all/part of accumulator
|
||||||
|
wire accum_neg_l;
|
||||||
|
wire accum_inv_h;
|
||||||
|
wire accum_incr_h;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Arithmetic circuit
|
||||||
|
|
||||||
|
// Combinatorials:
|
||||||
|
reg [2*XLEN-1:0] accum_next;
|
||||||
|
reg [2*XLEN-1:0] addend;
|
||||||
|
reg [2*XLEN-1:0] shift_tmp;
|
||||||
|
reg [2*XLEN-1:0] addsub_tmp;
|
||||||
|
reg neg_l_borrow;
|
||||||
|
|
||||||
|
always @ (*) begin: alu
|
||||||
|
integer i;
|
||||||
|
// Multiply/divide iteration layers
|
||||||
|
accum_next = accum;
|
||||||
|
addend = {2*XLEN{1'b0}};
|
||||||
|
addsub_tmp = {2*XLEN{1'b0}};
|
||||||
|
neg_l_borrow = 1'b0;
|
||||||
|
for (i = 0; i < MULDIV_UNROLL; i = i + 1) begin
|
||||||
|
addend = {is_div && |op_b_r, op_b_r, {XLEN-1{1'b0}}};
|
||||||
|
shift_tmp = is_div ? accum_next : accum_next >> 1;
|
||||||
|
addsub_tmp = shift_tmp + addend;
|
||||||
|
accum_next = (is_div ? !addsub_tmp[2 * XLEN - 1] : accum_next[0]) ?
|
||||||
|
addsub_tmp : shift_tmp;
|
||||||
|
if (is_div)
|
||||||
|
accum_next = {accum_next[2*XLEN-2:0], !addsub_tmp[2 * XLEN - 1]};
|
||||||
|
end
|
||||||
|
// Alternative path for negation of all/part of accumulator
|
||||||
|
if (accum_neg_l)
|
||||||
|
{neg_l_borrow, accum_next[XLEN-1:0]} = {~accum[XLEN-1:0]} + 1'b1;
|
||||||
|
if (accum_incr_h || accum_inv_h)
|
||||||
|
accum_next[XLEN +: XLEN] = (accum[XLEN +: XLEN] ^ {XLEN{accum_inv_h}})
|
||||||
|
+ {{XLEN-1{1'b0}}, accum_incr_h};
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Main state machine
|
||||||
|
|
||||||
|
reg sign_preadj_done;
|
||||||
|
reg [W_CTR-1:0] ctr;
|
||||||
|
reg sign_postadj_done;
|
||||||
|
reg sign_postadj_carry;
|
||||||
|
|
||||||
|
localparam CTR_TOP = XLEN[W_CTR-1:0];
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
ctr <= {W_CTR{1'b0}};
|
||||||
|
sign_preadj_done <= 1'b1;
|
||||||
|
sign_postadj_done <= 1'b1;
|
||||||
|
sign_postadj_carry <= 1'b0;
|
||||||
|
op_r <= {W_MULOP{1'b0}};
|
||||||
|
op_a_neg_r <= 1'b0;
|
||||||
|
op_b_neg_r <= 1'b0;
|
||||||
|
op_b_r <= {XLEN{1'b0}};
|
||||||
|
accum <= {XLEN*2{1'b0}};
|
||||||
|
end else if (op_kill || (op_vld && op_rdy)) begin
|
||||||
|
// Initialise circuit with operands + state
|
||||||
|
ctr <= op_vld ? CTR_TOP : {W_CTR{1'b0}};
|
||||||
|
sign_preadj_done <= !op_vld;
|
||||||
|
sign_postadj_done <= !op_vld;
|
||||||
|
sign_postadj_carry <= 1'b0;
|
||||||
|
op_r <= op;
|
||||||
|
op_b_r <= op_b;
|
||||||
|
accum <= {{XLEN{1'b0}}, op_a};
|
||||||
|
end else if (!sign_preadj_done) begin
|
||||||
|
// Pre-adjust sign if necessary, else perform first iteration immediately
|
||||||
|
op_a_neg_r <= op_a_neg;
|
||||||
|
op_b_neg_r <= op_b_neg;
|
||||||
|
sign_preadj_done <= 1'b1;
|
||||||
|
if (accum_neg_l || (op_b_neg ^ is_div)) begin
|
||||||
|
if (accum_neg_l)
|
||||||
|
accum[0 +: XLEN] <= accum_next[0 +: XLEN];
|
||||||
|
if (op_b_neg ^ is_div)
|
||||||
|
op_b_r <= -op_b_r;
|
||||||
|
end else begin
|
||||||
|
ctr <= ctr - MULDIV_UNROLL[W_CTR-1:0];
|
||||||
|
accum <= accum_next;
|
||||||
|
end
|
||||||
|
end else if (|ctr) begin
|
||||||
|
ctr <= ctr - MULDIV_UNROLL[W_CTR-1:0];
|
||||||
|
accum <= accum_next;
|
||||||
|
end else if (!sign_postadj_done || sign_postadj_carry) begin
|
||||||
|
sign_postadj_done <= 1'b1;
|
||||||
|
if (accum_inv_h || accum_incr_h)
|
||||||
|
accum[XLEN +: XLEN] <= accum_next[XLEN +: XLEN];
|
||||||
|
if (accum_neg_l) begin
|
||||||
|
accum[0 +: XLEN] <= accum_next[0 +: XLEN];
|
||||||
|
if (!is_div) begin
|
||||||
|
sign_postadj_carry <= neg_l_borrow;
|
||||||
|
sign_postadj_done <= !neg_l_borrow;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Sign adjustment control
|
||||||
|
|
||||||
|
// Pre-adjustment: for any a, b we want |a|, |b|. Note that the magnitude of any
|
||||||
|
// 32-bit signed integer is representable by a 32-bit unsigned integer.
|
||||||
|
|
||||||
|
// Post-adjustment for division:
|
||||||
|
// We seek q, r to satisfy a = b * q + r, where a and b are given,
|
||||||
|
// and |r| < |b|. One way to do this is if
|
||||||
|
// sgn(r) = sgn(a)
|
||||||
|
// sgn(q) = sgn(a) ^ sgn(b)
|
||||||
|
// This has additional nice properties like
|
||||||
|
// -(a / b) = (-a) / b = a / (-b)
|
||||||
|
|
||||||
|
// Post-adjustment for multiplication:
|
||||||
|
// We have calculated the 2*XLEN result of |a| * |b|.
|
||||||
|
// Negate the entire accumulator if sgn(a) ^ sgn(b).
|
||||||
|
// This is done in two steps (to share div/mod circuit, and avoid 64-bit carry):
|
||||||
|
// - Negate lower half of accumulator, and invert upper half
|
||||||
|
// - Increment upper half if lower half carried
|
||||||
|
|
||||||
|
wire do_postadj = ~|{ctr, sign_postadj_done};
|
||||||
|
wire op_signs_differ = op_a_neg_r ^ op_b_neg_r;
|
||||||
|
|
||||||
|
assign accum_neg_l =
|
||||||
|
!sign_preadj_done && op_a_neg ||
|
||||||
|
do_postadj && !sign_postadj_carry && op_signs_differ && !(is_div && ~|op_b_r);
|
||||||
|
|
||||||
|
assign {accum_incr_h, accum_inv_h} =
|
||||||
|
do_postadj && is_div && op_a_neg_r ? 2'b11 :
|
||||||
|
do_postadj && !is_div && op_signs_differ && !sign_postadj_carry ? 2'b01 :
|
||||||
|
do_postadj && !is_div && op_signs_differ && sign_postadj_carry ? 2'b10 :
|
||||||
|
2'b00 ;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Outputs
|
||||||
|
|
||||||
|
assign op_rdy = ~|{ctr, accum_neg_l, accum_incr_h, accum_inv_h};
|
||||||
|
assign result_vld = op_rdy;
|
||||||
|
|
||||||
|
`ifndef RISCV_FORMAL_ALTOPS
|
||||||
|
|
||||||
|
assign {result_h, result_l} = accum;
|
||||||
|
|
||||||
|
`else
|
||||||
|
|
||||||
|
// Provide arithmetically simpler alternative operations, to speed up formal checks
|
||||||
|
always assert(XLEN == 32);
|
||||||
|
|
||||||
|
reg [XLEN-1:0] fml_a_saved;
|
||||||
|
reg [XLEN-1:0] fml_b_saved;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
fml_a_saved <= {XLEN{1'b0}};
|
||||||
|
fml_b_saved <= {XLEN{1'b0}};
|
||||||
|
end else if (op_vld && op_rdy) begin
|
||||||
|
fml_a_saved <= op_a;
|
||||||
|
fml_b_saved <= op_b;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign result_h =
|
||||||
|
op_r == M_OP_MULH ? (fml_a_saved + fml_b_saved) ^ 32'hf6583fb7 :
|
||||||
|
op_r == M_OP_MULHSU ? (fml_a_saved - fml_b_saved) ^ 32'hecfbe137 :
|
||||||
|
op_r == M_OP_MULHU ? (fml_a_saved + fml_b_saved) ^ 32'h949ce5e8 :
|
||||||
|
op_r == M_OP_REM ? (fml_a_saved - fml_b_saved) ^ 32'h8da68fa5 :
|
||||||
|
op_r == M_OP_REMU ? (fml_a_saved - fml_b_saved) ^ 32'h3138d0e1 : 32'hdeadbeef;
|
||||||
|
|
||||||
|
assign result_l =
|
||||||
|
op_r == M_OP_MUL ? (fml_a_saved + fml_b_saved) ^ 32'h5876063e :
|
||||||
|
op_r == M_OP_DIV ? (fml_a_saved - fml_b_saved) ^ 32'h7f8529ec :
|
||||||
|
op_r == M_OP_DIVU ? (fml_a_saved - fml_b_saved) ^ 32'h10e8fd70 : 32'hdeadbeef;
|
||||||
|
|
||||||
|
`endif
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Interface properties
|
||||||
|
|
||||||
|
`ifdef HAZARD3_ASSERTIONS
|
||||||
|
|
||||||
|
always @ (posedge clk) if (rst_n && $past(rst_n)) begin: properties
|
||||||
|
integer i;
|
||||||
|
reg alive;
|
||||||
|
|
||||||
|
if ($past(op_rdy && !op_vld))
|
||||||
|
assert(op_rdy);
|
||||||
|
|
||||||
|
if (result_vld && $past(result_vld) && !$past(op_kill))
|
||||||
|
assert($stable({result_h, result_l}));
|
||||||
|
|
||||||
|
// Kill will halt an in-progress operation, but a new operation may be
|
||||||
|
// asserted simultaneously with kill.
|
||||||
|
if ($past(op_kill))
|
||||||
|
assert(op_rdy == !$past(op_vld));
|
||||||
|
|
||||||
|
// We should be periodically ready (liveness property), unless new operations
|
||||||
|
// are forced in immediately, simultaneous with a kill, in which case there
|
||||||
|
// is no intermediate ready state.
|
||||||
|
alive = op_rdy || (op_kill && op_vld);
|
||||||
|
for (i = 1; i <= XLEN / MULDIV_UNROLL + 3; i = i + 1)
|
||||||
|
alive = alive || $past(op_rdy || (op_kill && op_vld), i);
|
||||||
|
assert(alive);
|
||||||
|
end
|
||||||
|
|
||||||
|
`endif
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// req: one-hot bitmap
|
||||||
|
// idx: index of the sole set bit in req
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_onehot_encode #(
|
||||||
|
parameter W_REQ = 16,
|
||||||
|
parameter W_GNT = $clog2(W_REQ) // do not modify
|
||||||
|
) (
|
||||||
|
input wire [W_REQ-1:0] req,
|
||||||
|
output reg [W_GNT-1:0] gnt
|
||||||
|
);
|
||||||
|
|
||||||
|
always @ (*) begin: encode
|
||||||
|
reg [W_GNT:0] i;
|
||||||
|
gnt = {W_GNT{1'b0}};
|
||||||
|
for (i = 0; i < W_REQ; i = i + 1) begin
|
||||||
|
gnt = gnt | ({W_GNT{req[i[W_GNT-1:0]]}} & i[W_GNT-1:0]);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// req: bitmap
|
||||||
|
// idx: bitmap with all bits clear except the least- (HIGHEST_WINS=0) or
|
||||||
|
// most- (HIGHEST_WINS=1) significant set bit in req.
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_onehot_priority #(
|
||||||
|
parameter W_REQ = 16,
|
||||||
|
parameter HIGHEST_WINS = 0
|
||||||
|
) (
|
||||||
|
input wire [W_REQ-1:0] req,
|
||||||
|
output reg [W_REQ-1:0] gnt
|
||||||
|
);
|
||||||
|
|
||||||
|
always @ (*) begin: select
|
||||||
|
integer i;
|
||||||
|
for (i = 0; i < W_REQ; i = i + 1) begin
|
||||||
|
gnt[i] = req[i] && ~|(req & (
|
||||||
|
HIGHEST_WINS ? ~({W_REQ{1'b1}} >> (W_REQ - 1 - i)) : ~({W_REQ{1'b1}} << i)
|
||||||
|
));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// req: bitmap of requests
|
||||||
|
// priority: packed array of dynamic priority level of each request
|
||||||
|
// gnt: one-hot bitmap with the highest-priority request.
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_onehot_priority_dynamic #(
|
||||||
|
parameter W_REQ = 8,
|
||||||
|
parameter N_PRIORITIES = 2,
|
||||||
|
parameter PRIORITY_HIGHEST_WINS = 1, // If 1, numerically highest level has greatest priority.
|
||||||
|
// Otherwise, numerically lowest wins.
|
||||||
|
parameter TIEBREAK_HIGHEST_WINS = 0, // If 1, highest-numbered request at the highest priority
|
||||||
|
// level wins the tiebreak. Otherwise, lowest-numbered.
|
||||||
|
// Do not modify:
|
||||||
|
parameter W_PRIORITY = $clog2(N_PRIORITIES)
|
||||||
|
) (
|
||||||
|
input wire [W_REQ*W_PRIORITY-1:0] pri,
|
||||||
|
input wire [W_REQ-1:0] req,
|
||||||
|
output wire [W_REQ-1:0] gnt
|
||||||
|
);
|
||||||
|
|
||||||
|
// 1. Stratify requests according to their level
|
||||||
|
reg [W_REQ-1:0] req_stratified [0:N_PRIORITIES-1];
|
||||||
|
reg [N_PRIORITIES-1:0] level_has_req;
|
||||||
|
|
||||||
|
always @ (*) begin: stratify
|
||||||
|
reg signed [31:0] i, j;
|
||||||
|
for (i = 0; i < N_PRIORITIES; i = i + 1) begin
|
||||||
|
for (j = 0; j < W_REQ; j = j + 1) begin
|
||||||
|
req_stratified[i][j] = req[j] &&
|
||||||
|
pri[W_PRIORITY * j +: W_PRIORITY] == i[W_PRIORITY-1:0];
|
||||||
|
end
|
||||||
|
level_has_req[i] = |req_stratified[i];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// 2. Select the highest level with active requests
|
||||||
|
wire [N_PRIORITIES-1:0] active_layer_sel;
|
||||||
|
|
||||||
|
hazard3_onehot_priority #(
|
||||||
|
.W_REQ (N_PRIORITIES),
|
||||||
|
.HIGHEST_WINS (PRIORITY_HIGHEST_WINS)
|
||||||
|
) prisel_layer (
|
||||||
|
.req (level_has_req),
|
||||||
|
.gnt (active_layer_sel)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. Mask only those requests at this level
|
||||||
|
reg [W_REQ-1:0] reqs_from_highest_layer;
|
||||||
|
|
||||||
|
always @ (*) begin: mux_reqs_by_layer
|
||||||
|
integer i;
|
||||||
|
reqs_from_highest_layer = {W_REQ{1'b0}};
|
||||||
|
for (i = 0; i < N_PRIORITIES; i = i + 1)
|
||||||
|
reqs_from_highest_layer = reqs_from_highest_layer |
|
||||||
|
(req_stratified[i] & {W_REQ{active_layer_sel[i]}});
|
||||||
|
end
|
||||||
|
|
||||||
|
// 4. Do a standard priority select on those requests as a tie break
|
||||||
|
hazard3_onehot_priority #(
|
||||||
|
.W_REQ (W_REQ),
|
||||||
|
.HIGHEST_WINS (TIEBREAK_HIGHEST_WINS)
|
||||||
|
) prisel_tiebreak (
|
||||||
|
.req (reqs_from_highest_layer),
|
||||||
|
.gnt (gnt)
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// req: bitmap
|
||||||
|
// gnt: index of least set bit (HIGHEST_WINS=0) or most set bit (HIGHEST_WINS=1)
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_priority_encode #(
|
||||||
|
parameter W_REQ = 16,
|
||||||
|
parameter HIGHEST_WINS = 0,
|
||||||
|
parameter W_GNT = $clog2(W_REQ) // do not modify
|
||||||
|
) (
|
||||||
|
input wire [W_REQ-1:0] req,
|
||||||
|
output wire [W_GNT-1:0] gnt
|
||||||
|
);
|
||||||
|
|
||||||
|
wire [W_REQ-1:0] gnt_onehot;
|
||||||
|
|
||||||
|
hazard3_onehot_priority #(
|
||||||
|
.W_REQ (W_REQ),
|
||||||
|
.HIGHEST_WINS (HIGHEST_WINS)
|
||||||
|
) priority_u (
|
||||||
|
.req (req),
|
||||||
|
.gnt (gnt_onehot)
|
||||||
|
);
|
||||||
|
|
||||||
|
hazard3_onehot_encode #(
|
||||||
|
.W_REQ (W_REQ)
|
||||||
|
) encode_u (
|
||||||
|
.req (gnt_onehot),
|
||||||
|
.gnt (gnt)
|
||||||
|
);
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// Implement the three shifts (left logical, right logical, right arithmetic)
|
||||||
|
// using a single log-type barrel shifter. Around 240 LUTs for 32 bits.
|
||||||
|
// (7 layers of 32 2-input muxes, some extra LUTs and LUT inputs used for arith)
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_shift_barrel #(
|
||||||
|
`include "hazard3_config.vh"
|
||||||
|
,
|
||||||
|
`include "hazard3_width_const.vh"
|
||||||
|
) (
|
||||||
|
input wire [W_DATA-1:0] din,
|
||||||
|
input wire [W_SHAMT-1:0] shamt,
|
||||||
|
input wire right_nleft,
|
||||||
|
input wire rotate,
|
||||||
|
input wire arith,
|
||||||
|
output reg [W_DATA-1:0] dout
|
||||||
|
);
|
||||||
|
|
||||||
|
reg [W_DATA-1:0] din_rev;
|
||||||
|
reg [W_DATA-1:0] shift_accum;
|
||||||
|
reg sext; // haha
|
||||||
|
|
||||||
|
always @ (*) begin: shift
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
for (i = 0; i < W_DATA; i = i + 1)
|
||||||
|
din_rev[i] = right_nleft ? din[W_DATA - 1 - i] : din[i];
|
||||||
|
|
||||||
|
sext = arith && din_rev[0];
|
||||||
|
|
||||||
|
shift_accum = din_rev;
|
||||||
|
for (i = 0; i < W_SHAMT; i = i + 1) begin
|
||||||
|
if (shamt[i]) begin
|
||||||
|
shift_accum = (shift_accum << (1 << i)) |
|
||||||
|
({W_DATA{sext}} & ~({W_DATA{1'b1}} << (1 << i))) |
|
||||||
|
({W_DATA{rotate && |EXTENSION_ZBB}} & (shift_accum >> (W_DATA - (1 << i))));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for (i = 0; i < W_DATA; i = i + 1)
|
||||||
|
dout[i] = right_nleft ? shift_accum[W_DATA - 1 - i] : shift_accum[i];
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifdef HAZARD3_ASSERTIONS
|
||||||
|
always @ (*) begin
|
||||||
|
if (right_nleft && arith && !rotate) begin: asr
|
||||||
|
assert($signed(dout) == $signed(din) >>> $signed(shamt));
|
||||||
|
end else if (right_nleft && !arith && !rotate) begin
|
||||||
|
assert(dout == din >> shamt);
|
||||||
|
end else if (!right_nleft && !arith && !rotate) begin
|
||||||
|
assert(dout == din << shamt);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Quick reference model for sequential unsigned multiply/divide/modulo
|
||||||
|
|
||||||
|
def div_step(w, accum, divisor):
|
||||||
|
sub_tmp = accum - (divisor << (w - 1))
|
||||||
|
underflow = sub_tmp < 0
|
||||||
|
if not underflow:
|
||||||
|
accum = sub_tmp
|
||||||
|
accum = (accum << 1) | (not underflow)
|
||||||
|
return accum
|
||||||
|
|
||||||
|
def divmod(w, dividend, divisor, debug=True):
|
||||||
|
accum = dividend
|
||||||
|
for i in range(w):
|
||||||
|
accum_prev = accum
|
||||||
|
accum = div_step(w, accum, divisor)
|
||||||
|
if debug:
|
||||||
|
print("Step {:02d}: accum {:0{}x} -> {:0{}x}".format(
|
||||||
|
i, accum_prev, int(w / 2), accum, int(w / 2)))
|
||||||
|
return (accum >> w, accum & ((1 << w) - 1))
|
||||||
|
|
||||||
|
def mul_step(w, accum, multiplicand):
|
||||||
|
add_en = accum & 1
|
||||||
|
accum = accum >> 1
|
||||||
|
if add_en:
|
||||||
|
accum += (multiplicand << (w - 1))
|
||||||
|
return accum
|
||||||
|
|
||||||
|
def mul(w, multiplicand, multiplier, debug=True):
|
||||||
|
accum = multiplier
|
||||||
|
for i in range(w):
|
||||||
|
accum_prev = accum
|
||||||
|
accum = mul_step(w, accum, multiplicand)
|
||||||
|
if debug:
|
||||||
|
print("Step {:02d}: accum {:0{}x} -> {:0{}x}".format(
|
||||||
|
i, accum_prev, int(w / 2), accum, int(w / 2)))
|
||||||
|
return (accum >> w, accum & ((1 << w) - 1))
|
||||||
|
|
||||||
|
def divtest(w=4):
|
||||||
|
for i in range(2 ** w):
|
||||||
|
for j in range(1, 2 ** w):
|
||||||
|
gatemod, gatediv = divmod(w, i, j, debug=False)
|
||||||
|
goldmod, golddiv = (i % j, i // j)
|
||||||
|
print("{:02d} % {:02d} = {:02d} (gold {:02d}); ./. = {:02d} (gold {:02d})"
|
||||||
|
.format(i, j, gatemod, goldmod, gatediv, golddiv))
|
||||||
|
assert(gatemod == goldmod)
|
||||||
|
assert(gatediv == golddiv)
|
||||||
|
|
||||||
|
def multest(w=4):
|
||||||
|
for i in range(2 ** w):
|
||||||
|
for j in range(2 ** w):
|
||||||
|
gateh, gatel = mul(w, i, j, debug=False)
|
||||||
|
gold = i * j
|
||||||
|
goldl, goldh = (gold & ((1 << w) - 1), gold >> w)
|
||||||
|
print("{:02d} * {:02d} = ({:02d} (gold {:02d}), {:02d} (gold {:02d})"
|
||||||
|
.format(i, j, gateh, goldh, gatel, goldl))
|
||||||
|
assert(gatel == goldl)
|
||||||
|
assert(gateh == goldh)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("Test division:")
|
||||||
|
divtest()
|
||||||
|
print("Test multiplication:")
|
||||||
|
multest()
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// APB-to-APB asynchronous bridge for connecting DTM to DM, in case DTM is in
|
||||||
|
// a different clock domain (e.g. running directly from crystal to get a
|
||||||
|
// fixed baud reference)
|
||||||
|
//
|
||||||
|
// Note this module depends on the hazard3_sync_1bit module (a flop-chain
|
||||||
|
// synchroniser) which should be reimplemented for your FPGA/process.
|
||||||
|
|
||||||
|
`ifndef HAZARD3_REG_KEEP_ATTRIBUTE
|
||||||
|
`define HAZARD3_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *)
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_apb_async_bridge #(
|
||||||
|
parameter W_ADDR = 8,
|
||||||
|
parameter W_DATA = 32,
|
||||||
|
parameter N_SYNC_STAGES = 2
|
||||||
|
) (
|
||||||
|
// Resets assumed to be synchronised externally
|
||||||
|
input wire clk_src,
|
||||||
|
input wire rst_n_src,
|
||||||
|
|
||||||
|
input wire clk_dst,
|
||||||
|
input wire rst_n_dst,
|
||||||
|
|
||||||
|
// APB port from Transport Module
|
||||||
|
input wire src_psel,
|
||||||
|
input wire src_penable,
|
||||||
|
input wire src_pwrite,
|
||||||
|
input wire [W_ADDR-1:0] src_paddr,
|
||||||
|
input wire [W_DATA-1:0] src_pwdata,
|
||||||
|
output wire [W_DATA-1:0] src_prdata,
|
||||||
|
output wire src_pready,
|
||||||
|
output wire src_pslverr,
|
||||||
|
|
||||||
|
// APB port to Debug Module
|
||||||
|
output wire dst_psel,
|
||||||
|
output wire dst_penable,
|
||||||
|
output wire dst_pwrite,
|
||||||
|
output wire [W_ADDR-1:0] dst_paddr,
|
||||||
|
output wire [W_DATA-1:0] dst_pwdata,
|
||||||
|
input wire [W_DATA-1:0] dst_prdata,
|
||||||
|
input wire dst_pready,
|
||||||
|
input wire dst_pslverr
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Clock-crossing registers
|
||||||
|
|
||||||
|
// We're using a modified req/ack handshake:
|
||||||
|
//
|
||||||
|
// - Initially both req and ack are low
|
||||||
|
// - src asserts req high
|
||||||
|
// - dst responds with ack high and begins transfer
|
||||||
|
// - src deasserts req once it sees ack high
|
||||||
|
// - dst deasserts ack once:
|
||||||
|
// - transfer is complete *and*
|
||||||
|
// - dst sees req deasserted
|
||||||
|
// - Once src sees ack low, a new transfer can begin.
|
||||||
|
//
|
||||||
|
// A NRZI toggle handshake might be more appropriate, but can cause spurious
|
||||||
|
// bus accesses when only one side of the link is reset.
|
||||||
|
|
||||||
|
`HAZARD3_REG_KEEP_ATTRIBUTE reg src_req;
|
||||||
|
wire dst_req;
|
||||||
|
|
||||||
|
`HAZARD3_REG_KEEP_ATTRIBUTE reg dst_ack;
|
||||||
|
wire src_ack;
|
||||||
|
|
||||||
|
// Note the launch registers are not resettable. We maintain setup/hold on
|
||||||
|
// launch-to-capture paths thanks to the req/ack handshake. A stray reset
|
||||||
|
// could violate this.
|
||||||
|
//
|
||||||
|
// The req/ack logic itself can be reset safely because the receiving domain
|
||||||
|
// is protected from metastability by a 2FF synchroniser.
|
||||||
|
|
||||||
|
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_ADDR + W_DATA + 1 -1:0] src_paddr_pwdata_pwrite; // launch
|
||||||
|
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_ADDR + W_DATA + 1 -1:0] dst_paddr_pwdata_pwrite; // capture
|
||||||
|
|
||||||
|
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_DATA + 1 -1:0] dst_prdata_pslverr; // launch
|
||||||
|
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_DATA + 1 -1:0] src_prdata_pslverr; // capture
|
||||||
|
|
||||||
|
hazard3_sync_1bit #(
|
||||||
|
.N_STAGES (N_SYNC_STAGES)
|
||||||
|
) sync_req (
|
||||||
|
.clk (clk_dst),
|
||||||
|
.rst_n (rst_n_dst),
|
||||||
|
.i (src_req),
|
||||||
|
.o (dst_req)
|
||||||
|
);
|
||||||
|
|
||||||
|
hazard3_sync_1bit #(
|
||||||
|
.N_STAGES (N_SYNC_STAGES)
|
||||||
|
) sync_ack (
|
||||||
|
.clk (clk_src),
|
||||||
|
.rst_n (rst_n_src),
|
||||||
|
.i (dst_ack),
|
||||||
|
.o (src_ack)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// src state machine
|
||||||
|
|
||||||
|
reg src_waiting_for_downstream;
|
||||||
|
reg src_pready_r;
|
||||||
|
|
||||||
|
always @ (posedge clk_src or negedge rst_n_src) begin
|
||||||
|
if (!rst_n_src) begin
|
||||||
|
src_req <= 1'b0;
|
||||||
|
src_waiting_for_downstream <= 1'b0;
|
||||||
|
src_prdata_pslverr <= {W_DATA + 1{1'b0}};
|
||||||
|
src_pready_r <= 1'b1;
|
||||||
|
end else if (src_waiting_for_downstream) begin
|
||||||
|
if (src_req && src_ack) begin
|
||||||
|
// Request was acknowledged, so deassert.
|
||||||
|
src_req <= 1'b0;
|
||||||
|
end else if (!(src_req || src_ack)) begin
|
||||||
|
// Downstream transfer has finished, data is valid.
|
||||||
|
src_pready_r <= 1'b1;
|
||||||
|
src_waiting_for_downstream <= 1'b0;
|
||||||
|
// Note this assignment is cross-domain (but data has been stable
|
||||||
|
// for duration of ack synchronisation delay):
|
||||||
|
src_prdata_pslverr <= dst_prdata_pslverr;
|
||||||
|
end
|
||||||
|
end else begin
|
||||||
|
// paddr, pwdata and pwrite are all valid during the setup phase, and
|
||||||
|
// APB defines the setup phase to always last one cycle and proceed
|
||||||
|
// to access phase. So, we can ignore penable, and pready is ignored.
|
||||||
|
if (src_psel) begin
|
||||||
|
src_pready_r <= 1'b0;
|
||||||
|
src_req <= 1'b1;
|
||||||
|
src_waiting_for_downstream <= 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Bus request launch register is not resettable
|
||||||
|
always @ (posedge clk_src) begin
|
||||||
|
if (src_psel && !src_waiting_for_downstream)
|
||||||
|
src_paddr_pwdata_pwrite <= {src_paddr, src_pwdata, src_pwrite};
|
||||||
|
end
|
||||||
|
|
||||||
|
assign {src_prdata, src_pslverr} = src_prdata_pslverr;
|
||||||
|
assign src_pready = src_pready_r;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// dst state machine
|
||||||
|
|
||||||
|
wire dst_bus_finish = dst_penable && dst_pready;
|
||||||
|
reg dst_psel_r;
|
||||||
|
reg dst_penable_r;
|
||||||
|
|
||||||
|
always @ (posedge clk_dst or negedge rst_n_dst) begin
|
||||||
|
if (!rst_n_dst) begin
|
||||||
|
dst_ack <= 1'b0;
|
||||||
|
end else if (dst_req) begin
|
||||||
|
dst_ack <= 1'b1;
|
||||||
|
end else if (!dst_req && dst_ack && !dst_psel_r) begin
|
||||||
|
dst_ack <= 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @ (posedge clk_dst or negedge rst_n_dst) begin
|
||||||
|
if (!rst_n_dst) begin
|
||||||
|
dst_psel_r <= 1'b0;
|
||||||
|
dst_penable_r <= 1'b0;
|
||||||
|
dst_paddr_pwdata_pwrite <= {W_ADDR + W_DATA + 1{1'b0}};
|
||||||
|
end else if (dst_req && !dst_ack) begin
|
||||||
|
dst_psel_r <= 1'b1;
|
||||||
|
// Note this assignment is cross-domain. The src register has been
|
||||||
|
// stable for the duration of the req sync delay.
|
||||||
|
dst_paddr_pwdata_pwrite <= src_paddr_pwdata_pwrite;
|
||||||
|
end else if (dst_psel_r && !dst_penable_r) begin
|
||||||
|
dst_penable_r <= 1'b1;
|
||||||
|
end else if (dst_bus_finish) begin
|
||||||
|
dst_psel_r <= 1'b0;
|
||||||
|
dst_penable_r <= 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Bus response launch register is not resettable
|
||||||
|
always @ (posedge clk_dst) begin
|
||||||
|
if (dst_bus_finish)
|
||||||
|
dst_prdata_pslverr <= {dst_prdata, dst_pslverr};
|
||||||
|
end
|
||||||
|
|
||||||
|
assign dst_psel = dst_psel_r;
|
||||||
|
assign dst_penable = dst_penable_r;
|
||||||
|
assign {dst_paddr, dst_pwdata, dst_pwrite} = dst_paddr_pwdata_pwrite;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// The output is asserted asynchronously when the input is asserted,
|
||||||
|
// but deasserted synchronously when clocked with the input deasserted.
|
||||||
|
// Input and output are both active-low.
|
||||||
|
//
|
||||||
|
// This is a baseline implementation -- you should replace it with cells
|
||||||
|
// specific to your FPGA/process
|
||||||
|
|
||||||
|
`ifndef HAZARD3_REG_KEEP_ATTRIBUTE
|
||||||
|
`define HAZARD3_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *)
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_reset_sync #(
|
||||||
|
parameter N_STAGES = 2 // Should be >= 2
|
||||||
|
) (
|
||||||
|
input wire clk,
|
||||||
|
input wire rst_n_in,
|
||||||
|
output wire rst_n_out
|
||||||
|
);
|
||||||
|
|
||||||
|
`HAZARD3_REG_KEEP_ATTRIBUTE reg [N_STAGES-1:0] delay;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n_in)
|
||||||
|
if (!rst_n_in)
|
||||||
|
delay <= {N_STAGES{1'b0}};
|
||||||
|
else
|
||||||
|
delay <= {delay[N_STAGES-2:0], 1'b1};
|
||||||
|
|
||||||
|
assign rst_n_out = delay[N_STAGES-1];
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// A 2FF synchronizer to mitigate metastabilities. This is a baseline
|
||||||
|
// implementation -- you should replace it with cells specific to your
|
||||||
|
// FPGA/process
|
||||||
|
|
||||||
|
`ifndef HAZARD3_REG_KEEP_ATTRIBUTE
|
||||||
|
`define HAZARD3_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *) (* async_reg *)
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_sync_1bit #(
|
||||||
|
parameter N_STAGES = 2 // Should be >=2
|
||||||
|
) (
|
||||||
|
input wire clk,
|
||||||
|
input wire rst_n,
|
||||||
|
input wire i,
|
||||||
|
output wire o
|
||||||
|
);
|
||||||
|
|
||||||
|
`HAZARD3_REG_KEEP_ATTRIBUTE reg [N_STAGES-1:0] sync_flops;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n)
|
||||||
|
if (!rst_n)
|
||||||
|
sync_flops <= {N_STAGES{1'b0}};
|
||||||
|
else
|
||||||
|
sync_flops <= {sync_flops[N_STAGES-2:0], i};
|
||||||
|
|
||||||
|
assign o = sync_flops[N_STAGES-1];
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
file hazard3_dm.v
|
||||||
@@ -0,0 +1,904 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// RISC-V Debug Module for Hazard3. Supports up to 32 cores (1 hart per core).
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_dm #(
|
||||||
|
// Where there are multiple harts per DM, the least-indexed hart is the
|
||||||
|
// least-significant on each concatenated hart access bus.
|
||||||
|
parameter N_HARTS = 1,
|
||||||
|
// Where there are multiple DMs, the address of each DM should be a
|
||||||
|
// multiple of 'h200, so that bits[8:2] decode correctly.
|
||||||
|
parameter NEXT_DM_ADDR = 32'h0000_0000,
|
||||||
|
// Implement support for system bus access:
|
||||||
|
parameter HAVE_SBA = 0,
|
||||||
|
|
||||||
|
// Do not modify:
|
||||||
|
parameter XLEN = 32, // Do not modify
|
||||||
|
parameter W_HARTSEL = N_HARTS > 1 ? $clog2(N_HARTS) : 1 // Do not modify
|
||||||
|
) (
|
||||||
|
// DM is assumed to be in same clock domain as core; clock crossing
|
||||||
|
// (if any) is inside DTM, or between DTM and DM.
|
||||||
|
input wire clk,
|
||||||
|
input wire rst_n,
|
||||||
|
|
||||||
|
// APB access from Debug Transport Module
|
||||||
|
input wire dmi_psel,
|
||||||
|
input wire dmi_penable,
|
||||||
|
input wire dmi_pwrite,
|
||||||
|
input wire [8:0] dmi_paddr,
|
||||||
|
input wire [31:0] dmi_pwdata,
|
||||||
|
output reg [31:0] dmi_prdata,
|
||||||
|
output wire dmi_pready,
|
||||||
|
output wire dmi_pslverr,
|
||||||
|
|
||||||
|
// Reset request/acknowledge. "req" is a pulse >= 1 cycle wide. "done" is
|
||||||
|
// level-sensitive, goes high once component is out of reset.
|
||||||
|
//
|
||||||
|
// The "sys" reset (ndmreset) is conventionally everything apart from DM +
|
||||||
|
// DTM, but, as per section 3.2 in 0.13.2 debug spec: "Exactly what is
|
||||||
|
// affected by this reset is implementation dependent, as long as it is
|
||||||
|
// possible to debug programs from the first instruction executed." So
|
||||||
|
// this could simply be an all-hart reset.
|
||||||
|
output wire sys_reset_req,
|
||||||
|
input wire sys_reset_done,
|
||||||
|
output wire [N_HARTS-1:0] hart_reset_req,
|
||||||
|
input wire [N_HARTS-1:0] hart_reset_done,
|
||||||
|
|
||||||
|
// Hart run/halt control
|
||||||
|
output wire [N_HARTS-1:0] hart_req_halt,
|
||||||
|
output wire [N_HARTS-1:0] hart_req_halt_on_reset,
|
||||||
|
output wire [N_HARTS-1:0] hart_req_resume,
|
||||||
|
input wire [N_HARTS-1:0] hart_halted,
|
||||||
|
input wire [N_HARTS-1:0] hart_running,
|
||||||
|
|
||||||
|
// Hart access to data0 CSR (assumed to be core-internal but per-hart)
|
||||||
|
output wire [N_HARTS*XLEN-1:0] hart_data0_rdata,
|
||||||
|
input wire [N_HARTS*XLEN-1:0] hart_data0_wdata,
|
||||||
|
input wire [N_HARTS-1:0] hart_data0_wen,
|
||||||
|
|
||||||
|
// Hart instruction injection
|
||||||
|
output wire [N_HARTS*32-1:0] hart_instr_data,
|
||||||
|
output reg [N_HARTS-1:0] hart_instr_data_vld,
|
||||||
|
input wire [N_HARTS-1:0] hart_instr_data_rdy,
|
||||||
|
input wire [N_HARTS-1:0] hart_instr_caught_exception,
|
||||||
|
input wire [N_HARTS-1:0] hart_instr_caught_ebreak,
|
||||||
|
|
||||||
|
// System bus access (optional) -- can be hooked up to the standalone AHB
|
||||||
|
// shim (hazard3_sbus_to_ahb.v) or the SBA input port on the processor
|
||||||
|
// wrapper, which muxes SBA into the processor's load/store bus access
|
||||||
|
// port. SBA does not increase debugger bus throughput, but supports
|
||||||
|
// minimally intrusive debug bus access for e.g. Segger RTT.
|
||||||
|
output wire [31:0] sbus_addr,
|
||||||
|
output wire sbus_write,
|
||||||
|
output wire [1:0] sbus_size,
|
||||||
|
output wire sbus_vld,
|
||||||
|
input wire sbus_rdy,
|
||||||
|
input wire sbus_err,
|
||||||
|
output wire [31:0] sbus_wdata,
|
||||||
|
input wire [31:0] sbus_rdata
|
||||||
|
);
|
||||||
|
|
||||||
|
wire dmi_write = dmi_psel && dmi_penable && dmi_pready && dmi_pwrite;
|
||||||
|
wire dmi_read = dmi_psel && dmi_penable && dmi_pready && !dmi_pwrite;
|
||||||
|
assign dmi_pready = 1'b1;
|
||||||
|
assign dmi_pslverr = 1'b0;
|
||||||
|
|
||||||
|
// Program buffer is fixed at 2 words plus impebreak. The main thing we care
|
||||||
|
// about is support for efficient memory block transfers using abstractauto;
|
||||||
|
// in this case 2 words + impebreak is sufficient for RV32I, and 1 word +
|
||||||
|
// impebreak is sufficient for RV32IC.
|
||||||
|
localparam PROGBUF_SIZE = 2;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Address constants
|
||||||
|
|
||||||
|
localparam ADDR_DATA0 = 7'h04;
|
||||||
|
// Other data registers not present.
|
||||||
|
localparam ADDR_DMCONTROL = 7'h10;
|
||||||
|
localparam ADDR_DMSTATUS = 7'h11;
|
||||||
|
localparam ADDR_HARTINFO = 7'h12;
|
||||||
|
localparam ADDR_HALTSUM1 = 7'h13;
|
||||||
|
localparam ADDR_HALTSUM0 = 7'h40;
|
||||||
|
// No HALTSUM2+ registers (we don't support >32 harts anyway)
|
||||||
|
localparam ADDR_HAWINDOWSEL = 7'h14;
|
||||||
|
localparam ADDR_HAWINDOW = 7'h15;
|
||||||
|
localparam ADDR_ABSTRACTCS = 7'h16;
|
||||||
|
localparam ADDR_COMMAND = 7'h17;
|
||||||
|
localparam ADDR_ABSTRACTAUTO = 7'h18;
|
||||||
|
localparam ADDR_CONFSTRPTR0 = 7'h19;
|
||||||
|
localparam ADDR_CONFSTRPTR1 = 7'h1a;
|
||||||
|
localparam ADDR_CONFSTRPTR2 = 7'h1b;
|
||||||
|
localparam ADDR_CONFSTRPTR3 = 7'h1c;
|
||||||
|
localparam ADDR_NEXTDM = 7'h1d;
|
||||||
|
localparam ADDR_PROGBUF0 = 7'h20;
|
||||||
|
localparam ADDR_PROGBUF1 = 7'h21;
|
||||||
|
// No authentication
|
||||||
|
localparam ADDR_SBCS = 7'h38;
|
||||||
|
localparam ADDR_SBADDRESS0 = 7'h39;
|
||||||
|
localparam ADDR_SBDATA0 = 7'h3c;
|
||||||
|
|
||||||
|
// APB is byte-addressed, DM registers are word-addressed.
|
||||||
|
wire [6:0] dmi_regaddr = dmi_paddr[8:2];
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Hart selection
|
||||||
|
|
||||||
|
reg dmactive;
|
||||||
|
|
||||||
|
// Some fiddliness to make sure we get a single-wide zero-valued signal when
|
||||||
|
// N_HARTS == 1 (so we can use this for indexing of per-hart signals)
|
||||||
|
reg [W_HARTSEL-1:0] hartsel;
|
||||||
|
wire [W_HARTSEL-1:0] hartsel_next;
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (N_HARTS > 1) begin: has_hartsel
|
||||||
|
|
||||||
|
// Only the lower 10 bits of hartsel are supported
|
||||||
|
assign hartsel_next = dmi_write && dmi_regaddr == ADDR_DMCONTROL ?
|
||||||
|
dmi_pwdata[16 +: W_HARTSEL] : hartsel;
|
||||||
|
|
||||||
|
end else begin: has_no_hartsel
|
||||||
|
|
||||||
|
assign hartsel_next = 1'b0;
|
||||||
|
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
hartsel <= {W_HARTSEL{1'b0}};
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
hartsel <= {W_HARTSEL{1'b0}};
|
||||||
|
end else begin
|
||||||
|
hartsel <= hartsel_next;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Also implement the hart array mask if there is more than one hart.
|
||||||
|
reg [N_HARTS-1:0] hart_array_mask;
|
||||||
|
reg hasel;
|
||||||
|
wire [N_HARTS-1:0] hart_array_mask_next;
|
||||||
|
wire hasel_next;
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (N_HARTS > 1) begin: has_array_mask
|
||||||
|
|
||||||
|
assign hart_array_mask_next = dmi_write && dmi_regaddr == ADDR_HAWINDOW ?
|
||||||
|
dmi_pwdata[N_HARTS-1:0] : hart_array_mask;
|
||||||
|
assign hasel_next = dmi_write && dmi_regaddr == ADDR_DMCONTROL ?
|
||||||
|
dmi_pwdata[26] : hasel;
|
||||||
|
|
||||||
|
end else begin: has_no_array_mask
|
||||||
|
|
||||||
|
assign hart_array_mask_next = 1'b0;
|
||||||
|
assign hasel_next = 1'b0;
|
||||||
|
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
hart_array_mask <= {N_HARTS{1'b0}};
|
||||||
|
hasel <= 1'b0;
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
hart_array_mask <= {N_HARTS{1'b0}};
|
||||||
|
hasel <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
hart_array_mask <= hart_array_mask_next;
|
||||||
|
hasel <= hasel_next;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Run/halt/reset control
|
||||||
|
|
||||||
|
// Normal read/write fields for dmcontrol (note some of these are per-hart
|
||||||
|
// fields that get rotated into dmcontrol based on the current/next hartsel).
|
||||||
|
reg [N_HARTS-1:0] dmcontrol_haltreq;
|
||||||
|
reg [N_HARTS-1:0] dmcontrol_hartreset;
|
||||||
|
reg [N_HARTS-1:0] dmcontrol_resethaltreq;
|
||||||
|
reg dmcontrol_ndmreset;
|
||||||
|
|
||||||
|
wire [N_HARTS-1:0] dmcontrol_op_mask;
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (N_HARTS > 1) begin: dmcontrol_multiple_harts
|
||||||
|
|
||||||
|
// Selection is the hart selected by hartsel, *plus* the hart array mask
|
||||||
|
// if hasel is set. Note we don't need to use the "next" version of
|
||||||
|
// hart_array_mask since it can't change simultaneously with dmcontrol.
|
||||||
|
assign dmcontrol_op_mask =
|
||||||
|
(hartsel_next >= N_HARTS ? {N_HARTS{1'b0}} : {{N_HARTS-1{1'b0}}, 1'b1} << hartsel_next)
|
||||||
|
| ({N_HARTS{hasel_next}} & hart_array_mask);
|
||||||
|
|
||||||
|
end else begin: dmcontrol_single_hart
|
||||||
|
|
||||||
|
assign dmcontrol_op_mask = 1'b1;
|
||||||
|
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
dmactive <= 1'b0;
|
||||||
|
dmcontrol_ndmreset <= 1'b0;
|
||||||
|
dmcontrol_haltreq <= {N_HARTS{1'b0}};
|
||||||
|
dmcontrol_hartreset <= {N_HARTS{1'b0}};
|
||||||
|
dmcontrol_resethaltreq <= {N_HARTS{1'b0}};
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
// Only dmactive is writable when !dmactive
|
||||||
|
if (dmi_write && dmi_regaddr == ADDR_DMCONTROL)
|
||||||
|
dmactive <= dmi_pwdata[0];
|
||||||
|
dmcontrol_ndmreset <= 1'b0;
|
||||||
|
dmcontrol_haltreq <= {N_HARTS{1'b0}};
|
||||||
|
dmcontrol_hartreset <= {N_HARTS{1'b0}};
|
||||||
|
dmcontrol_resethaltreq <= {N_HARTS{1'b0}};
|
||||||
|
end else if (dmi_write && dmi_regaddr == ADDR_DMCONTROL) begin
|
||||||
|
dmactive <= dmi_pwdata[0];
|
||||||
|
dmcontrol_ndmreset <= dmi_pwdata[1];
|
||||||
|
|
||||||
|
dmcontrol_haltreq <= (dmcontrol_haltreq & ~dmcontrol_op_mask) |
|
||||||
|
({N_HARTS{dmi_pwdata[31]}} & dmcontrol_op_mask);
|
||||||
|
|
||||||
|
dmcontrol_hartreset <= (dmcontrol_hartreset & ~dmcontrol_op_mask) |
|
||||||
|
({N_HARTS{dmi_pwdata[29]}} & dmcontrol_op_mask);
|
||||||
|
|
||||||
|
dmcontrol_resethaltreq <= (dmcontrol_resethaltreq
|
||||||
|
& ~({N_HARTS{dmi_pwdata[2]}} & dmcontrol_op_mask))
|
||||||
|
| ({N_HARTS{dmi_pwdata[3]}} & dmcontrol_op_mask);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign sys_reset_req = dmcontrol_ndmreset;
|
||||||
|
assign hart_reset_req = dmcontrol_hartreset;
|
||||||
|
assign hart_req_halt = dmcontrol_haltreq;
|
||||||
|
assign hart_req_halt_on_reset = dmcontrol_resethaltreq;
|
||||||
|
|
||||||
|
reg [N_HARTS-1:0] hart_reset_done_prev;
|
||||||
|
reg [N_HARTS-1:0] dmstatus_havereset;
|
||||||
|
wire [N_HARTS-1:0] hart_available = hart_reset_done & {N_HARTS{sys_reset_done}};
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
hart_reset_done_prev <= {N_HARTS{1'b0}};
|
||||||
|
end else begin
|
||||||
|
hart_reset_done_prev <= hart_reset_done;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
wire dmcontrol_ackhavereset = dmi_write && dmi_regaddr == ADDR_DMCONTROL && dmi_pwdata[28];
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
dmstatus_havereset <= {N_HARTS{1'b0}};
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
dmstatus_havereset <= {N_HARTS{1'b0}};
|
||||||
|
end else begin
|
||||||
|
dmstatus_havereset <= (dmstatus_havereset | (hart_reset_done & ~hart_reset_done_prev))
|
||||||
|
& ~({N_HARTS{dmcontrol_ackhavereset}} & dmcontrol_op_mask);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
reg [N_HARTS-1:0] dmstatus_resumeack;
|
||||||
|
reg [N_HARTS-1:0] dmcontrol_resumereq_sticky;
|
||||||
|
|
||||||
|
// Note: we are required to ignore resumereq when haltreq is also set, as per
|
||||||
|
// spec (odd since the host is forbidden from writing both at once anyway).
|
||||||
|
// The wording is odd, it refers only to `haltreq` which is specifically the
|
||||||
|
// write-only `dmcontrol` field, not the underlying halt request state bits.
|
||||||
|
wire dmcontrol_resumereq = dmi_write && dmi_regaddr == ADDR_DMCONTROL &&
|
||||||
|
dmi_pwdata[30] && !dmi_pwdata[31];
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
dmstatus_resumeack <= {N_HARTS{1'b0}};
|
||||||
|
dmcontrol_resumereq_sticky <= {N_HARTS{1'b0}};
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
dmstatus_resumeack <= {N_HARTS{1'b0}};
|
||||||
|
dmcontrol_resumereq_sticky <= {N_HARTS{1'b0}};
|
||||||
|
end else begin
|
||||||
|
dmstatus_resumeack <= (dmstatus_resumeack
|
||||||
|
| (dmcontrol_resumereq_sticky & hart_running & hart_available))
|
||||||
|
& ~({N_HARTS{dmcontrol_resumereq}} & dmcontrol_op_mask);
|
||||||
|
|
||||||
|
dmcontrol_resumereq_sticky <= (dmcontrol_resumereq_sticky
|
||||||
|
& ~(hart_running & hart_available))
|
||||||
|
| ({N_HARTS{dmcontrol_resumereq}} & dmcontrol_op_mask);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign hart_req_resume = dmcontrol_resumereq_sticky;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// System bus access
|
||||||
|
|
||||||
|
reg [31:0] sbaddress;
|
||||||
|
reg [31:0] sbdata;
|
||||||
|
|
||||||
|
// Update logic for address/data registers:
|
||||||
|
|
||||||
|
reg sbbusy;
|
||||||
|
reg sbautoincrement;
|
||||||
|
reg [2:0] sbaccess; // Size of the transfer
|
||||||
|
|
||||||
|
wire sbdata_write_blocked;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
sbaddress <= {32{1'b0}};
|
||||||
|
sbdata <= {32{1'b0}};
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
sbaddress <= {32{1'b0}};
|
||||||
|
sbdata <= {32{1'b0}};
|
||||||
|
end else if (HAVE_SBA) begin
|
||||||
|
if (dmi_write && dmi_regaddr == ADDR_SBDATA0 && !sbdata_write_blocked) begin
|
||||||
|
// Note sbbusyerror and sberror block writes to sbdata0, as the
|
||||||
|
// write is required to have no side effects when they are set.
|
||||||
|
sbdata <= dmi_pwdata;
|
||||||
|
end else if (sbus_vld && sbus_rdy && !sbus_write && !sbus_err) begin
|
||||||
|
// Make sure the lower byte lanes see appropriately shifted data as
|
||||||
|
// long as the transfer is naturally aligned
|
||||||
|
sbdata <= sbaddress[1:0] == 2'b01 ? {sbus_rdata[31:8], sbus_rdata[15:8]} :
|
||||||
|
sbaddress[1:0] == 2'b10 ? {sbus_rdata[31:16], sbus_rdata[31:16]} :
|
||||||
|
sbaddress[1:0] == 2'b11 ? {sbus_rdata[31:8], sbus_rdata[31:24]} : sbus_rdata;
|
||||||
|
end
|
||||||
|
if (dmi_write && dmi_regaddr == ADDR_SBADDRESS0 && !sbbusy) begin
|
||||||
|
// Note sbaddress can't be written when busy, but
|
||||||
|
// sberror/sbbusyerror do not prevent writes.
|
||||||
|
sbaddress <= dmi_pwdata;
|
||||||
|
end else if (sbus_vld && sbus_rdy && !sbus_err && sbautoincrement) begin
|
||||||
|
// Note: address increments only following a successful transfer.
|
||||||
|
// Spec 0.13.2 weirdly implies address should increment following
|
||||||
|
// a sbdata0 read with sbautoincrement=1 and sbreadondata=0, but
|
||||||
|
// this seems to be a typo, fixed in later versions.
|
||||||
|
sbaddress <= sbaddress + (
|
||||||
|
sbaccess[1:0] == 2'b00 ? 32'd1 :
|
||||||
|
sbaccess[1:0] == 2'b01 ? 32'd2 : 32'd4
|
||||||
|
);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Control logic:
|
||||||
|
|
||||||
|
reg sbbusyerror;
|
||||||
|
reg sbreadonaddr;
|
||||||
|
reg sbreadondata;
|
||||||
|
reg [2:0] sberror;
|
||||||
|
reg sb_current_is_write;
|
||||||
|
|
||||||
|
localparam SBERROR_OK = 3'h0;
|
||||||
|
localparam SBERROR_BADADDR = 3'h2;
|
||||||
|
localparam SBERROR_BADALIGN = 3'h3;
|
||||||
|
localparam SBERROR_BADSIZE = 3'h4;
|
||||||
|
|
||||||
|
assign sbdata_write_blocked = sbbusy || sbbusyerror || |sberror;
|
||||||
|
|
||||||
|
// Notes on behaviour of sbbusyerror: the sbbusyerror description says:
|
||||||
|
//
|
||||||
|
// "Set when the debugger attempts to read data while a read is in progress,
|
||||||
|
// or when the debugger initiates a new access while one is already in
|
||||||
|
// progress (while sbbusy is set)."
|
||||||
|
//
|
||||||
|
// However, sbaddress0 description says:
|
||||||
|
//
|
||||||
|
// "When the system bus master is busy, writes to this register will set
|
||||||
|
// sbbusyerror and don’t do anything else."
|
||||||
|
//
|
||||||
|
// ...not conditioned on sbreadonaddr. Likewise the sbdata0 description says:
|
||||||
|
//
|
||||||
|
// "If the bus master is busy then accesses set sbbusyerror, and don’t do
|
||||||
|
// anything else."
|
||||||
|
//
|
||||||
|
// ...not conditioned on sbreadondata. We are going to take the union of all
|
||||||
|
// the cases where the spec says we should raise an error:
|
||||||
|
|
||||||
|
wire sb_access_illegal_when_busy =
|
||||||
|
dmi_regaddr == ADDR_SBDATA0 && (dmi_read || dmi_write) ||
|
||||||
|
dmi_regaddr == ADDR_SBADDRESS0 && dmi_write;
|
||||||
|
|
||||||
|
wire sb_want_start_write = dmi_write && dmi_regaddr == ADDR_SBDATA0;
|
||||||
|
|
||||||
|
wire sb_want_start_read =
|
||||||
|
(sbreadonaddr && dmi_write && dmi_regaddr == ADDR_SBADDRESS0) ||
|
||||||
|
(sbreadondata && dmi_read && dmi_regaddr == ADDR_SBDATA0);
|
||||||
|
|
||||||
|
wire [1:0] sb_next_align = sbreadonaddr && dmi_write && dmi_regaddr == ADDR_SBADDRESS0 ?
|
||||||
|
dmi_pwdata[1:0] : sbaddress[1:0];
|
||||||
|
|
||||||
|
wire sb_badalign =
|
||||||
|
(sbaccess == 3'h1 && sb_next_align[0]) ||
|
||||||
|
(sbaccess == 3'h2 && |sb_next_align[1:0]);
|
||||||
|
|
||||||
|
wire sb_badsize = sbaccess > 3'h2;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
sbbusy <= 1'b0;
|
||||||
|
sbbusyerror <= 1'b0;
|
||||||
|
sbreadonaddr <= 1'b0;
|
||||||
|
sbreadondata <= 1'b0;
|
||||||
|
sbaccess <= 3'h0;
|
||||||
|
sbautoincrement <= 1'b0;
|
||||||
|
sberror <= 3'h0;
|
||||||
|
sb_current_is_write <= 1'b0;
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
sbbusy <= 1'b0;
|
||||||
|
sbbusyerror <= 1'b0;
|
||||||
|
sbreadonaddr <= 1'b0;
|
||||||
|
sbreadondata <= 1'b0;
|
||||||
|
sbaccess <= 3'h0;
|
||||||
|
sbautoincrement <= 1'b0;
|
||||||
|
sberror <= 3'h0;
|
||||||
|
sb_current_is_write <= 1'b0;
|
||||||
|
end else if (HAVE_SBA) begin
|
||||||
|
if (dmi_write && dmi_regaddr == ADDR_SBCS) begin
|
||||||
|
// Assume a transfer is not in progress when written (per spec)
|
||||||
|
sbbusyerror <= sbbusyerror && !dmi_pwdata[22];
|
||||||
|
sbreadonaddr <= dmi_pwdata[20];
|
||||||
|
sbaccess <= dmi_pwdata[19:17];
|
||||||
|
sbautoincrement <= dmi_pwdata[16];
|
||||||
|
sbreadondata <= dmi_pwdata[15];
|
||||||
|
sberror <= sberror & ~dmi_pwdata[14:12];
|
||||||
|
end
|
||||||
|
if (sbbusy) begin
|
||||||
|
if (sb_access_illegal_when_busy) begin
|
||||||
|
sbbusyerror <= 1'b1;
|
||||||
|
end
|
||||||
|
if (sbus_vld && sbus_rdy) begin
|
||||||
|
sbbusy <= 1'b0;
|
||||||
|
if (sbus_err) begin
|
||||||
|
sberror <= SBERROR_BADADDR;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end else if ((sb_want_start_read || sb_want_start_write) && ~|sberror && !sbbusyerror) begin
|
||||||
|
if (sb_badsize) begin
|
||||||
|
sberror <= SBERROR_BADSIZE;
|
||||||
|
end else if (sb_badalign) begin
|
||||||
|
sberror <= SBERROR_BADALIGN;
|
||||||
|
end else begin
|
||||||
|
sbbusy <= 1'b1;
|
||||||
|
sb_current_is_write <= sb_want_start_write;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign sbus_addr = sbaddress;
|
||||||
|
assign sbus_write = sb_current_is_write;
|
||||||
|
assign sbus_size = sbaccess[1:0];
|
||||||
|
assign sbus_vld = sbbusy;
|
||||||
|
|
||||||
|
// Replicate byte lanes to handle naturally-aligned cases.
|
||||||
|
assign sbus_wdata = sbaccess[1:0] == 2'b00 ? {4{sbdata[7:0]}} :
|
||||||
|
sbaccess[1:0] == 2'b01 ? {2{sbdata[15:0]}} : sbdata;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Abstract command data registers
|
||||||
|
|
||||||
|
wire abstractcs_busy;
|
||||||
|
|
||||||
|
// The same data0 register is aliased as a CSR on all harts connected to this
|
||||||
|
// DM. Cores may read data0 as a CSR when in debug mode, and may write it when:
|
||||||
|
//
|
||||||
|
// - That core is in debug mode, and...
|
||||||
|
// - We are currently executing an abstract command on that core
|
||||||
|
//
|
||||||
|
// The DM can also read/write data0 at all times.
|
||||||
|
|
||||||
|
reg [XLEN-1:0] abstract_data0;
|
||||||
|
|
||||||
|
assign hart_data0_rdata = {N_HARTS{abstract_data0}};
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin: update_hart_data0
|
||||||
|
reg signed [31:0] i;
|
||||||
|
if (!rst_n) begin
|
||||||
|
abstract_data0 <= {XLEN{1'b0}};
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
abstract_data0 <= {XLEN{1'b0}};
|
||||||
|
end else if (dmi_write && dmi_regaddr == ADDR_DATA0) begin
|
||||||
|
abstract_data0 <= dmi_pwdata;
|
||||||
|
end else begin
|
||||||
|
for (i = 0; i < N_HARTS; i = i + 1) begin
|
||||||
|
if (hartsel == i[W_HARTSEL-1:0] && hart_data0_wen[i] && hart_halted[i] && abstractcs_busy)
|
||||||
|
abstract_data0 <= hart_data0_wdata[i * XLEN +: XLEN];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
reg [XLEN-1:0] progbuf0;
|
||||||
|
reg [XLEN-1:0] progbuf1;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
progbuf0 <= {XLEN{1'b0}};
|
||||||
|
progbuf1 <= {XLEN{1'b0}};
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
progbuf0 <= {XLEN{1'b0}};
|
||||||
|
progbuf1 <= {XLEN{1'b0}};
|
||||||
|
end else if (dmi_write && !abstractcs_busy) begin
|
||||||
|
if (dmi_regaddr == ADDR_PROGBUF0)
|
||||||
|
progbuf0 <= dmi_pwdata;
|
||||||
|
if (dmi_regaddr == ADDR_PROGBUF1)
|
||||||
|
progbuf1 <= dmi_pwdata;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
reg abstractauto_autoexecdata;
|
||||||
|
reg [1:0] abstractauto_autoexecprogbuf;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
abstractauto_autoexecdata <= 1'b0;
|
||||||
|
abstractauto_autoexecprogbuf <= 2'b00;
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
abstractauto_autoexecdata <= 1'b0;
|
||||||
|
abstractauto_autoexecprogbuf <= 2'b00;
|
||||||
|
end else if (dmi_write && dmi_regaddr == ADDR_ABSTRACTAUTO) begin
|
||||||
|
abstractauto_autoexecdata <= dmi_pwdata[0];
|
||||||
|
abstractauto_autoexecprogbuf <= dmi_pwdata[17:16];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Abstract command state machine
|
||||||
|
|
||||||
|
localparam W_STATE = 4;
|
||||||
|
localparam S_IDLE = 4'd0;
|
||||||
|
|
||||||
|
localparam S_ISSUE_REGREAD = 4'd1;
|
||||||
|
localparam S_ISSUE_REGWRITE = 4'd2;
|
||||||
|
localparam S_ISSUE_REGEBREAK = 4'd3;
|
||||||
|
localparam S_WAIT_REGEBREAK = 4'd4;
|
||||||
|
|
||||||
|
localparam S_ISSUE_PROGBUF0 = 4'd5;
|
||||||
|
localparam S_ISSUE_PROGBUF1 = 4'd6;
|
||||||
|
localparam S_ISSUE_IMPEBREAK = 4'd7;
|
||||||
|
localparam S_WAIT_IMPEBREAK = 4'd8;
|
||||||
|
|
||||||
|
localparam CMDERR_OK = 3'h0;
|
||||||
|
localparam CMDERR_BUSY = 3'h1;
|
||||||
|
localparam CMDERR_UNSUPPORTED = 3'h2;
|
||||||
|
localparam CMDERR_EXCEPTION = 3'h3;
|
||||||
|
localparam CMDERR_HALTRESUME = 3'h4;
|
||||||
|
|
||||||
|
reg [2:0] abstractcs_cmderr;
|
||||||
|
reg [2:0] abstractcs_cmderr_nxt;
|
||||||
|
reg [W_STATE-1:0] acmd_state;
|
||||||
|
reg [W_STATE-1:0] acmd_state_nxt;
|
||||||
|
|
||||||
|
assign abstractcs_busy = acmd_state != S_IDLE;
|
||||||
|
|
||||||
|
wire start_abstract_cmd = abstractcs_cmderr == CMDERR_OK && !abstractcs_busy && (
|
||||||
|
(dmi_write && dmi_regaddr == ADDR_COMMAND) ||
|
||||||
|
((dmi_write || dmi_read) && abstractauto_autoexecdata && dmi_regaddr == ADDR_DATA0) ||
|
||||||
|
((dmi_write || dmi_read) && abstractauto_autoexecprogbuf[0] && dmi_regaddr == ADDR_PROGBUF0) ||
|
||||||
|
((dmi_write || dmi_read) && abstractauto_autoexecprogbuf[1] && dmi_regaddr == ADDR_PROGBUF1)
|
||||||
|
);
|
||||||
|
|
||||||
|
wire dmi_access_illegal_when_busy =
|
||||||
|
(dmi_write && (
|
||||||
|
dmi_regaddr == ADDR_ABSTRACTCS || dmi_regaddr == ADDR_COMMAND || dmi_regaddr == ADDR_ABSTRACTAUTO ||
|
||||||
|
dmi_regaddr == ADDR_DATA0 || dmi_regaddr == ADDR_PROGBUF0 || dmi_regaddr == ADDR_PROGBUF1)) ||
|
||||||
|
(dmi_read && (
|
||||||
|
dmi_regaddr == ADDR_DATA0 || dmi_regaddr == ADDR_PROGBUF0 || dmi_regaddr == ADDR_PROGBUF1));
|
||||||
|
|
||||||
|
// Decode what acmd may be triggered on this cycle, and whether it is
|
||||||
|
// supported -- command source may be a registered version of most recent
|
||||||
|
// command (if abstractauto is used) or a fresh command off the bus. We don't
|
||||||
|
// register the entire write data; repeats of unsupported commands are
|
||||||
|
// detected by just registering that the last written command was
|
||||||
|
// unsupported.
|
||||||
|
|
||||||
|
wire acmd_new = dmi_write && dmi_regaddr == ADDR_COMMAND;
|
||||||
|
|
||||||
|
wire acmd_new_postexec = dmi_pwdata[18];
|
||||||
|
wire acmd_new_transfer = dmi_pwdata[17];
|
||||||
|
wire acmd_new_write = dmi_pwdata[16];
|
||||||
|
wire [4:0] acmd_new_regno = dmi_pwdata[4:0];
|
||||||
|
|
||||||
|
// Note: regno and aarsize are permitted to have otherwise-invalid values if
|
||||||
|
// the transfer flag is not set.
|
||||||
|
wire acmd_new_unsupported =
|
||||||
|
(dmi_pwdata[31:24] != 8'h00 ) || // Only Access Register command supported
|
||||||
|
(dmi_pwdata[22:20] != 3'h2 && acmd_new_transfer) || // Must be 32 bits in size
|
||||||
|
(dmi_pwdata[19] ) || // aarpostincrement not supported
|
||||||
|
(dmi_pwdata[15:12] != 4'h1 && acmd_new_transfer) || // Only core register access supported
|
||||||
|
(dmi_pwdata[11:5] != 7'h0 && acmd_new_transfer); // Only GPRs supported
|
||||||
|
|
||||||
|
reg acmd_prev_postexec;
|
||||||
|
reg acmd_prev_transfer;
|
||||||
|
reg acmd_prev_write;
|
||||||
|
reg [4:0] acmd_prev_regno;
|
||||||
|
reg acmd_prev_unsupported;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
acmd_prev_postexec <= 1'b0;
|
||||||
|
acmd_prev_transfer <= 1'b0;
|
||||||
|
acmd_prev_write <= 1'b0;
|
||||||
|
acmd_prev_regno <= 5'h0;
|
||||||
|
acmd_prev_unsupported <= 1'b1;
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
acmd_prev_postexec <= 1'b0;
|
||||||
|
acmd_prev_transfer <= 1'b0;
|
||||||
|
acmd_prev_write <= 1'b0;
|
||||||
|
acmd_prev_regno <= 5'h0;
|
||||||
|
acmd_prev_unsupported <= 1'b1;
|
||||||
|
end else if (start_abstract_cmd && acmd_new) begin
|
||||||
|
acmd_prev_postexec <= acmd_new_postexec;
|
||||||
|
acmd_prev_transfer <= acmd_new_transfer;
|
||||||
|
acmd_prev_write <= acmd_new_write;
|
||||||
|
acmd_prev_regno <= acmd_new_regno;
|
||||||
|
acmd_prev_unsupported <= acmd_new_unsupported;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
wire acmd_postexec = acmd_new ? acmd_new_postexec : acmd_prev_postexec ;
|
||||||
|
wire acmd_transfer = acmd_new ? acmd_new_transfer : acmd_prev_transfer ;
|
||||||
|
wire acmd_write = acmd_new ? acmd_new_write : acmd_prev_write ;
|
||||||
|
wire [4:0] acmd_regno = acmd_new ? acmd_new_regno : acmd_prev_regno ;
|
||||||
|
wire acmd_unsupported = acmd_new ? acmd_new_unsupported : acmd_prev_unsupported;
|
||||||
|
|
||||||
|
always @ (*) begin
|
||||||
|
// Default: no state change
|
||||||
|
acmd_state_nxt = acmd_state;
|
||||||
|
abstractcs_cmderr_nxt = abstractcs_cmderr;
|
||||||
|
|
||||||
|
if (dmi_write && dmi_regaddr == ADDR_ABSTRACTCS && !abstractcs_busy)
|
||||||
|
abstractcs_cmderr_nxt = abstractcs_cmderr & ~dmi_pwdata[10:8];
|
||||||
|
if (abstractcs_cmderr == CMDERR_OK && abstractcs_busy && dmi_access_illegal_when_busy)
|
||||||
|
abstractcs_cmderr_nxt = CMDERR_BUSY;
|
||||||
|
if (acmd_state != S_IDLE && hart_instr_caught_exception[hartsel])
|
||||||
|
abstractcs_cmderr_nxt = CMDERR_EXCEPTION;
|
||||||
|
|
||||||
|
case (acmd_state)
|
||||||
|
S_IDLE: begin
|
||||||
|
if (start_abstract_cmd) begin
|
||||||
|
if (!hart_halted[hartsel] || !hart_available[hartsel]) begin
|
||||||
|
abstractcs_cmderr_nxt = CMDERR_HALTRESUME;
|
||||||
|
end else if (acmd_unsupported) begin
|
||||||
|
abstractcs_cmderr_nxt = CMDERR_UNSUPPORTED;
|
||||||
|
end else begin
|
||||||
|
if (acmd_transfer && acmd_write)
|
||||||
|
acmd_state_nxt = S_ISSUE_REGWRITE;
|
||||||
|
else if (acmd_transfer && !acmd_write)
|
||||||
|
acmd_state_nxt = S_ISSUE_REGREAD;
|
||||||
|
else if (acmd_postexec)
|
||||||
|
acmd_state_nxt = S_ISSUE_PROGBUF0;
|
||||||
|
else
|
||||||
|
acmd_state_nxt = S_IDLE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
S_ISSUE_REGREAD: begin
|
||||||
|
if (hart_instr_data_rdy[hartsel])
|
||||||
|
acmd_state_nxt = S_ISSUE_REGEBREAK;
|
||||||
|
end
|
||||||
|
S_ISSUE_REGWRITE: begin
|
||||||
|
if (hart_instr_data_rdy[hartsel])
|
||||||
|
acmd_state_nxt = S_ISSUE_REGEBREAK;
|
||||||
|
end
|
||||||
|
S_ISSUE_REGEBREAK: begin
|
||||||
|
if (hart_instr_data_rdy[hartsel])
|
||||||
|
acmd_state_nxt = S_WAIT_REGEBREAK;
|
||||||
|
end
|
||||||
|
S_WAIT_REGEBREAK: begin
|
||||||
|
if (hart_instr_caught_ebreak[hartsel]) begin
|
||||||
|
if (acmd_prev_postexec)
|
||||||
|
acmd_state_nxt = S_ISSUE_PROGBUF0;
|
||||||
|
else
|
||||||
|
acmd_state_nxt = S_IDLE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
S_ISSUE_PROGBUF0: begin
|
||||||
|
if (hart_instr_data_rdy[hartsel])
|
||||||
|
acmd_state_nxt = S_ISSUE_PROGBUF1;
|
||||||
|
end
|
||||||
|
S_ISSUE_PROGBUF1: begin
|
||||||
|
if (hart_instr_caught_exception[hartsel] || hart_instr_caught_ebreak[hartsel]) begin
|
||||||
|
acmd_state_nxt = S_IDLE;
|
||||||
|
end else if (hart_instr_data_rdy[hartsel]) begin
|
||||||
|
acmd_state_nxt = S_ISSUE_IMPEBREAK;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_ISSUE_IMPEBREAK: begin
|
||||||
|
if (hart_instr_caught_exception[hartsel] || hart_instr_caught_ebreak[hartsel]) begin
|
||||||
|
acmd_state_nxt = S_IDLE;
|
||||||
|
end else if (hart_instr_data_rdy[hartsel]) begin
|
||||||
|
acmd_state_nxt = S_WAIT_IMPEBREAK;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_WAIT_IMPEBREAK: begin
|
||||||
|
if (hart_instr_caught_exception[hartsel] || hart_instr_caught_ebreak[hartsel]) begin
|
||||||
|
acmd_state_nxt = S_IDLE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
default: begin
|
||||||
|
// Unreachable
|
||||||
|
end
|
||||||
|
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
abstractcs_cmderr <= CMDERR_OK;
|
||||||
|
acmd_state <= S_IDLE;
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
abstractcs_cmderr <= CMDERR_OK;
|
||||||
|
acmd_state <= S_IDLE;
|
||||||
|
end else begin
|
||||||
|
abstractcs_cmderr <= abstractcs_cmderr_nxt;
|
||||||
|
acmd_state <= acmd_state_nxt;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
wire [N_HARTS-1:0] hart_instr_data_vld_nxt = {{N_HARTS-1{1'b0}},
|
||||||
|
acmd_state_nxt == S_ISSUE_REGREAD || acmd_state_nxt == S_ISSUE_REGWRITE || acmd_state_nxt == S_ISSUE_REGEBREAK ||
|
||||||
|
acmd_state_nxt == S_ISSUE_PROGBUF0 || acmd_state_nxt == S_ISSUE_PROGBUF1 || acmd_state_nxt == S_ISSUE_IMPEBREAK
|
||||||
|
} << hartsel;
|
||||||
|
|
||||||
|
wire [31:0] hart_instr_data_nxt =
|
||||||
|
acmd_state_nxt == S_ISSUE_REGWRITE ? 32'hbff02073 | {20'd0, acmd_regno, 7'd0} : // csrr xx, dmdata0
|
||||||
|
acmd_state_nxt == S_ISSUE_REGREAD ? 32'hbff01073 | {12'd0, acmd_regno, 15'd0} : // csrw dmdata0, xx
|
||||||
|
acmd_state_nxt == S_ISSUE_PROGBUF0 ? progbuf0 :
|
||||||
|
acmd_state_nxt == S_ISSUE_PROGBUF1 ? progbuf1 :
|
||||||
|
32'h00100073; // ebreak
|
||||||
|
|
||||||
|
reg [31:0] hart_instr_data_reg;
|
||||||
|
assign hart_instr_data = {N_HARTS{hart_instr_data_reg}};
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
hart_instr_data_vld <= 1'b0;
|
||||||
|
hart_instr_data_reg <= 32'h00000000;
|
||||||
|
end else begin
|
||||||
|
hart_instr_data_vld <= hart_instr_data_vld_nxt;
|
||||||
|
if (hart_instr_data_vld_nxt) begin
|
||||||
|
hart_instr_data_reg <= hart_instr_data_nxt;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Status helper functions
|
||||||
|
|
||||||
|
function status_any;
|
||||||
|
input [N_HARTS-1:0] status_mask;
|
||||||
|
begin
|
||||||
|
status_any = status_mask[hartsel] || (hasel && |(status_mask & hart_array_mask));
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function status_all;
|
||||||
|
input [N_HARTS-1:0] status_mask;
|
||||||
|
begin
|
||||||
|
status_all = status_mask[hartsel] && (!hasel || ~|(~status_mask & hart_array_mask));
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function [1:0] status_all_any;
|
||||||
|
input [N_HARTS-1:0] status_mask;
|
||||||
|
begin
|
||||||
|
status_all_any = {
|
||||||
|
status_all(status_mask),
|
||||||
|
status_any(status_mask)
|
||||||
|
};
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DMI read data mux
|
||||||
|
|
||||||
|
always @ (*) begin
|
||||||
|
case (dmi_regaddr)
|
||||||
|
ADDR_DATA0: dmi_prdata = abstract_data0;
|
||||||
|
ADDR_DMCONTROL: dmi_prdata = {
|
||||||
|
1'b0, // haltreq is a W-only field
|
||||||
|
1'b0, // resumereq is a W1 field
|
||||||
|
status_any(dmcontrol_hartreset),
|
||||||
|
1'b0, // ackhavereset is a W1 field
|
||||||
|
1'b0, // reserved
|
||||||
|
hasel,
|
||||||
|
{{10-W_HARTSEL{1'b0}}, hartsel}, // hartsello
|
||||||
|
10'h0, // hartselhi
|
||||||
|
2'h0, // reserved
|
||||||
|
2'h0, // set/clrresethaltreq are W1 fields
|
||||||
|
dmcontrol_ndmreset,
|
||||||
|
dmactive
|
||||||
|
};
|
||||||
|
ADDR_DMSTATUS: dmi_prdata = {
|
||||||
|
9'h0, // reserved
|
||||||
|
1'b1, // impebreak = 1
|
||||||
|
2'h0, // reserved
|
||||||
|
status_all_any(dmstatus_havereset), // allhavereset, anyhavereset
|
||||||
|
status_all_any(dmstatus_resumeack), // allresumeack, anyresumeack
|
||||||
|
hartsel >= N_HARTS && !(hasel && |hart_array_mask), // allnonexistent
|
||||||
|
hartsel >= N_HARTS, // anynonexistent
|
||||||
|
status_all_any(~hart_available), // allunavail, anyunavail
|
||||||
|
status_all_any(hart_running & hart_available), // allrunning, anyrunning
|
||||||
|
status_all_any(hart_halted & hart_available), // allhalted, anyhalted
|
||||||
|
1'b1, // authenticated
|
||||||
|
1'b0, // authbusy
|
||||||
|
1'b1, // hasresethaltreq = 1 (we do support it)
|
||||||
|
1'b0, // confstrptrvalid
|
||||||
|
4'd2 // version = 2: RISC-V debug spec 0.13.2
|
||||||
|
};
|
||||||
|
ADDR_HARTINFO: dmi_prdata = {
|
||||||
|
8'h0, // reserved
|
||||||
|
4'h0, // nscratch = 0
|
||||||
|
3'h0, // reserved
|
||||||
|
1'b0, // dataccess = 0, data0 is mapped to each hart's CSR space
|
||||||
|
4'h1, // datasize = 1, a single data CSR (data0) is available
|
||||||
|
12'hbff // dataaddr, placed at the top of the M-custom space since
|
||||||
|
// the spec doesn't reserve a location for it.
|
||||||
|
};
|
||||||
|
ADDR_HALTSUM0: dmi_prdata = {
|
||||||
|
{XLEN - N_HARTS{1'b0}},
|
||||||
|
hart_halted & hart_available
|
||||||
|
};
|
||||||
|
ADDR_HALTSUM1: dmi_prdata = {
|
||||||
|
{XLEN - 1{1'b0}},
|
||||||
|
|(hart_halted & hart_available)
|
||||||
|
};
|
||||||
|
ADDR_HAWINDOWSEL: dmi_prdata = 32'h00000000;
|
||||||
|
ADDR_HAWINDOW: dmi_prdata = {
|
||||||
|
{32-N_HARTS{1'b0}},
|
||||||
|
hart_array_mask
|
||||||
|
};
|
||||||
|
ADDR_ABSTRACTCS: dmi_prdata = {
|
||||||
|
3'h0, // reserved
|
||||||
|
5'd2, // progbufsize = 2
|
||||||
|
11'h0, // reserved
|
||||||
|
abstractcs_busy,
|
||||||
|
1'b0,
|
||||||
|
abstractcs_cmderr,
|
||||||
|
4'h0,
|
||||||
|
4'd1 // datacount = 1
|
||||||
|
};
|
||||||
|
ADDR_ABSTRACTAUTO: dmi_prdata = {
|
||||||
|
14'h0,
|
||||||
|
abstractauto_autoexecprogbuf, // only progbuf0,1 present
|
||||||
|
15'h0,
|
||||||
|
abstractauto_autoexecdata // only data0 present
|
||||||
|
};
|
||||||
|
ADDR_SBCS: dmi_prdata = {
|
||||||
|
3'h1, // version = 1
|
||||||
|
6'h00,
|
||||||
|
sbbusyerror,
|
||||||
|
sbbusy,
|
||||||
|
sbreadonaddr,
|
||||||
|
sbaccess,
|
||||||
|
sbautoincrement,
|
||||||
|
sbreadondata,
|
||||||
|
sberror,
|
||||||
|
7'h20, // sbasize = 32
|
||||||
|
5'b00111 // 8, 16, 32-bit transfers supported
|
||||||
|
} & {32{|HAVE_SBA}};
|
||||||
|
ADDR_SBDATA0: dmi_prdata = sbdata & {32{|HAVE_SBA}};
|
||||||
|
ADDR_SBADDRESS0: dmi_prdata = sbaddress & {32{|HAVE_SBA}};
|
||||||
|
ADDR_CONFSTRPTR0: dmi_prdata = 32'h4c296328;
|
||||||
|
ADDR_CONFSTRPTR1: dmi_prdata = 32'h20656b75;
|
||||||
|
ADDR_CONFSTRPTR2: dmi_prdata = 32'h6e657257;
|
||||||
|
ADDR_CONFSTRPTR3: dmi_prdata = 32'h31322720;
|
||||||
|
ADDR_NEXTDM: dmi_prdata = NEXT_DM_ADDR;
|
||||||
|
ADDR_PROGBUF0: dmi_prdata = progbuf0;
|
||||||
|
ADDR_PROGBUF1: dmi_prdata = progbuf1;
|
||||||
|
default: dmi_prdata = {XLEN{1'b0}};
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// Standalone bus shim for connecting the DM's System Bus Access to AHB
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_sbus_to_ahb #(
|
||||||
|
parameter W_ADDR = 32,
|
||||||
|
parameter W_DATA = 32
|
||||||
|
) (
|
||||||
|
input wire clk,
|
||||||
|
input wire rst_n,
|
||||||
|
|
||||||
|
input wire [W_ADDR-1:0] sbus_addr,
|
||||||
|
input wire sbus_write,
|
||||||
|
input wire [1:0] sbus_size,
|
||||||
|
input wire sbus_vld,
|
||||||
|
output wire sbus_rdy,
|
||||||
|
output wire sbus_err,
|
||||||
|
input wire [W_DATA-1:0] sbus_wdata,
|
||||||
|
output wire [W_DATA-1:0] sbus_rdata,
|
||||||
|
|
||||||
|
output wire [W_ADDR-1:0] ahblm_haddr,
|
||||||
|
output wire ahblm_hwrite,
|
||||||
|
output wire [1:0] ahblm_htrans,
|
||||||
|
output wire [2:0] ahblm_hsize,
|
||||||
|
output wire [2:0] ahblm_hburst,
|
||||||
|
output wire [3:0] ahblm_hprot,
|
||||||
|
output wire ahblm_hmastlock,
|
||||||
|
input wire ahblm_hready,
|
||||||
|
input wire ahblm_hresp,
|
||||||
|
output wire [W_DATA-1:0] ahblm_hwdata,
|
||||||
|
input wire [W_DATA-1:0] ahblm_hrdata
|
||||||
|
);
|
||||||
|
|
||||||
|
// Most signals are simple tie-throughs
|
||||||
|
|
||||||
|
assign ahblm_haddr = sbus_addr;
|
||||||
|
assign ahblm_hwrite = sbus_write;
|
||||||
|
assign ahblm_hsize = {1'b0, sbus_size};
|
||||||
|
assign ahblm_hwdata = sbus_wdata;
|
||||||
|
|
||||||
|
// HPROT = noncacheable nonbufferable privileged data access:
|
||||||
|
assign ahblm_hprot = 4'b0011;
|
||||||
|
assign ahblm_hmastlock = 1'b0;
|
||||||
|
assign ahblm_hburst = 3'h0;
|
||||||
|
|
||||||
|
assign sbus_err = ahblm_hresp;
|
||||||
|
assign sbus_rdata = ahblm_hrdata;
|
||||||
|
|
||||||
|
// Handshaking
|
||||||
|
|
||||||
|
reg dph_active;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
dph_active <= 1'b0;
|
||||||
|
end else if (ahblm_hready) begin
|
||||||
|
dph_active <= ahblm_htrans[1];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign ahblm_htrans = sbus_vld && !dph_active ? 2'b10 : 2'b00;
|
||||||
|
|
||||||
|
assign sbus_rdy = ahblm_hready && dph_active;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
file hazard3_ecp5_jtag_dtm.v
|
||||||
|
file hazard3_jtag_dtm_core.v
|
||||||
|
|
||||||
|
file ../cdc/hazard3_apb_async_bridge.v
|
||||||
|
file ../cdc/hazard3_reset_sync.v
|
||||||
|
file ../cdc/hazard3_sync_1bit.v
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// The ECP5 JTAGG primitive (yes that is the correct spelling) allows you to
|
||||||
|
// add two custom DRs to the FPGA's chip TAP, selected using the 8-bit ER1
|
||||||
|
// (0x32) and ER2 (0x38) instructions.
|
||||||
|
//
|
||||||
|
// Brian Swetland pointed out on Twitter that the standard RISC-V JTAG-DTM
|
||||||
|
// only uses two DRs (DTMCS and DMI), besides the standard IDCODE and BYPASS
|
||||||
|
// which are provided already by the ECP5 TAP. This file instantiates the
|
||||||
|
// guts of Hazard3's standard JTAG-DTM and connects the DTMCS and DMI
|
||||||
|
// registers to the JTAGG primitive's ER1/ER2 DRs.
|
||||||
|
//
|
||||||
|
// The exciting part is that upstream OpenOCD already allows you to set the IR
|
||||||
|
// length *and* set custom DTMCS/DMI IR values for RISC-V JTAG DTMs. This
|
||||||
|
// means with the right config file, you can access a debug module hung from
|
||||||
|
// the ECP5 TAP in this fashion using only upstream OpenOCD and gdb.
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_ecp5_jtag_dtm #(
|
||||||
|
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||||
|
parameter W_PADDR = 9,
|
||||||
|
parameter ABITS = W_PADDR - 2 // do not modify
|
||||||
|
) (
|
||||||
|
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||||
|
output wire dmihardreset_req,
|
||||||
|
|
||||||
|
// Bus clock + reset for Debug Module Interface
|
||||||
|
input wire clk_dmi,
|
||||||
|
input wire rst_n_dmi,
|
||||||
|
|
||||||
|
// Debug Module Interface (APB)
|
||||||
|
output wire dmi_psel,
|
||||||
|
output wire dmi_penable,
|
||||||
|
output wire dmi_pwrite,
|
||||||
|
output wire [W_PADDR-1:0] dmi_paddr,
|
||||||
|
output wire [31:0] dmi_pwdata,
|
||||||
|
input wire [31:0] dmi_prdata,
|
||||||
|
input wire dmi_pready,
|
||||||
|
input wire dmi_pslverr
|
||||||
|
);
|
||||||
|
|
||||||
|
// Signals to/from the ECP5 TAP
|
||||||
|
|
||||||
|
wire jtdo2;
|
||||||
|
wire jtdo1;
|
||||||
|
wire jtdi;
|
||||||
|
wire jtck_posedge_dont_use;
|
||||||
|
wire jshift;
|
||||||
|
wire jupdate;
|
||||||
|
wire jrst_n;
|
||||||
|
wire jce2;
|
||||||
|
wire jce1;
|
||||||
|
|
||||||
|
JTAGG jtag_u (
|
||||||
|
.JTDO2 (jtdo2),
|
||||||
|
.JTDO1 (jtdo1),
|
||||||
|
.JTDI (jtdi),
|
||||||
|
.JTCK (jtck_posedge_dont_use),
|
||||||
|
.JRTI2 (/* unused */),
|
||||||
|
.JRTI1 (/* unused */),
|
||||||
|
.JSHIFT (jshift),
|
||||||
|
.JUPDATE (jupdate),
|
||||||
|
.JRSTN (jrst_n),
|
||||||
|
.JCE2 (jce2),
|
||||||
|
.JCE1 (jce1)
|
||||||
|
);
|
||||||
|
|
||||||
|
// JTAGG primitive asserts its signals synchronously to JTCK's posedge, but
|
||||||
|
// you get weird and inconsistent results if you try to consume them
|
||||||
|
// synchronously on JTCK's posedge, possibly due to a lack of hold
|
||||||
|
// constraints in nextpnr.
|
||||||
|
//
|
||||||
|
// A quick hack is to move the sampling onto the negedge of the clock. This
|
||||||
|
// then creates more problems because we would be running our shift logic on
|
||||||
|
// a different edge from the control + CDC logic in the DTM core.
|
||||||
|
//
|
||||||
|
// So, even worse hack, move all our JTAG-domain logic onto the negedge
|
||||||
|
// (or near enough) by inverting the clock.
|
||||||
|
|
||||||
|
wire jtck = !jtck_posedge_dont_use;
|
||||||
|
|
||||||
|
localparam W_DR_SHIFT = ABITS + 32 + 2;
|
||||||
|
|
||||||
|
reg core_dr_wen;
|
||||||
|
reg core_dr_ren;
|
||||||
|
reg core_dr_sel_dmi_ndtmcs;
|
||||||
|
reg dr_shift_en;
|
||||||
|
wire [W_DR_SHIFT-1:0] core_dr_wdata;
|
||||||
|
wire [W_DR_SHIFT-1:0] core_dr_rdata;
|
||||||
|
|
||||||
|
// Decode our shift controls from the interesting ECP5 ones, and re-register
|
||||||
|
// onto JTCK negedge (our posedge). Note without re-registering we observe
|
||||||
|
// them a half-cycle (effectively one cycle) too early. This is another
|
||||||
|
// consequence of the stupid JTDI thing
|
||||||
|
|
||||||
|
always @ (posedge jtck or negedge jrst_n) begin
|
||||||
|
if (!jrst_n) begin
|
||||||
|
core_dr_sel_dmi_ndtmcs <= 1'b0;
|
||||||
|
core_dr_wen <= 1'b0;
|
||||||
|
core_dr_ren <= 1'b0;
|
||||||
|
dr_shift_en <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
if (jce1 || jce2)
|
||||||
|
core_dr_sel_dmi_ndtmcs <= jce2;
|
||||||
|
core_dr_ren <= (jce1 || jce2) && !jshift;
|
||||||
|
core_dr_wen <= jupdate;
|
||||||
|
dr_shift_en <= jshift;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
reg [W_DR_SHIFT-1:0] dr_shift;
|
||||||
|
assign core_dr_wdata = dr_shift;
|
||||||
|
|
||||||
|
always @ (posedge jtck or negedge jrst_n) begin
|
||||||
|
if (!jrst_n) begin
|
||||||
|
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||||
|
end else if (core_dr_ren) begin
|
||||||
|
dr_shift <= core_dr_rdata;
|
||||||
|
end else if (dr_shift_en) begin
|
||||||
|
dr_shift <= {jtdi, dr_shift[W_DR_SHIFT-1:1]};
|
||||||
|
if (!core_dr_sel_dmi_ndtmcs)
|
||||||
|
dr_shift[31] <= jtdi;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Not documented on ECP5: as well as the posedge flop on JTDI, the ECP5 puts
|
||||||
|
// a negedge flop on JTDO1, JTDO2. (Conjecture based on dicking around with a
|
||||||
|
// logic analyser.) To get JTDOx to appear with the same timing as our shifter
|
||||||
|
// LSB (which we update on every JTCK negedge) we:
|
||||||
|
//
|
||||||
|
// - Register the LSB of the *next* value of dr_shift on the JTCK posedge, so
|
||||||
|
// half a cycle earlier than the actual dr_shift update
|
||||||
|
//
|
||||||
|
// - This then gets re-registered with the pointless JTDO negedge flops, so
|
||||||
|
// that it appears with the same timing as our DR shifter update.
|
||||||
|
|
||||||
|
reg dr_shift_next_halfcycle;
|
||||||
|
always @ (negedge jtck or negedge jrst_n) begin
|
||||||
|
if (!jrst_n) begin
|
||||||
|
dr_shift_next_halfcycle <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
dr_shift_next_halfcycle <=
|
||||||
|
core_dr_ren ? core_dr_rdata[0] :
|
||||||
|
dr_shift_en ? dr_shift[1] : dr_shift[0];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// We have only a single shifter for the ER1 and ER2 chains, so these are tied
|
||||||
|
// together:
|
||||||
|
|
||||||
|
assign jtdo1 = dr_shift_next_halfcycle;
|
||||||
|
assign jtdo2 = dr_shift_next_halfcycle;
|
||||||
|
|
||||||
|
// The actual DTM is in here:
|
||||||
|
|
||||||
|
hazard3_jtag_dtm_core #(
|
||||||
|
.DTMCS_IDLE_HINT (DTMCS_IDLE_HINT),
|
||||||
|
.W_ADDR (ABITS)
|
||||||
|
) inst_hazard3_jtag_dtm_core (
|
||||||
|
.tck (jtck),
|
||||||
|
.trst_n (jrst_n),
|
||||||
|
|
||||||
|
.clk_dmi (clk_dmi),
|
||||||
|
.rst_n_dmi (rst_n_dmi),
|
||||||
|
|
||||||
|
.dr_wen (core_dr_wen),
|
||||||
|
.dr_ren (core_dr_ren),
|
||||||
|
.dr_sel_dmi_ndtmcs (core_dr_sel_dmi_ndtmcs),
|
||||||
|
.dr_wdata (core_dr_wdata),
|
||||||
|
.dr_rdata (core_dr_rdata),
|
||||||
|
|
||||||
|
.dmihardreset_req (dmihardreset_req),
|
||||||
|
|
||||||
|
.dmi_psel (dmi_psel),
|
||||||
|
.dmi_penable (dmi_penable),
|
||||||
|
.dmi_pwrite (dmi_pwrite),
|
||||||
|
.dmi_paddr (dmi_paddr[W_PADDR-1:2]),
|
||||||
|
.dmi_pwdata (dmi_pwdata),
|
||||||
|
.dmi_prdata (dmi_prdata),
|
||||||
|
.dmi_pready (dmi_pready),
|
||||||
|
.dmi_pslverr (dmi_pslverr)
|
||||||
|
);
|
||||||
|
|
||||||
|
assign dmi_paddr[1:0] = 2'b00;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
file hazard3_jtag_dtm.v
|
||||||
|
file hazard3_jtag_dtm_core.v
|
||||||
|
|
||||||
|
file ../cdc/hazard3_apb_async_bridge.v
|
||||||
|
file ../cdc/hazard3_reset_sync.v
|
||||||
|
file ../cdc/hazard3_sync_1bit.v
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// Implementation of standard RISC-V JTAG-DTM with an APB Debug Module
|
||||||
|
// Interface. The TAP itself is clocked directly by JTAG TCK; a clock
|
||||||
|
// crossing is instantiated internally between the TCK domain and the DMI bus
|
||||||
|
// clock domain.
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_jtag_dtm #(
|
||||||
|
parameter IDCODE = 32'h0000_0001,
|
||||||
|
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||||
|
parameter W_PADDR = 9,
|
||||||
|
parameter ABITS = W_PADDR - 2 // do not modify
|
||||||
|
) (
|
||||||
|
// Standard JTAG signals -- the JTAG hardware is clocked directly by TCK.
|
||||||
|
input wire tck,
|
||||||
|
input wire trst_n,
|
||||||
|
input wire tms,
|
||||||
|
input wire tdi,
|
||||||
|
output reg tdo,
|
||||||
|
|
||||||
|
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||||
|
output wire dmihardreset_req,
|
||||||
|
|
||||||
|
// Bus clock + reset for Debug Module Interface
|
||||||
|
input wire clk_dmi,
|
||||||
|
input wire rst_n_dmi,
|
||||||
|
|
||||||
|
// Debug Module Interface (APB)
|
||||||
|
output wire dmi_psel,
|
||||||
|
output wire dmi_penable,
|
||||||
|
output wire dmi_pwrite,
|
||||||
|
output wire [W_PADDR-1:0] dmi_paddr,
|
||||||
|
output wire [31:0] dmi_pwdata,
|
||||||
|
input wire [31:0] dmi_prdata,
|
||||||
|
input wire dmi_pready,
|
||||||
|
input wire dmi_pslverr
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// TAP state machine
|
||||||
|
|
||||||
|
reg [3:0] tap_state;
|
||||||
|
localparam S_RESET = 4'd0;
|
||||||
|
localparam S_RUN_IDLE = 4'd1;
|
||||||
|
localparam S_SELECT_DR = 4'd2;
|
||||||
|
localparam S_CAPTURE_DR = 4'd3;
|
||||||
|
localparam S_SHIFT_DR = 4'd4;
|
||||||
|
localparam S_EXIT1_DR = 4'd5;
|
||||||
|
localparam S_PAUSE_DR = 4'd6;
|
||||||
|
localparam S_EXIT2_DR = 4'd7;
|
||||||
|
localparam S_UPDATE_DR = 4'd8;
|
||||||
|
localparam S_SELECT_IR = 4'd9;
|
||||||
|
localparam S_CAPTURE_IR = 4'd10;
|
||||||
|
localparam S_SHIFT_IR = 4'd11;
|
||||||
|
localparam S_EXIT1_IR = 4'd12;
|
||||||
|
localparam S_PAUSE_IR = 4'd13;
|
||||||
|
localparam S_EXIT2_IR = 4'd14;
|
||||||
|
localparam S_UPDATE_IR = 4'd15;
|
||||||
|
|
||||||
|
always @ (posedge tck or negedge trst_n) begin
|
||||||
|
if (!trst_n) begin
|
||||||
|
tap_state <= S_RESET;
|
||||||
|
end else case(tap_state)
|
||||||
|
S_RESET : tap_state <= tms ? S_RESET : S_RUN_IDLE ;
|
||||||
|
S_RUN_IDLE : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ;
|
||||||
|
|
||||||
|
S_SELECT_DR : tap_state <= tms ? S_SELECT_IR : S_CAPTURE_DR;
|
||||||
|
S_CAPTURE_DR : tap_state <= tms ? S_EXIT1_DR : S_SHIFT_DR ;
|
||||||
|
S_SHIFT_DR : tap_state <= tms ? S_EXIT1_DR : S_SHIFT_DR ;
|
||||||
|
S_EXIT1_DR : tap_state <= tms ? S_UPDATE_DR : S_PAUSE_DR ;
|
||||||
|
S_PAUSE_DR : tap_state <= tms ? S_EXIT2_DR : S_PAUSE_DR ;
|
||||||
|
S_EXIT2_DR : tap_state <= tms ? S_UPDATE_DR : S_SHIFT_DR ;
|
||||||
|
S_UPDATE_DR : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ;
|
||||||
|
|
||||||
|
S_SELECT_IR : tap_state <= tms ? S_RESET : S_CAPTURE_IR;
|
||||||
|
S_CAPTURE_IR : tap_state <= tms ? S_EXIT1_IR : S_SHIFT_IR ;
|
||||||
|
S_SHIFT_IR : tap_state <= tms ? S_EXIT1_IR : S_SHIFT_IR ;
|
||||||
|
S_EXIT1_IR : tap_state <= tms ? S_UPDATE_IR : S_PAUSE_IR ;
|
||||||
|
S_PAUSE_IR : tap_state <= tms ? S_EXIT2_IR : S_PAUSE_IR ;
|
||||||
|
S_EXIT2_IR : tap_state <= tms ? S_UPDATE_IR : S_SHIFT_IR ;
|
||||||
|
S_UPDATE_IR : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Instruction register
|
||||||
|
|
||||||
|
localparam W_IR = 5;
|
||||||
|
// All other encodings behave as BYPASS:
|
||||||
|
localparam IR_IDCODE = 5'h01;
|
||||||
|
localparam IR_DTMCS = 5'h10;
|
||||||
|
localparam IR_DMI = 5'h11;
|
||||||
|
|
||||||
|
reg [W_IR-1:0] ir_shift;
|
||||||
|
reg [W_IR-1:0] ir;
|
||||||
|
|
||||||
|
always @ (posedge tck or negedge trst_n) begin
|
||||||
|
if (!trst_n) begin
|
||||||
|
ir_shift <= {W_IR{1'b0}};
|
||||||
|
ir <= IR_IDCODE;
|
||||||
|
end else if (tap_state == S_RESET) begin
|
||||||
|
ir_shift <= {W_IR{1'b0}};
|
||||||
|
ir <= IR_IDCODE;
|
||||||
|
end else if (tap_state == S_CAPTURE_IR) begin
|
||||||
|
ir_shift <= ir;
|
||||||
|
end else if (tap_state == S_SHIFT_IR) begin
|
||||||
|
ir_shift <= {tdi, ir_shift[W_IR-1:1]};
|
||||||
|
end else if (tap_state == S_UPDATE_IR) begin
|
||||||
|
ir <= ir_shift;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Data registers
|
||||||
|
|
||||||
|
// Shift register is sized to largest DR, which is DMI:
|
||||||
|
// {addr[7:0], data[31:0], op[1:0]}
|
||||||
|
localparam W_DR_SHIFT = ABITS + 32 + 2;
|
||||||
|
|
||||||
|
reg [W_DR_SHIFT-1:0] dr_shift;
|
||||||
|
|
||||||
|
// Signals to/from the DTM core, which implements the DTMCS and DMI registers
|
||||||
|
wire core_dr_wen;
|
||||||
|
wire core_dr_ren;
|
||||||
|
wire core_dr_sel_dmi_ndtmcs;
|
||||||
|
wire [W_DR_SHIFT-1:0] core_dr_wdata;
|
||||||
|
wire [W_DR_SHIFT-1:0] core_dr_rdata;
|
||||||
|
|
||||||
|
always @ (posedge tck or negedge trst_n) begin
|
||||||
|
if (!trst_n) begin
|
||||||
|
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||||
|
end else if (tap_state == S_SHIFT_DR) begin
|
||||||
|
dr_shift <= {tdi, dr_shift[W_DR_SHIFT-1:1]};
|
||||||
|
// Shorten DR shift chain according to IR
|
||||||
|
if (ir == IR_DMI)
|
||||||
|
dr_shift[W_DR_SHIFT - 1] <= tdi;
|
||||||
|
else if (ir == IR_IDCODE || ir == IR_DTMCS)
|
||||||
|
dr_shift[31] <= tdi;
|
||||||
|
else // BYPASS
|
||||||
|
dr_shift[0] <= tdi;
|
||||||
|
end else if (tap_state == S_CAPTURE_DR) begin
|
||||||
|
if (ir == IR_DMI || ir == IR_DTMCS) begin
|
||||||
|
dr_shift <= core_dr_rdata;
|
||||||
|
end else if (ir == IR_IDCODE) begin
|
||||||
|
dr_shift <= {{W_DR_SHIFT-32{1'b0}}, IDCODE};
|
||||||
|
end else begin // BYPASS
|
||||||
|
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Must retime shift data onto negedge before presenting on TDO
|
||||||
|
|
||||||
|
always @ (negedge tck or negedge trst_n) begin
|
||||||
|
if (!trst_n) begin
|
||||||
|
tdo <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
tdo <= tap_state == S_SHIFT_IR ? ir_shift[0] :
|
||||||
|
tap_state == S_SHIFT_DR ? dr_shift[0] : 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Core logic and bus interface
|
||||||
|
|
||||||
|
assign core_dr_sel_dmi_ndtmcs = ir == IR_DMI;
|
||||||
|
assign core_dr_wen = (ir == IR_DMI || ir == IR_DTMCS) && tap_state == S_UPDATE_DR;
|
||||||
|
assign core_dr_ren = (ir == IR_DMI || ir == IR_DTMCS) && tap_state == S_CAPTURE_DR;
|
||||||
|
|
||||||
|
assign core_dr_wdata = dr_shift;
|
||||||
|
|
||||||
|
hazard3_jtag_dtm_core #(
|
||||||
|
.DTMCS_IDLE_HINT (DTMCS_IDLE_HINT),
|
||||||
|
.W_ADDR (ABITS)
|
||||||
|
) dtm_core (
|
||||||
|
.tck (tck),
|
||||||
|
.trst_n (trst_n),
|
||||||
|
.clk_dmi (clk_dmi),
|
||||||
|
.rst_n_dmi (rst_n_dmi),
|
||||||
|
|
||||||
|
.dmihardreset_req (dmihardreset_req),
|
||||||
|
|
||||||
|
.dr_wen (core_dr_wen),
|
||||||
|
.dr_ren (core_dr_ren),
|
||||||
|
.dr_sel_dmi_ndtmcs (core_dr_sel_dmi_ndtmcs),
|
||||||
|
.dr_wdata (core_dr_wdata),
|
||||||
|
.dr_rdata (core_dr_rdata),
|
||||||
|
|
||||||
|
.dmi_psel (dmi_psel),
|
||||||
|
.dmi_penable (dmi_penable),
|
||||||
|
.dmi_pwrite (dmi_pwrite),
|
||||||
|
.dmi_paddr (dmi_paddr[W_PADDR-1:2]),
|
||||||
|
.dmi_pwdata (dmi_pwdata),
|
||||||
|
.dmi_prdata (dmi_prdata),
|
||||||
|
.dmi_pready (dmi_pready),
|
||||||
|
.dmi_pslverr (dmi_pslverr)
|
||||||
|
);
|
||||||
|
|
||||||
|
assign dmi_paddr[1:0] = 2'b00;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// DTMCS + DMI control logic, bus interface and bus clock domain crossing for
|
||||||
|
// a standard RISC-V APB JTAG-DTM. Essentially everything apart from the
|
||||||
|
// actual TAP controller, IR and shift registers. Instantiated by
|
||||||
|
// hazard3_jtag_dtm.v.
|
||||||
|
//
|
||||||
|
// This core logic can be reused and connected to some other serial transport
|
||||||
|
// or, for example, the ECP5 JTAGG primitive (see hazard5_ecp5_jtag_dtm.v)
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_jtag_dtm_core #(
|
||||||
|
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||||
|
parameter W_ADDR = 8,
|
||||||
|
parameter W_DR_SHIFT = W_ADDR + 32 + 2 // do not modify
|
||||||
|
) (
|
||||||
|
input wire tck,
|
||||||
|
input wire trst_n,
|
||||||
|
|
||||||
|
input wire clk_dmi,
|
||||||
|
input wire rst_n_dmi,
|
||||||
|
|
||||||
|
// DR capture/update (read/write) signals
|
||||||
|
input wire dr_wen,
|
||||||
|
input wire dr_ren,
|
||||||
|
input wire dr_sel_dmi_ndtmcs,
|
||||||
|
input wire [W_DR_SHIFT-1:0] dr_wdata,
|
||||||
|
output wire [W_DR_SHIFT-1:0] dr_rdata,
|
||||||
|
|
||||||
|
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||||
|
output reg dmihardreset_req,
|
||||||
|
|
||||||
|
// Debug Module Interface (APB)
|
||||||
|
output wire dmi_psel,
|
||||||
|
output wire dmi_penable,
|
||||||
|
output wire dmi_pwrite,
|
||||||
|
output wire [W_ADDR-1:0] dmi_paddr,
|
||||||
|
output wire [31:0] dmi_pwdata,
|
||||||
|
input wire [31:0] dmi_prdata,
|
||||||
|
input wire dmi_pready,
|
||||||
|
input wire dmi_pslverr
|
||||||
|
);
|
||||||
|
|
||||||
|
wire write_dmi = dr_wen && dr_sel_dmi_ndtmcs;
|
||||||
|
wire write_dtmcs = dr_wen && !dr_sel_dmi_ndtmcs;
|
||||||
|
wire read_dmi = dr_ren && dr_sel_dmi_ndtmcs;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DMI bus adapter
|
||||||
|
|
||||||
|
reg [1:0] dmi_cmderr;
|
||||||
|
reg dmi_busy;
|
||||||
|
|
||||||
|
// DTM-domain bus, connected to a matching DM-domain bus via an APB crossing:
|
||||||
|
wire dtm_psel;
|
||||||
|
wire dtm_penable;
|
||||||
|
wire dtm_pwrite;
|
||||||
|
wire [W_ADDR-1:0] dtm_paddr;
|
||||||
|
wire [31:0] dtm_pwdata;
|
||||||
|
wire [31:0] dtm_prdata;
|
||||||
|
wire dtm_pready;
|
||||||
|
wire dtm_pslverr;
|
||||||
|
|
||||||
|
// We are relying on some particular features of our APB clock crossing here
|
||||||
|
// to save some registers:
|
||||||
|
//
|
||||||
|
// - The transfer is launched immediately when psel is seen, no need to
|
||||||
|
// actually assert an access phase (as the standard allows the CDC to
|
||||||
|
// assume that access immediately follows setup) and no need to maintain
|
||||||
|
// pwrite/paddr/pwdata valid after the setup phase
|
||||||
|
//
|
||||||
|
// - prdata/pslverr remain valid after the transfer completes, until the next
|
||||||
|
// transfer completes
|
||||||
|
//
|
||||||
|
// These allow us to connect the upstream side of the CDC directly to our DR
|
||||||
|
// shifter without any sample/hold registers in between.
|
||||||
|
|
||||||
|
// psel is only pulsed for one cycle, penable is not asserted.
|
||||||
|
assign dtm_psel = write_dmi &&
|
||||||
|
(dr_wdata[1:0] == 2'd1 || dr_wdata[1:0] == 2'd2) &&
|
||||||
|
!(dmi_busy || dmi_cmderr != 2'd0) && dtm_pready;
|
||||||
|
assign dtm_penable = 1'b0;
|
||||||
|
|
||||||
|
// paddr/pwdata/pwrite are valid momentarily when psel is asserted.
|
||||||
|
assign dtm_paddr = dr_wdata[34 +: W_ADDR];
|
||||||
|
assign dtm_pwrite = dr_wdata[1];
|
||||||
|
assign dtm_pwdata = dr_wdata[2 +: 32];
|
||||||
|
|
||||||
|
always @ (posedge tck or negedge trst_n) begin
|
||||||
|
if (!trst_n) begin
|
||||||
|
dmi_busy <= 1'b0;
|
||||||
|
dmi_cmderr <= 2'd0;
|
||||||
|
end else if (read_dmi) begin
|
||||||
|
// Reading while busy sets the busy sticky error. Note the capture
|
||||||
|
// into shift register should also reflect this update on-the-fly
|
||||||
|
if (dmi_busy && dmi_cmderr == 2'd0)
|
||||||
|
dmi_cmderr <= 2'h3;
|
||||||
|
end else if (write_dtmcs) begin
|
||||||
|
// Writing dtmcs.dmireset = 1 clears a sticky error
|
||||||
|
if (dr_wdata[16])
|
||||||
|
dmi_cmderr <= 2'd0;
|
||||||
|
end else if (write_dmi) begin
|
||||||
|
if (dtm_psel) begin
|
||||||
|
dmi_busy <= 1'b1;
|
||||||
|
end else if (dr_wdata[1:0] != 2'd0) begin
|
||||||
|
// DMI ignored operation, so set sticky busy
|
||||||
|
if (dmi_cmderr == 2'd0)
|
||||||
|
dmi_cmderr <= 2'd3;
|
||||||
|
end
|
||||||
|
end else if (dmi_busy && dtm_pready) begin
|
||||||
|
dmi_busy <= 1'b0;
|
||||||
|
if (dmi_cmderr == 2'd0 && dtm_pslverr)
|
||||||
|
dmi_cmderr <= 2'd2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// DTM logic is in TCK domain, actual DMI + DM is in processor domain
|
||||||
|
|
||||||
|
hazard3_apb_async_bridge #(
|
||||||
|
.W_ADDR (W_ADDR),
|
||||||
|
.W_DATA (32),
|
||||||
|
.N_SYNC_STAGES (2)
|
||||||
|
) inst_hazard3_apb_async_bridge (
|
||||||
|
.clk_src (tck),
|
||||||
|
.rst_n_src (trst_n),
|
||||||
|
|
||||||
|
.clk_dst (clk_dmi),
|
||||||
|
.rst_n_dst (rst_n_dmi),
|
||||||
|
|
||||||
|
.src_psel (dtm_psel),
|
||||||
|
.src_penable (dtm_penable),
|
||||||
|
.src_pwrite (dtm_pwrite),
|
||||||
|
.src_paddr (dtm_paddr),
|
||||||
|
.src_pwdata (dtm_pwdata),
|
||||||
|
.src_prdata (dtm_prdata),
|
||||||
|
.src_pready (dtm_pready),
|
||||||
|
.src_pslverr (dtm_pslverr),
|
||||||
|
|
||||||
|
.dst_psel (dmi_psel),
|
||||||
|
.dst_penable (dmi_penable),
|
||||||
|
.dst_pwrite (dmi_pwrite),
|
||||||
|
.dst_paddr (dmi_paddr),
|
||||||
|
.dst_pwdata (dmi_pwdata),
|
||||||
|
.dst_prdata (dmi_prdata),
|
||||||
|
.dst_pready (dmi_pready),
|
||||||
|
.dst_pslverr (dmi_pslverr)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// DR read/write
|
||||||
|
|
||||||
|
wire [W_DR_SHIFT-1:0] dtmcs_rdata = {
|
||||||
|
{W_ADDR{1'b0}},
|
||||||
|
19'h0,
|
||||||
|
DTMCS_IDLE_HINT[2:0],
|
||||||
|
dmi_cmderr,
|
||||||
|
W_ADDR[5:0], // abits
|
||||||
|
4'd1 // version
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [W_DR_SHIFT-1:0] dmi_rdata = {
|
||||||
|
{W_ADDR{1'b0}},
|
||||||
|
dtm_prdata,
|
||||||
|
dmi_busy && dmi_cmderr == 2'd0 ? 2'd3 : dmi_cmderr
|
||||||
|
};
|
||||||
|
|
||||||
|
assign dr_rdata = dr_sel_dmi_ndtmcs ? dmi_rdata : dtmcs_rdata;
|
||||||
|
|
||||||
|
always @ (posedge tck or negedge trst_n) begin
|
||||||
|
if (!trst_n) begin
|
||||||
|
dmihardreset_req <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
dmihardreset_req <= write_dtmcs && dr_wdata[17];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2025 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// Implement a RISC-V JTAG DTM tunnelled through a Xilinx BSCANE2 primitive.
|
||||||
|
//
|
||||||
|
// Xilinx allows up to four custom DRs to be added to the FPGA TAP controller.
|
||||||
|
// A JTAG-DTM only needs two: DTMCS and DMI.
|
||||||
|
//
|
||||||
|
// With the correct config, OpenOCD can treat the FPGA TAP as a JTAG DTM and
|
||||||
|
// access RISC-V debug directly. This allows you to debug internal RISC-V
|
||||||
|
// cores with the same JTAG interface you use to load the FPGA.
|
||||||
|
//
|
||||||
|
// CHAIN_DTMCS and CHAIN_DMI select which JTAG IR values are used to access
|
||||||
|
// these DRs. Values 1 through 4 correspond to Xilinx USER1 through USER4
|
||||||
|
// instructions, which have IR values 0x02, 0x03, 0x22, 0x23.
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_xilinx7_jtag_dtm #(
|
||||||
|
parameter SEL_DTMCS = 3,
|
||||||
|
parameter SEL_DMI = 4,
|
||||||
|
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||||
|
parameter W_PADDR = 9,
|
||||||
|
parameter ABITS = W_PADDR - 2 // do not modify
|
||||||
|
) (
|
||||||
|
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||||
|
output wire dmihardreset_req,
|
||||||
|
|
||||||
|
// Bus clock + reset for Debug Module Interface
|
||||||
|
input wire clk_dmi,
|
||||||
|
input wire rst_n_dmi,
|
||||||
|
|
||||||
|
// Debug Module Interface (APB)
|
||||||
|
output wire dmi_psel,
|
||||||
|
output wire dmi_penable,
|
||||||
|
output wire dmi_pwrite,
|
||||||
|
output wire [W_PADDR-1:0] dmi_paddr,
|
||||||
|
output wire [31:0] dmi_pwdata,
|
||||||
|
input wire [31:0] dmi_prdata,
|
||||||
|
input wire dmi_pready,
|
||||||
|
input wire dmi_pslverr
|
||||||
|
);
|
||||||
|
|
||||||
|
// Signals to/from the Xilinx TAP
|
||||||
|
|
||||||
|
wire jtck_unbuf;
|
||||||
|
wire jtck;
|
||||||
|
wire jtdo2;
|
||||||
|
wire jtdo1;
|
||||||
|
wire jtdi;
|
||||||
|
wire jshift;
|
||||||
|
wire jupdate;
|
||||||
|
wire jcapture;
|
||||||
|
wire jrst;
|
||||||
|
wire jrst_n = !jrst;
|
||||||
|
wire jce2;
|
||||||
|
wire jce1;
|
||||||
|
|
||||||
|
BSCANE2 #(
|
||||||
|
.JTAG_CHAIN (SEL_DTMCS) // Value for USER command.
|
||||||
|
) bscan_dtmcs (
|
||||||
|
.CAPTURE (jcapture), // CAPTURE output from TAP controller.
|
||||||
|
.DRCK (/* unused */), // Gated TCK output. When SEL is asserted, DRCK toggles when CAPTURE or SHIFT are asserted.
|
||||||
|
.RESET (jrst), // Reset output for TAP controller.
|
||||||
|
.RUNTEST (/* unused */), // Output asserted when TAP controller is in Run Test/Idle state.
|
||||||
|
.SEL (jce1), // USER instruction active output.
|
||||||
|
.SHIFT (jshift), // SHIFT output from TAP controller.
|
||||||
|
.TCK (jtck_unbuf), // Test Clock output. Fabric connection to TAP Clock pin.
|
||||||
|
.TDI (jtdi), // Test Data Input (TDI) output from TAP controller.
|
||||||
|
.TMS (/* unused */), // Test Mode Select output. Fabric connection to TAP.
|
||||||
|
.UPDATE (jupdate), // UPDATE output from TAP controller
|
||||||
|
.TDO (jtdo1) // Test Data Output (TDO) input for USER function.
|
||||||
|
);
|
||||||
|
|
||||||
|
BSCANE2 #(
|
||||||
|
.JTAG_CHAIN (SEL_DMI)
|
||||||
|
) bscan_dmi (
|
||||||
|
.CAPTURE (/* unused */),
|
||||||
|
.DRCK (/* unused */),
|
||||||
|
.RESET (/* unused */),
|
||||||
|
.RUNTEST (/* unused */),
|
||||||
|
.SEL (jce2),
|
||||||
|
.SHIFT (/* unused */),
|
||||||
|
.TCK (/* unused */),
|
||||||
|
.TDI (/* unused */),
|
||||||
|
.TMS (/* unused */),
|
||||||
|
.UPDATE (/* unused */),
|
||||||
|
.TDO (jtdo2)
|
||||||
|
);
|
||||||
|
|
||||||
|
BUFG bufg_jtck (
|
||||||
|
.I (jtck_unbuf),
|
||||||
|
.O (jtck)
|
||||||
|
);
|
||||||
|
|
||||||
|
localparam W_DR_SHIFT = ABITS + 32 + 2;
|
||||||
|
|
||||||
|
wire core_dr_wen = jupdate;
|
||||||
|
wire core_dr_ren = jcapture;
|
||||||
|
wire core_dr_sel_dmi_ndtmcs = !jce1;
|
||||||
|
wire dr_shift_en = jshift;
|
||||||
|
wire [W_DR_SHIFT-1:0] core_dr_wdata;
|
||||||
|
wire [W_DR_SHIFT-1:0] core_dr_rdata;
|
||||||
|
|
||||||
|
reg [W_DR_SHIFT-1:0] dr_shift;
|
||||||
|
assign core_dr_wdata = dr_shift;
|
||||||
|
|
||||||
|
always @ (posedge jtck or negedge jrst_n) begin
|
||||||
|
if (!jrst_n) begin
|
||||||
|
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||||
|
end else if (core_dr_ren) begin
|
||||||
|
dr_shift <= core_dr_rdata;
|
||||||
|
end else if (dr_shift_en) begin
|
||||||
|
dr_shift <= {jtdi, dr_shift[W_DR_SHIFT-1:1]};
|
||||||
|
if (!core_dr_sel_dmi_ndtmcs)
|
||||||
|
dr_shift[31] <= jtdi;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// We have only a single shifter for the two DRs, so these are tied together:
|
||||||
|
assign jtdo1 = dr_shift[0];
|
||||||
|
assign jtdo2 = dr_shift[0];
|
||||||
|
|
||||||
|
// The actual DTM is in here:
|
||||||
|
|
||||||
|
hazard3_jtag_dtm_core #(
|
||||||
|
.DTMCS_IDLE_HINT (DTMCS_IDLE_HINT),
|
||||||
|
.W_ADDR (ABITS)
|
||||||
|
) inst_hazard3_jtag_dtm_core (
|
||||||
|
.tck (jtck),
|
||||||
|
.trst_n (jrst_n),
|
||||||
|
|
||||||
|
.clk_dmi (clk_dmi),
|
||||||
|
.rst_n_dmi (rst_n_dmi),
|
||||||
|
|
||||||
|
.dr_wen (core_dr_wen),
|
||||||
|
.dr_ren (core_dr_ren),
|
||||||
|
.dr_sel_dmi_ndtmcs (core_dr_sel_dmi_ndtmcs),
|
||||||
|
.dr_wdata (core_dr_wdata),
|
||||||
|
.dr_rdata (core_dr_rdata),
|
||||||
|
|
||||||
|
.dmihardreset_req (dmihardreset_req),
|
||||||
|
|
||||||
|
.dmi_psel (dmi_psel),
|
||||||
|
.dmi_penable (dmi_penable),
|
||||||
|
.dmi_pwrite (dmi_pwrite),
|
||||||
|
.dmi_paddr (dmi_paddr[W_PADDR-1:2]),
|
||||||
|
.dmi_pwdata (dmi_pwdata),
|
||||||
|
.dmi_prdata (dmi_prdata),
|
||||||
|
.dmi_pready (dmi_pready),
|
||||||
|
.dmi_pslverr (dmi_pslverr)
|
||||||
|
);
|
||||||
|
|
||||||
|
assign dmi_paddr[1:0] = 2'b00;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
file hazard3_core.v
|
||||||
|
file hazard3_cpu_1port.v
|
||||||
|
file hazard3_cpu_2port.v
|
||||||
|
file arith/hazard3_alu.v
|
||||||
|
file arith/hazard3_branchcmp.v
|
||||||
|
file arith/hazard3_mul_fast.v
|
||||||
|
file arith/hazard3_muldiv_seq.v
|
||||||
|
file arith/hazard3_onehot_encode.v
|
||||||
|
file arith/hazard3_onehot_priority.v
|
||||||
|
file arith/hazard3_onehot_priority_dynamic.v
|
||||||
|
file arith/hazard3_priority_encode.v
|
||||||
|
file arith/hazard3_shift_barrel.v
|
||||||
|
file hazard3_csr.v
|
||||||
|
file hazard3_decode.v
|
||||||
|
file hazard3_frontend.v
|
||||||
|
file hazard3_instr_decompress.v
|
||||||
|
file hazard3_irq_ctrl.v
|
||||||
|
file hazard3_pmp.v
|
||||||
|
file hazard3_power_ctrl.v
|
||||||
|
file hazard3_regfile_1w2r.v
|
||||||
|
file hazard3_triggers.v
|
||||||
|
include .
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// Hazard3 CPU configuration parameters
|
||||||
|
|
||||||
|
// To configure Hazard3 you can either edit this file, or set parameters on
|
||||||
|
// your top-level instantiation, it's up to you. These parameters are all
|
||||||
|
// plumbed through Hazard3's internal hierarchy to the appropriate places.
|
||||||
|
|
||||||
|
// If you add a parameter here, you should add a matching line to
|
||||||
|
// hazard3_config_inst.vh to propagate the parameter through module
|
||||||
|
// instantiations.
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Reset state configuration
|
||||||
|
|
||||||
|
// RESET_VECTOR: Address of first instruction executed.
|
||||||
|
parameter RESET_VECTOR = 32'h00000000,
|
||||||
|
|
||||||
|
// MTVEC_INIT: Initial value of trap vector base. Bits clear in MTVEC_WMASK
|
||||||
|
// will never change from this initial value. Bits set in MTVEC_WMASK can be
|
||||||
|
// written/set/cleared as normal.
|
||||||
|
//
|
||||||
|
// Note that mtvec bits 1:0 do not affect the trap base (as per RISC-V spec).
|
||||||
|
// Bit 1 is don't care, bit 0 selects the vectoring mode: unvectored if == 0
|
||||||
|
// (all traps go to mtvec), vectored if == 1 (exceptions go to mtvec, IRQs to
|
||||||
|
// mtvec + mcause * 4). This means MTVEC_INIT also sets the initial vectoring
|
||||||
|
// mode.
|
||||||
|
parameter MTVEC_INIT = 32'h00000000,
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Standard RISC-V ISA support
|
||||||
|
|
||||||
|
// EXTENSION_A: Support for atomic read/modify/write instructions
|
||||||
|
parameter EXTENSION_A = 1,
|
||||||
|
|
||||||
|
// EXTENSION_C: Support for compressed (variable-width) instructions
|
||||||
|
parameter EXTENSION_C = 1,
|
||||||
|
|
||||||
|
// EXTENSION_E: Implement the RV32E base extension rather than RV32I. This
|
||||||
|
// reduces the number of integer registers from 31 to 15.
|
||||||
|
parameter EXTENSION_E = 0,
|
||||||
|
|
||||||
|
// EXTENSION_M: Support for hardware multiply/divide/modulo instructions
|
||||||
|
parameter EXTENSION_M = 1,
|
||||||
|
|
||||||
|
// EXTENSION_ZBA: Support for Zba address generation instructions
|
||||||
|
parameter EXTENSION_ZBA = 0,
|
||||||
|
|
||||||
|
// EXTENSION_ZBB: Support for Zbb basic bit manipulation instructions
|
||||||
|
parameter EXTENSION_ZBB = 0,
|
||||||
|
|
||||||
|
// EXTENSION_ZBC: Support for Zbc carry-less multiplication instructions
|
||||||
|
parameter EXTENSION_ZBC = 0,
|
||||||
|
|
||||||
|
// EXTENSION_ZBKB: Support for Zbkb basic bit manipulation for cryptography
|
||||||
|
// Requires: Zbb. (This flag enables instructions in Zbkb which aren't in Zbb.)
|
||||||
|
parameter EXTENSION_ZBKB = 0,
|
||||||
|
|
||||||
|
// EXTENSION_ZBKX: support for Zbkx crossbar permutation instructions
|
||||||
|
parameter EXTENSION_ZBKX = 0,
|
||||||
|
|
||||||
|
// EXTENSION_ZBS: Support for Zbs single-bit manipulation instructions
|
||||||
|
parameter EXTENSION_ZBS = 0,
|
||||||
|
|
||||||
|
// EXTENSION_ZCB: Support for Zcb basic additional compressed instructions
|
||||||
|
// Requires: EXTENSION_C. (Some Zcb instructions also require Zbb or M.)
|
||||||
|
// Note Zca is equivalent to C, as we do not support the F extension.
|
||||||
|
parameter EXTENSION_ZCB = 0,
|
||||||
|
|
||||||
|
// EXTENSION_ZCLSD: Support for Zclsd compressed load/store pair instructions
|
||||||
|
// Requires: EXTENSION_ZILSD, EXTENSION_C.
|
||||||
|
parameter EXTENSION_ZCLSD = 0,
|
||||||
|
|
||||||
|
// EXTENSION_ZCMP: Support for Zcmp push/pop instructions.
|
||||||
|
// Requires: EXTENSION_C.
|
||||||
|
parameter EXTENSION_ZCMP = 0,
|
||||||
|
|
||||||
|
// EXTENSION_ZIFENCEI: Support for the fence.i instruction
|
||||||
|
// Optional, since a plain branch/jump will also flush the prefetch queue.
|
||||||
|
parameter EXTENSION_ZIFENCEI = 0,
|
||||||
|
|
||||||
|
// EXTENSION_ZILSD: Support for Zilsd load/store pair instructions
|
||||||
|
parameter EXTENSION_ZILSD = 0,
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Custom RISC-V extensions
|
||||||
|
|
||||||
|
// EXTENSION_XH3B: Custom bit-extract-multiple instructions for Hazard3
|
||||||
|
parameter EXTENSION_XH3BEXTM = 0,
|
||||||
|
|
||||||
|
// EXTENSION_XH3IRQ: Custom preemptive, prioritised interrupt support. Can be
|
||||||
|
// disabled if an external interrupt controller (e.g. PLIC) is used. If
|
||||||
|
// disabled, and NUM_IRQS > 1, the external interrupts are simply OR'd into
|
||||||
|
// mip.meip.
|
||||||
|
parameter EXTENSION_XH3IRQ = 0,
|
||||||
|
|
||||||
|
// EXTENSION_XH3PMPM: PMPCFGMx CSRs to enforce PMP regions in M-mode without
|
||||||
|
// locking. Unlike ePMP mseccfg.rlb, locked and unlocked regions can coexist
|
||||||
|
parameter EXTENSION_XH3PMPM = 0,
|
||||||
|
|
||||||
|
// EXTENSION_XH3POWER: Custom power management controls for Hazard3
|
||||||
|
parameter EXTENSION_XH3POWER = 0,
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Standard CSR support
|
||||||
|
|
||||||
|
// Note the Zicsr extension is implied by any of CSR_M_MANDATORY, CSR_M_TRAP,
|
||||||
|
// CSR_COUNTER.
|
||||||
|
|
||||||
|
// CSR_M_MANDATORY: Bare minimum CSR support e.g. misa. Spec says must = 1 if
|
||||||
|
// CSRs are present, but I won't tell anyone.
|
||||||
|
parameter CSR_M_MANDATORY = 1,
|
||||||
|
|
||||||
|
// CSR_M_TRAP: Include M-mode trap-handling CSRs, and enable trap support.
|
||||||
|
parameter CSR_M_TRAP = 1,
|
||||||
|
|
||||||
|
// CSR_COUNTER: Include performance counters and Zicntr CSRs
|
||||||
|
parameter CSR_COUNTER = 0,
|
||||||
|
|
||||||
|
// U_MODE: Support the U (user) execution mode. In U mode, the core performs
|
||||||
|
// unprivileged bus accesses, and software's access to CSRs is restricted.
|
||||||
|
// Additionally, if the PMP is included, the core may restrict U-mode
|
||||||
|
// software's access to memory.
|
||||||
|
// Requires: CSR_M_TRAP.
|
||||||
|
parameter U_MODE = 0,
|
||||||
|
|
||||||
|
// PMP_REGIONS: Number of physical memory protection regions, or 0 for no PMP.
|
||||||
|
// PMP is more useful if U mode is supported, but this is not a requirement.
|
||||||
|
parameter PMP_REGIONS = 0,
|
||||||
|
|
||||||
|
// PMP_GRAIN: This is the "G" parameter in the privileged spec. Minimum PMP
|
||||||
|
// region size is 1 << (G + 2) bytes. If G > 0, PMCFG.A can not be set to
|
||||||
|
// NA4 (will get set to OFF instead). If G > 1, the G - 1 LSBs of pmpaddr are
|
||||||
|
// read-only-0 when PMPCFG.A is OFF, and read-only-1 when PMPCFG.A is NAPOT.
|
||||||
|
parameter PMP_GRAIN = 0,
|
||||||
|
|
||||||
|
// PMP_MATCH_NAPOT: Enable PMP support for the NAPOT (naturally-aligned
|
||||||
|
// power-of-two) and NA4 (naturally-aligned four-byte) matching modes. When
|
||||||
|
// disabled, attempting to select these modes will set the PMP region to OFF.
|
||||||
|
parameter PMP_MATCH_NAPOT = 1,
|
||||||
|
|
||||||
|
// PMP_MATCH_TOR: Enable PMP support for the TOR (top-of-range) matching mode.
|
||||||
|
// When disabled, attempting to select this mode will set the region to OFF.
|
||||||
|
parameter PMP_MATCH_TOR = 0,
|
||||||
|
|
||||||
|
// PMPADDR_HARDWIRED: If a bit is 1, the corresponding region's pmpaddr and
|
||||||
|
// pmpcfg registers are read-only. PMP_GRAIN is ignored on hardwired regions.
|
||||||
|
// It's recommended to make hardwired regions the highest-numbered, so they
|
||||||
|
// can be overridden by lower-numbered regions.
|
||||||
|
parameter PMP_HARDWIRED = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){1'b0}},
|
||||||
|
|
||||||
|
// PMPADDR_HARDWIRED_ADDR: Values of pmpaddr registers whose PMP_HARDWIRED
|
||||||
|
// bits are set to 1. Non-hardwired regions reset to all-zeroes.
|
||||||
|
parameter PMP_HARDWIRED_ADDR = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){32'h0}},
|
||||||
|
|
||||||
|
// PMPCFG_RESET_VAL: Values of pmpcfg registers whose PMP_HARDWIRED bits are
|
||||||
|
// set to 1. Non-hardwired regions reset to all zeroes.
|
||||||
|
parameter PMP_HARDWIRED_CFG = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){8'h00}},
|
||||||
|
|
||||||
|
// DEBUG_SUPPORT: Support for run/halt and instruction injection from an
|
||||||
|
// external Debug Module, support for Debug Mode, and Debug Mode CSRs.
|
||||||
|
// Requires: CSR_M_MANDATORY, CSR_M_TRAP.
|
||||||
|
parameter DEBUG_SUPPORT = 0,
|
||||||
|
|
||||||
|
// BREAKPOINT_TRIGGERS: Number of triggers which support type=2 execute=1
|
||||||
|
// (but not store/load=1, i.e. not a watchpoint). Requires: DEBUG_SUPPORT
|
||||||
|
parameter BREAKPOINT_TRIGGERS = 0,
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// External interrupt support
|
||||||
|
|
||||||
|
// NUM_IRQS: Number of external IRQs. Minimum 1, maximum 512. Note that if
|
||||||
|
// EXTENSION_XH3IRQ (Hazard3 interrupt controller) is disabled then multiple
|
||||||
|
// external interrupts are simply OR'd into mip.meip.
|
||||||
|
parameter NUM_IRQS = 1,
|
||||||
|
|
||||||
|
// IRQ_PRIORITY_BITS: Number of priority bits implemented for each interrupt
|
||||||
|
// in meipra, if EXTENSION_XH3IRQ is enabled. The number of distinct levels
|
||||||
|
// is (1 << IRQ_PRIORITY_BITS). Minimum 0, max 4. Note that multiple priority
|
||||||
|
// levels with a large number of IRQs will have a severe effect on timing.
|
||||||
|
parameter IRQ_PRIORITY_BITS = 0,
|
||||||
|
|
||||||
|
// IRQ_INPUT_BYPASS: disable the input registers on the external interrupts,
|
||||||
|
// to reduce latency by one cycle. Can be applied on an IRQ-by-IRQ basis.
|
||||||
|
// Ignored if EXTENSION_XH3IRQ is disabled.
|
||||||
|
parameter IRQ_INPUT_BYPASS = {(NUM_IRQS > 0 ? NUM_IRQS : 1){1'b0}},
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// ID registers
|
||||||
|
|
||||||
|
// JEDEC JEP106-compliant vendor ID, can be left at 0 if "not implemented or
|
||||||
|
// [...] this is a non-commercial implementation" (RISC-V spec).
|
||||||
|
// 31:7 is continuation code count, 6:0 is ID. Parity bit is not stored.
|
||||||
|
parameter MVENDORID_VAL = 32'h0,
|
||||||
|
|
||||||
|
// Pointer to configuration structure blob, or all-zeroes. Must be at least
|
||||||
|
// 4-byte-aligned.
|
||||||
|
parameter MCONFIGPTR_VAL = 32'h0,
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Performance/size options
|
||||||
|
|
||||||
|
// REDUCED_BYPASS: Remove all forwarding paths except X->X (so back-to-back
|
||||||
|
// ALU ops can still run at 1 CPI), to save area.
|
||||||
|
parameter REDUCED_BYPASS = 0,
|
||||||
|
|
||||||
|
// MULDIV_UNROLL: Bits per clock for multiply/divide circuit, if present. Must
|
||||||
|
// be a power of 2.
|
||||||
|
parameter MULDIV_UNROLL = 1,
|
||||||
|
|
||||||
|
// MUL_FAST: Use single-cycle multiply circuit for MUL instructions, retiring
|
||||||
|
// to stage 3. The sequential multiply/divide circuit is still used for MULH*
|
||||||
|
parameter MUL_FAST = 0,
|
||||||
|
|
||||||
|
// MUL_FASTER: Retire fast multiply results to stage 2 instead of stage 3.
|
||||||
|
// Throughput is the same, but latency is reduced from 2 cycles to 1 cycle.
|
||||||
|
// Requires: MUL_FAST.
|
||||||
|
parameter MUL_FASTER = 0,
|
||||||
|
|
||||||
|
// MULH_FAST: extend the fast multiply circuit to also cover MULH*, and remove
|
||||||
|
// the multiply functionality from the sequential multiply/divide circuit.
|
||||||
|
// Requires: MUL_FAST
|
||||||
|
parameter MULH_FAST = 0,
|
||||||
|
|
||||||
|
// FAST_BRANCHCMP: Instantiate a separate comparator (eq/lt/ltu) for branch
|
||||||
|
// comparisons, rather than using the ALU. Improves fetch address delay,
|
||||||
|
// especially if Zba extension is enabled. Disabling may save area.
|
||||||
|
parameter FAST_BRANCHCMP = 1,
|
||||||
|
|
||||||
|
// RESET_REGFILE: whether to support reset of the general purpose registers.
|
||||||
|
// There are around 1k bits in the register file, so the reset can be
|
||||||
|
// disabled e.g. to permit block-RAM inference on FPGA.
|
||||||
|
parameter RESET_REGFILE = 0,
|
||||||
|
|
||||||
|
// BRANCH_PREDICTOR: enable branch prediction. The branch predictor consists
|
||||||
|
// of a single BTB entry which is allocated on a taken backward branch, and
|
||||||
|
// cleared on a mispredicted nontaken branch, a fence.i or a trap. Successful
|
||||||
|
// prediction eliminates the 1-cyle fetch bubble on a taken branch, usually
|
||||||
|
// making tight loops faster.
|
||||||
|
parameter BRANCH_PREDICTOR = 0,
|
||||||
|
|
||||||
|
// MTVEC_WMASK: Mask of which bits in mtvec are writable. Full writability is
|
||||||
|
// recommended, because a common idiom in setup code is to set mtvec just
|
||||||
|
// past code that may trap, as a hardware "try {...} catch" block.
|
||||||
|
//
|
||||||
|
// - The vectoring mode can be made fixed by clearing the LSB of MTVEC_WMASK
|
||||||
|
//
|
||||||
|
// - In vectored mode, the vector table must be aligned to its size, rounded
|
||||||
|
// up to a power of two.
|
||||||
|
parameter MTVEC_WMASK = 32'hfffffffd,
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Port size parameters (do not modify)
|
||||||
|
|
||||||
|
parameter W_ADDR = 32, // Do not modify
|
||||||
|
parameter W_DATA = 32 // Do not modify
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// Pass-through of parameters defined in hazard3_config.vh, so that these can
|
||||||
|
// be set at instantiation rather than editing the config file, and will flow
|
||||||
|
// correctly down through the hierarchy.
|
||||||
|
|
||||||
|
.RESET_VECTOR (RESET_VECTOR),
|
||||||
|
.MTVEC_INIT (MTVEC_INIT),
|
||||||
|
.EXTENSION_A (EXTENSION_A),
|
||||||
|
.EXTENSION_C (EXTENSION_C),
|
||||||
|
.EXTENSION_E (EXTENSION_E),
|
||||||
|
.EXTENSION_M (EXTENSION_M),
|
||||||
|
.EXTENSION_ZBA (EXTENSION_ZBA),
|
||||||
|
.EXTENSION_ZBB (EXTENSION_ZBB),
|
||||||
|
.EXTENSION_ZBC (EXTENSION_ZBC),
|
||||||
|
.EXTENSION_ZBKB (EXTENSION_ZBKB),
|
||||||
|
.EXTENSION_ZBKX (EXTENSION_ZBKX),
|
||||||
|
.EXTENSION_ZBS (EXTENSION_ZBS),
|
||||||
|
.EXTENSION_ZCB (EXTENSION_ZCB),
|
||||||
|
.EXTENSION_ZCLSD (EXTENSION_ZCLSD),
|
||||||
|
.EXTENSION_ZCMP (EXTENSION_ZCMP),
|
||||||
|
.EXTENSION_ZIFENCEI (EXTENSION_ZIFENCEI),
|
||||||
|
.EXTENSION_ZILSD (EXTENSION_ZILSD),
|
||||||
|
.EXTENSION_XH3BEXTM (EXTENSION_XH3BEXTM),
|
||||||
|
.EXTENSION_XH3IRQ (EXTENSION_XH3IRQ),
|
||||||
|
.EXTENSION_XH3PMPM (EXTENSION_XH3PMPM),
|
||||||
|
.EXTENSION_XH3POWER (EXTENSION_XH3POWER),
|
||||||
|
.CSR_M_MANDATORY (CSR_M_MANDATORY),
|
||||||
|
.CSR_M_TRAP (CSR_M_TRAP),
|
||||||
|
.CSR_COUNTER (CSR_COUNTER),
|
||||||
|
.U_MODE (U_MODE),
|
||||||
|
.PMP_REGIONS (PMP_REGIONS),
|
||||||
|
.PMP_GRAIN (PMP_GRAIN),
|
||||||
|
.PMP_MATCH_NAPOT (PMP_MATCH_NAPOT),
|
||||||
|
.PMP_MATCH_TOR (PMP_MATCH_TOR),
|
||||||
|
.PMP_HARDWIRED (PMP_HARDWIRED),
|
||||||
|
.PMP_HARDWIRED_ADDR (PMP_HARDWIRED_ADDR),
|
||||||
|
.PMP_HARDWIRED_CFG (PMP_HARDWIRED_CFG),
|
||||||
|
.DEBUG_SUPPORT (DEBUG_SUPPORT),
|
||||||
|
.BREAKPOINT_TRIGGERS (BREAKPOINT_TRIGGERS),
|
||||||
|
.NUM_IRQS (NUM_IRQS),
|
||||||
|
.IRQ_PRIORITY_BITS (IRQ_PRIORITY_BITS),
|
||||||
|
.IRQ_INPUT_BYPASS (IRQ_INPUT_BYPASS),
|
||||||
|
.MVENDORID_VAL (MVENDORID_VAL),
|
||||||
|
.MCONFIGPTR_VAL (MCONFIGPTR_VAL),
|
||||||
|
.REDUCED_BYPASS (REDUCED_BYPASS),
|
||||||
|
.MULDIV_UNROLL (MULDIV_UNROLL),
|
||||||
|
.MUL_FAST (MUL_FAST),
|
||||||
|
.MUL_FASTER (MUL_FASTER),
|
||||||
|
.MULH_FAST (MULH_FAST),
|
||||||
|
.FAST_BRANCHCMP (FAST_BRANCHCMP),
|
||||||
|
.BRANCH_PREDICTOR (BRANCH_PREDICTOR),
|
||||||
|
.MTVEC_WMASK (MTVEC_WMASK),
|
||||||
|
.RESET_REGFILE (RESET_REGFILE),
|
||||||
|
.W_ADDR (W_ADDR),
|
||||||
|
.W_DATA (W_DATA)
|
||||||
@@ -0,0 +1,343 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// Single-ported top level file for Hazard3 CPU. This file instantiates the
|
||||||
|
// Hazard3 core, and arbitrates its instruction fetch and load/store signals
|
||||||
|
// down to a single AHB5 master port.
|
||||||
|
|
||||||
|
`ifdef HAZARD3_RVFI_STANDALONE
|
||||||
|
`include "hazard3_rvfi_standalone_defs.vh"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_cpu_1port #(
|
||||||
|
`include "hazard3_config.vh"
|
||||||
|
) (
|
||||||
|
// Global signals
|
||||||
|
input wire clk,
|
||||||
|
input wire clk_always_on,
|
||||||
|
input wire rst_n,
|
||||||
|
|
||||||
|
`ifdef RISCV_FORMAL
|
||||||
|
`RVFI_OUTPUTS ,
|
||||||
|
`endif
|
||||||
|
|
||||||
|
// Power control signals
|
||||||
|
output wire pwrup_req,
|
||||||
|
input wire pwrup_ack,
|
||||||
|
output wire clk_en,
|
||||||
|
output wire unblock_out,
|
||||||
|
input wire unblock_in,
|
||||||
|
|
||||||
|
// AHB5 Master port
|
||||||
|
output reg [W_ADDR-1:0] haddr,
|
||||||
|
output reg hwrite,
|
||||||
|
output reg [1:0] htrans,
|
||||||
|
output reg [2:0] hsize,
|
||||||
|
output wire [2:0] hburst,
|
||||||
|
output reg [3:0] hprot,
|
||||||
|
output wire hmastlock,
|
||||||
|
output reg [7:0] hmaster,
|
||||||
|
output reg hexcl,
|
||||||
|
input wire hready,
|
||||||
|
input wire hresp,
|
||||||
|
input wire hexokay,
|
||||||
|
output wire [W_DATA-1:0] hwdata,
|
||||||
|
input wire [W_DATA-1:0] hrdata,
|
||||||
|
|
||||||
|
// Memory ordering signals
|
||||||
|
output wire fence_i_vld,
|
||||||
|
output wire fence_d_vld,
|
||||||
|
input wire fence_rdy,
|
||||||
|
|
||||||
|
// Debugger run/halt control
|
||||||
|
input wire dbg_req_halt,
|
||||||
|
input wire dbg_req_halt_on_reset,
|
||||||
|
input wire dbg_req_resume,
|
||||||
|
output wire dbg_halted,
|
||||||
|
output wire dbg_running,
|
||||||
|
// Debugger access to data0 CSR
|
||||||
|
input wire [W_DATA-1:0] dbg_data0_rdata,
|
||||||
|
output wire [W_DATA-1:0] dbg_data0_wdata,
|
||||||
|
output wire dbg_data0_wen,
|
||||||
|
// Debugger instruction injection
|
||||||
|
input wire [W_DATA-1:0] dbg_instr_data,
|
||||||
|
input wire dbg_instr_data_vld,
|
||||||
|
output wire dbg_instr_data_rdy,
|
||||||
|
output wire dbg_instr_caught_exception,
|
||||||
|
output wire dbg_instr_caught_ebreak,
|
||||||
|
|
||||||
|
// Optional debug system bus access patch-through
|
||||||
|
input wire [W_ADDR-1:0] dbg_sbus_addr,
|
||||||
|
input wire dbg_sbus_write,
|
||||||
|
input wire [1:0] dbg_sbus_size,
|
||||||
|
input wire dbg_sbus_vld,
|
||||||
|
output wire dbg_sbus_rdy,
|
||||||
|
output wire dbg_sbus_err,
|
||||||
|
input wire [W_DATA-1:0] dbg_sbus_wdata,
|
||||||
|
output wire [W_DATA-1:0] dbg_sbus_rdata,
|
||||||
|
|
||||||
|
// Identification CSR values
|
||||||
|
input wire [W_DATA-1:0] mhartid_val,
|
||||||
|
input wire [3:0] eco_version,
|
||||||
|
|
||||||
|
// Level-sensitive interrupt sources
|
||||||
|
input wire [NUM_IRQS-1:0] irq, // -> mip.meip
|
||||||
|
input wire soft_irq, // -> mip.msip
|
||||||
|
input wire timer_irq // -> mip.mtip
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Processor core
|
||||||
|
|
||||||
|
// Instruction fetch signals
|
||||||
|
wire core_aph_req_i;
|
||||||
|
wire core_aph_panic_i;
|
||||||
|
wire core_aph_ready_i;
|
||||||
|
wire core_dph_ready_i;
|
||||||
|
wire core_dph_err_i;
|
||||||
|
|
||||||
|
wire [W_ADDR-1:0] core_haddr_i;
|
||||||
|
wire [2:0] core_hsize_i;
|
||||||
|
wire core_priv_i;
|
||||||
|
wire [W_DATA-1:0] core_rdata_i;
|
||||||
|
|
||||||
|
|
||||||
|
// Load/store signals
|
||||||
|
wire core_aph_req_d;
|
||||||
|
wire core_aph_excl_d;
|
||||||
|
wire core_aph_ready_d;
|
||||||
|
wire core_dph_ready_d;
|
||||||
|
wire core_dph_err_d;
|
||||||
|
wire core_dph_exokay_d;
|
||||||
|
|
||||||
|
wire [W_ADDR-1:0] core_haddr_d;
|
||||||
|
wire [2:0] core_hsize_d;
|
||||||
|
wire core_priv_d;
|
||||||
|
wire core_hwrite_d;
|
||||||
|
wire [W_DATA-1:0] core_wdata_d;
|
||||||
|
wire [W_DATA-1:0] core_rdata_d;
|
||||||
|
|
||||||
|
|
||||||
|
hazard3_core #(
|
||||||
|
`include "hazard3_config_inst.vh"
|
||||||
|
) core (
|
||||||
|
.clk (clk),
|
||||||
|
.clk_always_on (clk_always_on),
|
||||||
|
.rst_n (rst_n),
|
||||||
|
|
||||||
|
.pwrup_req (pwrup_req),
|
||||||
|
.pwrup_ack (pwrup_ack),
|
||||||
|
.clk_en (clk_en),
|
||||||
|
.unblock_out (unblock_out),
|
||||||
|
.unblock_in (unblock_in),
|
||||||
|
|
||||||
|
`ifdef RISCV_FORMAL
|
||||||
|
`RVFI_CONN ,
|
||||||
|
`endif
|
||||||
|
|
||||||
|
.bus_aph_req_i (core_aph_req_i),
|
||||||
|
.bus_aph_panic_i (core_aph_panic_i),
|
||||||
|
.bus_aph_ready_i (core_aph_ready_i),
|
||||||
|
.bus_dph_ready_i (core_dph_ready_i),
|
||||||
|
.bus_dph_err_i (core_dph_err_i),
|
||||||
|
.bus_haddr_i (core_haddr_i),
|
||||||
|
.bus_hsize_i (core_hsize_i),
|
||||||
|
.bus_priv_i (core_priv_i),
|
||||||
|
.bus_rdata_i (core_rdata_i),
|
||||||
|
|
||||||
|
.bus_aph_req_d (core_aph_req_d),
|
||||||
|
.bus_aph_excl_d (core_aph_excl_d),
|
||||||
|
.bus_aph_ready_d (core_aph_ready_d),
|
||||||
|
.bus_dph_ready_d (core_dph_ready_d),
|
||||||
|
.bus_dph_err_d (core_dph_err_d),
|
||||||
|
.bus_dph_exokay_d (core_dph_exokay_d),
|
||||||
|
.bus_haddr_d (core_haddr_d),
|
||||||
|
.bus_hsize_d (core_hsize_d),
|
||||||
|
.bus_priv_d (core_priv_d),
|
||||||
|
.bus_hwrite_d (core_hwrite_d),
|
||||||
|
.bus_wdata_d (core_wdata_d),
|
||||||
|
.bus_rdata_d (core_rdata_d),
|
||||||
|
|
||||||
|
.fence_i_vld (fence_i_vld),
|
||||||
|
.fence_d_vld (fence_d_vld),
|
||||||
|
.fence_rdy (fence_rdy),
|
||||||
|
|
||||||
|
.dbg_req_halt (dbg_req_halt),
|
||||||
|
.dbg_req_halt_on_reset (dbg_req_halt_on_reset),
|
||||||
|
.dbg_req_resume (dbg_req_resume),
|
||||||
|
.dbg_halted (dbg_halted),
|
||||||
|
.dbg_running (dbg_running),
|
||||||
|
.dbg_data0_rdata (dbg_data0_rdata),
|
||||||
|
.dbg_data0_wdata (dbg_data0_wdata),
|
||||||
|
.dbg_data0_wen (dbg_data0_wen),
|
||||||
|
.dbg_instr_data (dbg_instr_data),
|
||||||
|
.dbg_instr_data_vld (dbg_instr_data_vld),
|
||||||
|
.dbg_instr_data_rdy (dbg_instr_data_rdy),
|
||||||
|
.dbg_instr_caught_exception (dbg_instr_caught_exception),
|
||||||
|
.dbg_instr_caught_ebreak (dbg_instr_caught_ebreak),
|
||||||
|
|
||||||
|
.mhartid_val (mhartid_val),
|
||||||
|
.eco_version (eco_version),
|
||||||
|
|
||||||
|
.irq (irq),
|
||||||
|
.soft_irq (soft_irq),
|
||||||
|
.timer_irq (timer_irq)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Arbitration state machine
|
||||||
|
|
||||||
|
wire bus_gnt_i;
|
||||||
|
wire bus_gnt_d;
|
||||||
|
wire bus_gnt_s;
|
||||||
|
|
||||||
|
reg bus_hold_aph;
|
||||||
|
reg [2:0] bus_gnt_ids_prev;
|
||||||
|
|
||||||
|
// Note use of clk_always_on: SBA may use this arbiter to access the bus
|
||||||
|
// whilst the core is asleep.
|
||||||
|
|
||||||
|
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
bus_hold_aph <= 1'b0;
|
||||||
|
bus_gnt_ids_prev <= 3'h0;
|
||||||
|
end else begin
|
||||||
|
bus_hold_aph <= htrans[1] && !hready && !hresp;
|
||||||
|
bus_gnt_ids_prev <= {bus_gnt_i, bus_gnt_d, bus_gnt_s};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Debug SBA access is lower priority than load/store, but higher than
|
||||||
|
// instruction fetch. This isn't ideal, but in a tight loop the core may be
|
||||||
|
// performing an instruction fetch or load/store on every single cycle, and
|
||||||
|
// this is a simple way to guarantee eventual success of debugger accesses. A
|
||||||
|
// more complex way would be to add a "panic timer" to boost a stalled sbus
|
||||||
|
// access over an instruction fetch.
|
||||||
|
|
||||||
|
// Note that, often, the sbus will be disconnected: it doesn't provide any
|
||||||
|
// increase in debugger bus throughput compared with the program buffer and
|
||||||
|
// autoexec. It's useful for "minimally intrusive" debug bus access(i.e. less
|
||||||
|
// intrusive than halting the core and resuming it) e.g. for Segger RTT.
|
||||||
|
|
||||||
|
reg bus_active_dph_s;
|
||||||
|
|
||||||
|
assign {bus_gnt_i, bus_gnt_d, bus_gnt_s} =
|
||||||
|
bus_hold_aph ? bus_gnt_ids_prev :
|
||||||
|
core_aph_panic_i ? 3'b100 :
|
||||||
|
core_aph_req_d ? 3'b010 :
|
||||||
|
dbg_sbus_vld && !bus_active_dph_s ? 3'b001 :
|
||||||
|
core_aph_req_i ? 3'b100 :
|
||||||
|
3'b000 ;
|
||||||
|
reg bus_active_dph_i;
|
||||||
|
reg bus_active_dph_d;
|
||||||
|
|
||||||
|
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
bus_active_dph_i <= 1'b0;
|
||||||
|
bus_active_dph_d <= 1'b0;
|
||||||
|
bus_active_dph_s <= 1'b0;
|
||||||
|
end else if (hready) begin
|
||||||
|
bus_active_dph_i <= bus_gnt_i;
|
||||||
|
bus_active_dph_d <= bus_gnt_d;
|
||||||
|
bus_active_dph_s <= bus_gnt_s;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Address phase request muxing
|
||||||
|
|
||||||
|
localparam HTRANS_IDLE = 2'b00;
|
||||||
|
localparam HTRANS_NSEQ = 2'b10;
|
||||||
|
|
||||||
|
wire [3:0] hprot_data = {
|
||||||
|
2'b00, // Noncacheable/nonbufferable
|
||||||
|
core_priv_d, // Privileged or Normal as per core state
|
||||||
|
1'b1 // Data access
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [3:0] hprot_instr = {
|
||||||
|
2'b00, // Noncacheable/nonbufferable
|
||||||
|
core_priv_i, // Privileged or Normal as per core state
|
||||||
|
1'b0 // Instruction access
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [3:0] hprot_sbus = {
|
||||||
|
2'b00, // Noncacheable/nonbufferable
|
||||||
|
1'b1, // Always privileged
|
||||||
|
1'b1 // Data access
|
||||||
|
};
|
||||||
|
|
||||||
|
assign hburst = 3'b000; // HBURST_SINGLE
|
||||||
|
assign hmastlock = 1'b0;
|
||||||
|
|
||||||
|
always @ (*) begin
|
||||||
|
if (bus_gnt_s) begin
|
||||||
|
htrans = HTRANS_NSEQ;
|
||||||
|
hexcl = 1'b0;
|
||||||
|
haddr = dbg_sbus_addr;
|
||||||
|
hsize = {1'b0, dbg_sbus_size};
|
||||||
|
hwrite = dbg_sbus_write;
|
||||||
|
hprot = hprot_sbus;
|
||||||
|
hmaster = 8'h01;
|
||||||
|
end else if (bus_gnt_d) begin
|
||||||
|
htrans = HTRANS_NSEQ;
|
||||||
|
hexcl = core_aph_excl_d;
|
||||||
|
haddr = core_haddr_d;
|
||||||
|
hsize = core_hsize_d;
|
||||||
|
hwrite = core_hwrite_d;
|
||||||
|
hprot = hprot_data;
|
||||||
|
hmaster = 8'h00;
|
||||||
|
end else if (bus_gnt_i) begin
|
||||||
|
htrans = HTRANS_NSEQ;
|
||||||
|
hexcl = 1'b0;
|
||||||
|
haddr = core_haddr_i;
|
||||||
|
hsize = core_hsize_i;
|
||||||
|
hwrite = 1'b0;
|
||||||
|
hprot = hprot_instr;
|
||||||
|
hmaster = 8'h00;
|
||||||
|
end else begin
|
||||||
|
htrans = HTRANS_IDLE;
|
||||||
|
hexcl = 1'b0;
|
||||||
|
haddr = {W_ADDR{1'b0}};
|
||||||
|
hsize = 3'h0;
|
||||||
|
hwrite = 1'b0;
|
||||||
|
hprot = 4'h0;
|
||||||
|
hmaster = 8'h00;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign hwdata = bus_active_dph_s ? dbg_sbus_wdata : core_wdata_d;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Response routing
|
||||||
|
|
||||||
|
// Data buses directly connected
|
||||||
|
assign core_rdata_d = hrdata;
|
||||||
|
assign core_rdata_i = hrdata;
|
||||||
|
assign dbg_sbus_rdata = hrdata;
|
||||||
|
|
||||||
|
// Handhshake based on grant and bus stall
|
||||||
|
assign core_aph_ready_i = hready && bus_gnt_i;
|
||||||
|
assign core_dph_ready_i = bus_active_dph_i && hready;
|
||||||
|
assign core_dph_err_i = bus_active_dph_i && hresp;
|
||||||
|
|
||||||
|
// D-side errors are reported even when not ready, so that the core can make
|
||||||
|
// use of the two-phase error response to cleanly squash a second load/store
|
||||||
|
// chasing the faulting one down the pipeline.
|
||||||
|
assign core_aph_ready_d = hready && bus_gnt_d;
|
||||||
|
assign core_dph_ready_d = bus_active_dph_d && hready;
|
||||||
|
assign core_dph_err_d = bus_active_dph_d && hresp;
|
||||||
|
assign core_dph_exokay_d = bus_active_dph_d && hexokay;
|
||||||
|
|
||||||
|
assign dbg_sbus_err = bus_active_dph_s && hresp;
|
||||||
|
assign dbg_sbus_rdy = bus_active_dph_s && hready;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// Dual-ported top level file for Hazard3 CPU. This file instantiates the
|
||||||
|
// Hazard3 core, and interfaces its instruction fetch and load/store signals
|
||||||
|
// to a pair of AHB5 master ports.
|
||||||
|
|
||||||
|
`ifdef HAZARD3_RVFI_STANDALONE
|
||||||
|
`include "hazard3_rvfi_standalone_defs.vh"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_cpu_2port #(
|
||||||
|
`include "hazard3_config.vh"
|
||||||
|
) (
|
||||||
|
// Global signals
|
||||||
|
input wire clk,
|
||||||
|
input wire clk_always_on,
|
||||||
|
input wire rst_n,
|
||||||
|
|
||||||
|
// Power control signals
|
||||||
|
output wire pwrup_req,
|
||||||
|
input wire pwrup_ack,
|
||||||
|
output wire clk_en,
|
||||||
|
output wire unblock_out,
|
||||||
|
input wire unblock_in,
|
||||||
|
|
||||||
|
`ifdef RISCV_FORMAL
|
||||||
|
`RVFI_OUTPUTS ,
|
||||||
|
`endif
|
||||||
|
|
||||||
|
// Instruction fetch port
|
||||||
|
output wire [W_ADDR-1:0] i_haddr,
|
||||||
|
output wire i_hwrite,
|
||||||
|
output wire [1:0] i_htrans,
|
||||||
|
output wire [2:0] i_hsize,
|
||||||
|
output wire [2:0] i_hburst,
|
||||||
|
output wire [3:0] i_hprot,
|
||||||
|
output wire i_hmastlock,
|
||||||
|
output wire [7:0] i_hmaster,
|
||||||
|
input wire i_hready,
|
||||||
|
input wire i_hresp,
|
||||||
|
output wire [W_DATA-1:0] i_hwdata,
|
||||||
|
input wire [W_DATA-1:0] i_hrdata,
|
||||||
|
|
||||||
|
// Load/store port
|
||||||
|
output wire [W_ADDR-1:0] d_haddr,
|
||||||
|
output wire d_hwrite,
|
||||||
|
output wire [1:0] d_htrans,
|
||||||
|
output wire [2:0] d_hsize,
|
||||||
|
output wire [2:0] d_hburst,
|
||||||
|
output wire [3:0] d_hprot,
|
||||||
|
output wire d_hmastlock,
|
||||||
|
output wire [7:0] d_hmaster,
|
||||||
|
output wire d_hexcl,
|
||||||
|
input wire d_hready,
|
||||||
|
input wire d_hresp,
|
||||||
|
input wire d_hexokay,
|
||||||
|
output wire [W_DATA-1:0] d_hwdata,
|
||||||
|
input wire [W_DATA-1:0] d_hrdata,
|
||||||
|
|
||||||
|
// Memory ordering signals
|
||||||
|
output wire fence_i_vld,
|
||||||
|
output wire fence_d_vld,
|
||||||
|
input wire fence_rdy,
|
||||||
|
|
||||||
|
// Debugger run/halt control
|
||||||
|
input wire dbg_req_halt,
|
||||||
|
input wire dbg_req_halt_on_reset,
|
||||||
|
input wire dbg_req_resume,
|
||||||
|
output wire dbg_halted,
|
||||||
|
output wire dbg_running,
|
||||||
|
// Debugger access to data0 CSR
|
||||||
|
input wire [W_DATA-1:0] dbg_data0_rdata,
|
||||||
|
output wire [W_DATA-1:0] dbg_data0_wdata,
|
||||||
|
output wire dbg_data0_wen,
|
||||||
|
// Debugger instruction injection
|
||||||
|
input wire [W_DATA-1:0] dbg_instr_data,
|
||||||
|
input wire dbg_instr_data_vld,
|
||||||
|
output wire dbg_instr_data_rdy,
|
||||||
|
output wire dbg_instr_caught_exception,
|
||||||
|
output wire dbg_instr_caught_ebreak,
|
||||||
|
// Optional debug system bus access patch-through
|
||||||
|
input wire [W_ADDR-1:0] dbg_sbus_addr,
|
||||||
|
input wire dbg_sbus_write,
|
||||||
|
input wire [1:0] dbg_sbus_size,
|
||||||
|
input wire dbg_sbus_vld,
|
||||||
|
output wire dbg_sbus_rdy,
|
||||||
|
output wire dbg_sbus_err,
|
||||||
|
input wire [W_DATA-1:0] dbg_sbus_wdata,
|
||||||
|
output wire [W_DATA-1:0] dbg_sbus_rdata,
|
||||||
|
|
||||||
|
// Identification CSR values
|
||||||
|
input wire [W_DATA-1:0] mhartid_val,
|
||||||
|
input wire [3:0] eco_version,
|
||||||
|
|
||||||
|
// Level-sensitive interrupt sources
|
||||||
|
input wire [NUM_IRQS-1:0] irq, // -> mip.meip
|
||||||
|
input wire soft_irq, // -> mip.msip
|
||||||
|
input wire timer_irq // -> mip.mtip
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Processor core
|
||||||
|
|
||||||
|
// Instruction fetch signals
|
||||||
|
wire core_aph_req_i;
|
||||||
|
wire core_aph_ready_i;
|
||||||
|
wire core_dph_ready_i;
|
||||||
|
wire core_dph_err_i;
|
||||||
|
|
||||||
|
wire [W_ADDR-1:0] core_haddr_i;
|
||||||
|
wire [2:0] core_hsize_i;
|
||||||
|
wire core_priv_i;
|
||||||
|
wire [W_DATA-1:0] core_rdata_i;
|
||||||
|
|
||||||
|
|
||||||
|
// Load/store signals
|
||||||
|
wire core_aph_req_d;
|
||||||
|
wire core_aph_excl_d;
|
||||||
|
wire core_aph_ready_d;
|
||||||
|
wire core_dph_ready_d;
|
||||||
|
wire core_dph_err_d;
|
||||||
|
wire core_dph_exokay_d;
|
||||||
|
|
||||||
|
wire [W_ADDR-1:0] core_haddr_d;
|
||||||
|
wire [2:0] core_hsize_d;
|
||||||
|
wire core_priv_d;
|
||||||
|
wire core_hwrite_d;
|
||||||
|
wire [W_DATA-1:0] core_wdata_d;
|
||||||
|
wire [W_DATA-1:0] core_rdata_d;
|
||||||
|
|
||||||
|
hazard3_core #(
|
||||||
|
`include "hazard3_config_inst.vh"
|
||||||
|
) core (
|
||||||
|
.clk (clk),
|
||||||
|
.clk_always_on (clk_always_on),
|
||||||
|
.rst_n (rst_n),
|
||||||
|
|
||||||
|
.pwrup_req (pwrup_req),
|
||||||
|
.pwrup_ack (pwrup_ack),
|
||||||
|
.clk_en (clk_en),
|
||||||
|
.unblock_out (unblock_out),
|
||||||
|
.unblock_in (unblock_in),
|
||||||
|
|
||||||
|
`ifdef RISCV_FORMAL
|
||||||
|
`RVFI_CONN ,
|
||||||
|
`endif
|
||||||
|
|
||||||
|
.bus_aph_req_i (core_aph_req_i),
|
||||||
|
.bus_aph_panic_i (/* unused for 2port */),
|
||||||
|
.bus_aph_ready_i (core_aph_ready_i),
|
||||||
|
.bus_dph_ready_i (core_dph_ready_i),
|
||||||
|
.bus_dph_err_i (core_dph_err_i),
|
||||||
|
.bus_haddr_i (core_haddr_i),
|
||||||
|
.bus_hsize_i (core_hsize_i),
|
||||||
|
.bus_priv_i (core_priv_i),
|
||||||
|
.bus_rdata_i (core_rdata_i),
|
||||||
|
|
||||||
|
.bus_aph_req_d (core_aph_req_d),
|
||||||
|
.bus_aph_excl_d (core_aph_excl_d),
|
||||||
|
.bus_aph_ready_d (core_aph_ready_d),
|
||||||
|
.bus_dph_ready_d (core_dph_ready_d),
|
||||||
|
.bus_dph_err_d (core_dph_err_d),
|
||||||
|
.bus_dph_exokay_d (core_dph_exokay_d),
|
||||||
|
.bus_haddr_d (core_haddr_d),
|
||||||
|
.bus_hsize_d (core_hsize_d),
|
||||||
|
.bus_priv_d (core_priv_d),
|
||||||
|
.bus_hwrite_d (core_hwrite_d),
|
||||||
|
.bus_wdata_d (core_wdata_d),
|
||||||
|
.bus_rdata_d (core_rdata_d),
|
||||||
|
|
||||||
|
.fence_i_vld (fence_i_vld),
|
||||||
|
.fence_d_vld (fence_d_vld),
|
||||||
|
.fence_rdy (fence_rdy),
|
||||||
|
|
||||||
|
.dbg_req_halt (dbg_req_halt),
|
||||||
|
.dbg_req_halt_on_reset (dbg_req_halt_on_reset),
|
||||||
|
.dbg_req_resume (dbg_req_resume),
|
||||||
|
.dbg_halted (dbg_halted),
|
||||||
|
.dbg_running (dbg_running),
|
||||||
|
.dbg_data0_rdata (dbg_data0_rdata),
|
||||||
|
.dbg_data0_wdata (dbg_data0_wdata),
|
||||||
|
.dbg_data0_wen (dbg_data0_wen),
|
||||||
|
.dbg_instr_data (dbg_instr_data),
|
||||||
|
.dbg_instr_data_vld (dbg_instr_data_vld),
|
||||||
|
.dbg_instr_data_rdy (dbg_instr_data_rdy),
|
||||||
|
.dbg_instr_caught_exception (dbg_instr_caught_exception),
|
||||||
|
.dbg_instr_caught_ebreak (dbg_instr_caught_ebreak),
|
||||||
|
|
||||||
|
.mhartid_val (mhartid_val),
|
||||||
|
.eco_version (eco_version),
|
||||||
|
|
||||||
|
.irq (irq),
|
||||||
|
.soft_irq (soft_irq),
|
||||||
|
.timer_irq (timer_irq)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Instruction port
|
||||||
|
|
||||||
|
localparam HTRANS_IDLE = 2'b00;
|
||||||
|
localparam HTRANS_NSEQ = 2'b10;
|
||||||
|
|
||||||
|
assign i_haddr = core_haddr_i;
|
||||||
|
assign i_htrans = core_aph_req_i ? HTRANS_NSEQ : HTRANS_IDLE;
|
||||||
|
assign i_hsize = core_hsize_i;
|
||||||
|
|
||||||
|
reg dphase_active_i;
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
dphase_active_i <= 1'b0;
|
||||||
|
end else if (i_hready) begin
|
||||||
|
dphase_active_i <= core_aph_req_i;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifdef HAZARD3_ASSERTIONS
|
||||||
|
// Wake->sleep transition must wait for outstanding instruction fetches to
|
||||||
|
// complete, in particular because the arbiter clock will stop
|
||||||
|
always @ (posedge clk) if (!rst_n) assert(clk_en || !(core_aph_req_i || dphase_active_i));
|
||||||
|
`endif
|
||||||
|
|
||||||
|
assign core_aph_ready_i = i_hready && core_aph_req_i;
|
||||||
|
assign core_dph_ready_i = i_hready && dphase_active_i;
|
||||||
|
assign core_dph_err_i = i_hready && dphase_active_i && i_hresp;
|
||||||
|
|
||||||
|
assign core_rdata_i = i_hrdata;
|
||||||
|
|
||||||
|
assign i_hwrite = 1'b0;
|
||||||
|
assign i_hburst = 3'h0;
|
||||||
|
assign i_hmastlock = 1'b0;
|
||||||
|
assign i_hmaster = 8'h00;
|
||||||
|
assign i_hwdata = {W_DATA{1'b0}};
|
||||||
|
|
||||||
|
assign i_hprot = {
|
||||||
|
2'b00, // Noncacheable/nonbufferable
|
||||||
|
core_priv_i, // Privileged or Normal as per core state
|
||||||
|
1'b0 // Instruction access
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Load/store port
|
||||||
|
|
||||||
|
// The debug module has optional System Bus Access support, which can be muxed
|
||||||
|
// into the processor's D port here (or connected to a standalone AHB shim).
|
||||||
|
// This confers absolutely no advantage for debugger bus throughput, but
|
||||||
|
// allows the debugger to access the bus with minimal disturbance to the
|
||||||
|
// processor.
|
||||||
|
|
||||||
|
wire bus_gnt_d;
|
||||||
|
wire bus_gnt_s;
|
||||||
|
|
||||||
|
reg bus_hold_aph;
|
||||||
|
reg [1:0] bus_gnt_ds_prev;
|
||||||
|
reg bus_active_dph_d;
|
||||||
|
reg bus_active_dph_s;
|
||||||
|
|
||||||
|
// clk_always_on is used because SBA may access the bus through this arbiter
|
||||||
|
// whilst the core is asleep (same is not true for I-side interface)
|
||||||
|
|
||||||
|
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
bus_hold_aph <= 1'b0;
|
||||||
|
bus_gnt_ds_prev <= 2'h0;
|
||||||
|
end else begin
|
||||||
|
bus_hold_aph <= d_htrans[1] && !d_hready && !d_hresp;
|
||||||
|
bus_gnt_ds_prev <= {bus_gnt_d, bus_gnt_s};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign {bus_gnt_d, bus_gnt_s} =
|
||||||
|
bus_hold_aph ? bus_gnt_ds_prev :
|
||||||
|
core_aph_req_d ? 2'b10 :
|
||||||
|
dbg_sbus_vld && !bus_active_dph_s ? 2'b01 :
|
||||||
|
2'b00 ;
|
||||||
|
|
||||||
|
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
bus_active_dph_d <= 1'b0;
|
||||||
|
bus_active_dph_s <= 1'b0;
|
||||||
|
end else if (d_hready) begin
|
||||||
|
bus_active_dph_d <= bus_gnt_d;
|
||||||
|
bus_active_dph_s <= bus_gnt_s;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign d_htrans = bus_gnt_d || bus_gnt_s ? HTRANS_NSEQ : HTRANS_IDLE;
|
||||||
|
|
||||||
|
assign d_haddr = bus_gnt_s ? dbg_sbus_addr : core_haddr_d;
|
||||||
|
assign d_hwrite = bus_gnt_s ? dbg_sbus_write : core_hwrite_d;
|
||||||
|
assign d_hsize = bus_gnt_s ? {1'b0, dbg_sbus_size} : core_hsize_d;
|
||||||
|
assign d_hexcl = bus_gnt_s ? 1'b0 : core_aph_excl_d;
|
||||||
|
|
||||||
|
assign d_hprot = {
|
||||||
|
2'b00, // Noncacheable/nonbufferable
|
||||||
|
bus_gnt_s || core_priv_d, // Privileged or Normal as per core state
|
||||||
|
1'b1 // Data access
|
||||||
|
};
|
||||||
|
|
||||||
|
assign d_hwdata = bus_active_dph_s ? dbg_sbus_wdata : core_wdata_d;
|
||||||
|
|
||||||
|
// D-side errors are reported even when not ready, so that the core can make
|
||||||
|
// use of the two-phase error response to cleanly squash a second load/store
|
||||||
|
// chasing the faulting one down the pipeline.
|
||||||
|
assign core_aph_ready_d = d_hready && bus_gnt_d;
|
||||||
|
assign core_dph_ready_d = bus_active_dph_d && d_hready;
|
||||||
|
assign core_dph_err_d = bus_active_dph_d && d_hresp;
|
||||||
|
assign core_dph_exokay_d = bus_active_dph_d && d_hexokay;
|
||||||
|
assign core_rdata_d = d_hrdata;
|
||||||
|
|
||||||
|
assign dbg_sbus_err = bus_active_dph_s && d_hresp;
|
||||||
|
assign dbg_sbus_rdy = bus_active_dph_s && d_hready;
|
||||||
|
assign dbg_sbus_rdata = d_hrdata;
|
||||||
|
|
||||||
|
assign d_hburst = 3'h0;
|
||||||
|
assign d_hmastlock = 1'b0;
|
||||||
|
assign d_hmaster = bus_gnt_s ? 8'h01 : 8'h00;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// List of addresses for CSRs implemented by Hazard3, including custom CSRs.
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// M-mode CSRs
|
||||||
|
|
||||||
|
// Machine Information Registers (RO)
|
||||||
|
localparam MVENDORID = 12'hf11; // Vendor ID.
|
||||||
|
localparam MARCHID = 12'hf12; // Architecture ID.
|
||||||
|
localparam MIMPID = 12'hf13; // Implementation ID.
|
||||||
|
localparam MHARTID = 12'hf14; // Hardware thread ID.
|
||||||
|
localparam MCONFIGPTR = 12'hf15; // Pointer to configuration data structure.
|
||||||
|
|
||||||
|
// Machine Trap Setup (RW)
|
||||||
|
localparam MSTATUS = 12'h300; // Machine status register.
|
||||||
|
localparam MSTATUSH = 12'h310; // As of priv-1.12 this must be present even if tied 0.
|
||||||
|
localparam MISA = 12'h301; // ISA and extensions
|
||||||
|
localparam MEDELEG = 12'h302; // Machine exception delegation register.
|
||||||
|
localparam MIDELEG = 12'h303; // Machine interrupt delegation register.
|
||||||
|
localparam MIE = 12'h304; // Machine interrupt-enable register.
|
||||||
|
localparam MTVEC = 12'h305; // Machine trap-handler base address.
|
||||||
|
localparam MCOUNTEREN = 12'h306; // Machine counter enable.
|
||||||
|
|
||||||
|
// Machine Trap Handling (RW)
|
||||||
|
localparam MSCRATCH = 12'h340; // Scratch register for machine trap handlers.
|
||||||
|
localparam MEPC = 12'h341; // Machine exception program counter.
|
||||||
|
localparam MCAUSE = 12'h342; // Machine trap cause.
|
||||||
|
localparam MTVAL = 12'h343; // Machine bad address or instruction.
|
||||||
|
localparam MIP = 12'h344; // Machine interrupt pending.
|
||||||
|
|
||||||
|
// Machine Memory Protection (RW)
|
||||||
|
localparam PMPCFG0 = 12'h3a0; // Physical memory protection configuration.
|
||||||
|
localparam PMPCFG1 = 12'h3a1; // Physical memory protection configuration, RV32 only.
|
||||||
|
localparam PMPCFG2 = 12'h3a2; // Physical memory protection configuration.
|
||||||
|
localparam PMPCFG3 = 12'h3a3; // Physical memory protection configuration, RV32 only.
|
||||||
|
localparam PMPADDR0 = 12'h3b0; // Physical memory protection address register.
|
||||||
|
localparam PMPADDR1 = 12'h3b1; // ...
|
||||||
|
localparam PMPADDR2 = 12'h3b2;
|
||||||
|
localparam PMPADDR3 = 12'h3b3;
|
||||||
|
localparam PMPADDR4 = 12'h3b4;
|
||||||
|
localparam PMPADDR5 = 12'h3b5;
|
||||||
|
localparam PMPADDR6 = 12'h3b6;
|
||||||
|
localparam PMPADDR7 = 12'h3b7;
|
||||||
|
localparam PMPADDR8 = 12'h3b8;
|
||||||
|
localparam PMPADDR9 = 12'h3b9;
|
||||||
|
localparam PMPADDR10 = 12'h3ba;
|
||||||
|
localparam PMPADDR11 = 12'h3bb;
|
||||||
|
localparam PMPADDR12 = 12'h3bc;
|
||||||
|
localparam PMPADDR13 = 12'h3bd;
|
||||||
|
localparam PMPADDR14 = 12'h3be;
|
||||||
|
localparam PMPADDR15 = 12'h3bf;
|
||||||
|
|
||||||
|
localparam MSECCFG = 12'h747;
|
||||||
|
localparam MSECCFGH = 12'h757;
|
||||||
|
|
||||||
|
// Performance counters (RW)
|
||||||
|
localparam MCYCLE = 12'hb00; // Raw cycles since start of day
|
||||||
|
localparam MINSTRET = 12'hb02; // Instruction retire count since start of day
|
||||||
|
localparam MHPMCOUNTER3 = 12'hb03; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER4 = 12'hb04; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER5 = 12'hb05; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER6 = 12'hb06; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER7 = 12'hb07; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER8 = 12'hb08; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER9 = 12'hb09; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER10 = 12'hb0a; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER11 = 12'hb0b; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER12 = 12'hb0c; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER13 = 12'hb0d; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER14 = 12'hb0e; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER15 = 12'hb0f; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER16 = 12'hb10; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER17 = 12'hb11; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER18 = 12'hb12; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER19 = 12'hb13; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER20 = 12'hb14; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER21 = 12'hb15; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER22 = 12'hb16; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER23 = 12'hb17; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER24 = 12'hb18; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER25 = 12'hb19; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER26 = 12'hb1a; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER27 = 12'hb1b; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER28 = 12'hb1c; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER29 = 12'hb1d; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER30 = 12'hb1e; // WARL (we tie to 0)
|
||||||
|
localparam MHPMCOUNTER31 = 12'hb1f; // WARL (we tie to 0)
|
||||||
|
|
||||||
|
localparam MCYCLEH = 12'hb80; // High halves of each counter
|
||||||
|
localparam MINSTRETH = 12'hb82;
|
||||||
|
localparam MHPMCOUNTER3H = 12'hb83;
|
||||||
|
localparam MHPMCOUNTER4H = 12'hb84;
|
||||||
|
localparam MHPMCOUNTER5H = 12'hb85;
|
||||||
|
localparam MHPMCOUNTER6H = 12'hb86;
|
||||||
|
localparam MHPMCOUNTER7H = 12'hb87;
|
||||||
|
localparam MHPMCOUNTER8H = 12'hb88;
|
||||||
|
localparam MHPMCOUNTER9H = 12'hb89;
|
||||||
|
localparam MHPMCOUNTER10H = 12'hb8a;
|
||||||
|
localparam MHPMCOUNTER11H = 12'hb8b;
|
||||||
|
localparam MHPMCOUNTER12H = 12'hb8c;
|
||||||
|
localparam MHPMCOUNTER13H = 12'hb8d;
|
||||||
|
localparam MHPMCOUNTER14H = 12'hb8e;
|
||||||
|
localparam MHPMCOUNTER15H = 12'hb8f;
|
||||||
|
localparam MHPMCOUNTER16H = 12'hb90;
|
||||||
|
localparam MHPMCOUNTER17H = 12'hb91;
|
||||||
|
localparam MHPMCOUNTER18H = 12'hb92;
|
||||||
|
localparam MHPMCOUNTER19H = 12'hb93;
|
||||||
|
localparam MHPMCOUNTER20H = 12'hb94;
|
||||||
|
localparam MHPMCOUNTER21H = 12'hb95;
|
||||||
|
localparam MHPMCOUNTER22H = 12'hb96;
|
||||||
|
localparam MHPMCOUNTER23H = 12'hb97;
|
||||||
|
localparam MHPMCOUNTER24H = 12'hb98;
|
||||||
|
localparam MHPMCOUNTER25H = 12'hb99;
|
||||||
|
localparam MHPMCOUNTER26H = 12'hb9a;
|
||||||
|
localparam MHPMCOUNTER27H = 12'hb9b;
|
||||||
|
localparam MHPMCOUNTER28H = 12'hb9c;
|
||||||
|
localparam MHPMCOUNTER29H = 12'hb9d;
|
||||||
|
localparam MHPMCOUNTER30H = 12'hb9e;
|
||||||
|
localparam MHPMCOUNTER31H = 12'hb9f;
|
||||||
|
|
||||||
|
localparam MCOUNTINHIBIT = 12'h320; // Count inhibit register for mcycle/minstret
|
||||||
|
localparam MHPMEVENT3 = 12'h323; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT4 = 12'h324; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT5 = 12'h325; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT6 = 12'h326; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT7 = 12'h327; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT8 = 12'h328; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT9 = 12'h329; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT10 = 12'h32a; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT11 = 12'h32b; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT12 = 12'h32c; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT13 = 12'h32d; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT14 = 12'h32e; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT15 = 12'h32f; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT16 = 12'h330; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT17 = 12'h331; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT18 = 12'h332; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT19 = 12'h333; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT20 = 12'h334; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT21 = 12'h335; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT22 = 12'h336; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT23 = 12'h337; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT24 = 12'h338; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT25 = 12'h339; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT26 = 12'h33a; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT27 = 12'h33b; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT28 = 12'h33c; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT29 = 12'h33d; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT30 = 12'h33e; // WARL (we tie to 0)
|
||||||
|
localparam MHPMEVENT31 = 12'h33f; // WARL (we tie to 0)
|
||||||
|
|
||||||
|
// Other standard M-mode CSRs:
|
||||||
|
localparam MENVCFG = 12'h30a;
|
||||||
|
localparam MENVCFGH = 12'h31a;
|
||||||
|
|
||||||
|
// Custom M-mode CSRs:
|
||||||
|
localparam PMPCFGM0 = 12'hbd0; // Make PMP regions M-mode without locking
|
||||||
|
// bd1 // (reserved for >32 regions)
|
||||||
|
|
||||||
|
localparam MEIEA = 12'hbe0; // External interrupt pending array
|
||||||
|
localparam MEIPA = 12'hbe1; // External interrupt enable array
|
||||||
|
localparam MEIFA = 12'hbe2; // External interrupt force array
|
||||||
|
localparam MEIPRA = 12'hbe3; // External interrupt priority array
|
||||||
|
localparam MEINEXT = 12'hbe4; // Next external interrupt
|
||||||
|
localparam MEICONTEXT = 12'hbe5; // External interrupt context register
|
||||||
|
|
||||||
|
localparam MSLEEP = 12'hbf0; // M-mode sleep control register
|
||||||
|
localparam H3MISA = 12'hbf1; // Hazard3 M-mode ISA identification register
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// U-mode CSRs
|
||||||
|
|
||||||
|
// Read-only aliases of M-mode counter CSRs:
|
||||||
|
localparam CYCLE = 12'hc00;
|
||||||
|
localparam TIME = 12'hc01;
|
||||||
|
localparam INSTRET = 12'hc02;
|
||||||
|
localparam CYCLEH = 12'hc80;
|
||||||
|
localparam TIMEH = 12'hc81;
|
||||||
|
localparam INSTRETH = 12'hc82;
|
||||||
|
|
||||||
|
// Custom U-mode CSRs
|
||||||
|
localparam SLEEP = 12'h8f0; // U-mode subset of M-mode sleep control
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Trigger Module
|
||||||
|
|
||||||
|
localparam TSELECT = 12'h7a0;
|
||||||
|
localparam TDATA1 = 12'h7a1;
|
||||||
|
localparam TDATA2 = 12'h7a2;
|
||||||
|
localparam TDATA3 = 12'h7a3;
|
||||||
|
localparam TINFO = 12'h7a4;
|
||||||
|
localparam TCONTROL = 12'h7a5;
|
||||||
|
localparam MCONTEXT = 12'h7a8;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// D-mode CSRs
|
||||||
|
|
||||||
|
localparam DCSR = 12'h7b0;
|
||||||
|
localparam DPC = 12'h7b1;
|
||||||
|
localparam DMDATA0 = 12'hbff; // Custom read/write
|
||||||
@@ -0,0 +1,594 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2023 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_decode #(
|
||||||
|
`include "hazard3_config.vh"
|
||||||
|
,
|
||||||
|
`include "hazard3_width_const.vh"
|
||||||
|
) (
|
||||||
|
input wire clk,
|
||||||
|
input wire rst_n,
|
||||||
|
|
||||||
|
input wire [31:0] fd_cir,
|
||||||
|
input wire [1:0] fd_cir_err,
|
||||||
|
input wire [1:0] fd_cir_predbranch,
|
||||||
|
input wire [1:0] fd_cir_vld,
|
||||||
|
input wire fd_cir_is_32bit,
|
||||||
|
input wire fd_cir_invalid_16bit,
|
||||||
|
input wire fd_cir_is_uop,
|
||||||
|
input wire fd_cir_uop_nonfinal,
|
||||||
|
input wire fd_cir_uop_no_pc_update,
|
||||||
|
input wire fd_cir_uop_atomic,
|
||||||
|
|
||||||
|
output wire [1:0] df_cir_use,
|
||||||
|
output wire df_cir_flush_behind,
|
||||||
|
|
||||||
|
output wire df_uop_stall,
|
||||||
|
output wire df_uop_clear,
|
||||||
|
output wire df_lspair_phase_next,
|
||||||
|
|
||||||
|
output wire [W_ADDR-1:0] d_pc,
|
||||||
|
|
||||||
|
input wire debug_mode,
|
||||||
|
input wire m_mode,
|
||||||
|
input wire trap_wfi,
|
||||||
|
|
||||||
|
input wire [W_ADDR-1:0] debug_dpc_wdata,
|
||||||
|
input wire debug_dpc_wen,
|
||||||
|
output wire [W_ADDR-1:0] debug_dpc_rdata,
|
||||||
|
|
||||||
|
output wire d_starved,
|
||||||
|
input wire x_stall,
|
||||||
|
input wire f_jump_now,
|
||||||
|
input wire [W_ADDR-1:0] f_jump_target,
|
||||||
|
input wire x_jump_not_except,
|
||||||
|
input wire [W_ADDR-1:0] d_btb_target_addr,
|
||||||
|
|
||||||
|
output reg [W_DATA-1:0] d_imm,
|
||||||
|
output reg [W_REGADDR-1:0] d_rs1,
|
||||||
|
output reg [W_REGADDR-1:0] d_rs2,
|
||||||
|
output reg [W_REGADDR-1:0] d_rd,
|
||||||
|
output reg [2:0] d_funct3_32b,
|
||||||
|
output reg [6:0] d_funct7_32b,
|
||||||
|
output reg [W_ALUSRC-1:0] d_alusrc_a,
|
||||||
|
output reg [W_ALUSRC-1:0] d_alusrc_b,
|
||||||
|
output reg [W_ALUOP-1:0] d_aluop,
|
||||||
|
output reg [W_MEMOP-1:0] d_memop,
|
||||||
|
output reg [W_MULOP-1:0] d_mulop,
|
||||||
|
output reg d_csr_ren,
|
||||||
|
output reg d_csr_wen,
|
||||||
|
output reg [1:0] d_csr_wtype,
|
||||||
|
output reg d_csr_w_imm,
|
||||||
|
output reg [W_BCOND-1:0] d_branchcond,
|
||||||
|
output reg [W_ADDR-1:0] d_addr_offs,
|
||||||
|
output reg d_addr_is_regoffs,
|
||||||
|
output reg [W_EXCEPT-1:0] d_except,
|
||||||
|
output reg d_sleep_wfi,
|
||||||
|
output reg d_sleep_block,
|
||||||
|
output reg d_sleep_unblock,
|
||||||
|
output wire d_no_pc_increment,
|
||||||
|
output wire d_uninterruptible,
|
||||||
|
output wire [W_ADDR-1:0] d_lspair_offset,
|
||||||
|
output reg d_fence_i,
|
||||||
|
output reg d_fence_d
|
||||||
|
);
|
||||||
|
|
||||||
|
`include "rv_opcodes.vh"
|
||||||
|
`include "hazard3_ops.vh"
|
||||||
|
|
||||||
|
localparam HAVE_CSR = CSR_M_MANDATORY || CSR_M_TRAP || CSR_COUNTER;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
wire [31:0] d_instr = fd_cir | {
|
||||||
|
30'd0, {2{~|EXTENSION_C}}
|
||||||
|
};
|
||||||
|
|
||||||
|
reg d_invalid_32bit;
|
||||||
|
wire d_invalid = fd_cir_invalid_16bit || d_invalid_32bit;
|
||||||
|
|
||||||
|
assign d_uninterruptible = |EXTENSION_ZCMP && fd_cir_uop_atomic;
|
||||||
|
|
||||||
|
`ifdef HAZARD3_ASSERTIONS
|
||||||
|
always @ (posedge clk) if (rst_n) begin
|
||||||
|
assert(!(d_invalid && fd_cir_is_uop));
|
||||||
|
assert(!(d_invalid && fd_cir_uop_atomic));
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
|
||||||
|
wire d_lspair_nonfinal;
|
||||||
|
// Signal to null the mepc offset when taking an exception on this
|
||||||
|
// instruction (because uops in a sequence *which can except*, so excluding
|
||||||
|
// the final sp adjust on popret/popretz, will all have the same PC as the
|
||||||
|
// next uop, which will be in stage 2 when they take their exception)
|
||||||
|
assign d_no_pc_increment = fd_cir_uop_nonfinal || d_lspair_nonfinal;
|
||||||
|
|
||||||
|
assign df_uop_stall = x_stall || d_starved;
|
||||||
|
|
||||||
|
// Note !df_cir_flush_behind because the jump in cm.popret/popretz is the
|
||||||
|
// *penultimate* instruction: we execute the stack adjustment in the fetch
|
||||||
|
// bubble to save a cycle, still need to finish the uop sequence.
|
||||||
|
//
|
||||||
|
// The sp adjust cannot generate an exception (it's an `add` with the same
|
||||||
|
// PMP.X and breakpoint comparison results as earlier uops) and interrupts are
|
||||||
|
// suppressed for this part of the sequence.
|
||||||
|
assign df_uop_clear = f_jump_now && !df_cir_flush_behind;
|
||||||
|
|
||||||
|
// Decode various immediate formats
|
||||||
|
wire [31:0] d_imm_i = {{21{d_instr[31]}}, d_instr[30:20]};
|
||||||
|
wire [31:0] d_imm_s = {{21{d_instr[31]}}, d_instr[30:25], d_instr[11:7]};
|
||||||
|
wire [31:0] d_imm_b = {{20{d_instr[31]}}, d_instr[7], d_instr[30:25], d_instr[11:8], 1'b0};
|
||||||
|
wire [31:0] d_imm_u = {d_instr[31:12], {12{1'b0}}};
|
||||||
|
wire [31:0] d_imm_j = {{12{d_instr[31]}}, d_instr[19:12], d_instr[20], d_instr[30:21], 1'b0};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// PC/CIR control
|
||||||
|
|
||||||
|
// Must not flag bus error for a valid 16-bit instruction *followed by* an
|
||||||
|
// error, because instruction fetch errors are speculative, and can be
|
||||||
|
// flushed by e.g. a branch instruction. Note the 16 LSBs must be valid for
|
||||||
|
// us to know an instruction's size.
|
||||||
|
wire d_except_instr_bus_fault = fd_cir_vld > 2'd0 && fd_cir_err[0] ||
|
||||||
|
fd_cir_vld > 2'd1 && fd_cir_is_32bit && fd_cir_err[1];
|
||||||
|
|
||||||
|
assign d_starved = ~|fd_cir_vld || fd_cir_vld[0] && fd_cir_is_32bit;
|
||||||
|
|
||||||
|
wire d_stall = x_stall || d_starved || fd_cir_uop_nonfinal || d_lspair_nonfinal;
|
||||||
|
|
||||||
|
assign df_cir_use =
|
||||||
|
d_starved || d_stall ? 2'h0 :
|
||||||
|
fd_cir_is_32bit ? 2'h2 : 2'h1;
|
||||||
|
|
||||||
|
// CIR Locking is required if we successfully assert a jump request, but
|
||||||
|
// decode is stalled. It is not possible to gate the jump request if the
|
||||||
|
// stall depends on bus stall (as this would create a through-path from bus
|
||||||
|
// stall to bus request) so instead we instruct the frontend to preserve the
|
||||||
|
// stalled instruction when flushing, and fill in behind it.
|
||||||
|
//
|
||||||
|
// Once the stall clears, the stalled instruction can execute its remaining
|
||||||
|
// side effects e.g. writing a link value to the register file.
|
||||||
|
wire jump_caused_by_d = f_jump_now && x_jump_not_except;
|
||||||
|
wire assert_cir_lock = jump_caused_by_d && d_stall;
|
||||||
|
|
||||||
|
// CIR lock ends naturally when an instruction (not just uop) graduates to the
|
||||||
|
// next stage:
|
||||||
|
wire finished_cir_lock = !d_stall;
|
||||||
|
|
||||||
|
// CIR lock can meet an untimely end due to trap entry. One way to reach this
|
||||||
|
// is a dphase load fault on the final load in a cm.popret: here the `ret`
|
||||||
|
// issues a fetch address while stalled on the first dphase cycle, then is
|
||||||
|
// flushed by trap on second cycle.
|
||||||
|
wire deassert_cir_lock = finished_cir_lock || (f_jump_now && !x_jump_not_except);
|
||||||
|
|
||||||
|
reg cir_lock_prev;
|
||||||
|
wire cir_lock = (cir_lock_prev && !deassert_cir_lock) || assert_cir_lock;
|
||||||
|
assign df_cir_flush_behind = assert_cir_lock && !cir_lock_prev;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
cir_lock_prev <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
cir_lock_prev <= cir_lock;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
reg [W_ADDR-1:0] pc;
|
||||||
|
wire [W_ADDR-1:0] pc_seq_next = pc + (
|
||||||
|
|EXTENSION_ZCMP && fd_cir_is_uop && fd_cir_uop_no_pc_update ? 32'd0 :
|
||||||
|
fd_cir_is_32bit ? 32'd4 : 32'd2
|
||||||
|
);
|
||||||
|
|
||||||
|
assign d_pc = pc;
|
||||||
|
assign debug_dpc_rdata = pc;
|
||||||
|
|
||||||
|
// Frontend should mark the whole instruction, and nothing but the
|
||||||
|
// instruction, as a predicted branch. This goes wrong when we execute the
|
||||||
|
// address containing the predicted branch twice with different 16-bit
|
||||||
|
// alignments (!). We need to issue a branch-to-self to get back on a linear
|
||||||
|
// path, otherwise PC and CIR will diverge and we will misexecute.
|
||||||
|
wire partial_predicted_branch = !d_starved &&
|
||||||
|
|BRANCH_PREDICTOR && fd_cir_is_32bit && ^fd_cir_predbranch;
|
||||||
|
|
||||||
|
wire predicted_branch = |BRANCH_PREDICTOR && fd_cir_predbranch[0];
|
||||||
|
|
||||||
|
// Generally locking takes place on a stalled jump/branch, which may need the
|
||||||
|
// original PC available to produce a link address when it unstalls. An
|
||||||
|
// exception to this is jumps in micro-op sequences: in this case the jump is
|
||||||
|
// the penultimate instruction in the sequence (ret before addi sp) and we
|
||||||
|
// need to capture the pc mid-uop-sequence.
|
||||||
|
wire hold_pc_on_cir_lock = assert_cir_lock && !(fd_cir_is_uop && !fd_cir_uop_no_pc_update && !x_stall);
|
||||||
|
wire update_pc_on_cir_unlock = cir_lock_prev && finished_cir_lock && !fd_cir_uop_no_pc_update;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
pc <= RESET_VECTOR;
|
||||||
|
end else begin
|
||||||
|
if (debug_dpc_wen) begin
|
||||||
|
pc <= debug_dpc_wdata;
|
||||||
|
end else if (debug_mode) begin
|
||||||
|
pc <= pc;
|
||||||
|
end else if ((f_jump_now && !hold_pc_on_cir_lock) || update_pc_on_cir_unlock) begin
|
||||||
|
pc <= f_jump_target;
|
||||||
|
end else if (!f_jump_now && fd_cir_uop_nonfinal && !fd_cir_uop_no_pc_update && !x_stall) begin
|
||||||
|
// End of previously stalled jr uop in cm.popret and cm.popretz:
|
||||||
|
// safe to update PC as next instruction (addi sp) cannot trap.
|
||||||
|
pc <= f_jump_target;
|
||||||
|
end else if (!d_stall && !cir_lock) begin
|
||||||
|
// If this instruction is a predicted-taken branch (and has not
|
||||||
|
// generated a mispredict recovery jump) then set PC to the
|
||||||
|
// prediction target instead of the sequentially next PC
|
||||||
|
pc <= predicted_branch ? d_btb_target_addr : pc_seq_next;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifdef HAZARD3_ASSERTIONS
|
||||||
|
always @ (posedge clk) if (rst_n) begin
|
||||||
|
if (~|fd_cir_vld) assert(!fd_cir_is_uop);
|
||||||
|
if (fd_cir_uop_no_pc_update) assert(fd_cir_is_uop);
|
||||||
|
if (fd_cir_uop_nonfinal) assert(fd_cir_is_uop);
|
||||||
|
if ($past(df_uop_clear)) assert(!fd_cir_is_uop);
|
||||||
|
// Important to avoid spurious PC updates following a trap on the final
|
||||||
|
// load of a cm.popret:
|
||||||
|
if ($past(df_uop_clear)) assert(!fd_cir_uop_no_pc_update);
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
|
||||||
|
wire [W_ADDR-1:0] branch_offs =
|
||||||
|
!fd_cir_is_32bit && predicted_branch ? 32'd2 :
|
||||||
|
fd_cir_is_32bit && predicted_branch ? 32'd4 : d_imm_b;
|
||||||
|
|
||||||
|
always @ (*) begin
|
||||||
|
casez ({|EXTENSION_A, d_instr[6:2]})
|
||||||
|
{1'bz, 5'b11011}: d_addr_offs = d_imm_j ; // JAL
|
||||||
|
{1'bz, 5'b11000}: d_addr_offs = branch_offs ; // Branches
|
||||||
|
{1'bz, 5'b01000}: d_addr_offs = d_imm_s ; // Store
|
||||||
|
{1'bz, 5'b11001}: d_addr_offs = d_imm_i ; // JALR
|
||||||
|
{1'bz, 5'b00000}: d_addr_offs = d_imm_i ; // Loads
|
||||||
|
{1'b1, 5'b01011}: d_addr_offs = 32'h0000_0000; // Atomics
|
||||||
|
default: d_addr_offs = 32'hxxxx_xxxx;
|
||||||
|
endcase
|
||||||
|
if (partial_predicted_branch) begin
|
||||||
|
d_addr_offs = 32'h0000_0000;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Track phase of load/store pair instructions (Zilsd and Zclsd)
|
||||||
|
|
||||||
|
// This could be shared with uop_ctr (for Zcmp) but the two are fundamentally
|
||||||
|
// different: Zcmp has 16-bit instructions which expand to sequences of
|
||||||
|
// 32-bit, whereas Zilsd has multi-phase 32-bit instructions and Zclsd has
|
||||||
|
// direct 16-bit aliases of those instructions. Therefore it's cleaner to
|
||||||
|
// separate the phasing from the decompression for Zilsd/Zclsd.
|
||||||
|
|
||||||
|
wire d_lspair_phase;
|
||||||
|
|
||||||
|
// Reorder accesses to avoid clobbering rs1 (base) in first half of load:
|
||||||
|
wire d_lspair_reg_sel = d_lspair_phase == d_instr[15];
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (EXTENSION_ZILSD) begin: have_lspair_reg_sel
|
||||||
|
reg d_lspair_phase_r;
|
||||||
|
assign d_lspair_phase = d_lspair_phase_r;
|
||||||
|
reg instr_is_lspair;
|
||||||
|
always @ (*) begin
|
||||||
|
casez ({d_invalid || d_starved, d_instr})
|
||||||
|
{1'b0, `RVOPC_LD}: instr_is_lspair = 1'b1;
|
||||||
|
{1'b0, `RVOPC_SD}: instr_is_lspair = 1'b1;
|
||||||
|
default: instr_is_lspair = 1'b0;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
d_lspair_phase_r <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
d_lspair_phase_r <= df_lspair_phase_next;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign df_lspair_phase_next =
|
||||||
|
!d_stall || f_jump_now ? 1'b0 :
|
||||||
|
instr_is_lspair && !x_stall ? 1'b1 : d_lspair_phase_r;
|
||||||
|
|
||||||
|
assign d_lspair_nonfinal = instr_is_lspair && !d_lspair_phase_r;
|
||||||
|
|
||||||
|
assign d_lspair_offset = {
|
||||||
|
29'h0,
|
||||||
|
d_lspair_reg_sel && instr_is_lspair,
|
||||||
|
2'h0
|
||||||
|
};
|
||||||
|
|
||||||
|
end else begin: no_lspair_reg_sel
|
||||||
|
|
||||||
|
assign d_lspair_phase = 1'b0;
|
||||||
|
assign df_lspair_phase_next = 1'b0;
|
||||||
|
assign d_lspair_nonfinal = 1'b0;
|
||||||
|
assign d_lspair_offset = 32'd0;
|
||||||
|
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Decode X controls
|
||||||
|
|
||||||
|
localparam X0 = {W_REGADDR{1'b0}};
|
||||||
|
|
||||||
|
// First decode the instruction bits, based on available extensions and
|
||||||
|
// privilege state, without gating in any stall/exception signals.
|
||||||
|
reg [W_REGADDR-1:0] raw_rs1;
|
||||||
|
reg [W_REGADDR-1:0] raw_rs2;
|
||||||
|
reg [W_REGADDR-1:0] raw_rd;
|
||||||
|
reg [W_DATA-1:0] raw_imm;
|
||||||
|
reg [W_ALUSRC-1:0] raw_alusrc_a;
|
||||||
|
reg [W_ALUSRC-1:0] raw_alusrc_b;
|
||||||
|
reg [W_ALUOP-1:0] raw_aluop;
|
||||||
|
reg [W_MEMOP-1:0] raw_memop;
|
||||||
|
reg [W_MULOP-1:0] raw_mulop;
|
||||||
|
reg raw_csr_ren;
|
||||||
|
reg raw_csr_wen;
|
||||||
|
reg [1:0] raw_csr_wtype;
|
||||||
|
reg raw_csr_w_imm;
|
||||||
|
reg [W_BCOND-1:0] raw_branchcond;
|
||||||
|
reg raw_addr_is_regoffs;
|
||||||
|
reg [W_EXCEPT-1:0] raw_except;
|
||||||
|
reg raw_sleep_wfi;
|
||||||
|
reg raw_sleep_block;
|
||||||
|
reg raw_sleep_unblock;
|
||||||
|
reg raw_fence_i;
|
||||||
|
reg raw_fence_d;
|
||||||
|
|
||||||
|
always @ (*) begin
|
||||||
|
// Assign some defaults
|
||||||
|
raw_rs1 = d_instr[19:15];
|
||||||
|
raw_rs2 = d_instr[24:20];
|
||||||
|
raw_rd = d_instr[11: 7];
|
||||||
|
raw_imm = d_imm_i;
|
||||||
|
raw_alusrc_a = ALUSRCA_RS1;
|
||||||
|
raw_alusrc_b = ALUSRCB_RS2;
|
||||||
|
raw_aluop = ALUOP_ADD;
|
||||||
|
raw_memop = MEMOP_NONE;
|
||||||
|
raw_mulop = M_OP_MUL;
|
||||||
|
raw_csr_ren = 1'b0;
|
||||||
|
raw_csr_wen = 1'b0;
|
||||||
|
raw_csr_wtype = CSR_WTYPE_W;
|
||||||
|
raw_csr_w_imm = 1'b0;
|
||||||
|
raw_branchcond = BCOND_NEVER;
|
||||||
|
raw_addr_is_regoffs = 1'b0;
|
||||||
|
raw_except = EXCEPT_NONE;
|
||||||
|
raw_sleep_wfi = 1'b0;
|
||||||
|
raw_sleep_block = 1'b0;
|
||||||
|
raw_sleep_unblock = 1'b0;
|
||||||
|
raw_fence_i = 1'b0;
|
||||||
|
raw_fence_d = 1'b0;
|
||||||
|
// Note this funct3/funct7 are valid only for 32-bit instructions. They
|
||||||
|
// are useful for clusters of related ALU ops, such as sh*add, clmul.
|
||||||
|
d_funct3_32b = fd_cir[14:12];
|
||||||
|
d_funct7_32b = fd_cir[31:25];
|
||||||
|
|
||||||
|
d_invalid_32bit = 1'b0;
|
||||||
|
|
||||||
|
casez (d_instr)
|
||||||
|
`RVOPC_BEQ: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_SUB; raw_branchcond = BCOND_ZERO; end
|
||||||
|
`RVOPC_BNE: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_SUB; raw_branchcond = BCOND_NZERO; end
|
||||||
|
`RVOPC_BLT: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LT; raw_branchcond = BCOND_NZERO; end
|
||||||
|
`RVOPC_BGE: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LT; raw_branchcond = BCOND_ZERO; end
|
||||||
|
`RVOPC_BLTU: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LTU; raw_branchcond = BCOND_NZERO; end
|
||||||
|
`RVOPC_BGEU: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LTU; raw_branchcond = BCOND_ZERO; end
|
||||||
|
`RVOPC_JALR: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_branchcond = BCOND_ALWAYS; raw_addr_is_regoffs = 1'b1;
|
||||||
|
raw_rs2 = X0; raw_aluop = ALUOP_ADD; raw_alusrc_a = ALUSRCA_PC; raw_alusrc_b = ALUSRCB_IMM; raw_imm = fd_cir_is_32bit ? 32'd4 : 32'd2; end
|
||||||
|
`RVOPC_JAL: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_branchcond = BCOND_ALWAYS; raw_rs1 = X0;
|
||||||
|
raw_rs2 = X0; raw_aluop = ALUOP_ADD; raw_alusrc_a = ALUSRCA_PC; raw_alusrc_b = ALUSRCB_IMM; raw_imm = fd_cir_is_32bit ? 32'd4 : 32'd2; end
|
||||||
|
`RVOPC_LUI: begin raw_aluop = ALUOP_RS2; raw_imm = d_imm_u; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; raw_rs1 = X0; end
|
||||||
|
`RVOPC_AUIPC: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_aluop = ALUOP_ADD; raw_imm = d_imm_u; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; raw_alusrc_a = ALUSRCA_PC; raw_rs1 = X0; end
|
||||||
|
`RVOPC_ADDI: begin raw_aluop = ALUOP_ADD; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||||
|
`RVOPC_SLLI: begin raw_aluop = ALUOP_SLL; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||||
|
`RVOPC_SLTI: begin raw_aluop = ALUOP_LT; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||||
|
`RVOPC_SLTIU: begin raw_aluop = ALUOP_LTU; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||||
|
`RVOPC_XORI: begin raw_aluop = ALUOP_XOR; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||||
|
`RVOPC_SRLI: begin raw_aluop = ALUOP_SRL; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||||
|
`RVOPC_SRAI: begin raw_aluop = ALUOP_SRA; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||||
|
`RVOPC_ORI: begin raw_aluop = ALUOP_OR; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||||
|
`RVOPC_ANDI: begin raw_aluop = ALUOP_AND; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end
|
||||||
|
`RVOPC_ADD: begin raw_aluop = ALUOP_ADD; end
|
||||||
|
`RVOPC_SUB: begin raw_aluop = ALUOP_SUB; end
|
||||||
|
`RVOPC_SLL: begin raw_aluop = ALUOP_SLL; end
|
||||||
|
`RVOPC_SLTU: begin raw_aluop = ALUOP_LTU; end
|
||||||
|
`RVOPC_XOR: begin raw_aluop = ALUOP_XOR; end
|
||||||
|
`RVOPC_SRL: begin raw_aluop = ALUOP_SRL; end
|
||||||
|
`RVOPC_SRA: begin raw_aluop = ALUOP_SRA; end
|
||||||
|
`RVOPC_OR: begin raw_aluop = ALUOP_OR; end
|
||||||
|
`RVOPC_AND: begin raw_aluop = ALUOP_AND; end
|
||||||
|
`RVOPC_LB: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LB; end
|
||||||
|
`RVOPC_LH: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LH; end
|
||||||
|
`RVOPC_LW: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LW; end
|
||||||
|
`RVOPC_LBU: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LBU; end
|
||||||
|
`RVOPC_LHU: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LHU; end
|
||||||
|
`RVOPC_SB: begin raw_addr_is_regoffs = 1'b1; raw_aluop = ALUOP_RS2; raw_memop = MEMOP_SB; raw_rd = X0; end
|
||||||
|
`RVOPC_SH: begin raw_addr_is_regoffs = 1'b1; raw_aluop = ALUOP_RS2; raw_memop = MEMOP_SH; raw_rd = X0; end
|
||||||
|
`RVOPC_SW: begin raw_addr_is_regoffs = 1'b1; raw_aluop = ALUOP_RS2; raw_memop = MEMOP_SW; raw_rd = X0; end
|
||||||
|
|
||||||
|
`RVOPC_SLT: begin
|
||||||
|
raw_aluop = ALUOP_LT;
|
||||||
|
if (|EXTENSION_XH3POWER && ~|raw_rd && ~|raw_rs1) begin
|
||||||
|
if (raw_rs2 == 5'h00) begin
|
||||||
|
// h3.block (power management hint)
|
||||||
|
d_invalid_32bit = trap_wfi;
|
||||||
|
raw_sleep_block = !trap_wfi;
|
||||||
|
end else if (raw_rs2 == 5'h01) begin
|
||||||
|
// h3.unblock (power management hint)
|
||||||
|
d_invalid_32bit = trap_wfi;
|
||||||
|
raw_sleep_unblock = !trap_wfi;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
`RVOPC_MUL: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MUL; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_MULH: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MULH; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_MULHSU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MULHSU; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_MULHU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MULHU; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_DIV: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_DIV; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_DIVU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_DIVU; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_REM: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_REM; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_REMU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_REMU; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
|
||||||
|
`RVOPC_LR_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_LR_W; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_SC_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_SC_W; raw_aluop = ALUOP_RS2; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_AMOSWAP_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_RS2; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_AMOADD_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_ADD; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_AMOXOR_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_XOR; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_AMOAND_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_AND; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_AMOOR_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_OR; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_AMOMIN_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MIN; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_AMOMAX_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MAX; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_AMOMINU_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MINU; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_AMOMAXU_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MAXU; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
|
||||||
|
`RVOPC_LD: if (EXTENSION_ZILSD) begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LW; raw_rd = {d_instr[11: 8], d_lspair_reg_sel}; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_SD: if (EXTENSION_ZILSD) begin raw_addr_is_regoffs = 1'b1; raw_rd = X0; raw_memop = MEMOP_SW; raw_rs2 = {d_instr[24:21], d_lspair_reg_sel}; raw_aluop = ALUOP_RS2; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
|
||||||
|
`RVOPC_SH1ADD: if (EXTENSION_ZBA) begin raw_aluop = ALUOP_SHXADD; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_SH2ADD: if (EXTENSION_ZBA) begin raw_aluop = ALUOP_SHXADD; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_SH3ADD: if (EXTENSION_ZBA) begin raw_aluop = ALUOP_SHXADD; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
|
||||||
|
`RVOPC_ANDN: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ANDN; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_CLZ: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_CLZ; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_CPOP: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_CPOP; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_CTZ: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_CTZ; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_MAX: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MAX; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_MAXU: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MAXU; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_MIN: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MIN; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_MINU: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MINU; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_ORC_B: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ORC_B; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_ORN: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ORN; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_REV8: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_REV8; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_ROL: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ROL; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_ROR: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ROR; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_RORI: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ROR; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_SEXT_B: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_SEXT_B; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_SEXT_H: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_SEXT_H; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_XNOR: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_XNOR; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
// Note: ZEXT_H is a subset of PACK from Zbkb. This is fine as long
|
||||||
|
// as this case appears first, since Zbkb implies Zbb on Hazard3.
|
||||||
|
`RVOPC_ZEXT_H: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ZEXT_H; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
|
||||||
|
`RVOPC_CLMUL: if (EXTENSION_ZBC) begin raw_aluop = ALUOP_CLMUL; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_CLMULH: if (EXTENSION_ZBC) begin raw_aluop = ALUOP_CLMUL; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_CLMULR: if (EXTENSION_ZBC) begin raw_aluop = ALUOP_CLMUL; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
|
||||||
|
`RVOPC_BCLR: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BCLR; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_BCLRI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BCLR; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_BEXT: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BEXT; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_BEXTI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BEXT; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_BINV: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BINV; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_BINVI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BINV; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_BSET: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BSET; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_BSETI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BSET; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
|
||||||
|
`RVOPC_PACK: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_PACK; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_PACKH: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_PACKH; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_BREV8: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_BREV8; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_UNZIP: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_UNZIP; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_ZIP: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_ZIP; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
|
||||||
|
`RVOPC_XPERM8: if (EXTENSION_ZBKX) begin raw_aluop = ALUOP_XPERM; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_XPERM4: if (EXTENSION_ZBKX) begin raw_aluop = ALUOP_XPERM; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
|
||||||
|
`RVOPC_H3_BEXTM: if (EXTENSION_XH3BEXTM) begin
|
||||||
|
raw_aluop = ALUOP_BEXTM; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_H3_BEXTMI: if (EXTENSION_XH3BEXTM) begin
|
||||||
|
raw_aluop = ALUOP_BEXTM; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_CSRRW: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = 1'b1 ; raw_csr_ren = |raw_rd; raw_csr_wtype = CSR_WTYPE_W; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_CSRRS: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_S; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_CSRRC: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_C; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_CSRRWI: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = 1'b1 ; raw_csr_ren = |raw_rd; raw_csr_wtype = CSR_WTYPE_W; raw_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_CSRRSI: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_S; raw_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_CSRRCI: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_C; raw_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
|
||||||
|
`RVOPC_FENCE: begin raw_rs2 = X0; raw_fence_d = 1'b1; end // Note rs1/rd are zero in instruction
|
||||||
|
`RVOPC_FENCE_I: if (EXTENSION_ZIFENCEI) begin raw_except = debug_mode ? EXCEPT_NONE : EXCEPT_REFETCH; raw_fence_i = 1'b1; end else begin d_invalid_32bit = 1'b1; end // note rs1/rs2/rd are zero in instruction
|
||||||
|
`RVOPC_ECALL: if (HAVE_CSR) begin raw_except = m_mode || !U_MODE ? EXCEPT_ECALL_M : EXCEPT_ECALL_U; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_EBREAK: if (HAVE_CSR) begin raw_except = EXCEPT_EBREAK; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_MRET: if (HAVE_CSR && m_mode) begin raw_except = EXCEPT_MRET; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
`RVOPC_WFI: if (HAVE_CSR && !trap_wfi) begin raw_sleep_wfi = 1'b1; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
|
|
||||||
|
default: begin d_invalid_32bit = 1'b1; end
|
||||||
|
endcase
|
||||||
|
|
||||||
|
if (|EXTENSION_E && (raw_rd[4] || raw_rs1[4] || raw_rs2[4])) begin
|
||||||
|
d_invalid_32bit = 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Then gate key signals based on CIR fullness, fetch faults etc. The split
|
||||||
|
// helps to avoid an event scheduling feedback loop that makes simulators
|
||||||
|
// unhappy and slow, particularly verilator
|
||||||
|
|
||||||
|
localparam [4:0] REGADDR_MASK = {~|EXTENSION_E, 4'hf};
|
||||||
|
|
||||||
|
always @ (*) begin
|
||||||
|
// Pass through by default
|
||||||
|
d_rs1 = raw_rs1 & REGADDR_MASK;
|
||||||
|
d_rs2 = raw_rs2 & REGADDR_MASK;
|
||||||
|
d_rd = raw_rd & REGADDR_MASK;
|
||||||
|
d_imm = raw_imm;
|
||||||
|
d_alusrc_a = raw_alusrc_a;
|
||||||
|
d_alusrc_b = raw_alusrc_b;
|
||||||
|
d_aluop = raw_aluop;
|
||||||
|
d_memop = raw_memop;
|
||||||
|
d_mulop = raw_mulop;
|
||||||
|
d_csr_ren = raw_csr_ren;
|
||||||
|
d_csr_wen = raw_csr_wen;
|
||||||
|
d_csr_wtype = raw_csr_wtype;
|
||||||
|
d_csr_w_imm = raw_csr_w_imm;
|
||||||
|
d_branchcond = raw_branchcond;
|
||||||
|
d_addr_is_regoffs = raw_addr_is_regoffs;
|
||||||
|
d_except = raw_except;
|
||||||
|
d_sleep_wfi = raw_sleep_wfi;
|
||||||
|
d_sleep_block = raw_sleep_block;
|
||||||
|
d_sleep_unblock = raw_sleep_unblock;
|
||||||
|
d_fence_i = raw_fence_i;
|
||||||
|
d_fence_d = raw_fence_d;
|
||||||
|
|
||||||
|
if (d_invalid || d_starved || d_except_instr_bus_fault || partial_predicted_branch) begin
|
||||||
|
d_rs1 = {W_REGADDR{1'b0}};
|
||||||
|
d_rs2 = {W_REGADDR{1'b0}};
|
||||||
|
d_rd = {W_REGADDR{1'b0}};
|
||||||
|
d_memop = MEMOP_NONE;
|
||||||
|
d_branchcond = BCOND_NEVER;
|
||||||
|
d_csr_ren = 1'b0;
|
||||||
|
d_csr_wen = 1'b0;
|
||||||
|
d_except = EXCEPT_NONE;
|
||||||
|
d_sleep_wfi = 1'b0;
|
||||||
|
d_sleep_block = 1'b0;
|
||||||
|
d_sleep_unblock = 1'b0;
|
||||||
|
d_fence_i = 1'b0;
|
||||||
|
d_fence_d = 1'b0;
|
||||||
|
|
||||||
|
if (EXTENSION_M)
|
||||||
|
d_aluop = ALUOP_ADD;
|
||||||
|
|
||||||
|
if (d_except_instr_bus_fault)
|
||||||
|
d_except = EXCEPT_INSTR_FAULT;
|
||||||
|
else if (d_invalid && !d_starved)
|
||||||
|
d_except = EXCEPT_INSTR_ILLEGAL;
|
||||||
|
end
|
||||||
|
if (partial_predicted_branch) begin
|
||||||
|
d_addr_is_regoffs = 1'b0;
|
||||||
|
d_branchcond = BCOND_ALWAYS;
|
||||||
|
end
|
||||||
|
if (cir_lock_prev) begin
|
||||||
|
d_branchcond = BCOND_NEVER;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,906 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_frontend #(
|
||||||
|
`include "hazard3_config.vh"
|
||||||
|
) (
|
||||||
|
input wire clk,
|
||||||
|
input wire rst_n,
|
||||||
|
|
||||||
|
// Fetch interface
|
||||||
|
// addr_vld may be asserted at any time, but after assertion,
|
||||||
|
// neither addr nor addr_vld may change until the cycle after addr_rdy.
|
||||||
|
// There is no backpressure on the data interface; the front end
|
||||||
|
// must ensure it does not request data it cannot receive.
|
||||||
|
// addr_rdy and dat_vld may be functions of hready, and
|
||||||
|
// may not be used to compute combinational outputs.
|
||||||
|
output wire mem_size, // 1'b1 -> 32 bit access
|
||||||
|
output wire [W_ADDR-1:0] mem_addr,
|
||||||
|
output wire mem_priv,
|
||||||
|
output wire mem_addr_vld,
|
||||||
|
input wire mem_addr_rdy,
|
||||||
|
input wire [W_DATA-1:0] mem_data,
|
||||||
|
input wire mem_data_err,
|
||||||
|
input wire mem_data_vld,
|
||||||
|
|
||||||
|
// Jump/flush interface
|
||||||
|
// Processor may assert vld at any time. The request will not go through
|
||||||
|
// unless rdy is high. Processor *may* alter request during this time.
|
||||||
|
// Inputs must not be a function of hready.
|
||||||
|
input wire [W_ADDR-1:0] jump_target,
|
||||||
|
input wire jump_priv,
|
||||||
|
input wire jump_target_vld,
|
||||||
|
output wire jump_target_rdy,
|
||||||
|
|
||||||
|
// Interface to the branch target buffer. `src_addr` is the address of the
|
||||||
|
// last halfword of a taken backward branch. The frontend redirects fetch
|
||||||
|
// such that `src_addr` appears to be sequentially followed by `target`.
|
||||||
|
input wire btb_set,
|
||||||
|
input wire [W_ADDR-1:0] btb_set_src_addr,
|
||||||
|
input wire btb_set_src_size,
|
||||||
|
input wire [W_ADDR-1:0] btb_set_target_addr,
|
||||||
|
input wire btb_clear,
|
||||||
|
output wire [W_ADDR-1:0] btb_target_addr_out,
|
||||||
|
|
||||||
|
// Interface to Decode
|
||||||
|
output reg [31:0] cir, // Current instruction register; pre-expanded to 32-bit
|
||||||
|
output wire [31:0] cir_raw, // Unexpanded instruction data
|
||||||
|
output reg [1:0] cir_vld, // number of valid halfwords in CIR
|
||||||
|
input wire [1:0] cir_use, // number of halfwords D intends to consume
|
||||||
|
// *may* be a function of hready
|
||||||
|
output wire [1:0] cir_err, // Bus error on upper/lower halfword of CIR.
|
||||||
|
output wire [1:0] cir_predbranch, // Set for last halfword of a predicted-taken branch
|
||||||
|
output wire cir_break_any, // Set for exact match of a breakpoint address on CIR LSB
|
||||||
|
output wire cir_break_d_mode, // As above but specifically break to debug mode
|
||||||
|
output reg cir_is_32bit, // Can't be decoded from CIR due to pre-expansion
|
||||||
|
output reg cir_invalid_16bit, // Expanded an invalid 32-bit instruction
|
||||||
|
output reg cir_is_uop, // Current instruction is part of a micro-op sequence
|
||||||
|
output reg cir_uop_nonfinal, // ...and there are more to follow in this instruction
|
||||||
|
output reg cir_uop_no_pc_update, // Suppress PC increment or jump (note the jump in cm.popret is not the final uop!)
|
||||||
|
output reg cir_uop_atomic, // Prevent IRQ entry, so intermediate states are not observed
|
||||||
|
input wire uop_stall,
|
||||||
|
input wire uop_clear,
|
||||||
|
|
||||||
|
// "flush_behind": do not flush the oldest instruction when accepting a
|
||||||
|
// jump request (but still flush younger instructions). Sometimes a
|
||||||
|
// stalled instruction may assert a jump request, because e.g. the stall
|
||||||
|
// is dependent on a bus stall signal so can't gate the request.
|
||||||
|
input wire cir_flush_behind,
|
||||||
|
// Required for regnum predecode when Zilsd is enabled:
|
||||||
|
input wire df_lspair_phase_next,
|
||||||
|
|
||||||
|
// Signal to power controller that power down is safe. (When going to
|
||||||
|
// sleep, first the pipeline is stalled, and then the power controller
|
||||||
|
// waits for the frontend to naturally come to a halt before releasing
|
||||||
|
// its power request. This avoids manually halting the frontend.)
|
||||||
|
output wire pwrdown_ok,
|
||||||
|
// Signal to delay the first instruction fetch following reset, because
|
||||||
|
// powerup has not yet been negotiated.
|
||||||
|
input wire delay_first_fetch,
|
||||||
|
|
||||||
|
// Provide the rs1/rs2 register numbers which will be in CIR next cycle.
|
||||||
|
// Coarse: valid if this instruction has a nonzero register operand.
|
||||||
|
// (Suitable for regfile read)
|
||||||
|
output reg [4:0] predecode_rs1_coarse,
|
||||||
|
output reg [4:0] predecode_rs2_coarse,
|
||||||
|
// Fine: like coarse, but accurate zeroing when the operand is implicit.
|
||||||
|
// (Suitable for bypass. Still not precise enough for stall logic.)
|
||||||
|
output reg [4:0] predecode_rs1_fine,
|
||||||
|
output reg [4:0] predecode_rs2_fine,
|
||||||
|
|
||||||
|
// Debugger instruction injection: instruction fetch is suppressed when in
|
||||||
|
// debug halt state, and the DM can then inject instructions into the last
|
||||||
|
// entry of the prefetch queue using the vld/rdy handshake.
|
||||||
|
input wire debug_mode,
|
||||||
|
input wire [W_DATA-1:0] dbg_instr_data,
|
||||||
|
input wire dbg_instr_data_vld,
|
||||||
|
output wire dbg_instr_data_rdy,
|
||||||
|
|
||||||
|
// PMP query->kill interface for X permission checks
|
||||||
|
output wire [W_ADDR-1:0] pmp_i_addr,
|
||||||
|
output wire pmp_i_m_mode,
|
||||||
|
input wire pmp_i_kill,
|
||||||
|
|
||||||
|
// Trigger unit query->break interface for breakpoints
|
||||||
|
output wire [W_ADDR-1:0] trigger_addr,
|
||||||
|
output wire trigger_m_mode,
|
||||||
|
input wire [1:0] trigger_break_any,
|
||||||
|
input wire [1:0] trigger_break_d_mode
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
`include "rv_opcodes.vh"
|
||||||
|
|
||||||
|
localparam W_BUNDLE = 16;
|
||||||
|
// This is the minimum for full throughput (enough to avoid dropping data when
|
||||||
|
// decode stalls) and there is no significant advantage to going larger.
|
||||||
|
localparam FIFO_DEPTH = 2;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Fetch queue
|
||||||
|
|
||||||
|
wire jump_now = jump_target_vld && jump_target_rdy;
|
||||||
|
reg [1:0] mem_data_hwvld;
|
||||||
|
|
||||||
|
// PMP X faults are checked in parallel with the fetch (fine if executable
|
||||||
|
// memory is read-idempotent) and failures are promoted to bus errors:
|
||||||
|
wire pmp_kill_fetch_dph;
|
||||||
|
wire mem_or_pmp_err = mem_data_err || pmp_kill_fetch_dph;
|
||||||
|
|
||||||
|
// Similarly, breakpoint matches are checked during fetch data phase. These
|
||||||
|
// are called mem_xxx because they are the breakpoint metadata for the data
|
||||||
|
// coming back from memory in this dphase.
|
||||||
|
wire [1:0] mem_break_any;
|
||||||
|
wire [1:0] mem_break_d_mode;
|
||||||
|
|
||||||
|
// Mark data as containing a predicted-taken branch instruction so that
|
||||||
|
// mispredicts can be recovered -- need to track both halfwords so that we
|
||||||
|
// can mark the entire instruction, and nothing but the instruction:
|
||||||
|
reg [1:0] mem_data_predbranch;
|
||||||
|
|
||||||
|
// Bus errors (and other metadata) travel alongside data. They cause an
|
||||||
|
// exception if the core decodes the instruction, but until then can be
|
||||||
|
// flushed harmlessly.
|
||||||
|
|
||||||
|
reg [W_DATA-1:0] fifo_mem [0:FIFO_DEPTH];
|
||||||
|
reg fifo_err [0:FIFO_DEPTH];
|
||||||
|
reg [1:0] fifo_break_any [0:FIFO_DEPTH];
|
||||||
|
reg [1:0] fifo_break_d_mode [0:FIFO_DEPTH];
|
||||||
|
reg [1:0] fifo_predbranch [0:FIFO_DEPTH];
|
||||||
|
reg [1:0] fifo_valid_hw [0:FIFO_DEPTH];
|
||||||
|
reg fifo_valid [0:FIFO_DEPTH];
|
||||||
|
reg fifo_valid_m1 [0:FIFO_DEPTH];
|
||||||
|
|
||||||
|
wire [W_DATA-1:0] fifo_rdata = fifo_mem[0];
|
||||||
|
wire fifo_full = fifo_valid[FIFO_DEPTH - 1];
|
||||||
|
wire fifo_empty = !fifo_valid[0];
|
||||||
|
wire fifo_almost_full = fifo_valid[FIFO_DEPTH - 2];
|
||||||
|
|
||||||
|
wire fifo_push;
|
||||||
|
wire fifo_pop;
|
||||||
|
wire fifo_dbg_inject = DEBUG_SUPPORT && dbg_instr_data_vld && dbg_instr_data_rdy;
|
||||||
|
|
||||||
|
always @ (*) begin: boundary_conditions
|
||||||
|
integer i;
|
||||||
|
fifo_mem[FIFO_DEPTH] = mem_data;
|
||||||
|
fifo_predbranch[FIFO_DEPTH] = 2'b00;
|
||||||
|
fifo_err[FIFO_DEPTH] = 1'b0;
|
||||||
|
fifo_break_any[FIFO_DEPTH] = 2'b00;
|
||||||
|
fifo_break_d_mode[FIFO_DEPTH] = 2'b00;
|
||||||
|
for (i = 0; i <= FIFO_DEPTH; i = i + 1) begin
|
||||||
|
fifo_valid[i] = |EXTENSION_C ? |fifo_valid_hw[i] : fifo_valid_hw[i][0];
|
||||||
|
// valid-to-right condition: i == 0 || fifo_valid[i - 1], but without
|
||||||
|
// using negative array bound (seems broken in Yosys?) or OOB in the
|
||||||
|
// short circuit case (gives lint although result is well-defined)
|
||||||
|
if (i == 0) begin
|
||||||
|
fifo_valid_m1[i] = 1'b1;
|
||||||
|
end else begin
|
||||||
|
fifo_valid_m1[i] = fifo_valid[i - 1];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin: fifo_update
|
||||||
|
integer i;
|
||||||
|
if (!rst_n) begin
|
||||||
|
for (i = 0; i < FIFO_DEPTH; i = i + 1) begin
|
||||||
|
fifo_valid_hw[i] <= 2'b00;
|
||||||
|
fifo_mem[i] <= 32'd0;
|
||||||
|
fifo_err[i] <= 1'b0;
|
||||||
|
fifo_break_any[i] <= 2'b00;
|
||||||
|
fifo_break_d_mode[i] <= 2'b00;
|
||||||
|
fifo_predbranch[i] <= 2'b00;
|
||||||
|
end
|
||||||
|
// This exists only for loop boundary conditions, but is tied off in
|
||||||
|
// this synchronous process to work around a Verilator scheduling
|
||||||
|
// issue (see issue #21)
|
||||||
|
fifo_valid_hw[FIFO_DEPTH] <= 2'b00;
|
||||||
|
end else begin
|
||||||
|
for (i = 0; i < FIFO_DEPTH; i = i + 1) begin
|
||||||
|
if (fifo_pop || (fifo_push && !fifo_valid[i])) begin
|
||||||
|
fifo_mem[i] <= fifo_valid[i + 1] ? fifo_mem[i + 1] : mem_data;
|
||||||
|
fifo_err[i] <= fifo_valid[i + 1] ? fifo_err[i + 1] : mem_or_pmp_err;
|
||||||
|
fifo_break_any[i] <= fifo_valid[i + 1] ? fifo_break_any[i + 1] : mem_break_any;
|
||||||
|
fifo_break_d_mode[i] <= fifo_valid[i + 1] ? fifo_break_d_mode[i + 1] : mem_break_d_mode;
|
||||||
|
fifo_predbranch[i] <= fifo_valid[i + 1] ? fifo_predbranch[i + 1] : mem_data_predbranch;
|
||||||
|
end
|
||||||
|
fifo_valid_hw[i] <=
|
||||||
|
jump_now ? 2'h0 :
|
||||||
|
fifo_valid[i + 1] && fifo_pop ? fifo_valid_hw[i + 1] :
|
||||||
|
fifo_valid[i] && fifo_pop ? mem_data_hwvld & {2{fifo_push}} :
|
||||||
|
fifo_valid[i] ? fifo_valid_hw[i] :
|
||||||
|
fifo_push && !fifo_pop && fifo_valid_m1[i] ? mem_data_hwvld : 2'h0;
|
||||||
|
end
|
||||||
|
// Allow DM to inject instructions directly into the lowest-numbered
|
||||||
|
// queue entry. This mux should not extend critical path since it is
|
||||||
|
// balanced with the instruction-assembly muxes on the queue bypass
|
||||||
|
// path. Note that flush takes precedence over debug injection
|
||||||
|
// (and the debug module design must account for this)
|
||||||
|
if (fifo_dbg_inject) begin
|
||||||
|
fifo_mem[0] <= dbg_instr_data;
|
||||||
|
fifo_err[0] <= 1'b0;
|
||||||
|
fifo_predbranch[0] <= 2'b00;
|
||||||
|
fifo_break_any[0] <= 2'b00;
|
||||||
|
fifo_break_d_mode[0] <= 2'b00;
|
||||||
|
fifo_valid_hw[0] <= jump_now ? 2'b00 : 2'b11;
|
||||||
|
end
|
||||||
|
fifo_valid_hw[FIFO_DEPTH] <= 2'b00;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifdef HAZARD3_ASSERTIONS
|
||||||
|
always @ (posedge clk) if (rst_n) begin
|
||||||
|
// FIFO validity must be compact, so we can always consume from the end
|
||||||
|
if (!fifo_valid[0]) begin
|
||||||
|
assert(!fifo_valid[1]);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
|
||||||
|
assign pwrdown_ok = (fifo_full && !jump_target_vld) || debug_mode;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Branch target buffer
|
||||||
|
|
||||||
|
wire [W_ADDR-1:0] btb_src_addr;
|
||||||
|
wire btb_src_size;
|
||||||
|
wire [W_ADDR-1:0] btb_target_addr;
|
||||||
|
wire btb_valid;
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (BRANCH_PREDICTOR) begin: have_btb
|
||||||
|
reg [W_ADDR-1:0] btb_src_addr_r;
|
||||||
|
reg btb_src_size_r;
|
||||||
|
reg [W_ADDR-1:0] btb_target_addr_r;
|
||||||
|
reg btb_valid_r;
|
||||||
|
assign btb_src_addr = btb_src_addr_r;
|
||||||
|
assign btb_src_size = btb_src_size_r;
|
||||||
|
assign btb_target_addr = btb_target_addr_r;
|
||||||
|
assign btb_valid = btb_valid_r;
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
btb_src_addr_r <= {W_ADDR{1'b0}};
|
||||||
|
btb_src_size_r <= 1'b0;
|
||||||
|
btb_target_addr_r <= {W_ADDR{1'b0}};
|
||||||
|
btb_valid_r <= 1'b0;
|
||||||
|
end else if (btb_clear) begin
|
||||||
|
// Clear takes precedences over set. E.g. if a taken branch is in
|
||||||
|
// stage 2 and an exception is in stage 3, we must clear the BTB.
|
||||||
|
btb_valid_r <= 1'b0;
|
||||||
|
end else if (btb_set) begin
|
||||||
|
btb_src_addr_r <= btb_set_src_addr;
|
||||||
|
btb_src_size_r <= btb_set_src_size;
|
||||||
|
btb_target_addr_r <= btb_set_target_addr;
|
||||||
|
btb_valid_r <= 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end else begin: no_btb
|
||||||
|
assign btb_src_addr = {W_ADDR{1'b0}};
|
||||||
|
assign btb_src_size = 1'b0;
|
||||||
|
assign btb_target_addr = {W_ADDR{1'b0}};
|
||||||
|
assign btb_valid = 1'b0;
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
// Decode uses the target address to set the PC to the correct branch target
|
||||||
|
// value following a predicted-taken branch (as normally it would update PC
|
||||||
|
// by following an X jump request, and in this case there is none).
|
||||||
|
//
|
||||||
|
// Note this assumes the BTB target has not changed by the time the predicted
|
||||||
|
// branch arrives at decode! This is always true because the only way for the
|
||||||
|
// target address to change is when an older branch is taken, which would
|
||||||
|
// flush the younger predicted-taken branch before it reaches decode.
|
||||||
|
|
||||||
|
assign btb_target_addr_out = btb_target_addr;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Fetch request generation
|
||||||
|
|
||||||
|
// Fetch addr runs ahead of the PC, in word increments.
|
||||||
|
reg [W_ADDR-1:0] fetch_addr;
|
||||||
|
reg fetch_priv;
|
||||||
|
reg btb_prev_start_of_overhanging;
|
||||||
|
reg [1:0] mem_aph_hwvld;
|
||||||
|
reg mem_addr_hold;
|
||||||
|
|
||||||
|
wire btb_match_word = |BRANCH_PREDICTOR && btb_valid && (
|
||||||
|
fetch_addr[W_ADDR-1:2] == btb_src_addr[W_ADDR-1:2]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Catch case where predicted-taken branch instruction extends into next word:
|
||||||
|
wire btb_src_overhanging = btb_src_size && btb_src_addr[1];
|
||||||
|
|
||||||
|
// Suppress case where we have jumped immediately after a word-aligned halfword-sized
|
||||||
|
// branch, and the jump target went into fetch_addr due to an address-phase hold:
|
||||||
|
wire btb_jumped_beyond = !btb_src_size && !btb_src_addr[1] && !mem_aph_hwvld[0];
|
||||||
|
|
||||||
|
wire btb_match_current_addr = btb_match_word && !btb_src_overhanging && !btb_jumped_beyond;
|
||||||
|
wire btb_match_next_addr = btb_match_word && btb_src_overhanging;
|
||||||
|
|
||||||
|
wire btb_match_now = btb_match_current_addr || btb_prev_start_of_overhanging;
|
||||||
|
|
||||||
|
// Post-increment if jump request is going straight through
|
||||||
|
wire [W_ADDR-1:0] jump_target_post_increment =
|
||||||
|
{jump_target[W_ADDR-1:2], 2'b00} +
|
||||||
|
{{W_ADDR-3{1'b0}}, mem_addr_rdy && !mem_addr_hold, 2'b00};
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
fetch_addr <= RESET_VECTOR;
|
||||||
|
// M-mode at reset:
|
||||||
|
fetch_priv <= 1'b1;
|
||||||
|
btb_prev_start_of_overhanging <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
if (jump_now) begin
|
||||||
|
fetch_addr <= jump_target_post_increment;
|
||||||
|
fetch_priv <= jump_priv || !U_MODE;
|
||||||
|
btb_prev_start_of_overhanging <= 1'b0;
|
||||||
|
end else if (mem_addr_vld && mem_addr_rdy) begin
|
||||||
|
if (btb_match_now && |BRANCH_PREDICTOR) begin
|
||||||
|
fetch_addr <= {btb_target_addr[W_ADDR-1:2], 2'b00};
|
||||||
|
end else begin
|
||||||
|
fetch_addr <= fetch_addr + 32'd4;
|
||||||
|
end
|
||||||
|
btb_prev_start_of_overhanging <= btb_match_next_addr;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Combinatorially generate the address-phase request
|
||||||
|
|
||||||
|
reg reset_holdoff;
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
reset_holdoff <= 1'b1;
|
||||||
|
end else begin
|
||||||
|
reset_holdoff <= (|EXTENSION_XH3POWER && delay_first_fetch) ? reset_holdoff : 1'b0;
|
||||||
|
// This should be impossible, but assert to be sure, because it *will*
|
||||||
|
// change the fetch address (and we shouldn't check it in hardware if
|
||||||
|
// we can prove it doesn't happen)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifdef HAZARD3_ASSERTIONS
|
||||||
|
always @ (posedge clk) if (rst_n) begin
|
||||||
|
assert(!(jump_target_vld && reset_holdoff));
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
|
||||||
|
reg [W_ADDR-1:0] mem_addr_r;
|
||||||
|
reg mem_priv_r;
|
||||||
|
reg mem_addr_vld_r;
|
||||||
|
|
||||||
|
// Downstream accesses are always word-sized word-aligned.
|
||||||
|
assign mem_addr = mem_addr_r;
|
||||||
|
assign mem_priv = mem_priv_r;
|
||||||
|
assign mem_addr_vld = mem_addr_vld_r && !reset_holdoff;
|
||||||
|
assign mem_size = 1'b1;
|
||||||
|
|
||||||
|
wire fetch_stall;
|
||||||
|
|
||||||
|
always @ (*) begin
|
||||||
|
mem_addr_r = fetch_addr;
|
||||||
|
mem_priv_r = fetch_priv;
|
||||||
|
mem_addr_vld_r = 1'b1;
|
||||||
|
case (1'b1)
|
||||||
|
mem_addr_hold : begin mem_addr_r = fetch_addr; end
|
||||||
|
jump_target_vld || reset_holdoff : begin
|
||||||
|
mem_addr_r = {jump_target[W_ADDR-1:2], 2'b00};
|
||||||
|
mem_priv_r = jump_priv || !U_MODE;
|
||||||
|
end
|
||||||
|
DEBUG_SUPPORT && debug_mode : begin mem_addr_vld_r = 1'b0; end
|
||||||
|
!fetch_stall : begin mem_addr_r = fetch_addr; end
|
||||||
|
default : begin mem_addr_vld_r = 1'b0; end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
assign jump_target_rdy = !mem_addr_hold;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Bus Pipeline Tracking
|
||||||
|
|
||||||
|
// Keep track of some useful state of the memory interface
|
||||||
|
|
||||||
|
reg [1:0] pending_fetches;
|
||||||
|
reg [1:0] ctr_flush_pending;
|
||||||
|
|
||||||
|
wire [1:0] pending_fetches_next = pending_fetches + (mem_addr_vld && !mem_addr_hold) - mem_data_vld;
|
||||||
|
|
||||||
|
// Using the non-registered version of pending_fetches would improve FIFO
|
||||||
|
// utilisation, but create a combinatorial path from hready to address phase!
|
||||||
|
// This means at least a 2-word FIFO is required for full fetch throughput.
|
||||||
|
assign fetch_stall = fifo_full
|
||||||
|
|| fifo_almost_full && |pending_fetches
|
||||||
|
|| pending_fetches > 2'h1;
|
||||||
|
|
||||||
|
// Debugger only injects instructions when the frontend is at rest and empty.
|
||||||
|
assign dbg_instr_data_rdy = DEBUG_SUPPORT && !fifo_valid[0] && ~|ctr_flush_pending;
|
||||||
|
|
||||||
|
wire cir_room_for_fetch;
|
||||||
|
// If fetch data is forwarded past the FIFO, ensure it is not also written to it.
|
||||||
|
assign fifo_push = mem_data_vld && ~|ctr_flush_pending && !(cir_room_for_fetch && fifo_empty)
|
||||||
|
&& !(DEBUG_SUPPORT && debug_mode);
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
mem_addr_hold <= 1'b0;
|
||||||
|
pending_fetches <= 2'h0;
|
||||||
|
ctr_flush_pending <= 2'h0;
|
||||||
|
end else begin
|
||||||
|
mem_addr_hold <= mem_addr_vld && !mem_addr_rdy;
|
||||||
|
pending_fetches <= pending_fetches_next;
|
||||||
|
if (jump_now) begin
|
||||||
|
ctr_flush_pending <= pending_fetches - mem_data_vld;
|
||||||
|
end else if (|ctr_flush_pending && mem_data_vld) begin
|
||||||
|
ctr_flush_pending <= ctr_flush_pending - 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifdef HAZARD3_ASSERTIONS
|
||||||
|
always @ (posedge clk) if (rst_n) begin
|
||||||
|
assert(ctr_flush_pending <= pending_fetches);
|
||||||
|
assert(pending_fetches < 2'd3);
|
||||||
|
assert(!(mem_data_vld && !pending_fetches));
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
mem_data_hwvld <= 2'b11;
|
||||||
|
mem_aph_hwvld <= 2'b11;
|
||||||
|
mem_data_predbranch <= 2'b00;
|
||||||
|
end else begin
|
||||||
|
if (jump_now) begin
|
||||||
|
if (|EXTENSION_C) begin
|
||||||
|
if (mem_addr_rdy) begin
|
||||||
|
mem_aph_hwvld <= 2'b11;
|
||||||
|
mem_data_hwvld <= {1'b1, !jump_target[1]};
|
||||||
|
end else begin
|
||||||
|
mem_aph_hwvld <= {1'b1, !jump_target[1]};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mem_data_predbranch <= 2'b00;
|
||||||
|
end else if (mem_addr_vld && mem_addr_rdy) begin
|
||||||
|
if (|EXTENSION_C) begin
|
||||||
|
// If a predicted-taken branch instruction only spans the first
|
||||||
|
// half of a word, need to flag the second half as invalid.
|
||||||
|
mem_data_hwvld <= mem_aph_hwvld & {
|
||||||
|
!(|BRANCH_PREDICTOR && btb_match_now && (btb_src_addr[1] == btb_src_size)),
|
||||||
|
1'b1
|
||||||
|
};
|
||||||
|
// Also need to take the alignment of the destination into account.
|
||||||
|
mem_aph_hwvld <= {
|
||||||
|
1'b1,
|
||||||
|
!(|BRANCH_PREDICTOR && btb_match_now && btb_target_addr[1])
|
||||||
|
};
|
||||||
|
end
|
||||||
|
mem_data_predbranch <=
|
||||||
|
|BRANCH_PREDICTOR && btb_match_word ? (
|
||||||
|
btb_src_addr[1] ? 2'b10 :
|
||||||
|
btb_src_size ? 2'b11 : 2'b01
|
||||||
|
) :
|
||||||
|
|BRANCH_PREDICTOR && btb_prev_start_of_overhanging ? (
|
||||||
|
2'b01
|
||||||
|
) : 2'b00;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// PMP and trigger unit interfacing: query -> kill/break
|
||||||
|
|
||||||
|
wire [W_ADDR-1:0] pmp_trigger_check_dph_addr;
|
||||||
|
wire pmp_trigger_check_dph_m_mode;
|
||||||
|
|
||||||
|
// Register the fetch address into stage F so that the PMP can check it in
|
||||||
|
// parallel with the bus data phase. Feels wasteful to have a separate
|
||||||
|
// register, but using the fetch_addr counter is fraught due to the way that
|
||||||
|
// new addresses go into it or past it (depending on aphase hold).
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (PMP_REGIONS > 0 || DEBUG_SUPPORT != 0) begin: have_check_reg
|
||||||
|
|
||||||
|
reg [W_ADDR-1:0] check_addr_dph;
|
||||||
|
reg check_m_mode_dph;
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
check_addr_dph <= {W_ADDR{1'b0}};
|
||||||
|
check_m_mode_dph <= 1'b0;
|
||||||
|
end else if (mem_addr_vld && mem_addr_rdy) begin
|
||||||
|
check_addr_dph <= mem_addr;
|
||||||
|
check_m_mode_dph <= mem_priv;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign pmp_trigger_check_dph_addr = check_addr_dph;
|
||||||
|
assign pmp_trigger_check_dph_m_mode = check_m_mode_dph;
|
||||||
|
|
||||||
|
end else begin: no_check_reg
|
||||||
|
|
||||||
|
assign pmp_trigger_check_dph_addr = {W_ADDR{1'b0}};
|
||||||
|
assign pmp_trigger_check_dph_m_mode = 1'b0;
|
||||||
|
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (PMP_REGIONS == 0) begin: no_pmp
|
||||||
|
|
||||||
|
assign pmp_i_addr = {W_ADDR{1'b0}};
|
||||||
|
assign pmp_i_m_mode = 1'b0;
|
||||||
|
assign pmp_kill_fetch_dph = 1'b0;
|
||||||
|
|
||||||
|
end else begin: have_pmp
|
||||||
|
|
||||||
|
assign pmp_i_addr = pmp_trigger_check_dph_addr;
|
||||||
|
assign pmp_i_m_mode = pmp_trigger_check_dph_m_mode;
|
||||||
|
assign pmp_kill_fetch_dph = pmp_i_kill && !debug_mode;
|
||||||
|
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (DEBUG_SUPPORT == 0) begin: no_triggers
|
||||||
|
|
||||||
|
assign trigger_addr = {W_ADDR{1'b0}};
|
||||||
|
assign trigger_m_mode = 1'b0;
|
||||||
|
assign mem_break_any = 2'b00;
|
||||||
|
assign mem_break_d_mode = 2'b00;
|
||||||
|
|
||||||
|
end else begin: have_triggers
|
||||||
|
|
||||||
|
assign trigger_addr = pmp_trigger_check_dph_addr;
|
||||||
|
assign trigger_m_mode = pmp_trigger_check_dph_m_mode;
|
||||||
|
assign mem_break_any = trigger_break_any & {|EXTENSION_C, 1'b1};
|
||||||
|
assign mem_break_d_mode = trigger_break_d_mode & {|EXTENSION_C, 1'b1};
|
||||||
|
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Instruction buffer
|
||||||
|
|
||||||
|
// The instruction buffer is a 3 x ~16-bit shift register:
|
||||||
|
//
|
||||||
|
// * 2 x 16-bit entries form the 32-bit current instruction register (CIR)
|
||||||
|
// which is the processor's decode window
|
||||||
|
//
|
||||||
|
// * 1 x 16-bit entry allows the decode window to be non-32-bit-aligned with
|
||||||
|
// respect to the 2 x 32-bit prefetch queue entries, which are always
|
||||||
|
// naturally aligned in memory (if fully populated).
|
||||||
|
//
|
||||||
|
// The third entry should be trimmed for non-RVC configurations due to
|
||||||
|
// constant-folding on EXTENSION_C; it is unnecessary here because the
|
||||||
|
// instructions are always 32-bit-aligned.
|
||||||
|
|
||||||
|
// The entries ("slots") are slightly larger than 16 bits because they also
|
||||||
|
// contain metadata like bus errors:
|
||||||
|
localparam W_SLOT = 4 + W_BUNDLE;
|
||||||
|
localparam SLOT_BREAK_ANY_BIT = 3 + W_BUNDLE;
|
||||||
|
localparam SLOT_BREAK_D_MODE_BIT = 2 + W_BUNDLE;
|
||||||
|
localparam SLOT_ERR_BIT = 1 + W_BUNDLE;
|
||||||
|
localparam SLOT_PREDBRANCH_BIT = 0 + W_BUNDLE;
|
||||||
|
|
||||||
|
reg [3*W_SLOT-1:0] buf_contents;
|
||||||
|
reg [1:0] buf_level;
|
||||||
|
|
||||||
|
wire fetch_data_vld = !fifo_empty || (mem_data_vld && ~|ctr_flush_pending && !debug_mode);
|
||||||
|
|
||||||
|
wire [W_DATA-1:0] fetch_data = fifo_empty ? mem_data : fifo_rdata;
|
||||||
|
wire [1:0] fetch_data_hwvld = fifo_empty ? mem_data_hwvld : fifo_valid_hw[0];
|
||||||
|
wire fetch_bus_err = fifo_empty ? mem_or_pmp_err : fifo_err[0];
|
||||||
|
wire [1:0] fetch_break_any = fifo_empty ? mem_break_any : fifo_break_any[0];
|
||||||
|
wire [1:0] fetch_break_d_mode = fifo_empty ? mem_break_d_mode : fifo_break_d_mode[0];
|
||||||
|
wire [1:0] fetch_predbranch = fifo_empty ? mem_data_predbranch : fifo_predbranch[0];
|
||||||
|
|
||||||
|
wire [W_SLOT-1:0] fetch_contents_hw1 = {
|
||||||
|
fetch_break_any[1],
|
||||||
|
fetch_break_d_mode[1],
|
||||||
|
fetch_bus_err,
|
||||||
|
fetch_predbranch[1],
|
||||||
|
fetch_data[W_BUNDLE +: W_BUNDLE]
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [W_SLOT-1:0] fetch_contents_hw0 = {
|
||||||
|
fetch_break_any[0],
|
||||||
|
fetch_break_d_mode[0],
|
||||||
|
fetch_bus_err,
|
||||||
|
fetch_predbranch[0],
|
||||||
|
fetch_data[0 +: W_BUNDLE]
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [2*W_SLOT-1:0] fetch_contents_aligned = {
|
||||||
|
fetch_contents_hw1,
|
||||||
|
fetch_data_hwvld[0] || ~|EXTENSION_C ? fetch_contents_hw0 : fetch_contents_hw1
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shift not-yet-used contents down to backfill D's consumption. We don't care
|
||||||
|
// about anything which is invalid or will be overlaid with fresh data, so
|
||||||
|
// choose these values in a way that minimises muxes.
|
||||||
|
wire [3*W_SLOT-1:0] buf_shifted =
|
||||||
|
cir_use[1] ? {buf_contents[W_SLOT +: 2 * W_SLOT], buf_contents[2 * W_SLOT +: W_SLOT]} :
|
||||||
|
cir_use[0] && EXTENSION_C ? {buf_contents[2 * W_SLOT +: W_SLOT], buf_contents[W_SLOT +: 2 * W_SLOT]} :
|
||||||
|
buf_contents;
|
||||||
|
|
||||||
|
wire [1:0] level_next_no_fetch = buf_level - cir_use;
|
||||||
|
|
||||||
|
// Overlay fresh fetch data onto the shifted/recycled buffer contents. Again,
|
||||||
|
// if something won't be looked at, generate the cheapest possible garbage.
|
||||||
|
assign cir_room_for_fetch = level_next_no_fetch <= (|EXTENSION_C && ~&fetch_data_hwvld ? 2'h2 : 2'h1);
|
||||||
|
assign fifo_pop = cir_room_for_fetch && !fifo_empty;
|
||||||
|
|
||||||
|
wire [3*W_SLOT-1:0] buf_shifted_plus_fetch =
|
||||||
|
!cir_room_for_fetch ? buf_shifted :
|
||||||
|
level_next_no_fetch[1] && |EXTENSION_C ? {fetch_contents_aligned[0 +: W_SLOT], buf_shifted[0 +: 2 * W_SLOT]} :
|
||||||
|
level_next_no_fetch[0] && |EXTENSION_C ? {fetch_contents_aligned, buf_shifted[0 +: W_SLOT]} :
|
||||||
|
{buf_shifted[2 * W_SLOT +: W_SLOT], fetch_contents_aligned};
|
||||||
|
|
||||||
|
wire [1:0] fetch_fill_amount = cir_room_for_fetch && fetch_data_vld ? (
|
||||||
|
&fetch_data_hwvld || ~|EXTENSION_C ? 2'h2 : 2'h1
|
||||||
|
) : 2'h0;
|
||||||
|
|
||||||
|
wire [1:0] buf_level_next = {1'b1, |EXTENSION_C} & (
|
||||||
|
jump_now && cir_flush_behind ? (cir_is_32bit ? 2'h2 : 2'h1) :
|
||||||
|
jump_now ? 2'h0 : level_next_no_fetch + fetch_fill_amount
|
||||||
|
);
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
buf_level <= 2'h0;
|
||||||
|
cir_vld <= 2'h0;
|
||||||
|
// Mysterious reset value ensures address buses are zero in reset
|
||||||
|
// (see definition of d_addr_offs in hazard3_decode)
|
||||||
|
buf_contents <= {{3 * W_SLOT - 2{1'b0}}, 2'b11};
|
||||||
|
end else begin
|
||||||
|
buf_level <= buf_level_next;
|
||||||
|
cir_vld <= buf_level_next & ~(buf_level_next >> 1'b1);
|
||||||
|
buf_contents <= buf_shifted_plus_fetch;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifdef HAZARD3_ASSERTIONS
|
||||||
|
reg [1:0] prop_past_buf_level; // Workaround for weird non-constant $past reset issue
|
||||||
|
always @ (posedge clk) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
prop_past_buf_level <= 2'h0;
|
||||||
|
end else begin
|
||||||
|
prop_past_buf_level <= buf_level;
|
||||||
|
|
||||||
|
assert(cir_vld <= 2);
|
||||||
|
assert(cir_use <= cir_vld);
|
||||||
|
if (!jump_now) assert(buf_level_next >= level_next_no_fetch);
|
||||||
|
// We fetch 32 bits per cycle, max. If this happens it's due to negative overflow.
|
||||||
|
if (prop_past_buf_level == 2'h0)
|
||||||
|
assert(buf_level != 2'h3);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
|
||||||
|
assign cir_err = {
|
||||||
|
buf_contents[1 * W_SLOT + SLOT_ERR_BIT],
|
||||||
|
buf_contents[0 * W_SLOT + SLOT_ERR_BIT]
|
||||||
|
};
|
||||||
|
|
||||||
|
assign cir_predbranch = {
|
||||||
|
buf_contents[1 * W_SLOT + SLOT_PREDBRANCH_BIT],
|
||||||
|
buf_contents[0 * W_SLOT + SLOT_PREDBRANCH_BIT]
|
||||||
|
};
|
||||||
|
|
||||||
|
assign cir_break_any = buf_contents[0 * W_SLOT + SLOT_BREAK_ANY_BIT] && |cir_vld;
|
||||||
|
|
||||||
|
assign cir_break_d_mode = buf_contents[0 * W_SLOT + SLOT_BREAK_D_MODE_BIT] && |cir_vld;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Register number predecode
|
||||||
|
|
||||||
|
wire [31:0] next_instr = {
|
||||||
|
buf_shifted_plus_fetch[1 * W_SLOT +: W_BUNDLE],
|
||||||
|
buf_shifted_plus_fetch[0 * W_SLOT +: W_BUNDLE]
|
||||||
|
};
|
||||||
|
|
||||||
|
wire next_instr_is_32bit = next_instr[1:0] == 2'b11 || ~|EXTENSION_C;
|
||||||
|
|
||||||
|
wire [3:0] decomp_uop_step;
|
||||||
|
wire [3:0] uop_ctr = decomp_uop_step & {4{|EXTENSION_ZCMP}};
|
||||||
|
|
||||||
|
wire [4:0] zcmp_pushpop_rs2 =
|
||||||
|
uop_ctr == 4'h0 ? 5'd01 : // ra
|
||||||
|
uop_ctr == 4'h1 ? 5'd08 : // s0
|
||||||
|
uop_ctr == 4'h2 ? 5'd09 : // s1
|
||||||
|
5'd15 + {1'b0, uop_ctr} ; // s2-s11
|
||||||
|
|
||||||
|
wire [4:0] zcmp_pushpop_rs1 =
|
||||||
|
uop_ctr < 4'hd ? 5'd02 : // sp (addr base reg)
|
||||||
|
uop_ctr == 4'hd ? 5'd00 : // zero (clear a0)
|
||||||
|
uop_ctr == 4'he ? 5'd01 : // ra (ret)
|
||||||
|
5'd02 ; // sp (stack adj)
|
||||||
|
|
||||||
|
wire [4:0] zcmp_sa01_r1s = {|next_instr[9:8], ~|next_instr[9:8], next_instr[9:7]};
|
||||||
|
wire [4:0] zcmp_sa01_r2s = {|next_instr[4:3], ~|next_instr[4:3], next_instr[4:2]};
|
||||||
|
|
||||||
|
wire [4:0] zcmp_mvsa01_rs1 = {4'h5, uop_ctr[0]};
|
||||||
|
wire [4:0] zcmp_mva01s_rs1 = uop_ctr[0] ? zcmp_sa01_r2s : zcmp_sa01_r1s;
|
||||||
|
|
||||||
|
// "coarse" because the mapping of pair (x0, x1) -> (x0, x0) is not yet applied
|
||||||
|
wire [4:0] zilsd_rs2_coarse = { next_instr[24:21], df_lspair_phase_next ^ ~next_instr[15]};
|
||||||
|
wire [4:0] zclsd_sd_rs2_coarse = {2'b01, next_instr[4:3], df_lspair_phase_next ^ ~next_instr[7] };
|
||||||
|
wire [4:0] zclsd_sdsp_rs2_coarse = { next_instr[6:3], df_lspair_phase_next ^ 1'b1 };
|
||||||
|
|
||||||
|
always @ (*) begin
|
||||||
|
|
||||||
|
casez ({next_instr_is_32bit, |EXTENSION_ZCMP, next_instr[15:0]})
|
||||||
|
{1'b1, 1'bz, 16'bzzzzzzzzzzzzzzzz}: predecode_rs1_coarse = next_instr[19:15]; // 32-bit R, S, B formats
|
||||||
|
{1'b0, 1'bz, 16'b00zzzzzzzzzzzz00}: predecode_rs1_coarse = 5'd2; // c.addi4spn + don't care
|
||||||
|
{1'b0, 1'bz, 16'b0zzzzzzzzzzzzz01}: predecode_rs1_coarse = next_instr[11:7]; // c.addi, c.addi16sp + don't care (jal, li)
|
||||||
|
{1'b0, 1'bz, 16'bz1zzzzzzzzzzzz10}: predecode_rs1_coarse = 5'd2; // c.lwsp, c.swsp, c.ldsp, c.sdsp
|
||||||
|
{1'b0, 1'bz, 16'bz00zzzzzzzzzzz10}: predecode_rs1_coarse = next_instr[11:7]; // c.slli, c.mv, c.add
|
||||||
|
{1'b0, 1'b1, 16'b1011zzzzzzzzzz10}: predecode_rs1_coarse = zcmp_pushpop_rs1; // cm.push, cm.pop*
|
||||||
|
{1'b0, 1'b1, 16'b1010zzzzz0zzzz10}: predecode_rs1_coarse = zcmp_mvsa01_rs1; // cm.mvsa01
|
||||||
|
{1'b0, 1'b1, 16'b1010zzzzz1zzzz10}: predecode_rs1_coarse = zcmp_mva01s_rs1; // cm.mva01s
|
||||||
|
default: predecode_rs1_coarse = {2'b01, next_instr[9:7]};
|
||||||
|
endcase
|
||||||
|
|
||||||
|
casez ({next_instr_is_32bit, |EXTENSION_ZCMP, |EXTENSION_ZILSD, |EXTENSION_ZCLSD, next_instr[15:0]})
|
||||||
|
{1'b1, 1'bz, 1'b1, 1'bz, 16'bzz11zzzzz0z0zzzz}: predecode_rs2_coarse = zilsd_rs2_coarse; // ld, sd (Zilsd)
|
||||||
|
{1'b1, 1'bz, 1'b0, 1'bz, 16'bzz11zzzzz0z0zzzz}: predecode_rs2_coarse = next_instr[24:20]; // ld, sd (no Zilsd)
|
||||||
|
|
||||||
|
{1'b1, 1'bz, 1'bz, 1'bz, 16'bzz0zzzzzzzzzzzzz}: predecode_rs2_coarse = next_instr[24:20]; // (cover remaining 32-bit
|
||||||
|
{1'b1, 1'bz, 1'bz, 1'bz, 16'bzz10zzzzzzzzzzzz}: predecode_rs2_coarse = next_instr[24:20]; // patterns, without overlap)
|
||||||
|
{1'b1, 1'bz, 1'bz, 1'bz, 16'bzz11zzzzz1zzzzzz}: predecode_rs2_coarse = next_instr[24:20];
|
||||||
|
{1'b1, 1'bz, 1'bz, 1'bz, 16'bzz11zzzzz0z1zzzz}: predecode_rs2_coarse = next_instr[24:20];
|
||||||
|
|
||||||
|
{1'b0, 1'bz, 1'b1, 1'b1, 16'bzz1zzzzzzzzzzz00}: predecode_rs2_coarse = zclsd_sd_rs2_coarse;
|
||||||
|
{1'b0, 1'bz, 1'bz, 1'bz, 16'bzz0zzzzzzzzzzz10}: predecode_rs2_coarse = next_instr[6:2]; // c.add, c.swsp
|
||||||
|
{1'b0, 1'b1, 1'bz, 1'bz, 16'bz01zzzzzzzzzzz10}: predecode_rs2_coarse = zcmp_pushpop_rs2; // cm.push
|
||||||
|
{1'b0, 1'bz, 1'b1, 1'b1, 16'bz11zzzzzzzzzzz10}: predecode_rs2_coarse = zclsd_sdsp_rs2_coarse;
|
||||||
|
default: predecode_rs2_coarse = {2'b01, next_instr[4:2]};
|
||||||
|
endcase
|
||||||
|
|
||||||
|
// The "fine" predecode targets those instructions which either:
|
||||||
|
// - Have an implicit zero-register operand in their expanded form (e.g. c.beqz)
|
||||||
|
// - Do not have a register operand on that port, but rely on the port being 0
|
||||||
|
// We don't care about instructions which ignore the reg ports, e.g. ebreak
|
||||||
|
|
||||||
|
casez ({|EXTENSION_C, next_instr})
|
||||||
|
// -> addi rd, x0, imm:
|
||||||
|
{1'b1, 16'hzzzz, `RVOPC_C_LI}: predecode_rs1_fine = 5'd0;
|
||||||
|
{1'b1, 16'hzzzz, `RVOPC_C_MV}: begin
|
||||||
|
if (next_instr[6:2] == 5'd0) begin
|
||||||
|
// c.jr has rs1 as normal
|
||||||
|
predecode_rs1_fine = predecode_rs1_coarse;
|
||||||
|
end else begin
|
||||||
|
// -> add rd, x0, rs2:
|
||||||
|
predecode_rs1_fine = 5'd0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
default: predecode_rs1_fine = predecode_rs1_coarse;
|
||||||
|
endcase
|
||||||
|
|
||||||
|
casez ({|EXTENSION_C, |EXTENSION_ZILSD, |EXTENSION_ZCLSD, next_instr})
|
||||||
|
{1'b1, 1'bz, 1'bz, 16'hzzzz, `RVOPC_C_BEQZ}: predecode_rs2_fine = 5'd0; // -> beq rs1, x0, label
|
||||||
|
{1'b1, 1'bz, 1'bz, 16'hzzzz, `RVOPC_C_BNEZ}: predecode_rs2_fine = 5'd0; // -> bne rs1, x0, label
|
||||||
|
{1'b1, 1'b1, 1'bz, `RVOPC_SD }: predecode_rs2_fine = predecode_rs2_coarse & {5{|next_instr[24:21]}};
|
||||||
|
{1'b1, 1'b1, 1'b1, 16'hzzzz, `RVOPC_C_SDSP}: predecode_rs2_fine = predecode_rs2_coarse & {5{|next_instr[ 6: 3]}};
|
||||||
|
default: predecode_rs2_fine = predecode_rs2_coarse;
|
||||||
|
endcase
|
||||||
|
|
||||||
|
if (|EXTENSION_E) begin
|
||||||
|
predecode_rs1_coarse[4] = 1'b0;
|
||||||
|
predecode_rs2_coarse[4] = 1'b0;
|
||||||
|
predecode_rs1_fine[4] = 1'b0;
|
||||||
|
predecode_rs2_fine[4] = 1'b0;
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Instruction decompression
|
||||||
|
|
||||||
|
// Instructions are decompressed at the end of stage 1 (fetch data phase). On
|
||||||
|
// ASIC, where the register file is synthesised with muxes, this puts
|
||||||
|
// decompression somewhat in parallel with register file read, which uses
|
||||||
|
// approximately decoded regnums.
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (~|EXTENSION_C) begin: no_decompress
|
||||||
|
|
||||||
|
// No decompression; instructions decoded directly from prefetch buffer
|
||||||
|
always @ (*) begin
|
||||||
|
cir = {
|
||||||
|
buf_contents[1 * W_SLOT +: W_BUNDLE],
|
||||||
|
buf_contents[0 * W_SLOT +: W_BUNDLE]
|
||||||
|
} | 32'd3;
|
||||||
|
cir_is_32bit = 1'b1;
|
||||||
|
cir_invalid_16bit = ~&buf_contents[1:0];
|
||||||
|
cir_is_uop = 1'b0;
|
||||||
|
cir_uop_nonfinal = 1'b0;
|
||||||
|
cir_uop_no_pc_update = 1'b0;
|
||||||
|
cir_uop_atomic = 1'b0;
|
||||||
|
end
|
||||||
|
|
||||||
|
assign decomp_uop_step = 4'h0;
|
||||||
|
|
||||||
|
end else begin: have_decompress
|
||||||
|
|
||||||
|
wire decomp_instr_is_32bit;
|
||||||
|
wire [31:0] decomp_instr_out;
|
||||||
|
wire decomp_is_uop;
|
||||||
|
wire decomp_is_final_uop;
|
||||||
|
wire decomp_uop_no_pc_update;
|
||||||
|
wire decomp_uop_atomic;
|
||||||
|
wire decomp_invalid;
|
||||||
|
|
||||||
|
wire first_uop = ~|decomp_uop_step;
|
||||||
|
// Ensure the first uop goes straight through, as it is registered into CIR:
|
||||||
|
wire uop_stall_non_first = first_uop ? ~|buf_level_next : uop_stall;
|
||||||
|
// Ensure the uop counter stops at 0 after rolling over once:
|
||||||
|
wire uop_stall_on_repeat = cir_is_uop && !cir_uop_nonfinal && ~|cir_use;
|
||||||
|
|
||||||
|
hazard3_instr_decompress #(
|
||||||
|
`include "hazard3_config_inst.vh"
|
||||||
|
) decomp (
|
||||||
|
.clk (clk),
|
||||||
|
.rst_n (rst_n),
|
||||||
|
|
||||||
|
.instr_in (next_instr),
|
||||||
|
|
||||||
|
.instr_is_32bit (decomp_instr_is_32bit),
|
||||||
|
.instr_out (decomp_instr_out),
|
||||||
|
|
||||||
|
.instr_out_is_uop (decomp_is_uop),
|
||||||
|
.instr_out_is_final_uop (decomp_is_final_uop),
|
||||||
|
.instr_out_uop_no_pc_update (decomp_uop_no_pc_update),
|
||||||
|
.instr_out_uop_atomic (decomp_uop_atomic),
|
||||||
|
.instr_out_uop_stall (uop_stall_non_first || uop_stall_on_repeat),
|
||||||
|
.instr_out_uop_clear (uop_clear),
|
||||||
|
.df_uop_step (decomp_uop_step),
|
||||||
|
|
||||||
|
.invalid (decomp_invalid)
|
||||||
|
);
|
||||||
|
|
||||||
|
wire cir_clken =
|
||||||
|
~|cir_vld || (!cir_vld[1] && &buf_contents[1:0]) ||
|
||||||
|
|cir_use || (|EXTENSION_ZCMP && cir_is_uop && !uop_stall) ||
|
||||||
|
(|EXTENSION_ZCMP && uop_clear);
|
||||||
|
|
||||||
|
wire cir_is_uop_next = decomp_is_uop && |buf_level_next;
|
||||||
|
wire cir_uop_nonfinal_next = cir_is_uop_next && !decomp_is_final_uop;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
cir <= 32'd3;
|
||||||
|
cir_is_32bit <= 1'b0;
|
||||||
|
cir_invalid_16bit <= 1'b0;
|
||||||
|
cir_is_uop <= 1'b0;
|
||||||
|
cir_uop_nonfinal <= 1'b0;
|
||||||
|
cir_uop_no_pc_update <= 1'b0;
|
||||||
|
cir_uop_atomic <= 1'b0;
|
||||||
|
end else if (cir_clken) begin
|
||||||
|
cir <= decomp_instr_out | 32'd3;
|
||||||
|
cir_is_32bit <= decomp_instr_is_32bit;
|
||||||
|
cir_invalid_16bit <= decomp_invalid;
|
||||||
|
cir_is_uop <= |EXTENSION_ZCMP && !uop_clear && cir_is_uop_next;
|
||||||
|
cir_uop_nonfinal <= |EXTENSION_ZCMP && !uop_clear && cir_uop_nonfinal_next;
|
||||||
|
cir_uop_no_pc_update <= |EXTENSION_ZCMP && !uop_clear && decomp_uop_no_pc_update;
|
||||||
|
cir_uop_atomic <= |EXTENSION_ZCMP && !uop_clear && decomp_uop_atomic;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
assign cir_raw = {
|
||||||
|
buf_contents[1 * W_SLOT +: W_BUNDLE],
|
||||||
|
buf_contents[0 * W_SLOT +: W_BUNDLE]
|
||||||
|
};
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,478 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2021-2023 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
// Little instructions go in, big instructions come out
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module hazard3_instr_decompress #(
|
||||||
|
`include "hazard3_config.vh"
|
||||||
|
) (
|
||||||
|
input wire clk,
|
||||||
|
input wire rst_n,
|
||||||
|
|
||||||
|
input wire [31:0] instr_in,
|
||||||
|
|
||||||
|
output reg instr_is_32bit,
|
||||||
|
output reg [31:0] instr_out,
|
||||||
|
|
||||||
|
// If instruction is a non-final uop, need to suppress PC update, and null
|
||||||
|
// the PC offset in the mepc address in stage 3.
|
||||||
|
output wire instr_out_is_uop,
|
||||||
|
output wire instr_out_is_final_uop,
|
||||||
|
output wire instr_out_uop_no_pc_update,
|
||||||
|
// Indicate instr_out is a uop from the noninterruptible part of a uop
|
||||||
|
// sequence. If one uop is noninterruptible, all following uops until the
|
||||||
|
// end of the sequence are also noninterruptible.
|
||||||
|
output wire instr_out_uop_atomic,
|
||||||
|
// Current ucode sequence is stalled on downstream execution
|
||||||
|
input wire instr_out_uop_stall,
|
||||||
|
input wire instr_out_uop_clear,
|
||||||
|
|
||||||
|
// To regnum decoder in frontend
|
||||||
|
output wire [3:0] df_uop_step,
|
||||||
|
|
||||||
|
output reg invalid
|
||||||
|
);
|
||||||
|
|
||||||
|
`include "rv_opcodes.vh"
|
||||||
|
|
||||||
|
localparam W_REGADDR = 5;
|
||||||
|
localparam PASSTHROUGH = ~|EXTENSION_C;
|
||||||
|
|
||||||
|
// Long-register formats: cr, ci, css
|
||||||
|
// Short-register formats: ciw, cl, cs, cb, cj
|
||||||
|
wire [W_REGADDR-1:0] rd_l = instr_in[11:7];
|
||||||
|
wire [W_REGADDR-1:0] rs1_l = instr_in[11:7];
|
||||||
|
wire [W_REGADDR-1:0] rs2_l = instr_in[6:2];
|
||||||
|
wire [W_REGADDR-1:0] rd_s = {2'b01, instr_in[4:2]};
|
||||||
|
wire [W_REGADDR-1:0] rs1_s = {2'b01, instr_in[9:7]};
|
||||||
|
wire [W_REGADDR-1:0] rs2_s = {2'b01, instr_in[4:2]};
|
||||||
|
|
||||||
|
// Mapping of cx -> x immediate formats (we are *expanding* instructions, not
|
||||||
|
// decoding them):
|
||||||
|
|
||||||
|
wire [31:0] imm_ci = {
|
||||||
|
{7{instr_in[12]}},
|
||||||
|
instr_in[6:2],
|
||||||
|
20'h00000
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [31:0] imm_cj = {
|
||||||
|
instr_in[12],
|
||||||
|
instr_in[8],
|
||||||
|
instr_in[10:9],
|
||||||
|
instr_in[6],
|
||||||
|
instr_in[7],
|
||||||
|
instr_in[2],
|
||||||
|
instr_in[11],
|
||||||
|
instr_in[5:3],
|
||||||
|
{9{instr_in[12]}},
|
||||||
|
12'h000
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [31:0] imm_cb ={
|
||||||
|
{4{instr_in[12]}},
|
||||||
|
instr_in[6:5],
|
||||||
|
instr_in[2],
|
||||||
|
13'h0000,
|
||||||
|
instr_in[11:10],
|
||||||
|
instr_in[4:3],
|
||||||
|
instr_in[12],
|
||||||
|
7'h00
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [31:0] imm_c_lb = {
|
||||||
|
10'h0,
|
||||||
|
instr_in[5],
|
||||||
|
instr_in[6],
|
||||||
|
20'h00000
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [31:0] imm_c_lh = {
|
||||||
|
10'h000,
|
||||||
|
instr_in[5],
|
||||||
|
1'b0,
|
||||||
|
20'h00000
|
||||||
|
};
|
||||||
|
|
||||||
|
function [31:0] rfmt_rd; input [4:0] rd; begin rfmt_rd = {20'h00000, rd, 7'h00}; end endfunction
|
||||||
|
function [31:0] rfmt_rs1; input [4:0] rs1; begin rfmt_rs1 = {12'h000, rs1, 15'h0000}; end endfunction
|
||||||
|
function [31:0] rfmt_rs2; input [4:0] rs2; begin rfmt_rs2 = {7'h00, rs2, 20'h00000}; end endfunction
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Push/pop and friends
|
||||||
|
|
||||||
|
// The longest uop sequence is a maximal cm.popretz:
|
||||||
|
//
|
||||||
|
// - 13x lw (counter = 0..12)
|
||||||
|
// - 1x addi to set a0 to zero (counter = 13 ) < atomic section
|
||||||
|
// - 1x jalr to jump through ra (counter = 14 ) < atomic section
|
||||||
|
// - 1x addi to adjust sp (counter = 15 ) < atomic section
|
||||||
|
|
||||||
|
wire [3:0] uop_ctr;
|
||||||
|
reg [3:0] uop_ctr_nxt_in_seq;
|
||||||
|
reg in_uop_seq;
|
||||||
|
reg uop_no_pc_update;
|
||||||
|
|
||||||
|
wire zcmp_is_pushpop = instr_in[12];
|
||||||
|
wire uop_seq_end = |EXTENSION_ZCMP && (zcmp_is_pushpop ? uop_ctr == 4'hf : uop_ctr[0]);
|
||||||
|
wire uop_atomic = |EXTENSION_ZCMP && (zcmp_is_pushpop ? uop_ctr >= 4'he : uop_ctr[0]);
|
||||||
|
|
||||||
|
wire [3:0] uop_ctr_nxt =
|
||||||
|
instr_out_uop_clear ? 4'h0 :
|
||||||
|
instr_out_uop_stall ? uop_ctr : uop_ctr_nxt_in_seq;
|
||||||
|
|
||||||
|
assign instr_out_is_uop = in_uop_seq;
|
||||||
|
assign instr_out_is_final_uop = uop_seq_end;
|
||||||
|
assign instr_out_uop_atomic = uop_atomic;
|
||||||
|
assign instr_out_uop_no_pc_update = uop_no_pc_update;
|
||||||
|
assign df_uop_step = uop_ctr;
|
||||||
|
|
||||||
|
// The offset from current sp value to the lowest-addressed saved register, +64.
|
||||||
|
wire [3:0] zcmp_rlist = instr_in[7:4];
|
||||||
|
wire [3:0] zcmp_n_regs = zcmp_rlist == 4'hf ? 4'hd : zcmp_rlist - 4'h3;
|
||||||
|
wire zcmp_rlist_invalid = zcmp_rlist < 4'h4 || (|EXTENSION_E && zcmp_rlist > 4'h6);
|
||||||
|
|
||||||
|
wire [11:0] zcmp_stack_adj_base =
|
||||||
|
zcmp_rlist == 4'hf ? 12'h040 :
|
||||||
|
zcmp_rlist >= 4'hc ? 12'h030 :
|
||||||
|
zcmp_rlist >= 4'h8 ? 12'h020 : 12'h010;
|
||||||
|
|
||||||
|
wire [11:0] zcmp_stack_adj = zcmp_stack_adj_base + {6'h00, instr_in[3:2], 4'h0};
|
||||||
|
|
||||||
|
// Note we perform all load/stores before moving the stack pointer.
|
||||||
|
wire [11:0] zcmp_stack_lw_offset = -{6'h00, {zcmp_n_regs - uop_ctr}, 2'h0} + zcmp_stack_adj;
|
||||||
|
wire [11:0] zcmp_stack_sw_offset = -{6'h00, {zcmp_n_regs - uop_ctr}, 2'h0};
|
||||||
|
|
||||||
|
wire [4:0] zcmp_ls_reg =
|
||||||
|
uop_ctr == 4'h0 ? 5'd01 : // ra
|
||||||
|
uop_ctr == 4'h1 ? 5'd08 : // s0
|
||||||
|
uop_ctr == 4'h2 ? 5'd09 : // s1
|
||||||
|
5'd15 + {1'b0, uop_ctr}; // s2-s11 (s2 == x18)
|
||||||
|
|
||||||
|
wire [31:0] zcmp_push_sw_instr = `RVOPC_NOZ_SW | rfmt_rs1(5'd2) | rfmt_rs2(zcmp_ls_reg) | {
|
||||||
|
zcmp_stack_sw_offset[11:5], 13'h0000, zcmp_stack_sw_offset[4:0], 7'h00
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [31:0] zcmp_pop_lw_instr = `RVOPC_NOZ_LW | rfmt_rd(zcmp_ls_reg) | rfmt_rs1(5'd2)| {
|
||||||
|
zcmp_stack_lw_offset[11:0], 20'h00000
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [31:0] zcmp_push_stack_adj_instr = `RVOPC_NOZ_ADDI | rfmt_rd(5'd2) | rfmt_rs1(5'd2) | {
|
||||||
|
-zcmp_stack_adj,
|
||||||
|
20'h00000
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [31:0] zcmp_pop_stack_adj_instr = `RVOPC_NOZ_ADDI | rfmt_rd(5'd2) | rfmt_rs1(5'd2) | {
|
||||||
|
zcmp_stack_adj,
|
||||||
|
20'h00000
|
||||||
|
};
|
||||||
|
|
||||||
|
wire [4:0] zcmp_sa01_r1s = {|instr_in[9:8], ~|instr_in[9:8], instr_in[9:7]};
|
||||||
|
wire [4:0] zcmp_sa01_r2s = {|instr_in[4:3], ~|instr_in[4:3], instr_in[4:2]};
|
||||||
|
|
||||||
|
wire zcmp_sa01_invalid = |EXTENSION_E && |{instr_in[9:8], instr_in[4:3]};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (PASSTHROUGH) begin: instr_passthrough
|
||||||
|
always @ (*) begin
|
||||||
|
instr_is_32bit = 1'b1;
|
||||||
|
instr_out = instr_in;
|
||||||
|
invalid = 1'b0;
|
||||||
|
end
|
||||||
|
end else begin: instr_decompress
|
||||||
|
always @ (*) begin
|
||||||
|
if (instr_in[1:0] == 2'b11) begin
|
||||||
|
instr_is_32bit = 1'b1;
|
||||||
|
instr_out = instr_in;
|
||||||
|
invalid = 1'b0;
|
||||||
|
in_uop_seq = 1'b0;
|
||||||
|
uop_no_pc_update = 1'b0;
|
||||||
|
uop_ctr_nxt_in_seq = uop_ctr;
|
||||||
|
end else begin
|
||||||
|
instr_is_32bit = 1'b0;
|
||||||
|
instr_out = 32'd0;
|
||||||
|
invalid = 1'b0;
|
||||||
|
in_uop_seq = 1'b0;
|
||||||
|
uop_no_pc_update = 1'b0;
|
||||||
|
uop_ctr_nxt_in_seq = uop_ctr;
|
||||||
|
casez (instr_in[15:0])
|
||||||
|
`RVOPC_C_ADDI4SPN: begin
|
||||||
|
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(rd_s) | rfmt_rs1(5'd2)
|
||||||
|
| {2'h0, instr_in[10:7], instr_in[12:11], instr_in[5], instr_in[6], 2'b00, 20'h00000};
|
||||||
|
invalid = ~|instr_in[12:2]; // Always-invalid all-zeroes instruction
|
||||||
|
end
|
||||||
|
`RVOPC_C_LW: instr_out = `RVOPC_NOZ_LW | rfmt_rd(rd_s) | rfmt_rs1(rs1_s)
|
||||||
|
| {5'h00, instr_in[5], instr_in[12:10], instr_in[6], 2'b00, 20'h00000};
|
||||||
|
`RVOPC_C_SW: instr_out = `RVOPC_NOZ_SW | rfmt_rs2(rs2_s) | rfmt_rs1(rs1_s)
|
||||||
|
| {5'h00, instr_in[5], instr_in[12], 13'h0000, instr_in[11:10], instr_in[6], 2'b00, 7'h00};
|
||||||
|
`RVOPC_C_ADDI: instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(rd_l) | rfmt_rs1(rs1_l) | imm_ci;
|
||||||
|
`RVOPC_C_JAL: instr_out = `RVOPC_NOZ_JAL | rfmt_rd(5'd1) | imm_cj;
|
||||||
|
`RVOPC_C_J: instr_out = `RVOPC_NOZ_JAL | rfmt_rd(5'd0) | imm_cj;
|
||||||
|
`RVOPC_C_LI: instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(rd_l) | imm_ci;
|
||||||
|
`RVOPC_C_LUI: begin
|
||||||
|
if (rd_l == 5'd2) begin
|
||||||
|
// addi16sp
|
||||||
|
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd2) | rfmt_rs1(5'd2) |
|
||||||
|
{{3{instr_in[12]}}, instr_in[4:3], instr_in[5], instr_in[2], instr_in[6], 24'h000000};
|
||||||
|
end else begin
|
||||||
|
instr_out = `RVOPC_NOZ_LUI | rfmt_rd(rd_l) | {{15{instr_in[12]}}, instr_in[6:2], 12'h000};
|
||||||
|
end
|
||||||
|
invalid = ~|{instr_in[12], instr_in[6:2]}; // RESERVED if imm == 0
|
||||||
|
end
|
||||||
|
`RVOPC_C_SLLI: instr_out = `RVOPC_NOZ_SLLI | rfmt_rd(rs1_l) | rfmt_rs1(rs1_l) | imm_ci;
|
||||||
|
`RVOPC_C_SRAI: instr_out = `RVOPC_NOZ_SRAI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | imm_ci;
|
||||||
|
`RVOPC_C_SRLI: instr_out = `RVOPC_NOZ_SRLI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | imm_ci;
|
||||||
|
`RVOPC_C_ANDI: instr_out = `RVOPC_NOZ_ANDI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | imm_ci;
|
||||||
|
`RVOPC_C_AND: instr_out = `RVOPC_NOZ_AND | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s);
|
||||||
|
`RVOPC_C_OR: instr_out = `RVOPC_NOZ_OR | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s);
|
||||||
|
`RVOPC_C_XOR: instr_out = `RVOPC_NOZ_XOR | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s);
|
||||||
|
`RVOPC_C_SUB: instr_out = `RVOPC_NOZ_SUB | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s);
|
||||||
|
`RVOPC_C_ADD: begin
|
||||||
|
if (|rs2_l) begin
|
||||||
|
instr_out = `RVOPC_NOZ_ADD | rfmt_rd(rd_l) | rfmt_rs1(rs1_l) | rfmt_rs2(rs2_l);
|
||||||
|
end else if (|rs1_l) begin // jalr
|
||||||
|
instr_out = `RVOPC_NOZ_JALR | rfmt_rd(5'd1) | rfmt_rs1(rs1_l);
|
||||||
|
end else begin // ebreak
|
||||||
|
instr_out = `RVOPC_NOZ_EBREAK;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
`RVOPC_C_MV: begin
|
||||||
|
if (|rs2_l) begin // mv
|
||||||
|
instr_out = `RVOPC_NOZ_ADD | rfmt_rd(rd_l) | rfmt_rs2(rs2_l);
|
||||||
|
end else begin // jr
|
||||||
|
instr_out = `RVOPC_NOZ_JALR | rfmt_rs1(rs1_l);
|
||||||
|
invalid = ~|rs1_l; // RESERVED
|
||||||
|
end
|
||||||
|
end
|
||||||
|
`RVOPC_C_LWSP: begin
|
||||||
|
instr_out = `RVOPC_NOZ_LW | rfmt_rd(rd_l) | rfmt_rs1(5'd2) |
|
||||||
|
{4'h0, instr_in[3:2], instr_in[12], instr_in[6:4], 2'b00, 20'h00000};
|
||||||
|
invalid = ~|rd_l; // RESERVED
|
||||||
|
end
|
||||||
|
`RVOPC_C_SWSP: instr_out = `RVOPC_NOZ_SW | rfmt_rs2(rs2_l) | rfmt_rs1(5'd2)
|
||||||
|
| {4'h0, instr_in[8:7], instr_in[12], 13'h0000, instr_in[11:9], 2'b00, 7'h00};
|
||||||
|
`RVOPC_C_BEQZ: instr_out = `RVOPC_NOZ_BEQ | rfmt_rs1(rs1_s) | imm_cb;
|
||||||
|
`RVOPC_C_BNEZ: instr_out = `RVOPC_NOZ_BNE | rfmt_rs1(rs1_s) | imm_cb;
|
||||||
|
|
||||||
|
// Optional Zcb instructions:
|
||||||
|
`RVOPC_C_LBU: begin
|
||||||
|
instr_out = `RVOPC_NOZ_LBU | rfmt_rd(rd_s) | rfmt_rs1(rs1_s) | imm_c_lb;
|
||||||
|
invalid = ~|EXTENSION_ZCB;
|
||||||
|
end
|
||||||
|
`RVOPC_C_LHU: begin
|
||||||
|
instr_out = `RVOPC_NOZ_LHU | rfmt_rd(rd_s) | rfmt_rs1(rs1_s) | imm_c_lh;
|
||||||
|
invalid = ~|EXTENSION_ZCB;
|
||||||
|
end
|
||||||
|
`RVOPC_C_LH: begin
|
||||||
|
instr_out = `RVOPC_NOZ_LH | rfmt_rd(rd_s) | rfmt_rs1(rs1_s) | imm_c_lh;
|
||||||
|
invalid = ~|EXTENSION_ZCB;
|
||||||
|
end
|
||||||
|
`RVOPC_C_SB: begin
|
||||||
|
instr_out = `RVOPC_NOZ_SB | rfmt_rs2(rd_s) | rfmt_rs1(rs1_s) | imm_c_lb >> 13;
|
||||||
|
invalid = ~|EXTENSION_ZCB;
|
||||||
|
end
|
||||||
|
`RVOPC_C_SH: begin
|
||||||
|
instr_out = `RVOPC_NOZ_SH | rfmt_rs2(rd_s) | rfmt_rs1(rs1_s) | imm_c_lh >> 13;
|
||||||
|
invalid = ~|EXTENSION_ZCB;
|
||||||
|
end
|
||||||
|
`RVOPC_C_ZEXT_B: begin
|
||||||
|
instr_out = `RVOPC_NOZ_ANDI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | 32'h0ff00000;
|
||||||
|
invalid = ~|EXTENSION_ZCB;
|
||||||
|
end
|
||||||
|
`RVOPC_C_SEXT_B: begin
|
||||||
|
instr_out = `RVOPC_NOZ_SEXT_B | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s);
|
||||||
|
invalid = ~|EXTENSION_ZCB || ~|EXTENSION_ZBB;
|
||||||
|
end
|
||||||
|
`RVOPC_C_ZEXT_H: begin
|
||||||
|
instr_out = `RVOPC_NOZ_ZEXT_H | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s);
|
||||||
|
invalid = ~|EXTENSION_ZCB || ~|EXTENSION_ZBB;
|
||||||
|
end
|
||||||
|
`RVOPC_C_SEXT_H: begin
|
||||||
|
instr_out = `RVOPC_NOZ_SEXT_H | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s);
|
||||||
|
invalid = ~|EXTENSION_ZCB || ~|EXTENSION_ZBB;
|
||||||
|
end
|
||||||
|
`RVOPC_C_NOT: begin
|
||||||
|
instr_out = `RVOPC_NOZ_XORI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | 32'hfff00000;
|
||||||
|
invalid = ~|EXTENSION_ZCB;
|
||||||
|
end
|
||||||
|
`RVOPC_C_MUL: begin
|
||||||
|
instr_out = `RVOPC_NOZ_MUL | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s);
|
||||||
|
invalid = ~|EXTENSION_ZCB || ~|EXTENSION_M;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Optional Zclsd instructions:
|
||||||
|
`RVOPC_C_LD: begin
|
||||||
|
instr_out = `RVOPC_NOZ_LD | rfmt_rd(rd_s) | rfmt_rs1(rs1_s)
|
||||||
|
| {4'h0, instr_in[6:5], instr_in[12:10], 3'b000, 20'h00000};
|
||||||
|
invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD;
|
||||||
|
end
|
||||||
|
`RVOPC_C_SD: begin
|
||||||
|
instr_out = `RVOPC_NOZ_SD | rfmt_rs2(rs2_s) | rfmt_rs1(rs1_s)
|
||||||
|
| {4'h0, instr_in[6:5], instr_in[12], 13'h0000, instr_in[11:10], 3'b000, 7'h00};
|
||||||
|
invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD;
|
||||||
|
end
|
||||||
|
`RVOPC_C_LDSP: begin
|
||||||
|
instr_out = `RVOPC_NOZ_LD | rfmt_rd(rd_l) | rfmt_rs1(5'd2) |
|
||||||
|
{3'h0, instr_in[4:2], instr_in[12], instr_in[6:5], 3'b000, 20'h00000};
|
||||||
|
invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD || ~|rd_l; // RESERVED
|
||||||
|
end
|
||||||
|
`RVOPC_C_SDSP: begin
|
||||||
|
instr_out = `RVOPC_NOZ_SD | rfmt_rs2(rs2_l) | rfmt_rs1(5'd2)
|
||||||
|
| {3'h0, instr_in[9:7], instr_in[12], 13'h0000, instr_in[11:10], 3'b000, 7'h00};
|
||||||
|
invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Optional Zcmp instructions:
|
||||||
|
`RVOPC_CM_PUSH: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin
|
||||||
|
invalid = 1'b1;
|
||||||
|
end else if (uop_ctr == 4'hf) begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = 4'h0;
|
||||||
|
instr_out = zcmp_push_stack_adj_instr;
|
||||||
|
end else begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||||
|
instr_out = zcmp_push_sw_instr;
|
||||||
|
uop_no_pc_update = 1'b1;
|
||||||
|
if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin
|
||||||
|
uop_ctr_nxt_in_seq = 4'hf;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
`RVOPC_CM_POP: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin
|
||||||
|
invalid = 1'b1;
|
||||||
|
end else if (uop_ctr == 4'hf) begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = 4'h0;
|
||||||
|
instr_out = zcmp_pop_stack_adj_instr;
|
||||||
|
end else begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||||
|
uop_no_pc_update = 1'b1;
|
||||||
|
instr_out = zcmp_pop_lw_instr;
|
||||||
|
if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin
|
||||||
|
uop_ctr_nxt_in_seq = 4'hf;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
`RVOPC_CM_POPRET: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin
|
||||||
|
invalid = 1'b1;
|
||||||
|
end else if (uop_ctr == 4'he) begin
|
||||||
|
// Note although this is only the first instruction in the uninterruptible sequence,
|
||||||
|
// we mark this instruction as uninterruptible: there is some special case logic to
|
||||||
|
// allow this jump to execute without flushing the final stack adjust uop, which can
|
||||||
|
// cause the wrong exception PC to be sampled if this uop is interrupted.
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||||
|
instr_out = `RVOPC_NOZ_JALR | rfmt_rs1(5'd1);
|
||||||
|
end else if (uop_ctr == 4'hf) begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = 4'h0;
|
||||||
|
uop_no_pc_update = 1'b1;
|
||||||
|
instr_out = zcmp_pop_stack_adj_instr;
|
||||||
|
end else begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||||
|
instr_out = zcmp_pop_lw_instr;
|
||||||
|
uop_no_pc_update = 1'b1;
|
||||||
|
if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin
|
||||||
|
uop_ctr_nxt_in_seq = 4'he;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
`RVOPC_CM_POPRETZ: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin
|
||||||
|
invalid = 1'b1;
|
||||||
|
end else if (uop_ctr == 4'hd) begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||||
|
uop_no_pc_update = 1'b1;
|
||||||
|
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd10); // li a0, 0
|
||||||
|
end else if (uop_ctr == 4'he) begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||||
|
instr_out = `RVOPC_NOZ_JALR | rfmt_rs1(5'd1);
|
||||||
|
end else if (uop_ctr == 4'hf) begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = 4'h0;
|
||||||
|
uop_no_pc_update = 1'b1;
|
||||||
|
instr_out = zcmp_pop_stack_adj_instr;
|
||||||
|
end else begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||||
|
uop_no_pc_update = 1'b1;
|
||||||
|
instr_out = zcmp_pop_lw_instr;
|
||||||
|
if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin
|
||||||
|
uop_ctr_nxt_in_seq = 4'hd;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
`RVOPC_CM_MVSA01: if (~|EXTENSION_ZCMP || zcmp_sa01_invalid) begin
|
||||||
|
invalid = 1'b1;
|
||||||
|
end else if (uop_ctr == 4'h0) begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||||
|
uop_no_pc_update = 1'b1;
|
||||||
|
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(zcmp_sa01_r1s) | rfmt_rs1(5'd10);
|
||||||
|
end else begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = 4'h0;
|
||||||
|
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(zcmp_sa01_r2s) | rfmt_rs1(5'd11);
|
||||||
|
end
|
||||||
|
|
||||||
|
`RVOPC_CM_MVA01S: if (~|EXTENSION_ZCMP || zcmp_sa01_invalid) begin
|
||||||
|
invalid = 1'b1;
|
||||||
|
end else if (uop_ctr == 4'h0) begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = uop_ctr + 4'h1;
|
||||||
|
uop_no_pc_update = 1'b1;
|
||||||
|
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd10) | rfmt_rs1(zcmp_sa01_r1s);
|
||||||
|
end else begin
|
||||||
|
in_uop_seq = 1'b1;
|
||||||
|
uop_ctr_nxt_in_seq = 4'h0;
|
||||||
|
instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd11) | rfmt_rs1(zcmp_sa01_r2s);
|
||||||
|
end
|
||||||
|
|
||||||
|
default: invalid = 1'b1;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (EXTENSION_ZCMP) begin: have_uop_ctr
|
||||||
|
reg [3:0] uop_ctr_r;
|
||||||
|
assign uop_ctr = uop_ctr_r;
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
uop_ctr_r <= 4'h0;
|
||||||
|
end else begin
|
||||||
|
uop_ctr_r <= uop_ctr_nxt;
|
||||||
|
`ifdef HAZARD3_ASSERTIONS
|
||||||
|
assert(in_uop_seq || uop_ctr_r == 4'h0);
|
||||||
|
assert(in_uop_seq || zcmp_ls_reg == 5'h01);
|
||||||
|
assert(in_uop_seq || !uop_atomic);
|
||||||
|
assert(in_uop_seq || !uop_no_pc_update);
|
||||||
|
if (uop_seq_end) begin
|
||||||
|
assert(in_uop_seq);
|
||||||
|
assert(instr_out_uop_stall || uop_ctr_nxt == 4'h0);
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end else begin: no_uop_ctr
|
||||||
|
assign uop_ctr = 4'h0;
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
/*****************************************************************************\
|
||||||
|
| Copyright (C) 2022 Luke Wren |
|
||||||
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
// Hazard3 interrupt controller. Support for up to 512 external interrupt
|
||||||
|
// lines, with up to 16 levels of preemption.
|
||||||
|
|
||||||
|
module hazard3_irq_ctrl #(
|
||||||
|
`include "hazard3_config.vh"
|
||||||
|
) (
|
||||||
|
input wire clk,
|
||||||
|
input wire clk_always_on,
|
||||||
|
input wire rst_n,
|
||||||
|
|
||||||
|
// CSR interface
|
||||||
|
input wire [11:0] addr,
|
||||||
|
input wire [1:0] wtype,
|
||||||
|
input wire wen_m_mode,
|
||||||
|
input wire ren_m_mode,
|
||||||
|
input wire [W_DATA-1:0] wdata_raw,
|
||||||
|
input wire [W_DATA-1:0] wdata,
|
||||||
|
output reg [W_DATA-1:0] rdata,
|
||||||
|
|
||||||
|
// Trap entry/exit signals for context update
|
||||||
|
input wire trapreg_update_enter,
|
||||||
|
input wire trapreg_update_exit,
|
||||||
|
input wire trap_entry_is_eirq,
|
||||||
|
|
||||||
|
// Interface for clearing and saving mie.mtie/msie via meicontext
|
||||||
|
output wire meicontext_clearts,
|
||||||
|
input wire mie_mtie,
|
||||||
|
input wire mie_msie,
|
||||||
|
|
||||||
|
// External IRQ inputs:
|
||||||
|
input wire [NUM_IRQS-1:0] irq,
|
||||||
|
|
||||||
|
// mip.meip:
|
||||||
|
output wire external_irq_pending
|
||||||
|
);
|
||||||
|
|
||||||
|
`include "hazard3_ops.vh"
|
||||||
|
`include "hazard3_csr_addr.vh"
|
||||||
|
|
||||||
|
localparam MAX_IRQS = 512;
|
||||||
|
localparam [3:0] IRQ_PRIORITY_MASK = ~(4'hf >> IRQ_PRIORITY_BITS);
|
||||||
|
localparam W_IRQ_INDEX = $clog2(MAX_IRQS);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// IRQ input flops
|
||||||
|
|
||||||
|
// Register external IRQ signals (mainly to avoid a through-path from IRQs to
|
||||||
|
// bus request signals). Always clocked, as it's used to generate a wakeup.
|
||||||
|
// Input registers can be removed on a per-IRQ basis, but this should be done
|
||||||
|
// with care as it does create a through-path from the IRQ to the bus.
|
||||||
|
|
||||||
|
wire [NUM_IRQS-1:0] irq_r;
|
||||||
|
|
||||||
|
genvar g;
|
||||||
|
generate
|
||||||
|
for (g = 0; g < NUM_IRQS; g = g + 1) begin: irq_reg_loop
|
||||||
|
if (IRQ_INPUT_BYPASS[g]) begin: no_reg
|
||||||
|
assign irq_r[g] = irq[g];
|
||||||
|
end else begin: have_reg
|
||||||
|
reg q;
|
||||||
|
always @ (posedge clk_always_on or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
q <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
q <= irq[g];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign irq_r[g] = q;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// CSR write
|
||||||
|
|
||||||
|
// Assigned later:
|
||||||
|
wire [W_IRQ_INDEX-1:0] meinext_irq;
|
||||||
|
wire meinext_noirq;
|
||||||
|
reg [3:0] eirq_highest_priority;
|
||||||
|
|
||||||
|
// Interrupt array registers:
|
||||||
|
reg [NUM_IRQS-1:0] meiea;
|
||||||
|
reg [NUM_IRQS-1:0] meifa;
|
||||||
|
reg [4*NUM_IRQS-1:0] meipra;
|
||||||
|
|
||||||
|
// Padded vectors for CSR readout
|
||||||
|
wire [MAX_IRQS-1:0] meiea_rdata = {{MAX_IRQS-NUM_IRQS{1'b0}}, meiea};
|
||||||
|
wire [MAX_IRQS-1:0] meifa_rdata = {{MAX_IRQS-NUM_IRQS{1'b0}}, meifa};
|
||||||
|
wire [4*MAX_IRQS-1:0] meipra_rdata = {{4*(MAX_IRQS-NUM_IRQS){1'b0}}, meipra};
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin: update_irq_reg_arrays
|
||||||
|
reg signed [31:0] i;
|
||||||
|
if (!rst_n) begin
|
||||||
|
meiea <= {NUM_IRQS{1'b0}};
|
||||||
|
meifa <= {NUM_IRQS{1'b0}};
|
||||||
|
meipra <= {4*NUM_IRQS{1'b0}};
|
||||||
|
end else begin
|
||||||
|
for (i = 0; i < NUM_IRQS; i = i + 1) begin
|
||||||
|
// CSR write update. Note raw wdata is used for array indexing --
|
||||||
|
// necessary for correctness, and also avoid a loop with rdata.
|
||||||
|
if (wen_m_mode && addr == MEIEA && $signed(wdata_raw[4:0]) == i[W_IRQ_INDEX-1:4]) begin
|
||||||
|
meiea[i] <= wdata[16 + (i % 16)];
|
||||||
|
end
|
||||||
|
if (wen_m_mode && addr == MEIFA && $signed(wdata_raw[4:0]) == i[W_IRQ_INDEX-1:4]) begin
|
||||||
|
meifa[i] <= wdata[16 + (i % 16)];
|
||||||
|
end
|
||||||
|
if (wen_m_mode && addr == MEIPRA && $signed(wdata_raw[6:0]) == i[W_IRQ_INDEX-1:2]) begin
|
||||||
|
meipra[4 * i +: 4] <= wdata[16 + 4 * (i % 4) +: 4] & IRQ_PRIORITY_MASK;
|
||||||
|
end
|
||||||
|
// Clear IRQ force when the corresponding IRQ is sampled from meinext
|
||||||
|
// (so that an IRQ can be posted *once* without modifying the ISR source)
|
||||||
|
if (meinext_irq == i[W_IRQ_INDEX-1:0] && ren_m_mode && addr == MEINEXT && !meinext_noirq) begin
|
||||||
|
meifa[i[$clog2(NUM_IRQS)-1:0]] <= 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
reg [3:0] meicontext_pppreempt;
|
||||||
|
reg [3:0] meicontext_ppreempt;
|
||||||
|
reg [4:0] meicontext_preempt;
|
||||||
|
reg meicontext_noirq;
|
||||||
|
reg [W_IRQ_INDEX-1:0] meicontext_irq;
|
||||||
|
reg meicontext_mreteirq;
|
||||||
|
|
||||||
|
wire [4:0] preempt_level_next = meinext_noirq ? 5'h10 : (
|
||||||
|
(5'd1 << (4 - IRQ_PRIORITY_BITS)) + {1'b0, eirq_highest_priority}
|
||||||
|
);
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
meicontext_pppreempt <= 4'h0;
|
||||||
|
meicontext_ppreempt <= 4'h0;
|
||||||
|
meicontext_preempt <= 5'h0;
|
||||||
|
meicontext_noirq <= 1'b1;
|
||||||
|
meicontext_irq <= {W_IRQ_INDEX{1'b0}};
|
||||||
|
meicontext_mreteirq <= 1'b0;
|
||||||
|
end else if (trapreg_update_enter) begin
|
||||||
|
if (trap_entry_is_eirq) begin
|
||||||
|
// Priority save. Note the MSB of preempt needn't be saved since,
|
||||||
|
// when it is set, preemption is impossible, so we won't be here.
|
||||||
|
meicontext_pppreempt <= meicontext_ppreempt & IRQ_PRIORITY_MASK;
|
||||||
|
meicontext_ppreempt <= meicontext_preempt[3:0] & IRQ_PRIORITY_MASK;
|
||||||
|
// Setting preempt isn't strictly necessary, since an updating read
|
||||||
|
// of meinext ought to be performed before re-enabling IRQs via
|
||||||
|
// mstatus.mie, but it seems the least surprising thing to do:
|
||||||
|
meicontext_preempt <= preempt_level_next & {1'b1, IRQ_PRIORITY_MASK};
|
||||||
|
meicontext_mreteirq <= 1'b1;
|
||||||
|
end else begin
|
||||||
|
meicontext_mreteirq <= 1'b0;
|
||||||
|
end
|
||||||
|
end else if (trapreg_update_exit) begin
|
||||||
|
meicontext_mreteirq <= 1'b0;
|
||||||
|
if (meicontext_mreteirq) begin
|
||||||
|
// Priority restore
|
||||||
|
meicontext_pppreempt <= 4'h0;
|
||||||
|
meicontext_ppreempt <= meicontext_pppreempt & IRQ_PRIORITY_MASK;
|
||||||
|
meicontext_preempt <= {1'b0, meicontext_ppreempt & IRQ_PRIORITY_MASK};
|
||||||
|
end
|
||||||
|
end else if (wen_m_mode && addr == MEICONTEXT) begin
|
||||||
|
meicontext_pppreempt <= wdata[31:28] & IRQ_PRIORITY_MASK;
|
||||||
|
meicontext_ppreempt <= wdata[27:24] & IRQ_PRIORITY_MASK;
|
||||||
|
meicontext_preempt <= wdata[20:16] & {1'b1, IRQ_PRIORITY_MASK};
|
||||||
|
meicontext_noirq <= wdata[15];
|
||||||
|
meicontext_irq <= wdata[12:4];
|
||||||
|
meicontext_mreteirq <= wdata[0];
|
||||||
|
end else if (wen_m_mode && addr == MEINEXT && wdata[0]) begin
|
||||||
|
// Interrupt has been sampled, with the update request set, so update
|
||||||
|
// the context (including preemption level) appropriately.
|
||||||
|
meicontext_preempt <= preempt_level_next & {1'b1, IRQ_PRIORITY_MASK};
|
||||||
|
meicontext_noirq <= meinext_noirq;
|
||||||
|
meicontext_irq <= meinext_irq;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign meicontext_clearts = wen_m_mode && wtype != CSR_WTYPE_C && addr == MEICONTEXT && wdata_raw[1];
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// External interrupt logic
|
||||||
|
|
||||||
|
// Trap request is asserted when there is an interrupt at or above our current
|
||||||
|
// preemption level. meinext displays interrupts at or above our *previous*
|
||||||
|
// preemption level: this masking helps avoid re-taking IRQs in frames that you
|
||||||
|
// have preempted.
|
||||||
|
|
||||||
|
wire [NUM_IRQS-1:0] meipa = irq_r | meifa;
|
||||||
|
wire [MAX_IRQS-1:0] meipa_rdata = {{MAX_IRQS-NUM_IRQS{1'b0}}, meipa};
|
||||||
|
|
||||||
|
reg [NUM_IRQS-1:0] eirq_active_above_preempt;
|
||||||
|
reg [NUM_IRQS-1:0] eirq_active_above_ppreempt;
|
||||||
|
|
||||||
|
always @ (*) begin: eirq_compare
|
||||||
|
integer i;
|
||||||
|
for (i = 0; i < NUM_IRQS; i = i + 1) begin
|
||||||
|
eirq_active_above_preempt[i] = meipa[i] && meiea[i] && {1'b0, meipra[i * 4 +: 4]} >= meicontext_preempt;
|
||||||
|
eirq_active_above_ppreempt[i] = meipa[i] && meiea[i] && meipra[i * 4 +: 4] >= meicontext_ppreempt;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign external_irq_pending = |eirq_active_above_preempt;
|
||||||
|
assign meinext_noirq = ~|eirq_active_above_ppreempt;
|
||||||
|
|
||||||
|
// Two things remaining to calculate:
|
||||||
|
//
|
||||||
|
// - What is the IRQ number of the highest-priority pending IRQ that is above
|
||||||
|
// meicontext.ppreempt
|
||||||
|
// - What is the priority of that IRQ
|
||||||
|
//
|
||||||
|
// In the second case we can relax the calculation to ignore ppreempt, since it
|
||||||
|
// only needs to be valid if such an IRQ exists. Currently we choose to reuse
|
||||||
|
// the same priority selector (possibly longer critpath while saving area), but
|
||||||
|
// we could use a second priority selector that ignores ppreempt masking.
|
||||||
|
|
||||||
|
wire [NUM_IRQS-1:0] highest_eirq_onehot;
|
||||||
|
wire [W_IRQ_INDEX-1:0] meinext_irq_unmasked;
|
||||||
|
|
||||||
|
hazard3_onehot_priority_dynamic #(
|
||||||
|
.W_REQ (NUM_IRQS),
|
||||||
|
.N_PRIORITIES (16),
|
||||||
|
.PRIORITY_HIGHEST_WINS (1),
|
||||||
|
.TIEBREAK_HIGHEST_WINS (0)
|
||||||
|
) eirq_priority_u (
|
||||||
|
.pri (meipra[4*NUM_IRQS-1:0] & {NUM_IRQS{IRQ_PRIORITY_MASK}}),
|
||||||
|
.req (eirq_active_above_ppreempt),
|
||||||
|
.gnt (highest_eirq_onehot)
|
||||||
|
);
|
||||||
|
|
||||||
|
always @ (*) begin: get_highest_eirq_priority
|
||||||
|
integer i;
|
||||||
|
eirq_highest_priority = 4'h0;
|
||||||
|
for (i = 0; i < NUM_IRQS; i = i + 1) begin
|
||||||
|
eirq_highest_priority = eirq_highest_priority | (
|
||||||
|
meipra[4 * i +: 4] & {4{highest_eirq_onehot[i]}}
|
||||||
|
);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
wire [$clog2(NUM_IRQS)-1:0] meinext_irq_unmasked_nopad;
|
||||||
|
|
||||||
|
hazard3_onehot_encode #(
|
||||||
|
.W_REQ (NUM_IRQS)
|
||||||
|
) eirq_encode_u (
|
||||||
|
.req (highest_eirq_onehot),
|
||||||
|
.gnt (meinext_irq_unmasked_nopad)
|
||||||
|
);
|
||||||
|
|
||||||
|
generate
|
||||||
|
if ($clog2(NUM_IRQS) == $clog2(MAX_IRQS)) begin: encode_eirq_no_padding
|
||||||
|
assign meinext_irq_unmasked = meinext_irq_unmasked_nopad;
|
||||||
|
end else begin: encode_eirq_padded
|
||||||
|
assign meinext_irq_unmasked = {
|
||||||
|
{$clog2(MAX_IRQS) - $clog2(NUM_IRQS){1'b0}},
|
||||||
|
meinext_irq_unmasked_nopad
|
||||||
|
};
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
// It is unnecessary to mask meinext_irq based on meinext_noirq because:
|
||||||
|
// - The value of the CSR field is unimportant when noirq is set
|
||||||
|
// - There are no IRQ inputs to the priority selector when there
|
||||||
|
// are no IRQs, so result is already 0.
|
||||||
|
assign meinext_irq = meinext_irq_unmasked;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// CSR read
|
||||||
|
|
||||||
|
always @ (*) begin
|
||||||
|
rdata = {W_DATA{1'b0}};
|
||||||
|
case (addr)
|
||||||
|
|
||||||
|
MEIEA: rdata = {
|
||||||
|
meiea_rdata[wdata_raw[4:0] * 16 +: 16],
|
||||||
|
16'h0
|
||||||
|
};
|
||||||
|
|
||||||
|
MEIPA: rdata = {
|
||||||
|
meipa_rdata[wdata_raw[4:0] * 16 +: 16],
|
||||||
|
16'h0
|
||||||
|
};
|
||||||
|
|
||||||
|
MEIFA: rdata = {
|
||||||
|
meifa_rdata[wdata_raw[4:0] * 16 +: 16],
|
||||||
|
16'h0
|
||||||
|
};
|
||||||
|
|
||||||
|
MEIPRA: rdata = {
|
||||||
|
meipra_rdata[wdata_raw[6:0] * 16 +: 16],
|
||||||
|
16'h0
|
||||||
|
};
|
||||||
|
|
||||||
|
MEINEXT: rdata = {
|
||||||
|
meinext_noirq,
|
||||||
|
20'h0,
|
||||||
|
meinext_irq,
|
||||||
|
2'h0
|
||||||
|
};
|
||||||
|
|
||||||
|
MEICONTEXT: rdata = {
|
||||||
|
meicontext_pppreempt,
|
||||||
|
meicontext_ppreempt,
|
||||||
|
3'h0,
|
||||||
|
meicontext_preempt,
|
||||||
|
meicontext_noirq,
|
||||||
|
2'h0,
|
||||||
|
meicontext_irq,
|
||||||
|
mie_mtie && meicontext_clearts,
|
||||||
|
mie_msie && meicontext_clearts,
|
||||||
|
1'b0,
|
||||||
|
meicontext_mreteirq
|
||||||
|
};
|
||||||
|
|
||||||
|
default: rdata = {W_DATA{1'b0}};
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
|
`default_nettype wire
|
||||||
|
`endif
|
||||||