feat: publish FreeRTOS C FC03 card
@@ -0,0 +1,63 @@
|
|||||||
|
name: Render PDF
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- deploy
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
render-pdf:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install TeX
|
||||||
|
run: |
|
||||||
|
if ! command -v latexmk >/dev/null 2>&1; then
|
||||||
|
if ! command -v apt-get >/dev/null 2>&1; then
|
||||||
|
echo "latexmk is missing and apt-get is unavailable" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if command -v sudo >/dev/null 2>&1; then
|
||||||
|
SUDO=sudo
|
||||||
|
else
|
||||||
|
SUDO=
|
||||||
|
fi
|
||||||
|
$SUDO apt-get update
|
||||||
|
$SUDO apt-get install -y --no-install-recommends \
|
||||||
|
latexmk \
|
||||||
|
texlive-fonts-recommended \
|
||||||
|
texlive-lang-polish \
|
||||||
|
texlive-latex-base \
|
||||||
|
texlive-latex-extra \
|
||||||
|
texlive-latex-recommended
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Render PDF with commit metadata
|
||||||
|
run: ./scripts/render_pdf.sh
|
||||||
|
|
||||||
|
- name: Commit rendered PDF
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
repo_name="${GITHUB_REPOSITORY#*/}"
|
||||||
|
pdf_file="$(find doc/pdf -maxdepth 1 -type f -name '*.pdf' -print -quit)"
|
||||||
|
if [ -z "$pdf_file" ]; then
|
||||||
|
echo "rendered PDF is missing" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
remote_sha="$(git ls-remote origin refs/heads/deploy | cut -f1)"
|
||||||
|
if [ "$remote_sha" != "$GITHUB_SHA" ]; then
|
||||||
|
echo "deploy advanced from $GITHUB_SHA to $remote_sha; skip publishing stale PDF"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
git config user.name "gitea-actions"
|
||||||
|
git config user.email "gitea-actions@noreply.local"
|
||||||
|
printf '%s\n' "$GITHUB_SHA" > doc/pdf/source-commit.txt
|
||||||
|
git add -u doc/pdf
|
||||||
|
git add -f "$pdf_file"
|
||||||
|
git add doc/pdf/source-commit.txt
|
||||||
|
git diff --cached --quiet && exit 0
|
||||||
|
git commit -m "Render ${repo_name} PDF for ${GITHUB_SHA::12} [skip actions]"
|
||||||
|
git push origin HEAD:deploy
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
build/
|
||||||
|
host-build/
|
||||||
|
*.pdf
|
||||||
|
doc/build-meta.tex
|
||||||
|
*.aux
|
||||||
|
*.fdb_latexmk
|
||||||
|
*.fls
|
||||||
|
*.log
|
||||||
|
*.out
|
||||||
|
*.synctex.gz
|
||||||
|
.gdb_history
|
||||||
|
.rv/
|
||||||
|
.stem/
|
||||||
|
.nvim/
|
||||||
|
.vim/
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
RISCV_PREFIX ?= riscv64-unknown-elf-
|
||||||
|
CC := $(RISCV_PREFIX)gcc
|
||||||
|
OBJCOPY := $(RISCV_PREFIX)objcopy
|
||||||
|
OBJDUMP := $(RISCV_PREFIX)objdump
|
||||||
|
SIZE := $(RISCV_PREFIX)size
|
||||||
|
NM := $(RISCV_PREFIX)nm
|
||||||
|
READELF := $(RISCV_PREFIX)readelf
|
||||||
|
HOSTCC ?= cc
|
||||||
|
|
||||||
|
LOCAL_ENV_ROOT := $(abspath ../../../rv32i-hazard3-env)
|
||||||
|
RV_ENV_ROOT ?= $(if $(wildcard /opt/rv-env/vendor/Hazard3),/opt/rv-env,$(LOCAL_ENV_ROOT))
|
||||||
|
H3_COMMON := $(RV_ENV_ROOT)/vendor/Hazard3/test/sim/common
|
||||||
|
H3_INIT := $(H3_COMMON)/init.S
|
||||||
|
LDSCRIPT := $(H3_COMMON)/link_hazard3.ld
|
||||||
|
TB ?= $(RV_ENV_ROOT)/vendor/Hazard3/test/sim/tb_verilator/tb
|
||||||
|
|
||||||
|
FREERTOS := vendor/FreeRTOS-Kernel
|
||||||
|
FREERTOS_PORT := $(FREERTOS)/portable/GCC/RISC-V
|
||||||
|
BUILD := build
|
||||||
|
|
||||||
|
ARCH ?= rv32i_zicsr_zifencei
|
||||||
|
ABI ?= ilp32
|
||||||
|
CPPFLAGS := -Iinclude -Isrc/common -I$(FREERTOS)/include -I$(FREERTOS_PORT)
|
||||||
|
TARGET_FLAGS := -march=$(ARCH) -mabi=$(ABI) -Og -g3 -ffreestanding -fno-builtin -fno-stack-protector -ffunction-sections -fdata-sections -Wall -Wextra -Werror
|
||||||
|
CFLAGS := -std=c11 $(TARGET_FLAGS) $(CPPFLAGS)
|
||||||
|
ASFLAGS := -march=$(ARCH) -mabi=$(ABI) -g3 $(CPPFLAGS)
|
||||||
|
LDFLAGS := -march=$(ARCH) -mabi=$(ABI) -nostdlib -nostartfiles -T$(LDSCRIPT) -Wl,--no-relax -Wl,--gc-sections
|
||||||
|
LIBS := -lgcc
|
||||||
|
|
||||||
|
TASKS := task01_scheduler
|
||||||
|
COMMON_OBJS := $(BUILD)/common/init.o $(BUILD)/common/crt0.o $(BUILD)/common/lab_io.o
|
||||||
|
KERNEL_OBJS := \
|
||||||
|
$(COMMON_OBJS) \
|
||||||
|
$(BUILD)/common/lab_freertos.o \
|
||||||
|
$(BUILD)/common/hazard3_freertos_traps.o \
|
||||||
|
$(BUILD)/common/memops.o \
|
||||||
|
$(BUILD)/kernel/tasks.o \
|
||||||
|
$(BUILD)/kernel/list.o \
|
||||||
|
$(BUILD)/kernel/heap_4.o \
|
||||||
|
$(BUILD)/kernel/port.o \
|
||||||
|
$(BUILD)/kernel/portASM.o
|
||||||
|
|
||||||
|
.PHONY: all tasks check check-abi host-test sim check-determinism pdf clean + check-support task1 t1
|
||||||
|
|
||||||
|
all: tasks
|
||||||
|
tasks: $(addprefix $(BUILD)/,$(addsuffix /prog.bin,$(TASKS)))
|
||||||
|
task1 t1: $(BUILD)/task01_scheduler/prog.bin
|
||||||
|
|
||||||
|
check-support:
|
||||||
|
@test -f "$(H3_INIT)" || { echo "missing $(H3_INIT); set RV_ENV_ROOT" >&2; exit 1; }
|
||||||
|
@test -f "$(LDSCRIPT)" || { echo "missing $(LDSCRIPT)" >&2; exit 1; }
|
||||||
|
|
||||||
|
$(BUILD)/common/init.o: $(H3_INIT) | check-support
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(ASFLAGS) -c $< -o $@
|
||||||
|
$(BUILD)/common/crt0.o: src/common/crt0.S
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(ASFLAGS) -c $< -o $@
|
||||||
|
$(BUILD)/common/hazard3_freertos_traps.o: src/common/hazard3_freertos_traps.S
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(ASFLAGS) -c $< -o $@
|
||||||
|
$(BUILD)/common/lab_io.o: src/common/lab_io.c src/common/lab_io.h
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
$(BUILD)/common/lab_freertos.o: src/common/lab_freertos.c src/common/lab_freertos.h
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
$(BUILD)/common/memops.o: src/common/memops.c
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(BUILD)/kernel/tasks.o: $(FREERTOS)/tasks.c
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
$(BUILD)/kernel/list.o: $(FREERTOS)/list.c
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
$(BUILD)/kernel/heap_4.o: $(FREERTOS)/portable/MemMang/heap_4.c
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
$(BUILD)/kernel/port.o: $(FREERTOS_PORT)/port.c
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
$(BUILD)/kernel/portASM.o: $(FREERTOS_PORT)/portASM.S
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(ASFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(BUILD)/task01_scheduler/app.o: src/tasks/task01_scheduler.c include/task01_scheduler.h include/task01_scheduler_model.h
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
$(BUILD)/task01_scheduler/task01_scheduler.s: src/tasks/task01_scheduler.c include/task01_scheduler.h include/task01_scheduler_model.h
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CFLAGS) -S $< -o $@
|
||||||
|
$(BUILD)/task01_scheduler/prog.elf: $(KERNEL_OBJS) $(BUILD)/task01_scheduler/app.o
|
||||||
|
$(CC) $(LDFLAGS) -Wl,-Map,$(BUILD)/task01_scheduler/prog.map -o $@ $^ $(LIBS)
|
||||||
|
$(SIZE) -A -x $@
|
||||||
|
$(BUILD)/task01_scheduler/prog.bin: $(BUILD)/task01_scheduler/prog.elf $(BUILD)/task01_scheduler/task01_scheduler.s
|
||||||
|
$(OBJCOPY) -O binary $< $@
|
||||||
|
$(OBJDUMP) -d -M no-aliases,numeric $< > $(BUILD)/task01_scheduler/prog.lst
|
||||||
|
|
||||||
|
host-test:
|
||||||
|
mkdir -p $(BUILD)/host
|
||||||
|
$(HOSTCC) -std=c11 -Wall -Wextra -Werror -Iinclude tests/scheduler_model_host.c -o $(BUILD)/host/scheduler_model_host
|
||||||
|
$(BUILD)/host/scheduler_model_host
|
||||||
|
|
||||||
|
check-abi: $(BUILD)/task01_scheduler/prog.elf
|
||||||
|
RISCV_NM=$(NM) RISCV_READELF=$(READELF) ./scripts/check_abi.sh
|
||||||
|
|
||||||
|
sim: tasks
|
||||||
|
@test -x "$(TB)" || { echo "missing simulator $(TB)" >&2; exit 1; }
|
||||||
|
./scripts/run_sim.sh "$(TB)" "$(BUILD)"
|
||||||
|
|
||||||
|
check-determinism: tasks
|
||||||
|
@test -x "$(TB)" || { echo "missing simulator $(TB)" >&2; exit 1; }
|
||||||
|
./scripts/check_determinism.sh "$(TB)" "$(BUILD)"
|
||||||
|
|
||||||
|
check: tasks host-test check-abi sim check-determinism
|
||||||
|
|
||||||
|
pdf: tasks
|
||||||
|
./scripts/render_pdf.sh
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILD)
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# FC03 — Tick, Priorities, Preemption and Time Slicing
|
||||||
|
|
||||||
|
FC03 is a freestanding C11 FreeRTOS card for RV32I/Hazard3. It turns the
|
||||||
|
scheduler rules from Chapter 4 of the official FreeRTOS Kernel Book into one
|
||||||
|
bounded, replayable experiment.
|
||||||
|
|
||||||
|
The program proves two independent facts:
|
||||||
|
|
||||||
|
```text
|
||||||
|
equal priorities: peer A -> peer B -> peer A
|
||||||
|
priority change: before_raise -> HIGH -> after_raise
|
||||||
|
```
|
||||||
|
|
||||||
|
The first trace is produced by two CPU-bound tasks at priority 2 with
|
||||||
|
preemption and time slicing enabled. The second trace is produced when peer A
|
||||||
|
raises a Ready task from priority 0 to priority 3. That task must run before
|
||||||
|
`vTaskPrioritySet()` returns to peer A.
|
||||||
|
|
||||||
|
## Book mapping
|
||||||
|
|
||||||
|
- Chapter 4.5 — task priorities;
|
||||||
|
- Chapter 4.6 — time measurement and the tick;
|
||||||
|
- Chapter 4.9 — changing task priority;
|
||||||
|
- Chapter 4.12 — time slicing.
|
||||||
|
|
||||||
|
The order of the FreeRTOS C series is curated for the laboratory. It is not a
|
||||||
|
claim that every optional chapter or API variant appears in the first fifteen
|
||||||
|
cards.
|
||||||
|
|
||||||
|
## Build and checks
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make task1
|
||||||
|
make host-test
|
||||||
|
make check-abi
|
||||||
|
./scripts/run_sim.sh
|
||||||
|
./scripts/check_determinism.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
`check-abi` rejects C++ runtime dependencies. The deterministic replay uses
|
||||||
|
application-owned records and public FreeRTOS APIs. Kernel internals may be
|
||||||
|
inspected in GDB, but they are never required by the application assertions.
|
||||||
|
|
||||||
|
## Evidence contract
|
||||||
|
|
||||||
|
- `E01`–`E05`: configuration, handles, priorities and scheduler start;
|
||||||
|
- `E06`: first bounded tick-hook observation;
|
||||||
|
- `E07`: committed A-B-A task-change trace;
|
||||||
|
- `E08`–`E10`: immediate-preemption marker projection;
|
||||||
|
- `E11`: peer self-deletion complete;
|
||||||
|
- `E12`: low-priority verifier publishes PASS.
|
||||||
|
|
||||||
|
The tick hook writes only a bounded diagnostic record and calls a dedicated
|
||||||
|
ISR-safe checkpoint sink. It does not print, allocate, block or use task-only
|
||||||
|
APIs.
|
||||||
|
|
||||||
|
## Generated material
|
||||||
|
|
||||||
|
`json/card_source.json` is the single card source. Rendering produces HTML and
|
||||||
|
TeX only; PDF generation and commits are separate, explicit operations.
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 38 KiB |
@@ -0,0 +1,19 @@
|
|||||||
|
@startuml
|
||||||
|
!include card-uml-style.iuml
|
||||||
|
scale 620 width
|
||||||
|
top to bottom direction
|
||||||
|
|
||||||
|
class "01 Hazard3 timer\nmtime · mtimecmp" as Timer <<hardware>>
|
||||||
|
class "02 RV32I FreeRTOS port\nmachine-timer trap · tick hook" as Port <<boundary>>
|
||||||
|
class "03 FreeRTOS scheduler\nReady lists · priorities · time slice" as Scheduler <<kernel>>
|
||||||
|
class "04 C application tasks\npeerA · peerB · high probe · verifier" as Application <<application>>
|
||||||
|
|
||||||
|
Timer --> Port : interrupt when mtime >= mtimecmp
|
||||||
|
Port --> Scheduler : xTaskIncrementTick()
|
||||||
|
Scheduler --> Application : select highest-priority Ready task
|
||||||
|
|
||||||
|
note right of Port
|
||||||
|
Tick hook records bounded evidence only.
|
||||||
|
Application uses public task APIs.
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
<?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="502.6301px" preserveAspectRatio="none" style="width:614px;height:502px;" version="1.1" viewBox="0 0 614 502" width="614.6243px" zoomAndPan="magnify"><defs/><g><!--MD5=[1b844c90688aafb95cd8c95626c3b83a]
|
||||||
|
class Timer--><rect fill="#F5F8FA" height="73.2232" id="Timer" style="stroke: #607985; stroke-width: 1.3439306358381504;" width="170.2312" x="77.5" y="7.2483"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="9.8555" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="62.7168" x="131.2572" y="26.2518">«hardware»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="13.4393" lengthAdjust="spacingAndGlyphs" textLength="137.0809" x="94.0751" y="51.8023">01 Hazard3 timer</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="13.4393" lengthAdjust="spacingAndGlyphs" textLength="129.0173" x="98.1069" y="67.4465">mtime · mtimecmp</text><!--MD5=[a0bfe97ac0a38e6e74cc1c7c7b744b71]
|
||||||
|
class Port--><rect fill="#F5F8FA" height="73.2232" id="Port" style="stroke: #607985; stroke-width: 1.3439306358381504;" width="258.0347" x="33.5983" y="144.9832"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="9.8555" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="62.7168" x="131.2572" y="163.9868">«boundary»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="13.4393" lengthAdjust="spacingAndGlyphs" textLength="185.4624" x="69.8844" y="189.5372">02 RV32I FreeRTOS port</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="13.4393" lengthAdjust="spacingAndGlyphs" textLength="241.9075" x="41.6618" y="205.1814">machine-timer trap · tick hook</text><!--MD5=[8523c9eb2e1b5151dfc9c2a797373c5f]
|
||||||
|
class Scheduler--><rect fill="#F5F8FA" height="73.2232" id="Scheduler" style="stroke: #607985; stroke-width: 1.3439306358381504;" width="314.4798" x="5.3757" y="282.7182"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="9.8555" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="50.1734" x="137.5289" y="301.7218">«kernel»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="13.4393" lengthAdjust="spacingAndGlyphs" textLength="177.3988" x="73.9162" y="327.2722">03 FreeRTOS scheduler</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="13.4393" lengthAdjust="spacingAndGlyphs" textLength="298.3526" x="13.4393" y="342.9164">Ready lists · priorities · time slice</text><!--MD5=[0b4e4b4980470ea387cc45de9bd14b5b]
|
||||||
|
class Application--><rect fill="#F5F8FA" height="73.2232" id="Application" style="stroke: #607985; stroke-width: 1.3439306358381504;" width="314.4798" x="5.3757" y="420.4442"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="9.8555" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="81.5318" x="121.8497" y="439.4478">«application»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="13.4393" lengthAdjust="spacingAndGlyphs" textLength="185.4624" x="69.8844" y="464.9982">04 C application tasks</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="13.4393" lengthAdjust="spacingAndGlyphs" textLength="298.3526" x="13.4393" y="480.6424">peerA · peerB · high probe · verifier</text><path d="M323.4393,159.2199 L323.4393,178.0081 L291.9734,181.5919 L323.4393,185.1757 L323.4393,203.9616 A0,0 0 0 0 323.4393,203.9616 L603.8728,203.9616 A0,0 0 0 0 603.8728,203.9616 L603.8728,168.1795 L594.9133,159.2199 L323.4393,159.2199 A0,0 0 0 0 323.4393,159.2199 " fill="#F7F5ED" style="stroke: #8A887C; stroke-width: 0.8959537572254336;"/><path d="M594.9133,159.2199 L594.9133,168.1795 L603.8728,168.1795 L594.9133,159.2199 " fill="#F7F5ED" style="stroke: #8A887C; stroke-width: 0.8959537572254336;"/><text fill="#3E4B50" font-family="DejaVu Sans Mono" font-size="10.7514" lengthAdjust="spacingAndGlyphs" textLength="250.8671" x="334.1908" y="179.0552">Tick hook records bounded evidence only.</text><text fill="#3E4B50" font-family="DejaVu Sans Mono" font-size="10.7514" lengthAdjust="spacingAndGlyphs" textLength="213.237" x="334.1908" y="191.5705">Application uses public task APIs.</text><!--MD5=[03ba2351f170e1f43ca8cdfe4a465b34]
|
||||||
|
link Timer to Port--><path d="M162.6156,80.8329 C162.6156,98.7251 162.6156,120.4341 162.6156,139.0251 " fill="none" id="Timer->Port" style="stroke: #607985; stroke-width: 1.1647398843930636;"/><polygon fill="#607985" points="162.6156,143.3347,166.1994,135.2711,162.6156,138.8549,159.0318,135.2711,162.6156,143.3347" style="stroke: #607985; stroke-width: 1.1647398843930636;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="12.5434" lengthAdjust="spacingAndGlyphs" textLength="229.3642" x="168.8873" y="117.2043">interrupt when mtime >= mtimecmp</text><!--MD5=[2547f420d094b531404e4e613dbd9530]
|
||||||
|
link Port to Scheduler--><path d="M162.6156,218.5679 C162.6156,236.4601 162.6156,258.1601 162.6156,276.7601 " fill="none" id="Port->Scheduler" style="stroke: #607985; stroke-width: 1.1647398843930636;"/><polygon fill="#607985" points="162.6156,281.0697,166.1994,273.0061,162.6156,276.5899,159.0318,273.0061,162.6156,281.0697" style="stroke: #607985; stroke-width: 1.1647398843930636;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="12.5434" lengthAdjust="spacingAndGlyphs" textLength="143.3526" x="168.8873" y="254.9393">xTaskIncrementTick()</text><!--MD5=[f41f4fa5c5c04a19c1d6c66df1ba7f13]
|
||||||
|
link Scheduler to Application--><path d="M162.6156,356.2939 C162.6156,374.1861 162.6156,395.8951 162.6156,414.4951 " fill="none" id="Scheduler->Application" style="stroke: #607985; stroke-width: 1.1647398843930636;"/><polygon fill="#607985" points="162.6156,418.8046,166.1994,410.741,162.6156,414.3249,159.0318,410.741,162.6156,418.8046" style="stroke: #607985; stroke-width: 1.1647398843930636;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="12.5434" lengthAdjust="spacingAndGlyphs" textLength="243.6994" x="168.8873" y="392.6653">select highest-priority Ready task</text><!--MD5=[dcf19c44d9290853f91ec953b4b334e5]
|
||||||
|
@startuml
|
||||||
|
skinparam backgroundColor white
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam monochrome false
|
||||||
|
skinparam defaultFontName "DejaVu Sans Mono"
|
||||||
|
skinparam defaultFontSize 14
|
||||||
|
skinparam defaultFontColor #24343D
|
||||||
|
skinparam titleFontName "DejaVu Sans Mono"
|
||||||
|
skinparam titleFontSize 13
|
||||||
|
skinparam titleFontColor #536B78
|
||||||
|
skinparam titleFontStyle plain
|
||||||
|
|
||||||
|
skinparam ArrowColor #607985
|
||||||
|
skinparam ArrowFontColor #425B67
|
||||||
|
skinparam ArrowThickness 1.3
|
||||||
|
skinparam LineThickness 1.3
|
||||||
|
skinparam nodesep 36
|
||||||
|
skinparam ranksep 42
|
||||||
|
skinparam Padding 6
|
||||||
|
|
||||||
|
skinparam classBackgroundColor #F5F8FA
|
||||||
|
skinparam classBorderColor #607985
|
||||||
|
skinparam classBorderThickness 1.5
|
||||||
|
skinparam classFontColor #1F3039
|
||||||
|
skinparam classFontSize 15
|
||||||
|
skinparam classAttributeFontColor #2E424D
|
||||||
|
skinparam classAttributeFontSize 13
|
||||||
|
skinparam classStereotypeFontColor #526B77
|
||||||
|
skinparam classStereotypeFontSize 11
|
||||||
|
skinparam minClassWidth 190
|
||||||
|
skinparam ClassAttributeIconSize 0
|
||||||
|
|
||||||
|
skinparam stateBackgroundColor #F5F8FA
|
||||||
|
skinparam stateBorderColor #607985
|
||||||
|
skinparam stateBorderThickness 1.5
|
||||||
|
skinparam stateFontColor #1F3039
|
||||||
|
skinparam stateFontSize 14
|
||||||
|
skinparam stateAttributeFontSize 13
|
||||||
|
|
||||||
|
skinparam noteBackgroundColor #F7F5ED
|
||||||
|
skinparam noteBorderColor #8A887C
|
||||||
|
skinparam noteFontColor #3E4B50
|
||||||
|
skinparam noteFontSize 12
|
||||||
|
|
||||||
|
skinparam ParticipantBackgroundColor #F5F8FA
|
||||||
|
skinparam ParticipantBorderColor #607985
|
||||||
|
skinparam ParticipantBorderThickness 1.5
|
||||||
|
skinparam ParticipantFontColor #1F3039
|
||||||
|
skinparam ParticipantFontSize 13
|
||||||
|
skinparam ActorBorderColor #607985
|
||||||
|
skinparam ActorFontColor #1F3039
|
||||||
|
skinparam LifeLineBorderColor #8297A1
|
||||||
|
skinparam LifeLineBackgroundColor #F9FBFC
|
||||||
|
skinparam SequenceArrowColor #607985
|
||||||
|
skinparam SequenceMessageAlign center
|
||||||
|
skinparam ParticipantPadding 18
|
||||||
|
skinparam BoxPadding 8
|
||||||
|
|
||||||
|
hide circle
|
||||||
|
hide empty members
|
||||||
|
scale 620 width
|
||||||
|
top to bottom direction
|
||||||
|
|
||||||
|
class "01 Hazard3 timer\nmtime · mtimecmp" as Timer <<hardware>>
|
||||||
|
class "02 RV32I FreeRTOS port\nmachine-timer trap · tick hook" as Port <<boundary>>
|
||||||
|
class "03 FreeRTOS scheduler\nReady lists · priorities · time slice" as Scheduler <<kernel>>
|
||||||
|
class "04 C application tasks\npeerA · peerB · high probe · verifier" as Application <<application>>
|
||||||
|
|
||||||
|
Timer - -> Port : interrupt when mtime >= mtimecmp
|
||||||
|
Port - -> Scheduler : xTaskIncrementTick()
|
||||||
|
Scheduler - -> Application : select highest-priority Ready task
|
||||||
|
|
||||||
|
note right of Port
|
||||||
|
Tick hook records bounded evidence only.
|
||||||
|
Application uses public task APIs.
|
||||||
|
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.8 KiB |
|
After Width: | Height: | Size: 42 KiB |
@@ -0,0 +1,53 @@
|
|||||||
|
@startuml
|
||||||
|
!include card-uml-style.iuml
|
||||||
|
scale 900 width
|
||||||
|
left to right direction
|
||||||
|
|
||||||
|
class "01 PeerContext [2]" as Peers <<C struct>> {
|
||||||
|
id : uint32_t
|
||||||
|
configured_priority : UBaseType_t = 2
|
||||||
|
handle : TaskHandle_t
|
||||||
|
iterations · checksum
|
||||||
|
first_tick · last_tick
|
||||||
|
entry_sp · stack range
|
||||||
|
}
|
||||||
|
|
||||||
|
class "02 HighProbeContext" as Probe <<C struct>> {
|
||||||
|
initial_priority = 0
|
||||||
|
target_priority = 3
|
||||||
|
handle : TaskHandle_t
|
||||||
|
run_tick
|
||||||
|
ran_before_caller_returned
|
||||||
|
}
|
||||||
|
|
||||||
|
class "03 VerifierContext" as Verify <<C struct>> {
|
||||||
|
configured_priority = 1
|
||||||
|
handle : TaskHandle_t
|
||||||
|
pass
|
||||||
|
}
|
||||||
|
|
||||||
|
class "04 switch trace" as SwitchTrace <<bounded record>> {
|
||||||
|
task[16]
|
||||||
|
tick[16]
|
||||||
|
count · changes
|
||||||
|
captured A · B · A
|
||||||
|
}
|
||||||
|
|
||||||
|
class "05 preemption markers" as OrderTrace <<bounded record>> {
|
||||||
|
1 = before_raise
|
||||||
|
2 = high_probe
|
||||||
|
3 = after_raise
|
||||||
|
}
|
||||||
|
|
||||||
|
Peers --> SwitchTrace : one entry per observed task change
|
||||||
|
Peers --> Probe : vTaskPrioritySet(handle, 3)
|
||||||
|
Probe --> OrderTrace : marker 2
|
||||||
|
Peers --> OrderTrace : markers 1 and 3
|
||||||
|
Verify --> Peers : checks both
|
||||||
|
Verify --> Probe : checks preemption proof
|
||||||
|
|
||||||
|
note bottom of SwitchTrace
|
||||||
|
A→B→A is a committed subsequence.
|
||||||
|
Repeated loop iterations are not logged.
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
<?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="324.9253px" preserveAspectRatio="none" style="width:896px;height:324px;" version="1.1" viewBox="0 0 896 324" width="896.2343px" zoomAndPan="magnify"><defs/><g><!--MD5=[24a425f7d3746dcb4595e0db6477f052]
|
||||||
|
class Peers--><rect fill="#F5F8FA" height="126.4532" id="Peers" style="stroke: #607985; stroke-width: 0.8069336521219366;" width="172.1458" x="192.5882" y="74.5069"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="5.9175" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="37.6569" x="259.8326" y="85.9172">«C struct»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="8.0693" lengthAdjust="spacingAndGlyphs" textLength="91.9904" x="232.6659" y="101.2584">01 PeerContext [2]</text><line style="stroke: #607985; stroke-width: 0.8069336521219366;" x1="193.1261" x2="364.1961" y1="109.0789" y2="109.0789"/><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="55.9474" x="199.0436" y="120.95">id : uint32_t</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="159.2349" x="199.0436" y="135.5462">configured_priority : UBaseType_t = 2</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="90.3766" x="199.0436" y="150.1425">handle : TaskHandle_t</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="90.3766" x="199.0436" y="164.7387">iterations · checksum</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="94.6802" x="199.0436" y="179.335">first_tick · last_tick</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="94.6802" x="199.0436" y="193.9312">entry_sp · stack range</text><!--MD5=[07843a28108f8f9fb73050ea95a4c52b]
|
||||||
|
class Probe--><rect fill="#F5F8FA" height="111.857" id="Probe" style="stroke: #607985; stroke-width: 0.8069336521219366;" width="124.8057" x="562.9707" y="207.6886"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="5.9175" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="37.6569" x="606.5451" y="219.0989">«C struct»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="8.0693" lengthAdjust="spacingAndGlyphs" textLength="96.832" x="576.9576" y="234.4401">02 HighProbeContext</text><line style="stroke: #607985; stroke-width: 0.8069336521219366;" x1="563.5087" x2="687.2385" y1="242.2606" y2="242.2606"/><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="86.0729" x="569.4262" y="254.1317">initial_priority = 0</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="81.7693" x="569.4262" y="268.7279">target_priority = 3</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="90.3766" x="569.4262" y="283.3242">handle : TaskHandle_t</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="34.4292" x="569.4262" y="297.9204">run_tick</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="111.8948" x="569.4262" y="312.5167">ran_before_caller_returned</text><!--MD5=[ec6b96eb83f6cad8feb931a71676c16d]
|
||||||
|
class Verify--><rect fill="#F5F8FA" height="82.6645" id="Verify" style="stroke: #607985; stroke-width: 0.8069336521219366;" width="111.8948" x="3.2277" y="196.9994"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="5.9175" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="37.6569" x="40.3467" y="208.4097">«C struct»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="8.0693" lengthAdjust="spacingAndGlyphs" textLength="91.9904" x="13.1799" y="223.7509">03 VerifierContext</text><line style="stroke: #607985; stroke-width: 0.8069336521219366;" x1="3.7657" x2="114.5846" y1="231.5715" y2="231.5715"/><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="98.9839" x="9.6832" y="243.4425">configured_priority = 1</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="90.3766" x="9.6832" y="258.0387">handle : TaskHandle_t</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="17.2146" x="9.6832" y="272.635">pass</text><!--MD5=[052633df24897554133d204f5acba5bb]
|
||||||
|
class SwitchTrace--><rect fill="#F5F8FA" height="97.2607" id="SwitchTrace" style="stroke: #607985; stroke-width: 0.8069336521219366;" width="102.2116" x="574.2678" y="44.9946"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="5.9175" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="60.251" x="595.2481" y="56.4049">«bounded record»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="8.0693" lengthAdjust="spacingAndGlyphs" textLength="77.4656" x="586.6408" y="71.7461">04 switch trace</text><line style="stroke: #607985; stroke-width: 0.8069336521219366;" x1="574.8057" x2="675.9414" y1="79.5667" y2="79.5667"/><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="34.4292" x="580.7233" y="91.4377">task[16]</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="34.4292" x="580.7233" y="106.034">tick[16]</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="64.5547" x="580.7233" y="120.6302">count · changes</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="77.4656" x="580.7233" y="135.2265">captured A · B · A</text><!--MD5=[903c07076df7fb06c97b1a15577bcab5]
|
||||||
|
class OrderTrace--><rect fill="#F5F8FA" height="82.6645" id="OrderTrace" style="stroke: #607985; stroke-width: 0.8069336521219366;" width="116.1984" x="774.1184" y="93.7119"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="5.9175" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="60.251" x="802.0921" y="105.1222">«bounded record»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="8.0693" lengthAdjust="spacingAndGlyphs" textLength="106.5152" x="778.96" y="120.4634">05 preemption markers</text><line style="stroke: #607985; stroke-width: 0.8069336521219366;" x1="774.6563" x2="889.7788" y1="128.284" y2="128.284"/><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="68.8583" x="780.5738" y="140.155">1 = before_raise</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="60.251" x="780.5738" y="154.7512">2 = high_probe</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="6.9934" lengthAdjust="spacingAndGlyphs" textLength="64.5547" x="780.5738" y="169.3475">3 = after_raise</text><path d="M541.1835,161.4244 L541.1835,188.2886 A0,0 0 0 0 541.1835,188.2886 L709.5637,188.2886 A0,0 0 0 0 709.5637,188.2886 L709.5637,166.8039 L704.1841,161.4244 L627.5254,161.4244 L625.3736,142.4668 L623.2218,161.4244 L541.1835,161.4244 A0,0 0 0 0 541.1835,161.4244 " fill="#F7F5ED" style="stroke: #8A887C; stroke-width: 0.5379557680812911;"/><path d="M704.1841,161.4244 L704.1841,166.8039 L709.5637,166.8039 L704.1841,161.4244 " fill="#F7F5ED" style="stroke: #8A887C; stroke-width: 0.5379557680812911;"/><text fill="#3E4B50" font-family="DejaVu Sans Mono" font-size="6.4555" lengthAdjust="spacingAndGlyphs" textLength="124.2678" x="547.639" y="173.334">A→B→A is a committed subsequence.</text><text fill="#3E4B50" font-family="DejaVu Sans Mono" font-size="6.4555" lengthAdjust="spacingAndGlyphs" textLength="150.6276" x="547.639" y="180.8486">Repeated loop iterations are not logged.</text><!--MD5=[96579476f064ff1ffa3f552d3ff15ad8]
|
||||||
|
link Peers to SwitchTrace--><path d="M364.8577,126.8016 C429.2833,118.5816 515.5983,107.5643 570.728,100.5278 " fill="none" id="Peers->SwitchTrace" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><polygon fill="#607985" points="573.3425,100.1943,568.2687,98.6686,570.6741,100.5326,568.81,102.938,573.3425,100.1943" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="7.5314" lengthAdjust="spacingAndGlyphs" textLength="146.324" x="379.7968" y="101.2783">one entry per observed task change</text><!--MD5=[e0cf694b28b62e4fb8bc23e78e5c0c4e]
|
||||||
|
link Peers to Probe--><path d="M364.8577,168.9342 C425.0873,190.8667 504.4411,219.7711 559.5655,239.8476 " fill="none" id="Peers->Probe" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><polygon fill="#607985" points="562.0669,240.7567,558.2558,237.0762,559.54,239.835,556.7811,241.1193,562.0669,240.7567" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="7.5314" lengthAdjust="spacingAndGlyphs" textLength="116.1984" x="394.8595" y="169.5503">vTaskPrioritySet(handle, 3)</text><!--MD5=[5dc87c6eff7493b6adeed17dd71f1a79]
|
||||||
|
link Probe to OrderTrace--><path d="M687.9271,224.8655 C714.303,208.3879 744.9558,189.2367 771.165,172.8613 " fill="none" id="Probe->OrderTrace" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><polygon fill="#607985" points="773.3168,171.5111,768.0707,172.2524,771.0359,172.9366,770.3516,175.9018,773.3168,171.5111" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="7.5314" lengthAdjust="spacingAndGlyphs" textLength="34.4292" x="724.6264" y="174.9191">marker 2</text><!--MD5=[16ba7551656b4926f6566f6281eca0fa]
|
||||||
|
link Peers to OrderTrace--><path d="M364.89,88.8273 C452.8942,45.1829 593.0801,-5.9283 709.5637,35.5266 C741.1363,46.7591 770.4603,70.0418 792.4573,91.2642 " fill="none" id="Peers->OrderTrace" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><polygon fill="#607985" points="794.2487,93.0072,792.2868,88.0855,792.3237,91.1285,789.2808,91.1654,794.2487,93.0072" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="7.5314" lengthAdjust="spacingAndGlyphs" textLength="64.5547" x="593.0962" y="14.8288">markers 1 and 3</text><!--MD5=[5305fbff9ac0c80de73336ac373d19f8]
|
||||||
|
link Verify to Peers--><path d="M115.2516,212.7507 C137.7812,202.379 164.3616,190.1351 189.4465,178.5798 " fill="none" id="Verify->Peers" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><polygon fill="#607985" points="191.7597,177.5146,186.462,177.593,189.3182,178.6433,188.2679,181.4994,191.7597,177.5146" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="7.5314" lengthAdjust="spacingAndGlyphs" textLength="47.3401" x="130.1853" y="179.6369">checks both</text><!--MD5=[58befbd82e70776cbcefaf9d91600c8d]
|
||||||
|
link Verify to Probe--><path d="M115.1871,240.8159 C220.1261,245.5123 447.208,255.6689 559.3933,260.688 " fill="none" id="Verify->Probe" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><polygon fill="#607985" points="562.0293,260.8063,557.291,258.4357,559.3423,260.6835,557.0945,262.7348,562.0293,260.8063" style="stroke: #607985; stroke-width: 0.6993424985056784;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="7.5314" lengthAdjust="spacingAndGlyphs" textLength="98.9839" x="229.1692" y="239.1349">checks preemption proof</text><!--MD5=[544ef8bcaf0d67d3a948fb6a2c2ba12b]
|
||||||
|
@startuml
|
||||||
|
skinparam backgroundColor white
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam monochrome false
|
||||||
|
skinparam defaultFontName "DejaVu Sans Mono"
|
||||||
|
skinparam defaultFontSize 14
|
||||||
|
skinparam defaultFontColor #24343D
|
||||||
|
skinparam titleFontName "DejaVu Sans Mono"
|
||||||
|
skinparam titleFontSize 13
|
||||||
|
skinparam titleFontColor #536B78
|
||||||
|
skinparam titleFontStyle plain
|
||||||
|
|
||||||
|
skinparam ArrowColor #607985
|
||||||
|
skinparam ArrowFontColor #425B67
|
||||||
|
skinparam ArrowThickness 1.3
|
||||||
|
skinparam LineThickness 1.3
|
||||||
|
skinparam nodesep 36
|
||||||
|
skinparam ranksep 42
|
||||||
|
skinparam Padding 6
|
||||||
|
|
||||||
|
skinparam classBackgroundColor #F5F8FA
|
||||||
|
skinparam classBorderColor #607985
|
||||||
|
skinparam classBorderThickness 1.5
|
||||||
|
skinparam classFontColor #1F3039
|
||||||
|
skinparam classFontSize 15
|
||||||
|
skinparam classAttributeFontColor #2E424D
|
||||||
|
skinparam classAttributeFontSize 13
|
||||||
|
skinparam classStereotypeFontColor #526B77
|
||||||
|
skinparam classStereotypeFontSize 11
|
||||||
|
skinparam minClassWidth 190
|
||||||
|
skinparam ClassAttributeIconSize 0
|
||||||
|
|
||||||
|
skinparam stateBackgroundColor #F5F8FA
|
||||||
|
skinparam stateBorderColor #607985
|
||||||
|
skinparam stateBorderThickness 1.5
|
||||||
|
skinparam stateFontColor #1F3039
|
||||||
|
skinparam stateFontSize 14
|
||||||
|
skinparam stateAttributeFontSize 13
|
||||||
|
|
||||||
|
skinparam noteBackgroundColor #F7F5ED
|
||||||
|
skinparam noteBorderColor #8A887C
|
||||||
|
skinparam noteFontColor #3E4B50
|
||||||
|
skinparam noteFontSize 12
|
||||||
|
|
||||||
|
skinparam ParticipantBackgroundColor #F5F8FA
|
||||||
|
skinparam ParticipantBorderColor #607985
|
||||||
|
skinparam ParticipantBorderThickness 1.5
|
||||||
|
skinparam ParticipantFontColor #1F3039
|
||||||
|
skinparam ParticipantFontSize 13
|
||||||
|
skinparam ActorBorderColor #607985
|
||||||
|
skinparam ActorFontColor #1F3039
|
||||||
|
skinparam LifeLineBorderColor #8297A1
|
||||||
|
skinparam LifeLineBackgroundColor #F9FBFC
|
||||||
|
skinparam SequenceArrowColor #607985
|
||||||
|
skinparam SequenceMessageAlign center
|
||||||
|
skinparam ParticipantPadding 18
|
||||||
|
skinparam BoxPadding 8
|
||||||
|
|
||||||
|
hide circle
|
||||||
|
hide empty members
|
||||||
|
scale 900 width
|
||||||
|
left to right direction
|
||||||
|
|
||||||
|
class "01 PeerContext [2]" as Peers <<C struct>> {
|
||||||
|
id : uint32_t
|
||||||
|
configured_priority : UBaseType_t = 2
|
||||||
|
handle : TaskHandle_t
|
||||||
|
iterations · checksum
|
||||||
|
first_tick · last_tick
|
||||||
|
entry_sp · stack range
|
||||||
|
}
|
||||||
|
|
||||||
|
class "02 HighProbeContext" as Probe <<C struct>> {
|
||||||
|
initial_priority = 0
|
||||||
|
target_priority = 3
|
||||||
|
handle : TaskHandle_t
|
||||||
|
run_tick
|
||||||
|
ran_before_caller_returned
|
||||||
|
}
|
||||||
|
|
||||||
|
class "03 VerifierContext" as Verify <<C struct>> {
|
||||||
|
configured_priority = 1
|
||||||
|
handle : TaskHandle_t
|
||||||
|
pass
|
||||||
|
}
|
||||||
|
|
||||||
|
class "04 switch trace" as SwitchTrace <<bounded record>> {
|
||||||
|
task[16]
|
||||||
|
tick[16]
|
||||||
|
count · changes
|
||||||
|
captured A · B · A
|
||||||
|
}
|
||||||
|
|
||||||
|
class "05 preemption markers" as OrderTrace <<bounded record>> {
|
||||||
|
1 = before_raise
|
||||||
|
2 = high_probe
|
||||||
|
3 = after_raise
|
||||||
|
}
|
||||||
|
|
||||||
|
Peers - -> SwitchTrace : one entry per observed task change
|
||||||
|
Peers - -> Probe : vTaskPrioritySet(handle, 3)
|
||||||
|
Probe - -> OrderTrace : marker 2
|
||||||
|
Peers - -> OrderTrace : markers 1 and 3
|
||||||
|
Verify - -> Peers : checks both
|
||||||
|
Verify - -> Probe : checks preemption proof
|
||||||
|
|
||||||
|
note bottom of SwitchTrace
|
||||||
|
A→B→A is a committed subsequence.
|
||||||
|
Repeated loop iterations are not logged.
|
||||||
|
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: 15 KiB |
|
After Width: | Height: | Size: 32 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
@startuml
|
||||||
|
!include card-uml-style.iuml
|
||||||
|
scale 900 width
|
||||||
|
left to right direction
|
||||||
|
|
||||||
|
class "01 peerA\npriority 2 · CPU-bound" as PeerA <<task>>
|
||||||
|
class "02 peerB\npriority 2 · CPU-bound" as PeerB <<task>>
|
||||||
|
class "03 high_probe\npriority 0 → 3" as High <<task>>
|
||||||
|
class "04 verifier\npriority 1 · runs last" as Verifier <<task>>
|
||||||
|
|
||||||
|
PeerA <--> PeerB : tick time slicing\nA → B → A
|
||||||
|
PeerA --> High : raise to priority 3
|
||||||
|
High --> PeerA : preempts before call returns
|
||||||
|
PeerA --> Verifier : stop requested after return
|
||||||
|
PeerB --> Verifier : both peers self-delete
|
||||||
|
|
||||||
|
note right of High
|
||||||
|
HIGH writes marker 2 and self-deletes.
|
||||||
|
peerA then writes marker 3.
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
<?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="233.2553px" preserveAspectRatio="none" style="width:895px;height:233px;" version="1.1" viewBox="0 0 895 233" width="895.7845px" zoomAndPan="magnify"><defs/><g><!--MD5=[d4f5dc331a1814e15a9185448555d0ce]
|
||||||
|
class PeerA--><rect fill="#F5F8FA" height="57.4191" id="PeerA" style="stroke: #607985; stroke-width: 1.053864168618267;" width="151.7564" x="4.2155" y="96.4426"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="7.7283" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="29.5082" x="65.3396" y="111.3446">«task»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="10.5386" lengthAdjust="spacingAndGlyphs" textLength="56.9087" x="51.6393" y="131.3803">01 peerA</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="10.5386" lengthAdjust="spacingAndGlyphs" textLength="139.1101" x="10.5386" y="143.648">priority 2 · CPU-bound</text><!--MD5=[627f71e47f335e85ff7a0fe7aa09dc61]
|
||||||
|
class PeerB--><rect fill="#F5F8FA" height="57.4191" id="PeerB" style="stroke: #607985; stroke-width: 1.053864168618267;" width="151.7564" x="357.6112" y="75.3653"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="7.7283" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="29.5082" x="418.7354" y="90.2673">«task»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="10.5386" lengthAdjust="spacingAndGlyphs" textLength="56.9087" x="405.0351" y="110.3031">02 peerB</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="10.5386" lengthAdjust="spacingAndGlyphs" textLength="139.1101" x="363.9344" y="122.5707">priority 2 · CPU-bound</text><!--MD5=[87dec8bc37044ee287e36547435f1f2f]
|
||||||
|
class High--><rect fill="#F5F8FA" height="57.4191" id="High" style="stroke: #607985; stroke-width: 1.053864168618267;" width="133.4895" x="366.7447" y="162.4848"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="7.7283" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="29.5082" x="418.7354" y="177.3867">«task»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="10.5386" lengthAdjust="spacingAndGlyphs" textLength="88.5246" x="389.2272" y="197.4225">03 high_probe</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="10.5386" lengthAdjust="spacingAndGlyphs" textLength="88.5246" x="389.2272" y="209.6901">priority 0 → 3</text><!--MD5=[b38b728b548c0e3506dacd7eefeceafe]
|
||||||
|
class Verifier--><rect fill="#F5F8FA" height="57.4191" id="Verifier" style="stroke: #607985; stroke-width: 1.053864168618267;" width="151.7564" x="706.4403" y="16.3489"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="7.7283" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="29.5082" x="767.5644" y="31.2509">«task»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="10.5386" lengthAdjust="spacingAndGlyphs" textLength="75.8782" x="744.3794" y="51.2867">04 verifier</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="10.5386" lengthAdjust="spacingAndGlyphs" textLength="139.1101" x="712.7635" y="63.5543">priority 1 · runs last</text><path d="M677.2834,173.6557 L677.2834,188.3888 L500.5644,191.1991 L677.2834,194.0094 L677.2834,208.7406 A0,0 0 0 0 677.2834,208.7406 L887.3536,208.7406 A0,0 0 0 0 887.3536,208.7406 L887.3536,180.6815 L880.3279,173.6557 L677.2834,173.6557 A0,0 0 0 0 677.2834,173.6557 " fill="#F7F5ED" style="stroke: #8A887C; stroke-width: 0.702576112412178;"/><path d="M880.3279,173.6557 L880.3279,180.6815 L887.3536,180.6815 L880.3279,173.6557 " fill="#F7F5ED" style="stroke: #8A887C; stroke-width: 0.702576112412178;"/><text fill="#3E4B50" font-family="DejaVu Sans Mono" font-size="8.4309" lengthAdjust="spacingAndGlyphs" textLength="186.8852" x="685.7143" y="189.2098">HIGH writes marker 2 and self-deletes.</text><text fill="#3E4B50" font-family="DejaVu Sans Mono" font-size="8.4309" lengthAdjust="spacingAndGlyphs" textLength="132.7869" x="685.7143" y="199.0239">peerA then writes marker 3.</text><!--MD5=[e17ca89136f6e96c8edfc2efe34a138f]
|
||||||
|
link PeerA to PeerB--><path d="M160.8759,120.3583 C218.3677,116.9157 295.5176,112.2998 352.9532,108.8571 " fill="none" id="PeerA-PeerB" style="stroke: #607985; stroke-width: 0.9133489461358314;"/><polygon fill="#607985" points="356.3396,108.6534,349.8594,106.2268,352.833,108.8638,350.1961,111.8374,356.3396,108.6534" style="stroke: #607985; stroke-width: 0.9133489461358314;"/><polygon fill="#607985" points="157.37,120.5691,163.8502,122.9956,160.8766,120.3587,163.5135,117.3851,157.37,120.5691" style="stroke: #607985; stroke-width: 0.9133489461358314;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="9.8361" lengthAdjust="spacingAndGlyphs" textLength="95.5504" x="206.5574" y="92.034">tick time slicing</text><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="9.8361" lengthAdjust="spacingAndGlyphs" textLength="50.5855" x="229.0398" y="103.4838">A → B → A</text><!--MD5=[7fd7a179d117b50a2a0b920d41dd806d]
|
||||||
|
link PeerA to High--><path d="M104.6136,154.1593 C121.082,171.815 144.6674,192.7799 170.726,202.4403 C232.5246,225.3443 308.2061,218.7541 362.3255,208.7073 " fill="none" id="PeerA->High" style="stroke: #607985; stroke-width: 0.9133489461358314;"/><polygon fill="#607985" points="365.466,208.1101,358.731,206.5226,362.0142,208.7623,359.7746,212.0455,365.466,208.1101" style="stroke: #607985; stroke-width: 0.9133489461358314;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="9.8361" lengthAdjust="spacingAndGlyphs" textLength="106.7916" x="200.9368" y="195.4111">raise to priority 3</text><!--MD5=[d62c0dcf4c85e672724ed87df3e66a79]
|
||||||
|
link High to PeerA--><path d="M366.5831,181.3349 C313.4543,173.1007 237.0562,160.5527 170.726,146.9368 C167.4098,146.2553 164.0234,145.5386 160.6089,144.7939 " fill="none" id="High->PeerA" style="stroke: #607985; stroke-width: 0.9133489461358314;"/><polygon fill="#607985" points="157.3138,144.0703,162.8768,148.1853,160.743,144.8323,164.0961,142.6986,157.3138,144.0703" style="stroke: #607985; stroke-width: 0.9133489461358314;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="9.8361" lengthAdjust="spacingAndGlyphs" textLength="157.377" x="175.644" y="139.9076">preempts before call returns</text><!--MD5=[da61d059c86f4bb0c5cb4c08f1d74312]
|
||||||
|
link PeerA to Verifier--><path d="M120.8009,96.1616 C135.7588,86.7681 153.3513,77.3396 170.726,71.7611 C353.459,13.0398 582.7869,23.9438 701.7471,35.4169 " fill="none" id="PeerA->Verifier" style="stroke: #607985; stroke-width: 0.9133489461358314;"/><polygon fill="#607985" points="705.2248,35.7541,699.2049,32.3423,701.7286,35.4125,698.6583,37.9362,705.2248,35.7541" style="stroke: #607985; stroke-width: 0.9133489461358314;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="9.8361" lengthAdjust="spacingAndGlyphs" textLength="151.7564" x="357.6112" y="19.3385">stop requested after return</text><!--MD5=[4142c62ab8c50b841a4bc588d0039040]
|
||||||
|
link PeerB to Verifier--><path d="M509.5293,91.363 C554.0445,83.8454 611.5012,74.1218 662.5293,65.4379 C675.2529,63.267 688.7424,60.9696 701.8946,58.7213 " fill="none" id="PeerB->Verifier" style="stroke: #607985; stroke-width: 0.9133489461358314;"/><polygon fill="#607985" points="705.2951,58.1382,698.5887,56.434,701.8325,58.7303,699.5361,61.9741,705.2951,58.1382" style="stroke: #607985; stroke-width: 0.9133489461358314;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="9.8361" lengthAdjust="spacingAndGlyphs" textLength="123.6534" x="533.9578" y="58.4087">both peers self-delete</text><!--MD5=[137f40278e8a32983395a4c1fad58d90]
|
||||||
|
@startuml
|
||||||
|
skinparam backgroundColor white
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam monochrome false
|
||||||
|
skinparam defaultFontName "DejaVu Sans Mono"
|
||||||
|
skinparam defaultFontSize 14
|
||||||
|
skinparam defaultFontColor #24343D
|
||||||
|
skinparam titleFontName "DejaVu Sans Mono"
|
||||||
|
skinparam titleFontSize 13
|
||||||
|
skinparam titleFontColor #536B78
|
||||||
|
skinparam titleFontStyle plain
|
||||||
|
|
||||||
|
skinparam ArrowColor #607985
|
||||||
|
skinparam ArrowFontColor #425B67
|
||||||
|
skinparam ArrowThickness 1.3
|
||||||
|
skinparam LineThickness 1.3
|
||||||
|
skinparam nodesep 36
|
||||||
|
skinparam ranksep 42
|
||||||
|
skinparam Padding 6
|
||||||
|
|
||||||
|
skinparam classBackgroundColor #F5F8FA
|
||||||
|
skinparam classBorderColor #607985
|
||||||
|
skinparam classBorderThickness 1.5
|
||||||
|
skinparam classFontColor #1F3039
|
||||||
|
skinparam classFontSize 15
|
||||||
|
skinparam classAttributeFontColor #2E424D
|
||||||
|
skinparam classAttributeFontSize 13
|
||||||
|
skinparam classStereotypeFontColor #526B77
|
||||||
|
skinparam classStereotypeFontSize 11
|
||||||
|
skinparam minClassWidth 190
|
||||||
|
skinparam ClassAttributeIconSize 0
|
||||||
|
|
||||||
|
skinparam stateBackgroundColor #F5F8FA
|
||||||
|
skinparam stateBorderColor #607985
|
||||||
|
skinparam stateBorderThickness 1.5
|
||||||
|
skinparam stateFontColor #1F3039
|
||||||
|
skinparam stateFontSize 14
|
||||||
|
skinparam stateAttributeFontSize 13
|
||||||
|
|
||||||
|
skinparam noteBackgroundColor #F7F5ED
|
||||||
|
skinparam noteBorderColor #8A887C
|
||||||
|
skinparam noteFontColor #3E4B50
|
||||||
|
skinparam noteFontSize 12
|
||||||
|
|
||||||
|
skinparam ParticipantBackgroundColor #F5F8FA
|
||||||
|
skinparam ParticipantBorderColor #607985
|
||||||
|
skinparam ParticipantBorderThickness 1.5
|
||||||
|
skinparam ParticipantFontColor #1F3039
|
||||||
|
skinparam ParticipantFontSize 13
|
||||||
|
skinparam ActorBorderColor #607985
|
||||||
|
skinparam ActorFontColor #1F3039
|
||||||
|
skinparam LifeLineBorderColor #8297A1
|
||||||
|
skinparam LifeLineBackgroundColor #F9FBFC
|
||||||
|
skinparam SequenceArrowColor #607985
|
||||||
|
skinparam SequenceMessageAlign center
|
||||||
|
skinparam ParticipantPadding 18
|
||||||
|
skinparam BoxPadding 8
|
||||||
|
|
||||||
|
hide circle
|
||||||
|
hide empty members
|
||||||
|
scale 900 width
|
||||||
|
left to right direction
|
||||||
|
|
||||||
|
class "01 peerA\npriority 2 · CPU-bound" as PeerA <<task>>
|
||||||
|
class "02 peerB\npriority 2 · CPU-bound" as PeerB <<task>>
|
||||||
|
class "03 high_probe\npriority 0 → 3" as High <<task>>
|
||||||
|
class "04 verifier\npriority 1 · runs last" as Verifier <<task>>
|
||||||
|
|
||||||
|
PeerA <- -> PeerB : tick time slicing\nA → B → A
|
||||||
|
PeerA - -> High : raise to priority 3
|
||||||
|
High - -> PeerA : preempts before call returns
|
||||||
|
PeerA - -> Verifier : stop requested after return
|
||||||
|
PeerB - -> Verifier : both peers self-delete
|
||||||
|
|
||||||
|
note right of High
|
||||||
|
HIGH writes marker 2 and self-deletes.
|
||||||
|
peerA then writes marker 3.
|
||||||
|
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: 10 KiB |
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,36 @@
|
|||||||
|
@startuml
|
||||||
|
!include card-uml-style.iuml
|
||||||
|
scale 900 width
|
||||||
|
hide footbox
|
||||||
|
skinparam ParticipantPadding 16
|
||||||
|
|
||||||
|
participant "main" as Main
|
||||||
|
participant "Scheduler" as Scheduler
|
||||||
|
participant "tick hook" as Tick
|
||||||
|
participant "high probe" as High
|
||||||
|
participant "peerA" as A
|
||||||
|
participant "peerB" as B
|
||||||
|
participant "verifier" as Verify
|
||||||
|
|
||||||
|
Main -> Main : <b>01</b> config + contexts committed
|
||||||
|
||26||
|
||||||
|
Main -> High : <b>02</b> create Ready at priority 0
|
||||||
|
||26||
|
||||||
|
Main -> A : <b>03</b> priorities high=0, peers=2/2, verifier=1
|
||||||
|
Main -> B
|
||||||
|
Main -> Verify
|
||||||
|
||26||
|
||||||
|
Main -> Scheduler : <b>04</b> vTaskStartScheduler()
|
||||||
|
activate Scheduler
|
||||||
|
Scheduler -> A : <b>05</b> first peer runs
|
||||||
|
deactivate Scheduler
|
||||||
|
||26||
|
||||||
|
Tick -> Tick : <b>06</b> first machine-timer tick recorded
|
||||||
|
||26||
|
||||||
|
A -> B : time slice
|
||||||
|
B -> A : <b>07</b> committed subsequence A → B → A
|
||||||
|
|
||||||
|
note over Main,Verify
|
||||||
|
Continue on part 2/2 with identical participant order.
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,36 @@
|
|||||||
|
@startuml
|
||||||
|
!include card-uml-style.iuml
|
||||||
|
scale 900 width
|
||||||
|
hide footbox
|
||||||
|
skinparam ParticipantPadding 16
|
||||||
|
|
||||||
|
participant "main" as Main
|
||||||
|
participant "Scheduler" as Scheduler
|
||||||
|
participant "tick hook" as Tick
|
||||||
|
participant "high probe" as High
|
||||||
|
participant "peerA" as A
|
||||||
|
participant "peerB" as B
|
||||||
|
participant "verifier" as Verify
|
||||||
|
|
||||||
|
A -> A : <b>08</b> marker before_raise
|
||||||
|
A -> Scheduler : vTaskPrioritySet(high, 3)
|
||||||
|
activate Scheduler
|
||||||
|
Scheduler -> High : <b>09</b> immediate higher-priority selection
|
||||||
|
activate High
|
||||||
|
High -> High : marker HIGH
|
||||||
|
High -> Scheduler : vTaskDelete(NULL)
|
||||||
|
deactivate High
|
||||||
|
Scheduler -> A : return to caller
|
||||||
|
deactivate Scheduler
|
||||||
|
||26||
|
||||||
|
A -> A : <b>10</b> marker after_raise; request stop
|
||||||
|
A -> Scheduler : self-delete after bounded cleanup
|
||||||
|
B -> Scheduler : <b>11</b> second peer finishes and self-deletes
|
||||||
|
||26||
|
||||||
|
Scheduler -> Verify : <b>12</b> lowest-priority verifier publishes PASS
|
||||||
|
|
||||||
|
note over Scheduler,Verify
|
||||||
|
Exact claim: marker projection before → HIGH → after.
|
||||||
|
Another Ready task may legally run before peerA is selected again.
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,28 @@
|
|||||||
|
@startuml
|
||||||
|
!include card-uml-style.iuml
|
||||||
|
scale 900 width
|
||||||
|
left to right direction
|
||||||
|
|
||||||
|
state "01 high Ready\npriority 0" as HighLow
|
||||||
|
state "02 high Ready\npriority 3" as HighReady
|
||||||
|
state "03 high Running" as HighRun
|
||||||
|
state "04 high Deleted" as HighDeleted
|
||||||
|
|
||||||
|
state "05 peers Ready / Running\npriority 2 · time sliced" as Peers
|
||||||
|
state "06 peers Completed / Deleted" as PeersDone
|
||||||
|
|
||||||
|
state "07 verifier Ready\npriority 1" as VerifyReady
|
||||||
|
state "08 verifier Running\nPASS" as VerifyRun
|
||||||
|
|
||||||
|
HighLow --> HighReady : peerA vTaskPrioritySet
|
||||||
|
HighReady --> HighRun : preempts priority 2
|
||||||
|
HighRun --> HighDeleted : self-delete
|
||||||
|
Peers --> Peers : tick rotates equal priority
|
||||||
|
Peers --> PeersDone : stop after peerA returns
|
||||||
|
VerifyReady --> VerifyRun : priorities 2/3 no longer Ready
|
||||||
|
|
||||||
|
note bottom of Peers
|
||||||
|
Ready does not mean Running.
|
||||||
|
Priority selects first; time slicing acts only among equals.
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,56 @@
|
|||||||
|
@startuml
|
||||||
|
!include card-uml-style.iuml
|
||||||
|
scale 900 width
|
||||||
|
left to right direction
|
||||||
|
|
||||||
|
class "01 timer CSRs" as Timer <<runtime>> {
|
||||||
|
mtime
|
||||||
|
mtimecmp
|
||||||
|
mcause = 0x80000007
|
||||||
|
mepc = interrupted task PC
|
||||||
|
}
|
||||||
|
|
||||||
|
class "02 ISR evidence" as ISR <<runtime>> {
|
||||||
|
first_tick_count
|
||||||
|
ISR sp
|
||||||
|
one-shot tick checkpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
class "03 scheduler evidence" as Kernel <<debugger only>> {
|
||||||
|
Ready lists by priority
|
||||||
|
selected TCB
|
||||||
|
tick count
|
||||||
|
}
|
||||||
|
|
||||||
|
class "04 peer runtime" as Peer <<runtime>> {
|
||||||
|
context address
|
||||||
|
handle / TCB
|
||||||
|
task stack range
|
||||||
|
PC · SP · priority 2
|
||||||
|
}
|
||||||
|
|
||||||
|
class "05 high runtime" as High <<runtime>> {
|
||||||
|
handle / TCB
|
||||||
|
priority 0 → 3
|
||||||
|
PC · SP · run_tick
|
||||||
|
}
|
||||||
|
|
||||||
|
class "06 committed evidence" as Evidence <<application>> {
|
||||||
|
switch trace A · B · A
|
||||||
|
markers before · HIGH · after
|
||||||
|
source blob · ELF hash
|
||||||
|
PASS
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer --> ISR : machine-timer trap
|
||||||
|
ISR --> Kernel : scheduling opportunity
|
||||||
|
Kernel --> Peer : equal-priority rotation
|
||||||
|
Kernel --> High : select after priority raise
|
||||||
|
Peer --> Evidence
|
||||||
|
High --> Evidence
|
||||||
|
|
||||||
|
note bottom of Kernel
|
||||||
|
Kernel internals are debugger observations only.
|
||||||
|
Program assertions use public API and application records.
|
||||||
|
end note
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<?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="166.3973px" preserveAspectRatio="none" style="width:897px;height:166px;" version="1.1" viewBox="0 0 897 166" width="897.0057px" zoomAndPan="magnify"><defs/><g><!--MD5=[1b844c90688aafb95cd8c95626c3b83a]
|
||||||
|
class Timer--><rect fill="#F5F8FA" height="77.3371" id="Timer" style="stroke: #607985; stroke-width: 0.6416349809885932;" width="99.2395" x="2.5665" y="39.683"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="4.7053" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="26.9487" x="38.712" y="48.7559">«runtime»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="6.4163" lengthAdjust="spacingAndGlyphs" textLength="53.8973" x="25.2376" y="60.9545">01 timer CSRs</text><line style="stroke: #607985; stroke-width: 0.6416349809885932;" x1="2.9943" x2="101.3783" y1="67.173" y2="67.173"/><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="17.1103" x="7.6996" y="76.6123">mtime</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="27.3764" x="7.6996" y="88.2185">mtimecmp</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="65.019" x="7.6996" y="99.8248">mcause = 0x80000007</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="88.9734" x="7.6996" y="111.431">mepc = interrupted task PC</text><!--MD5=[4fa375cf5dd147cdc7dc8493ef379d20]
|
||||||
|
class ISR--><rect fill="#F5F8FA" height="65.7308" id="ISR" style="stroke: #607985; stroke-width: 0.6416349809885932;" width="92.3954" x="187.3574" y="45.4876"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="4.7053" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="26.9487" x="220.0808" y="54.5606">«runtime»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="6.4163" lengthAdjust="spacingAndGlyphs" textLength="61.597" x="202.7567" y="66.7591">02 ISR evidence</text><line style="stroke: #607985; stroke-width: 0.6416349809885932;" x1="187.7852" x2="279.3251" y1="72.9777" y2="72.9777"/><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="54.7529" x="192.4905" y="82.417">first_tick_count</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="20.5323" x="192.4905" y="94.0232">ISR sp</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="82.1293" x="192.4905" y="105.6294">one-shot tick checkpoint</text><!--MD5=[daeea0df713f5a1e6085e9f07acfb9b8]
|
||||||
|
class Kernel--><rect fill="#F5F8FA" height="65.7308" id="Kernel" style="stroke: #607985; stroke-width: 0.6416349809885932;" width="92.3954" x="426.6873" y="45.4876"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="4.7053" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="44.9144" x="450.4278" y="54.5606">«debugger only»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="6.4163" lengthAdjust="spacingAndGlyphs" textLength="84.6958" x="430.5371" y="66.7591">03 scheduler evidence</text><line style="stroke: #607985; stroke-width: 0.6416349809885932;" x1="427.115" x2="518.6549" y1="72.9777" y2="72.9777"/><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="78.7072" x="431.8203" y="82.417">Ready lists by priority</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="41.0646" x="431.8203" y="94.0232">selected TCB</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="34.2205" x="431.8203" y="105.6294">tick count</text><!--MD5=[5502d0b26b9225c8066cf2f4a2442e7f]
|
||||||
|
class Peer--><rect fill="#F5F8FA" height="77.3371" id="Peer" style="stroke: #607985; stroke-width: 0.6416349809885932;" width="81.2738" x="683.1274" y="3.3237"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="4.7053" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="26.9487" x="710.2899" y="12.3966">«runtime»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="6.4163" lengthAdjust="spacingAndGlyphs" textLength="61.597" x="692.9658" y="24.5952">04 peer runtime</text><line style="stroke: #607985; stroke-width: 0.6416349809885932;" x1="683.5551" x2="763.9734" y1="30.8137" y2="30.8137"/><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="51.3308" x="688.2605" y="40.253">context address</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="41.0646" x="688.2605" y="51.8592">handle / TCB</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="54.7529" x="688.2605" y="63.4655">task stack range</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="68.4411" x="688.2605" y="75.0717">PC · SP · priority 2</text><!--MD5=[87dec8bc37044ee287e36547435f1f2f]
|
||||||
|
class High--><rect fill="#F5F8FA" height="65.7308" id="High" style="stroke: #607985; stroke-width: 0.6416349809885932;" width="81.2738" x="683.1274" y="95.9629"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="4.7053" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="26.9487" x="710.2899" y="105.0358">«runtime»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="6.4163" lengthAdjust="spacingAndGlyphs" textLength="61.597" x="692.9658" y="117.2344">05 high runtime</text><line style="stroke: #607985; stroke-width: 0.6416349809885932;" x1="683.5551" x2="763.9734" y1="123.453" y2="123.453"/><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="41.0646" x="688.2605" y="132.8922">handle / TCB</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="47.9087" x="688.2605" y="144.4985">priority 0 → 3</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="61.597" x="688.2605" y="156.1047">PC · SP · run_tick</text><!--MD5=[13f56afd7aace6dccdfaaac41cf97a6a]
|
||||||
|
class Evidence--><rect fill="#F5F8FA" height="77.3371" id="Evidence" style="stroke: #607985; stroke-width: 0.6416349809885932;" width="109.5057" x="782.7947" y="46.9548"/><text fill="#526B77" font-family="DejaVu Sans Mono" font-size="4.7053" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="38.9259" x="818.0846" y="56.0278">«application»</text><text fill="#1F3039" font-family="DejaVu Sans Mono" font-size="6.4163" lengthAdjust="spacingAndGlyphs" textLength="84.6958" x="795.1996" y="68.2263">06 committed evidence</text><line style="stroke: #607985; stroke-width: 0.6416349809885932;" x1="783.2224" x2="891.8726" y1="74.4449" y2="74.4449"/><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="75.2852" x="787.9278" y="83.8842">switch trace A · B · A</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="99.2395" x="787.9278" y="95.4904">markers before · HIGH · after</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="75.2852" x="787.9278" y="107.0966">source blob · ELF hash</text><text fill="#2E424D" font-family="DejaVu Sans Mono" font-size="5.5608" lengthAdjust="spacingAndGlyphs" textLength="13.6882" x="787.9278" y="118.7029">PASS</text><path d="M378.9924,126.7015 L378.9924,148.0626 A0,0 0 0 0 378.9924,148.0626 L566.7776,148.0626 A0,0 0 0 0 566.7776,148.0626 L566.7776,130.9791 L562.5,126.7015 L474.596,126.7015 L472.885,111.2937 L471.174,126.7015 L378.9924,126.7015 A0,0 0 0 0 378.9924,126.7015 " fill="#F7F5ED" style="stroke: #8A887C; stroke-width: 0.42775665399239543;"/><path d="M562.5,126.7015 L562.5,130.9791 L566.7776,130.9791 L562.5,126.7015 " fill="#F7F5ED" style="stroke: #8A887C; stroke-width: 0.42775665399239543;"/><text fill="#3E4B50" font-family="DejaVu Sans Mono" font-size="5.1331" lengthAdjust="spacingAndGlyphs" textLength="143.7262" x="384.1255" y="136.1715">Kernel internals are debugger observations only.</text><text fill="#3E4B50" font-family="DejaVu Sans Mono" font-size="5.1331" lengthAdjust="spacingAndGlyphs" textLength="173.6692" x="384.1255" y="142.1467">Program assertions use public API and application records.</text><!--MD5=[251ca16ac899d6bfafc4fee663c65b4c]
|
||||||
|
link Timer to ISR--><path d="M101.9387,78.3522 C127.6212,78.3522 158.9159,78.3522 184.4957,78.3522 " fill="none" id="Timer->ISR" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><polygon fill="#607985" points="186.6217,78.3522,182.7719,76.6412,184.4829,78.3522,182.7719,80.0632,186.6217,78.3522" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="5.9886" lengthAdjust="spacingAndGlyphs" textLength="61.597" x="113.7833" y="74.0725">machine-timer trap</text><!--MD5=[d88c108211e48cabe7c391ba8266ba05]
|
||||||
|
link ISR to Kernel--><path d="M279.9625,78.3522 C321.2837,78.3522 381.7685,78.3522 423.8726,78.3522 " fill="none" id="ISR->Kernel" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><polygon fill="#607985" points="425.9686,78.3522,422.1188,76.6412,423.8298,78.3522,422.1188,80.0632,425.9686,78.3522" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="5.9886" lengthAdjust="spacingAndGlyphs" textLength="75.2852" x="291.73" y="74.0725">scheduling opportunity</text><!--MD5=[451546734f5ef62236a1fbbfa501c405]
|
||||||
|
link Kernel to Peer--><path d="M519.1725,71.6835 C565.1307,64.9976 635.3341,54.7913 680.2785,48.2552 " fill="none" id="Kernel->Peer" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><polygon fill="#607985" points="682.2933,47.9601,678.2383,46.8173,680.1765,48.266,678.7277,50.2041,682.2933,47.9601" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="5.9886" lengthAdjust="spacingAndGlyphs" textLength="78.7072" x="585.5989" y="45.5668">equal-priority rotation</text><!--MD5=[d35ae36b39c9f0c2492e9b2be2215125]
|
||||||
|
link Kernel to High--><path d="M519.1725,87.6088 C565.1307,96.8869 635.3341,111.0627 680.2785,120.1355 " fill="none" id="Kernel->High" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><polygon fill="#607985" points="682.2975,120.5418,678.8608,118.1052,680.2008,120.12,678.1859,121.46,682.2975,120.5418" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><text fill="#425B67" font-family="DejaVu Sans Mono" font-size="5.9886" lengthAdjust="spacingAndGlyphs" textLength="92.3954" x="578.7548" y="94.7888">select after priority raise</text><!--MD5=[533f32753ee57a27250c7f5700b6fd04]
|
||||||
|
link Peer to Evidence--><path d="M764.4824,57.5589 C769.5471,59.5138 774.7956,61.5456 780.057,63.5775 " fill="none" id="Peer->Evidence" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><polygon fill="#607985" points="782.0333,64.3389,779.0592,61.355,780.0383,63.5678,777.8255,64.5469,782.0333,64.3389" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><!--MD5=[e08fc291d1c1b0105a0d24240eaba8e1]
|
||||||
|
link High to Evidence--><path d="M764.4824,113.4111 C769.5471,111.4734 774.7956,109.4672 780.057,107.4525 " fill="none" id="High->Evidence" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><polygon fill="#607985" points="782.0333,106.6996,777.8263,106.476,780.0355,107.4633,779.0482,109.6725,782.0333,106.6996" style="stroke: #607985; stroke-width: 0.5560836501901141;"/><!--MD5=[b5b70233b96aab6bafbd562394375464]
|
||||||
|
@startuml
|
||||||
|
skinparam backgroundColor white
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam monochrome false
|
||||||
|
skinparam defaultFontName "DejaVu Sans Mono"
|
||||||
|
skinparam defaultFontSize 14
|
||||||
|
skinparam defaultFontColor #24343D
|
||||||
|
skinparam titleFontName "DejaVu Sans Mono"
|
||||||
|
skinparam titleFontSize 13
|
||||||
|
skinparam titleFontColor #536B78
|
||||||
|
skinparam titleFontStyle plain
|
||||||
|
|
||||||
|
skinparam ArrowColor #607985
|
||||||
|
skinparam ArrowFontColor #425B67
|
||||||
|
skinparam ArrowThickness 1.3
|
||||||
|
skinparam LineThickness 1.3
|
||||||
|
skinparam nodesep 36
|
||||||
|
skinparam ranksep 42
|
||||||
|
skinparam Padding 6
|
||||||
|
|
||||||
|
skinparam classBackgroundColor #F5F8FA
|
||||||
|
skinparam classBorderColor #607985
|
||||||
|
skinparam classBorderThickness 1.5
|
||||||
|
skinparam classFontColor #1F3039
|
||||||
|
skinparam classFontSize 15
|
||||||
|
skinparam classAttributeFontColor #2E424D
|
||||||
|
skinparam classAttributeFontSize 13
|
||||||
|
skinparam classStereotypeFontColor #526B77
|
||||||
|
skinparam classStereotypeFontSize 11
|
||||||
|
skinparam minClassWidth 190
|
||||||
|
skinparam ClassAttributeIconSize 0
|
||||||
|
|
||||||
|
skinparam stateBackgroundColor #F5F8FA
|
||||||
|
skinparam stateBorderColor #607985
|
||||||
|
skinparam stateBorderThickness 1.5
|
||||||
|
skinparam stateFontColor #1F3039
|
||||||
|
skinparam stateFontSize 14
|
||||||
|
skinparam stateAttributeFontSize 13
|
||||||
|
|
||||||
|
skinparam noteBackgroundColor #F7F5ED
|
||||||
|
skinparam noteBorderColor #8A887C
|
||||||
|
skinparam noteFontColor #3E4B50
|
||||||
|
skinparam noteFontSize 12
|
||||||
|
|
||||||
|
skinparam ParticipantBackgroundColor #F5F8FA
|
||||||
|
skinparam ParticipantBorderColor #607985
|
||||||
|
skinparam ParticipantBorderThickness 1.5
|
||||||
|
skinparam ParticipantFontColor #1F3039
|
||||||
|
skinparam ParticipantFontSize 13
|
||||||
|
skinparam ActorBorderColor #607985
|
||||||
|
skinparam ActorFontColor #1F3039
|
||||||
|
skinparam LifeLineBorderColor #8297A1
|
||||||
|
skinparam LifeLineBackgroundColor #F9FBFC
|
||||||
|
skinparam SequenceArrowColor #607985
|
||||||
|
skinparam SequenceMessageAlign center
|
||||||
|
skinparam ParticipantPadding 18
|
||||||
|
skinparam BoxPadding 8
|
||||||
|
|
||||||
|
hide circle
|
||||||
|
hide empty members
|
||||||
|
scale 900 width
|
||||||
|
left to right direction
|
||||||
|
|
||||||
|
class "01 timer CSRs" as Timer <<runtime>> {
|
||||||
|
mtime
|
||||||
|
mtimecmp
|
||||||
|
mcause = 0x80000007
|
||||||
|
mepc = interrupted task PC
|
||||||
|
}
|
||||||
|
|
||||||
|
class "02 ISR evidence" as ISR <<runtime>> {
|
||||||
|
first_tick_count
|
||||||
|
ISR sp
|
||||||
|
one-shot tick checkpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
class "03 scheduler evidence" as Kernel <<debugger only>> {
|
||||||
|
Ready lists by priority
|
||||||
|
selected TCB
|
||||||
|
tick count
|
||||||
|
}
|
||||||
|
|
||||||
|
class "04 peer runtime" as Peer <<runtime>> {
|
||||||
|
context address
|
||||||
|
handle / TCB
|
||||||
|
task stack range
|
||||||
|
PC · SP · priority 2
|
||||||
|
}
|
||||||
|
|
||||||
|
class "05 high runtime" as High <<runtime>> {
|
||||||
|
handle / TCB
|
||||||
|
priority 0 → 3
|
||||||
|
PC · SP · run_tick
|
||||||
|
}
|
||||||
|
|
||||||
|
class "06 committed evidence" as Evidence <<application>> {
|
||||||
|
switch trace A · B · A
|
||||||
|
markers before · HIGH · after
|
||||||
|
source blob · ELF hash
|
||||||
|
PASS
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer - -> ISR : machine-timer trap
|
||||||
|
ISR - -> Kernel : scheduling opportunity
|
||||||
|
Kernel - -> Peer : equal-priority rotation
|
||||||
|
Kernel - -> High : select after priority raise
|
||||||
|
Peer - -> Evidence
|
||||||
|
High - -> Evidence
|
||||||
|
|
||||||
|
note bottom of Kernel
|
||||||
|
Kernel internals are debugger observations only.
|
||||||
|
Program assertions use public API and application records.
|
||||||
|
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: 15 KiB |
@@ -0,0 +1,60 @@
|
|||||||
|
' Shared print-first style for every diagram in this card.
|
||||||
|
skinparam backgroundColor white
|
||||||
|
skinparam shadowing false
|
||||||
|
skinparam monochrome false
|
||||||
|
skinparam defaultFontName "DejaVu Sans Mono"
|
||||||
|
skinparam defaultFontSize 14
|
||||||
|
skinparam defaultFontColor #24343D
|
||||||
|
skinparam titleFontName "DejaVu Sans Mono"
|
||||||
|
skinparam titleFontSize 13
|
||||||
|
skinparam titleFontColor #536B78
|
||||||
|
skinparam titleFontStyle plain
|
||||||
|
|
||||||
|
skinparam ArrowColor #607985
|
||||||
|
skinparam ArrowFontColor #425B67
|
||||||
|
skinparam ArrowThickness 1.3
|
||||||
|
skinparam LineThickness 1.3
|
||||||
|
skinparam nodesep 36
|
||||||
|
skinparam ranksep 42
|
||||||
|
skinparam Padding 6
|
||||||
|
|
||||||
|
skinparam classBackgroundColor #F5F8FA
|
||||||
|
skinparam classBorderColor #607985
|
||||||
|
skinparam classBorderThickness 1.5
|
||||||
|
skinparam classFontColor #1F3039
|
||||||
|
skinparam classFontSize 15
|
||||||
|
skinparam classAttributeFontColor #2E424D
|
||||||
|
skinparam classAttributeFontSize 13
|
||||||
|
skinparam classStereotypeFontColor #526B77
|
||||||
|
skinparam classStereotypeFontSize 11
|
||||||
|
skinparam minClassWidth 190
|
||||||
|
skinparam ClassAttributeIconSize 0
|
||||||
|
|
||||||
|
skinparam stateBackgroundColor #F5F8FA
|
||||||
|
skinparam stateBorderColor #607985
|
||||||
|
skinparam stateBorderThickness 1.5
|
||||||
|
skinparam stateFontColor #1F3039
|
||||||
|
skinparam stateFontSize 14
|
||||||
|
skinparam stateAttributeFontSize 13
|
||||||
|
|
||||||
|
skinparam noteBackgroundColor #F7F5ED
|
||||||
|
skinparam noteBorderColor #8A887C
|
||||||
|
skinparam noteFontColor #3E4B50
|
||||||
|
skinparam noteFontSize 12
|
||||||
|
|
||||||
|
skinparam ParticipantBackgroundColor #F5F8FA
|
||||||
|
skinparam ParticipantBorderColor #607985
|
||||||
|
skinparam ParticipantBorderThickness 1.5
|
||||||
|
skinparam ParticipantFontColor #1F3039
|
||||||
|
skinparam ParticipantFontSize 13
|
||||||
|
skinparam ActorBorderColor #607985
|
||||||
|
skinparam ActorFontColor #1F3039
|
||||||
|
skinparam LifeLineBorderColor #8297A1
|
||||||
|
skinparam LifeLineBackgroundColor #F9FBFC
|
||||||
|
skinparam SequenceArrowColor #607985
|
||||||
|
skinparam SequenceMessageAlign center
|
||||||
|
skinparam ParticipantPadding 18
|
||||||
|
skinparam BoxPadding 8
|
||||||
|
|
||||||
|
hide circle
|
||||||
|
hide empty members
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Card plan — FC03
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Make scheduler policy observable without teaching delays, queues, semaphores
|
||||||
|
or C++ prematurely. A student must be able to distinguish priority selection
|
||||||
|
from time slicing and show the difference in Hazard3/GDB.
|
||||||
|
|
||||||
|
## Task
|
||||||
|
|
||||||
|
```text
|
||||||
|
Task01 · Controlled scheduler experiment
|
||||||
|
├── A1 CONTEXT timer -> tick ISR -> scheduler -> tasks
|
||||||
|
├── A2 STRUCTURE contexts and bounded evidence records
|
||||||
|
├── A4 APPLICATION four-task topology and priority contract
|
||||||
|
├── A5 FLOW deterministic E01-E12 replay
|
||||||
|
├── A6 STATE Ready/Running/Deleted and priority transition
|
||||||
|
├── A7 RUNTIME CSRs, ISR stack, TCB/stack and committed traces
|
||||||
|
└── Exercise bounded observation with time slicing disabled
|
||||||
|
```
|
||||||
|
|
||||||
|
A3 is not repeated because callback dispatch was established in FC02. A8 is
|
||||||
|
not used because the scheduler policy is a kernel configuration rather than an
|
||||||
|
application design pattern.
|
||||||
|
|
||||||
|
## Runtime contract
|
||||||
|
|
||||||
|
1. `high_probe` is Ready at priority 0.
|
||||||
|
2. `peer_a` and `peer_b` are CPU-bound at priority 2.
|
||||||
|
3. `verifier` is Ready at priority 1 and cannot run while peers remain Ready.
|
||||||
|
4. The committed task-change trace must contain A-B-A.
|
||||||
|
5. Peer A records `before_raise` and calls `vTaskPrioritySet(high, 3)`.
|
||||||
|
6. High records `HIGH` and self-deletes before the API call returns.
|
||||||
|
7. Peer A records `after_raise`, requests peer termination and both peers
|
||||||
|
self-delete.
|
||||||
|
8. The verifier checks every invariant and exits with PASS.
|
||||||
|
|
||||||
|
The exact ordering claim applies to the three-marker projection only. Other
|
||||||
|
legal scheduling activity may occur before peer A is selected again.
|
||||||
|
|
||||||
|
## Exercise
|
||||||
|
|
||||||
|
Disable `configUSE_TIME_SLICING` while keeping both CPU-bound peers at the same
|
||||||
|
priority. This is an expected-noncompletion experiment: observe for a bounded
|
||||||
|
number of ticks that one peer may starve the other, capture the debugger state,
|
||||||
|
then restore the configuration. The exercise does not redefine the card's PASS
|
||||||
|
contract.
|
||||||
|
|
||||||
|
The series-level rationale and all FC03 acceptance gates are maintained in:
|
||||||
|
|
||||||
|
`meta/workspace-info/plans/informatyka/freertos-c-fc03-a1-a8.md`.
|
||||||
|
|
||||||
@@ -0,0 +1,347 @@
|
|||||||
|
% 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=0.5cm,headheight=24mm,headsep=3mm,includehead]{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}{M. Pabiszczak}
|
||||||
|
\newcommand{\DocumentYear}{2026}
|
||||||
|
\newcommand{\CardSeries}{freertos-c}
|
||||||
|
\newcommand{\CardNumber}{03}
|
||||||
|
\newcommand{\CardCount}{15}
|
||||||
|
\newcommand{\CardSlug}{tick-priority-preemption-timeslicing}
|
||||||
|
\newcommand{\CardVersion}{v00.01}
|
||||||
|
\newcommand{\DocumentUUID}{c4bf7933-9a39-4968-b40e-158bafbb3eea}
|
||||||
|
|
||||||
|
\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%
|
||||||
|
\qrcode[level=L,height=22mm]{https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-c-scheduler}%
|
||||||
|
\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 Tick, priorytety, wywłaszczenie i time slicing}\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-18T00: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 03/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 c4bf7933-9a39-4968-b40e-158bafbb3eea}\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 2214a13f-34c6-40a1-a561-70a00ec98285}\hfill{\fontsize{3.4}{3.75}\selectfont\ttfamily\bfseries AUTHOR M. 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-c-scheduler}{\nolinkurl{https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-c-scheduler}}}} \\%
|
||||||
|
\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/2214a13f-34c6-40a1-a561-70a00ec98285}{\nolinkurl{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/2214a13f-34c6-40a1-a561-70a00ec98285}}}} \\%
|
||||||
|
\end{tabularx}%
|
||||||
|
\end{minipage}%
|
||||||
|
\vrule width0.35pt%
|
||||||
|
\begin{minipage}[c][23mm][c]{24mm}\centering%
|
||||||
|
\qrcode[level=L,height=22mm]{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/2214a13f-34c6-40a1-a561-70a00ec98285}%
|
||||||
|
\end{minipage}%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\endgroup%
|
||||||
|
}
|
||||||
|
|
||||||
|
\pagestyle{fancy}
|
||||||
|
\fancyhf{}
|
||||||
|
\fancyhead[C]{\ESCPageResourceHeader}
|
||||||
|
\renewcommand{\headrulewidth}{0pt}
|
||||||
|
\renewcommand{\footrulewidth}{0pt}
|
||||||
|
|
||||||
|
\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.25em}%
|
||||||
|
\noindent\textcolor{black!18}{\rule{\textwidth}{0.25pt}}%
|
||||||
|
\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~DBG}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.03}{Odtwarza scheduler w Hazard3/GDB.}}\par%
|
||||||
|
\hspace*{0.54em}\textcolor{blue!70!black}{KW~LOCAL~DBG}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.03}{Łączy checkpoint, UML, rejestry i pamięć.}}\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]{.03}{Analizuje reguły wyboru i stany tasków.}}\par%
|
||||||
|
\hspace*{0.54em}\textcolor{blue!70!black}{KW~LOCAL~KW~RTOS}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.03}{Rozdziela priorytet od time slicing.}}\par%
|
||||||
|
\end{minipage}%
|
||||||
|
}%
|
||||||
|
\end{minipage}%
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\sloppy
|
||||||
|
|
||||||
|
\vspace{1.0em}
|
||||||
|
\noindent{\Large\bfseries Cel karty}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
Uczeń rozróżnia wybór najwyższego priorytetu od time slicing między równymi priorytetami. Odtwarza tick oraz natychmiastowe wywłaszczenie po zmianie priorytetu i wiąże ślad aplikacji ze stanem Hazard3/GDB.
|
||||||
|
|
||||||
|
\vspace{0.8em}
|
||||||
|
\noindent\textcolor{black!25}{\rule{\textwidth}{0.35pt}}
|
||||||
|
\vspace{0.7em}
|
||||||
|
\noindent{\Large\bfseries Zakres karty}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
Dwa CPU-bound taski o priorytecie 2 tworzą ślad A-B-A. Peer A podnosi Ready task z priorytetu 0 do 3; HIGH musi wykonać się przed powrotem vTaskPrioritySet. Verifier o priorytecie 1 publikuje PASS dopiero po zakończeniu wyższych tasków. Poza zakresem są delay, kolejki, semafory, ISR-safe API i C++.
|
||||||
|
\vspace{0.55em}
|
||||||
|
\small\begin{tabularx}{\textwidth}{@{}>{\ttfamily\raggedright\arraybackslash}p{0.85cm}>{\ttfamily\raggedright\arraybackslash}p{1.45cm}X>{\raggedright\arraybackslash}p{2.45cm}>{\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
|
||||||
|
\multicolumn{1}{|l|}{\textbf{\texttt{FC03}}} & \multicolumn{1}{l|}{\textbf{\texttt{Task01}}} & \multicolumn{1}{l|}{\textbf{Tick, priority selection, preemption i time slicing}} & \multicolumn{1}{l|}{\textbf{główny}} & \multicolumn{1}{l|}{\textbf{opracowane}} & \multicolumn{1}{l|}{\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 — Context \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.9303\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a1-context.png}}
|
||||||
|
\caption{A1 CONTEXT — timer, port, scheduler i taski aplikacji. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a1-context}
|
||||||
|
\end{figure}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\begin{landscape}
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\noindent{\small\bfseries A2 — Structure \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.95319\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a2-structure.png}}
|
||||||
|
\caption{A2 STRUCTURE — konteksty tasków i rekordy śladów. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a2-structure}
|
||||||
|
\end{figure}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
\end{landscape}
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\begin{landscape}
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\noindent{\small\bfseries A4 — Application \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.95213\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a4-application.png}}
|
||||||
|
\caption{A4 APPLICATION — topologia tasków i inwarianty priorytetów. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a4-application}
|
||||||
|
\end{figure}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
\end{landscape}
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\begin{landscape}
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\noindent{\small\bfseries A5 — Flow \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.96064\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a5-flow-a.png}}
|
||||||
|
\caption{A5 część 1/2 — konfiguracja, start, pierwszy tick i A-B-A. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a5-flow-a}
|
||||||
|
\end{figure}
|
||||||
|
\clearpage
|
||||||
|
\noindent{\small\bfseries A5 — Flow \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.96064\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a5-flow-b.png}}
|
||||||
|
\caption{A5 część 2/2 — priority raise, natychmiastowe wywłaszczenie i PASS. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a5-flow-b}
|
||||||
|
\end{figure}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
\end{landscape}
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\begin{landscape}
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\noindent{\small\bfseries A6 — State \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.95319\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a6-state.png}}
|
||||||
|
\caption{A6 STATE — przejścia high probe, peerów i verifiera. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a6-state}
|
||||||
|
\end{figure}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
\end{landscape}
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\begin{landscape}
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\noindent{\small\bfseries A7 — Runtime \hfill część 1/1 \textperiodcentered\ r1 c1}\par
|
||||||
|
\vspace{0.35em}
|
||||||
|
\begin{figure}[H]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.95426\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a7-runtime.png}}
|
||||||
|
\caption{A7 RUNTIME — timer, ISR, scheduler, taski i końcowy dowód. — część 1/1 (r1 c1).}
|
||||||
|
\label{fig:a7-runtime}
|
||||||
|
\end{figure}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
\end{landscape}
|
||||||
|
|
||||||
|
\clearpage
|
||||||
|
\ESCSectionBlockStart
|
||||||
|
\section{Task01 — Scheduler experiment}
|
||||||
|
|
||||||
|
\begin{ESCTaskFrame}{TASK01 · Kontrolowany eksperyment schedulera FreeRTOS}
|
||||||
|
{\scriptsize\ttfamily 2214a13f-34c6-40a1-a561-70a00ec98285}\par
|
||||||
|
\begin{ESCBlockFrame}{A — model ticka i wyboru taska}
|
||||||
|
Najpierw odczytaj A1, A2 i A4 oraz zapisz przewidywaną kolejność.
|
||||||
|
\begin{ESCStepFrame}{read-priorities · Odczytaj kontrakt priorytetów.}
|
||||||
|
Wyjaśnij, dlaczego high=0, peery=2, verifier=1 nie powodują przedwczesnego uruchomienia high ani verifiera.
|
||||||
|
\end{ESCStepFrame}
|
||||||
|
\begin{ESCStepFrame}{predict-traces · Przewidź dwa ślady.}
|
||||||
|
Rozdziel task-change trace A-B-A od marker projection before-HIGH-after.
|
||||||
|
\end{ESCStepFrame}
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{B — deterministyczny replay E01–E12}
|
||||||
|
Każdy RUN replay zaczyna się od czystej RAM i zweryfikowanego obrazu.
|
||||||
|
\begin{ESCStepFrame}{follow-tick · Zbadaj pierwszy tick.}
|
||||||
|
Na E06 zapisz mcause, mepc, ISR SP i tick.
|
||||||
|
\end{ESCStepFrame}
|
||||||
|
\begin{ESCStepFrame}{follow-timeslice · Zbadaj A-B-A.}
|
||||||
|
Na E07 zapisz triplet tasków, ticki i liczbę obserwowanych zmian.
|
||||||
|
\end{ESCStepFrame}
|
||||||
|
\begin{ESCStepFrame}{follow-preemption · Zbadaj natychmiastowe wywłaszczenie.}
|
||||||
|
Porównaj E08, E09 i E10; pokaż, że HIGH wykonał się przed after_raise.
|
||||||
|
\end{ESCStepFrame}
|
||||||
|
\begin{ESCStepFrame}{finish-pass · Zamknij dowód.}
|
||||||
|
Na E11 sprawdź self-delete peerów, a na E12 końcowe inwarianty i PASS.
|
||||||
|
\end{ESCStepFrame}
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\begin{ESCBlockFrame}{Ćwiczenie · Ćwiczenie — bounded observation bez time slicing}
|
||||||
|
Ustaw configUSE_TIME_SLICING=0, nie zmieniając dwóch CPU-bound peerów o priorytecie 2. Uruchom tylko ograniczoną obserwację debuggera; nie oczekuj PASS. Zapisz, który peer pozostaje Running, jaki jest tick count i dlaczego drugi może być głodzony. Następnie przywróć konfigurację.
|
||||||
|
\par\textbf{Evidence:} Timestampowany snapshot PC/SP, tick count i obu liczników iteracji oraz krótkie wyjaśnienie expected noncompletion.
|
||||||
|
\par\textbf{Acceptance:} Uczeń nie interpretuje braku PASS jako błędu harnessu i poprawnie wiąże głodzenie z brakiem rotacji równych priorytetów.
|
||||||
|
\end{ESCBlockFrame}
|
||||||
|
\tcblower\textbf{Task acceptance:} Program wypisuje PASS; trace zawiera A-B-A; marker projection to before-HIGH-after; pierwszy tick ma machine-timer mcause; TCB i stack są spójne.
|
||||||
|
\end{ESCTaskFrame}
|
||||||
|
\begin{ESCConclusionFrame}
|
||||||
|
Priorytet wybiera najwyższy Ready task. Time slicing rotuje wyłącznie taski o tym samym priorytecie, a uczynienie wyższego taska Ready może wywłaszczyć caller przed powrotem API.
|
||||||
|
\end{ESCConclusionFrame}
|
||||||
|
\ESCSectionBlockEnd
|
||||||
|
|
||||||
|
\end{document}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# FC03 page layout
|
||||||
|
|
||||||
|
## Physical sheets
|
||||||
|
|
||||||
|
1. Goal, scope, book mapping and viewpoint index — portrait.
|
||||||
|
2. A1 Context — portrait, vertical system boundary.
|
||||||
|
3. A2 Structure — landscape.
|
||||||
|
4. A4 Application — landscape.
|
||||||
|
5. A5 Flow part 1, E01-E07 — landscape.
|
||||||
|
6. A5 Flow part 2, E08-E12 — landscape.
|
||||||
|
7. A6 State — landscape.
|
||||||
|
8. A7 Runtime — landscape.
|
||||||
|
9. Task instructions and exercise — portrait.
|
||||||
|
|
||||||
|
Every diagram begins on a fresh physical sheet. A5 is one logical sequence
|
||||||
|
split at a semantic boundary. Both halves keep the same participants, scale,
|
||||||
|
header geometry and vertical origin. The web viewer may join those two sheets
|
||||||
|
side by side; print keeps them as separate A4 pages.
|
||||||
|
|
||||||
|
No box is cut across a page boundary. A diagram that fits a single physical
|
||||||
|
sheet is scaled to that sheet instead of being tiled.
|
||||||
|
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#ifndef FREERTOS_CONFIG_H
|
||||||
|
#define FREERTOS_CONFIG_H
|
||||||
|
|
||||||
|
/* Hazard3 testbench: mtime advances once per simulated cycle. */
|
||||||
|
#define configCPU_CLOCK_HZ ( 1000000UL )
|
||||||
|
#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 configNUMBER_OF_CORES 1
|
||||||
|
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
|
||||||
|
#define configUSE_IDLE_HOOK 0
|
||||||
|
#define configUSE_TICK_HOOK 1
|
||||||
|
#define configMAX_PRIORITIES 5
|
||||||
|
#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 configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||||
|
#define configSUPPORT_STATIC_ALLOCATION 0
|
||||||
|
#define configTOTAL_HEAP_SIZE ( 16U * 1024U )
|
||||||
|
#define configAPPLICATION_ALLOCATED_HEAP 1
|
||||||
|
#define configUSE_MALLOC_FAILED_HOOK 1
|
||||||
|
#define configHEAP_CLEAR_MEMORY_ON_FREE 0
|
||||||
|
|
||||||
|
#define configUSE_TIMERS 0
|
||||||
|
#define configUSE_MUTEXES 0
|
||||||
|
#define configUSE_RECURSIVE_MUTEXES 0
|
||||||
|
#define configUSE_COUNTING_SEMAPHORES 0
|
||||||
|
#define configUSE_TASK_NOTIFICATIONS 0
|
||||||
|
#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 configRECORD_STACK_HIGH_ADDRESS 1
|
||||||
|
|
||||||
|
#define INCLUDE_vTaskDelay 0
|
||||||
|
#define INCLUDE_vTaskDelayUntil 0
|
||||||
|
#define INCLUDE_vTaskDelete 1
|
||||||
|
#define INCLUDE_vTaskSuspend 0
|
||||||
|
#define INCLUDE_uxTaskPriorityGet 1
|
||||||
|
#define INCLUDE_vTaskPrioritySet 1
|
||||||
|
#define INCLUDE_xTaskGetSchedulerState 1
|
||||||
|
#define INCLUDE_uxTaskGetStackHighWaterMark 1
|
||||||
|
|
||||||
|
/* Hazard3 is RV32I: no floating-point or vector register file to save. */
|
||||||
|
#define configENABLE_FPU 0
|
||||||
|
#define configENABLE_VPU 0
|
||||||
|
|
||||||
|
/* Keep interrupt and task stack domains visually distinct. */
|
||||||
|
#define configISR_STACK_SIZE_WORDS 128
|
||||||
|
|
||||||
|
#ifndef __ASSEMBLER__
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" void lab_assert_fail( const char * file, int line );
|
||||||
|
#else
|
||||||
|
void lab_assert_fail( const char * file, int line );
|
||||||
|
#endif
|
||||||
|
#define configASSERT( condition ) do { if( !( condition ) ) { lab_assert_fail( __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,11 @@
|
|||||||
|
#ifndef LAB_FREESTANDING_STDLIB_H
|
||||||
|
#define LAB_FREESTANDING_STDLIB_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The selected FreeRTOS sources include <stdlib.h> for standard types, but the
|
||||||
|
* configuration used by this card does not call hosted stdlib functions.
|
||||||
|
* GCC's freestanding headers provide size_t through <stddef.h>.
|
||||||
|
*/
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef LAB_FREESTANDING_STRING_H
|
||||||
|
#define LAB_FREESTANDING_STRING_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
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 );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
#ifndef TASK01_SCHEDULER_H
|
||||||
|
#define TASK01_SCHEDULER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
#define SCHED_TRACE_CAPACITY 16U
|
||||||
|
#define SCHED_PEER_STACK_WORDS 192U
|
||||||
|
#define SCHED_PROBE_STACK_WORDS 192U
|
||||||
|
#define SCHED_VERIFY_STACK_WORDS 192U
|
||||||
|
#define SCHED_PEER_PRIORITY 2U
|
||||||
|
#define SCHED_PROBE_INITIAL_PRIORITY 0U
|
||||||
|
#define SCHED_PROBE_TARGET_PRIORITY 3U
|
||||||
|
#define SCHED_VERIFY_PRIORITY 1U
|
||||||
|
#define SCHED_TIMEOUT_TICKS 100U
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SCHED_PHASE_PREPARED = 0,
|
||||||
|
SCHED_PHASE_READY,
|
||||||
|
SCHED_PHASE_RUNNING,
|
||||||
|
SCHED_PHASE_COMPLETED
|
||||||
|
} SchedulerPhase;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
UBaseType_t configured_priority;
|
||||||
|
volatile UBaseType_t observed_priority;
|
||||||
|
TaskHandle_t handle;
|
||||||
|
uintptr_t created_handle;
|
||||||
|
uintptr_t tcb_address;
|
||||||
|
uintptr_t entry_sp;
|
||||||
|
uintptr_t stack_low;
|
||||||
|
uintptr_t stack_high;
|
||||||
|
volatile uint32_t iterations;
|
||||||
|
volatile uint32_t checksum;
|
||||||
|
volatile TickType_t first_tick;
|
||||||
|
volatile TickType_t last_tick;
|
||||||
|
volatile SchedulerPhase phase;
|
||||||
|
} PeerContext;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
UBaseType_t initial_priority;
|
||||||
|
UBaseType_t target_priority;
|
||||||
|
volatile UBaseType_t observed_initial_priority;
|
||||||
|
volatile UBaseType_t observed_priority;
|
||||||
|
TaskHandle_t handle;
|
||||||
|
uintptr_t created_handle;
|
||||||
|
uintptr_t tcb_address;
|
||||||
|
uintptr_t entry_sp;
|
||||||
|
uintptr_t stack_low;
|
||||||
|
uintptr_t stack_high;
|
||||||
|
volatile TickType_t run_tick;
|
||||||
|
volatile uint32_t ran_before_caller_returned;
|
||||||
|
volatile uint32_t verifier_was_not_running;
|
||||||
|
volatile SchedulerPhase phase;
|
||||||
|
} HighProbeContext;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
UBaseType_t configured_priority;
|
||||||
|
volatile UBaseType_t observed_priority;
|
||||||
|
TaskHandle_t handle;
|
||||||
|
volatile uint32_t pass;
|
||||||
|
volatile SchedulerPhase phase;
|
||||||
|
} VerifierContext;
|
||||||
|
|
||||||
|
void peer_task_entry( void * pv_parameters );
|
||||||
|
void high_probe_task_entry( void * pv_parameters );
|
||||||
|
void verifier_task_entry( void * pv_parameters );
|
||||||
|
void task01_scheduler_checkpoint( uint32_t point, const void * subject );
|
||||||
|
void task01_scheduler_checkpoint_committed( uint32_t point,
|
||||||
|
const void * subject );
|
||||||
|
void task01_tick_checkpoint_committed( void );
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#ifndef TASK01_SCHEDULER_MODEL_H
|
||||||
|
#define TASK01_SCHEDULER_MODEL_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SCHED_PEER_A = 0U,
|
||||||
|
SCHED_PEER_B = 1U,
|
||||||
|
SCHED_ORDER_BEFORE_RAISE = 1U,
|
||||||
|
SCHED_ORDER_HIGH_PROBE = 2U,
|
||||||
|
SCHED_ORDER_AFTER_RAISE = 3U
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int scheduler_trace_has_aba( const uint32_t * task_ids,
|
||||||
|
size_t count )
|
||||||
|
{
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
for( index = 2U; index < count; ++index )
|
||||||
|
{
|
||||||
|
if( task_ids[ index - 2U ] == SCHED_PEER_A &&
|
||||||
|
task_ids[ index - 1U ] == SCHED_PEER_B &&
|
||||||
|
task_ids[ index ] == SCHED_PEER_A )
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int scheduler_ticks_nondecreasing( const uint32_t * ticks,
|
||||||
|
size_t count )
|
||||||
|
{
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
for( index = 1U; index < count; ++index )
|
||||||
|
{
|
||||||
|
if( ticks[ index ] < ticks[ index - 1U ] )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int scheduler_preemption_order_valid( const uint32_t * order,
|
||||||
|
size_t count )
|
||||||
|
{
|
||||||
|
return count == 3U &&
|
||||||
|
order[ 0 ] == SCHED_ORDER_BEFORE_RAISE &&
|
||||||
|
order[ 1 ] == SCHED_ORDER_HIGH_PROBE &&
|
||||||
|
order[ 2 ] == SCHED_ORDER_AFTER_RAISE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,586 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../../../tools/card-layouts/schemas/card-source.schema.json",
|
||||||
|
"schema": "esc-card-source.v1",
|
||||||
|
"card": {
|
||||||
|
"id": "mpabi-freertos-c-03-scheduler",
|
||||||
|
"series": "freertos-c",
|
||||||
|
"series_title": "FreeRTOS C",
|
||||||
|
"number": "03",
|
||||||
|
"count": "15",
|
||||||
|
"slug": "tick-priority-preemption-timeslicing",
|
||||||
|
"title": "Tick, priorytety, wywłaszczenie i time slicing",
|
||||||
|
"topic": "Scheduler FreeRTOS obserwowany w C i Hazard3",
|
||||||
|
"project": "Freestanding C nad FreeRTOS",
|
||||||
|
"subject": "Informatyka",
|
||||||
|
"level": "Rok 2 · L03 · RV32I/Hazard3",
|
||||||
|
"revision_date": "2026-07-18T00:00:00+02:00",
|
||||||
|
"status": "Gotowa",
|
||||||
|
"version": "v00.01",
|
||||||
|
"uuid": "c4bf7933-9a39-4968-b40e-158bafbb3eea",
|
||||||
|
"author": "M. Pabiszczak",
|
||||||
|
"year": "2026"
|
||||||
|
},
|
||||||
|
"generated": {
|
||||||
|
"tex": "doc/generated/main.tex",
|
||||||
|
"html": "web/index.html",
|
||||||
|
"html_css": "web/style.css",
|
||||||
|
"html_tree_inspector": false,
|
||||||
|
"react_app": true
|
||||||
|
},
|
||||||
|
"render_dictionary": false,
|
||||||
|
"viewpoints": [
|
||||||
|
{"id": "A1", "label": "CONTEXT", "subtitle": "Timer → tick ISR → scheduler → taski", "status": "enabled", "target_label": "fig:a1-context"},
|
||||||
|
{"id": "A2", "label": "STRUCTURE", "subtitle": "Konteksty i ograniczone rekordy dowodowe", "status": "enabled", "target_label": "fig:a2-structure"},
|
||||||
|
{"id": "A3", "label": "DISPATCH", "subtitle": "Callback i context", "status": "unavailable", "reason": "Mechanizm TaskFunction_t i void* został udowodniony w FC02 i w FC03 nie ulega zmianie."},
|
||||||
|
{"id": "A4", "label": "APPLICATION", "subtitle": "Cztery taski i kontrakt priorytetów", "status": "enabled", "target_label": "fig:a4-application"},
|
||||||
|
{"id": "A5", "label": "FLOW", "subtitle": "Deterministyczny przebieg E01–E12", "status": "enabled", "target_label": "fig:a5-flow-a"},
|
||||||
|
{"id": "A6", "label": "STATE", "subtitle": "Ready, Running, Deleted i zmiana priorytetu", "status": "enabled", "target_label": "fig:a6-state"},
|
||||||
|
{"id": "A7", "label": "RUNTIME", "subtitle": "Tick, CSRs, TCB, stosy i ślady", "status": "enabled", "target_label": "fig:a7-runtime"},
|
||||||
|
{"id": "A8", "label": "PATTERNS", "subtitle": "Wzorce aplikacyjne", "status": "unavailable", "reason": "Polityka schedulera jest konfiguracją kernela, a nie wzorcem projektowym aplikacji."}
|
||||||
|
],
|
||||||
|
"debug_strategies": {
|
||||||
|
"code.tick-boundary": {
|
||||||
|
"id": "code.tick-boundary",
|
||||||
|
"kind": "code",
|
||||||
|
"action": "open",
|
||||||
|
"prerequisites": ["repozytorium FC03", "zweryfikowany source blob"],
|
||||||
|
"layout": ["read-only source", "granica timer/port/kernel/aplikacja"],
|
||||||
|
"commands": ["rg -n 'configTICK_RATE_HZ|vApplicationTickHook|xTaskIncrementTick' include src vendor/FreeRTOS-Kernel"],
|
||||||
|
"expected_observations": ["tick jest okazją do planowania", "aplikacja zapisuje tylko ograniczony rekord"],
|
||||||
|
"assertions": ["tick hook nie drukuje i nie blokuje", "aplikacja nie zależy od pxCurrentTCB"],
|
||||||
|
"evidence_fields": ["code_ref", "source_git_blob", "kotwica UML"]
|
||||||
|
},
|
||||||
|
"code.scheduler-data": {
|
||||||
|
"id": "code.scheduler-data",
|
||||||
|
"kind": "code",
|
||||||
|
"action": "open",
|
||||||
|
"prerequisites": ["freestanding C11", "include/task01_scheduler.h"],
|
||||||
|
"layout": ["konteksty C", "priorytety", "bufory śladów"],
|
||||||
|
"commands": ["rg -n 'PeerContext|HighProbeContext|g_switch_task|g_preemption_order' include src"],
|
||||||
|
"expected_observations": ["taski mają jawne konteksty", "rekordy mają stałą pojemność"],
|
||||||
|
"assertions": ["high ma kontrakt 0→3", "verifier ma priorytet 1"],
|
||||||
|
"evidence_fields": ["layout pól", "stałe priorytetów", "pojemność śladu"]
|
||||||
|
},
|
||||||
|
"run.prepare": {
|
||||||
|
"id": "run.prepare",
|
||||||
|
"kind": "run",
|
||||||
|
"action": "replay",
|
||||||
|
"prerequisites": ["Hazard3 online", "czysta RAM", "aktualny ELF"],
|
||||||
|
"layout": ["source", "konteksty", "uchwyty", "priorytety"],
|
||||||
|
"commands": ["print g_high_probe", "print g_peer_a", "print g_peer_b", "print g_verifier"],
|
||||||
|
"expected_observations": ["high jest Ready przy priorytecie 0", "peery mają priorytet 2, verifier 1"],
|
||||||
|
"assertions": ["wszystkie uchwyty są ważne przed startem schedulera", "publiczne API zwraca oczekiwane priorytety"],
|
||||||
|
"evidence_fields": ["event_id", "handle", "priority", "timestamp"]
|
||||||
|
},
|
||||||
|
"run.tick": {
|
||||||
|
"id": "run.tick",
|
||||||
|
"kind": "run",
|
||||||
|
"action": "replay",
|
||||||
|
"prerequisites": ["scheduler uruchomiony", "tick hook włączony"],
|
||||||
|
"layout": ["tick hook", "CSR", "ISR stack", "przerwany task"],
|
||||||
|
"commands": ["print g_tick_hook_count", "print/x g_first_tick_mcause", "print/x g_first_tick_mepc", "print/x g_first_tick_sp"],
|
||||||
|
"expected_observations": ["mcause oznacza machine-timer interrupt", "mepc wskazuje przerwany kod"],
|
||||||
|
"assertions": ["checkpoint ticka jest jednorazowy", "sink ISR nie korzysta z task-context API"],
|
||||||
|
"evidence_fields": ["tick", "mcause", "mepc", "ISR SP", "timestamp"]
|
||||||
|
},
|
||||||
|
"run.timeslice": {
|
||||||
|
"id": "run.timeslice",
|
||||||
|
"kind": "run",
|
||||||
|
"action": "replay",
|
||||||
|
"prerequisites": ["dwa CPU-bound taski priority=2", "time slicing włączony"],
|
||||||
|
"layout": ["pętle peerów", "task-change trace", "ticki"],
|
||||||
|
"commands": ["print g_switch_task", "print g_switch_tick", "print g_aba_task", "print g_peer_changes"],
|
||||||
|
"expected_observations": ["zatwierdzony ślad zawiera A→B→A", "ticki są niemalejące"],
|
||||||
|
"assertions": ["zapisywana jest jedna pozycja na obserwowaną zmianę taska", "dowód nie zakłada równego udziału CPU"],
|
||||||
|
"evidence_fields": ["triplet A/B/A", "ticks", "change count", "timestamp"]
|
||||||
|
},
|
||||||
|
"run.preempt": {
|
||||||
|
"id": "run.preempt",
|
||||||
|
"kind": "run",
|
||||||
|
"action": "replay",
|
||||||
|
"prerequisites": ["A→B→A wykryte", "high Ready przy priorytecie 0"],
|
||||||
|
"layout": ["call site vTaskPrioritySet", "high task", "markery kolejności"],
|
||||||
|
"commands": ["print g_preemption_order", "print g_high_probe", "info registers pc sp a0 a1"],
|
||||||
|
"expected_observations": ["high uruchamia się przy priorytecie 3", "caller nie zapisuje after przed HIGH"],
|
||||||
|
"assertions": ["projekcja markerów to before/HIGH/after", "verifier jeszcze nie działa"],
|
||||||
|
"evidence_fields": ["marker projection", "priority", "PC", "SP", "timestamp"]
|
||||||
|
},
|
||||||
|
"run.state": {
|
||||||
|
"id": "run.state",
|
||||||
|
"kind": "run",
|
||||||
|
"action": "replay",
|
||||||
|
"prerequisites": ["kanoniczny checkpoint FC03"],
|
||||||
|
"layout": ["kontekst aplikacyjny", "publiczny stan taska", "source"],
|
||||||
|
"commands": ["print g_peer_a.phase", "print g_peer_b.phase", "print g_high_probe.phase", "print g_peers_finished"],
|
||||||
|
"expected_observations": ["Ready nie oznacza Running", "każdy task czyści własny publiczny handle"],
|
||||||
|
"assertions": ["high i peery kończą się przed verifierem", "brak suspend/resume w eksperymencie"],
|
||||||
|
"evidence_fields": ["phase", "handle", "event_id", "PC"]
|
||||||
|
},
|
||||||
|
"run.runtime": {
|
||||||
|
"id": "run.runtime",
|
||||||
|
"kind": "run",
|
||||||
|
"action": "replay",
|
||||||
|
"prerequisites": ["Hazard3/GDB online", "symbole ELF"],
|
||||||
|
"layout": ["timer/CSR", "handle/TCB", "task stack", "committed evidence"],
|
||||||
|
"commands": ["print g_peer_a.created_handle", "print g_peer_a.tcb_address", "print g_peer_a.stack_low", "print g_peer_a.stack_high", "info registers sp pc"],
|
||||||
|
"expected_observations": ["handle/TCB i stack są rozdzielone", "SP należy do stosu wybranego taska"],
|
||||||
|
"assertions": ["created_handle == tcb_address", "źródło, ELF i obraz są identyfikowalne"],
|
||||||
|
"evidence_fields": ["handle/TCB", "stack range", "SP", "PC", "hash"]
|
||||||
|
},
|
||||||
|
"run.pass": {
|
||||||
|
"id": "run.pass",
|
||||||
|
"kind": "run",
|
||||||
|
"action": "replay",
|
||||||
|
"prerequisites": ["high i oba peery zakończone", "verifier wybrany"],
|
||||||
|
"layout": ["konteksty", "ślady", "końcowe asercje"],
|
||||||
|
"commands": ["print g_scheduler_pass", "print g_aba_task", "print g_preemption_order", "print g_tick_hook_count"],
|
||||||
|
"expected_observations": ["A/B/A i before/HIGH/after są obecne", "program kończy się PASS"],
|
||||||
|
"assertions": ["PASS=1", "g_peers_finished=2", "tick hook count>=2"],
|
||||||
|
"evidence_fields": ["PASS", "traces", "ticks", "source blob", "image hash", "timestamp"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"template": "templates/karta-klasyczna.json",
|
||||||
|
"title_block": {
|
||||||
|
"category": "KARTA PRACY · INFORMATYKA",
|
||||||
|
"prepared_by": "M. Pabiszczak",
|
||||||
|
"prepared_on": "2026-07-18T00:00:00+02:00",
|
||||||
|
"title": "Tick, priorytety, wywłaszczenie i time slicing",
|
||||||
|
"url": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/2214a13f-34c6-40a1-a561-70a00ec98285",
|
||||||
|
"repository_url": "https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-c-scheduler",
|
||||||
|
"url_host_uuid": "dce7fb9d-7b2f-5d49-96a2-3a30d3070b84",
|
||||||
|
"url_domain": "mpabi.pl",
|
||||||
|
"doc_uuid": "2214a13f-34c6-40a1-a561-70a00ec98285",
|
||||||
|
"revision": "v00.01",
|
||||||
|
"issued_on": "2026-07-18T00:00:00+02:00",
|
||||||
|
"series": "FREERTOS-C-03",
|
||||||
|
"document_type": "karta pracy",
|
||||||
|
"tool": "card-layouts",
|
||||||
|
"show_qr": true,
|
||||||
|
"show_repository_qr": true,
|
||||||
|
"height_cm": 2.6,
|
||||||
|
"repeat_on_every_page": true,
|
||||||
|
"replace_front_matter": true
|
||||||
|
},
|
||||||
|
"front_page_scope": {
|
||||||
|
"title": "Cel karty",
|
||||||
|
"content_tex": "Uczeń rozróżnia wybór najwyższego priorytetu od time slicing między równymi priorytetami. Odtwarza tick oraz natychmiastowe wywłaszczenie po zmianie priorytetu i wiąże ślad aplikacji ze stanem Hazard3/GDB.",
|
||||||
|
"scope_title": "Zakres karty",
|
||||||
|
"scope_content_tex": "Dwa CPU-bound taski o priorytecie 2 tworzą ślad A-B-A. Peer A podnosi Ready task z priorytetu 0 do 3; HIGH musi wykonać się przed powrotem vTaskPrioritySet. Verifier o priorytecie 1 publikuje PASS dopiero po zakończeniu wyższych tasków. Poza zakresem są delay, kolejki, semafory, ISR-safe API i C++.",
|
||||||
|
"scope_table": {
|
||||||
|
"headers": ["Lekcja", "Task", "Najważniejsza idea", "Priorytet", "Status", "Version"],
|
||||||
|
"rows": [{"chapter": "FC03", "task": "Task01", "idea_tex": "Tick, priority selection, preemption i time slicing", "priority": "główny", "status": "ready", "version": "v00.01", "key": true}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"side_margin_tree_layout": {
|
||||||
|
"columns": [
|
||||||
|
{"id": "zawodowe", "label": "TECH", "side": "left", "tree": "WE -> EK -> KW", "description": "Dowody schedulera w Hazard3."},
|
||||||
|
{"id": "ogolne", "label": "OG", "side": "right", "tree": "WE -> EN -> KW", "description": "Model czasu i priorytetów."}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"learning_effects": {
|
||||||
|
"FC03.EN01": {"bloom_level": "Analiza", "label": "Model schedulera", "text": "Uczeń odróżnia priorytet, Ready/Running, tick i time slicing.", "assessment_criteria": ["FC03.KW01"]},
|
||||||
|
"FC03.EK01": {"bloom_level": "Zastosowanie", "label": "Replay wywłaszczenia", "text": "Uczeń odtwarza E01–E12 i dowodzi A-B-A oraz before-HIGH-after.", "assessment_criteria": ["FC03.KW01"]}
|
||||||
|
},
|
||||||
|
"assessment_criteria": {
|
||||||
|
"FC03.KW01": {"text": "Program kończy się PASS; ślad zawiera A-B-A, markery before-HIGH-after, poprawny machine-timer mcause i zgodne zakresy stosów.", "learning_effects": ["FC03.EN01", "FC03.EK01"]}
|
||||||
|
},
|
||||||
|
"educational_requirements": {
|
||||||
|
"FC03.WE01": {
|
||||||
|
"text": "Zaprojektowanie i zbadanie kontrolowanego eksperymentu schedulera FreeRTOS w C.",
|
||||||
|
"label": "FreeRTOS scheduler experiment",
|
||||||
|
"learning_effects": ["FC03.EN01", "FC03.EK01"],
|
||||||
|
"learning_tree": {
|
||||||
|
"schema": "we-learning-tree.v1",
|
||||||
|
"policy": "Każda aktywna kotwica UML wskazuje kod lub zweryfikowany checkpoint.",
|
||||||
|
"ogolne": [{
|
||||||
|
"effect_ref": "FC03.EN01",
|
||||||
|
"display": "EN LOCAL RTOS.03",
|
||||||
|
"source": "LOCAL",
|
||||||
|
"official": "RTOS",
|
||||||
|
"local": "03",
|
||||||
|
"kind": "EN",
|
||||||
|
"tree_id": "FC03.WE01.OG.LOCAL.RTOS.03",
|
||||||
|
"text": "Analizuje reguły wyboru i stany tasków.",
|
||||||
|
"kw": [{"criterion_ref": "FC03.KW01", "display": "KW LOCAL RTOS.03", "source": "LOCAL", "kind": "KW", "official": "RTOS", "local": "03", "text": "Rozdziela priorytet od time slicing."}]
|
||||||
|
}],
|
||||||
|
"zawodowe": [{
|
||||||
|
"effect_ref": "FC03.EK01",
|
||||||
|
"display": "EK LOCAL DBG.03",
|
||||||
|
"source": "LOCAL",
|
||||||
|
"official": "DBG",
|
||||||
|
"local": "03",
|
||||||
|
"kind": "EK",
|
||||||
|
"tree_id": "FC03.WE01.TECH.LOCAL.DBG.03",
|
||||||
|
"text": "Odtwarza scheduler w Hazard3/GDB.",
|
||||||
|
"kw": [{"criterion_ref": "FC03.KW01", "display": "KW LOCAL DBG.03", "source": "LOCAL", "kind": "KW", "official": "DBG", "local": "03", "text": "Łączy checkpoint, UML, rejestry i pamięć."}]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"debug_checkpoints": {
|
||||||
|
"schema": "stem-debug-checkpoints.v1",
|
||||||
|
"semantics": "deterministic-replay",
|
||||||
|
"artifact": {
|
||||||
|
"source": "src/tasks/task01_scheduler.c",
|
||||||
|
"source_git_blob": "34b18bfc69ac51f2ead2cd38ea02f2a5005ac802",
|
||||||
|
"elf": "build/task01_scheduler/prog.elf",
|
||||||
|
"hazard3_elf_sha256": "47a2f8aa02953de388a52d03cba99b25e750309f11d46894ee8a31872a285496",
|
||||||
|
"hazard3_image": "build/task01_scheduler/prog.bin",
|
||||||
|
"hazard3_image_sha256": "59b2cc89a674f6b04896c269a0d25f75a4e7759ad6c236887b0a49843ed2cda6"
|
||||||
|
},
|
||||||
|
"targets": {
|
||||||
|
"hazard3-sim": {"adapter": "hazard3-reset-load-replay.v1", "baseline": "restart-backend", "clean_ram": true}
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"task01.config": {
|
||||||
|
"event_id": "E01",
|
||||||
|
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 1"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_last_checkpoint", "equals": 1}, {"expr": "g_high_probe.handle == 0", "equals": 1}]}
|
||||||
|
},
|
||||||
|
"task01.high-dormant": {
|
||||||
|
"event_id": "E02",
|
||||||
|
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 2 && $a1 == &g_high_probe"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_high_probe.handle != 0", "equals": 1}, {"expr": "g_high_probe.phase", "equals": 1}]}
|
||||||
|
},
|
||||||
|
"task01.priorities": {
|
||||||
|
"event_id": "E03",
|
||||||
|
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 3 && $a1 == &g_high_probe"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_high_probe.observed_initial_priority", "equals": 0}, {"expr": "g_peer_a.observed_priority", "equals": 2}, {"expr": "g_peer_b.observed_priority", "equals": 2}, {"expr": "g_verifier.observed_priority", "equals": 1}]}
|
||||||
|
},
|
||||||
|
"task01.scheduler": {
|
||||||
|
"event_id": "E04",
|
||||||
|
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 4"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_peer_a.handle != 0", "equals": 1}, {"expr": "g_peer_b.handle != 0", "equals": 1}, {"expr": "g_high_probe.handle != 0", "equals": 1}, {"expr": "g_verifier.handle != 0", "equals": 1}]}
|
||||||
|
},
|
||||||
|
"task01.peer-entry": {
|
||||||
|
"event_id": "E05",
|
||||||
|
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 5"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_peer_a.iterations + g_peer_b.iterations >= 0", "equals": 1}]}
|
||||||
|
},
|
||||||
|
"task01.first-tick": {
|
||||||
|
"event_id": "E06",
|
||||||
|
"stop": {"symbol": "task01_tick_checkpoint_committed", "offset": 0, "condition": "g_tick_hook_count == 1"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_tick_hook_count", "equals": 1}, {"expr": "g_first_tick_mcause", "equals": 2147483655}, {"expr": "g_first_tick_sp != 0", "equals": 1}, {"expr": "g_first_tick_mepc != 0", "equals": 1}]}
|
||||||
|
},
|
||||||
|
"task01.timeslice": {
|
||||||
|
"event_id": "E07",
|
||||||
|
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 7 && $a1 == &g_peer_a"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_aba_seen", "equals": 1}, {"expr": "g_aba_task[0]", "equals": 0}, {"expr": "g_aba_task[1]", "equals": 1}, {"expr": "g_aba_task[2]", "equals": 0}, {"expr": "g_peer_changes >= 2", "equals": 1}]}
|
||||||
|
},
|
||||||
|
"task01.before-raise": {
|
||||||
|
"event_id": "E08",
|
||||||
|
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 8 && $a1 == &g_peer_a"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_preemption_order_count", "equals": 1}, {"expr": "g_preemption_order[0]", "equals": 1}, {"expr": "g_peer_after_raise", "equals": 0}]}
|
||||||
|
},
|
||||||
|
"task01.high-preempts": {
|
||||||
|
"event_id": "E09",
|
||||||
|
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 9 && $a1 == &g_high_probe"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_preemption_order_count", "equals": 2}, {"expr": "g_preemption_order[1]", "equals": 2}, {"expr": "g_high_probe.observed_priority", "equals": 3}, {"expr": "g_peer_after_raise", "equals": 0}]}
|
||||||
|
},
|
||||||
|
"task01.after-raise": {
|
||||||
|
"event_id": "E10",
|
||||||
|
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 10 && $a1 == &g_peer_a"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_preemption_order_count", "equals": 3}, {"expr": "g_preemption_order[2]", "equals": 3}, {"expr": "g_stop_peers", "equals": 1}]}
|
||||||
|
},
|
||||||
|
"task01.peers-finished": {
|
||||||
|
"event_id": "E11",
|
||||||
|
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 11"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_peers_finished", "equals": 2}, {"expr": "g_peer_a.handle == 0", "equals": 1}, {"expr": "g_peer_b.handle == 0", "equals": 1}]}
|
||||||
|
},
|
||||||
|
"task01.pass": {
|
||||||
|
"event_id": "E12",
|
||||||
|
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 12 && $a1 == &g_verifier"},
|
||||||
|
"verify": {"expressions": [{"expr": "g_scheduler_pass", "equals": 1}, {"expr": "g_peers_finished", "equals": 2}, {"expr": "g_aba_seen", "equals": 1}, {"expr": "g_preemption_order_count", "equals": 3}, {"expr": "g_tick_hook_count >= 2", "equals": 1}]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"title": "A1 — Context",
|
||||||
|
"order": 10,
|
||||||
|
"content_kind": "prose",
|
||||||
|
"asset_page_mode": "one-per-page",
|
||||||
|
"page_orientation": "portrait",
|
||||||
|
"content_tex": "Timer Hazard3 wywołuje port RV32I, kernel podejmuje decyzję planistyczną, a aplikacja zapisuje ograniczony dowód.",
|
||||||
|
"assets": [{
|
||||||
|
"path": "assets/a1-context.png",
|
||||||
|
"html_path": "assets/a1-context.svg",
|
||||||
|
"source_path": "assets/a1-context.puml",
|
||||||
|
"caption": "A1 CONTEXT — timer, port, scheduler i taski aplikacji.",
|
||||||
|
"label": "fig:a1-context",
|
||||||
|
"alt": "Pionowy kontekst ticka i schedulera FreeRTOS na Hazard3.",
|
||||||
|
"kind": "diagram",
|
||||||
|
"width": 1.0,
|
||||||
|
"page_grid": {"columns": 1, "rows": 1, "page_width": 660, "page_height": 760, "overview": true, "step_tiles": {"timer": 1, "port": 1, "scheduler": 1, "application": 1}},
|
||||||
|
"interactive": {
|
||||||
|
"kind": "uml-class",
|
||||||
|
"storage_key": "fc03-a1-context",
|
||||||
|
"title": "A1 · granice ticka i schedulera",
|
||||||
|
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
|
||||||
|
"block": {"id": "a1-context", "label": "A1 CONTEXT", "description": "Kierunek wykonania i odpowiedzialności."},
|
||||||
|
"phases": [{"id": "tick-boundary", "label": "TICK / SCHEDULER", "steps": [
|
||||||
|
{"id": "timer", "number": 1, "label": "mtime reaches mtimecmp", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "01", "svg_target": "Timer", "code_ref": "include/FreeRTOSConfig.h:4", "description": "Hazard3 generuje machine-timer interrupt.", "evidence": "Adresy mtime i mtimecmp pochodzą z konfiguracji portu."},
|
||||||
|
{"id": "port", "number": 2, "label": "RV32I port handles the tick", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "02", "svg_target": "Port", "code_ref": "vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portASM.S:1", "description": "Port przełącza domenę ISR i wchodzi do kernela.", "evidence": "Tick hook działa w kontekście przerwania."},
|
||||||
|
{"id": "scheduler", "number": 3, "label": "scheduler selects a Ready task", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "03", "svg_target": "Scheduler", "code_ref": "vendor/FreeRTOS-Kernel/tasks.c:1", "description": "Priorytet wybiera klasę, time slicing rotuje równych.", "evidence": "Reguły pozostają własnością kernela."},
|
||||||
|
{"id": "application", "number": 4, "label": "application records evidence", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "04", "svg_target": "Application", "code_ref": "src/tasks/task01_scheduler.c:116", "description": "Aplikacja zapisuje tylko bounded evidence.", "evidence": "Brak drukowania, alokacji i blokowania w tick hook."}
|
||||||
|
]}]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "A2 — Structure",
|
||||||
|
"order": 20,
|
||||||
|
"content_kind": "prose",
|
||||||
|
"asset_page_mode": "one-per-page",
|
||||||
|
"page_orientation": "landscape",
|
||||||
|
"content_tex": "Jawne konteksty C przechowują publiczne uchwyty i pomiary; dwa ograniczone bufory dowodzą niezależnie time slicing oraz wywłaszczenia.",
|
||||||
|
"assets": [{
|
||||||
|
"path": "assets/a2-structure.png",
|
||||||
|
"html_path": "assets/a2-structure.svg",
|
||||||
|
"source_path": "assets/a2-structure.puml",
|
||||||
|
"caption": "A2 STRUCTURE — konteksty tasków i rekordy śladów.",
|
||||||
|
"label": "fig:a2-structure",
|
||||||
|
"alt": "Struktury peer, high probe, verifier i dwa rekordy dowodowe.",
|
||||||
|
"kind": "diagram",
|
||||||
|
"width": 1.0,
|
||||||
|
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"peers": 1, "probe": 1, "verifier": 1, "switch-trace": 1, "order-trace": 1}},
|
||||||
|
"interactive": {
|
||||||
|
"kind": "uml-class",
|
||||||
|
"storage_key": "fc03-a2-structure",
|
||||||
|
"title": "A2 · dane eksperymentu",
|
||||||
|
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
|
||||||
|
"block": {"id": "a2-structure", "label": "A2 STRUCTURE", "description": "Jawne dane aplikacji, nie kopia prywatnych struktur kernela."},
|
||||||
|
"phases": [{"id": "contexts-traces", "label": "CONTEXTS / TRACES", "steps": [
|
||||||
|
{"id": "peers", "number": 1, "label": "two equal-priority peer contexts", "mode": "CODE", "strategy_ref": "code.scheduler-data", "svg_label": "01", "svg_target": "Peers", "code_ref": "include/task01_scheduler.h:23", "description": "Peery dzielą typ, ale mają osobne context, handle i stosy.", "evidence": "configured_priority=2 dla obu."},
|
||||||
|
{"id": "probe", "number": 2, "label": "high probe priority contract 0 to 3", "mode": "CODE", "strategy_ref": "code.scheduler-data", "svg_label": "02", "svg_target": "Probe", "code_ref": "include/task01_scheduler.h:43", "description": "Probe jest Ready, lecz początkowo poniżej verifiera.", "evidence": "initial_priority=0, target_priority=3."},
|
||||||
|
{"id": "verifier", "number": 3, "label": "verifier is deliberately lowest runnable task", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.prepare", "svg_label": "03", "svg_target": "Verify", "code_ref": "src/tasks/task01_scheduler.c:519", "snapshot_ref": "task01.priorities", "description": "Verifier ma priorytet 1.", "evidence": "Nie może opublikować PASS przed zakończeniem peerów."},
|
||||||
|
{"id": "switch-trace", "number": 4, "label": "task-change trace stores id and tick", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "04", "svg_target": "SwitchTrace", "code_ref": "src/tasks/task01_scheduler.c:172", "snapshot_ref": "task01.timeslice", "description": "Powtórzenia w tej samej obserwowanej zmianie nie są logowane.", "evidence": "Zatwierdzony podciąg A/B/A."},
|
||||||
|
{"id": "order-trace", "number": 5, "label": "preemption markers are separate", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.preempt", "svg_label": "05", "svg_target": "OrderTrace", "code_ref": "src/tasks/task01_scheduler.c:305", "snapshot_ref": "task01.after-raise", "description": "Marker projection nie udaje pełnego task trace.", "evidence": "before/HIGH/after."}
|
||||||
|
]}]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "A4 — Application",
|
||||||
|
"order": 30,
|
||||||
|
"content_kind": "prose",
|
||||||
|
"asset_page_mode": "one-per-page",
|
||||||
|
"page_orientation": "landscape",
|
||||||
|
"content_tex": "Cztery taski tworzą kontrolowany eksperyment: dwa równorzędne peery, probe podnoszony do priorytetu 3 i verifier uruchamiany na końcu.",
|
||||||
|
"assets": [{
|
||||||
|
"path": "assets/a4-application.png",
|
||||||
|
"html_path": "assets/a4-application.svg",
|
||||||
|
"source_path": "assets/a4-application.puml",
|
||||||
|
"caption": "A4 APPLICATION — topologia tasków i inwarianty priorytetów.",
|
||||||
|
"label": "fig:a4-application",
|
||||||
|
"alt": "Dwa peery, high probe i verifier w kontrolowanym eksperymencie.",
|
||||||
|
"kind": "diagram",
|
||||||
|
"width": 1.0,
|
||||||
|
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"peer-a": 1, "peer-b": 1, "high": 1, "verifier": 1}},
|
||||||
|
"interactive": {
|
||||||
|
"kind": "uml-class",
|
||||||
|
"storage_key": "fc03-a4-application",
|
||||||
|
"title": "A4 · topologia eksperymentu",
|
||||||
|
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
|
||||||
|
"block": {"id": "a4-application", "label": "A4 APPLICATION", "description": "Konkretny przypadek użycia schedulera."},
|
||||||
|
"phases": [{"id": "priority-topology", "label": "TASKS / PRIORITIES", "steps": [
|
||||||
|
{"id": "peer-a", "number": 1, "label": "peer A runs CPU-bound at priority 2", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.timeslice", "svg_label": "01", "svg_target": "PeerA", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.peer-entry", "description": "Peer A rejestruje zmiany i inicjuje raise.", "evidence": "Własny context, TCB i stos."},
|
||||||
|
{"id": "peer-b", "number": 2, "label": "peer B competes at equal priority", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "02", "svg_target": "PeerB", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.timeslice", "description": "B jest konieczny do podciągu A/B/A.", "evidence": "Oba peery wykonują pracę."},
|
||||||
|
{"id": "high", "number": 3, "label": "peer A raises high probe to 3", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "03", "svg_target": "High", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "High preemptuje caller i self-delete.", "evidence": "Marker HIGH poprzedza after_raise."},
|
||||||
|
{"id": "verifier", "number": 4, "label": "verifier runs last at priority 1", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "04", "svg_target": "Verifier", "code_ref": "src/tasks/task01_scheduler.c:386", "snapshot_ref": "task01.pass", "description": "Verifier publikuje końcowy dowód.", "evidence": "PASS dopiero po self-delete tasków o priorytecie 2/3."}
|
||||||
|
]}]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "A5 — Flow",
|
||||||
|
"order": 40,
|
||||||
|
"content_kind": "prose",
|
||||||
|
"asset_page_mode": "one-per-page",
|
||||||
|
"page_orientation": "landscape",
|
||||||
|
"spread_group": "fc03-scheduler-flow",
|
||||||
|
"spread_label": "FC03 · scheduler experiment",
|
||||||
|
"spread_columns": 2,
|
||||||
|
"content_tex": "E01--E12 tworzą jeden logiczny przebieg rozłożony na dwie poziome strony.",
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"path": "assets/a5-flow-a.png",
|
||||||
|
"html_path": "assets/a5-flow-a.svg",
|
||||||
|
"source_path": "assets/a5-flow-a.puml",
|
||||||
|
"caption": "A5 część 1/2 — konfiguracja, start, pierwszy tick i A-B-A.",
|
||||||
|
"label": "fig:a5-flow-a",
|
||||||
|
"alt": "Pierwsza połowa sekwencji eksperymentu schedulera.",
|
||||||
|
"kind": "diagram",
|
||||||
|
"width": 1.0,
|
||||||
|
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"config": 1, "high-ready": 1, "priorities": 1, "scheduler": 1, "peer-entry": 1, "first-tick": 1, "timeslice": 1}},
|
||||||
|
"interactive": {
|
||||||
|
"kind": "uml-sequence",
|
||||||
|
"storage_key": "fc03-a5-flow-a",
|
||||||
|
"title": "część 1/2 · przygotowanie i time slicing",
|
||||||
|
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
|
||||||
|
"block": {"id": "a5-flow", "label": "A5 FLOW", "description": "Jeden przebieg E01–E12 i wspólni uczestnicy."},
|
||||||
|
"phases": [{"id": "prepare-timeslice", "label": "PREPARE / TIME SLICE", "steps": [
|
||||||
|
{"id": "config", "number": 1, "label": "scheduler configuration committed", "mode": "RUN", "event_id": "E01", "strategy_ref": "run.prepare", "svg_label": "01", "code_ref": "src/tasks/task01_scheduler.c:486", "snapshot_ref": "task01.config", "description": "Eksperyment zaczyna się z czystą RAM.", "evidence": "One core, preemption, time slicing i tick hook są w kodzie konfiguracji."},
|
||||||
|
{"id": "high-ready", "number": 2, "label": "high probe Ready at priority 0", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.prepare", "svg_label": "02", "code_ref": "src/tasks/task01_scheduler.c:488", "snapshot_ref": "task01.high-dormant", "description": "High istnieje, lecz nie może jeszcze wyprzedzić verifiera.", "evidence": "handle != NULL, phase=READY."},
|
||||||
|
{"id": "priorities", "number": 3, "label": "public API confirms 0,2,2,1", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.prepare", "svg_label": "03", "code_ref": "src/tasks/task01_scheduler.c:527", "snapshot_ref": "task01.priorities", "description": "Priorytety są odczytywane przez publiczne API.", "evidence": "high=0, A=2, B=2, verifier=1."},
|
||||||
|
{"id": "scheduler", "number": 4, "label": "vTaskStartScheduler", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.prepare", "svg_label": "04", "code_ref": "src/tasks/task01_scheduler.c:545", "snapshot_ref": "task01.scheduler", "description": "Ostatni checkpoint na startup stack.", "evidence": "Wszystkie handle są ważne."},
|
||||||
|
{"id": "peer-entry", "number": 5, "label": "first equal-priority peer runs", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.timeslice", "svg_label": "05", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.peer-entry", "description": "Pierwszy peer przechodzi do Running.", "evidence": "SP należy do jego task stack."},
|
||||||
|
{"id": "first-tick", "number": 6, "label": "first machine-timer tick observed", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.tick", "svg_label": "06", "code_ref": "src/tasks/task01_scheduler.c:116", "snapshot_ref": "task01.first-tick", "description": "ISR-safe sink przechwytuje pierwszy tick.", "evidence": "mcause=0x80000007, mepc i ISR SP."},
|
||||||
|
{"id": "timeslice", "number": 7, "label": "trace contains A to B to A", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "07", "code_ref": "src/tasks/task01_scheduler.c:290", "snapshot_ref": "task01.timeslice", "description": "Checkpoint emituje peer A po wykryciu podciągu.", "evidence": "A/B/A i niemalejące ticki."}
|
||||||
|
]}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "assets/a5-flow-b.png",
|
||||||
|
"html_path": "assets/a5-flow-b.svg",
|
||||||
|
"source_path": "assets/a5-flow-b.puml",
|
||||||
|
"caption": "A5 część 2/2 — priority raise, natychmiastowe wywłaszczenie i PASS.",
|
||||||
|
"label": "fig:a5-flow-b",
|
||||||
|
"alt": "Druga połowa sekwencji eksperymentu schedulera.",
|
||||||
|
"kind": "diagram",
|
||||||
|
"width": 1.0,
|
||||||
|
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"before-raise": 1, "high-preempts": 1, "after-raise": 1, "peers-finished": 1, "pass": 1}},
|
||||||
|
"interactive": {
|
||||||
|
"kind": "uml-sequence",
|
||||||
|
"storage_key": "fc03-a5-flow-b",
|
||||||
|
"title": "część 2/2 · preemption i zakończenie",
|
||||||
|
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
|
||||||
|
"block": {"id": "a5-flow", "label": "A5 FLOW", "description": "Kontynuacja tych samych tasków i rekordów."},
|
||||||
|
"phases": [{"id": "preempt-finish", "label": "PREEMPT / FINISH", "steps": [
|
||||||
|
{"id": "before-raise", "number": 8, "label": "peer A commits before_raise", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.preempt", "svg_label": "08", "code_ref": "src/tasks/task01_scheduler.c:299", "snapshot_ref": "task01.before-raise", "description": "Pierwszy marker poprzedza vTaskPrioritySet.", "evidence": "order={before}, after flag=0."},
|
||||||
|
{"id": "high-preempts", "number": 9, "label": "high runs before call returns", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "09", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "Zmiana 0→3 czyni high najwyższym Ready taskiem.", "evidence": "order={before,HIGH}, caller after flag=0."},
|
||||||
|
{"id": "after-raise", "number": 10, "label": "peer A commits after_raise and stop", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.preempt", "svg_label": "10", "code_ref": "src/tasks/task01_scheduler.c:303", "snapshot_ref": "task01.after-raise", "description": "Caller wraca dopiero po self-delete high.", "evidence": "order={before,HIGH,after}, stop=1."},
|
||||||
|
{"id": "peers-finished", "number": 11, "label": "both peers clear handles and self-delete", "mode": "RUN", "event_id": "E11", "strategy_ref": "run.state", "svg_label": "11", "code_ref": "src/tasks/task01_scheduler.c:325", "snapshot_ref": "task01.peers-finished", "description": "Każdy peer kończy własny lifetime taska.", "evidence": "g_peers_finished=2, oba handle=NULL."},
|
||||||
|
{"id": "pass", "number": 12, "label": "low-priority verifier publishes PASS", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "12", "code_ref": "src/tasks/task01_scheduler.c:386", "snapshot_ref": "task01.pass", "description": "Verifier sprawdza wszystkie niezależne dowody.", "evidence": "PASS=1 i poprawny exit code."}
|
||||||
|
]}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "A6 — State",
|
||||||
|
"order": 50,
|
||||||
|
"content_kind": "prose",
|
||||||
|
"asset_page_mode": "one-per-page",
|
||||||
|
"page_orientation": "landscape",
|
||||||
|
"content_tex": "Priorytet wybiera najwyższy Ready task; time slicing dotyczy tylko równych priorytetów. Stan diagnostyczny aplikacji nie zastępuje stanu kernela.",
|
||||||
|
"assets": [{
|
||||||
|
"path": "assets/a6-state.png",
|
||||||
|
"html_path": "assets/a6-state.svg",
|
||||||
|
"source_path": "assets/a6-state.puml",
|
||||||
|
"caption": "A6 STATE — przejścia high probe, peerów i verifiera.",
|
||||||
|
"label": "fig:a6-state",
|
||||||
|
"alt": "Stany Ready, Running i Deleted oraz zmiana priorytetu high probe.",
|
||||||
|
"kind": "diagram",
|
||||||
|
"width": 1.0,
|
||||||
|
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"high-low": 1, "high-ready": 1, "high-run": 1, "high-deleted": 1, "peers": 1, "peers-done": 1, "verify-ready": 1, "verify-run": 1}},
|
||||||
|
"interactive": {
|
||||||
|
"kind": "uml-class",
|
||||||
|
"storage_key": "fc03-a6-state",
|
||||||
|
"title": "A6 · priorytet i stan",
|
||||||
|
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
|
||||||
|
"block": {"id": "a6-state", "label": "A6 STATE", "description": "Ready nie oznacza Running."},
|
||||||
|
"phases": [
|
||||||
|
{"id": "high-probe", "label": "HIGH PROBE", "steps": [
|
||||||
|
{"id": "high-low", "number": 1, "label": "high Ready at priority 0", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.state", "svg_label": "01", "svg_target": "HighLow", "code_ref": "src/tasks/task01_scheduler.c:497", "snapshot_ref": "task01.high-dormant", "description": "Task istnieje, ale nie jest wybrany.", "evidence": "phase=READY, priority=0."},
|
||||||
|
{"id": "high-ready", "number": 2, "label": "priority changes from 0 to 3", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.preempt", "svg_label": "02", "svg_target": "HighReady", "code_ref": "src/tasks/task01_scheduler.c:300", "snapshot_ref": "task01.before-raise", "description": "API zmienia pozycję taska w polityce wyboru.", "evidence": "Caller zapisał before marker."},
|
||||||
|
{"id": "high-run", "number": 3, "label": "high selected and Running", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "03", "svg_target": "HighRun", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "Najwyższy Ready task wywłaszcza caller.", "evidence": "priority=3, PC/SP należą do high."},
|
||||||
|
{"id": "high-deleted", "number": 4, "label": "high self-deletes", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.state", "svg_label": "04", "code_ref": "src/tasks/task01_scheduler.c:366", "snapshot_ref": "task01.after-raise", "description": "High czyści handle przed vTaskDelete(NULL).", "evidence": "Caller może wrócić i zapisać after."}
|
||||||
|
]},
|
||||||
|
{"id": "peers-verifier", "label": "PEERS / VERIFIER", "steps": [
|
||||||
|
{"id": "peers", "number": 5, "label": "peers alternate Ready and Running", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "05", "svg_target": "Peers", "code_ref": "src/tasks/task01_scheduler.c:279", "snapshot_ref": "task01.timeslice", "description": "Tick rotuje równorzędne peery.", "evidence": "A/B/A."},
|
||||||
|
{"id": "peers-done", "number": 6, "label": "both peers complete and delete", "mode": "RUN", "event_id": "E11", "strategy_ref": "run.state", "svg_label": "06", "code_ref": "src/tasks/task01_scheduler.c:325", "snapshot_ref": "task01.peers-finished", "description": "Stop request nie usuwa taska z zewnątrz.", "evidence": "Każdy peer self-delete."},
|
||||||
|
{"id": "verify-ready", "number": 7, "label": "verifier waits as lower Ready task", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.prepare", "svg_label": "07", "svg_target": "VerifyReady", "code_ref": "src/tasks/task01_scheduler.c:519", "snapshot_ref": "task01.priorities", "description": "Priorytet 1 przegrywa z peerami 2.", "evidence": "Verifier nie wykonuje się przed stop."},
|
||||||
|
{"id": "verify-run", "number": 8, "label": "verifier runs and publishes PASS", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "08", "code_ref": "src/tasks/task01_scheduler.c:386", "snapshot_ref": "task01.pass", "description": "Po usunięciu wyższych tasków verifier staje się najwyższy.", "evidence": "PASS=1."}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "A7 — Runtime",
|
||||||
|
"order": 60,
|
||||||
|
"content_kind": "prose",
|
||||||
|
"asset_page_mode": "one-per-page",
|
||||||
|
"page_orientation": "landscape",
|
||||||
|
"content_tex": "Dowód runtime łączy timer CSRs, ISR stack, Ready lists obserwowane w debuggerze, TCB/stosy tasków oraz aplikacyjne rekordy commit.",
|
||||||
|
"assets": [{
|
||||||
|
"path": "assets/a7-runtime.png",
|
||||||
|
"html_path": "assets/a7-runtime.svg",
|
||||||
|
"source_path": "assets/a7-runtime.puml",
|
||||||
|
"caption": "A7 RUNTIME — timer, ISR, scheduler, taski i końcowy dowód.",
|
||||||
|
"label": "fig:a7-runtime",
|
||||||
|
"alt": "Runtime evidence od timer CSRs do śladu aplikacji.",
|
||||||
|
"kind": "diagram",
|
||||||
|
"width": 1.0,
|
||||||
|
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"timer": 1, "isr": 1, "kernel": 1, "peer": 1, "high": 1, "evidence": 1}},
|
||||||
|
"interactive": {
|
||||||
|
"kind": "uml-class",
|
||||||
|
"storage_key": "fc03-a7-runtime",
|
||||||
|
"title": "A7 · mapa dowodów runtime",
|
||||||
|
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
|
||||||
|
"block": {"id": "a7-runtime", "label": "A7 RUNTIME", "description": "Kernel internals są tylko obserwacją debuggera."},
|
||||||
|
"phases": [
|
||||||
|
{"id": "timer-cpu", "label": "TIMER / CPU", "steps": [
|
||||||
|
{"id": "timer", "number": 1, "label": "timer CSRs define the interrupt", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.tick", "svg_label": "01", "svg_target": "Timer", "code_ref": "include/FreeRTOSConfig.h:4", "snapshot_ref": "task01.first-tick", "description": "Mtime/mtimecmp prowadzą do machine-timer trap.", "evidence": "mcause=0x80000007."},
|
||||||
|
{"id": "isr", "number": 2, "label": "ISR evidence captures tick PC and SP", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.tick", "svg_label": "02", "svg_target": "ISR", "code_ref": "src/tasks/task01_scheduler.c:116", "snapshot_ref": "task01.first-tick", "description": "Mepc opisuje przerwany task, SP domenę ISR.", "evidence": "Niezerowe mepc/SP i one-shot checkpoint."},
|
||||||
|
{"id": "kernel", "number": 3, "label": "Ready lists are debugger-only evidence", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.runtime", "svg_label": "03", "svg_target": "Kernel", "code_ref": "vendor/FreeRTOS-Kernel/tasks.c:1", "snapshot_ref": "task01.timeslice", "description": "Aplikacja nie odczytuje pxCurrentTCB.", "evidence": "Publiczne rekordy wystarczają do PASS."}
|
||||||
|
]},
|
||||||
|
{"id": "tasks-evidence", "label": "TASKS / EVIDENCE", "steps": [
|
||||||
|
{"id": "peer", "number": 4, "label": "peer handle TCB stack PC and SP", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.runtime", "svg_label": "04", "svg_target": "Peer", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.peer-entry", "description": "Context, TCB i stack są osobnymi obszarami.", "evidence": "created_handle==tcb_address i SP w stack range."},
|
||||||
|
{"id": "high", "number": 5, "label": "high runs at changed priority", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "05", "svg_target": "High", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "PC/SP i run_tick identyfikują wybrany task.", "evidence": "priority=3 i marker HIGH."},
|
||||||
|
{"id": "evidence", "number": 6, "label": "committed evidence binds source and ELF", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "06", "svg_target": "Evidence", "code_ref": "src/tasks/task01_scheduler.c:450", "snapshot_ref": "task01.pass", "description": "Końcowy snapshot wiąże wynik z artefaktami.", "evidence": "event_id, source blob, ELF/image hash, PASS i timestamp."}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{"title": "Task01 — Scheduler experiment", "order": 70, "content_kind": "tasks", "task_refs": ["task01"]}
|
||||||
|
],
|
||||||
|
"tasks": {
|
||||||
|
"task01": {
|
||||||
|
"title": "Kontrolowany eksperyment schedulera FreeRTOS",
|
||||||
|
"uuid": "2214a13f-34c6-40a1-a561-70a00ec98285",
|
||||||
|
"prompt_tex": "Przejdź po A1, A2, A4, A5, A6 i A7. Dla kroków RUN użyj Enter lub F2 i udowodnij oddzielnie time slicing A-B-A oraz natychmiastowe wywłaszczenie before-HIGH-after.",
|
||||||
|
"criterion": "Program wypisuje PASS; trace zawiera A-B-A; marker projection to before-HIGH-after; pierwszy tick ma machine-timer mcause; TCB i stack są spójne.",
|
||||||
|
"conclusion_tex": "Priorytet wybiera najwyższy Ready task. Time slicing rotuje wyłącznie taski o tym samym priorytecie, a uczynienie wyższego taska Ready może wywłaszczyć caller przed powrotem API.",
|
||||||
|
"flow": [
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "scheduler-model",
|
||||||
|
"title": "A — model ticka i wyboru taska",
|
||||||
|
"content_tex": "Najpierw odczytaj A1, A2 i A4 oraz zapisz przewidywaną kolejność.",
|
||||||
|
"steps": [
|
||||||
|
{"id": "read-priorities", "title": "Odczytaj kontrakt priorytetów.", "content_tex": "Wyjaśnij, dlaczego high=0, peery=2, verifier=1 nie powodują przedwczesnego uruchomienia high ani verifiera."},
|
||||||
|
{"id": "predict-traces", "title": "Przewidź dwa ślady.", "content_tex": "Rozdziel task-change trace A-B-A od marker projection before-HIGH-after."}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"id": "hazard3-replay",
|
||||||
|
"title": "B — deterministyczny replay E01–E12",
|
||||||
|
"content_tex": "Każdy RUN replay zaczyna się od czystej RAM i zweryfikowanego obrazu.",
|
||||||
|
"steps": [
|
||||||
|
{"id": "follow-tick", "title": "Zbadaj pierwszy tick.", "content_tex": "Na E06 zapisz mcause, mepc, ISR SP i tick."},
|
||||||
|
{"id": "follow-timeslice", "title": "Zbadaj A-B-A.", "content_tex": "Na E07 zapisz triplet tasków, ticki i liczbę obserwowanych zmian."},
|
||||||
|
{"id": "follow-preemption", "title": "Zbadaj natychmiastowe wywłaszczenie.", "content_tex": "Porównaj E08, E09 i E10; pokaż, że HIGH wykonał się przed after_raise."},
|
||||||
|
{"id": "finish-pass", "title": "Zamknij dowód.", "content_tex": "Na E11 sprawdź self-delete peerów, a na E12 końcowe inwarianty i PASS."}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "exercise",
|
||||||
|
"id": "no-timeslicing-bounded-observation",
|
||||||
|
"title": "Ćwiczenie — bounded observation bez time slicing",
|
||||||
|
"prompt_tex": "Ustaw configUSE_TIME_SLICING=0, nie zmieniając dwóch CPU-bound peerów o priorytecie 2. Uruchom tylko ograniczoną obserwację debuggera; nie oczekuj PASS. Zapisz, który peer pozostaje Running, jaki jest tick count i dlaczego drugi może być głodzony. Następnie przywróć konfigurację.",
|
||||||
|
"evidence_tex": "Timestampowany snapshot PC/SP, tick count i obu liczników iteracji oraz krótkie wyjaśnienie expected noncompletion.",
|
||||||
|
"criterion": "Uczeń nie interpretuje braku PASS jako błędu harnessu i poprawnie wiąże głodzenie z brakiem rotacji równych priorytetów.",
|
||||||
|
"based_on": ["scheduler-model", "hazard3-replay"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"educational_requirement_refs": ["FC03.WE01"],
|
||||||
|
"learning_effect_refs": ["FC03.EN01", "FC03.EK01"],
|
||||||
|
"assessment_criterion_ref": "FC03.KW01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tasks_order": ["task01"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
nm=${RISCV_NM:-riscv64-unknown-elf-nm}
|
||||||
|
readelf=${RISCV_READELF:-riscv64-unknown-elf-readelf}
|
||||||
|
elf=build/task01_scheduler/prog.elf
|
||||||
|
|
||||||
|
if "$nm" --defined-only "$elf" | awk '{print $3}' | grep -Eq '^_Z|^__cxa_|^_Unwind_'; then
|
||||||
|
echo "unexpected C++ or unwind symbol in the C teaching ELF" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if "$readelf" -S "$elf" | grep -Eq '\.init_array|\.eh_frame|\.gcc_except_table'; then
|
||||||
|
echo "unexpected C++ initialization or unwind section in $elf" >&2
|
||||||
|
"$readelf" -S "$elf" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for symbol in peer_task_entry high_probe_task_entry verifier_task_entry \
|
||||||
|
vApplicationTickHook task01_scheduler_checkpoint \
|
||||||
|
task01_scheduler_checkpoint_committed task01_tick_checkpoint_committed \
|
||||||
|
xTaskCreate vTaskPrioritySet uxTaskPriorityGet vTaskDelete; do
|
||||||
|
if ! "$nm" --defined-only "$elf" | grep -Eq "[[:space:]]${symbol}$"; then
|
||||||
|
echo "required C/FreeRTOS symbol is missing: $symbol" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "PASS ABI: freestanding C11 scheduler experiment, no C++ runtime"
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
tb=${1:?usage: check_determinism.sh /path/to/tb [build-dir]}
|
||||||
|
build_dir=${2:-build}
|
||||||
|
artifact="$build_dir/task01_scheduler/prog.bin"
|
||||||
|
reference="$build_dir/determinism-1.log"
|
||||||
|
|
||||||
|
run_once() {
|
||||||
|
output=$1
|
||||||
|
"$tb" --bin "$artifact" --cycles 3000000 --cpuret >"$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_once "$reference"
|
||||||
|
run_once "$build_dir/determinism-2.log"
|
||||||
|
run_once "$build_dir/determinism-3.log"
|
||||||
|
cmp "$reference" "$build_dir/determinism-2.log"
|
||||||
|
cmp "$reference" "$build_dir/determinism-3.log"
|
||||||
|
echo "PASS determinism: three clean Hazard3 processes produced identical evidence"
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
script_dir=$(CDPATH= cd "$(dirname "$0")" && pwd)
|
||||||
|
repo_root=$(CDPATH= cd "$script_dir/.." && pwd)
|
||||||
|
tex_path="$repo_root/doc/generated/main.tex"
|
||||||
|
|
||||||
|
read_tex_command() {
|
||||||
|
sed -n "s/^\\\\newcommand{\\\\$1}{\\([^}]*\\)}$/\\1/p" "$tex_path" | head -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
publisher=mpabi
|
||||||
|
area=inf
|
||||||
|
series=$(read_tex_command CardSeries)
|
||||||
|
number=$(read_tex_command CardNumber)
|
||||||
|
slug=$(read_tex_command CardSlug)
|
||||||
|
version=$(read_tex_command CardVersion)
|
||||||
|
uuid=$(read_tex_command DocumentUUID)
|
||||||
|
|
||||||
|
if [ -z "$publisher" ] || [ -z "$area" ] || [ -z "$series" ] || \
|
||||||
|
[ -z "$number" ] || [ -z "$slug" ] || [ -z "$version" ] || [ -z "$uuid" ]; then
|
||||||
|
echo "missing card metadata in $tex_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
commit=${GITEA_SHA:-${GITHUB_SHA:-}}
|
||||||
|
if [ -z "$commit" ]; then
|
||||||
|
git_root=$(git -C "$repo_root" rev-parse --show-toplevel 2>/dev/null || true)
|
||||||
|
if [ "$git_root" = "$repo_root" ]; then
|
||||||
|
commit=$(git -C "$repo_root" rev-parse HEAD)
|
||||||
|
else
|
||||||
|
commit=local
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
short_commit=$(printf '%s' "$commit" | cut -c1-12)
|
||||||
|
meta="$repo_root/doc/build-meta.tex"
|
||||||
|
trap 'rm -f "$meta"' EXIT HUP INT TERM
|
||||||
|
printf '\\newcommand{\\BuildCommit}{%s}\n' "$short_commit" > "$meta"
|
||||||
|
|
||||||
|
out="$repo_root/doc/pdf"
|
||||||
|
mkdir -p "$out"
|
||||||
|
find "$out" -maxdepth 1 -type f -name '*.pdf' -delete
|
||||||
|
(
|
||||||
|
cd "$repo_root/doc/generated"
|
||||||
|
latexmk -pdf -interaction=nonstopmode -halt-on-error -outdir="$out" main.tex
|
||||||
|
)
|
||||||
|
|
||||||
|
target="$out/$publisher-$area-$series-$number-$slug-$version-$uuid.pdf"
|
||||||
|
mv "$out/main.pdf" "$target"
|
||||||
|
find "$out" -maxdepth 1 -type f \( -name '*.aux' -o -name '*.log' -o \
|
||||||
|
-name '*.out' -o -name '*.fls' -o -name '*.fdb_latexmk' \) -delete
|
||||||
|
printf '%s\n' "$target"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
tb=${1:?usage: run_sim.sh /path/to/tb [build-dir]}
|
||||||
|
build_dir=${2:-build}
|
||||||
|
|
||||||
|
echo "== task01_scheduler =="
|
||||||
|
"$tb" --bin "$build_dir/task01_scheduler/prog.bin" --cycles 3000000 --cpuret
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
.section .text
|
||||||
|
.global _start
|
||||||
|
.type _start, @function
|
||||||
|
|
||||||
|
_start:
|
||||||
|
.option push
|
||||||
|
.option norelax
|
||||||
|
la gp, __global_pointer$
|
||||||
|
.option pop
|
||||||
|
|
||||||
|
/* Hazard3 init.S has already installed the bootstrap stack. */
|
||||||
|
la a0, __bss_start
|
||||||
|
la a1, __bss_end
|
||||||
|
1:
|
||||||
|
bgeu a0, a1, 2f
|
||||||
|
sb zero, 0(a0)
|
||||||
|
addi a0, a0, 1
|
||||||
|
j 1b
|
||||||
|
2:
|
||||||
|
call main
|
||||||
|
tail _exit
|
||||||
|
|
||||||
|
.size _start, .-_start
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
.section .text.hazard3_freertos_traps, "ax", @progbits
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hazard3 init.S owns mtvec and exposes weak vector targets. Strong symbols
|
||||||
|
* below route exceptions (including ecall/yield) and the machine timer IRQ to
|
||||||
|
* the official FreeRTOS RISC-V trap handler.
|
||||||
|
*/
|
||||||
|
.global handle_exception
|
||||||
|
.type handle_exception, @function
|
||||||
|
handle_exception:
|
||||||
|
j freertos_risc_v_trap_handler
|
||||||
|
.size handle_exception, .-handle_exception
|
||||||
|
|
||||||
|
.global isr_machine_timer
|
||||||
|
.type isr_machine_timer, @function
|
||||||
|
isr_machine_timer:
|
||||||
|
j freertos_risc_v_trap_handler
|
||||||
|
.size isr_machine_timer, .-isr_machine_timer
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "lab_freertos.h"
|
||||||
|
#include "lab_io.h"
|
||||||
|
|
||||||
|
/* Official heap_4.c uses this application-owned arena. */
|
||||||
|
__attribute__( ( aligned( 16 ) ) ) uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
|
||||||
|
volatile uint32_t g_failure_code;
|
||||||
|
|
||||||
|
int lab_address_in_heap( const volatile void * address )
|
||||||
|
{
|
||||||
|
const uintptr_t value = ( uintptr_t ) address;
|
||||||
|
const uintptr_t first = ( uintptr_t ) &ucHeap[ 0 ];
|
||||||
|
const uintptr_t past_last = ( uintptr_t ) &ucHeap[ configTOTAL_HEAP_SIZE ];
|
||||||
|
|
||||||
|
return ( value >= first ) && ( value < past_last );
|
||||||
|
}
|
||||||
|
|
||||||
|
void lab_heap_initialize( void )
|
||||||
|
{
|
||||||
|
void * probe = pvPortMalloc( 1U );
|
||||||
|
|
||||||
|
if( probe == NULL )
|
||||||
|
{
|
||||||
|
lab_fail( 0xA6000002UL );
|
||||||
|
}
|
||||||
|
vPortFree( probe );
|
||||||
|
}
|
||||||
|
|
||||||
|
void lab_fail( uint32_t code )
|
||||||
|
{
|
||||||
|
g_failure_code = code;
|
||||||
|
lab_puts( "FAIL\n" );
|
||||||
|
lab_put_u32( code );
|
||||||
|
lab_exit( code );
|
||||||
|
}
|
||||||
|
|
||||||
|
void lab_assert_fail( const char * file, int line )
|
||||||
|
{
|
||||||
|
( void ) file;
|
||||||
|
lab_fail( 0xA5000000UL | ( ( uint32_t ) line & 0xFFFFUL ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void vApplicationMallocFailedHook( void )
|
||||||
|
{
|
||||||
|
lab_fail( 0xA6000001UL );
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef LAB_FREERTOS_H
|
||||||
|
#define LAB_FREERTOS_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
|
||||||
|
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
|
||||||
|
extern volatile uint32_t g_failure_code;
|
||||||
|
|
||||||
|
int lab_address_in_heap( const volatile void * address );
|
||||||
|
void lab_heap_initialize( void );
|
||||||
|
void lab_fail( uint32_t code ) __attribute__( ( noreturn ) );
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "lab_io.h"
|
||||||
|
|
||||||
|
#define H3_IO_BASE ( 0xC0000000UL )
|
||||||
|
#define H3_IO_PRINT_CHAR ( *( ( volatile uint32_t * ) ( H3_IO_BASE + 0x0UL ) ) )
|
||||||
|
#define H3_IO_PRINT_U32 ( *( ( volatile uint32_t * ) ( H3_IO_BASE + 0x4UL ) ) )
|
||||||
|
#define H3_IO_EXIT ( *( ( volatile uint32_t * ) ( H3_IO_BASE + 0x8UL ) ) )
|
||||||
|
|
||||||
|
void lab_puts( const char * text )
|
||||||
|
{
|
||||||
|
while( *text != '\0' )
|
||||||
|
{
|
||||||
|
H3_IO_PRINT_CHAR = ( uint32_t ) ( unsigned char ) *text;
|
||||||
|
++text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void lab_put_u32( uint32_t value )
|
||||||
|
{
|
||||||
|
H3_IO_PRINT_U32 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lab_exit( uint32_t code )
|
||||||
|
{
|
||||||
|
H3_IO_EXIT = code;
|
||||||
|
for( ; ; )
|
||||||
|
{
|
||||||
|
__asm volatile ( "wfi" );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef LAB_IO_H
|
||||||
|
#define LAB_IO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void lab_puts( const char * text );
|
||||||
|
void lab_put_u32( uint32_t value );
|
||||||
|
void lab_exit( uint32_t code ) __attribute__( ( noreturn ) );
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
void * memcpy( void * destination, const void * source, size_t count )
|
||||||
|
{
|
||||||
|
unsigned char * out = ( unsigned char * ) destination;
|
||||||
|
const unsigned char * in = ( const unsigned char * ) source;
|
||||||
|
|
||||||
|
for( size_t index = 0; index < count; ++index )
|
||||||
|
{
|
||||||
|
out[ index ] = in[ index ];
|
||||||
|
}
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
void * memset( void * destination, int value, size_t count )
|
||||||
|
{
|
||||||
|
unsigned char * out = ( unsigned char * ) destination;
|
||||||
|
|
||||||
|
for( size_t index = 0; index < count; ++index )
|
||||||
|
{
|
||||||
|
out[ index ] = ( unsigned char ) value;
|
||||||
|
}
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strlen( const char * text )
|
||||||
|
{
|
||||||
|
size_t length = 0;
|
||||||
|
|
||||||
|
while( text[ length ] != '\0' )
|
||||||
|
{
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
@@ -0,0 +1,547 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "lab_freertos.h"
|
||||||
|
#include "lab_io.h"
|
||||||
|
#include "task01_scheduler.h"
|
||||||
|
#include "task01_scheduler_model.h"
|
||||||
|
|
||||||
|
#define MCAUSE_MACHINE_TIMER_INTERRUPT ( 0x80000007UL )
|
||||||
|
|
||||||
|
volatile uint32_t g_last_checkpoint;
|
||||||
|
volatile uintptr_t g_checkpoint_subject;
|
||||||
|
|
||||||
|
volatile uint32_t g_switch_task[ SCHED_TRACE_CAPACITY ];
|
||||||
|
volatile TickType_t g_switch_tick[ SCHED_TRACE_CAPACITY ];
|
||||||
|
volatile uint32_t g_switch_count;
|
||||||
|
volatile uint32_t g_last_peer_id = UINT32_MAX;
|
||||||
|
volatile uint32_t g_peer_changes;
|
||||||
|
volatile uint32_t g_aba_task[ 3 ];
|
||||||
|
volatile TickType_t g_aba_tick[ 3 ];
|
||||||
|
volatile uint32_t g_aba_seen;
|
||||||
|
|
||||||
|
volatile uint32_t g_preemption_order[ 3 ];
|
||||||
|
volatile uint32_t g_preemption_order_count;
|
||||||
|
volatile uint32_t g_priority_raise_started;
|
||||||
|
volatile uint32_t g_peer_after_raise;
|
||||||
|
volatile uint32_t g_stop_peers;
|
||||||
|
volatile uint32_t g_peers_finished;
|
||||||
|
volatile uint32_t g_verifier_started;
|
||||||
|
|
||||||
|
volatile uint32_t g_tick_hook_count;
|
||||||
|
volatile TickType_t g_first_tick_count;
|
||||||
|
volatile uintptr_t g_first_tick_sp;
|
||||||
|
volatile uintptr_t g_first_tick_mepc;
|
||||||
|
volatile uint32_t g_first_tick_mcause;
|
||||||
|
|
||||||
|
volatile uint32_t g_scheduler_pass;
|
||||||
|
|
||||||
|
PeerContext g_peer_a = {
|
||||||
|
.id = SCHED_PEER_A,
|
||||||
|
.configured_priority = SCHED_PEER_PRIORITY,
|
||||||
|
.handle = NULL,
|
||||||
|
.phase = SCHED_PHASE_PREPARED
|
||||||
|
};
|
||||||
|
|
||||||
|
PeerContext g_peer_b = {
|
||||||
|
.id = SCHED_PEER_B,
|
||||||
|
.configured_priority = SCHED_PEER_PRIORITY,
|
||||||
|
.handle = NULL,
|
||||||
|
.phase = SCHED_PHASE_PREPARED
|
||||||
|
};
|
||||||
|
|
||||||
|
HighProbeContext g_high_probe = {
|
||||||
|
.initial_priority = SCHED_PROBE_INITIAL_PRIORITY,
|
||||||
|
.target_priority = SCHED_PROBE_TARGET_PRIORITY,
|
||||||
|
.handle = NULL,
|
||||||
|
.phase = SCHED_PHASE_PREPARED
|
||||||
|
};
|
||||||
|
|
||||||
|
VerifierContext g_verifier = {
|
||||||
|
.configured_priority = SCHED_VERIFY_PRIORITY,
|
||||||
|
.handle = NULL,
|
||||||
|
.phase = SCHED_PHASE_PREPARED
|
||||||
|
};
|
||||||
|
|
||||||
|
static uintptr_t read_sp( void )
|
||||||
|
{
|
||||||
|
uintptr_t value;
|
||||||
|
|
||||||
|
__asm__ volatile( "mv %0, sp" : "=r"( value ) );
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uintptr_t read_mepc( void )
|
||||||
|
{
|
||||||
|
uintptr_t value;
|
||||||
|
|
||||||
|
__asm__ volatile( "csrr %0, mepc" : "=r"( value ) );
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t read_mcause( void )
|
||||||
|
{
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
__asm__ volatile( "csrr %0, mcause" : "=r"( value ) );
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__( ( noinline, used ) )
|
||||||
|
void task01_scheduler_checkpoint_committed( uint32_t point,
|
||||||
|
const void * subject )
|
||||||
|
{
|
||||||
|
( void ) point;
|
||||||
|
( void ) subject;
|
||||||
|
__asm__ volatile( "" ::: "memory" );
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__( ( noinline, used ) )
|
||||||
|
void task01_scheduler_checkpoint( uint32_t point, const void * subject )
|
||||||
|
{
|
||||||
|
g_last_checkpoint = point;
|
||||||
|
g_checkpoint_subject = ( uintptr_t ) subject;
|
||||||
|
__asm__ volatile( "" ::: "memory" );
|
||||||
|
task01_scheduler_checkpoint_committed( point, subject );
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__( ( noinline, used ) )
|
||||||
|
void task01_tick_checkpoint_committed( void )
|
||||||
|
{
|
||||||
|
__asm__ volatile( "" ::: "memory" );
|
||||||
|
}
|
||||||
|
|
||||||
|
void vApplicationTickHook( void )
|
||||||
|
{
|
||||||
|
++g_tick_hook_count;
|
||||||
|
|
||||||
|
if( g_tick_hook_count == 1U )
|
||||||
|
{
|
||||||
|
g_first_tick_count = xTaskGetTickCountFromISR();
|
||||||
|
g_first_tick_sp = read_sp();
|
||||||
|
g_first_tick_mepc = read_mepc();
|
||||||
|
g_first_tick_mcause = read_mcause();
|
||||||
|
g_last_checkpoint = 6U;
|
||||||
|
g_checkpoint_subject = ( uintptr_t ) &g_tick_hook_count;
|
||||||
|
__asm__ volatile( "" ::: "memory" );
|
||||||
|
task01_tick_checkpoint_committed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void capture_peer_runtime( PeerContext * peer )
|
||||||
|
{
|
||||||
|
TaskStatus_t info;
|
||||||
|
uintptr_t first;
|
||||||
|
uintptr_t last;
|
||||||
|
|
||||||
|
vTaskGetInfo( NULL, &info, pdFALSE, eRunning );
|
||||||
|
first = ( uintptr_t ) info.pxStackBase;
|
||||||
|
last = ( uintptr_t ) info.pxEndOfStack;
|
||||||
|
|
||||||
|
peer->observed_priority = uxTaskPriorityGet( NULL );
|
||||||
|
peer->tcb_address = ( uintptr_t ) info.xHandle;
|
||||||
|
peer->entry_sp = read_sp();
|
||||||
|
peer->stack_low = first < last ? first : last;
|
||||||
|
peer->stack_high = first < last ? last : first;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void capture_probe_runtime( HighProbeContext * probe )
|
||||||
|
{
|
||||||
|
TaskStatus_t info;
|
||||||
|
uintptr_t first;
|
||||||
|
uintptr_t last;
|
||||||
|
|
||||||
|
vTaskGetInfo( NULL, &info, pdFALSE, eRunning );
|
||||||
|
first = ( uintptr_t ) info.pxStackBase;
|
||||||
|
last = ( uintptr_t ) info.pxEndOfStack;
|
||||||
|
|
||||||
|
probe->observed_priority = uxTaskPriorityGet( NULL );
|
||||||
|
probe->tcb_address = ( uintptr_t ) info.xHandle;
|
||||||
|
probe->entry_sp = read_sp();
|
||||||
|
probe->stack_low = first < last ? first : last;
|
||||||
|
probe->stack_high = first < last ? last : first;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int volatile_trace_has_aba( void )
|
||||||
|
{
|
||||||
|
uint32_t index;
|
||||||
|
|
||||||
|
for( index = 2U; index < g_switch_count; ++index )
|
||||||
|
{
|
||||||
|
if( g_switch_task[ index - 2U ] == SCHED_PEER_A &&
|
||||||
|
g_switch_task[ index - 1U ] == SCHED_PEER_B &&
|
||||||
|
g_switch_task[ index ] == SCHED_PEER_A )
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BaseType_t record_peer_switch( uint32_t id, TickType_t tick )
|
||||||
|
{
|
||||||
|
BaseType_t aba_completed_by_a = pdFALSE;
|
||||||
|
|
||||||
|
taskENTER_CRITICAL();
|
||||||
|
if( g_last_peer_id != id )
|
||||||
|
{
|
||||||
|
if( g_last_peer_id != UINT32_MAX )
|
||||||
|
{
|
||||||
|
++g_peer_changes;
|
||||||
|
}
|
||||||
|
g_last_peer_id = id;
|
||||||
|
|
||||||
|
if( g_switch_count < SCHED_TRACE_CAPACITY )
|
||||||
|
{
|
||||||
|
g_switch_task[ g_switch_count ] = id;
|
||||||
|
g_switch_tick[ g_switch_count ] = tick;
|
||||||
|
++g_switch_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( id == SCHED_PEER_A &&
|
||||||
|
g_aba_seen == 0U &&
|
||||||
|
volatile_trace_has_aba() != 0 )
|
||||||
|
{
|
||||||
|
const uint32_t last = g_switch_count - 1U;
|
||||||
|
|
||||||
|
g_aba_task[ 0 ] = g_switch_task[ last - 2U ];
|
||||||
|
g_aba_task[ 1 ] = g_switch_task[ last - 1U ];
|
||||||
|
g_aba_task[ 2 ] = g_switch_task[ last ];
|
||||||
|
g_aba_tick[ 0 ] = g_switch_tick[ last - 2U ];
|
||||||
|
g_aba_tick[ 1 ] = g_switch_tick[ last - 1U ];
|
||||||
|
g_aba_tick[ 2 ] = g_switch_tick[ last ];
|
||||||
|
g_aba_seen = 1U;
|
||||||
|
aba_completed_by_a = pdTRUE;
|
||||||
|
}
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
|
||||||
|
return aba_completed_by_a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void append_preemption_marker( uint32_t marker )
|
||||||
|
{
|
||||||
|
taskENTER_CRITICAL();
|
||||||
|
if( g_preemption_order_count < 3U )
|
||||||
|
{
|
||||||
|
g_preemption_order[ g_preemption_order_count ] = marker;
|
||||||
|
++g_preemption_order_count;
|
||||||
|
}
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sp_inside_peer_stack( const PeerContext * peer )
|
||||||
|
{
|
||||||
|
return peer->entry_sp >= peer->stack_low &&
|
||||||
|
peer->entry_sp <= peer->stack_high;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sp_inside_probe_stack( const HighProbeContext * probe )
|
||||||
|
{
|
||||||
|
return probe->entry_sp >= probe->stack_low &&
|
||||||
|
probe->entry_sp <= probe->stack_high;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__( ( noinline, used ) )
|
||||||
|
void peer_task_entry( void * pv_parameters )
|
||||||
|
{
|
||||||
|
PeerContext * peer = ( PeerContext * ) pv_parameters;
|
||||||
|
TickType_t observed_tick;
|
||||||
|
BaseType_t emit_entry = pdFALSE;
|
||||||
|
|
||||||
|
if( peer == NULL || peer->id > SCHED_PEER_B )
|
||||||
|
{
|
||||||
|
lab_fail( 0xFC030101U );
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_peer_runtime( peer );
|
||||||
|
peer->phase = SCHED_PHASE_RUNNING;
|
||||||
|
peer->first_tick = xTaskGetTickCount();
|
||||||
|
observed_tick = peer->first_tick;
|
||||||
|
|
||||||
|
taskENTER_CRITICAL();
|
||||||
|
if( g_peer_a.iterations == 0U && g_peer_b.iterations == 0U )
|
||||||
|
{
|
||||||
|
emit_entry = pdTRUE;
|
||||||
|
}
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
|
||||||
|
if( emit_entry != pdFALSE )
|
||||||
|
{
|
||||||
|
task01_scheduler_checkpoint( 5U, peer );
|
||||||
|
}
|
||||||
|
|
||||||
|
for( ; ; )
|
||||||
|
{
|
||||||
|
const TickType_t tick = xTaskGetTickCount();
|
||||||
|
|
||||||
|
++peer->iterations;
|
||||||
|
peer->checksum += ( peer->id + 1U ) *
|
||||||
|
( ( peer->iterations & 0xFFU ) + 1U );
|
||||||
|
|
||||||
|
if( tick != observed_tick || g_switch_count == 0U )
|
||||||
|
{
|
||||||
|
observed_tick = tick;
|
||||||
|
|
||||||
|
if( record_peer_switch( peer->id, tick ) != pdFALSE )
|
||||||
|
{
|
||||||
|
task01_scheduler_checkpoint( 7U, peer );
|
||||||
|
|
||||||
|
taskENTER_CRITICAL();
|
||||||
|
if( g_priority_raise_started == 0U )
|
||||||
|
{
|
||||||
|
g_priority_raise_started = 1U;
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
|
||||||
|
append_preemption_marker( SCHED_ORDER_BEFORE_RAISE );
|
||||||
|
task01_scheduler_checkpoint( 8U, peer );
|
||||||
|
vTaskPrioritySet( g_high_probe.handle,
|
||||||
|
SCHED_PROBE_TARGET_PRIORITY );
|
||||||
|
append_preemption_marker( SCHED_ORDER_AFTER_RAISE );
|
||||||
|
g_peer_after_raise = 1U;
|
||||||
|
g_stop_peers = 1U;
|
||||||
|
task01_scheduler_checkpoint( 10U, peer );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( g_stop_peers != 0U )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ( tick - peer->first_tick ) > SCHED_TIMEOUT_TICKS )
|
||||||
|
{
|
||||||
|
lab_fail( 0xFC030102U );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
peer->last_tick = xTaskGetTickCount();
|
||||||
|
peer->phase = SCHED_PHASE_COMPLETED;
|
||||||
|
peer->handle = NULL;
|
||||||
|
|
||||||
|
taskENTER_CRITICAL();
|
||||||
|
++g_peers_finished;
|
||||||
|
if( g_peers_finished == 2U )
|
||||||
|
{
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
task01_scheduler_checkpoint( 11U, peer );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelete( NULL );
|
||||||
|
lab_fail( 0xFC030103U );
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__( ( noinline, used ) )
|
||||||
|
void high_probe_task_entry( void * pv_parameters )
|
||||||
|
{
|
||||||
|
HighProbeContext * probe = ( HighProbeContext * ) pv_parameters;
|
||||||
|
|
||||||
|
if( probe == NULL )
|
||||||
|
{
|
||||||
|
lab_fail( 0xFC030201U );
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_probe_runtime( probe );
|
||||||
|
probe->phase = SCHED_PHASE_RUNNING;
|
||||||
|
probe->run_tick = xTaskGetTickCount();
|
||||||
|
append_preemption_marker( SCHED_ORDER_HIGH_PROBE );
|
||||||
|
probe->ran_before_caller_returned =
|
||||||
|
( g_peer_after_raise == 0U ) &&
|
||||||
|
( g_preemption_order_count == 2U );
|
||||||
|
probe->verifier_was_not_running = ( g_verifier_started == 0U );
|
||||||
|
task01_scheduler_checkpoint( 9U, probe );
|
||||||
|
|
||||||
|
probe->phase = SCHED_PHASE_COMPLETED;
|
||||||
|
probe->handle = NULL;
|
||||||
|
vTaskDelete( NULL );
|
||||||
|
lab_fail( 0xFC030202U );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int trace_ticks_are_nondecreasing( void )
|
||||||
|
{
|
||||||
|
uint32_t index;
|
||||||
|
|
||||||
|
for( index = 1U; index < g_switch_count; ++index )
|
||||||
|
{
|
||||||
|
if( g_switch_tick[ index ] < g_switch_tick[ index - 1U ] )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__( ( noinline, used ) )
|
||||||
|
void verifier_task_entry( void * pv_parameters )
|
||||||
|
{
|
||||||
|
VerifierContext * verifier = ( VerifierContext * ) pv_parameters;
|
||||||
|
|
||||||
|
if( verifier == NULL )
|
||||||
|
{
|
||||||
|
lab_fail( 0xFC030301U );
|
||||||
|
}
|
||||||
|
|
||||||
|
verifier->phase = SCHED_PHASE_RUNNING;
|
||||||
|
verifier->observed_priority = uxTaskPriorityGet( NULL );
|
||||||
|
g_verifier_started = 1U;
|
||||||
|
|
||||||
|
g_scheduler_pass =
|
||||||
|
configUSE_PREEMPTION == 1 &&
|
||||||
|
configUSE_TIME_SLICING == 1 &&
|
||||||
|
g_peer_a.observed_priority == SCHED_PEER_PRIORITY &&
|
||||||
|
g_peer_b.observed_priority == SCHED_PEER_PRIORITY &&
|
||||||
|
g_high_probe.observed_initial_priority ==
|
||||||
|
SCHED_PROBE_INITIAL_PRIORITY &&
|
||||||
|
g_high_probe.observed_priority == SCHED_PROBE_TARGET_PRIORITY &&
|
||||||
|
verifier->observed_priority == SCHED_VERIFY_PRIORITY &&
|
||||||
|
g_peer_a.phase == SCHED_PHASE_COMPLETED &&
|
||||||
|
g_peer_b.phase == SCHED_PHASE_COMPLETED &&
|
||||||
|
g_high_probe.phase == SCHED_PHASE_COMPLETED &&
|
||||||
|
g_peer_a.handle == NULL &&
|
||||||
|
g_peer_b.handle == NULL &&
|
||||||
|
g_high_probe.handle == NULL &&
|
||||||
|
g_peers_finished == 2U &&
|
||||||
|
g_peer_a.iterations > 0U &&
|
||||||
|
g_peer_b.iterations > 0U &&
|
||||||
|
g_peer_a.checksum != 0U &&
|
||||||
|
g_peer_b.checksum != 0U &&
|
||||||
|
g_tick_hook_count >= 2U &&
|
||||||
|
g_first_tick_mcause == MCAUSE_MACHINE_TIMER_INTERRUPT &&
|
||||||
|
g_first_tick_sp != 0U &&
|
||||||
|
g_first_tick_mepc != 0U &&
|
||||||
|
g_aba_seen == 1U &&
|
||||||
|
g_aba_task[ 0 ] == SCHED_PEER_A &&
|
||||||
|
g_aba_task[ 1 ] == SCHED_PEER_B &&
|
||||||
|
g_aba_task[ 2 ] == SCHED_PEER_A &&
|
||||||
|
g_aba_tick[ 0 ] <= g_aba_tick[ 1 ] &&
|
||||||
|
g_aba_tick[ 1 ] <= g_aba_tick[ 2 ] &&
|
||||||
|
trace_ticks_are_nondecreasing() != 0 &&
|
||||||
|
g_peer_changes >= 2U &&
|
||||||
|
g_priority_raise_started == 1U &&
|
||||||
|
g_peer_after_raise == 1U &&
|
||||||
|
g_high_probe.ran_before_caller_returned == 1U &&
|
||||||
|
g_high_probe.verifier_was_not_running == 1U &&
|
||||||
|
g_preemption_order_count == 3U &&
|
||||||
|
g_preemption_order[ 0 ] == SCHED_ORDER_BEFORE_RAISE &&
|
||||||
|
g_preemption_order[ 1 ] == SCHED_ORDER_HIGH_PROBE &&
|
||||||
|
g_preemption_order[ 2 ] == SCHED_ORDER_AFTER_RAISE &&
|
||||||
|
g_high_probe.run_tick >= g_aba_tick[ 2 ] &&
|
||||||
|
g_peer_a.created_handle == g_peer_a.tcb_address &&
|
||||||
|
g_peer_b.created_handle == g_peer_b.tcb_address &&
|
||||||
|
g_high_probe.created_handle == g_high_probe.tcb_address &&
|
||||||
|
sp_inside_peer_stack( &g_peer_a ) != 0 &&
|
||||||
|
sp_inside_peer_stack( &g_peer_b ) != 0 &&
|
||||||
|
sp_inside_probe_stack( &g_high_probe ) != 0;
|
||||||
|
|
||||||
|
verifier->pass = g_scheduler_pass;
|
||||||
|
verifier->phase = SCHED_PHASE_COMPLETED;
|
||||||
|
verifier->handle = NULL;
|
||||||
|
task01_scheduler_checkpoint( 12U, verifier );
|
||||||
|
|
||||||
|
if( g_scheduler_pass == 0U )
|
||||||
|
{
|
||||||
|
lab_fail( 0xFC030302U );
|
||||||
|
}
|
||||||
|
|
||||||
|
lab_puts( "PASS FC03: tick + priorities + preemption + time slicing\n" );
|
||||||
|
lab_put_u32( g_tick_hook_count );
|
||||||
|
lab_put_u32( g_peer_changes );
|
||||||
|
lab_put_u32( g_preemption_order_count );
|
||||||
|
lab_exit( 0U );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_task_or_fail( TaskFunction_t entry,
|
||||||
|
const char * name,
|
||||||
|
configSTACK_DEPTH_TYPE stack_words,
|
||||||
|
void * context,
|
||||||
|
UBaseType_t priority,
|
||||||
|
TaskHandle_t * handle,
|
||||||
|
uint32_t failure_code )
|
||||||
|
{
|
||||||
|
if( xTaskCreate( entry,
|
||||||
|
name,
|
||||||
|
stack_words,
|
||||||
|
context,
|
||||||
|
priority,
|
||||||
|
handle ) != pdPASS )
|
||||||
|
{
|
||||||
|
lab_fail( failure_code );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main( void )
|
||||||
|
{
|
||||||
|
lab_heap_initialize();
|
||||||
|
task01_scheduler_checkpoint( 1U, &g_peer_a );
|
||||||
|
|
||||||
|
create_task_or_fail( high_probe_task_entry,
|
||||||
|
"high-probe",
|
||||||
|
SCHED_PROBE_STACK_WORDS,
|
||||||
|
&g_high_probe,
|
||||||
|
SCHED_PROBE_INITIAL_PRIORITY,
|
||||||
|
&g_high_probe.handle,
|
||||||
|
0xFC030401U );
|
||||||
|
g_high_probe.created_handle = ( uintptr_t ) g_high_probe.handle;
|
||||||
|
g_high_probe.phase = SCHED_PHASE_READY;
|
||||||
|
task01_scheduler_checkpoint( 2U, &g_high_probe );
|
||||||
|
|
||||||
|
create_task_or_fail( peer_task_entry,
|
||||||
|
"peer-a",
|
||||||
|
SCHED_PEER_STACK_WORDS,
|
||||||
|
&g_peer_a,
|
||||||
|
SCHED_PEER_PRIORITY,
|
||||||
|
&g_peer_a.handle,
|
||||||
|
0xFC030402U );
|
||||||
|
g_peer_a.created_handle = ( uintptr_t ) g_peer_a.handle;
|
||||||
|
g_peer_a.phase = SCHED_PHASE_READY;
|
||||||
|
|
||||||
|
create_task_or_fail( peer_task_entry,
|
||||||
|
"peer-b",
|
||||||
|
SCHED_PEER_STACK_WORDS,
|
||||||
|
&g_peer_b,
|
||||||
|
SCHED_PEER_PRIORITY,
|
||||||
|
&g_peer_b.handle,
|
||||||
|
0xFC030403U );
|
||||||
|
g_peer_b.created_handle = ( uintptr_t ) g_peer_b.handle;
|
||||||
|
g_peer_b.phase = SCHED_PHASE_READY;
|
||||||
|
|
||||||
|
create_task_or_fail( verifier_task_entry,
|
||||||
|
"verify",
|
||||||
|
SCHED_VERIFY_STACK_WORDS,
|
||||||
|
&g_verifier,
|
||||||
|
SCHED_VERIFY_PRIORITY,
|
||||||
|
&g_verifier.handle,
|
||||||
|
0xFC030404U );
|
||||||
|
g_verifier.phase = SCHED_PHASE_READY;
|
||||||
|
|
||||||
|
g_high_probe.observed_initial_priority =
|
||||||
|
uxTaskPriorityGet( g_high_probe.handle );
|
||||||
|
g_peer_a.observed_priority = uxTaskPriorityGet( g_peer_a.handle );
|
||||||
|
g_peer_b.observed_priority = uxTaskPriorityGet( g_peer_b.handle );
|
||||||
|
g_verifier.observed_priority = uxTaskPriorityGet( g_verifier.handle );
|
||||||
|
task01_scheduler_checkpoint( 3U, &g_high_probe );
|
||||||
|
|
||||||
|
if( g_high_probe.observed_initial_priority !=
|
||||||
|
SCHED_PROBE_INITIAL_PRIORITY ||
|
||||||
|
g_peer_a.observed_priority != SCHED_PEER_PRIORITY ||
|
||||||
|
g_peer_b.observed_priority != SCHED_PEER_PRIORITY ||
|
||||||
|
g_verifier.observed_priority != SCHED_VERIFY_PRIORITY )
|
||||||
|
{
|
||||||
|
lab_fail( 0xFC030405U );
|
||||||
|
}
|
||||||
|
|
||||||
|
task01_scheduler_checkpoint( 4U, &g_peer_a );
|
||||||
|
vTaskStartScheduler();
|
||||||
|
lab_fail( 0xFC030406U );
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "task01_scheduler_model.h"
|
||||||
|
|
||||||
|
int main( void )
|
||||||
|
{
|
||||||
|
const uint32_t valid_trace[] = {
|
||||||
|
SCHED_PEER_B,
|
||||||
|
SCHED_PEER_A,
|
||||||
|
SCHED_PEER_B,
|
||||||
|
SCHED_PEER_A
|
||||||
|
};
|
||||||
|
const uint32_t invalid_trace[] = {
|
||||||
|
SCHED_PEER_A,
|
||||||
|
SCHED_PEER_A,
|
||||||
|
SCHED_PEER_B
|
||||||
|
};
|
||||||
|
const uint32_t valid_ticks[] = { 1U, 2U, 2U, 3U };
|
||||||
|
const uint32_t invalid_ticks[] = { 1U, 3U, 2U };
|
||||||
|
const uint32_t valid_order[] = {
|
||||||
|
SCHED_ORDER_BEFORE_RAISE,
|
||||||
|
SCHED_ORDER_HIGH_PROBE,
|
||||||
|
SCHED_ORDER_AFTER_RAISE
|
||||||
|
};
|
||||||
|
|
||||||
|
assert( scheduler_trace_has_aba( valid_trace, 4U ) == 1 );
|
||||||
|
assert( scheduler_trace_has_aba( invalid_trace, 3U ) == 0 );
|
||||||
|
assert( scheduler_ticks_nondecreasing( valid_ticks, 4U ) == 1 );
|
||||||
|
assert( scheduler_ticks_nondecreasing( invalid_ticks, 3U ) == 0 );
|
||||||
|
assert( scheduler_preemption_order_valid( valid_order, 3U ) == 1 );
|
||||||
|
assert( scheduler_preemption_order_valid( valid_order, 2U ) == 0 );
|
||||||
|
|
||||||
|
puts( "PASS scheduler model: A-B-A, tick order and preemption order" );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Vendored FreeRTOS kernel subset
|
||||||
|
|
||||||
|
- Upstream: <https://github.com/FreeRTOS/FreeRTOS-Kernel>
|
||||||
|
- Release: `V11.3.0`
|
||||||
|
- Commit: `9b777ae5c5b8e9e456065a00294d1e5f5f9facf5`
|
||||||
|
- License: MIT; see `LICENSE.md` in this directory.
|
||||||
|
- Imported: 2026-07-14.
|
||||||
|
|
||||||
|
The vendored files are unmodified upstream sources. This card needs only
|
||||||
|
`tasks.c`, `list.c`, `heap_4.c`, the GCC RISC-V port and public headers.
|
||||||
|
Hazard3-specific configuration and trap routing live outside this directory in
|
||||||
|
`include/` and `src/common/`.
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _MSC_VER /* Visual Studio doesn't support #warning. */
|
||||||
|
#warning The name of this file has changed to stack_macros.h. Please update your code accordingly. This source file (which has the original name) will be removed in a future release.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "stack_macros.h"
|
||||||
@@ -0,0 +1,427 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file atomic.h
|
||||||
|
* @brief FreeRTOS atomic operation support.
|
||||||
|
*
|
||||||
|
* This file implements atomic functions by disabling interrupts globally.
|
||||||
|
* Implementations with architecture specific atomic instructions can be
|
||||||
|
* provided under each compiler directory.
|
||||||
|
*
|
||||||
|
* The atomic interface can be used in FreeRTOS tasks on all FreeRTOS ports. It
|
||||||
|
* can also be used in Interrupt Service Routines (ISRs) on FreeRTOS ports that
|
||||||
|
* support nested interrupts (i.e. portHAS_NESTED_INTERRUPTS is set to 1). The
|
||||||
|
* atomic interface must not be used in ISRs on FreeRTOS ports that do not
|
||||||
|
* support nested interrupts (i.e. portHAS_NESTED_INTERRUPTS is set to 0)
|
||||||
|
* because ISRs on these ports cannot be interrupted and therefore, do not need
|
||||||
|
* atomics in ISRs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ATOMIC_H
|
||||||
|
#define ATOMIC_H
|
||||||
|
|
||||||
|
#ifndef INC_FREERTOS_H
|
||||||
|
#error "include FreeRTOS.h must appear in source files before include atomic.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Standard includes. */
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Port specific definitions -- entering/exiting critical section.
|
||||||
|
* Refer template -- ./lib/FreeRTOS/portable/Compiler/Arch/portmacro.h
|
||||||
|
*
|
||||||
|
* Every call to ATOMIC_EXIT_CRITICAL() must be closely paired with
|
||||||
|
* ATOMIC_ENTER_CRITICAL().
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if ( portHAS_NESTED_INTERRUPTS == 1 )
|
||||||
|
|
||||||
|
/* Nested interrupt scheme is supported in this port. */
|
||||||
|
#define ATOMIC_ENTER_CRITICAL() \
|
||||||
|
UBaseType_t uxCriticalSectionType = portSET_INTERRUPT_MASK_FROM_ISR()
|
||||||
|
|
||||||
|
#define ATOMIC_EXIT_CRITICAL() \
|
||||||
|
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxCriticalSectionType )
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Nested interrupt scheme is NOT supported in this port. */
|
||||||
|
#define ATOMIC_ENTER_CRITICAL() portENTER_CRITICAL()
|
||||||
|
#define ATOMIC_EXIT_CRITICAL() portEXIT_CRITICAL()
|
||||||
|
|
||||||
|
#endif /* portSET_INTERRUPT_MASK_FROM_ISR() */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Port specific definition -- "always inline".
|
||||||
|
* Inline is compiler specific, and may not always get inlined depending on your
|
||||||
|
* optimization level. Also, inline is considered as performance optimization
|
||||||
|
* for atomic. Thus, if portFORCE_INLINE is not provided by portmacro.h,
|
||||||
|
* instead of resulting error, simply define it away.
|
||||||
|
*/
|
||||||
|
#ifndef portFORCE_INLINE
|
||||||
|
#define portFORCE_INLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ATOMIC_COMPARE_AND_SWAP_SUCCESS 0x1U /**< Compare and swap succeeded, swapped. */
|
||||||
|
#define ATOMIC_COMPARE_AND_SWAP_FAILURE 0x0U /**< Compare and swap failed, did not swap. */
|
||||||
|
|
||||||
|
/*----------------------------- Swap && CAS ------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic compare-and-swap
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic compare-and-swap operation on the specified values.
|
||||||
|
*
|
||||||
|
* @param[in, out] pulDestination Pointer to memory location from where value is
|
||||||
|
* to be loaded and checked.
|
||||||
|
* @param[in] ulExchange If condition meets, write this value to memory.
|
||||||
|
* @param[in] ulComparand Swap condition.
|
||||||
|
*
|
||||||
|
* @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
|
||||||
|
*
|
||||||
|
* @note This function only swaps *pulDestination with ulExchange, if previous
|
||||||
|
* *pulDestination value equals ulComparand.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_CompareAndSwap_u32( uint32_t volatile * pulDestination,
|
||||||
|
uint32_t ulExchange,
|
||||||
|
uint32_t ulComparand )
|
||||||
|
{
|
||||||
|
uint32_t ulReturnValue;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
if( *pulDestination == ulComparand )
|
||||||
|
{
|
||||||
|
*pulDestination = ulExchange;
|
||||||
|
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulReturnValue;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic swap (pointers)
|
||||||
|
*
|
||||||
|
* @brief Atomically sets the address pointed to by *ppvDestination to the value
|
||||||
|
* of *pvExchange.
|
||||||
|
*
|
||||||
|
* @param[in, out] ppvDestination Pointer to memory location from where a pointer
|
||||||
|
* value is to be loaded and written back to.
|
||||||
|
* @param[in] pvExchange Pointer value to be written to *ppvDestination.
|
||||||
|
*
|
||||||
|
* @return The initial value of *ppvDestination.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE void * Atomic_SwapPointers_p32( void * volatile * ppvDestination,
|
||||||
|
void * pvExchange )
|
||||||
|
{
|
||||||
|
void * pReturnValue;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
pReturnValue = *ppvDestination;
|
||||||
|
*ppvDestination = pvExchange;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return pReturnValue;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic compare-and-swap (pointers)
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic compare-and-swap operation on the specified pointer
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* @param[in, out] ppvDestination Pointer to memory location from where a pointer
|
||||||
|
* value is to be loaded and checked.
|
||||||
|
* @param[in] pvExchange If condition meets, write this value to memory.
|
||||||
|
* @param[in] pvComparand Swap condition.
|
||||||
|
*
|
||||||
|
* @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
|
||||||
|
*
|
||||||
|
* @note This function only swaps *ppvDestination with pvExchange, if previous
|
||||||
|
* *ppvDestination value equals pvComparand.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_CompareAndSwapPointers_p32( void * volatile * ppvDestination,
|
||||||
|
void * pvExchange,
|
||||||
|
void * pvComparand )
|
||||||
|
{
|
||||||
|
uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
if( *ppvDestination == pvComparand )
|
||||||
|
{
|
||||||
|
*ppvDestination = pvExchange;
|
||||||
|
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulReturnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*----------------------------- Arithmetic ------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic add
|
||||||
|
*
|
||||||
|
* @brief Atomically adds count to the value of the specified pointer points to.
|
||||||
|
*
|
||||||
|
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||||
|
* loaded and written back to.
|
||||||
|
* @param[in] ulCount Value to be added to *pulAddend.
|
||||||
|
*
|
||||||
|
* @return previous *pulAddend value.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_Add_u32( uint32_t volatile * pulAddend,
|
||||||
|
uint32_t ulCount )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulAddend;
|
||||||
|
*pulAddend += ulCount;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic subtract
|
||||||
|
*
|
||||||
|
* @brief Atomically subtracts count from the value of the specified pointer
|
||||||
|
* pointers to.
|
||||||
|
*
|
||||||
|
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||||
|
* loaded and written back to.
|
||||||
|
* @param[in] ulCount Value to be subtract from *pulAddend.
|
||||||
|
*
|
||||||
|
* @return previous *pulAddend value.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_Subtract_u32( uint32_t volatile * pulAddend,
|
||||||
|
uint32_t ulCount )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulAddend;
|
||||||
|
*pulAddend -= ulCount;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic increment
|
||||||
|
*
|
||||||
|
* @brief Atomically increments the value of the specified pointer points to.
|
||||||
|
*
|
||||||
|
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||||
|
* loaded and written back to.
|
||||||
|
*
|
||||||
|
* @return *pulAddend value before increment.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_Increment_u32( uint32_t volatile * pulAddend )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulAddend;
|
||||||
|
*pulAddend += 1;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic decrement
|
||||||
|
*
|
||||||
|
* @brief Atomically decrements the value of the specified pointer points to
|
||||||
|
*
|
||||||
|
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||||
|
* loaded and written back to.
|
||||||
|
*
|
||||||
|
* @return *pulAddend value before decrement.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_Decrement_u32( uint32_t volatile * pulAddend )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulAddend;
|
||||||
|
*pulAddend -= 1;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------- Bitwise Logical ------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic OR
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic OR operation on the specified values.
|
||||||
|
*
|
||||||
|
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||||
|
* to be loaded and written back to.
|
||||||
|
* @param [in] ulValue Value to be ORed with *pulDestination.
|
||||||
|
*
|
||||||
|
* @return The original value of *pulDestination.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_OR_u32( uint32_t volatile * pulDestination,
|
||||||
|
uint32_t ulValue )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulDestination;
|
||||||
|
*pulDestination |= ulValue;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic AND
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic AND operation on the specified values.
|
||||||
|
*
|
||||||
|
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||||
|
* to be loaded and written back to.
|
||||||
|
* @param [in] ulValue Value to be ANDed with *pulDestination.
|
||||||
|
*
|
||||||
|
* @return The original value of *pulDestination.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_AND_u32( uint32_t volatile * pulDestination,
|
||||||
|
uint32_t ulValue )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulDestination;
|
||||||
|
*pulDestination &= ulValue;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic NAND
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic NAND operation on the specified values.
|
||||||
|
*
|
||||||
|
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||||
|
* to be loaded and written back to.
|
||||||
|
* @param [in] ulValue Value to be NANDed with *pulDestination.
|
||||||
|
*
|
||||||
|
* @return The original value of *pulDestination.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_NAND_u32( uint32_t volatile * pulDestination,
|
||||||
|
uint32_t ulValue )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulDestination;
|
||||||
|
*pulDestination = ~( ulCurrent & ulValue );
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic XOR
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic XOR operation on the specified values.
|
||||||
|
*
|
||||||
|
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||||
|
* to be loaded and written back to.
|
||||||
|
* @param [in] ulValue Value to be XORed with *pulDestination.
|
||||||
|
*
|
||||||
|
* @return The original value of *pulDestination.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_XOR_u32( uint32_t volatile * pulDestination,
|
||||||
|
uint32_t ulValue )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulDestination;
|
||||||
|
*pulDestination ^= ulValue;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
#endif /* ATOMIC_H */
|
||||||
@@ -0,0 +1,765 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CO_ROUTINE_H
|
||||||
|
#define CO_ROUTINE_H
|
||||||
|
|
||||||
|
#ifndef INC_FREERTOS_H
|
||||||
|
#error "include FreeRTOS.h must appear in source files before include croutine.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/* Used to hide the implementation of the co-routine control block. The
|
||||||
|
* control block structure however has to be included in the header due to
|
||||||
|
* the macro implementation of the co-routine functionality. */
|
||||||
|
typedef void * CoRoutineHandle_t;
|
||||||
|
|
||||||
|
/* Defines the prototype to which co-routine functions must conform. */
|
||||||
|
typedef void (* crCOROUTINE_CODE)( CoRoutineHandle_t xHandle,
|
||||||
|
UBaseType_t uxIndex );
|
||||||
|
|
||||||
|
typedef struct corCoRoutineControlBlock
|
||||||
|
{
|
||||||
|
crCOROUTINE_CODE pxCoRoutineFunction;
|
||||||
|
ListItem_t xGenericListItem; /**< List item used to place the CRCB in ready and blocked queues. */
|
||||||
|
ListItem_t xEventListItem; /**< List item used to place the CRCB in event lists. */
|
||||||
|
UBaseType_t uxPriority; /**< The priority of the co-routine in relation to other co-routines. */
|
||||||
|
UBaseType_t uxIndex; /**< Used to distinguish between co-routines when multiple co-routines use the same co-routine function. */
|
||||||
|
uint16_t uxState; /**< Used internally by the co-routine implementation. */
|
||||||
|
} CRCB_t; /* Co-routine control block. Note must be identical in size down to uxPriority with TCB_t. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* @code{c}
|
||||||
|
* BaseType_t xCoRoutineCreate(
|
||||||
|
* crCOROUTINE_CODE pxCoRoutineCode,
|
||||||
|
* UBaseType_t uxPriority,
|
||||||
|
* UBaseType_t uxIndex
|
||||||
|
* );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Create a new co-routine and add it to the list of co-routines that are
|
||||||
|
* ready to run.
|
||||||
|
*
|
||||||
|
* @param pxCoRoutineCode Pointer to the co-routine function. Co-routine
|
||||||
|
* functions require special syntax - see the co-routine section of the WEB
|
||||||
|
* documentation for more information.
|
||||||
|
*
|
||||||
|
* @param uxPriority The priority with respect to other co-routines at which
|
||||||
|
* the co-routine will run.
|
||||||
|
*
|
||||||
|
* @param uxIndex Used to distinguish between different co-routines that
|
||||||
|
* execute the same function. See the example below and the co-routine section
|
||||||
|
* of the WEB documentation for further information.
|
||||||
|
*
|
||||||
|
* @return pdPASS if the co-routine was successfully created and added to a ready
|
||||||
|
* list, otherwise an error code defined with ProjDefs.h.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // Co-routine to be created.
|
||||||
|
* void vFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
* {
|
||||||
|
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
* // This may not be necessary for const variables.
|
||||||
|
* static const char cLedToFlash[ 2 ] = { 5, 6 };
|
||||||
|
* static const TickType_t uxFlashRates[ 2 ] = { 200, 400 };
|
||||||
|
*
|
||||||
|
* // Must start every co-routine with a call to crSTART();
|
||||||
|
* crSTART( xHandle );
|
||||||
|
*
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // This co-routine just delays for a fixed period, then toggles
|
||||||
|
* // an LED. Two co-routines are created using this function, so
|
||||||
|
* // the uxIndex parameter is used to tell the co-routine which
|
||||||
|
* // LED to flash and how int32_t to delay. This assumes xQueue has
|
||||||
|
* // already been created.
|
||||||
|
* vParTestToggleLED( cLedToFlash[ uxIndex ] );
|
||||||
|
* crDELAY( xHandle, uxFlashRates[ uxIndex ] );
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Must end every co-routine with a call to crEND();
|
||||||
|
* crEND();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Function that creates two co-routines.
|
||||||
|
* void vOtherFunction( void )
|
||||||
|
* {
|
||||||
|
* uint8_t ucParameterToPass;
|
||||||
|
* TaskHandle_t xHandle;
|
||||||
|
*
|
||||||
|
* // Create two co-routines at priority 0. The first is given index 0
|
||||||
|
* // so (from the code above) toggles LED 5 every 200 ticks. The second
|
||||||
|
* // is given index 1 so toggles LED 6 every 400 ticks.
|
||||||
|
* for( uxIndex = 0; uxIndex < 2; uxIndex++ )
|
||||||
|
* {
|
||||||
|
* xCoRoutineCreate( vFlashCoRoutine, 0, uxIndex );
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xCoRoutineCreate xCoRoutineCreate
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode,
|
||||||
|
UBaseType_t uxPriority,
|
||||||
|
UBaseType_t uxIndex );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* @code{c}
|
||||||
|
* void vCoRoutineSchedule( void );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Run a co-routine.
|
||||||
|
*
|
||||||
|
* vCoRoutineSchedule() executes the highest priority co-routine that is able
|
||||||
|
* to run. The co-routine will execute until it either blocks, yields or is
|
||||||
|
* preempted by a task. Co-routines execute cooperatively so one
|
||||||
|
* co-routine cannot be preempted by another, but can be preempted by a task.
|
||||||
|
*
|
||||||
|
* If an application comprises of both tasks and co-routines then
|
||||||
|
* vCoRoutineSchedule should be called from the idle task (in an idle task
|
||||||
|
* hook).
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // This idle task hook will schedule a co-routine each time it is called.
|
||||||
|
* // The rest of the idle task will execute between co-routine calls.
|
||||||
|
* void vApplicationIdleHook( void )
|
||||||
|
* {
|
||||||
|
* vCoRoutineSchedule();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Alternatively, if you do not require any other part of the idle task to
|
||||||
|
* // execute, the idle task hook can call vCoRoutineSchedule() within an
|
||||||
|
* // infinite loop.
|
||||||
|
* void vApplicationIdleHook( void )
|
||||||
|
* {
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* vCoRoutineSchedule();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup vCoRoutineSchedule vCoRoutineSchedule
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
void vCoRoutineSchedule( void );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* @code{c}
|
||||||
|
* crSTART( CoRoutineHandle_t xHandle );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* This macro MUST always be called at the start of a co-routine function.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // Co-routine to be created.
|
||||||
|
* void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
* {
|
||||||
|
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
* static int32_t ulAVariable;
|
||||||
|
*
|
||||||
|
* // Must start every co-routine with a call to crSTART();
|
||||||
|
* crSTART( xHandle );
|
||||||
|
*
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // Co-routine functionality goes here.
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Must end every co-routine with a call to crEND();
|
||||||
|
* crEND();
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup crSTART crSTART
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crSTART( pxCRCB ) \
|
||||||
|
switch( ( ( CRCB_t * ) ( pxCRCB ) )->uxState ) { \
|
||||||
|
case 0:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* @code{c}
|
||||||
|
* crEND();
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* This macro MUST always be called at the end of a co-routine function.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // Co-routine to be created.
|
||||||
|
* void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
* {
|
||||||
|
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
* static int32_t ulAVariable;
|
||||||
|
*
|
||||||
|
* // Must start every co-routine with a call to crSTART();
|
||||||
|
* crSTART( xHandle );
|
||||||
|
*
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // Co-routine functionality goes here.
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Must end every co-routine with a call to crEND();
|
||||||
|
* crEND();
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup crSTART crSTART
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#define crEND() }
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These macros are intended for internal use by the co-routine implementation
|
||||||
|
* only. The macros should not be used directly by application writers.
|
||||||
|
*/
|
||||||
|
#define crSET_STATE0( xHandle ) \
|
||||||
|
( ( CRCB_t * ) ( xHandle ) )->uxState = ( __LINE__ * 2 ); return; \
|
||||||
|
case ( __LINE__ * 2 ):
|
||||||
|
#define crSET_STATE1( xHandle ) \
|
||||||
|
( ( CRCB_t * ) ( xHandle ) )->uxState = ( ( __LINE__ * 2 ) + 1 ); return; \
|
||||||
|
case ( ( __LINE__ * 2 ) + 1 ):
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* @code{c}
|
||||||
|
* crDELAY( CoRoutineHandle_t xHandle, TickType_t xTicksToDelay );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Delay a co-routine for a fixed period of time.
|
||||||
|
*
|
||||||
|
* crDELAY can only be called from the co-routine function itself - not
|
||||||
|
* from within a function called by the co-routine function. This is because
|
||||||
|
* co-routines do not maintain their own stack.
|
||||||
|
*
|
||||||
|
* @param xHandle The handle of the co-routine to delay. This is the xHandle
|
||||||
|
* parameter of the co-routine function.
|
||||||
|
*
|
||||||
|
* @param xTickToDelay The number of ticks that the co-routine should delay
|
||||||
|
* for. The actual amount of time this equates to is defined by
|
||||||
|
* configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant portTICK_PERIOD_MS
|
||||||
|
* can be used to convert ticks to milliseconds.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // Co-routine to be created.
|
||||||
|
* void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
* {
|
||||||
|
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
* // This may not be necessary for const variables.
|
||||||
|
* // We are to delay for 200ms.
|
||||||
|
* static const xTickType xDelayTime = 200 / portTICK_PERIOD_MS;
|
||||||
|
*
|
||||||
|
* // Must start every co-routine with a call to crSTART();
|
||||||
|
* crSTART( xHandle );
|
||||||
|
*
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // Delay for 200ms.
|
||||||
|
* crDELAY( xHandle, xDelayTime );
|
||||||
|
*
|
||||||
|
* // Do something here.
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Must end every co-routine with a call to crEND();
|
||||||
|
* crEND();
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup crDELAY crDELAY
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crDELAY( xHandle, xTicksToDelay ) \
|
||||||
|
do { \
|
||||||
|
if( ( xTicksToDelay ) > 0 ) \
|
||||||
|
{ \
|
||||||
|
vCoRoutineAddToDelayedList( ( xTicksToDelay ), NULL ); \
|
||||||
|
} \
|
||||||
|
crSET_STATE0( ( xHandle ) ); \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code{c}
|
||||||
|
* crQUEUE_SEND(
|
||||||
|
* CoRoutineHandle_t xHandle,
|
||||||
|
* QueueHandle_t pxQueue,
|
||||||
|
* void *pvItemToQueue,
|
||||||
|
* TickType_t xTicksToWait,
|
||||||
|
* BaseType_t *pxResult
|
||||||
|
* )
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine
|
||||||
|
* equivalent to the xQueueSend() and xQueueReceive() functions used by tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas
|
||||||
|
* xQueueSend() and xQueueReceive() can only be used from tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND can only be called from the co-routine function itself - not
|
||||||
|
* from within a function called by the co-routine function. This is because
|
||||||
|
* co-routines do not maintain their own stack.
|
||||||
|
*
|
||||||
|
* See the co-routine section of the WEB documentation for information on
|
||||||
|
* passing data between tasks and co-routines and between ISR's and
|
||||||
|
* co-routines.
|
||||||
|
*
|
||||||
|
* @param xHandle The handle of the calling co-routine. This is the xHandle
|
||||||
|
* parameter of the co-routine function.
|
||||||
|
*
|
||||||
|
* @param pxQueue The handle of the queue on which the data will be posted.
|
||||||
|
* The handle is obtained as the return value when the queue is created using
|
||||||
|
* the xQueueCreate() API function.
|
||||||
|
*
|
||||||
|
* @param pvItemToQueue A pointer to the data being posted onto the queue.
|
||||||
|
* The number of bytes of each queued item is specified when the queue is
|
||||||
|
* created. This number of bytes is copied from pvItemToQueue into the queue
|
||||||
|
* itself.
|
||||||
|
*
|
||||||
|
* @param xTickToDelay The number of ticks that the co-routine should block
|
||||||
|
* to wait for space to become available on the queue, should space not be
|
||||||
|
* available immediately. The actual amount of time this equates to is defined
|
||||||
|
* by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant
|
||||||
|
* portTICK_PERIOD_MS can be used to convert ticks to milliseconds (see example
|
||||||
|
* below).
|
||||||
|
*
|
||||||
|
* @param pxResult The variable pointed to by pxResult will be set to pdPASS if
|
||||||
|
* data was successfully posted onto the queue, otherwise it will be set to an
|
||||||
|
* error defined within ProjDefs.h.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // Co-routine function that blocks for a fixed period then posts a number onto
|
||||||
|
* // a queue.
|
||||||
|
* static void prvCoRoutineFlashTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
* {
|
||||||
|
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
* static BaseType_t xNumberToPost = 0;
|
||||||
|
* static BaseType_t xResult;
|
||||||
|
*
|
||||||
|
* // Co-routines must begin with a call to crSTART().
|
||||||
|
* crSTART( xHandle );
|
||||||
|
*
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // This assumes the queue has already been created.
|
||||||
|
* crQUEUE_SEND( xHandle, xCoRoutineQueue, &xNumberToPost, NO_DELAY, &xResult );
|
||||||
|
*
|
||||||
|
* if( xResult != pdPASS )
|
||||||
|
* {
|
||||||
|
* // The message was not posted!
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Increment the number to be posted onto the queue.
|
||||||
|
* xNumberToPost++;
|
||||||
|
*
|
||||||
|
* // Delay for 100 ticks.
|
||||||
|
* crDELAY( xHandle, 100 );
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Co-routines must end with a call to crEND().
|
||||||
|
* crEND();
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup crQUEUE_SEND crQUEUE_SEND
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crQUEUE_SEND( xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult ) \
|
||||||
|
do { \
|
||||||
|
*( pxResult ) = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), ( xTicksToWait ) ); \
|
||||||
|
if( *( pxResult ) == errQUEUE_BLOCKED ) \
|
||||||
|
{ \
|
||||||
|
crSET_STATE0( ( xHandle ) ); \
|
||||||
|
*pxResult = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), 0 ); \
|
||||||
|
} \
|
||||||
|
if( *pxResult == errQUEUE_YIELD ) \
|
||||||
|
{ \
|
||||||
|
crSET_STATE1( ( xHandle ) ); \
|
||||||
|
*pxResult = pdPASS; \
|
||||||
|
} \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* @code{c}
|
||||||
|
* crQUEUE_RECEIVE(
|
||||||
|
* CoRoutineHandle_t xHandle,
|
||||||
|
* QueueHandle_t pxQueue,
|
||||||
|
* void *pvBuffer,
|
||||||
|
* TickType_t xTicksToWait,
|
||||||
|
* BaseType_t *pxResult
|
||||||
|
* )
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine
|
||||||
|
* equivalent to the xQueueSend() and xQueueReceive() functions used by tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas
|
||||||
|
* xQueueSend() and xQueueReceive() can only be used from tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_RECEIVE can only be called from the co-routine function itself - not
|
||||||
|
* from within a function called by the co-routine function. This is because
|
||||||
|
* co-routines do not maintain their own stack.
|
||||||
|
*
|
||||||
|
* See the co-routine section of the WEB documentation for information on
|
||||||
|
* passing data between tasks and co-routines and between ISR's and
|
||||||
|
* co-routines.
|
||||||
|
*
|
||||||
|
* @param xHandle The handle of the calling co-routine. This is the xHandle
|
||||||
|
* parameter of the co-routine function.
|
||||||
|
*
|
||||||
|
* @param pxQueue The handle of the queue from which the data will be received.
|
||||||
|
* The handle is obtained as the return value when the queue is created using
|
||||||
|
* the xQueueCreate() API function.
|
||||||
|
*
|
||||||
|
* @param pvBuffer The buffer into which the received item is to be copied.
|
||||||
|
* The number of bytes of each queued item is specified when the queue is
|
||||||
|
* created. This number of bytes is copied into pvBuffer.
|
||||||
|
*
|
||||||
|
* @param xTickToDelay The number of ticks that the co-routine should block
|
||||||
|
* to wait for data to become available from the queue, should data not be
|
||||||
|
* available immediately. The actual amount of time this equates to is defined
|
||||||
|
* by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant
|
||||||
|
* portTICK_PERIOD_MS can be used to convert ticks to milliseconds (see the
|
||||||
|
* crQUEUE_SEND example).
|
||||||
|
*
|
||||||
|
* @param pxResult The variable pointed to by pxResult will be set to pdPASS if
|
||||||
|
* data was successfully retrieved from the queue, otherwise it will be set to
|
||||||
|
* an error code as defined within ProjDefs.h.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // A co-routine receives the number of an LED to flash from a queue. It
|
||||||
|
* // blocks on the queue until the number is received.
|
||||||
|
* static void prvCoRoutineFlashWorkTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
* {
|
||||||
|
* // Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
* static BaseType_t xResult;
|
||||||
|
* static UBaseType_t uxLEDToFlash;
|
||||||
|
*
|
||||||
|
* // All co-routines must start with a call to crSTART().
|
||||||
|
* crSTART( xHandle );
|
||||||
|
*
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // Wait for data to become available on the queue.
|
||||||
|
* crQUEUE_RECEIVE( xHandle, xCoRoutineQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
|
||||||
|
*
|
||||||
|
* if( xResult == pdPASS )
|
||||||
|
* {
|
||||||
|
* // We received the LED to flash - flash it!
|
||||||
|
* vParTestToggleLED( uxLEDToFlash );
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* crEND();
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup crQUEUE_RECEIVE crQUEUE_RECEIVE
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crQUEUE_RECEIVE( xHandle, pxQueue, pvBuffer, xTicksToWait, pxResult ) \
|
||||||
|
do { \
|
||||||
|
*( pxResult ) = xQueueCRReceive( ( pxQueue ), ( pvBuffer ), ( xTicksToWait ) ); \
|
||||||
|
if( *( pxResult ) == errQUEUE_BLOCKED ) \
|
||||||
|
{ \
|
||||||
|
crSET_STATE0( ( xHandle ) ); \
|
||||||
|
*( pxResult ) = xQueueCRReceive( ( pxQueue ), ( pvBuffer ), 0 ); \
|
||||||
|
} \
|
||||||
|
if( *( pxResult ) == errQUEUE_YIELD ) \
|
||||||
|
{ \
|
||||||
|
crSET_STATE1( ( xHandle ) ); \
|
||||||
|
*( pxResult ) = pdPASS; \
|
||||||
|
} \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* @code{c}
|
||||||
|
* crQUEUE_SEND_FROM_ISR(
|
||||||
|
* QueueHandle_t pxQueue,
|
||||||
|
* void *pvItemToQueue,
|
||||||
|
* BaseType_t xCoRoutinePreviouslyWoken
|
||||||
|
* )
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the
|
||||||
|
* co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR()
|
||||||
|
* functions used by tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to
|
||||||
|
* pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and
|
||||||
|
* xQueueReceiveFromISR() can only be used to pass data between a task and and
|
||||||
|
* ISR.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND_FROM_ISR can only be called from an ISR to send data to a queue
|
||||||
|
* that is being used from within a co-routine.
|
||||||
|
*
|
||||||
|
* See the co-routine section of the WEB documentation for information on
|
||||||
|
* passing data between tasks and co-routines and between ISR's and
|
||||||
|
* co-routines.
|
||||||
|
*
|
||||||
|
* @param xQueue The handle to the queue on which the item is to be posted.
|
||||||
|
*
|
||||||
|
* @param pvItemToQueue A pointer to the item that is to be placed on the
|
||||||
|
* queue. The size of the items the queue will hold was defined when the
|
||||||
|
* queue was created, so this many bytes will be copied from pvItemToQueue
|
||||||
|
* into the queue storage area.
|
||||||
|
*
|
||||||
|
* @param xCoRoutinePreviouslyWoken This is included so an ISR can post onto
|
||||||
|
* the same queue multiple times from a single interrupt. The first call
|
||||||
|
* should always pass in pdFALSE. Subsequent calls should pass in
|
||||||
|
* the value returned from the previous call.
|
||||||
|
*
|
||||||
|
* @return pdTRUE if a co-routine was woken by posting onto the queue. This is
|
||||||
|
* used by the ISR to determine if a context switch may be required following
|
||||||
|
* the ISR.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // A co-routine that blocks on a queue waiting for characters to be received.
|
||||||
|
* static void vReceivingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
* {
|
||||||
|
* char cRxedChar;
|
||||||
|
* BaseType_t xResult;
|
||||||
|
*
|
||||||
|
* // All co-routines must start with a call to crSTART().
|
||||||
|
* crSTART( xHandle );
|
||||||
|
*
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // Wait for data to become available on the queue. This assumes the
|
||||||
|
* // queue xCommsRxQueue has already been created!
|
||||||
|
* crQUEUE_RECEIVE( xHandle, xCommsRxQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
|
||||||
|
*
|
||||||
|
* // Was a character received?
|
||||||
|
* if( xResult == pdPASS )
|
||||||
|
* {
|
||||||
|
* // Process the character here.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // All co-routines must end with a call to crEND().
|
||||||
|
* crEND();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // An ISR that uses a queue to send characters received on a serial port to
|
||||||
|
* // a co-routine.
|
||||||
|
* void vUART_ISR( void )
|
||||||
|
* {
|
||||||
|
* char cRxedChar;
|
||||||
|
* BaseType_t xCRWokenByPost = pdFALSE;
|
||||||
|
*
|
||||||
|
* // We loop around reading characters until there are none left in the UART.
|
||||||
|
* while( UART_RX_REG_NOT_EMPTY() )
|
||||||
|
* {
|
||||||
|
* // Obtain the character from the UART.
|
||||||
|
* cRxedChar = UART_RX_REG;
|
||||||
|
*
|
||||||
|
* // Post the character onto a queue. xCRWokenByPost will be pdFALSE
|
||||||
|
* // the first time around the loop. If the post causes a co-routine
|
||||||
|
* // to be woken (unblocked) then xCRWokenByPost will be set to pdTRUE.
|
||||||
|
* // In this manner we can ensure that if more than one co-routine is
|
||||||
|
* // blocked on the queue only one is woken by this ISR no matter how
|
||||||
|
* // many characters are posted to the queue.
|
||||||
|
* xCRWokenByPost = crQUEUE_SEND_FROM_ISR( xCommsRxQueue, &cRxedChar, xCRWokenByPost );
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup crQUEUE_SEND_FROM_ISR crQUEUE_SEND_FROM_ISR
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crQUEUE_SEND_FROM_ISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) \
|
||||||
|
xQueueCRSendFromISR( ( pxQueue ), ( pvItemToQueue ), ( xCoRoutinePreviouslyWoken ) )
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* @code{c}
|
||||||
|
* crQUEUE_SEND_FROM_ISR(
|
||||||
|
* QueueHandle_t pxQueue,
|
||||||
|
* void *pvBuffer,
|
||||||
|
* BaseType_t * pxCoRoutineWoken
|
||||||
|
* )
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the
|
||||||
|
* co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR()
|
||||||
|
* functions used by tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to
|
||||||
|
* pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and
|
||||||
|
* xQueueReceiveFromISR() can only be used to pass data between a task and and
|
||||||
|
* ISR.
|
||||||
|
*
|
||||||
|
* crQUEUE_RECEIVE_FROM_ISR can only be called from an ISR to receive data
|
||||||
|
* from a queue that is being used from within a co-routine (a co-routine
|
||||||
|
* posted to the queue).
|
||||||
|
*
|
||||||
|
* See the co-routine section of the WEB documentation for information on
|
||||||
|
* passing data between tasks and co-routines and between ISR's and
|
||||||
|
* co-routines.
|
||||||
|
*
|
||||||
|
* @param xQueue The handle to the queue on which the item is to be posted.
|
||||||
|
*
|
||||||
|
* @param pvBuffer A pointer to a buffer into which the received item will be
|
||||||
|
* placed. The size of the items the queue will hold was defined when the
|
||||||
|
* queue was created, so this many bytes will be copied from the queue into
|
||||||
|
* pvBuffer.
|
||||||
|
*
|
||||||
|
* @param pxCoRoutineWoken A co-routine may be blocked waiting for space to become
|
||||||
|
* available on the queue. If crQUEUE_RECEIVE_FROM_ISR causes such a
|
||||||
|
* co-routine to unblock *pxCoRoutineWoken will get set to pdTRUE, otherwise
|
||||||
|
* *pxCoRoutineWoken will remain unchanged.
|
||||||
|
*
|
||||||
|
* @return pdTRUE an item was successfully received from the queue, otherwise
|
||||||
|
* pdFALSE.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // A co-routine that posts a character to a queue then blocks for a fixed
|
||||||
|
* // period. The character is incremented each time.
|
||||||
|
* static void vSendingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
* {
|
||||||
|
* // cChar holds its value while this co-routine is blocked and must therefore
|
||||||
|
* // be declared static.
|
||||||
|
* static char cCharToTx = 'a';
|
||||||
|
* BaseType_t xResult;
|
||||||
|
*
|
||||||
|
* // All co-routines must start with a call to crSTART().
|
||||||
|
* crSTART( xHandle );
|
||||||
|
*
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // Send the next character to the queue.
|
||||||
|
* crQUEUE_SEND( xHandle, xCoRoutineQueue, &cCharToTx, NO_DELAY, &xResult );
|
||||||
|
*
|
||||||
|
* if( xResult == pdPASS )
|
||||||
|
* {
|
||||||
|
* // The character was successfully posted to the queue.
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // Could not post the character to the queue.
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Enable the UART Tx interrupt to cause an interrupt in this
|
||||||
|
* // hypothetical UART. The interrupt will obtain the character
|
||||||
|
* // from the queue and send it.
|
||||||
|
* ENABLE_RX_INTERRUPT();
|
||||||
|
*
|
||||||
|
* // Increment to the next character then block for a fixed period.
|
||||||
|
* // cCharToTx will maintain its value across the delay as it is
|
||||||
|
* // declared static.
|
||||||
|
* cCharToTx++;
|
||||||
|
* if( cCharToTx > 'x' )
|
||||||
|
* {
|
||||||
|
* cCharToTx = 'a';
|
||||||
|
* }
|
||||||
|
* crDELAY( 100 );
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // All co-routines must end with a call to crEND().
|
||||||
|
* crEND();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // An ISR that uses a queue to receive characters to send on a UART.
|
||||||
|
* void vUART_ISR( void )
|
||||||
|
* {
|
||||||
|
* char cCharToTx;
|
||||||
|
* BaseType_t xCRWokenByPost = pdFALSE;
|
||||||
|
*
|
||||||
|
* while( UART_TX_REG_EMPTY() )
|
||||||
|
* {
|
||||||
|
* // Are there any characters in the queue waiting to be sent?
|
||||||
|
* // xCRWokenByPost will automatically be set to pdTRUE if a co-routine
|
||||||
|
* // is woken by the post - ensuring that only a single co-routine is
|
||||||
|
* // woken no matter how many times we go around this loop.
|
||||||
|
* if( crQUEUE_RECEIVE_FROM_ISR( pxQueue, &cCharToTx, &xCRWokenByPost ) )
|
||||||
|
* {
|
||||||
|
* SEND_CHARACTER( cCharToTx );
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup crQUEUE_RECEIVE_FROM_ISR crQUEUE_RECEIVE_FROM_ISR
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crQUEUE_RECEIVE_FROM_ISR( pxQueue, pvBuffer, pxCoRoutineWoken ) \
|
||||||
|
xQueueCRReceiveFromISR( ( pxQueue ), ( pvBuffer ), ( pxCoRoutineWoken ) )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is intended for internal use by the co-routine macros only.
|
||||||
|
* The macro nature of the co-routine implementation requires that the
|
||||||
|
* prototype appears here. The function should not be used by application
|
||||||
|
* writers.
|
||||||
|
*
|
||||||
|
* Removes the current co-routine from its ready list and places it in the
|
||||||
|
* appropriate delayed list.
|
||||||
|
*/
|
||||||
|
void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay,
|
||||||
|
List_t * pxEventList );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is intended for internal use by the queue implementation only.
|
||||||
|
* The function should not be used by application writers.
|
||||||
|
*
|
||||||
|
* Removes the highest priority co-routine from the event list and places it in
|
||||||
|
* the pending ready list.
|
||||||
|
*/
|
||||||
|
BaseType_t xCoRoutineRemoveFromEventList( const List_t * pxEventList );
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function resets the internal state of the coroutine module. It must be
|
||||||
|
* called by the application before restarting the scheduler.
|
||||||
|
*/
|
||||||
|
void vCoRoutineResetState( void ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
#endif /* CO_ROUTINE_H */
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DEPRECATED_DEFINITIONS_H
|
||||||
|
#define DEPRECATED_DEFINITIONS_H
|
||||||
|
|
||||||
|
|
||||||
|
/* Each FreeRTOS port has a unique portmacro.h header file. Originally a
|
||||||
|
* pre-processor definition was used to ensure the pre-processor found the correct
|
||||||
|
* portmacro.h file for the port being used. That scheme was deprecated in favour
|
||||||
|
* of setting the compiler's include path such that it found the correct
|
||||||
|
* portmacro.h file - removing the need for the constant and allowing the
|
||||||
|
* portmacro.h file to be located anywhere in relation to the port being used. The
|
||||||
|
* definitions below remain in the code for backward compatibility only. New
|
||||||
|
* projects should not use them. */
|
||||||
|
|
||||||
|
#ifdef OPEN_WATCOM_INDUSTRIAL_PC_PORT
|
||||||
|
#include "..\..\Source\portable\owatcom\16bitdos\pc\portmacro.h"
|
||||||
|
typedef void ( __interrupt __far * pxISR )();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OPEN_WATCOM_FLASH_LITE_186_PORT
|
||||||
|
#include "..\..\Source\portable\owatcom\16bitdos\flsh186\portmacro.h"
|
||||||
|
typedef void ( __interrupt __far * pxISR )();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_MEGA_AVR
|
||||||
|
#include "../portable/GCC/ATMega323/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IAR_MEGA_AVR
|
||||||
|
#include "../portable/IAR/ATMega323/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MPLAB_PIC24_PORT
|
||||||
|
#include "../../Source/portable/MPLAB/PIC24_dsPIC/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MPLAB_DSPIC_PORT
|
||||||
|
#include "../../Source/portable/MPLAB/PIC24_dsPIC/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MPLAB_PIC18F_PORT
|
||||||
|
#include "../../Source/portable/MPLAB/PIC18F/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MPLAB_PIC32MX_PORT
|
||||||
|
#include "../../Source/portable/MPLAB/PIC32MX/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _FEDPICC
|
||||||
|
#include "libFreeRTOS/Include/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SDCC_CYGNAL
|
||||||
|
#include "../../Source/portable/SDCC/Cygnal/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_ARM7
|
||||||
|
#include "../../Source/portable/GCC/ARM7_LPC2000/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_ARM7_ECLIPSE
|
||||||
|
#include "portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ROWLEY_LPC23xx
|
||||||
|
#include "../../Source/portable/GCC/ARM7_LPC23xx/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IAR_MSP430
|
||||||
|
#include "..\..\Source\portable\IAR\MSP430\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_MSP430
|
||||||
|
#include "../../Source/portable/GCC/MSP430F449/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ROWLEY_MSP430
|
||||||
|
#include "../../Source/portable/Rowley/MSP430F449/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARM7_LPC21xx_KEIL_RVDS
|
||||||
|
#include "..\..\Source\portable\RVDS\ARM7_LPC21xx\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SAM7_GCC
|
||||||
|
#include "../../Source/portable/GCC/ARM7_AT91SAM7S/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SAM7_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\AtmelSAM7S64\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SAM9XE_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\AtmelSAM9XE\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LPC2000_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\LPC2000\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STR71X_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\STR71x\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STR75X_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\STR75x\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STR75X_GCC
|
||||||
|
#include "..\..\Source\portable\GCC\STR75x\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STR91X_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\STR91x\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_H8S
|
||||||
|
#include "../../Source/portable/GCC/H8S2329/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_AT91FR40008
|
||||||
|
#include "../../Source/portable/GCC/ARM7_AT91FR40008/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RVDS_ARMCM3_LM3S102
|
||||||
|
#include "../../Source/portable/RVDS/ARM_CM3/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_ARMCM3_LM3S102
|
||||||
|
#include "../../Source/portable/GCC/ARM_CM3/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_ARMCM3
|
||||||
|
#include "../../Source/portable/GCC/ARM_CM3/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IAR_ARM_CM3
|
||||||
|
#include "../../Source/portable/IAR/ARM_CM3/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IAR_ARMCM3_LM
|
||||||
|
#include "../../Source/portable/IAR/ARM_CM3/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HCS12_CODE_WARRIOR
|
||||||
|
#include "../../Source/portable/CodeWarrior/HCS12/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MICROBLAZE_GCC
|
||||||
|
#include "../../Source/portable/GCC/MicroBlaze/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TERN_EE
|
||||||
|
#include "..\..\Source\portable\Paradigm\Tern_EE\small\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_HCS12
|
||||||
|
#include "../../Source/portable/GCC/HCS12/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_MCF5235
|
||||||
|
#include "../../Source/portable/GCC/MCF5235/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COLDFIRE_V2_GCC
|
||||||
|
#include "../../../Source/portable/GCC/ColdFire_V2/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COLDFIRE_V2_CODEWARRIOR
|
||||||
|
#include "../../Source/portable/CodeWarrior/ColdFire_V2/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_PPC405
|
||||||
|
#include "../../Source/portable/GCC/PPC405_Xilinx/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_PPC440
|
||||||
|
#include "../../Source/portable/GCC/PPC440_Xilinx/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _16FX_SOFTUNE
|
||||||
|
#include "..\..\Source\portable\Softune\MB96340\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BCC_INDUSTRIAL_PC_PORT
|
||||||
|
|
||||||
|
/* A short file name has to be used in place of the normal
|
||||||
|
* FreeRTOSConfig.h when using the Borland compiler. */
|
||||||
|
#include "frconfig.h"
|
||||||
|
#include "..\portable\BCC\16BitDOS\PC\prtmacro.h"
|
||||||
|
typedef void ( __interrupt __far * pxISR )();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BCC_FLASH_LITE_186_PORT
|
||||||
|
|
||||||
|
/* A short file name has to be used in place of the normal
|
||||||
|
* FreeRTOSConfig.h when using the Borland compiler. */
|
||||||
|
#include "frconfig.h"
|
||||||
|
#include "..\portable\BCC\16BitDOS\flsh186\prtmacro.h"
|
||||||
|
typedef void ( __interrupt __far * pxISR )();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#ifdef __AVR32_AVR32A__
|
||||||
|
#include "portmacro.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __ICCAVR32__
|
||||||
|
#ifdef __CORE__
|
||||||
|
#if __CORE__ == __AVR32A__
|
||||||
|
#include "portmacro.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __91467D
|
||||||
|
#include "portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __96340
|
||||||
|
#include "portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __IAR_V850ES_Fx3__
|
||||||
|
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_V850ES_Jx3__
|
||||||
|
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_V850ES_Jx3_L__
|
||||||
|
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_V850ES_Jx2__
|
||||||
|
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_V850ES_Hx2__
|
||||||
|
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_78K0R_Kx3__
|
||||||
|
#include "../../Source/portable/IAR/78K0R/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_78K0R_Kx3L__
|
||||||
|
#include "../../Source/portable/IAR/78K0R/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* DEPRECATED_DEFINITIONS_H */
|
||||||
@@ -0,0 +1,848 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EVENT_GROUPS_H
|
||||||
|
#define EVENT_GROUPS_H
|
||||||
|
|
||||||
|
#ifndef INC_FREERTOS_H
|
||||||
|
#error "include FreeRTOS.h" must appear in source files before "include event_groups.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* FreeRTOS includes. */
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
|
/* The following bit fields convey control information in a task's event list
|
||||||
|
* item value. It is important they don't clash with the
|
||||||
|
* taskEVENT_LIST_ITEM_VALUE_IN_USE definition. */
|
||||||
|
#if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS )
|
||||||
|
#define eventCLEAR_EVENTS_ON_EXIT_BIT ( ( uint16_t ) 0x0100U )
|
||||||
|
#define eventUNBLOCKED_DUE_TO_BIT_SET ( ( uint16_t ) 0x0200U )
|
||||||
|
#define eventWAIT_FOR_ALL_BITS ( ( uint16_t ) 0x0400U )
|
||||||
|
#define eventEVENT_BITS_CONTROL_BYTES ( ( uint16_t ) 0xff00U )
|
||||||
|
#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS )
|
||||||
|
#define eventCLEAR_EVENTS_ON_EXIT_BIT ( ( uint32_t ) 0x01000000U )
|
||||||
|
#define eventUNBLOCKED_DUE_TO_BIT_SET ( ( uint32_t ) 0x02000000U )
|
||||||
|
#define eventWAIT_FOR_ALL_BITS ( ( uint32_t ) 0x04000000U )
|
||||||
|
#define eventEVENT_BITS_CONTROL_BYTES ( ( uint32_t ) 0xff000000U )
|
||||||
|
#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_64_BITS )
|
||||||
|
#define eventCLEAR_EVENTS_ON_EXIT_BIT ( ( uint64_t ) 0x0100000000000000U )
|
||||||
|
#define eventUNBLOCKED_DUE_TO_BIT_SET ( ( uint64_t ) 0x0200000000000000U )
|
||||||
|
#define eventWAIT_FOR_ALL_BITS ( ( uint64_t ) 0x0400000000000000U )
|
||||||
|
#define eventEVENT_BITS_CONTROL_BYTES ( ( uint64_t ) 0xff00000000000000U )
|
||||||
|
#endif /* if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS ) */
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event group is a collection of bits to which an application can assign a
|
||||||
|
* meaning. For example, an application may create an event group to convey
|
||||||
|
* the status of various CAN bus related events in which bit 0 might mean "A CAN
|
||||||
|
* message has been received and is ready for processing", bit 1 might mean "The
|
||||||
|
* application has queued a message that is ready for sending onto the CAN
|
||||||
|
* network", and bit 2 might mean "It is time to send a SYNC message onto the
|
||||||
|
* CAN network" etc. A task can then test the bit values to see which events
|
||||||
|
* are active, and optionally enter the Blocked state to wait for a specified
|
||||||
|
* bit or a group of specified bits to be active. To continue the CAN bus
|
||||||
|
* example, a CAN controlling task can enter the Blocked state (and therefore
|
||||||
|
* not consume any processing time) until either bit 0, bit 1 or bit 2 are
|
||||||
|
* active, at which time the bit that was actually active would inform the task
|
||||||
|
* which action it had to take (process a received message, send a message, or
|
||||||
|
* send a SYNC).
|
||||||
|
*
|
||||||
|
* The event groups implementation contains intelligence to avoid race
|
||||||
|
* conditions that would otherwise occur were an application to use a simple
|
||||||
|
* variable for the same purpose. This is particularly important with respect
|
||||||
|
* to when a bit within an event group is to be cleared, and when bits have to
|
||||||
|
* be set and then tested atomically - as is the case where event groups are
|
||||||
|
* used to create a synchronisation point between multiple tasks (a
|
||||||
|
* 'rendezvous').
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*
|
||||||
|
* Type by which event groups are referenced. For example, a call to
|
||||||
|
* xEventGroupCreate() returns an EventGroupHandle_t variable that can then
|
||||||
|
* be used as a parameter to other event group functions.
|
||||||
|
*
|
||||||
|
* \defgroup EventGroupHandle_t EventGroupHandle_t
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
struct EventGroupDef_t;
|
||||||
|
typedef struct EventGroupDef_t * EventGroupHandle_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The type that holds event bits always matches TickType_t - therefore the
|
||||||
|
* number of bits it holds is set by configTICK_TYPE_WIDTH_IN_BITS (16 bits if set to 0,
|
||||||
|
* 32 bits if set to 1, 64 bits if set to 2.
|
||||||
|
*
|
||||||
|
* \defgroup EventBits_t EventBits_t
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
typedef TickType_t EventBits_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* EventGroupHandle_t xEventGroupCreate( void );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Create a new event group.
|
||||||
|
*
|
||||||
|
* Internally, within the FreeRTOS implementation, event groups use a [small]
|
||||||
|
* block of memory, in which the event group's structure is stored. If an event
|
||||||
|
* groups is created using xEventGroupCreate() then the required memory is
|
||||||
|
* automatically dynamically allocated inside the xEventGroupCreate() function.
|
||||||
|
* (see https://www.FreeRTOS.org/a00111.html). If an event group is created
|
||||||
|
* using xEventGroupCreateStatic() then the application writer must instead
|
||||||
|
* provide the memory that will get used by the event group.
|
||||||
|
* xEventGroupCreateStatic() therefore allows an event group to be created
|
||||||
|
* without using any dynamic memory allocation.
|
||||||
|
*
|
||||||
|
* Although event groups are not related to ticks, for internal implementation
|
||||||
|
* reasons the number of bits available for use in an event group is dependent
|
||||||
|
* on the configTICK_TYPE_WIDTH_IN_BITS setting in FreeRTOSConfig.h. If
|
||||||
|
* configTICK_TYPE_WIDTH_IN_BITS is 0 then each event group contains 8 usable bits (bit
|
||||||
|
* 0 to bit 7). If configTICK_TYPE_WIDTH_IN_BITS is set to 1 then each event group has
|
||||||
|
* 24 usable bits (bit 0 to bit 23). If configTICK_TYPE_WIDTH_IN_BITS is set to 2 then
|
||||||
|
* each event group has 56 usable bits (bit 0 to bit 53). The EventBits_t type
|
||||||
|
* is used to store event bits within an event group.
|
||||||
|
*
|
||||||
|
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupCreate()
|
||||||
|
* to be available.
|
||||||
|
*
|
||||||
|
* @return If the event group was created then a handle to the event group is
|
||||||
|
* returned. If there was insufficient FreeRTOS heap available to create the
|
||||||
|
* event group then NULL is returned. See https://www.FreeRTOS.org/a00111.html
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // Declare a variable to hold the created event group.
|
||||||
|
* EventGroupHandle_t xCreatedEventGroup;
|
||||||
|
*
|
||||||
|
* // Attempt to create the event group.
|
||||||
|
* xCreatedEventGroup = xEventGroupCreate();
|
||||||
|
*
|
||||||
|
* // Was the event group created successfully?
|
||||||
|
* if( xCreatedEventGroup == NULL )
|
||||||
|
* {
|
||||||
|
* // The event group was not created because there was insufficient
|
||||||
|
* // FreeRTOS heap available.
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // The event group was created.
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xEventGroupCreate xEventGroupCreate
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
|
||||||
|
EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t * pxEventGroupBuffer );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Create a new event group.
|
||||||
|
*
|
||||||
|
* Internally, within the FreeRTOS implementation, event groups use a [small]
|
||||||
|
* block of memory, in which the event group's structure is stored. If an event
|
||||||
|
* groups is created using xEventGroupCreate() then the required memory is
|
||||||
|
* automatically dynamically allocated inside the xEventGroupCreate() function.
|
||||||
|
* (see https://www.FreeRTOS.org/a00111.html). If an event group is created
|
||||||
|
* using xEventGroupCreateStatic() then the application writer must instead
|
||||||
|
* provide the memory that will get used by the event group.
|
||||||
|
* xEventGroupCreateStatic() therefore allows an event group to be created
|
||||||
|
* without using any dynamic memory allocation.
|
||||||
|
*
|
||||||
|
* Although event groups are not related to ticks, for internal implementation
|
||||||
|
* reasons the number of bits available for use in an event group is dependent
|
||||||
|
* on the configTICK_TYPE_WIDTH_IN_BITS setting in FreeRTOSConfig.h. If
|
||||||
|
* configTICK_TYPE_WIDTH_IN_BITS is 0 then each event group contains 8 usable bits (bit
|
||||||
|
* 0 to bit 7). If configTICK_TYPE_WIDTH_IN_BITS is set to 1 then each event group has
|
||||||
|
* 24 usable bits (bit 0 to bit 23). If configTICK_TYPE_WIDTH_IN_BITS is set to 2 then
|
||||||
|
* each event group has 56 usable bits (bit 0 to bit 53). The EventBits_t type
|
||||||
|
* is used to store event bits within an event group.
|
||||||
|
*
|
||||||
|
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupCreateStatic()
|
||||||
|
* to be available.
|
||||||
|
*
|
||||||
|
* @param pxEventGroupBuffer pxEventGroupBuffer must point to a variable of type
|
||||||
|
* StaticEventGroup_t, which will be then be used to hold the event group's data
|
||||||
|
* structures, removing the need for the memory to be allocated dynamically.
|
||||||
|
*
|
||||||
|
* @return If the event group was created then a handle to the event group is
|
||||||
|
* returned. If pxEventGroupBuffer was NULL then NULL is returned.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // StaticEventGroup_t is a publicly accessible structure that has the same
|
||||||
|
* // size and alignment requirements as the real event group structure. It is
|
||||||
|
* // provided as a mechanism for applications to know the size of the event
|
||||||
|
* // group (which is dependent on the architecture and configuration file
|
||||||
|
* // settings) without breaking the strict data hiding policy by exposing the
|
||||||
|
* // real event group internals. This StaticEventGroup_t variable is passed
|
||||||
|
* // into the xSemaphoreCreateEventGroupStatic() function and is used to store
|
||||||
|
* // the event group's data structures
|
||||||
|
* StaticEventGroup_t xEventGroupBuffer;
|
||||||
|
*
|
||||||
|
* // Create the event group without dynamically allocating any memory.
|
||||||
|
* xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||||
|
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
|
||||||
|
* const EventBits_t uxBitsToWaitFor,
|
||||||
|
* const BaseType_t xClearOnExit,
|
||||||
|
* const BaseType_t xWaitForAllBits,
|
||||||
|
* const TickType_t xTicksToWait );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* [Potentially] block to wait for one or more bits to be set within a
|
||||||
|
* previously created event group.
|
||||||
|
*
|
||||||
|
* This function cannot be called from an interrupt.
|
||||||
|
*
|
||||||
|
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupWaitBits()
|
||||||
|
* to be available.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are being tested. The
|
||||||
|
* event group must have previously been created using a call to
|
||||||
|
* xEventGroupCreate().
|
||||||
|
*
|
||||||
|
* @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test
|
||||||
|
* inside the event group. For example, to wait for bit 0 and/or bit 2 set
|
||||||
|
* uxBitsToWaitFor to 0x05. To wait for bits 0 and/or bit 1 and/or bit 2 set
|
||||||
|
* uxBitsToWaitFor to 0x07. Etc.
|
||||||
|
*
|
||||||
|
* @param xClearOnExit If xClearOnExit is set to pdTRUE then any bits within
|
||||||
|
* uxBitsToWaitFor that are set within the event group will be cleared before
|
||||||
|
* xEventGroupWaitBits() returns if the wait condition was met (if the function
|
||||||
|
* returns for a reason other than a timeout). If xClearOnExit is set to
|
||||||
|
* pdFALSE then the bits set in the event group are not altered when the call to
|
||||||
|
* xEventGroupWaitBits() returns.
|
||||||
|
*
|
||||||
|
* @param xWaitForAllBits If xWaitForAllBits is set to pdTRUE then
|
||||||
|
* xEventGroupWaitBits() will return when either all the bits in uxBitsToWaitFor
|
||||||
|
* are set or the specified block time expires. If xWaitForAllBits is set to
|
||||||
|
* pdFALSE then xEventGroupWaitBits() will return when any one of the bits set
|
||||||
|
* in uxBitsToWaitFor is set or the specified block time expires. The block
|
||||||
|
* time is specified by the xTicksToWait parameter.
|
||||||
|
*
|
||||||
|
* @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait
|
||||||
|
* for one/all (depending on the xWaitForAllBits value) of the bits specified by
|
||||||
|
* uxBitsToWaitFor to become set. A value of portMAX_DELAY can be used to block
|
||||||
|
* indefinitely (provided INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h).
|
||||||
|
*
|
||||||
|
* @return The value of the event group at the time either the bits being waited
|
||||||
|
* for became set, or the block time expired. Test the return value to know
|
||||||
|
* which bits were set. If xEventGroupWaitBits() returned because its timeout
|
||||||
|
* expired then not all the bits being waited for will be set. If
|
||||||
|
* xEventGroupWaitBits() returned because the bits it was waiting for were set
|
||||||
|
* then the returned value is the event group value before any bits were
|
||||||
|
* automatically cleared in the case that xClearOnExit parameter was set to
|
||||||
|
* pdTRUE.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* #define BIT_0 ( 1 << 0 )
|
||||||
|
* #define BIT_4 ( 1 << 4 )
|
||||||
|
*
|
||||||
|
* void aFunction( EventGroupHandle_t xEventGroup )
|
||||||
|
* {
|
||||||
|
* EventBits_t uxBits;
|
||||||
|
* const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
|
||||||
|
*
|
||||||
|
* // Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
|
||||||
|
* // the event group. Clear the bits before exiting.
|
||||||
|
* uxBits = xEventGroupWaitBits(
|
||||||
|
* xEventGroup, // The event group being tested.
|
||||||
|
* BIT_0 | BIT_4, // The bits within the event group to wait for.
|
||||||
|
* pdTRUE, // BIT_0 and BIT_4 should be cleared before returning.
|
||||||
|
* pdFALSE, // Don't wait for both bits, either bit will do.
|
||||||
|
* xTicksToWait ); // Wait a maximum of 100ms for either bit to be set.
|
||||||
|
*
|
||||||
|
* if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
|
||||||
|
* {
|
||||||
|
* // xEventGroupWaitBits() returned because both bits were set.
|
||||||
|
* }
|
||||||
|
* else if( ( uxBits & BIT_0 ) != 0 )
|
||||||
|
* {
|
||||||
|
* // xEventGroupWaitBits() returned because just BIT_0 was set.
|
||||||
|
* }
|
||||||
|
* else if( ( uxBits & BIT_4 ) != 0 )
|
||||||
|
* {
|
||||||
|
* // xEventGroupWaitBits() returned because just BIT_4 was set.
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // xEventGroupWaitBits() returned because xTicksToWait ticks passed
|
||||||
|
* // without either BIT_0 or BIT_4 becoming set.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xEventGroupWaitBits xEventGroupWaitBits
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToWaitFor,
|
||||||
|
const BaseType_t xClearOnExit,
|
||||||
|
const BaseType_t xWaitForAllBits,
|
||||||
|
TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Clear bits within an event group. This function cannot be called from an
|
||||||
|
* interrupt.
|
||||||
|
*
|
||||||
|
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupClearBits()
|
||||||
|
* to be available.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are to be cleared.
|
||||||
|
*
|
||||||
|
* @param uxBitsToClear A bitwise value that indicates the bit or bits to clear
|
||||||
|
* in the event group. For example, to clear bit 3 only, set uxBitsToClear to
|
||||||
|
* 0x08. To clear bit 3 and bit 0 set uxBitsToClear to 0x09.
|
||||||
|
*
|
||||||
|
* @return The value of the event group before the specified bits were cleared.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* #define BIT_0 ( 1 << 0 )
|
||||||
|
* #define BIT_4 ( 1 << 4 )
|
||||||
|
*
|
||||||
|
* void aFunction( EventGroupHandle_t xEventGroup )
|
||||||
|
* {
|
||||||
|
* EventBits_t uxBits;
|
||||||
|
*
|
||||||
|
* // Clear bit 0 and bit 4 in xEventGroup.
|
||||||
|
* uxBits = xEventGroupClearBits(
|
||||||
|
* xEventGroup, // The event group being updated.
|
||||||
|
* BIT_0 | BIT_4 );// The bits being cleared.
|
||||||
|
*
|
||||||
|
* if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
|
||||||
|
* {
|
||||||
|
* // Both bit 0 and bit 4 were set before xEventGroupClearBits() was
|
||||||
|
* // called. Both will now be clear (not set).
|
||||||
|
* }
|
||||||
|
* else if( ( uxBits & BIT_0 ) != 0 )
|
||||||
|
* {
|
||||||
|
* // Bit 0 was set before xEventGroupClearBits() was called. It will
|
||||||
|
* // now be clear.
|
||||||
|
* }
|
||||||
|
* else if( ( uxBits & BIT_4 ) != 0 )
|
||||||
|
* {
|
||||||
|
* // Bit 4 was set before xEventGroupClearBits() was called. It will
|
||||||
|
* // now be clear.
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // Neither bit 0 nor bit 4 were set in the first place.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xEventGroupClearBits xEventGroupClearBits
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* A version of xEventGroupClearBits() that can be called from an interrupt.
|
||||||
|
*
|
||||||
|
* Setting bits in an event group is not a deterministic operation because there
|
||||||
|
* are an unknown number of tasks that may be waiting for the bit or bits being
|
||||||
|
* set. FreeRTOS does not allow nondeterministic operations to be performed
|
||||||
|
* while interrupts are disabled, so protects event groups that are accessed
|
||||||
|
* from tasks by suspending the scheduler rather than disabling interrupts. As
|
||||||
|
* a result event groups cannot be accessed directly from an interrupt service
|
||||||
|
* routine. Therefore xEventGroupClearBitsFromISR() sends a message to the
|
||||||
|
* timer task to have the clear operation performed in the context of the timer
|
||||||
|
* task.
|
||||||
|
*
|
||||||
|
* @note If this function returns pdPASS then the timer task is ready to run
|
||||||
|
* and a portYIELD_FROM_ISR(pdTRUE) should be executed to perform the needed
|
||||||
|
* clear on the event group. This behavior is different from
|
||||||
|
* xEventGroupSetBitsFromISR because the parameter xHigherPriorityTaskWoken is
|
||||||
|
* not present.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are to be cleared.
|
||||||
|
*
|
||||||
|
* @param uxBitsToClear A bitwise value that indicates the bit or bits to clear.
|
||||||
|
* For example, to clear bit 3 only, set uxBitsToClear to 0x08. To clear bit 3
|
||||||
|
* and bit 0 set uxBitsToClear to 0x09.
|
||||||
|
*
|
||||||
|
* @return If the request to execute the function was posted successfully then
|
||||||
|
* pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned
|
||||||
|
* if the timer service queue was full.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* #define BIT_0 ( 1 << 0 )
|
||||||
|
* #define BIT_4 ( 1 << 4 )
|
||||||
|
*
|
||||||
|
* // An event group which it is assumed has already been created by a call to
|
||||||
|
* // xEventGroupCreate().
|
||||||
|
* EventGroupHandle_t xEventGroup;
|
||||||
|
*
|
||||||
|
* void anInterruptHandler( void )
|
||||||
|
* {
|
||||||
|
* // Clear bit 0 and bit 4 in xEventGroup.
|
||||||
|
* xResult = xEventGroupClearBitsFromISR(
|
||||||
|
* xEventGroup, // The event group being updated.
|
||||||
|
* BIT_0 | BIT_4 ); // The bits being set.
|
||||||
|
*
|
||||||
|
* if( xResult == pdPASS )
|
||||||
|
* {
|
||||||
|
* // The message was posted successfully.
|
||||||
|
* portYIELD_FROM_ISR(pdTRUE);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xEventGroupClearBitsFromISR xEventGroupClearBitsFromISR
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
#if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )
|
||||||
|
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif /* if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Set bits within an event group.
|
||||||
|
* This function cannot be called from an interrupt. xEventGroupSetBitsFromISR()
|
||||||
|
* is a version that can be called from an interrupt.
|
||||||
|
*
|
||||||
|
* Setting bits in an event group will automatically unblock tasks that are
|
||||||
|
* blocked waiting for the bits.
|
||||||
|
*
|
||||||
|
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupSetBits()
|
||||||
|
* to be available.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are to be set.
|
||||||
|
*
|
||||||
|
* @param uxBitsToSet A bitwise value that indicates the bit or bits to set.
|
||||||
|
* For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3
|
||||||
|
* and bit 0 set uxBitsToSet to 0x09.
|
||||||
|
*
|
||||||
|
* @return The value of the event group at the time the call to
|
||||||
|
* xEventGroupSetBits() returns. Returned value might have the bits specified
|
||||||
|
* by the uxBitsToSet parameter cleared if setting a bit results in a task
|
||||||
|
* that was waiting for the bit leaving the blocked state then it is possible
|
||||||
|
* the bit will be cleared automatically (see the xClearBitOnExit parameter
|
||||||
|
* of xEventGroupWaitBits()).
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* #define BIT_0 ( 1 << 0 )
|
||||||
|
* #define BIT_4 ( 1 << 4 )
|
||||||
|
*
|
||||||
|
* void aFunction( EventGroupHandle_t xEventGroup )
|
||||||
|
* {
|
||||||
|
* EventBits_t uxBits;
|
||||||
|
*
|
||||||
|
* // Set bit 0 and bit 4 in xEventGroup.
|
||||||
|
* uxBits = xEventGroupSetBits(
|
||||||
|
* xEventGroup, // The event group being updated.
|
||||||
|
* BIT_0 | BIT_4 );// The bits being set.
|
||||||
|
*
|
||||||
|
* if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
|
||||||
|
* {
|
||||||
|
* // Both bit 0 and bit 4 remained set when the function returned.
|
||||||
|
* }
|
||||||
|
* else if( ( uxBits & BIT_0 ) != 0 )
|
||||||
|
* {
|
||||||
|
* // Bit 0 remained set when the function returned, but bit 4 was
|
||||||
|
* // cleared. It might be that bit 4 was cleared automatically as a
|
||||||
|
* // task that was waiting for bit 4 was removed from the Blocked
|
||||||
|
* // state.
|
||||||
|
* }
|
||||||
|
* else if( ( uxBits & BIT_4 ) != 0 )
|
||||||
|
* {
|
||||||
|
* // Bit 4 remained set when the function returned, but bit 0 was
|
||||||
|
* // cleared. It might be that bit 0 was cleared automatically as a
|
||||||
|
* // task that was waiting for bit 0 was removed from the Blocked
|
||||||
|
* // state.
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // Neither bit 0 nor bit 4 remained set. It might be that a task
|
||||||
|
* // was waiting for both of the bits to be set, and the bits were
|
||||||
|
* // cleared as the task left the Blocked state.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xEventGroupSetBits xEventGroupSetBits
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* A version of xEventGroupSetBits() that can be called from an interrupt.
|
||||||
|
*
|
||||||
|
* Setting bits in an event group is not a deterministic operation because there
|
||||||
|
* are an unknown number of tasks that may be waiting for the bit or bits being
|
||||||
|
* set. FreeRTOS does not allow nondeterministic operations to be performed in
|
||||||
|
* interrupts or from critical sections. Therefore xEventGroupSetBitsFromISR()
|
||||||
|
* sends a message to the timer task to have the set operation performed in the
|
||||||
|
* context of the timer task - where a scheduler lock is used in place of a
|
||||||
|
* critical section.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are to be set.
|
||||||
|
*
|
||||||
|
* @param uxBitsToSet A bitwise value that indicates the bit or bits to set.
|
||||||
|
* For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3
|
||||||
|
* and bit 0 set uxBitsToSet to 0x09.
|
||||||
|
*
|
||||||
|
* @param pxHigherPriorityTaskWoken As mentioned above, calling this function
|
||||||
|
* will result in a message being sent to the timer daemon task. If the
|
||||||
|
* priority of the timer daemon task is higher than the priority of the
|
||||||
|
* currently running task (the task the interrupt interrupted) then
|
||||||
|
* *pxHigherPriorityTaskWoken will be set to pdTRUE by
|
||||||
|
* xEventGroupSetBitsFromISR(), indicating that a context switch should be
|
||||||
|
* requested before the interrupt exits. For that reason
|
||||||
|
* *pxHigherPriorityTaskWoken must be initialised to pdFALSE. See the
|
||||||
|
* example code below.
|
||||||
|
*
|
||||||
|
* @return If the request to execute the function was posted successfully then
|
||||||
|
* pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned
|
||||||
|
* if the timer service queue was full.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* #define BIT_0 ( 1 << 0 )
|
||||||
|
* #define BIT_4 ( 1 << 4 )
|
||||||
|
*
|
||||||
|
* // An event group which it is assumed has already been created by a call to
|
||||||
|
* // xEventGroupCreate().
|
||||||
|
* EventGroupHandle_t xEventGroup;
|
||||||
|
*
|
||||||
|
* void anInterruptHandler( void )
|
||||||
|
* {
|
||||||
|
* BaseType_t xHigherPriorityTaskWoken, xResult;
|
||||||
|
*
|
||||||
|
* // xHigherPriorityTaskWoken must be initialised to pdFALSE.
|
||||||
|
* xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
*
|
||||||
|
* // Set bit 0 and bit 4 in xEventGroup.
|
||||||
|
* xResult = xEventGroupSetBitsFromISR(
|
||||||
|
* xEventGroup, // The event group being updated.
|
||||||
|
* BIT_0 | BIT_4 // The bits being set.
|
||||||
|
* &xHigherPriorityTaskWoken );
|
||||||
|
*
|
||||||
|
* // Was the message posted successfully?
|
||||||
|
* if( xResult == pdPASS )
|
||||||
|
* {
|
||||||
|
* // If xHigherPriorityTaskWoken is now set to pdTRUE then a context
|
||||||
|
* // switch should be requested. The macro used is port specific and
|
||||||
|
* // will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() -
|
||||||
|
* // refer to the documentation page for the port being used.
|
||||||
|
* portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
#if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )
|
||||||
|
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToSet,
|
||||||
|
BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif /* if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
|
||||||
|
* const EventBits_t uxBitsToSet,
|
||||||
|
* const EventBits_t uxBitsToWaitFor,
|
||||||
|
* TickType_t xTicksToWait );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Atomically set bits within an event group, then wait for a combination of
|
||||||
|
* bits to be set within the same event group. This functionality is typically
|
||||||
|
* used to synchronise multiple tasks, where each task has to wait for the other
|
||||||
|
* tasks to reach a synchronisation point before proceeding.
|
||||||
|
*
|
||||||
|
* This function cannot be used from an interrupt.
|
||||||
|
*
|
||||||
|
* The function will return before its block time expires if the bits specified
|
||||||
|
* by the uxBitsToWait parameter are set, or become set within that time. In
|
||||||
|
* this case all the bits specified by uxBitsToWait will be automatically
|
||||||
|
* cleared before the function returns.
|
||||||
|
*
|
||||||
|
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupSync()
|
||||||
|
* to be available.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are being tested. The
|
||||||
|
* event group must have previously been created using a call to
|
||||||
|
* xEventGroupCreate().
|
||||||
|
*
|
||||||
|
* @param uxBitsToSet The bits to set in the event group before determining
|
||||||
|
* if, and possibly waiting for, all the bits specified by the uxBitsToWait
|
||||||
|
* parameter are set.
|
||||||
|
*
|
||||||
|
* @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test
|
||||||
|
* inside the event group. For example, to wait for bit 0 and bit 2 set
|
||||||
|
* uxBitsToWaitFor to 0x05. To wait for bits 0 and bit 1 and bit 2 set
|
||||||
|
* uxBitsToWaitFor to 0x07. Etc.
|
||||||
|
*
|
||||||
|
* @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait
|
||||||
|
* for all of the bits specified by uxBitsToWaitFor to become set.
|
||||||
|
*
|
||||||
|
* @return The value of the event group at the time either the bits being waited
|
||||||
|
* for became set, or the block time expired. Test the return value to know
|
||||||
|
* which bits were set. If xEventGroupSync() returned because its timeout
|
||||||
|
* expired then not all the bits being waited for will be set. If
|
||||||
|
* xEventGroupSync() returned because all the bits it was waiting for were
|
||||||
|
* set then the returned value is the event group value before any bits were
|
||||||
|
* automatically cleared.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* @code{c}
|
||||||
|
* // Bits used by the three tasks.
|
||||||
|
* #define TASK_0_BIT ( 1 << 0 )
|
||||||
|
* #define TASK_1_BIT ( 1 << 1 )
|
||||||
|
* #define TASK_2_BIT ( 1 << 2 )
|
||||||
|
*
|
||||||
|
* #define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )
|
||||||
|
*
|
||||||
|
* // Use an event group to synchronise three tasks. It is assumed this event
|
||||||
|
* // group has already been created elsewhere.
|
||||||
|
* EventGroupHandle_t xEventBits;
|
||||||
|
*
|
||||||
|
* void vTask0( void *pvParameters )
|
||||||
|
* {
|
||||||
|
* EventBits_t uxReturn;
|
||||||
|
* TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
|
||||||
|
*
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // Perform task functionality here.
|
||||||
|
*
|
||||||
|
* // Set bit 0 in the event flag to note this task has reached the
|
||||||
|
* // sync point. The other two tasks will set the other two bits defined
|
||||||
|
* // by ALL_SYNC_BITS. All three tasks have reached the synchronisation
|
||||||
|
* // point when all the ALL_SYNC_BITS are set. Wait a maximum of 100ms
|
||||||
|
* // for this to happen.
|
||||||
|
* uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait );
|
||||||
|
*
|
||||||
|
* if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
|
||||||
|
* {
|
||||||
|
* // All three tasks reached the synchronisation point before the call
|
||||||
|
* // to xEventGroupSync() timed out.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void vTask1( void *pvParameters )
|
||||||
|
* {
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // Perform task functionality here.
|
||||||
|
*
|
||||||
|
* // Set bit 1 in the event flag to note this task has reached the
|
||||||
|
* // synchronisation point. The other two tasks will set the other two
|
||||||
|
* // bits defined by ALL_SYNC_BITS. All three tasks have reached the
|
||||||
|
* // synchronisation point when all the ALL_SYNC_BITS are set. Wait
|
||||||
|
* // indefinitely for this to happen.
|
||||||
|
* xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
|
||||||
|
*
|
||||||
|
* // xEventGroupSync() was called with an indefinite block time, so
|
||||||
|
* // this task will only reach here if the synchronisation was made by all
|
||||||
|
* // three tasks, so there is no need to test the return value.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void vTask2( void *pvParameters )
|
||||||
|
* {
|
||||||
|
* for( ;; )
|
||||||
|
* {
|
||||||
|
* // Perform task functionality here.
|
||||||
|
*
|
||||||
|
* // Set bit 2 in the event flag to note this task has reached the
|
||||||
|
* // synchronisation point. The other two tasks will set the other two
|
||||||
|
* // bits defined by ALL_SYNC_BITS. All three tasks have reached the
|
||||||
|
* // synchronisation point when all the ALL_SYNC_BITS are set. Wait
|
||||||
|
* // indefinitely for this to happen.
|
||||||
|
* xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
|
||||||
|
*
|
||||||
|
* // xEventGroupSync() was called with an indefinite block time, so
|
||||||
|
* // this task will only reach here if the synchronisation was made by all
|
||||||
|
* // three tasks, so there is no need to test the return value.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xEventGroupSync xEventGroupSync
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToSet,
|
||||||
|
const EventBits_t uxBitsToWaitFor,
|
||||||
|
TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Returns the current value of the bits in an event group. This function
|
||||||
|
* cannot be used from an interrupt.
|
||||||
|
*
|
||||||
|
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupGetBits()
|
||||||
|
* to be available.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group being queried.
|
||||||
|
*
|
||||||
|
* @return The event group bits at the time xEventGroupGetBits() was called.
|
||||||
|
*
|
||||||
|
* \defgroup xEventGroupGetBits xEventGroupGetBits
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( ( xEventGroup ), 0 )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* A version of xEventGroupGetBits() that can be called from an ISR.
|
||||||
|
*
|
||||||
|
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupGetBitsFromISR()
|
||||||
|
* to be available.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group being queried.
|
||||||
|
*
|
||||||
|
* @return The event group bits at the time xEventGroupGetBitsFromISR() was called.
|
||||||
|
*
|
||||||
|
* \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* void xEventGroupDelete( EventGroupHandle_t xEventGroup );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Delete an event group that was previously created by a call to
|
||||||
|
* xEventGroupCreate(). Tasks that are blocked on the event group will be
|
||||||
|
* unblocked and obtain 0 as the event group's value.
|
||||||
|
*
|
||||||
|
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for vEventGroupDelete()
|
||||||
|
* to be available.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group being deleted.
|
||||||
|
*/
|
||||||
|
void vEventGroupDelete( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
* @code{c}
|
||||||
|
* BaseType_t xEventGroupGetStaticBuffer( EventGroupHandle_t xEventGroup,
|
||||||
|
* StaticEventGroup_t ** ppxEventGroupBuffer );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Retrieve a pointer to a statically created event groups's data structure
|
||||||
|
* buffer. It is the same buffer that is supplied at the time of creation.
|
||||||
|
*
|
||||||
|
* The configUSE_EVENT_GROUPS configuration constant must be set to 1 for xEventGroupGetStaticBuffer()
|
||||||
|
* to be available.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group for which to retrieve the buffer.
|
||||||
|
*
|
||||||
|
* @param ppxEventGroupBuffer Used to return a pointer to the event groups's
|
||||||
|
* data structure buffer.
|
||||||
|
*
|
||||||
|
* @return pdTRUE if the buffer was retrieved, pdFALSE otherwise.
|
||||||
|
*/
|
||||||
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||||
|
BaseType_t xEventGroupGetStaticBuffer( EventGroupHandle_t xEventGroup,
|
||||||
|
StaticEventGroup_t ** ppxEventGroupBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||||||
|
|
||||||
|
/* For internal use only. */
|
||||||
|
void vEventGroupSetBitsCallback( void * pvEventGroup,
|
||||||
|
uint32_t ulBitsToSet ) PRIVILEGED_FUNCTION;
|
||||||
|
void vEventGroupClearBitsCallback( void * pvEventGroup,
|
||||||
|
uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
|
||||||
|
#if ( configUSE_TRACE_FACILITY == 1 )
|
||||||
|
UBaseType_t uxEventGroupGetNumber( void * xEventGroup ) PRIVILEGED_FUNCTION;
|
||||||
|
void vEventGroupSetNumber( void * xEventGroup,
|
||||||
|
UBaseType_t uxEventGroupNumber ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
#endif /* EVENT_GROUPS_H */
|
||||||
@@ -0,0 +1,511 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the list implementation used by the scheduler. While it is tailored
|
||||||
|
* heavily for the schedulers needs, it is also available for use by
|
||||||
|
* application code.
|
||||||
|
*
|
||||||
|
* list_ts can only store pointers to list_item_ts. Each ListItem_t contains a
|
||||||
|
* numeric value (xItemValue). Most of the time the lists are sorted in
|
||||||
|
* ascending item value order.
|
||||||
|
*
|
||||||
|
* Lists are created already containing one list item. The value of this
|
||||||
|
* item is the maximum possible that can be stored, it is therefore always at
|
||||||
|
* the end of the list and acts as a marker. The list member pxHead always
|
||||||
|
* points to this marker - even though it is at the tail of the list. This
|
||||||
|
* is because the tail contains a wrap back pointer to the true head of
|
||||||
|
* the list.
|
||||||
|
*
|
||||||
|
* In addition to it's value, each list item contains a pointer to the next
|
||||||
|
* item in the list (pxNext), a pointer to the list it is in (pxContainer)
|
||||||
|
* and a pointer back to the object that contains it. These later two
|
||||||
|
* pointers are included for efficiency of list manipulation. There is
|
||||||
|
* effectively a two way link between the object containing the list item and
|
||||||
|
* the list item itself.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* \page ListIntroduction List Implementation
|
||||||
|
* \ingroup FreeRTOSIntro
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LIST_H
|
||||||
|
#define LIST_H
|
||||||
|
|
||||||
|
#ifndef INC_FREERTOS_H
|
||||||
|
#error "FreeRTOS.h must be included before list.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The list structure members are modified from within interrupts, and therefore
|
||||||
|
* by rights should be declared volatile. However, they are only modified in a
|
||||||
|
* functionally atomic way (within critical sections of with the scheduler
|
||||||
|
* suspended) and are either passed by reference into a function or indexed via
|
||||||
|
* a volatile variable. Therefore, in all use cases tested so far, the volatile
|
||||||
|
* qualifier can be omitted in order to provide a moderate performance
|
||||||
|
* improvement without adversely affecting functional behaviour. The assembly
|
||||||
|
* instructions generated by the IAR, ARM and GCC compilers when the respective
|
||||||
|
* compiler's options were set for maximum optimisation has been inspected and
|
||||||
|
* deemed to be as intended. That said, as compiler technology advances, and
|
||||||
|
* especially if aggressive cross module optimisation is used (a use case that
|
||||||
|
* has not been exercised to any great extend) then it is feasible that the
|
||||||
|
* volatile qualifier will be needed for correct optimisation. It is expected
|
||||||
|
* that a compiler removing essential code because, without the volatile
|
||||||
|
* qualifier on the list structure members and with aggressive cross module
|
||||||
|
* optimisation, the compiler deemed the code unnecessary will result in
|
||||||
|
* complete and obvious failure of the scheduler. If this is ever experienced
|
||||||
|
* then the volatile qualifier can be inserted in the relevant places within the
|
||||||
|
* list structures by simply defining configLIST_VOLATILE to volatile in
|
||||||
|
* FreeRTOSConfig.h (as per the example at the bottom of this comment block).
|
||||||
|
* If configLIST_VOLATILE is not defined then the preprocessor directives below
|
||||||
|
* will simply #define configLIST_VOLATILE away completely.
|
||||||
|
*
|
||||||
|
* To use volatile list structure members then add the following line to
|
||||||
|
* FreeRTOSConfig.h (without the quotes):
|
||||||
|
* "#define configLIST_VOLATILE volatile"
|
||||||
|
*/
|
||||||
|
#ifndef configLIST_VOLATILE
|
||||||
|
#define configLIST_VOLATILE
|
||||||
|
#endif /* configSUPPORT_CROSS_MODULE_OPTIMISATION */
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/* Macros that can be used to place known values within the list structures,
|
||||||
|
* then check that the known values do not get corrupted during the execution of
|
||||||
|
* the application. These may catch the list data structures being overwritten in
|
||||||
|
* memory. They will not catch data errors caused by incorrect configuration or
|
||||||
|
* use of FreeRTOS.*/
|
||||||
|
#if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0 )
|
||||||
|
/* Define the macros to do nothing. */
|
||||||
|
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
|
||||||
|
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
|
||||||
|
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE
|
||||||
|
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE
|
||||||
|
#define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem )
|
||||||
|
#define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem )
|
||||||
|
#define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList )
|
||||||
|
#define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList )
|
||||||
|
#define listTEST_LIST_ITEM_INTEGRITY( pxItem )
|
||||||
|
#define listTEST_LIST_INTEGRITY( pxList )
|
||||||
|
#else /* if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0 ) */
|
||||||
|
/* Define macros that add new members into the list structures. */
|
||||||
|
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue1;
|
||||||
|
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue2;
|
||||||
|
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue1;
|
||||||
|
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue2;
|
||||||
|
|
||||||
|
/* Define macros that set the new structure members to known values. */
|
||||||
|
#define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue1 = pdINTEGRITY_CHECK_VALUE
|
||||||
|
#define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue2 = pdINTEGRITY_CHECK_VALUE
|
||||||
|
#define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ) ( pxList )->xListIntegrityValue1 = pdINTEGRITY_CHECK_VALUE
|
||||||
|
#define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ) ( pxList )->xListIntegrityValue2 = pdINTEGRITY_CHECK_VALUE
|
||||||
|
|
||||||
|
/* Define macros that will assert if one of the structure members does not
|
||||||
|
* contain its expected value. */
|
||||||
|
#define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
|
||||||
|
#define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
|
||||||
|
#endif /* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definition of the only type of object that a list can contain.
|
||||||
|
*/
|
||||||
|
struct xLIST;
|
||||||
|
struct xLIST_ITEM
|
||||||
|
{
|
||||||
|
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /**< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
configLIST_VOLATILE TickType_t xItemValue; /**< The value being listed. In most cases this is used to sort the list in ascending order. */
|
||||||
|
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /**< Pointer to the next ListItem_t in the list. */
|
||||||
|
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /**< Pointer to the previous ListItem_t in the list. */
|
||||||
|
void * pvOwner; /**< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
|
||||||
|
struct xLIST * configLIST_VOLATILE pxContainer; /**< Pointer to the list in which this list item is placed (if any). */
|
||||||
|
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /**< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
};
|
||||||
|
typedef struct xLIST_ITEM ListItem_t;
|
||||||
|
|
||||||
|
#if ( configUSE_MINI_LIST_ITEM == 1 )
|
||||||
|
struct xMINI_LIST_ITEM
|
||||||
|
{
|
||||||
|
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /**< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
configLIST_VOLATILE TickType_t xItemValue;
|
||||||
|
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
|
||||||
|
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
|
||||||
|
};
|
||||||
|
typedef struct xMINI_LIST_ITEM MiniListItem_t;
|
||||||
|
#else
|
||||||
|
typedef struct xLIST_ITEM MiniListItem_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definition of the type of queue used by the scheduler.
|
||||||
|
*/
|
||||||
|
typedef struct xLIST
|
||||||
|
{
|
||||||
|
listFIRST_LIST_INTEGRITY_CHECK_VALUE /**< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
|
||||||
|
ListItem_t * configLIST_VOLATILE pxIndex; /**< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
|
||||||
|
MiniListItem_t xListEnd; /**< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
|
||||||
|
listSECOND_LIST_INTEGRITY_CHECK_VALUE /**< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
} List_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to set the owner of a list item. The owner of a list item
|
||||||
|
* is the object (usually a TCB) that contains the list item.
|
||||||
|
*
|
||||||
|
* \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to get the owner of a list item. The owner of a list item
|
||||||
|
* is the object (usually a TCB) that contains the list item.
|
||||||
|
*
|
||||||
|
* \page listGET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to set the value of the list item. In most cases the value is
|
||||||
|
* used to sort the list in ascending order.
|
||||||
|
*
|
||||||
|
* \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to retrieve the value of the list item. The value can
|
||||||
|
* represent anything - for example the priority of a task, or the time at
|
||||||
|
* which a task should be unblocked.
|
||||||
|
*
|
||||||
|
* \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to retrieve the value of the list item at the head of a given
|
||||||
|
* list.
|
||||||
|
*
|
||||||
|
* \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the list item at the head of the list.
|
||||||
|
*
|
||||||
|
* \page listGET_HEAD_ENTRY listGET_HEAD_ENTRY
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the next list item.
|
||||||
|
*
|
||||||
|
* \page listGET_NEXT listGET_NEXT
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the list item that marks the end of the list
|
||||||
|
*
|
||||||
|
* \page listGET_END_MARKER listGET_END_MARKER
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to determine if a list contains any items. The macro will
|
||||||
|
* only have the value true if the list is empty.
|
||||||
|
*
|
||||||
|
* \page listLIST_IS_EMPTY listLIST_IS_EMPTY
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to return the number of items in the list.
|
||||||
|
*/
|
||||||
|
#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access function to obtain the owner of the next entry in a list.
|
||||||
|
*
|
||||||
|
* The list member pxIndex is used to walk through a list. Calling
|
||||||
|
* listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list
|
||||||
|
* and returns that entry's pxOwner parameter. Using multiple calls to this
|
||||||
|
* function it is therefore possible to move through every item contained in
|
||||||
|
* a list.
|
||||||
|
*
|
||||||
|
* The pxOwner parameter of a list item is a pointer to the object that owns
|
||||||
|
* the list item. In the scheduler this is normally a task control block.
|
||||||
|
* The pxOwner parameter effectively creates a two way link between the list
|
||||||
|
* item and its owner.
|
||||||
|
*
|
||||||
|
* @param pxTCB pxTCB is set to the address of the owner of the next list item.
|
||||||
|
* @param pxList The list from which the next item owner is to be returned.
|
||||||
|
*
|
||||||
|
* \page listGET_OWNER_OF_NEXT_ENTRY listGET_OWNER_OF_NEXT_ENTRY
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#if ( configNUMBER_OF_CORES == 1 )
|
||||||
|
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
|
||||||
|
do { \
|
||||||
|
List_t * const pxConstList = ( pxList ); \
|
||||||
|
/* Increment the index to the next item and return the item, ensuring */ \
|
||||||
|
/* we don't return the marker used at the end of the list. */ \
|
||||||
|
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
|
||||||
|
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
|
||||||
|
{ \
|
||||||
|
( pxConstList )->pxIndex = ( pxConstList )->xListEnd.pxNext; \
|
||||||
|
} \
|
||||||
|
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
|
||||||
|
} while( 0 )
|
||||||
|
#else /* #if ( configNUMBER_OF_CORES == 1 ) */
|
||||||
|
|
||||||
|
/* This function is not required in SMP. FreeRTOS SMP scheduler doesn't use
|
||||||
|
* pxIndex and it should always point to the xListEnd. Not defining this macro
|
||||||
|
* here to prevent updating pxIndex.
|
||||||
|
*/
|
||||||
|
#endif /* #if ( configNUMBER_OF_CORES == 1 ) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Version of uxListRemove() that does not return a value. Provided as a slight
|
||||||
|
* optimisation for xTaskIncrementTick() by being inline.
|
||||||
|
*
|
||||||
|
* Remove an item from a list. The list item has a pointer to the list that
|
||||||
|
* it is in, so only the list item need be passed into the function.
|
||||||
|
*
|
||||||
|
* @param uxListRemove The item to be removed. The item will remove itself from
|
||||||
|
* the list pointed to by it's pxContainer parameter.
|
||||||
|
*
|
||||||
|
* @return The number of items that remain in the list after the list item has
|
||||||
|
* been removed.
|
||||||
|
*
|
||||||
|
* \page listREMOVE_ITEM listREMOVE_ITEM
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listREMOVE_ITEM( pxItemToRemove ) \
|
||||||
|
do { \
|
||||||
|
/* The list item knows which list it is in. Obtain the list from the list \
|
||||||
|
* item. */ \
|
||||||
|
List_t * const pxList = ( pxItemToRemove )->pxContainer; \
|
||||||
|
\
|
||||||
|
( pxItemToRemove )->pxNext->pxPrevious = ( pxItemToRemove )->pxPrevious; \
|
||||||
|
( pxItemToRemove )->pxPrevious->pxNext = ( pxItemToRemove )->pxNext; \
|
||||||
|
/* Make sure the index is left pointing to a valid item. */ \
|
||||||
|
if( pxList->pxIndex == ( pxItemToRemove ) ) \
|
||||||
|
{ \
|
||||||
|
pxList->pxIndex = ( pxItemToRemove )->pxPrevious; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
( pxItemToRemove )->pxContainer = NULL; \
|
||||||
|
( ( pxList )->uxNumberOfItems ) = ( UBaseType_t ) ( ( ( pxList )->uxNumberOfItems ) - 1U ); \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inline version of vListInsertEnd() to provide slight optimisation for
|
||||||
|
* xTaskIncrementTick().
|
||||||
|
*
|
||||||
|
* Insert a list item into a list. The item will be inserted in a position
|
||||||
|
* such that it will be the last item within the list returned by multiple
|
||||||
|
* calls to listGET_OWNER_OF_NEXT_ENTRY.
|
||||||
|
*
|
||||||
|
* The list member pxIndex is used to walk through a list. Calling
|
||||||
|
* listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list.
|
||||||
|
* Placing an item in a list using vListInsertEnd effectively places the item
|
||||||
|
* in the list position pointed to by pxIndex. This means that every other
|
||||||
|
* item within the list will be returned by listGET_OWNER_OF_NEXT_ENTRY before
|
||||||
|
* the pxIndex parameter again points to the item being inserted.
|
||||||
|
*
|
||||||
|
* @param pxList The list into which the item is to be inserted.
|
||||||
|
*
|
||||||
|
* @param pxNewListItem The list item to be inserted into the list.
|
||||||
|
*
|
||||||
|
* \page listINSERT_END listINSERT_END
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listINSERT_END( pxList, pxNewListItem ) \
|
||||||
|
do { \
|
||||||
|
ListItem_t * const pxIndex = ( pxList )->pxIndex; \
|
||||||
|
\
|
||||||
|
/* Only effective when configASSERT() is also defined, these tests may catch \
|
||||||
|
* the list data structures being overwritten in memory. They will not catch \
|
||||||
|
* data errors caused by incorrect configuration or use of FreeRTOS. */ \
|
||||||
|
listTEST_LIST_INTEGRITY( ( pxList ) ); \
|
||||||
|
listTEST_LIST_ITEM_INTEGRITY( ( pxNewListItem ) ); \
|
||||||
|
\
|
||||||
|
/* Insert a new list item into ( pxList ), but rather than sort the list, \
|
||||||
|
* makes the new list item the last item to be removed by a call to \
|
||||||
|
* listGET_OWNER_OF_NEXT_ENTRY(). */ \
|
||||||
|
( pxNewListItem )->pxNext = pxIndex; \
|
||||||
|
( pxNewListItem )->pxPrevious = pxIndex->pxPrevious; \
|
||||||
|
\
|
||||||
|
pxIndex->pxPrevious->pxNext = ( pxNewListItem ); \
|
||||||
|
pxIndex->pxPrevious = ( pxNewListItem ); \
|
||||||
|
\
|
||||||
|
/* Remember which list the item is in. */ \
|
||||||
|
( pxNewListItem )->pxContainer = ( pxList ); \
|
||||||
|
\
|
||||||
|
( ( pxList )->uxNumberOfItems ) = ( UBaseType_t ) ( ( ( pxList )->uxNumberOfItems ) + 1U ); \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access function to obtain the owner of the first entry in a list. Lists
|
||||||
|
* are normally sorted in ascending item value order.
|
||||||
|
*
|
||||||
|
* This function returns the pxOwner member of the first item in the list.
|
||||||
|
* The pxOwner parameter of a list item is a pointer to the object that owns
|
||||||
|
* the list item. In the scheduler this is normally a task control block.
|
||||||
|
* The pxOwner parameter effectively creates a two way link between the list
|
||||||
|
* item and its owner.
|
||||||
|
*
|
||||||
|
* @param pxList The list from which the owner of the head item is to be
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( ( &( ( pxList )->xListEnd ) )->pxNext->pvOwner )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check to see if a list item is within a list. The list item maintains a
|
||||||
|
* "container" pointer that points to the list it is in. All this macro does
|
||||||
|
* is check to see if the container and the list match.
|
||||||
|
*
|
||||||
|
* @param pxList The list we want to know if the list item is within.
|
||||||
|
* @param pxListItem The list item we want to know if is in the list.
|
||||||
|
* @return pdTRUE if the list item is in the list, otherwise pdFALSE.
|
||||||
|
*/
|
||||||
|
#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the list a list item is contained within (referenced from).
|
||||||
|
*
|
||||||
|
* @param pxListItem The list item being queried.
|
||||||
|
* @return A pointer to the List_t object that references the pxListItem
|
||||||
|
*/
|
||||||
|
#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This provides a crude means of knowing if a list has been initialised, as
|
||||||
|
* pxList->xListEnd.xItemValue is set to portMAX_DELAY by the vListInitialise()
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must be called before a list is used! This initialises all the members
|
||||||
|
* of the list structure and inserts the xListEnd item into the list as a
|
||||||
|
* marker to the back of the list.
|
||||||
|
*
|
||||||
|
* @param pxList Pointer to the list being initialised.
|
||||||
|
*
|
||||||
|
* \page vListInitialise vListInitialise
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
void vListInitialise( List_t * const pxList ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must be called before a list item is used. This sets the list container to
|
||||||
|
* null so the item does not think that it is already contained in a list.
|
||||||
|
*
|
||||||
|
* @param pxItem Pointer to the list item being initialised.
|
||||||
|
*
|
||||||
|
* \page vListInitialiseItem vListInitialiseItem
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
void vListInitialiseItem( ListItem_t * const pxItem ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert a list item into a list. The item will be inserted into the list in
|
||||||
|
* a position determined by its item value (ascending item value order).
|
||||||
|
*
|
||||||
|
* @param pxList The list into which the item is to be inserted.
|
||||||
|
*
|
||||||
|
* @param pxNewListItem The item that is to be placed in the list.
|
||||||
|
*
|
||||||
|
* \page vListInsert vListInsert
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
void vListInsert( List_t * const pxList,
|
||||||
|
ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert a list item into a list. The item will be inserted in a position
|
||||||
|
* such that it will be the last item within the list returned by multiple
|
||||||
|
* calls to listGET_OWNER_OF_NEXT_ENTRY.
|
||||||
|
*
|
||||||
|
* The list member pxIndex is used to walk through a list. Calling
|
||||||
|
* listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list.
|
||||||
|
* Placing an item in a list using vListInsertEnd effectively places the item
|
||||||
|
* in the list position pointed to by pxIndex. This means that every other
|
||||||
|
* item within the list will be returned by listGET_OWNER_OF_NEXT_ENTRY before
|
||||||
|
* the pxIndex parameter again points to the item being inserted.
|
||||||
|
*
|
||||||
|
* @param pxList The list into which the item is to be inserted.
|
||||||
|
*
|
||||||
|
* @param pxNewListItem The list item to be inserted into the list.
|
||||||
|
*
|
||||||
|
* \page vListInsertEnd vListInsertEnd
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
void vListInsertEnd( List_t * const pxList,
|
||||||
|
ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove an item from a list. The list item has a pointer to the list that
|
||||||
|
* it is in, so only the list item need be passed into the function.
|
||||||
|
*
|
||||||
|
* @param uxListRemove The item to be removed. The item will remove itself from
|
||||||
|
* the list pointed to by it's pxContainer parameter.
|
||||||
|
*
|
||||||
|
* @return The number of items that remain in the list after the list item has
|
||||||
|
* been removed.
|
||||||
|
*
|
||||||
|
* \page uxListRemove uxListRemove
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
#endif /* ifndef LIST_H */
|
||||||
@@ -0,0 +1,967 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Message buffers build functionality on top of FreeRTOS stream buffers.
|
||||||
|
* Whereas stream buffers are used to send a continuous stream of data from one
|
||||||
|
* task or interrupt to another, message buffers are used to send variable
|
||||||
|
* length discrete messages from one task or interrupt to another. Their
|
||||||
|
* implementation is light weight, making them particularly suited for interrupt
|
||||||
|
* to task and core to core communication scenarios.
|
||||||
|
*
|
||||||
|
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||||
|
* implementation (so also the message buffer implementation, as message buffers
|
||||||
|
* are built on top of stream buffers) assumes there is only one task or
|
||||||
|
* interrupt that will write to the buffer (the writer), and only one task or
|
||||||
|
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||||
|
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||||
|
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||||
|
* multiple different readers. If there are to be multiple different writers
|
||||||
|
* then the application writer must serialize calls to writing API functions
|
||||||
|
* (such as xStreamBufferSend()). Likewise, if there are to be multiple
|
||||||
|
* different readers then the application writer must serialize calls to reading
|
||||||
|
* API functions (such as xStreamBufferReceive()). One way to achieve such
|
||||||
|
* serialization in single core or SMP kernel is to place each API call inside a
|
||||||
|
* critical section and use a block time of 0.
|
||||||
|
*
|
||||||
|
* Message buffers hold variable length messages. To enable that, when a
|
||||||
|
* message is written to the message buffer an additional sizeof( size_t ) bytes
|
||||||
|
* are also written to store the message's length (that happens internally, with
|
||||||
|
* the API function). sizeof( size_t ) is typically 4 bytes on a 32-bit
|
||||||
|
* architecture, so writing a 10 byte message to a message buffer on a 32-bit
|
||||||
|
* architecture will actually reduce the available space in the message buffer
|
||||||
|
* by 14 bytes (10 byte are used by the message, and 4 bytes to hold the length
|
||||||
|
* of the message).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FREERTOS_MESSAGE_BUFFER_H
|
||||||
|
#define FREERTOS_MESSAGE_BUFFER_H
|
||||||
|
|
||||||
|
#ifndef INC_FREERTOS_H
|
||||||
|
#error "include FreeRTOS.h must appear in source files before include message_buffer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Message buffers are built onto of stream buffers. */
|
||||||
|
#include "stream_buffer.h"
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#if defined( __cplusplus )
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type by which message buffers are referenced. For example, a call to
|
||||||
|
* xMessageBufferCreate() returns an MessageBufferHandle_t variable that can
|
||||||
|
* then be used as a parameter to xMessageBufferSend(), xMessageBufferReceive(),
|
||||||
|
* etc. Message buffer is essentially built as a stream buffer hence its handle
|
||||||
|
* is also set to same type as a stream buffer handle.
|
||||||
|
*/
|
||||||
|
typedef StreamBufferHandle_t MessageBufferHandle_t;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
* @code{c}
|
||||||
|
* MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Creates a new message buffer using dynamically allocated memory. See
|
||||||
|
* xMessageBufferCreateStatic() for a version that uses statically allocated
|
||||||
|
* memory (memory that is allocated at compile time).
|
||||||
|
*
|
||||||
|
* configSUPPORT_DYNAMIC_ALLOCATION must be set to 1 or left undefined in
|
||||||
|
* FreeRTOSConfig.h for xMessageBufferCreate() to be available.
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferCreate() to be available.
|
||||||
|
*
|
||||||
|
* @param xBufferSizeBytes The total number of bytes (not messages) the message
|
||||||
|
* buffer will be able to hold at any one time. When a message is written to
|
||||||
|
* the message buffer an additional sizeof( size_t ) bytes are also written to
|
||||||
|
* store the message's length. sizeof( size_t ) is typically 4 bytes on a
|
||||||
|
* 32-bit architecture, so on most 32-bit architectures a 10 byte message will
|
||||||
|
* take up 14 bytes of message buffer space.
|
||||||
|
*
|
||||||
|
* @param pxSendCompletedCallback Callback invoked when a send operation to the
|
||||||
|
* message buffer is complete. If the parameter is NULL or xMessageBufferCreate()
|
||||||
|
* is called without the parameter, then it will use the default implementation
|
||||||
|
* provided by sbSEND_COMPLETED macro. To enable the callback,
|
||||||
|
* configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.
|
||||||
|
*
|
||||||
|
* @param pxReceiveCompletedCallback Callback invoked when a receive operation from
|
||||||
|
* the message buffer is complete. If the parameter is NULL or xMessageBufferCreate()
|
||||||
|
* is called without the parameter, it will use the default implementation provided
|
||||||
|
* by sbRECEIVE_COMPLETED macro. To enable the callback,
|
||||||
|
* configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.
|
||||||
|
*
|
||||||
|
* @return If NULL is returned, then the message buffer cannot be created
|
||||||
|
* because there is insufficient heap memory available for FreeRTOS to allocate
|
||||||
|
* the message buffer data structures and storage area. A non-NULL value being
|
||||||
|
* returned indicates that the message buffer has been created successfully -
|
||||||
|
* the returned value should be stored as the handle to the created message
|
||||||
|
* buffer.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
* @code{c}
|
||||||
|
*
|
||||||
|
* void vAFunction( void )
|
||||||
|
* {
|
||||||
|
* MessageBufferHandle_t xMessageBuffer;
|
||||||
|
* const size_t xMessageBufferSizeBytes = 100;
|
||||||
|
*
|
||||||
|
* // Create a message buffer that can hold 100 bytes. The memory used to hold
|
||||||
|
* // both the message buffer structure and the messages themselves is allocated
|
||||||
|
* // dynamically. Each message added to the buffer consumes an additional 4
|
||||||
|
* // bytes which are used to hold the length of the message.
|
||||||
|
* xMessageBuffer = xMessageBufferCreate( xMessageBufferSizeBytes );
|
||||||
|
*
|
||||||
|
* if( xMessageBuffer == NULL )
|
||||||
|
* {
|
||||||
|
* // There was not enough heap memory space available to create the
|
||||||
|
* // message buffer.
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* {
|
||||||
|
* // The message buffer was created successfully and can now be used.
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xMessageBufferCreate xMessageBufferCreate
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferCreate( xBufferSizeBytes ) \
|
||||||
|
xStreamBufferGenericCreate( ( xBufferSizeBytes ), ( size_t ) 0, sbTYPE_MESSAGE_BUFFER, NULL, NULL )
|
||||||
|
|
||||||
|
#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )
|
||||||
|
#define xMessageBufferCreateWithCallback( xBufferSizeBytes, pxSendCompletedCallback, pxReceiveCompletedCallback ) \
|
||||||
|
xStreamBufferGenericCreate( ( xBufferSizeBytes ), ( size_t ) 0, sbTYPE_MESSAGE_BUFFER, ( pxSendCompletedCallback ), ( pxReceiveCompletedCallback ) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
* @code{c}
|
||||||
|
* MessageBufferHandle_t xMessageBufferCreateStatic( size_t xBufferSizeBytes,
|
||||||
|
* uint8_t *pucMessageBufferStorageArea,
|
||||||
|
* StaticMessageBuffer_t *pxStaticMessageBuffer );
|
||||||
|
* @endcode
|
||||||
|
* Creates a new message buffer using statically allocated memory. See
|
||||||
|
* xMessageBufferCreate() for a version that uses dynamically allocated memory.
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferCreateStatic() to be available.
|
||||||
|
*
|
||||||
|
* @param xBufferSizeBytes The size, in bytes, of the buffer pointed to by the
|
||||||
|
* pucMessageBufferStorageArea parameter. When a message is written to the
|
||||||
|
* message buffer an additional sizeof( size_t ) bytes are also written to store
|
||||||
|
* the message's length. sizeof( size_t ) is typically 4 bytes on a 32-bit
|
||||||
|
* architecture, so on most 32-bit architecture a 10 byte message will take up
|
||||||
|
* 14 bytes of message buffer space. The maximum number of bytes that can be
|
||||||
|
* stored in the message buffer is actually (xBufferSizeBytes - 1).
|
||||||
|
*
|
||||||
|
* @param pucMessageBufferStorageArea Must point to a uint8_t array that is at
|
||||||
|
* least xBufferSizeBytes big. This is the array to which messages are
|
||||||
|
* copied when they are written to the message buffer.
|
||||||
|
*
|
||||||
|
* @param pxStaticMessageBuffer Must point to a variable of type
|
||||||
|
* StaticMessageBuffer_t, which will be used to hold the message buffer's data
|
||||||
|
* structure.
|
||||||
|
*
|
||||||
|
* @param pxSendCompletedCallback Callback invoked when a new message is sent to the message buffer.
|
||||||
|
* If the parameter is NULL or xMessageBufferCreate() is called without the parameter, then it will use the default
|
||||||
|
* implementation provided by sbSEND_COMPLETED macro. To enable the callback,
|
||||||
|
* configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.
|
||||||
|
*
|
||||||
|
* @param pxReceiveCompletedCallback Callback invoked when a message is read from a
|
||||||
|
* message buffer. If the parameter is NULL or xMessageBufferCreate() is called without the parameter, it will
|
||||||
|
* use the default implementation provided by sbRECEIVE_COMPLETED macro. To enable the callback,
|
||||||
|
* configUSE_SB_COMPLETED_CALLBACK must be set to 1 in FreeRTOSConfig.h.
|
||||||
|
*
|
||||||
|
* @return If the message buffer is created successfully then a handle to the
|
||||||
|
* created message buffer is returned. If either pucMessageBufferStorageArea or
|
||||||
|
* pxStaticmessageBuffer are NULL then NULL is returned.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
* @code{c}
|
||||||
|
*
|
||||||
|
* // Used to dimension the array used to hold the messages. The available space
|
||||||
|
* // will actually be one less than this, so 999.
|
||||||
|
#define STORAGE_SIZE_BYTES 1000
|
||||||
|
*
|
||||||
|
* // Defines the memory that will actually hold the messages within the message
|
||||||
|
* // buffer.
|
||||||
|
* static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ];
|
||||||
|
*
|
||||||
|
* // The variable used to hold the message buffer structure.
|
||||||
|
* StaticMessageBuffer_t xMessageBufferStruct;
|
||||||
|
*
|
||||||
|
* void MyFunction( void )
|
||||||
|
* {
|
||||||
|
* MessageBufferHandle_t xMessageBuffer;
|
||||||
|
*
|
||||||
|
* xMessageBuffer = xMessageBufferCreateStatic( sizeof( ucStorageBuffer ),
|
||||||
|
* ucStorageBuffer,
|
||||||
|
* &xMessageBufferStruct );
|
||||||
|
*
|
||||||
|
* // As neither the pucMessageBufferStorageArea or pxStaticMessageBuffer
|
||||||
|
* // parameters were NULL, xMessageBuffer will not be NULL, and can be used to
|
||||||
|
* // reference the created message buffer in other message buffer API calls.
|
||||||
|
*
|
||||||
|
* // Other code that uses the message buffer can go here.
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xMessageBufferCreateStatic xMessageBufferCreateStatic
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferCreateStatic( xBufferSizeBytes, pucMessageBufferStorageArea, pxStaticMessageBuffer ) \
|
||||||
|
xStreamBufferGenericCreateStatic( ( xBufferSizeBytes ), 0, sbTYPE_MESSAGE_BUFFER, ( pucMessageBufferStorageArea ), ( pxStaticMessageBuffer ), NULL, NULL )
|
||||||
|
|
||||||
|
#if ( configUSE_SB_COMPLETED_CALLBACK == 1 )
|
||||||
|
#define xMessageBufferCreateStaticWithCallback( xBufferSizeBytes, pucMessageBufferStorageArea, pxStaticMessageBuffer, pxSendCompletedCallback, pxReceiveCompletedCallback ) \
|
||||||
|
xStreamBufferGenericCreateStatic( ( xBufferSizeBytes ), 0, sbTYPE_MESSAGE_BUFFER, ( pucMessageBufferStorageArea ), ( pxStaticMessageBuffer ), ( pxSendCompletedCallback ), ( pxReceiveCompletedCallback ) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
* @code{c}
|
||||||
|
* BaseType_t xMessageBufferGetStaticBuffers( MessageBufferHandle_t xMessageBuffer,
|
||||||
|
* uint8_t ** ppucMessageBufferStorageArea,
|
||||||
|
* StaticMessageBuffer_t ** ppxStaticMessageBuffer );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Retrieve pointers to a statically created message buffer's data structure
|
||||||
|
* buffer and storage area buffer. These are the same buffers that are supplied
|
||||||
|
* at the time of creation.
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferGetStaticBuffers() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The message buffer for which to retrieve the buffers.
|
||||||
|
*
|
||||||
|
* @param ppucMessageBufferStorageArea Used to return a pointer to the
|
||||||
|
* message buffer's storage area buffer.
|
||||||
|
*
|
||||||
|
* @param ppxStaticMessageBuffer Used to return a pointer to the message
|
||||||
|
* buffer's data structure buffer.
|
||||||
|
*
|
||||||
|
* @return pdTRUE if buffers were retrieved, pdFALSE otherwise..
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferGetStaticBuffers xMessageBufferGetStaticBuffers
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||||
|
#define xMessageBufferGetStaticBuffers( xMessageBuffer, ppucMessageBufferStorageArea, ppxStaticMessageBuffer ) \
|
||||||
|
xStreamBufferGetStaticBuffers( ( xMessageBuffer ), ( ppucMessageBufferStorageArea ), ( ppxStaticMessageBuffer ) )
|
||||||
|
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
* @code{c}
|
||||||
|
* size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer,
|
||||||
|
* const void *pvTxData,
|
||||||
|
* size_t xDataLengthBytes,
|
||||||
|
* TickType_t xTicksToWait );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Sends a discrete message to the message buffer. The message can be any
|
||||||
|
* length that fits within the buffer's free space, and is copied into the
|
||||||
|
* buffer.
|
||||||
|
*
|
||||||
|
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||||
|
* implementation (so also the message buffer implementation, as message buffers
|
||||||
|
* are built on top of stream buffers) assumes there is only one task or
|
||||||
|
* interrupt that will write to the buffer (the writer), and only one task or
|
||||||
|
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||||
|
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||||
|
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||||
|
* multiple different readers. If there are to be multiple different writers
|
||||||
|
* then the application writer must serialize calls to writing API functions
|
||||||
|
* (such as xStreamBufferSend()). Likewise, if there are to be multiple
|
||||||
|
* different readers then the application writer must serialize calls to reading
|
||||||
|
* API functions (such as xStreamBufferReceive()). One way to achieve such
|
||||||
|
* serialization in single core or SMP kernel is to place each API call inside a
|
||||||
|
* critical section and use a block time of 0.
|
||||||
|
*
|
||||||
|
* Use xMessageBufferSend() to write to a message buffer from a task. Use
|
||||||
|
* xMessageBufferSendFromISR() to write to a message buffer from an interrupt
|
||||||
|
* service routine (ISR).
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferSend() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer to which a message is
|
||||||
|
* being sent.
|
||||||
|
*
|
||||||
|
* @param pvTxData A pointer to the message that is to be copied into the
|
||||||
|
* message buffer.
|
||||||
|
*
|
||||||
|
* @param xDataLengthBytes The length of the message. That is, the number of
|
||||||
|
* bytes to copy from pvTxData into the message buffer. When a message is
|
||||||
|
* written to the message buffer an additional sizeof( size_t ) bytes are also
|
||||||
|
* written to store the message's length. sizeof( size_t ) is typically 4 bytes
|
||||||
|
* on a 32-bit architecture, so on most 32-bit architecture setting
|
||||||
|
* xDataLengthBytes to 20 will reduce the free space in the message buffer by 24
|
||||||
|
* bytes (20 bytes of message data and 4 bytes to hold the message length).
|
||||||
|
*
|
||||||
|
* @param xTicksToWait The maximum amount of time the calling task should remain
|
||||||
|
* in the Blocked state to wait for enough space to become available in the
|
||||||
|
* message buffer, should the message buffer have insufficient space when
|
||||||
|
* xMessageBufferSend() is called. The calling task will never block if
|
||||||
|
* xTicksToWait is zero. The block time is specified in tick periods, so the
|
||||||
|
* absolute time it represents is dependent on the tick frequency. The macro
|
||||||
|
* pdMS_TO_TICKS() can be used to convert a time specified in milliseconds into
|
||||||
|
* a time specified in ticks. Setting xTicksToWait to portMAX_DELAY will cause
|
||||||
|
* the task to wait indefinitely (without timing out), provided
|
||||||
|
* INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. Tasks do not use any
|
||||||
|
* CPU time when they are in the Blocked state.
|
||||||
|
*
|
||||||
|
* @return The number of bytes written to the message buffer. If the call to
|
||||||
|
* xMessageBufferSend() times out before there was enough space to write the
|
||||||
|
* message into the message buffer then zero is returned. If the call did not
|
||||||
|
* time out then xDataLengthBytes is returned.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
* @code{c}
|
||||||
|
* void vAFunction( MessageBufferHandle_t xMessageBuffer )
|
||||||
|
* {
|
||||||
|
* size_t xBytesSent;
|
||||||
|
* uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
|
||||||
|
* char *pcStringToSend = "String to send";
|
||||||
|
* const TickType_t x100ms = pdMS_TO_TICKS( 100 );
|
||||||
|
*
|
||||||
|
* // Send an array to the message buffer, blocking for a maximum of 100ms to
|
||||||
|
* // wait for enough space to be available in the message buffer.
|
||||||
|
* xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );
|
||||||
|
*
|
||||||
|
* if( xBytesSent != sizeof( ucArrayToSend ) )
|
||||||
|
* {
|
||||||
|
* // The call to xMessageBufferSend() times out before there was enough
|
||||||
|
* // space in the buffer for the data to be written.
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Send the string to the message buffer. Return immediately if there is
|
||||||
|
* // not enough space in the buffer.
|
||||||
|
* xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );
|
||||||
|
*
|
||||||
|
* if( xBytesSent != strlen( pcStringToSend ) )
|
||||||
|
* {
|
||||||
|
* // The string could not be added to the message buffer because there was
|
||||||
|
* // not enough free space in the buffer.
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xMessageBufferSend xMessageBufferSend
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferSend( xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) \
|
||||||
|
xStreamBufferSend( ( xMessageBuffer ), ( pvTxData ), ( xDataLengthBytes ), ( xTicksToWait ) )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
* @code{c}
|
||||||
|
* size_t xMessageBufferSendFromISR( MessageBufferHandle_t xMessageBuffer,
|
||||||
|
* const void *pvTxData,
|
||||||
|
* size_t xDataLengthBytes,
|
||||||
|
* BaseType_t *pxHigherPriorityTaskWoken );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Interrupt safe version of the API function that sends a discrete message to
|
||||||
|
* the message buffer. The message can be any length that fits within the
|
||||||
|
* buffer's free space, and is copied into the buffer.
|
||||||
|
*
|
||||||
|
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||||
|
* implementation (so also the message buffer implementation, as message buffers
|
||||||
|
* are built on top of stream buffers) assumes there is only one task or
|
||||||
|
* interrupt that will write to the buffer (the writer), and only one task or
|
||||||
|
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||||
|
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||||
|
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||||
|
* multiple different readers. If there are to be multiple different writers
|
||||||
|
* then the application writer must serialize calls to writing API functions
|
||||||
|
* (such as xStreamBufferSend()). Likewise, if there are to be multiple
|
||||||
|
* different readers then the application writer must serialize calls to reading
|
||||||
|
* API functions (such as xStreamBufferReceive()). One way to achieve such
|
||||||
|
* serialization in single core or SMP kernel is to place each API call inside a
|
||||||
|
* critical section and use a block time of 0.
|
||||||
|
*
|
||||||
|
* Use xMessageBufferSend() to write to a message buffer from a task. Use
|
||||||
|
* xMessageBufferSendFromISR() to write to a message buffer from an interrupt
|
||||||
|
* service routine (ISR).
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferSendFromISR() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer to which a message is
|
||||||
|
* being sent.
|
||||||
|
*
|
||||||
|
* @param pvTxData A pointer to the message that is to be copied into the
|
||||||
|
* message buffer.
|
||||||
|
*
|
||||||
|
* @param xDataLengthBytes The length of the message. That is, the number of
|
||||||
|
* bytes to copy from pvTxData into the message buffer. When a message is
|
||||||
|
* written to the message buffer an additional sizeof( size_t ) bytes are also
|
||||||
|
* written to store the message's length. sizeof( size_t ) is typically 4 bytes
|
||||||
|
* on a 32-bit architecture, so on most 32-bit architecture setting
|
||||||
|
* xDataLengthBytes to 20 will reduce the free space in the message buffer by 24
|
||||||
|
* bytes (20 bytes of message data and 4 bytes to hold the message length).
|
||||||
|
*
|
||||||
|
* @param pxHigherPriorityTaskWoken It is possible that a message buffer will
|
||||||
|
* have a task blocked on it waiting for data. Calling
|
||||||
|
* xMessageBufferSendFromISR() can make data available, and so cause a task that
|
||||||
|
* was waiting for data to leave the Blocked state. If calling
|
||||||
|
* xMessageBufferSendFromISR() causes a task to leave the Blocked state, and the
|
||||||
|
* unblocked task has a priority higher than the currently executing task (the
|
||||||
|
* task that was interrupted), then, internally, xMessageBufferSendFromISR()
|
||||||
|
* will set *pxHigherPriorityTaskWoken to pdTRUE. If
|
||||||
|
* xMessageBufferSendFromISR() sets this value to pdTRUE, then normally a
|
||||||
|
* context switch should be performed before the interrupt is exited. This will
|
||||||
|
* ensure that the interrupt returns directly to the highest priority Ready
|
||||||
|
* state task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it
|
||||||
|
* is passed into the function. See the code example below for an example.
|
||||||
|
*
|
||||||
|
* @return The number of bytes actually written to the message buffer. If the
|
||||||
|
* message buffer didn't have enough free space for the message to be stored
|
||||||
|
* then 0 is returned, otherwise xDataLengthBytes is returned.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
* @code{c}
|
||||||
|
* // A message buffer that has already been created.
|
||||||
|
* MessageBufferHandle_t xMessageBuffer;
|
||||||
|
*
|
||||||
|
* void vAnInterruptServiceRoutine( void )
|
||||||
|
* {
|
||||||
|
* size_t xBytesSent;
|
||||||
|
* char *pcStringToSend = "String to send";
|
||||||
|
* BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
|
||||||
|
*
|
||||||
|
* // Attempt to send the string to the message buffer.
|
||||||
|
* xBytesSent = xMessageBufferSendFromISR( xMessageBuffer,
|
||||||
|
* ( void * ) pcStringToSend,
|
||||||
|
* strlen( pcStringToSend ),
|
||||||
|
* &xHigherPriorityTaskWoken );
|
||||||
|
*
|
||||||
|
* if( xBytesSent != strlen( pcStringToSend ) )
|
||||||
|
* {
|
||||||
|
* // The string could not be added to the message buffer because there was
|
||||||
|
* // not enough free space in the buffer.
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // If xHigherPriorityTaskWoken was set to pdTRUE inside
|
||||||
|
* // xMessageBufferSendFromISR() then a task that has a priority above the
|
||||||
|
* // priority of the currently executing task was unblocked and a context
|
||||||
|
* // switch should be performed to ensure the ISR returns to the unblocked
|
||||||
|
* // task. In most FreeRTOS ports this is done by simply passing
|
||||||
|
* // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
|
||||||
|
* // variables value, and perform the context switch if necessary. Check the
|
||||||
|
* // documentation for the port in use for port specific instructions.
|
||||||
|
* portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xMessageBufferSendFromISR xMessageBufferSendFromISR
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferSendFromISR( xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) \
|
||||||
|
xStreamBufferSendFromISR( ( xMessageBuffer ), ( pvTxData ), ( xDataLengthBytes ), ( pxHigherPriorityTaskWoken ) )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
* @code{c}
|
||||||
|
* size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer,
|
||||||
|
* void *pvRxData,
|
||||||
|
* size_t xBufferLengthBytes,
|
||||||
|
* TickType_t xTicksToWait );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Receives a discrete message from a message buffer. Messages can be of
|
||||||
|
* variable length and are copied out of the buffer.
|
||||||
|
*
|
||||||
|
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||||
|
* implementation (so also the message buffer implementation, as message buffers
|
||||||
|
* are built on top of stream buffers) assumes there is only one task or
|
||||||
|
* interrupt that will write to the buffer (the writer), and only one task or
|
||||||
|
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||||
|
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||||
|
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||||
|
* multiple different readers. If there are to be multiple different writers
|
||||||
|
* then the application writer must serialize calls to writing API functions
|
||||||
|
* (such as xStreamBufferSend()). Likewise, if there are to be multiple
|
||||||
|
* different readers then the application writer must serialize calls to reading
|
||||||
|
* API functions (such as xStreamBufferReceive()). One way to achieve such
|
||||||
|
* serialization in single core or SMP kernel is to place each API call inside a
|
||||||
|
* critical section and use a block time of 0.
|
||||||
|
*
|
||||||
|
* Use xMessageBufferReceive() to read from a message buffer from a task. Use
|
||||||
|
* xMessageBufferReceiveFromISR() to read from a message buffer from an
|
||||||
|
* interrupt service routine (ISR).
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferReceive() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer from which a message
|
||||||
|
* is being received.
|
||||||
|
*
|
||||||
|
* @param pvRxData A pointer to the buffer into which the received message is
|
||||||
|
* to be copied.
|
||||||
|
*
|
||||||
|
* @param xBufferLengthBytes The length of the buffer pointed to by the pvRxData
|
||||||
|
* parameter. This sets the maximum length of the message that can be received.
|
||||||
|
* If xBufferLengthBytes is too small to hold the next message then the message
|
||||||
|
* will be left in the message buffer and 0 will be returned.
|
||||||
|
*
|
||||||
|
* @param xTicksToWait The maximum amount of time the task should remain in the
|
||||||
|
* Blocked state to wait for a message, should the message buffer be empty.
|
||||||
|
* xMessageBufferReceive() will return immediately if xTicksToWait is zero and
|
||||||
|
* the message buffer is empty. The block time is specified in tick periods, so
|
||||||
|
* the absolute time it represents is dependent on the tick frequency. The
|
||||||
|
* macro pdMS_TO_TICKS() can be used to convert a time specified in milliseconds
|
||||||
|
* into a time specified in ticks. Setting xTicksToWait to portMAX_DELAY will
|
||||||
|
* cause the task to wait indefinitely (without timing out), provided
|
||||||
|
* INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. Tasks do not use any
|
||||||
|
* CPU time when they are in the Blocked state.
|
||||||
|
*
|
||||||
|
* @return The length, in bytes, of the message read from the message buffer, if
|
||||||
|
* any. If xMessageBufferReceive() times out before a message became available
|
||||||
|
* then zero is returned. If the length of the message is greater than
|
||||||
|
* xBufferLengthBytes then the message will be left in the message buffer and
|
||||||
|
* zero is returned.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
* @code{c}
|
||||||
|
* void vAFunction( MessageBuffer_t xMessageBuffer )
|
||||||
|
* {
|
||||||
|
* uint8_t ucRxData[ 20 ];
|
||||||
|
* size_t xReceivedBytes;
|
||||||
|
* const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
|
||||||
|
*
|
||||||
|
* // Receive the next message from the message buffer. Wait in the Blocked
|
||||||
|
* // state (so not using any CPU processing time) for a maximum of 100ms for
|
||||||
|
* // a message to become available.
|
||||||
|
* xReceivedBytes = xMessageBufferReceive( xMessageBuffer,
|
||||||
|
* ( void * ) ucRxData,
|
||||||
|
* sizeof( ucRxData ),
|
||||||
|
* xBlockTime );
|
||||||
|
*
|
||||||
|
* if( xReceivedBytes > 0 )
|
||||||
|
* {
|
||||||
|
* // A ucRxData contains a message that is xReceivedBytes long. Process
|
||||||
|
* // the message here....
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xMessageBufferReceive xMessageBufferReceive
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferReceive( xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) \
|
||||||
|
xStreamBufferReceive( ( xMessageBuffer ), ( pvRxData ), ( xBufferLengthBytes ), ( xTicksToWait ) )
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
* @code{c}
|
||||||
|
* size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer,
|
||||||
|
* void *pvRxData,
|
||||||
|
* size_t xBufferLengthBytes,
|
||||||
|
* BaseType_t *pxHigherPriorityTaskWoken );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* An interrupt safe version of the API function that receives a discrete
|
||||||
|
* message from a message buffer. Messages can be of variable length and are
|
||||||
|
* copied out of the buffer.
|
||||||
|
*
|
||||||
|
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||||
|
* implementation (so also the message buffer implementation, as message buffers
|
||||||
|
* are built on top of stream buffers) assumes there is only one task or
|
||||||
|
* interrupt that will write to the buffer (the writer), and only one task or
|
||||||
|
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||||
|
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||||
|
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||||
|
* multiple different readers. If there are to be multiple different writers
|
||||||
|
* then the application writer must serialize calls to writing API functions
|
||||||
|
* (such as xStreamBufferSend()). Likewise, if there are to be multiple
|
||||||
|
* different readers then the application writer must serialize calls to reading
|
||||||
|
* API functions (such as xStreamBufferReceive()). One way to achieve such
|
||||||
|
* serialization in single core or SMP kernel is to place each API call inside a
|
||||||
|
* critical section and use a block time of 0.
|
||||||
|
*
|
||||||
|
* Use xMessageBufferReceive() to read from a message buffer from a task. Use
|
||||||
|
* xMessageBufferReceiveFromISR() to read from a message buffer from an
|
||||||
|
* interrupt service routine (ISR).
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferReceiveFromISR() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer from which a message
|
||||||
|
* is being received.
|
||||||
|
*
|
||||||
|
* @param pvRxData A pointer to the buffer into which the received message is
|
||||||
|
* to be copied.
|
||||||
|
*
|
||||||
|
* @param xBufferLengthBytes The length of the buffer pointed to by the pvRxData
|
||||||
|
* parameter. This sets the maximum length of the message that can be received.
|
||||||
|
* If xBufferLengthBytes is too small to hold the next message then the message
|
||||||
|
* will be left in the message buffer and 0 will be returned.
|
||||||
|
*
|
||||||
|
* @param pxHigherPriorityTaskWoken It is possible that a message buffer will
|
||||||
|
* have a task blocked on it waiting for space to become available. Calling
|
||||||
|
* xMessageBufferReceiveFromISR() can make space available, and so cause a task
|
||||||
|
* that is waiting for space to leave the Blocked state. If calling
|
||||||
|
* xMessageBufferReceiveFromISR() causes a task to leave the Blocked state, and
|
||||||
|
* the unblocked task has a priority higher than the currently executing task
|
||||||
|
* (the task that was interrupted), then, internally,
|
||||||
|
* xMessageBufferReceiveFromISR() will set *pxHigherPriorityTaskWoken to pdTRUE.
|
||||||
|
* If xMessageBufferReceiveFromISR() sets this value to pdTRUE, then normally a
|
||||||
|
* context switch should be performed before the interrupt is exited. That will
|
||||||
|
* ensure the interrupt returns directly to the highest priority Ready state
|
||||||
|
* task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it is
|
||||||
|
* passed into the function. See the code example below for an example.
|
||||||
|
*
|
||||||
|
* @return The length, in bytes, of the message read from the message buffer, if
|
||||||
|
* any.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
* @code{c}
|
||||||
|
* // A message buffer that has already been created.
|
||||||
|
* MessageBuffer_t xMessageBuffer;
|
||||||
|
*
|
||||||
|
* void vAnInterruptServiceRoutine( void )
|
||||||
|
* {
|
||||||
|
* uint8_t ucRxData[ 20 ];
|
||||||
|
* size_t xReceivedBytes;
|
||||||
|
* BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
|
||||||
|
*
|
||||||
|
* // Receive the next message from the message buffer.
|
||||||
|
* xReceivedBytes = xMessageBufferReceiveFromISR( xMessageBuffer,
|
||||||
|
* ( void * ) ucRxData,
|
||||||
|
* sizeof( ucRxData ),
|
||||||
|
* &xHigherPriorityTaskWoken );
|
||||||
|
*
|
||||||
|
* if( xReceivedBytes > 0 )
|
||||||
|
* {
|
||||||
|
* // A ucRxData contains a message that is xReceivedBytes long. Process
|
||||||
|
* // the message here....
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // If xHigherPriorityTaskWoken was set to pdTRUE inside
|
||||||
|
* // xMessageBufferReceiveFromISR() then a task that has a priority above the
|
||||||
|
* // priority of the currently executing task was unblocked and a context
|
||||||
|
* // switch should be performed to ensure the ISR returns to the unblocked
|
||||||
|
* // task. In most FreeRTOS ports this is done by simply passing
|
||||||
|
* // xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
|
||||||
|
* // variables value, and perform the context switch if necessary. Check the
|
||||||
|
* // documentation for the port in use for port specific instructions.
|
||||||
|
* portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* \defgroup xMessageBufferReceiveFromISR xMessageBufferReceiveFromISR
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferReceiveFromISR( xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) \
|
||||||
|
xStreamBufferReceiveFromISR( ( xMessageBuffer ), ( pvRxData ), ( xBufferLengthBytes ), ( pxHigherPriorityTaskWoken ) )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
* @code{c}
|
||||||
|
* void vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Deletes a message buffer that was previously created using a call to
|
||||||
|
* xMessageBufferCreate() or xMessageBufferCreateStatic(). If the message
|
||||||
|
* buffer was created using dynamic memory (that is, by xMessageBufferCreate()),
|
||||||
|
* then the allocated memory is freed.
|
||||||
|
*
|
||||||
|
* A message buffer handle must not be used after the message buffer has been
|
||||||
|
* deleted.
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* vMessageBufferDelete() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer to be deleted.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define vMessageBufferDelete( xMessageBuffer ) \
|
||||||
|
vStreamBufferDelete( xMessageBuffer )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
* @code{c}
|
||||||
|
* BaseType_t xMessageBufferIsFull( MessageBufferHandle_t xMessageBuffer );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Tests to see if a message buffer is full. A message buffer is full if it
|
||||||
|
* cannot accept any more messages, of any size, until space is made available
|
||||||
|
* by a message being removed from the message buffer.
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferIsFull() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||||
|
*
|
||||||
|
* @return If the message buffer referenced by xMessageBuffer is full then
|
||||||
|
* pdTRUE is returned. Otherwise pdFALSE is returned.
|
||||||
|
*/
|
||||||
|
#define xMessageBufferIsFull( xMessageBuffer ) \
|
||||||
|
xStreamBufferIsFull( xMessageBuffer )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
* @code{c}
|
||||||
|
* BaseType_t xMessageBufferIsEmpty( MessageBufferHandle_t xMessageBuffer );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Tests to see if a message buffer is empty (does not contain any messages).
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferIsEmpty() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||||
|
*
|
||||||
|
* @return If the message buffer referenced by xMessageBuffer is empty then
|
||||||
|
* pdTRUE is returned. Otherwise pdFALSE is returned.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define xMessageBufferIsEmpty( xMessageBuffer ) \
|
||||||
|
xStreamBufferIsEmpty( xMessageBuffer )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
* @code{c}
|
||||||
|
* BaseType_t xMessageBufferReset( MessageBufferHandle_t xMessageBuffer );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Resets a message buffer to its initial empty state, discarding any message it
|
||||||
|
* contained.
|
||||||
|
*
|
||||||
|
* A message buffer can only be reset if there are no tasks blocked on it.
|
||||||
|
*
|
||||||
|
* Use xMessageBufferReset() to reset a message buffer from a task.
|
||||||
|
* Use xMessageBufferResetFromISR() to reset a message buffer from an
|
||||||
|
* interrupt service routine (ISR).
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferReset() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer being reset.
|
||||||
|
*
|
||||||
|
* @return If the message buffer was reset then pdPASS is returned. If the
|
||||||
|
* message buffer could not be reset because either there was a task blocked on
|
||||||
|
* the message queue to wait for space to become available, or to wait for a
|
||||||
|
* a message to be available, then pdFAIL is returned.
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferReset xMessageBufferReset
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferReset( xMessageBuffer ) \
|
||||||
|
xStreamBufferReset( xMessageBuffer )
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
* @code{c}
|
||||||
|
* BaseType_t xMessageBufferResetFromISR( MessageBufferHandle_t xMessageBuffer );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* An interrupt safe version of the API function that resets the message buffer.
|
||||||
|
* Resets a message buffer to its initial empty state, discarding any message it
|
||||||
|
* contained.
|
||||||
|
*
|
||||||
|
* A message buffer can only be reset if there are no tasks blocked on it.
|
||||||
|
*
|
||||||
|
* Use xMessageBufferReset() to reset a message buffer from a task.
|
||||||
|
* Use xMessageBufferResetFromISR() to reset a message buffer from an
|
||||||
|
* interrupt service routine (ISR).
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferResetFromISR() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer being reset.
|
||||||
|
*
|
||||||
|
* @return If the message buffer was reset then pdPASS is returned. If the
|
||||||
|
* message buffer could not be reset because either there was a task blocked on
|
||||||
|
* the message queue to wait for space to become available, or to wait for a
|
||||||
|
* a message to be available, then pdFAIL is returned.
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferResetFromISR xMessageBufferResetFromISR
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferResetFromISR( xMessageBuffer ) \
|
||||||
|
xStreamBufferResetFromISR( xMessageBuffer )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
* @code{c}
|
||||||
|
* size_t xMessageBufferSpaceAvailable( MessageBufferHandle_t xMessageBuffer );
|
||||||
|
* @endcode
|
||||||
|
* Returns the number of bytes of free space in the message buffer.
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferSpaceAvailable() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||||
|
*
|
||||||
|
* @return The number of bytes that can be written to the message buffer before
|
||||||
|
* the message buffer would be full. When a message is written to the message
|
||||||
|
* buffer an additional sizeof( size_t ) bytes are also written to store the
|
||||||
|
* message's length. sizeof( size_t ) is typically 4 bytes on a 32-bit
|
||||||
|
* architecture, so if xMessageBufferSpacesAvailable() returns 10, then the size
|
||||||
|
* of the largest message that can be written to the message buffer is 6 bytes.
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferSpaceAvailable xMessageBufferSpaceAvailable
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferSpaceAvailable( xMessageBuffer ) \
|
||||||
|
xStreamBufferSpacesAvailable( xMessageBuffer )
|
||||||
|
#define xMessageBufferSpacesAvailable( xMessageBuffer ) \
|
||||||
|
xStreamBufferSpacesAvailable( xMessageBuffer ) /* Corrects typo in original macro name. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
* @code{c}
|
||||||
|
* size_t xMessageBufferNextLengthBytes( MessageBufferHandle_t xMessageBuffer );
|
||||||
|
* @endcode
|
||||||
|
* Returns the length (in bytes) of the next message in a message buffer.
|
||||||
|
* Useful if xMessageBufferReceive() returned 0 because the size of the buffer
|
||||||
|
* passed into xMessageBufferReceive() was too small to hold the next message.
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferNextLengthBytes() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||||
|
*
|
||||||
|
* @return The length (in bytes) of the next message in the message buffer, or 0
|
||||||
|
* if the message buffer is empty.
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferNextLengthBytes xMessageBufferNextLengthBytes
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferNextLengthBytes( xMessageBuffer ) \
|
||||||
|
xStreamBufferNextMessageLengthBytes( xMessageBuffer )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
* @code{c}
|
||||||
|
* BaseType_t xMessageBufferSendCompletedFromISR( MessageBufferHandle_t xMessageBuffer, BaseType_t *pxHigherPriorityTaskWoken );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* For advanced users only.
|
||||||
|
*
|
||||||
|
* The sbSEND_COMPLETED() macro is called from within the FreeRTOS APIs when
|
||||||
|
* data is sent to a message buffer or stream buffer. If there was a task that
|
||||||
|
* was blocked on the message or stream buffer waiting for data to arrive then
|
||||||
|
* the sbSEND_COMPLETED() macro sends a notification to the task to remove it
|
||||||
|
* from the Blocked state. xMessageBufferSendCompletedFromISR() does the same
|
||||||
|
* thing. It is provided to enable application writers to implement their own
|
||||||
|
* version of sbSEND_COMPLETED(), and MUST NOT BE USED AT ANY OTHER TIME.
|
||||||
|
*
|
||||||
|
* See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for
|
||||||
|
* additional information.
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferSendCompletedFromISR() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the stream buffer to which data was
|
||||||
|
* written.
|
||||||
|
*
|
||||||
|
* @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be
|
||||||
|
* initialised to pdFALSE before it is passed into
|
||||||
|
* xMessageBufferSendCompletedFromISR(). If calling
|
||||||
|
* xMessageBufferSendCompletedFromISR() removes a task from the Blocked state,
|
||||||
|
* and the task has a priority above the priority of the currently running task,
|
||||||
|
* then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a
|
||||||
|
* context switch should be performed before exiting the ISR.
|
||||||
|
*
|
||||||
|
* @return If a task was removed from the Blocked state then pdTRUE is returned.
|
||||||
|
* Otherwise pdFALSE is returned.
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferSendCompletedFromISR xMessageBufferSendCompletedFromISR
|
||||||
|
* \ingroup StreamBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferSendCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) \
|
||||||
|
xStreamBufferSendCompletedFromISR( ( xMessageBuffer ), ( pxHigherPriorityTaskWoken ) )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
* @code{c}
|
||||||
|
* BaseType_t xMessageBufferReceiveCompletedFromISR( MessageBufferHandle_t xMessageBuffer, BaseType_t *pxHigherPriorityTaskWoken );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* For advanced users only.
|
||||||
|
*
|
||||||
|
* The sbRECEIVE_COMPLETED() macro is called from within the FreeRTOS APIs when
|
||||||
|
* data is read out of a message buffer or stream buffer. If there was a task
|
||||||
|
* that was blocked on the message or stream buffer waiting for data to arrive
|
||||||
|
* then the sbRECEIVE_COMPLETED() macro sends a notification to the task to
|
||||||
|
* remove it from the Blocked state. xMessageBufferReceiveCompletedFromISR()
|
||||||
|
* does the same thing. It is provided to enable application writers to
|
||||||
|
* implement their own version of sbRECEIVE_COMPLETED(), and MUST NOT BE USED AT
|
||||||
|
* ANY OTHER TIME.
|
||||||
|
*
|
||||||
|
* See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for
|
||||||
|
* additional information.
|
||||||
|
*
|
||||||
|
* configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for
|
||||||
|
* xMessageBufferReceiveCompletedFromISR() to be available.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the stream buffer from which data was
|
||||||
|
* read.
|
||||||
|
*
|
||||||
|
* @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be
|
||||||
|
* initialised to pdFALSE before it is passed into
|
||||||
|
* xMessageBufferReceiveCompletedFromISR(). If calling
|
||||||
|
* xMessageBufferReceiveCompletedFromISR() removes a task from the Blocked state,
|
||||||
|
* and the task has a priority above the priority of the currently running task,
|
||||||
|
* then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a
|
||||||
|
* context switch should be performed before exiting the ISR.
|
||||||
|
*
|
||||||
|
* @return If a task was removed from the Blocked state then pdTRUE is returned.
|
||||||
|
* Otherwise pdFALSE is returned.
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferReceiveCompletedFromISR xMessageBufferReceiveCompletedFromISR
|
||||||
|
* \ingroup StreamBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferReceiveCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) \
|
||||||
|
xStreamBufferReceiveCompletedFromISR( ( xMessageBuffer ), ( pxHigherPriorityTaskWoken ) )
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#if defined( __cplusplus )
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
#endif /* !defined( FREERTOS_MESSAGE_BUFFER_H ) */
|
||||||
@@ -0,0 +1,495 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the MPU is used the standard (non MPU) API functions are mapped to
|
||||||
|
* equivalents that start "MPU_", the prototypes for which are defined in this
|
||||||
|
* header files. This will cause the application code to call the MPU_ version
|
||||||
|
* which wraps the non-MPU version with privilege promoting then demoting code,
|
||||||
|
* so the kernel code always runs will full privileges.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef MPU_PROTOTYPES_H
|
||||||
|
#define MPU_PROTOTYPES_H
|
||||||
|
|
||||||
|
typedef struct xTaskGenericNotifyParams
|
||||||
|
{
|
||||||
|
TaskHandle_t xTaskToNotify;
|
||||||
|
UBaseType_t uxIndexToNotify;
|
||||||
|
uint32_t ulValue;
|
||||||
|
eNotifyAction eAction;
|
||||||
|
uint32_t * pulPreviousNotificationValue;
|
||||||
|
} xTaskGenericNotifyParams_t;
|
||||||
|
|
||||||
|
typedef struct xTaskGenericNotifyWaitParams
|
||||||
|
{
|
||||||
|
UBaseType_t uxIndexToWaitOn;
|
||||||
|
uint32_t ulBitsToClearOnEntry;
|
||||||
|
uint32_t ulBitsToClearOnExit;
|
||||||
|
uint32_t * pulNotificationValue;
|
||||||
|
TickType_t xTicksToWait;
|
||||||
|
} xTaskGenericNotifyWaitParams_t;
|
||||||
|
|
||||||
|
typedef struct xTimerGenericCommandFromTaskParams
|
||||||
|
{
|
||||||
|
TimerHandle_t xTimer;
|
||||||
|
BaseType_t xCommandID;
|
||||||
|
TickType_t xOptionalValue;
|
||||||
|
BaseType_t * pxHigherPriorityTaskWoken;
|
||||||
|
TickType_t xTicksToWait;
|
||||||
|
} xTimerGenericCommandFromTaskParams_t;
|
||||||
|
|
||||||
|
typedef struct xEventGroupWaitBitsParams
|
||||||
|
{
|
||||||
|
EventGroupHandle_t xEventGroup;
|
||||||
|
EventBits_t uxBitsToWaitFor;
|
||||||
|
BaseType_t xClearOnExit;
|
||||||
|
BaseType_t xWaitForAllBits;
|
||||||
|
TickType_t xTicksToWait;
|
||||||
|
} xEventGroupWaitBitsParams_t;
|
||||||
|
|
||||||
|
/* MPU versions of task.h API functions. */
|
||||||
|
void MPU_vTaskDelay( const TickType_t xTicksToDelay ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
|
||||||
|
const TickType_t xTimeIncrement ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskAbortDelay( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxTaskPriorityGet( const TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
eTaskState MPU_eTaskGetState( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskGetInfo( TaskHandle_t xTask,
|
||||||
|
TaskStatus_t * pxTaskStatus,
|
||||||
|
BaseType_t xGetFreeStackSpace,
|
||||||
|
eTaskState eState ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskSuspend( TaskHandle_t xTaskToSuspend ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskResume( TaskHandle_t xTaskToResume ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TickType_t MPU_xTaskGetTickCount( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxTaskGetNumberOfTasks( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
configSTACK_DEPTH_TYPE MPU_uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskSetApplicationTaskTag( TaskHandle_t xTask,
|
||||||
|
TaskHookFunction_t pxHookFunction ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHookFunction_t MPU_xTaskGetApplicationTaskTag( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet,
|
||||||
|
BaseType_t xIndex,
|
||||||
|
void * pvValue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void * MPU_pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery,
|
||||||
|
BaseType_t xIndex ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xTaskGetIdleTaskHandle( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray,
|
||||||
|
const UBaseType_t uxArraySize,
|
||||||
|
configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime ) FREERTOS_SYSTEM_CALL;
|
||||||
|
configRUN_TIME_COUNTER_TYPE MPU_ulTaskGetRunTimeCounter( const TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
configRUN_TIME_COUNTER_TYPE MPU_ulTaskGetRunTimePercent( const TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
configRUN_TIME_COUNTER_TYPE MPU_ulTaskGetIdleRunTimeCounter( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
configRUN_TIME_COUNTER_TYPE MPU_ulTaskGetIdleRunTimePercent( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskGenericNotify( TaskHandle_t xTaskToNotify,
|
||||||
|
UBaseType_t uxIndexToNotify,
|
||||||
|
uint32_t ulValue,
|
||||||
|
eNotifyAction eAction,
|
||||||
|
uint32_t * pulPreviousNotificationValue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskGenericNotifyEntry( const xTaskGenericNotifyParams_t * pxParams ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn,
|
||||||
|
uint32_t ulBitsToClearOnEntry,
|
||||||
|
uint32_t ulBitsToClearOnExit,
|
||||||
|
uint32_t * pulNotificationValue,
|
||||||
|
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskGenericNotifyWaitEntry( const xTaskGenericNotifyWaitParams_t * pxParams ) FREERTOS_SYSTEM_CALL;
|
||||||
|
uint32_t MPU_ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn,
|
||||||
|
BaseType_t xClearCountOnExit,
|
||||||
|
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskGenericNotifyStateClear( TaskHandle_t xTask,
|
||||||
|
UBaseType_t uxIndexToClear ) FREERTOS_SYSTEM_CALL;
|
||||||
|
uint32_t MPU_ulTaskGenericNotifyValueClear( TaskHandle_t xTask,
|
||||||
|
UBaseType_t uxIndexToClear,
|
||||||
|
uint32_t ulBitsToClear ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut,
|
||||||
|
TickType_t * const pxTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xTaskGetCurrentTaskHandle( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskGetSchedulerState( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
/* Privileged only wrappers for Task APIs. These are needed so that
|
||||||
|
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||||
|
* with all the APIs. */
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 1 )
|
||||||
|
|
||||||
|
BaseType_t MPU_xTaskCreate( TaskFunction_t pxTaskCode,
|
||||||
|
const char * const pcName,
|
||||||
|
const configSTACK_DEPTH_TYPE uxStackDepth,
|
||||||
|
void * const pvParameters,
|
||||||
|
UBaseType_t uxPriority,
|
||||||
|
TaskHandle_t * const pxCreatedTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xTaskCreateStatic( TaskFunction_t pxTaskCode,
|
||||||
|
const char * const pcName,
|
||||||
|
const configSTACK_DEPTH_TYPE uxStackDepth,
|
||||||
|
void * const pvParameters,
|
||||||
|
UBaseType_t uxPriority,
|
||||||
|
StackType_t * const puxStackBuffer,
|
||||||
|
StaticTask_t * const pxTaskBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskDelete( TaskHandle_t xTaskToDelete ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskPrioritySet( TaskHandle_t xTask,
|
||||||
|
UBaseType_t uxNewPriority ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xTaskGetHandle( const char * pcNameToQuery ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask,
|
||||||
|
void * pvParameter ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskGetRunTimeStatistics( char * pcWriteBuffer,
|
||||||
|
size_t uxBufferLength ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskListTasks( char * pcWriteBuffer,
|
||||||
|
size_t uxBufferLength ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskSuspendAll( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskResumeAll( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
#else /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||||
|
|
||||||
|
BaseType_t MPU_xTaskCreate( TaskFunction_t pxTaskCode,
|
||||||
|
const char * const pcName,
|
||||||
|
const configSTACK_DEPTH_TYPE uxStackDepth,
|
||||||
|
void * const pvParameters,
|
||||||
|
UBaseType_t uxPriority,
|
||||||
|
TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION;
|
||||||
|
TaskHandle_t MPU_xTaskCreateStatic( TaskFunction_t pxTaskCode,
|
||||||
|
const char * const pcName,
|
||||||
|
const configSTACK_DEPTH_TYPE uxStackDepth,
|
||||||
|
void * const pvParameters,
|
||||||
|
UBaseType_t uxPriority,
|
||||||
|
StackType_t * const puxStackBuffer,
|
||||||
|
StaticTask_t * const pxTaskBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
void MPU_vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION;
|
||||||
|
void MPU_vTaskPrioritySet( TaskHandle_t xTask,
|
||||||
|
UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION;
|
||||||
|
TaskHandle_t MPU_xTaskGetHandle( const char * pcNameToQuery ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask,
|
||||||
|
void * pvParameter ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||||
|
|
||||||
|
char * MPU_pcTaskGetName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition,
|
||||||
|
TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition,
|
||||||
|
TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION;
|
||||||
|
void MPU_vTaskAllocateMPURegions( TaskHandle_t xTaskToModify,
|
||||||
|
const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xTaskGetStaticBuffers( TaskHandle_t xTask,
|
||||||
|
StackType_t ** ppuxStackBuffer,
|
||||||
|
StaticTask_t ** ppxTaskBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
UBaseType_t MPU_uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION;
|
||||||
|
UBaseType_t MPU_uxTaskBasePriorityGet( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION;
|
||||||
|
UBaseType_t MPU_uxTaskBasePriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION;
|
||||||
|
TaskHookFunction_t MPU_xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify,
|
||||||
|
UBaseType_t uxIndexToNotify,
|
||||||
|
uint32_t ulValue,
|
||||||
|
eNotifyAction eAction,
|
||||||
|
uint32_t * pulPreviousNotificationValue,
|
||||||
|
BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||||
|
void MPU_vTaskGenericNotifyGiveFromISR( TaskHandle_t xTaskToNotify,
|
||||||
|
UBaseType_t uxIndexToNotify,
|
||||||
|
BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/* MPU versions of queue.h API functions. */
|
||||||
|
BaseType_t MPU_xQueueGenericSend( QueueHandle_t xQueue,
|
||||||
|
const void * const pvItemToQueue,
|
||||||
|
TickType_t xTicksToWait,
|
||||||
|
const BaseType_t xCopyPosition ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueReceive( QueueHandle_t xQueue,
|
||||||
|
void * const pvBuffer,
|
||||||
|
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueuePeek( QueueHandle_t xQueue,
|
||||||
|
void * const pvBuffer,
|
||||||
|
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueSemaphoreTake( QueueHandle_t xQueue,
|
||||||
|
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxQueueMessagesWaiting( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxQueueSpacesAvailable( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xQueueGetMutexHolder( QueueHandle_t xSemaphore ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueTakeMutexRecursive( QueueHandle_t xMutex,
|
||||||
|
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vQueueAddToRegistry( QueueHandle_t xQueue,
|
||||||
|
const char * pcName ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vQueueUnregisterQueue( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
const char * MPU_pcQueueGetName( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,
|
||||||
|
QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueSetMemberHandle_t MPU_xQueueSelectFromSet( QueueSetHandle_t xQueueSet,
|
||||||
|
const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vQueueSetQueueNumber( QueueHandle_t xQueue,
|
||||||
|
UBaseType_t uxQueueNumber ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxQueueGetQueueNumber( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
uint8_t MPU_ucQueueGetQueueType( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
/* Privileged only wrappers for Queue APIs. These are needed so that
|
||||||
|
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||||
|
* with all the APIs. */
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 1 )
|
||||||
|
|
||||||
|
void MPU_vQueueDelete( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueCreateMutex( const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueCreateMutexStatic( const uint8_t ucQueueType,
|
||||||
|
StaticQueue_t * pxStaticQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount,
|
||||||
|
const UBaseType_t uxInitialCount ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount,
|
||||||
|
const UBaseType_t uxInitialCount,
|
||||||
|
StaticQueue_t * pxStaticQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueGenericCreate( const UBaseType_t uxQueueLength,
|
||||||
|
const UBaseType_t uxItemSize,
|
||||||
|
const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueGenericCreateStatic( const UBaseType_t uxQueueLength,
|
||||||
|
const UBaseType_t uxItemSize,
|
||||||
|
uint8_t * pucQueueStorage,
|
||||||
|
StaticQueue_t * pxStaticQueue,
|
||||||
|
const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueSetHandle_t MPU_xQueueCreateSet( const UBaseType_t uxEventQueueLength ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueSetHandle_t MPU_xQueueCreateSetStatic( const UBaseType_t uxEventQueueLength,
|
||||||
|
uint8_t * pucQueueStorage,
|
||||||
|
StaticQueue_t * pxStaticQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore,
|
||||||
|
QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueGenericReset( QueueHandle_t xQueue,
|
||||||
|
BaseType_t xNewQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
#else /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||||
|
|
||||||
|
void MPU_vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
|
||||||
|
QueueHandle_t MPU_xQueueCreateMutex( const uint8_t ucQueueType ) PRIVILEGED_FUNCTION;
|
||||||
|
QueueHandle_t MPU_xQueueCreateMutexStatic( const uint8_t ucQueueType,
|
||||||
|
StaticQueue_t * pxStaticQueue ) PRIVILEGED_FUNCTION;
|
||||||
|
QueueHandle_t MPU_xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount,
|
||||||
|
const UBaseType_t uxInitialCount ) PRIVILEGED_FUNCTION;
|
||||||
|
QueueHandle_t MPU_xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount,
|
||||||
|
const UBaseType_t uxInitialCount,
|
||||||
|
StaticQueue_t * pxStaticQueue ) PRIVILEGED_FUNCTION;
|
||||||
|
QueueHandle_t MPU_xQueueGenericCreate( const UBaseType_t uxQueueLength,
|
||||||
|
const UBaseType_t uxItemSize,
|
||||||
|
const uint8_t ucQueueType ) PRIVILEGED_FUNCTION;
|
||||||
|
QueueHandle_t MPU_xQueueGenericCreateStatic( const UBaseType_t uxQueueLength,
|
||||||
|
const UBaseType_t uxItemSize,
|
||||||
|
uint8_t * pucQueueStorage,
|
||||||
|
StaticQueue_t * pxStaticQueue,
|
||||||
|
const uint8_t ucQueueType ) PRIVILEGED_FUNCTION;
|
||||||
|
QueueSetHandle_t MPU_xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILEGED_FUNCTION;
|
||||||
|
QueueSetHandle_t MPU_xQueueCreateSetStatic( const UBaseType_t uxEventQueueLength,
|
||||||
|
uint8_t * pucQueueStorage,
|
||||||
|
StaticQueue_t * pxStaticQueue ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore,
|
||||||
|
QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xQueueGenericReset( QueueHandle_t xQueue,
|
||||||
|
BaseType_t xNewQueue ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||||
|
|
||||||
|
BaseType_t MPU_xQueueGenericGetStaticBuffers( QueueHandle_t xQueue,
|
||||||
|
uint8_t ** ppucQueueStorage,
|
||||||
|
StaticQueue_t ** ppxStaticQueue ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xQueueGenericSendFromISR( QueueHandle_t xQueue,
|
||||||
|
const void * const pvItemToQueue,
|
||||||
|
BaseType_t * const pxHigherPriorityTaskWoken,
|
||||||
|
const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xQueueGiveFromISR( QueueHandle_t xQueue,
|
||||||
|
BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xQueuePeekFromISR( QueueHandle_t xQueue,
|
||||||
|
void * const pvBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xQueueReceiveFromISR( QueueHandle_t xQueue,
|
||||||
|
void * const pvBuffer,
|
||||||
|
BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
|
||||||
|
UBaseType_t MPU_uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION;
|
||||||
|
TaskHandle_t MPU_xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION;
|
||||||
|
QueueSetMemberHandle_t MPU_xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/* MPU versions of timers.h API functions. */
|
||||||
|
void * MPU_pvTimerGetTimerID( const TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTimerSetTimerID( TimerHandle_t xTimer,
|
||||||
|
void * pvNewID ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTimerIsTimerActive( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xTimerGetTimerDaemonTaskHandle( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTimerGenericCommandFromTask( TimerHandle_t xTimer,
|
||||||
|
const BaseType_t xCommandID,
|
||||||
|
const TickType_t xOptionalValue,
|
||||||
|
BaseType_t * const pxHigherPriorityTaskWoken,
|
||||||
|
const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTimerGenericCommandFromTaskEntry( const xTimerGenericCommandFromTaskParams_t * pxParams ) FREERTOS_SYSTEM_CALL;
|
||||||
|
const char * MPU_pcTimerGetName( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTimerSetReloadMode( TimerHandle_t xTimer,
|
||||||
|
const BaseType_t xAutoReload ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTimerGetReloadMode( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxTimerGetReloadMode( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TickType_t MPU_xTimerGetPeriod( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TickType_t MPU_xTimerGetExpiryTime( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
/* Privileged only wrappers for Timer APIs. These are needed so that
|
||||||
|
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||||
|
* with all the APIs. */
|
||||||
|
TimerHandle_t MPU_xTimerCreate( const char * const pcTimerName,
|
||||||
|
const TickType_t xTimerPeriodInTicks,
|
||||||
|
const BaseType_t xAutoReload,
|
||||||
|
void * const pvTimerID,
|
||||||
|
TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION;
|
||||||
|
TimerHandle_t MPU_xTimerCreateStatic( const char * const pcTimerName,
|
||||||
|
const TickType_t xTimerPeriodInTicks,
|
||||||
|
const BaseType_t xAutoReload,
|
||||||
|
void * const pvTimerID,
|
||||||
|
TimerCallbackFunction_t pxCallbackFunction,
|
||||||
|
StaticTimer_t * pxTimerBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xTimerGetStaticBuffer( TimerHandle_t xTimer,
|
||||||
|
StaticTimer_t ** ppxTimerBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xTimerGenericCommandFromISR( TimerHandle_t xTimer,
|
||||||
|
const BaseType_t xCommandID,
|
||||||
|
const TickType_t xOptionalValue,
|
||||||
|
BaseType_t * const pxHigherPriorityTaskWoken,
|
||||||
|
const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/* MPU versions of event_group.h API functions. */
|
||||||
|
EventBits_t MPU_xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToWaitFor,
|
||||||
|
const BaseType_t xClearOnExit,
|
||||||
|
const BaseType_t xWaitForAllBits,
|
||||||
|
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
EventBits_t MPU_xEventGroupWaitBitsEntry( const xEventGroupWaitBitsParams_t * pxParams ) FREERTOS_SYSTEM_CALL;
|
||||||
|
EventBits_t MPU_xEventGroupClearBits( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToClear ) FREERTOS_SYSTEM_CALL;
|
||||||
|
EventBits_t MPU_xEventGroupSetBits( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToSet ) FREERTOS_SYSTEM_CALL;
|
||||||
|
EventBits_t MPU_xEventGroupSync( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToSet,
|
||||||
|
const EventBits_t uxBitsToWaitFor,
|
||||||
|
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
#if ( configUSE_TRACE_FACILITY == 1 )
|
||||||
|
UBaseType_t MPU_uxEventGroupGetNumber( void * xEventGroup ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vEventGroupSetNumber( void * xEventGroup,
|
||||||
|
UBaseType_t uxEventGroupNumber ) FREERTOS_SYSTEM_CALL;
|
||||||
|
#endif /* #if ( configUSE_TRACE_FACILITY == 1 ) */
|
||||||
|
|
||||||
|
/* Privileged only wrappers for Event Group APIs. These are needed so that
|
||||||
|
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||||
|
* with all the APIs. */
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 1 )
|
||||||
|
|
||||||
|
EventGroupHandle_t MPU_xEventGroupCreate( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
EventGroupHandle_t MPU_xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vEventGroupDelete( EventGroupHandle_t xEventGroup ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
#else /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||||
|
|
||||||
|
EventGroupHandle_t MPU_xEventGroupCreate( void ) PRIVILEGED_FUNCTION;
|
||||||
|
EventGroupHandle_t MPU_xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
void MPU_vEventGroupDelete( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||||
|
|
||||||
|
BaseType_t MPU_xEventGroupGetStaticBuffer( EventGroupHandle_t xEventGroup,
|
||||||
|
StaticEventGroup_t ** ppxEventGroupBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
EventBits_t MPU_xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||||
|
BaseType_t MPU_xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToClear ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToSet,
|
||||||
|
BaseType_t * pxHigherPriorityTaskWoken ) FREERTOS_SYSTEM_CALL;
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||||
|
|
||||||
|
/* MPU versions of message/stream_buffer.h API functions. */
|
||||||
|
size_t MPU_xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,
|
||||||
|
const void * pvTxData,
|
||||||
|
size_t xDataLengthBytes,
|
||||||
|
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
size_t MPU_xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,
|
||||||
|
void * pvRxData,
|
||||||
|
size_t xBufferLengthBytes,
|
||||||
|
TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
size_t MPU_xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
size_t MPU_xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer,
|
||||||
|
size_t xTriggerLevel ) FREERTOS_SYSTEM_CALL;
|
||||||
|
size_t MPU_xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
/* Privileged only wrappers for Stream Buffer APIs. These are needed so that
|
||||||
|
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||||
|
* with all the APIs. */
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 1 )
|
||||||
|
|
||||||
|
StreamBufferHandle_t MPU_xStreamBufferGenericCreate( size_t xBufferSizeBytes,
|
||||||
|
size_t xTriggerLevelBytes,
|
||||||
|
BaseType_t xStreamBufferType,
|
||||||
|
StreamBufferCallbackFunction_t pxSendCompletedCallback,
|
||||||
|
StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) FREERTOS_SYSTEM_CALL;
|
||||||
|
StreamBufferHandle_t MPU_xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes,
|
||||||
|
size_t xTriggerLevelBytes,
|
||||||
|
BaseType_t xStreamBufferType,
|
||||||
|
uint8_t * const pucStreamBufferStorageArea,
|
||||||
|
StaticStreamBuffer_t * const pxStaticStreamBuffer,
|
||||||
|
StreamBufferCallbackFunction_t pxSendCompletedCallback,
|
||||||
|
StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
#else /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||||
|
|
||||||
|
StreamBufferHandle_t MPU_xStreamBufferGenericCreate( size_t xBufferSizeBytes,
|
||||||
|
size_t xTriggerLevelBytes,
|
||||||
|
BaseType_t xStreamBufferType,
|
||||||
|
StreamBufferCallbackFunction_t pxSendCompletedCallback,
|
||||||
|
StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) PRIVILEGED_FUNCTION;
|
||||||
|
StreamBufferHandle_t MPU_xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes,
|
||||||
|
size_t xTriggerLevelBytes,
|
||||||
|
BaseType_t xStreamBufferType,
|
||||||
|
uint8_t * const pucStreamBufferStorageArea,
|
||||||
|
StaticStreamBuffer_t * const pxStaticStreamBuffer,
|
||||||
|
StreamBufferCallbackFunction_t pxSendCompletedCallback,
|
||||||
|
StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) PRIVILEGED_FUNCTION;
|
||||||
|
void MPU_vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||||
|
|
||||||
|
BaseType_t MPU_xStreamBufferGetStaticBuffers( StreamBufferHandle_t xStreamBuffers,
|
||||||
|
uint8_t * ppucStreamBufferStorageArea,
|
||||||
|
StaticStreamBuffer_t * ppxStaticStreamBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
size_t MPU_xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer,
|
||||||
|
const void * pvTxData,
|
||||||
|
size_t xDataLengthBytes,
|
||||||
|
BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||||
|
size_t MPU_xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer,
|
||||||
|
void * pvRxData,
|
||||||
|
size_t xBufferLengthBytes,
|
||||||
|
BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer,
|
||||||
|
BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer,
|
||||||
|
BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||||
|
BaseType_t MPU_xStreamBufferResetFromISR( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
#endif /* MPU_PROTOTYPES_H */
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPU_SYSCALL_NUMBERS_H
|
||||||
|
#define MPU_SYSCALL_NUMBERS_H
|
||||||
|
|
||||||
|
/* Numbers assigned to various system calls. */
|
||||||
|
#define SYSTEM_CALL_xTaskGenericNotify 0
|
||||||
|
#define SYSTEM_CALL_xTaskGenericNotifyWait 1
|
||||||
|
#define SYSTEM_CALL_xTimerGenericCommandFromTask 2
|
||||||
|
#define SYSTEM_CALL_xEventGroupWaitBits 3
|
||||||
|
#define SYSTEM_CALL_xTaskDelayUntil 4
|
||||||
|
#define SYSTEM_CALL_xTaskAbortDelay 5
|
||||||
|
#define SYSTEM_CALL_vTaskDelay 6
|
||||||
|
#define SYSTEM_CALL_uxTaskPriorityGet 7
|
||||||
|
#define SYSTEM_CALL_eTaskGetState 8
|
||||||
|
#define SYSTEM_CALL_vTaskGetInfo 9
|
||||||
|
#define SYSTEM_CALL_xTaskGetIdleTaskHandle 10
|
||||||
|
#define SYSTEM_CALL_vTaskSuspend 11
|
||||||
|
#define SYSTEM_CALL_vTaskResume 12
|
||||||
|
#define SYSTEM_CALL_xTaskGetTickCount 13
|
||||||
|
#define SYSTEM_CALL_uxTaskGetNumberOfTasks 14
|
||||||
|
#define SYSTEM_CALL_ulTaskGetRunTimeCounter 15
|
||||||
|
#define SYSTEM_CALL_ulTaskGetRunTimePercent 16
|
||||||
|
#define SYSTEM_CALL_ulTaskGetIdleRunTimePercent 17
|
||||||
|
#define SYSTEM_CALL_ulTaskGetIdleRunTimeCounter 18
|
||||||
|
#define SYSTEM_CALL_vTaskSetApplicationTaskTag 19
|
||||||
|
#define SYSTEM_CALL_xTaskGetApplicationTaskTag 20
|
||||||
|
#define SYSTEM_CALL_vTaskSetThreadLocalStoragePointer 21
|
||||||
|
#define SYSTEM_CALL_pvTaskGetThreadLocalStoragePointer 22
|
||||||
|
#define SYSTEM_CALL_uxTaskGetSystemState 23
|
||||||
|
#define SYSTEM_CALL_uxTaskGetStackHighWaterMark 24
|
||||||
|
#define SYSTEM_CALL_uxTaskGetStackHighWaterMark2 25
|
||||||
|
#define SYSTEM_CALL_xTaskGetCurrentTaskHandle 26
|
||||||
|
#define SYSTEM_CALL_xTaskGetSchedulerState 27
|
||||||
|
#define SYSTEM_CALL_vTaskSetTimeOutState 28
|
||||||
|
#define SYSTEM_CALL_xTaskCheckForTimeOut 29
|
||||||
|
#define SYSTEM_CALL_ulTaskGenericNotifyTake 30
|
||||||
|
#define SYSTEM_CALL_xTaskGenericNotifyStateClear 31
|
||||||
|
#define SYSTEM_CALL_ulTaskGenericNotifyValueClear 32
|
||||||
|
#define SYSTEM_CALL_xQueueGenericSend 33
|
||||||
|
#define SYSTEM_CALL_uxQueueMessagesWaiting 34
|
||||||
|
#define SYSTEM_CALL_uxQueueSpacesAvailable 35
|
||||||
|
#define SYSTEM_CALL_xQueueReceive 36
|
||||||
|
#define SYSTEM_CALL_xQueuePeek 37
|
||||||
|
#define SYSTEM_CALL_xQueueSemaphoreTake 38
|
||||||
|
#define SYSTEM_CALL_xQueueGetMutexHolder 39
|
||||||
|
#define SYSTEM_CALL_xQueueTakeMutexRecursive 40
|
||||||
|
#define SYSTEM_CALL_xQueueGiveMutexRecursive 41
|
||||||
|
#define SYSTEM_CALL_xQueueSelectFromSet 42
|
||||||
|
#define SYSTEM_CALL_xQueueAddToSet 43
|
||||||
|
#define SYSTEM_CALL_vQueueAddToRegistry 44
|
||||||
|
#define SYSTEM_CALL_vQueueUnregisterQueue 45
|
||||||
|
#define SYSTEM_CALL_pcQueueGetName 46
|
||||||
|
#define SYSTEM_CALL_pvTimerGetTimerID 47
|
||||||
|
#define SYSTEM_CALL_vTimerSetTimerID 48
|
||||||
|
#define SYSTEM_CALL_xTimerIsTimerActive 49
|
||||||
|
#define SYSTEM_CALL_xTimerGetTimerDaemonTaskHandle 50
|
||||||
|
#define SYSTEM_CALL_pcTimerGetName 51
|
||||||
|
#define SYSTEM_CALL_vTimerSetReloadMode 52
|
||||||
|
#define SYSTEM_CALL_xTimerGetReloadMode 53
|
||||||
|
#define SYSTEM_CALL_uxTimerGetReloadMode 54
|
||||||
|
#define SYSTEM_CALL_xTimerGetPeriod 55
|
||||||
|
#define SYSTEM_CALL_xTimerGetExpiryTime 56
|
||||||
|
#define SYSTEM_CALL_xEventGroupClearBits 57
|
||||||
|
#define SYSTEM_CALL_xEventGroupSetBits 58
|
||||||
|
#define SYSTEM_CALL_xEventGroupSync 59
|
||||||
|
#define SYSTEM_CALL_uxEventGroupGetNumber 60
|
||||||
|
#define SYSTEM_CALL_vEventGroupSetNumber 61
|
||||||
|
#define SYSTEM_CALL_xStreamBufferSend 62
|
||||||
|
#define SYSTEM_CALL_xStreamBufferReceive 63
|
||||||
|
#define SYSTEM_CALL_xStreamBufferIsFull 64
|
||||||
|
#define SYSTEM_CALL_xStreamBufferIsEmpty 65
|
||||||
|
#define SYSTEM_CALL_xStreamBufferSpacesAvailable 66
|
||||||
|
#define SYSTEM_CALL_xStreamBufferBytesAvailable 67
|
||||||
|
#define SYSTEM_CALL_xStreamBufferSetTriggerLevel 68
|
||||||
|
#define SYSTEM_CALL_xStreamBufferNextMessageLengthBytes 69
|
||||||
|
#define NUM_SYSTEM_CALLS 70 /* Total number of system calls. */
|
||||||
|
|
||||||
|
#endif /* MPU_SYSCALL_NUMBERS_H */
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPU_WRAPPERS_H
|
||||||
|
#define MPU_WRAPPERS_H
|
||||||
|
|
||||||
|
/* This file redefines API functions to be called through a wrapper macro, but
|
||||||
|
* only for ports that are using the MPU. */
|
||||||
|
#if ( portUSING_MPU_WRAPPERS == 1 )
|
||||||
|
|
||||||
|
/* MPU_WRAPPERS_INCLUDED_FROM_API_FILE will be defined when this file is
|
||||||
|
* included from queue.c or task.c to prevent it from having an effect within
|
||||||
|
* those files. */
|
||||||
|
#ifndef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Map standard (non MPU) API functions to equivalents that start
|
||||||
|
* "MPU_". This will cause the application code to call the MPU_
|
||||||
|
* version, which wraps the non-MPU version with privilege promoting
|
||||||
|
* then demoting code, so the kernel code always runs will full
|
||||||
|
* privileges.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Map standard task.h API functions to the MPU equivalents. */
|
||||||
|
#define vTaskDelay MPU_vTaskDelay
|
||||||
|
#define xTaskDelayUntil MPU_xTaskDelayUntil
|
||||||
|
#define xTaskAbortDelay MPU_xTaskAbortDelay
|
||||||
|
#define uxTaskPriorityGet MPU_uxTaskPriorityGet
|
||||||
|
#define eTaskGetState MPU_eTaskGetState
|
||||||
|
#define vTaskGetInfo MPU_vTaskGetInfo
|
||||||
|
#define vTaskSuspend MPU_vTaskSuspend
|
||||||
|
#define vTaskResume MPU_vTaskResume
|
||||||
|
#define xTaskGetTickCount MPU_xTaskGetTickCount
|
||||||
|
#define uxTaskGetNumberOfTasks MPU_uxTaskGetNumberOfTasks
|
||||||
|
#define uxTaskGetStackHighWaterMark MPU_uxTaskGetStackHighWaterMark
|
||||||
|
#define uxTaskGetStackHighWaterMark2 MPU_uxTaskGetStackHighWaterMark2
|
||||||
|
#define vTaskSetApplicationTaskTag MPU_vTaskSetApplicationTaskTag
|
||||||
|
#define xTaskGetApplicationTaskTag MPU_xTaskGetApplicationTaskTag
|
||||||
|
#define vTaskSetThreadLocalStoragePointer MPU_vTaskSetThreadLocalStoragePointer
|
||||||
|
#define pvTaskGetThreadLocalStoragePointer MPU_pvTaskGetThreadLocalStoragePointer
|
||||||
|
#define xTaskGetIdleTaskHandle MPU_xTaskGetIdleTaskHandle
|
||||||
|
#define uxTaskGetSystemState MPU_uxTaskGetSystemState
|
||||||
|
#define ulTaskGetIdleRunTimeCounter MPU_ulTaskGetIdleRunTimeCounter
|
||||||
|
#define ulTaskGetIdleRunTimePercent MPU_ulTaskGetIdleRunTimePercent
|
||||||
|
#define xTaskGenericNotify MPU_xTaskGenericNotify
|
||||||
|
#define xTaskGenericNotifyWait MPU_xTaskGenericNotifyWait
|
||||||
|
#define ulTaskGenericNotifyTake MPU_ulTaskGenericNotifyTake
|
||||||
|
#define xTaskGenericNotifyStateClear MPU_xTaskGenericNotifyStateClear
|
||||||
|
#define ulTaskGenericNotifyValueClear MPU_ulTaskGenericNotifyValueClear
|
||||||
|
#define vTaskSetTimeOutState MPU_vTaskSetTimeOutState
|
||||||
|
#define xTaskCheckForTimeOut MPU_xTaskCheckForTimeOut
|
||||||
|
#define xTaskGetCurrentTaskHandle MPU_xTaskGetCurrentTaskHandle
|
||||||
|
#define xTaskGetSchedulerState MPU_xTaskGetSchedulerState
|
||||||
|
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||||
|
#define ulTaskGetRunTimeCounter MPU_ulTaskGetRunTimeCounter
|
||||||
|
#define ulTaskGetRunTimePercent MPU_ulTaskGetRunTimePercent
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||||
|
|
||||||
|
/* Privileged only wrappers for Task APIs. These are needed so that
|
||||||
|
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||||
|
* with all the APIs. */
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 1 )
|
||||||
|
|
||||||
|
/* These are not needed in v2 because they do not take a task
|
||||||
|
* handle and therefore, no lookup is needed. Needed in v1 because
|
||||||
|
* these are available as system calls in v1. */
|
||||||
|
#define vTaskGetRunTimeStatistics MPU_vTaskGetRunTimeStatistics
|
||||||
|
#define vTaskListTasks MPU_vTaskListTasks
|
||||||
|
#define vTaskSuspendAll MPU_vTaskSuspendAll
|
||||||
|
#define xTaskCatchUpTicks MPU_xTaskCatchUpTicks
|
||||||
|
#define xTaskResumeAll MPU_xTaskResumeAll
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 1 ) */
|
||||||
|
|
||||||
|
#define xTaskCreate MPU_xTaskCreate
|
||||||
|
#define xTaskCreateStatic MPU_xTaskCreateStatic
|
||||||
|
#define vTaskDelete MPU_vTaskDelete
|
||||||
|
#define vTaskPrioritySet MPU_vTaskPrioritySet
|
||||||
|
#define xTaskGetHandle MPU_xTaskGetHandle
|
||||||
|
#define xTaskCallApplicationTaskHook MPU_xTaskCallApplicationTaskHook
|
||||||
|
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||||
|
#define pcTaskGetName MPU_pcTaskGetName
|
||||||
|
#define xTaskCreateRestricted MPU_xTaskCreateRestricted
|
||||||
|
#define xTaskCreateRestrictedStatic MPU_xTaskCreateRestrictedStatic
|
||||||
|
#define vTaskAllocateMPURegions MPU_vTaskAllocateMPURegions
|
||||||
|
#define xTaskGetStaticBuffers MPU_xTaskGetStaticBuffers
|
||||||
|
#define uxTaskPriorityGetFromISR MPU_uxTaskPriorityGetFromISR
|
||||||
|
#define uxTaskBasePriorityGet MPU_uxTaskBasePriorityGet
|
||||||
|
#define uxTaskBasePriorityGetFromISR MPU_uxTaskBasePriorityGetFromISR
|
||||||
|
#define xTaskResumeFromISR MPU_xTaskResumeFromISR
|
||||||
|
#define xTaskGetApplicationTaskTagFromISR MPU_xTaskGetApplicationTaskTagFromISR
|
||||||
|
#define xTaskGenericNotifyFromISR MPU_xTaskGenericNotifyFromISR
|
||||||
|
#define vTaskGenericNotifyGiveFromISR MPU_vTaskGenericNotifyGiveFromISR
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||||
|
|
||||||
|
/* Map standard queue.h API functions to the MPU equivalents. */
|
||||||
|
#define xQueueGenericSend MPU_xQueueGenericSend
|
||||||
|
#define xQueueReceive MPU_xQueueReceive
|
||||||
|
#define xQueuePeek MPU_xQueuePeek
|
||||||
|
#define xQueueSemaphoreTake MPU_xQueueSemaphoreTake
|
||||||
|
#define uxQueueMessagesWaiting MPU_uxQueueMessagesWaiting
|
||||||
|
#define uxQueueSpacesAvailable MPU_uxQueueSpacesAvailable
|
||||||
|
#define xQueueGetMutexHolder MPU_xQueueGetMutexHolder
|
||||||
|
#define xQueueTakeMutexRecursive MPU_xQueueTakeMutexRecursive
|
||||||
|
#define xQueueGiveMutexRecursive MPU_xQueueGiveMutexRecursive
|
||||||
|
#define xQueueAddToSet MPU_xQueueAddToSet
|
||||||
|
#define xQueueSelectFromSet MPU_xQueueSelectFromSet
|
||||||
|
|
||||||
|
#if ( configQUEUE_REGISTRY_SIZE > 0 )
|
||||||
|
#define vQueueAddToRegistry MPU_vQueueAddToRegistry
|
||||||
|
#define vQueueUnregisterQueue MPU_vQueueUnregisterQueue
|
||||||
|
#define pcQueueGetName MPU_pcQueueGetName
|
||||||
|
#endif /* #if ( configQUEUE_REGISTRY_SIZE > 0 ) */
|
||||||
|
|
||||||
|
/* Privileged only wrappers for Queue APIs. These are needed so that
|
||||||
|
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||||
|
* with all the APIs. */
|
||||||
|
#define vQueueDelete MPU_vQueueDelete
|
||||||
|
#define xQueueCreateMutex MPU_xQueueCreateMutex
|
||||||
|
#define xQueueCreateMutexStatic MPU_xQueueCreateMutexStatic
|
||||||
|
#define xQueueCreateCountingSemaphore MPU_xQueueCreateCountingSemaphore
|
||||||
|
#define xQueueCreateCountingSemaphoreStatic MPU_xQueueCreateCountingSemaphoreStatic
|
||||||
|
#define xQueueGenericCreate MPU_xQueueGenericCreate
|
||||||
|
#define xQueueGenericCreateStatic MPU_xQueueGenericCreateStatic
|
||||||
|
#define xQueueGenericReset MPU_xQueueGenericReset
|
||||||
|
#define xQueueCreateSet MPU_xQueueCreateSet
|
||||||
|
#define xQueueCreateSetStatic MPU_xQueueCreateSetStatic
|
||||||
|
#define xQueueRemoveFromSet MPU_xQueueRemoveFromSet
|
||||||
|
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||||
|
#define xQueueGenericGetStaticBuffers MPU_xQueueGenericGetStaticBuffers
|
||||||
|
#define xQueueGenericSendFromISR MPU_xQueueGenericSendFromISR
|
||||||
|
#define xQueueGiveFromISR MPU_xQueueGiveFromISR
|
||||||
|
#define xQueuePeekFromISR MPU_xQueuePeekFromISR
|
||||||
|
#define xQueueReceiveFromISR MPU_xQueueReceiveFromISR
|
||||||
|
#define xQueueIsQueueEmptyFromISR MPU_xQueueIsQueueEmptyFromISR
|
||||||
|
#define xQueueIsQueueFullFromISR MPU_xQueueIsQueueFullFromISR
|
||||||
|
#define uxQueueMessagesWaitingFromISR MPU_uxQueueMessagesWaitingFromISR
|
||||||
|
#define xQueueGetMutexHolderFromISR MPU_xQueueGetMutexHolderFromISR
|
||||||
|
#define xQueueSelectFromSetFromISR MPU_xQueueSelectFromSetFromISR
|
||||||
|
#endif /* if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||||
|
|
||||||
|
/* Map standard timer.h API functions to the MPU equivalents. */
|
||||||
|
#define pvTimerGetTimerID MPU_pvTimerGetTimerID
|
||||||
|
#define vTimerSetTimerID MPU_vTimerSetTimerID
|
||||||
|
#define xTimerIsTimerActive MPU_xTimerIsTimerActive
|
||||||
|
#define xTimerGetTimerDaemonTaskHandle MPU_xTimerGetTimerDaemonTaskHandle
|
||||||
|
#define xTimerGenericCommandFromTask MPU_xTimerGenericCommandFromTask
|
||||||
|
#define pcTimerGetName MPU_pcTimerGetName
|
||||||
|
#define vTimerSetReloadMode MPU_vTimerSetReloadMode
|
||||||
|
#define uxTimerGetReloadMode MPU_uxTimerGetReloadMode
|
||||||
|
#define xTimerGetPeriod MPU_xTimerGetPeriod
|
||||||
|
#define xTimerGetExpiryTime MPU_xTimerGetExpiryTime
|
||||||
|
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||||
|
#define xTimerGetReloadMode MPU_xTimerGetReloadMode
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||||
|
|
||||||
|
/* Privileged only wrappers for Timer APIs. These are needed so that
|
||||||
|
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||||
|
* with all the APIs. */
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||||
|
#define xTimerCreate MPU_xTimerCreate
|
||||||
|
#define xTimerCreateStatic MPU_xTimerCreateStatic
|
||||||
|
#define xTimerGetStaticBuffer MPU_xTimerGetStaticBuffer
|
||||||
|
#define xTimerGenericCommandFromISR MPU_xTimerGenericCommandFromISR
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||||
|
|
||||||
|
/* Map standard event_group.h API functions to the MPU equivalents. */
|
||||||
|
#define xEventGroupWaitBits MPU_xEventGroupWaitBits
|
||||||
|
#define xEventGroupClearBits MPU_xEventGroupClearBits
|
||||||
|
#define xEventGroupSetBits MPU_xEventGroupSetBits
|
||||||
|
#define xEventGroupSync MPU_xEventGroupSync
|
||||||
|
|
||||||
|
#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) )
|
||||||
|
#define uxEventGroupGetNumber MPU_uxEventGroupGetNumber
|
||||||
|
#define vEventGroupSetNumber MPU_vEventGroupSetNumber
|
||||||
|
#endif /* #if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) ) */
|
||||||
|
|
||||||
|
/* Privileged only wrappers for Event Group APIs. These are needed so that
|
||||||
|
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||||
|
* with all the APIs. */
|
||||||
|
#define xEventGroupCreate MPU_xEventGroupCreate
|
||||||
|
#define xEventGroupCreateStatic MPU_xEventGroupCreateStatic
|
||||||
|
#define vEventGroupDelete MPU_vEventGroupDelete
|
||||||
|
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||||
|
#define xEventGroupGetStaticBuffer MPU_xEventGroupGetStaticBuffer
|
||||||
|
#define xEventGroupClearBitsFromISR MPU_xEventGroupClearBitsFromISR
|
||||||
|
#define xEventGroupSetBitsFromISR MPU_xEventGroupSetBitsFromISR
|
||||||
|
#define xEventGroupGetBitsFromISR MPU_xEventGroupGetBitsFromISR
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||||
|
|
||||||
|
/* Map standard message/stream_buffer.h API functions to the MPU
|
||||||
|
* equivalents. */
|
||||||
|
#define xStreamBufferSend MPU_xStreamBufferSend
|
||||||
|
#define xStreamBufferReceive MPU_xStreamBufferReceive
|
||||||
|
#define xStreamBufferIsFull MPU_xStreamBufferIsFull
|
||||||
|
#define xStreamBufferIsEmpty MPU_xStreamBufferIsEmpty
|
||||||
|
#define xStreamBufferSpacesAvailable MPU_xStreamBufferSpacesAvailable
|
||||||
|
#define xStreamBufferBytesAvailable MPU_xStreamBufferBytesAvailable
|
||||||
|
#define xStreamBufferSetTriggerLevel MPU_xStreamBufferSetTriggerLevel
|
||||||
|
#define xStreamBufferNextMessageLengthBytes MPU_xStreamBufferNextMessageLengthBytes
|
||||||
|
|
||||||
|
/* Privileged only wrappers for Stream Buffer APIs. These are needed so that
|
||||||
|
* the application can use opaque handles maintained in mpu_wrappers.c
|
||||||
|
* with all the APIs. */
|
||||||
|
|
||||||
|
#define xStreamBufferGenericCreate MPU_xStreamBufferGenericCreate
|
||||||
|
#define xStreamBufferGenericCreateStatic MPU_xStreamBufferGenericCreateStatic
|
||||||
|
#define vStreamBufferDelete MPU_vStreamBufferDelete
|
||||||
|
#define xStreamBufferReset MPU_xStreamBufferReset
|
||||||
|
|
||||||
|
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
|
||||||
|
#define xStreamBufferGetStaticBuffers MPU_xStreamBufferGetStaticBuffers
|
||||||
|
#define xStreamBufferSendFromISR MPU_xStreamBufferSendFromISR
|
||||||
|
#define xStreamBufferReceiveFromISR MPU_xStreamBufferReceiveFromISR
|
||||||
|
#define xStreamBufferSendCompletedFromISR MPU_xStreamBufferSendCompletedFromISR
|
||||||
|
#define xStreamBufferReceiveCompletedFromISR MPU_xStreamBufferReceiveCompletedFromISR
|
||||||
|
#define xStreamBufferResetFromISR MPU_xStreamBufferResetFromISR
|
||||||
|
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
|
||||||
|
|
||||||
|
#if ( ( configUSE_MPU_WRAPPERS_V1 == 0 ) && ( configENABLE_ACCESS_CONTROL_LIST == 1 ) )
|
||||||
|
|
||||||
|
#define vGrantAccessToTask( xTask, xTaskToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xTaskToGrantAccess ) )
|
||||||
|
#define vRevokeAccessToTask( xTask, xTaskToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xTaskToRevokeAccess ) )
|
||||||
|
|
||||||
|
#define vGrantAccessToSemaphore( xTask, xSemaphoreToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xSemaphoreToGrantAccess ) )
|
||||||
|
#define vRevokeAccessToSemaphore( xTask, xSemaphoreToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xSemaphoreToRevokeAccess ) )
|
||||||
|
|
||||||
|
#define vGrantAccessToQueue( xTask, xQueueToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xQueueToGrantAccess ) )
|
||||||
|
#define vRevokeAccessToQueue( xTask, xQueueToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xQueueToRevokeAccess ) )
|
||||||
|
|
||||||
|
#define vGrantAccessToQueueSet( xTask, xQueueSetToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xQueueSetToGrantAccess ) )
|
||||||
|
#define vRevokeAccessToQueueSet( xTask, xQueueSetToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xQueueSetToRevokeAccess ) )
|
||||||
|
|
||||||
|
#define vGrantAccessToEventGroup( xTask, xEventGroupToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xEventGroupToGrantAccess ) )
|
||||||
|
#define vRevokeAccessToEventGroup( xTask, xEventGroupToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xEventGroupToRevokeAccess ) )
|
||||||
|
|
||||||
|
#define vGrantAccessToStreamBuffer( xTask, xStreamBufferToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xStreamBufferToGrantAccess ) )
|
||||||
|
#define vRevokeAccessToStreamBuffer( xTask, xStreamBufferToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xStreamBufferToRevokeAccess ) )
|
||||||
|
|
||||||
|
#define vGrantAccessToMessageBuffer( xTask, xMessageBufferToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xMessageBufferToGrantAccess ) )
|
||||||
|
#define vRevokeAccessToMessageBuffer( xTask, xMessageBufferToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xMessageBufferToRevokeAccess ) )
|
||||||
|
|
||||||
|
#define vGrantAccessToTimer( xTask, xTimerToGrantAccess ) vGrantAccessToKernelObject( ( xTask ), ( int32_t ) ( xTimerToGrantAccess ) )
|
||||||
|
#define vRevokeAccessToTimer( xTask, xTimerToRevokeAccess ) vRevokeAccessToKernelObject( ( xTask ), ( int32_t ) ( xTimerToRevokeAccess ) )
|
||||||
|
|
||||||
|
#endif /* #if ( ( configUSE_MPU_WRAPPERS_V1 == 0 ) && ( configENABLE_ACCESS_CONTROL_LIST == 1 ) ) */
|
||||||
|
|
||||||
|
#endif /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */
|
||||||
|
|
||||||
|
#define PRIVILEGED_FUNCTION __attribute__( ( section( "privileged_functions" ) ) )
|
||||||
|
#define PRIVILEGED_DATA __attribute__( ( section( "privileged_data" ) ) )
|
||||||
|
#define FREERTOS_SYSTEM_CALL __attribute__( ( section( "freertos_system_calls" ) ) )
|
||||||
|
|
||||||
|
#else /* portUSING_MPU_WRAPPERS */
|
||||||
|
|
||||||
|
#define PRIVILEGED_FUNCTION
|
||||||
|
#define PRIVILEGED_DATA
|
||||||
|
#define FREERTOS_SYSTEM_CALL
|
||||||
|
|
||||||
|
#endif /* portUSING_MPU_WRAPPERS */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* MPU_WRAPPERS_H */
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INC_NEWLIB_FREERTOS_H
|
||||||
|
#define INC_NEWLIB_FREERTOS_H
|
||||||
|
|
||||||
|
/* Note Newlib support has been included by popular demand, but is not
|
||||||
|
* used by the FreeRTOS maintainers themselves. FreeRTOS is not
|
||||||
|
* responsible for resulting newlib operation. User must be familiar with
|
||||||
|
* newlib and must provide system-wide implementations of the necessary
|
||||||
|
* stubs. Be warned that (at the time of writing) the current newlib design
|
||||||
|
* implements a system-wide malloc() that must be provided with locks.
|
||||||
|
*
|
||||||
|
* See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
|
||||||
|
* for additional information. */
|
||||||
|
|
||||||
|
#include <reent.h>
|
||||||
|
|
||||||
|
#define configUSE_C_RUNTIME_TLS_SUPPORT 1
|
||||||
|
|
||||||
|
#ifndef configTLS_BLOCK_TYPE
|
||||||
|
#define configTLS_BLOCK_TYPE struct _reent
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configINIT_TLS_BLOCK
|
||||||
|
#define configINIT_TLS_BLOCK( xTLSBlock, pxTopOfStack ) _REENT_INIT_PTR( &( xTLSBlock ) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configSET_TLS_BLOCK
|
||||||
|
#define configSET_TLS_BLOCK( xTLSBlock ) ( _impure_ptr = &( xTLSBlock ) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configDEINIT_TLS_BLOCK
|
||||||
|
#define configDEINIT_TLS_BLOCK( xTLSBlock ) _reclaim_reent( &( xTLSBlock ) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* INC_NEWLIB_FREERTOS_H */
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INC_PICOLIBC_FREERTOS_H
|
||||||
|
#define INC_PICOLIBC_FREERTOS_H
|
||||||
|
|
||||||
|
/* Use picolibc TLS support to allocate space for __thread variables,
|
||||||
|
* initialize them at thread creation and set the TLS context at
|
||||||
|
* thread switch time.
|
||||||
|
*
|
||||||
|
* See the picolibc TLS docs:
|
||||||
|
* https://github.com/picolibc/picolibc/blob/main/doc/tls.md
|
||||||
|
* for additional information. */
|
||||||
|
|
||||||
|
#include <picotls.h>
|
||||||
|
|
||||||
|
#define configUSE_C_RUNTIME_TLS_SUPPORT 1
|
||||||
|
|
||||||
|
#define configTLS_BLOCK_TYPE void *
|
||||||
|
|
||||||
|
#define picolibcTLS_SIZE ( ( portPOINTER_SIZE_TYPE ) _tls_size() )
|
||||||
|
#define picolibcSTACK_ALIGNMENT_MASK ( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK )
|
||||||
|
|
||||||
|
#if __PICOLIBC_MAJOR__ > 1 || __PICOLIBC_MINOR__ >= 8
|
||||||
|
|
||||||
|
/* Picolibc 1.8 and newer have explicit alignment values provided
|
||||||
|
* by the _tls_align() inline */
|
||||||
|
#define picolibcTLS_ALIGNMENT_MASK ( ( portPOINTER_SIZE_TYPE ) ( _tls_align() - 1 ) )
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* For older Picolibc versions, use the general port alignment value */
|
||||||
|
#define picolibcTLS_ALIGNMENT_MASK ( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Allocate thread local storage block off the end of the
|
||||||
|
* stack. The picolibcTLS_SIZE macro returns the size (in
|
||||||
|
* bytes) of the total TLS area used by the application.
|
||||||
|
* Calculate the top of stack address. */
|
||||||
|
#if ( portSTACK_GROWTH < 0 )
|
||||||
|
|
||||||
|
#define configINIT_TLS_BLOCK( xTLSBlock, pxTopOfStack ) \
|
||||||
|
do { \
|
||||||
|
xTLSBlock = ( void * ) ( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) - \
|
||||||
|
picolibcTLS_SIZE ) & \
|
||||||
|
~picolibcTLS_ALIGNMENT_MASK ); \
|
||||||
|
pxTopOfStack = ( StackType_t * ) ( ( ( ( portPOINTER_SIZE_TYPE ) xTLSBlock ) - 1 ) & \
|
||||||
|
~picolibcSTACK_ALIGNMENT_MASK ); \
|
||||||
|
_init_tls( xTLSBlock ); \
|
||||||
|
} while( 0 )
|
||||||
|
#else /* portSTACK_GROWTH */
|
||||||
|
#define configINIT_TLS_BLOCK( xTLSBlock, pxTopOfStack ) \
|
||||||
|
do { \
|
||||||
|
xTLSBlock = ( void * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack + \
|
||||||
|
picolibcTLS_ALIGNMENT_MASK ) & ~picolibcTLS_ALIGNMENT_MASK ); \
|
||||||
|
pxTopOfStack = ( StackType_t * ) ( ( ( ( ( portPOINTER_SIZE_TYPE ) xTLSBlock ) + \
|
||||||
|
picolibcTLS_SIZE ) + picolibcSTACK_ALIGNMENT_MASK ) & \
|
||||||
|
~picolibcSTACK_ALIGNMENT_MASK ); \
|
||||||
|
_init_tls( xTLSBlock ); \
|
||||||
|
} while( 0 )
|
||||||
|
#endif /* portSTACK_GROWTH */
|
||||||
|
|
||||||
|
#define configSET_TLS_BLOCK( xTLSBlock ) _set_tls( xTLSBlock )
|
||||||
|
|
||||||
|
#define configDEINIT_TLS_BLOCK( xTLSBlock )
|
||||||
|
|
||||||
|
#endif /* INC_PICOLIBC_FREERTOS_H */
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------
|
||||||
|
* Portable layer API. Each function must be defined for each port.
|
||||||
|
*----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef PORTABLE_H
|
||||||
|
#define PORTABLE_H
|
||||||
|
|
||||||
|
/* Each FreeRTOS port has a unique portmacro.h header file. Originally a
|
||||||
|
* pre-processor definition was used to ensure the pre-processor found the correct
|
||||||
|
* portmacro.h file for the port being used. That scheme was deprecated in favour
|
||||||
|
* of setting the compiler's include path such that it found the correct
|
||||||
|
* portmacro.h file - removing the need for the constant and allowing the
|
||||||
|
* portmacro.h file to be located anywhere in relation to the port being used.
|
||||||
|
* Purely for reasons of backward compatibility the old method is still valid, but
|
||||||
|
* to make it clear that new projects should not use it, support for the port
|
||||||
|
* specific constants has been moved into the deprecated_definitions.h header
|
||||||
|
* file. */
|
||||||
|
#include "deprecated_definitions.h"
|
||||||
|
|
||||||
|
/* If portENTER_CRITICAL is not defined then including deprecated_definitions.h
|
||||||
|
* did not result in a portmacro.h header file being included - and it should be
|
||||||
|
* included here. In this case the path to the correct portmacro.h header file
|
||||||
|
* must be set in the compiler's include path. */
|
||||||
|
#ifndef portENTER_CRITICAL
|
||||||
|
#include "portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if portBYTE_ALIGNMENT == 32
|
||||||
|
#define portBYTE_ALIGNMENT_MASK ( 0x001f )
|
||||||
|
#elif portBYTE_ALIGNMENT == 16
|
||||||
|
#define portBYTE_ALIGNMENT_MASK ( 0x000f )
|
||||||
|
#elif portBYTE_ALIGNMENT == 8
|
||||||
|
#define portBYTE_ALIGNMENT_MASK ( 0x0007 )
|
||||||
|
#elif portBYTE_ALIGNMENT == 4
|
||||||
|
#define portBYTE_ALIGNMENT_MASK ( 0x0003 )
|
||||||
|
#elif portBYTE_ALIGNMENT == 2
|
||||||
|
#define portBYTE_ALIGNMENT_MASK ( 0x0001 )
|
||||||
|
#elif portBYTE_ALIGNMENT == 1
|
||||||
|
#define portBYTE_ALIGNMENT_MASK ( 0x0000 )
|
||||||
|
#else /* if portBYTE_ALIGNMENT == 32 */
|
||||||
|
#error "Invalid portBYTE_ALIGNMENT definition"
|
||||||
|
#endif /* if portBYTE_ALIGNMENT == 32 */
|
||||||
|
|
||||||
|
#ifndef portUSING_MPU_WRAPPERS
|
||||||
|
#define portUSING_MPU_WRAPPERS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef portNUM_CONFIGURABLE_REGIONS
|
||||||
|
#define portNUM_CONFIGURABLE_REGIONS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef portHAS_STACK_OVERFLOW_CHECKING
|
||||||
|
#define portHAS_STACK_OVERFLOW_CHECKING 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef portARCH_NAME
|
||||||
|
#define portARCH_NAME NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef portBASE_TYPE_ENTER_CRITICAL
|
||||||
|
#define portBASE_TYPE_ENTER_CRITICAL() taskENTER_CRITICAL()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef portBASE_TYPE_EXIT_CRITICAL
|
||||||
|
#define portBASE_TYPE_EXIT_CRITICAL() taskEXIT_CRITICAL()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configSTACK_DEPTH_TYPE
|
||||||
|
#define configSTACK_DEPTH_TYPE StackType_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configSTACK_ALLOCATION_FROM_SEPARATE_HEAP
|
||||||
|
/* Defaults to 0 for backward compatibility. */
|
||||||
|
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "mpu_wrappers.h"
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup the stack of a new task so it is ready to be placed under the
|
||||||
|
* scheduler control. The registers have to be placed on the stack in
|
||||||
|
* the order that the port expects to find them.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if ( portUSING_MPU_WRAPPERS == 1 )
|
||||||
|
#if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
|
||||||
|
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
|
||||||
|
StackType_t * pxEndOfStack,
|
||||||
|
TaskFunction_t pxCode,
|
||||||
|
void * pvParameters,
|
||||||
|
BaseType_t xRunPrivileged,
|
||||||
|
xMPU_SETTINGS * xMPUSettings ) PRIVILEGED_FUNCTION;
|
||||||
|
#else
|
||||||
|
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
|
||||||
|
TaskFunction_t pxCode,
|
||||||
|
void * pvParameters,
|
||||||
|
BaseType_t xRunPrivileged,
|
||||||
|
xMPU_SETTINGS * xMPUSettings ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif /* if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ) */
|
||||||
|
#else /* if ( portUSING_MPU_WRAPPERS == 1 ) */
|
||||||
|
#if ( portHAS_STACK_OVERFLOW_CHECKING == 1 )
|
||||||
|
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
|
||||||
|
StackType_t * pxEndOfStack,
|
||||||
|
TaskFunction_t pxCode,
|
||||||
|
void * pvParameters ) PRIVILEGED_FUNCTION;
|
||||||
|
#else
|
||||||
|
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
|
||||||
|
TaskFunction_t pxCode,
|
||||||
|
void * pvParameters ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif
|
||||||
|
#endif /* if ( portUSING_MPU_WRAPPERS == 1 ) */
|
||||||
|
|
||||||
|
/* Used by heap_5.c to define the start address and size of each memory region
|
||||||
|
* that together comprise the total FreeRTOS heap space. */
|
||||||
|
typedef struct HeapRegion
|
||||||
|
{
|
||||||
|
uint8_t * pucStartAddress;
|
||||||
|
size_t xSizeInBytes;
|
||||||
|
} HeapRegion_t;
|
||||||
|
|
||||||
|
/* Used to pass information about the heap out of vPortGetHeapStats(). */
|
||||||
|
typedef struct xHeapStats
|
||||||
|
{
|
||||||
|
size_t xAvailableHeapSpaceInBytes; /* The total heap size currently available - this is the sum of all the free blocks, not the largest block that can be allocated. */
|
||||||
|
size_t xSizeOfLargestFreeBlockInBytes; /* The maximum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */
|
||||||
|
size_t xSizeOfSmallestFreeBlockInBytes; /* The minimum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */
|
||||||
|
size_t xNumberOfFreeBlocks; /* The number of free memory blocks within the heap at the time vPortGetHeapStats() is called. */
|
||||||
|
size_t xMinimumEverFreeBytesRemaining; /* The minimum amount of total free memory (sum of all free blocks) there has been in the heap since the system booted. */
|
||||||
|
size_t xNumberOfSuccessfulAllocations; /* The number of calls to pvPortMalloc() that have returned a valid memory block. */
|
||||||
|
size_t xNumberOfSuccessfulFrees; /* The number of calls to vPortFree() that has successfully freed a block of memory. */
|
||||||
|
} HeapStats_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used to define multiple heap regions for use by heap_5.c. This function
|
||||||
|
* must be called before any calls to pvPortMalloc() - not creating a task,
|
||||||
|
* queue, semaphore, mutex, software timer, event group, etc. will result in
|
||||||
|
* pvPortMalloc being called.
|
||||||
|
*
|
||||||
|
* pxHeapRegions passes in an array of HeapRegion_t structures - each of which
|
||||||
|
* defines a region of memory that can be used as the heap. The array is
|
||||||
|
* terminated by a HeapRegions_t structure that has a size of 0. The region
|
||||||
|
* with the lowest start address must appear first in the array.
|
||||||
|
*/
|
||||||
|
void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns a HeapStats_t structure filled with information about the current
|
||||||
|
* heap state.
|
||||||
|
*/
|
||||||
|
void vPortGetHeapStats( HeapStats_t * pxHeapStats );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Map to the memory management routines required for the port.
|
||||||
|
*/
|
||||||
|
void * pvPortMalloc( size_t xWantedSize ) PRIVILEGED_FUNCTION;
|
||||||
|
void * pvPortCalloc( size_t xNum,
|
||||||
|
size_t xSize ) PRIVILEGED_FUNCTION;
|
||||||
|
void vPortFree( void * pv ) PRIVILEGED_FUNCTION;
|
||||||
|
void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION;
|
||||||
|
size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION;
|
||||||
|
size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION;
|
||||||
|
void xPortResetHeapMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
#if ( configSTACK_ALLOCATION_FROM_SEPARATE_HEAP == 1 )
|
||||||
|
void * pvPortMallocStack( size_t xSize ) PRIVILEGED_FUNCTION;
|
||||||
|
void vPortFreeStack( void * pv ) PRIVILEGED_FUNCTION;
|
||||||
|
#else
|
||||||
|
#define pvPortMallocStack pvPortMalloc
|
||||||
|
#define vPortFreeStack vPortFree
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function resets the internal state of the heap module. It must be called
|
||||||
|
* by the application before restarting the scheduler.
|
||||||
|
*/
|
||||||
|
void vPortHeapResetState( void ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* task.h
|
||||||
|
* @code{c}
|
||||||
|
* void vApplicationMallocFailedHook( void )
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* This hook function is called when allocation failed.
|
||||||
|
*/
|
||||||
|
void vApplicationMallocFailedHook( void );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup the hardware ready for the scheduler to take control. This generally
|
||||||
|
* sets up a tick interrupt and sets timers for the correct tick frequency.
|
||||||
|
*/
|
||||||
|
BaseType_t xPortStartScheduler( void ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Undo any hardware/ISR setup that was performed by xPortStartScheduler() so
|
||||||
|
* the hardware is left in its original condition after the scheduler stops
|
||||||
|
* executing.
|
||||||
|
*/
|
||||||
|
void vPortEndScheduler( void ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The structures and methods of manipulating the MPU are contained within the
|
||||||
|
* port layer.
|
||||||
|
*
|
||||||
|
* Fills the xMPUSettings structure with the memory region information
|
||||||
|
* contained in xRegions.
|
||||||
|
*/
|
||||||
|
#if ( portUSING_MPU_WRAPPERS == 1 )
|
||||||
|
struct xMEMORY_REGION;
|
||||||
|
void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
|
||||||
|
const struct xMEMORY_REGION * const xRegions,
|
||||||
|
StackType_t * pxBottomOfStack,
|
||||||
|
configSTACK_DEPTH_TYPE uxStackDepth ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the calling task is authorized to access the given buffer.
|
||||||
|
*
|
||||||
|
* @param pvBuffer The buffer which the calling task wants to access.
|
||||||
|
* @param ulBufferLength The length of the pvBuffer.
|
||||||
|
* @param ulAccessRequested The permissions that the calling task wants.
|
||||||
|
*
|
||||||
|
* @return pdTRUE if the calling task is authorized to access the buffer,
|
||||||
|
* pdFALSE otherwise.
|
||||||
|
*/
|
||||||
|
#if ( portUSING_MPU_WRAPPERS == 1 )
|
||||||
|
BaseType_t xPortIsAuthorizedToAccessBuffer( const void * pvBuffer,
|
||||||
|
uint32_t ulBufferLength,
|
||||||
|
uint32_t ulAccessRequested ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the calling task is authorized to access the given kernel object.
|
||||||
|
*
|
||||||
|
* @param lInternalIndexOfKernelObject The index of the kernel object in the kernel
|
||||||
|
* object handle pool.
|
||||||
|
*
|
||||||
|
* @return pdTRUE if the calling task is authorized to access the kernel object,
|
||||||
|
* pdFALSE otherwise.
|
||||||
|
*/
|
||||||
|
#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) )
|
||||||
|
|
||||||
|
BaseType_t xPortIsAuthorizedToAccessKernelObject( int32_t lInternalIndexOfKernelObject ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
#endif /* PORTABLE_H */
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PROJDEFS_H
|
||||||
|
#define PROJDEFS_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Defines the prototype to which task functions must conform. Defined in this
|
||||||
|
* file to ensure the type is known before portable.h is included.
|
||||||
|
*/
|
||||||
|
typedef void (* TaskFunction_t)( void * arg );
|
||||||
|
|
||||||
|
/* Converts a time in milliseconds to a time in ticks. This macro can be
|
||||||
|
* overridden by a macro of the same name defined in FreeRTOSConfig.h in case the
|
||||||
|
* definition here is not suitable for your application. */
|
||||||
|
#ifndef pdMS_TO_TICKS
|
||||||
|
#define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( ( ( uint64_t ) ( xTimeInMs ) * ( uint64_t ) configTICK_RATE_HZ ) / ( uint64_t ) 1000U ) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Converts a time in ticks to a time in milliseconds. This macro can be
|
||||||
|
* overridden by a macro of the same name defined in FreeRTOSConfig.h in case the
|
||||||
|
* definition here is not suitable for your application. */
|
||||||
|
#ifndef pdTICKS_TO_MS
|
||||||
|
#define pdTICKS_TO_MS( xTimeInTicks ) ( ( TickType_t ) ( ( ( uint64_t ) ( xTimeInTicks ) * ( uint64_t ) 1000U ) / ( uint64_t ) configTICK_RATE_HZ ) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define pdFALSE ( ( BaseType_t ) 0 )
|
||||||
|
#define pdTRUE ( ( BaseType_t ) 1 )
|
||||||
|
#define pdFALSE_SIGNED ( ( BaseType_t ) 0 )
|
||||||
|
#define pdTRUE_SIGNED ( ( BaseType_t ) 1 )
|
||||||
|
#define pdFALSE_UNSIGNED ( ( UBaseType_t ) 0 )
|
||||||
|
#define pdTRUE_UNSIGNED ( ( UBaseType_t ) 1 )
|
||||||
|
|
||||||
|
#define pdPASS ( pdTRUE )
|
||||||
|
#define pdFAIL ( pdFALSE )
|
||||||
|
#define errQUEUE_EMPTY ( ( BaseType_t ) 0 )
|
||||||
|
#define errQUEUE_FULL ( ( BaseType_t ) 0 )
|
||||||
|
|
||||||
|
/* FreeRTOS error definitions. */
|
||||||
|
#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 )
|
||||||
|
#define errQUEUE_BLOCKED ( -4 )
|
||||||
|
#define errQUEUE_YIELD ( -5 )
|
||||||
|
|
||||||
|
/* Macros used for basic data corruption checks. */
|
||||||
|
#ifndef configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES
|
||||||
|
#define configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS )
|
||||||
|
#define pdINTEGRITY_CHECK_VALUE 0x5a5a
|
||||||
|
#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS )
|
||||||
|
#define pdINTEGRITY_CHECK_VALUE 0x5a5a5a5aUL
|
||||||
|
#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_64_BITS )
|
||||||
|
#define pdINTEGRITY_CHECK_VALUE 0x5a5a5a5a5a5a5a5aULL
|
||||||
|
#else
|
||||||
|
#error configTICK_TYPE_WIDTH_IN_BITS set to unsupported tick type width.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The following errno values are used by FreeRTOS+ components, not FreeRTOS
|
||||||
|
* itself. */
|
||||||
|
#define pdFREERTOS_ERRNO_NONE 0 /* No errors */
|
||||||
|
#define pdFREERTOS_ERRNO_ENOENT 2 /* No such file or directory */
|
||||||
|
#define pdFREERTOS_ERRNO_EINTR 4 /* Interrupted system call */
|
||||||
|
#define pdFREERTOS_ERRNO_EIO 5 /* I/O error */
|
||||||
|
#define pdFREERTOS_ERRNO_ENXIO 6 /* No such device or address */
|
||||||
|
#define pdFREERTOS_ERRNO_EBADF 9 /* Bad file number */
|
||||||
|
#define pdFREERTOS_ERRNO_EAGAIN 11 /* No more processes */
|
||||||
|
#define pdFREERTOS_ERRNO_EWOULDBLOCK 11 /* Operation would block */
|
||||||
|
#define pdFREERTOS_ERRNO_ENOMEM 12 /* Not enough memory */
|
||||||
|
#define pdFREERTOS_ERRNO_EACCES 13 /* Permission denied */
|
||||||
|
#define pdFREERTOS_ERRNO_EFAULT 14 /* Bad address */
|
||||||
|
#define pdFREERTOS_ERRNO_EBUSY 16 /* Mount device busy */
|
||||||
|
#define pdFREERTOS_ERRNO_EEXIST 17 /* File exists */
|
||||||
|
#define pdFREERTOS_ERRNO_EXDEV 18 /* Cross-device link */
|
||||||
|
#define pdFREERTOS_ERRNO_ENODEV 19 /* No such device */
|
||||||
|
#define pdFREERTOS_ERRNO_ENOTDIR 20 /* Not a directory */
|
||||||
|
#define pdFREERTOS_ERRNO_EISDIR 21 /* Is a directory */
|
||||||
|
#define pdFREERTOS_ERRNO_EINVAL 22 /* Invalid argument */
|
||||||
|
#define pdFREERTOS_ERRNO_ENOSPC 28 /* No space left on device */
|
||||||
|
#define pdFREERTOS_ERRNO_ESPIPE 29 /* Illegal seek */
|
||||||
|
#define pdFREERTOS_ERRNO_EROFS 30 /* Read only file system */
|
||||||
|
#define pdFREERTOS_ERRNO_EUNATCH 42 /* Protocol driver not attached */
|
||||||
|
#define pdFREERTOS_ERRNO_EBADE 50 /* Invalid exchange */
|
||||||
|
#define pdFREERTOS_ERRNO_EFTYPE 79 /* Inappropriate file type or format */
|
||||||
|
#define pdFREERTOS_ERRNO_ENMFILE 89 /* No more files */
|
||||||
|
#define pdFREERTOS_ERRNO_ENOTEMPTY 90 /* Directory not empty */
|
||||||
|
#define pdFREERTOS_ERRNO_ENAMETOOLONG 91 /* File or path name too long */
|
||||||
|
#define pdFREERTOS_ERRNO_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
|
||||||
|
#define pdFREERTOS_ERRNO_EAFNOSUPPORT 97 /* Address family not supported by protocol */
|
||||||
|
#define pdFREERTOS_ERRNO_ENOBUFS 105 /* No buffer space available */
|
||||||
|
#define pdFREERTOS_ERRNO_ENOPROTOOPT 109 /* Protocol not available */
|
||||||
|
#define pdFREERTOS_ERRNO_EADDRINUSE 112 /* Address already in use */
|
||||||
|
#define pdFREERTOS_ERRNO_ETIMEDOUT 116 /* Connection timed out */
|
||||||
|
#define pdFREERTOS_ERRNO_EINPROGRESS 119 /* Connection already in progress */
|
||||||
|
#define pdFREERTOS_ERRNO_EALREADY 120 /* Socket already connected */
|
||||||
|
#define pdFREERTOS_ERRNO_EADDRNOTAVAIL 125 /* Address not available */
|
||||||
|
#define pdFREERTOS_ERRNO_EISCONN 127 /* Socket is already connected */
|
||||||
|
#define pdFREERTOS_ERRNO_ENOTCONN 128 /* Socket is not connected */
|
||||||
|
#define pdFREERTOS_ERRNO_ENOMEDIUM 135 /* No medium inserted */
|
||||||
|
#define pdFREERTOS_ERRNO_EILSEQ 138 /* An invalid UTF-16 sequence was encountered. */
|
||||||
|
#define pdFREERTOS_ERRNO_ECANCELED 140 /* Operation canceled. */
|
||||||
|
|
||||||
|
/* The following endian values are used by FreeRTOS+ components, not FreeRTOS
|
||||||
|
* itself. */
|
||||||
|
#define pdFREERTOS_LITTLE_ENDIAN 0
|
||||||
|
#define pdFREERTOS_BIG_ENDIAN 1
|
||||||
|
|
||||||
|
/* Re-defining endian values for generic naming. */
|
||||||
|
#define pdLITTLE_ENDIAN pdFREERTOS_LITTLE_ENDIAN
|
||||||
|
#define pdBIG_ENDIAN pdFREERTOS_BIG_ENDIAN
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* PROJDEFS_H */
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STACK_MACROS_H
|
||||||
|
#define STACK_MACROS_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call the stack overflow hook function if the stack of the task being swapped
|
||||||
|
* out is currently overflowed, or looks like it might have overflowed in the
|
||||||
|
* past.
|
||||||
|
*
|
||||||
|
* Setting configCHECK_FOR_STACK_OVERFLOW to 1 will cause the macro to check
|
||||||
|
* the current stack state only - comparing the current top of stack value to
|
||||||
|
* the stack limit. Setting configCHECK_FOR_STACK_OVERFLOW to greater than 1
|
||||||
|
* will also cause the last few stack bytes to be checked to ensure the value
|
||||||
|
* to which the bytes were set when the task was created have not been
|
||||||
|
* overwritten. Note this second test does not guarantee that an overflowed
|
||||||
|
* stack will always be recognised.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* portSTACK_LIMIT_PADDING is a number of extra words to consider to be in
|
||||||
|
* use on the stack.
|
||||||
|
*/
|
||||||
|
#ifndef portSTACK_LIMIT_PADDING
|
||||||
|
#define portSTACK_LIMIT_PADDING 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Stack overflow check is not straight forward to implement for MPU ports
|
||||||
|
* because of the following reasons:
|
||||||
|
* 1. The context is stored in TCB and as a result, pxTopOfStack member points
|
||||||
|
* to the context location in TCB.
|
||||||
|
* 2. System calls are executed on a separate privileged only stack.
|
||||||
|
*
|
||||||
|
* It is still okay because an MPU region is used to protect task stack which
|
||||||
|
* means task stack overflow will trigger an MPU fault for unprivileged tasks.
|
||||||
|
* Additionally, architectures with hardware stack overflow checking support
|
||||||
|
* (such as Armv8-M) will trigger a fault when a task's stack overflows.
|
||||||
|
*/
|
||||||
|
#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) && ( portUSING_MPU_WRAPPERS != 1 ) )
|
||||||
|
|
||||||
|
/* Only the current stack state is to be checked. */
|
||||||
|
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
/* Is the currently saved stack pointer within the stack limit? */ \
|
||||||
|
if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack + portSTACK_LIMIT_PADDING ) \
|
||||||
|
{ \
|
||||||
|
char * pcOverflowTaskName = pxCurrentTCB->pcTaskName; \
|
||||||
|
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pcOverflowTaskName ); \
|
||||||
|
} \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH > 0 ) && ( portUSING_MPU_WRAPPERS != 1 ) )
|
||||||
|
|
||||||
|
/* Only the current stack state is to be checked. */
|
||||||
|
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
/* Is the currently saved stack pointer within the stack limit? */ \
|
||||||
|
if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack - portSTACK_LIMIT_PADDING ) \
|
||||||
|
{ \
|
||||||
|
char * pcOverflowTaskName = pxCurrentTCB->pcTaskName; \
|
||||||
|
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pcOverflowTaskName ); \
|
||||||
|
} \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) && ( portUSING_MPU_WRAPPERS != 1 ) )
|
||||||
|
|
||||||
|
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack; \
|
||||||
|
const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5U; \
|
||||||
|
\
|
||||||
|
if( ( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack + portSTACK_LIMIT_PADDING ) || \
|
||||||
|
( pulStack[ 0 ] != ulCheckValue ) || \
|
||||||
|
( pulStack[ 1 ] != ulCheckValue ) || \
|
||||||
|
( pulStack[ 2 ] != ulCheckValue ) || \
|
||||||
|
( pulStack[ 3 ] != ulCheckValue ) ) \
|
||||||
|
{ \
|
||||||
|
char * pcOverflowTaskName = pxCurrentTCB->pcTaskName; \
|
||||||
|
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pcOverflowTaskName ); \
|
||||||
|
} \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) && ( portUSING_MPU_WRAPPERS != 1 ) )
|
||||||
|
|
||||||
|
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
int8_t * pcEndOfStack = ( int8_t * ) pxCurrentTCB->pxEndOfStack; \
|
||||||
|
static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||||
|
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||||
|
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||||
|
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||||
|
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \
|
||||||
|
\
|
||||||
|
pcEndOfStack -= sizeof( ucExpectedStackBytes ); \
|
||||||
|
\
|
||||||
|
if( ( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack - portSTACK_LIMIT_PADDING ) || \
|
||||||
|
( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) ) \
|
||||||
|
{ \
|
||||||
|
char * pcOverflowTaskName = pxCurrentTCB->pcTaskName; \
|
||||||
|
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pcOverflowTaskName ); \
|
||||||
|
} \
|
||||||
|
} while( 0 )
|
||||||
|
|
||||||
|
#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Remove stack overflow macro if not being used. */
|
||||||
|
#ifndef taskCHECK_FOR_STACK_OVERFLOW
|
||||||
|
#define taskCHECK_FOR_STACK_OVERFLOW()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* STACK_MACROS_H */
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
|
||||||
|
* all the API functions to use the MPU wrappers. That should only be done when
|
||||||
|
* task.h is included from an application file. */
|
||||||
|
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
/* The MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be
|
||||||
|
* defined for the header files above, but not in this file, in order to
|
||||||
|
* generate the correct privileged Vs unprivileged linkage and placement. */
|
||||||
|
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------
|
||||||
|
* PUBLIC LIST API documented in list.h
|
||||||
|
*----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vListInitialise( List_t * const pxList )
|
||||||
|
{
|
||||||
|
traceENTER_vListInitialise( pxList );
|
||||||
|
|
||||||
|
/* The list structure contains a list item which is used to mark the
|
||||||
|
* end of the list. To initialise the list the list end is inserted
|
||||||
|
* as the only list entry. */
|
||||||
|
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
|
||||||
|
|
||||||
|
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
|
||||||
|
|
||||||
|
/* The list end value is the highest possible value in the list to
|
||||||
|
* ensure it remains at the end of the list. */
|
||||||
|
pxList->xListEnd.xItemValue = portMAX_DELAY;
|
||||||
|
|
||||||
|
/* The list end next and previous pointers point to itself so we know
|
||||||
|
* when the list is empty. */
|
||||||
|
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
|
||||||
|
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
|
||||||
|
|
||||||
|
/* Initialize the remaining fields of xListEnd when it is a proper ListItem_t */
|
||||||
|
#if ( configUSE_MINI_LIST_ITEM == 0 )
|
||||||
|
{
|
||||||
|
pxList->xListEnd.pvOwner = NULL;
|
||||||
|
pxList->xListEnd.pxContainer = NULL;
|
||||||
|
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( &( pxList->xListEnd ) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
|
||||||
|
|
||||||
|
/* Write known values into the list if
|
||||||
|
* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
|
||||||
|
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
|
||||||
|
|
||||||
|
traceRETURN_vListInitialise();
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vListInitialiseItem( ListItem_t * const pxItem )
|
||||||
|
{
|
||||||
|
traceENTER_vListInitialiseItem( pxItem );
|
||||||
|
|
||||||
|
/* Make sure the list item is not recorded as being on a list. */
|
||||||
|
pxItem->pxContainer = NULL;
|
||||||
|
|
||||||
|
/* Write known values into the list item if
|
||||||
|
* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
|
||||||
|
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
|
||||||
|
|
||||||
|
traceRETURN_vListInitialiseItem();
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vListInsertEnd( List_t * const pxList,
|
||||||
|
ListItem_t * const pxNewListItem )
|
||||||
|
{
|
||||||
|
ListItem_t * const pxIndex = pxList->pxIndex;
|
||||||
|
|
||||||
|
traceENTER_vListInsertEnd( pxList, pxNewListItem );
|
||||||
|
|
||||||
|
/* Only effective when configASSERT() is also defined, these tests may catch
|
||||||
|
* the list data structures being overwritten in memory. They will not catch
|
||||||
|
* data errors caused by incorrect configuration or use of FreeRTOS. */
|
||||||
|
listTEST_LIST_INTEGRITY( pxList );
|
||||||
|
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
|
||||||
|
|
||||||
|
/* Insert a new list item into pxList, but rather than sort the list,
|
||||||
|
* makes the new list item the last item to be removed by a call to
|
||||||
|
* listGET_OWNER_OF_NEXT_ENTRY(). */
|
||||||
|
pxNewListItem->pxNext = pxIndex;
|
||||||
|
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
|
||||||
|
|
||||||
|
/* Only used during decision coverage testing. */
|
||||||
|
mtCOVERAGE_TEST_DELAY();
|
||||||
|
|
||||||
|
pxIndex->pxPrevious->pxNext = pxNewListItem;
|
||||||
|
pxIndex->pxPrevious = pxNewListItem;
|
||||||
|
|
||||||
|
/* Remember which list the item is in. */
|
||||||
|
pxNewListItem->pxContainer = pxList;
|
||||||
|
|
||||||
|
( pxList->uxNumberOfItems ) = ( UBaseType_t ) ( pxList->uxNumberOfItems + 1U );
|
||||||
|
|
||||||
|
traceRETURN_vListInsertEnd();
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vListInsert( List_t * const pxList,
|
||||||
|
ListItem_t * const pxNewListItem )
|
||||||
|
{
|
||||||
|
ListItem_t * pxIterator;
|
||||||
|
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
|
||||||
|
|
||||||
|
traceENTER_vListInsert( pxList, pxNewListItem );
|
||||||
|
|
||||||
|
/* Only effective when configASSERT() is also defined, these tests may catch
|
||||||
|
* the list data structures being overwritten in memory. They will not catch
|
||||||
|
* data errors caused by incorrect configuration or use of FreeRTOS. */
|
||||||
|
listTEST_LIST_INTEGRITY( pxList );
|
||||||
|
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
|
||||||
|
|
||||||
|
/* Insert the new list item into the list, sorted in xItemValue order.
|
||||||
|
*
|
||||||
|
* If the list already contains a list item with the same item value then the
|
||||||
|
* new list item should be placed after it. This ensures that TCBs which are
|
||||||
|
* stored in ready lists (all of which have the same xItemValue value) get a
|
||||||
|
* share of the CPU. However, if the xItemValue is the same as the back marker
|
||||||
|
* the iteration loop below will not end. Therefore the value is checked
|
||||||
|
* first, and the algorithm slightly modified if necessary. */
|
||||||
|
if( xValueOfInsertion == portMAX_DELAY )
|
||||||
|
{
|
||||||
|
pxIterator = pxList->xListEnd.pxPrevious;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* *** NOTE ***********************************************************
|
||||||
|
* If you find your application is crashing here then likely causes are
|
||||||
|
* listed below. In addition see https://www.freertos.org/Why-FreeRTOS/FAQs for
|
||||||
|
* more tips, and ensure configASSERT() is defined!
|
||||||
|
* https://www.FreeRTOS.org/a00110.html#configASSERT
|
||||||
|
*
|
||||||
|
* 1) Stack overflow -
|
||||||
|
* see https://www.FreeRTOS.org/Stacks-and-stack-overflow-checking.html
|
||||||
|
* 2) Incorrect interrupt priority assignment, especially on Cortex-M
|
||||||
|
* parts where numerically high priority values denote low actual
|
||||||
|
* interrupt priorities, which can seem counter intuitive. See
|
||||||
|
* https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html and the definition
|
||||||
|
* of configMAX_SYSCALL_INTERRUPT_PRIORITY on
|
||||||
|
* https://www.FreeRTOS.org/a00110.html
|
||||||
|
* 3) Calling an API function from within a critical section or when
|
||||||
|
* the scheduler is suspended, or calling an API function that does
|
||||||
|
* not end in "FromISR" from an interrupt.
|
||||||
|
* 4) Using a queue or semaphore before it has been initialised or
|
||||||
|
* before the scheduler has been started (are interrupts firing
|
||||||
|
* before vTaskStartScheduler() has been called?).
|
||||||
|
* 5) If the FreeRTOS port supports interrupt nesting then ensure that
|
||||||
|
* the priority of the tick interrupt is at or below
|
||||||
|
* configMAX_SYSCALL_INTERRUPT_PRIORITY.
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext )
|
||||||
|
{
|
||||||
|
/* There is nothing to do here, just iterating to the wanted
|
||||||
|
* insertion position.
|
||||||
|
* IF YOU FIND YOUR CODE STUCK HERE, SEE THE NOTE JUST ABOVE.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pxNewListItem->pxNext = pxIterator->pxNext;
|
||||||
|
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
|
||||||
|
pxNewListItem->pxPrevious = pxIterator;
|
||||||
|
pxIterator->pxNext = pxNewListItem;
|
||||||
|
|
||||||
|
/* Remember which list the item is in. This allows fast removal of the
|
||||||
|
* item later. */
|
||||||
|
pxNewListItem->pxContainer = pxList;
|
||||||
|
|
||||||
|
( pxList->uxNumberOfItems ) = ( UBaseType_t ) ( pxList->uxNumberOfItems + 1U );
|
||||||
|
|
||||||
|
traceRETURN_vListInsert();
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
|
||||||
|
{
|
||||||
|
/* The list item knows which list it is in. Obtain the list from the list
|
||||||
|
* item. */
|
||||||
|
List_t * const pxList = pxItemToRemove->pxContainer;
|
||||||
|
|
||||||
|
traceENTER_uxListRemove( pxItemToRemove );
|
||||||
|
|
||||||
|
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
|
||||||
|
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
|
||||||
|
|
||||||
|
/* Only used during decision coverage testing. */
|
||||||
|
mtCOVERAGE_TEST_DELAY();
|
||||||
|
|
||||||
|
/* Make sure the index is left pointing to a valid item. */
|
||||||
|
if( pxList->pxIndex == pxItemToRemove )
|
||||||
|
{
|
||||||
|
pxList->pxIndex = pxItemToRemove->pxPrevious;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
pxItemToRemove->pxContainer = NULL;
|
||||||
|
( pxList->uxNumberOfItems ) = ( UBaseType_t ) ( pxList->uxNumberOfItems - 1U );
|
||||||
|
|
||||||
|
traceRETURN_uxListRemove( pxList->uxNumberOfItems );
|
||||||
|
|
||||||
|
return pxList->uxNumberOfItems;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------
|
||||||
|
* Implementation of functions defined in portable.h for the RISC-V port.
|
||||||
|
*----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Scheduler includes. */
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "portmacro.h"
|
||||||
|
|
||||||
|
/* Standard includes. */
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#ifdef configCLINT_BASE_ADDRESS
|
||||||
|
#warning "The configCLINT_BASE_ADDRESS constant has been deprecated. configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS are currently being derived from the (possibly 0) configCLINT_BASE_ADDRESS setting. Please update to define configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS directly in place of configCLINT_BASE_ADDRESS. See www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configMTIME_BASE_ADDRESS
|
||||||
|
#warning "configMTIME_BASE_ADDRESS must be defined in FreeRTOSConfig.h. If the target chip includes a memory-mapped mtime register then set configMTIME_BASE_ADDRESS to the mapped address. Otherwise set configMTIME_BASE_ADDRESS to 0. See www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configMTIMECMP_BASE_ADDRESS
|
||||||
|
#warning "configMTIMECMP_BASE_ADDRESS must be defined in FreeRTOSConfig.h. If the target chip includes a memory-mapped mtimecmp register then set configMTIMECMP_BASE_ADDRESS to the mapped address. Otherwise set configMTIMECMP_BASE_ADDRESS to 0. See www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Let the user override the pre-loading of the initial RA. */
|
||||||
|
#ifdef configTASK_RETURN_ADDRESS
|
||||||
|
#define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS
|
||||||
|
#else
|
||||||
|
#define portTASK_RETURN_ADDRESS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The stack used by interrupt service routines. Set configISR_STACK_SIZE_WORDS
|
||||||
|
* to use a statically allocated array as the interrupt stack. Alternative leave
|
||||||
|
* configISR_STACK_SIZE_WORDS undefined and update the linker script so that a
|
||||||
|
* linker variable names __freertos_irq_stack_top has the same value as the top
|
||||||
|
* of the stack used by main. Using the linker script method will repurpose the
|
||||||
|
* stack that was used by main before the scheduler was started for use as the
|
||||||
|
* interrupt stack after the scheduler has started. */
|
||||||
|
#ifdef configISR_STACK_SIZE_WORDS
|
||||||
|
static __attribute__( ( aligned( 16 ) ) ) StackType_t xISRStack[ configISR_STACK_SIZE_WORDS ] = { 0 };
|
||||||
|
const StackType_t xISRStackTop = ( StackType_t ) &( xISRStack[ configISR_STACK_SIZE_WORDS & ~portBYTE_ALIGNMENT_MASK ] );
|
||||||
|
|
||||||
|
/* Don't use 0xa5 as the stack fill bytes as that is used by the kernel for
|
||||||
|
* the task stacks, and so will legitimately appear in many positions within
|
||||||
|
* the ISR stack. */
|
||||||
|
#define portISR_STACK_FILL_BYTE 0xee
|
||||||
|
#else
|
||||||
|
extern const uint32_t __freertos_irq_stack_top[];
|
||||||
|
const StackType_t xISRStackTop = ( StackType_t ) __freertos_irq_stack_top;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup the timer to generate the tick interrupts. The implementation in this
|
||||||
|
* file is weak to allow application writers to change the timer used to
|
||||||
|
* generate the tick interrupt.
|
||||||
|
*/
|
||||||
|
void vPortSetupTimerInterrupt( void ) __attribute__( ( weak ) );
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Used to program the machine timer compare register. */
|
||||||
|
uint64_t ullNextTime = 0ULL;
|
||||||
|
const uint64_t * pullNextTime = &ullNextTime;
|
||||||
|
const size_t uxTimerIncrementsForOneTick = ( size_t ) ( ( configCPU_CLOCK_HZ ) / ( configTICK_RATE_HZ ) ); /* Assumes increment won't go over 32-bits. */
|
||||||
|
UBaseType_t const ullMachineTimerCompareRegisterBase = configMTIMECMP_BASE_ADDRESS;
|
||||||
|
volatile uint64_t * pullMachineTimerCompareRegister = NULL;
|
||||||
|
|
||||||
|
/* Holds the critical nesting value - deliberately non-zero at start up to
|
||||||
|
* ensure interrupts are not accidentally enabled before the scheduler starts. */
|
||||||
|
size_t xCriticalNesting = ( size_t ) 0xaaaaaaaa;
|
||||||
|
size_t * pxCriticalNesting = &xCriticalNesting;
|
||||||
|
|
||||||
|
/* Used to catch tasks that attempt to return from their implementing function. */
|
||||||
|
size_t xTaskReturnAddress = ( size_t ) portTASK_RETURN_ADDRESS;
|
||||||
|
|
||||||
|
/* Set configCHECK_FOR_STACK_OVERFLOW to 3 to add ISR stack checking to task
|
||||||
|
* stack checking. A problem in the ISR stack will trigger an assert, not call
|
||||||
|
* the stack overflow hook function (because the stack overflow hook is specific
|
||||||
|
* to a task stack, not the ISR stack). */
|
||||||
|
#if defined( configISR_STACK_SIZE_WORDS ) && ( configCHECK_FOR_STACK_OVERFLOW > 2 )
|
||||||
|
#warning "This path not tested, or even compiled yet."
|
||||||
|
|
||||||
|
static const uint8_t ucExpectedStackBytes[] =
|
||||||
|
{
|
||||||
|
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
|
||||||
|
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
|
||||||
|
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
|
||||||
|
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
|
||||||
|
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE
|
||||||
|
}; \
|
||||||
|
|
||||||
|
#define portCHECK_ISR_STACK() configASSERT( ( memcmp( ( void * ) xISRStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) == 0 ) )
|
||||||
|
#else /* if defined( configISR_STACK_SIZE_WORDS ) && ( configCHECK_FOR_STACK_OVERFLOW > 2 ) */
|
||||||
|
/* Define the function away. */
|
||||||
|
#define portCHECK_ISR_STACK()
|
||||||
|
#endif /* configCHECK_FOR_STACK_OVERFLOW > 2 */
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 )
|
||||||
|
|
||||||
|
void vPortSetupTimerInterrupt( void )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrentTimeHigh, ulCurrentTimeLow;
|
||||||
|
volatile uint32_t * const pulTimeHigh = ( volatile uint32_t * const ) ( ( configMTIME_BASE_ADDRESS ) + 4UL ); /* 8-byte type so high 32-bit word is 4 bytes up. */
|
||||||
|
volatile uint32_t * const pulTimeLow = ( volatile uint32_t * const ) ( configMTIME_BASE_ADDRESS );
|
||||||
|
volatile uint32_t ulHartId;
|
||||||
|
|
||||||
|
__asm volatile ( "csrr %0, mhartid" : "=r" ( ulHartId ) );
|
||||||
|
|
||||||
|
pullMachineTimerCompareRegister = ( volatile uint64_t * ) ( ullMachineTimerCompareRegisterBase + ( ulHartId * sizeof( uint64_t ) ) );
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ulCurrentTimeHigh = *pulTimeHigh;
|
||||||
|
ulCurrentTimeLow = *pulTimeLow;
|
||||||
|
} while( ulCurrentTimeHigh != *pulTimeHigh );
|
||||||
|
|
||||||
|
ullNextTime = ( uint64_t ) ulCurrentTimeHigh;
|
||||||
|
ullNextTime <<= 32ULL; /* High 4-byte word is 32-bits up. */
|
||||||
|
ullNextTime |= ( uint64_t ) ulCurrentTimeLow;
|
||||||
|
ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick;
|
||||||
|
*pullMachineTimerCompareRegister = ullNextTime;
|
||||||
|
|
||||||
|
/* Prepare the time to use after the next tick interrupt. */
|
||||||
|
ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIME_BASE_ADDRESS != 0 ) */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
BaseType_t xPortStartScheduler( void )
|
||||||
|
{
|
||||||
|
extern void xPortStartFirstTask( void );
|
||||||
|
|
||||||
|
#if ( configASSERT_DEFINED == 1 )
|
||||||
|
{
|
||||||
|
/* Check alignment of the interrupt stack - which is the same as the
|
||||||
|
* stack that was being used by main() prior to the scheduler being
|
||||||
|
* started. */
|
||||||
|
configASSERT( ( xISRStackTop & portBYTE_ALIGNMENT_MASK ) == 0 );
|
||||||
|
|
||||||
|
#ifdef configISR_STACK_SIZE_WORDS
|
||||||
|
{
|
||||||
|
memset( ( void * ) xISRStack, portISR_STACK_FILL_BYTE, sizeof( xISRStack ) );
|
||||||
|
}
|
||||||
|
#endif /* configISR_STACK_SIZE_WORDS */
|
||||||
|
}
|
||||||
|
#endif /* configASSERT_DEFINED */
|
||||||
|
|
||||||
|
/* If there is a CLINT then it is ok to use the default implementation
|
||||||
|
* in this file, otherwise vPortSetupTimerInterrupt() must be implemented to
|
||||||
|
* configure whichever clock is to be used to generate the tick interrupt. */
|
||||||
|
vPortSetupTimerInterrupt();
|
||||||
|
|
||||||
|
#if ( ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) )
|
||||||
|
{
|
||||||
|
/* Enable mtime and external interrupts. 1<<7 for timer interrupt,
|
||||||
|
* 1<<11 for external interrupt. _RB_ What happens here when mtime is
|
||||||
|
* not present as with pulpino? */
|
||||||
|
__asm volatile ( "csrs mie, %0" ::"r" ( 0x880 ) );
|
||||||
|
}
|
||||||
|
#endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) */
|
||||||
|
|
||||||
|
xPortStartFirstTask();
|
||||||
|
|
||||||
|
/* Should not get here as after calling xPortStartFirstTask() only tasks
|
||||||
|
* should be executing. */
|
||||||
|
return pdFAIL;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vPortEndScheduler( void )
|
||||||
|
{
|
||||||
|
/* Not implemented. */
|
||||||
|
for( ; ; )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
@@ -0,0 +1,406 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The FreeRTOS kernel's RISC-V port is split between the the code that is
|
||||||
|
* common across all currently supported RISC-V chips (implementations of the
|
||||||
|
* RISC-V ISA), and code which tailors the port to a specific RISC-V chip:
|
||||||
|
*
|
||||||
|
* + The code that is common to all RISC-V chips is implemented in
|
||||||
|
* FreeRTOS\Source\portable\GCC\RISC-V\portASM.S. There is only one
|
||||||
|
* portASM.S file because the same file is used no matter which RISC-V chip is
|
||||||
|
* in use.
|
||||||
|
*
|
||||||
|
* + The code that tailors the kernel's RISC-V port to a specific RISC-V
|
||||||
|
* chip is implemented in freertos_risc_v_chip_specific_extensions.h. There
|
||||||
|
* is one freertos_risc_v_chip_specific_extensions.h that can be used with any
|
||||||
|
* RISC-V chip that both includes a standard CLINT and does not add to the
|
||||||
|
* base set of RISC-V registers. There are additional
|
||||||
|
* freertos_risc_v_chip_specific_extensions.h files for RISC-V implementations
|
||||||
|
* that do not include a standard CLINT or do add to the base set of RISC-V
|
||||||
|
* registers.
|
||||||
|
*
|
||||||
|
* CARE MUST BE TAKEN TO INCLDUE THE CORRECT
|
||||||
|
* freertos_risc_v_chip_specific_extensions.h HEADER FILE FOR THE CHIP
|
||||||
|
* IN USE. To include the correct freertos_risc_v_chip_specific_extensions.h
|
||||||
|
* header file ensure the path to the correct header file is in the assembler's
|
||||||
|
* include path.
|
||||||
|
*
|
||||||
|
* This freertos_risc_v_chip_specific_extensions.h is for use on RISC-V chips
|
||||||
|
* that include a standard CLINT and do not add to the base set of RISC-V
|
||||||
|
* registers.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "portContext.h"
|
||||||
|
|
||||||
|
/* Check the freertos_risc_v_chip_specific_extensions.h and/or command line
|
||||||
|
definitions. */
|
||||||
|
#if defined( portasmHAS_CLINT ) && defined( portasmHAS_MTIME )
|
||||||
|
#error The portasmHAS_CLINT constant has been deprecated. Please replace it with portasmHAS_MTIME. portasmHAS_CLINT and portasmHAS_MTIME cannot both be defined at once. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef portasmHAS_CLINT
|
||||||
|
#warning The portasmHAS_CLINT constant has been deprecated. Please replace it with portasmHAS_MTIME and portasmHAS_SIFIVE_CLINT. For now portasmHAS_MTIME and portasmHAS_SIFIVE_CLINT are derived from portasmHAS_CLINT. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
|
||||||
|
#define portasmHAS_MTIME portasmHAS_CLINT
|
||||||
|
#define portasmHAS_SIFIVE_CLINT portasmHAS_CLINT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef portasmHAS_MTIME
|
||||||
|
#error freertos_risc_v_chip_specific_extensions.h must define portasmHAS_MTIME to either 1 (MTIME clock present) or 0 (MTIME clock not present). See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef portasmHAS_SIFIVE_CLINT
|
||||||
|
#define portasmHAS_SIFIVE_CLINT 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
.global xPortStartFirstTask
|
||||||
|
.global pxPortInitialiseStack
|
||||||
|
.global freertos_risc_v_trap_handler
|
||||||
|
.global freertos_risc_v_exception_handler
|
||||||
|
.global freertos_risc_v_interrupt_handler
|
||||||
|
.global freertos_risc_v_mtimer_interrupt_handler
|
||||||
|
|
||||||
|
.extern vTaskSwitchContext
|
||||||
|
.extern xTaskIncrementTick
|
||||||
|
.extern pullMachineTimerCompareRegister
|
||||||
|
.extern pullNextTime
|
||||||
|
.extern uxTimerIncrementsForOneTick /* size_t type so 32-bit on 32-bit core and 64-bits on 64-bit core. */
|
||||||
|
.extern xTaskReturnAddress
|
||||||
|
|
||||||
|
.weak freertos_risc_v_application_exception_handler
|
||||||
|
.weak freertos_risc_v_application_interrupt_handler
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.macro portUPDATE_MTIMER_COMPARE_REGISTER
|
||||||
|
load_x a0, pullMachineTimerCompareRegister /* Load address of compare register into a0. */
|
||||||
|
load_x a1, pullNextTime /* Load the address of ullNextTime into a1. */
|
||||||
|
|
||||||
|
#if( __riscv_xlen == 32 )
|
||||||
|
|
||||||
|
/* Update the 64-bit mtimer compare match value in two 32-bit writes. */
|
||||||
|
li a4, -1
|
||||||
|
lw a2, 0(a1) /* Load the low word of ullNextTime into a2. */
|
||||||
|
lw a3, 4(a1) /* Load the high word of ullNextTime into a3. */
|
||||||
|
sw a4, 0(a0) /* Low word no smaller than old value to start with - will be overwritten below. */
|
||||||
|
sw a3, 4(a0) /* Store high word of ullNextTime into compare register. No smaller than new value. */
|
||||||
|
sw a2, 0(a0) /* Store low word of ullNextTime into compare register. */
|
||||||
|
lw t0, uxTimerIncrementsForOneTick /* Load the value of ullTimerIncrementForOneTick into t0 (could this be optimized by storing in an array next to pullNextTime?). */
|
||||||
|
add a4, t0, a2 /* Add the low word of ullNextTime to the timer increments for one tick (assumes timer increment for one tick fits in 32-bits). */
|
||||||
|
sltu t1, a4, a2 /* See if the sum of low words overflowed (what about the zero case?). */
|
||||||
|
add t2, a3, t1 /* Add overflow to high word of ullNextTime. */
|
||||||
|
sw a4, 0(a1) /* Store new low word of ullNextTime. */
|
||||||
|
sw t2, 4(a1) /* Store new high word of ullNextTime. */
|
||||||
|
|
||||||
|
#endif /* __riscv_xlen == 32 */
|
||||||
|
|
||||||
|
#if( __riscv_xlen == 64 )
|
||||||
|
|
||||||
|
/* Update the 64-bit mtimer compare match value. */
|
||||||
|
ld t2, 0(a1) /* Load ullNextTime into t2. */
|
||||||
|
sd t2, 0(a0) /* Store ullNextTime into compare register. */
|
||||||
|
ld t0, uxTimerIncrementsForOneTick /* Load the value of ullTimerIncrementForOneTick into t0 (could this be optimized by storing in an array next to pullNextTime?). */
|
||||||
|
add t4, t0, t2 /* Add ullNextTime to the timer increments for one tick. */
|
||||||
|
sd t4, 0(a1) /* Store ullNextTime. */
|
||||||
|
|
||||||
|
#endif /* __riscv_xlen == 64 */
|
||||||
|
.endm
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unlike other ports pxPortInitialiseStack() is written in assembly code as it
|
||||||
|
* needs access to the portasmADDITIONAL_CONTEXT_SIZE constant. The prototype
|
||||||
|
* for the function is as per the other ports:
|
||||||
|
* StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters );
|
||||||
|
*
|
||||||
|
* As per the standard RISC-V ABI pxTopOfStack is passed in in a0, pxCode in
|
||||||
|
* a1, and pvParameters in a2. The new top of stack is passed out in a0.
|
||||||
|
*
|
||||||
|
* RISC-V maps registers to ABI names as follows (X1 to X31 integer registers
|
||||||
|
* for the 'I' profile, X1 to X15 for the 'E' profile, currently I assumed).
|
||||||
|
*
|
||||||
|
* Register ABI Name Description Saver
|
||||||
|
* x0 zero Hard-wired zero -
|
||||||
|
* x1 ra Return address Caller
|
||||||
|
* x2 sp Stack pointer Callee
|
||||||
|
* x3 gp Global pointer -
|
||||||
|
* x4 tp Thread pointer -
|
||||||
|
* x5-7 t0-2 Temporaries Caller
|
||||||
|
* x8 s0/fp Saved register/Frame pointer Callee
|
||||||
|
* x9 s1 Saved register Callee
|
||||||
|
* x10-11 a0-1 Function Arguments/return values Caller
|
||||||
|
* x12-17 a2-7 Function arguments Caller
|
||||||
|
* x18-27 s2-11 Saved registers Callee
|
||||||
|
* x28-31 t3-6 Temporaries Caller
|
||||||
|
*
|
||||||
|
* The RISC-V context is saved to FreeRTOS tasks in the following stack frame,
|
||||||
|
* where the global and thread pointers are currently assumed to be constant so
|
||||||
|
* are not saved:
|
||||||
|
*
|
||||||
|
* xCriticalNesting
|
||||||
|
* x31
|
||||||
|
* x30
|
||||||
|
* x29
|
||||||
|
* x28
|
||||||
|
* x27
|
||||||
|
* x26
|
||||||
|
* x25
|
||||||
|
* x24
|
||||||
|
* x23
|
||||||
|
* x22
|
||||||
|
* x21
|
||||||
|
* x20
|
||||||
|
* x19
|
||||||
|
* x18
|
||||||
|
* x17
|
||||||
|
* x16
|
||||||
|
* x15
|
||||||
|
* x14
|
||||||
|
* x13
|
||||||
|
* x12
|
||||||
|
* x11
|
||||||
|
* pvParameters
|
||||||
|
* x9
|
||||||
|
* x8
|
||||||
|
* x7
|
||||||
|
* x6
|
||||||
|
* x5
|
||||||
|
* portTASK_RETURN_ADDRESS
|
||||||
|
* [FPU registers (when enabled/available) go here]
|
||||||
|
* [VPU registers (when enabled/available) go here]
|
||||||
|
* mstatus
|
||||||
|
* [chip specific registers go here]
|
||||||
|
* pxCode
|
||||||
|
*/
|
||||||
|
pxPortInitialiseStack:
|
||||||
|
addi a0, a0, -portWORD_SIZE /* Space for critical nesting count. */
|
||||||
|
store_x x0, 0(a0) /* Critical nesting count starts at 0 for every task. */
|
||||||
|
|
||||||
|
#ifdef __riscv_32e
|
||||||
|
addi a0, a0, -(6 * portWORD_SIZE) /* Space for registers x10-x15. */
|
||||||
|
#else
|
||||||
|
addi a0, a0, -(22 * portWORD_SIZE) /* Space for registers x10-x31. */
|
||||||
|
#endif
|
||||||
|
store_x a2, 0(a0) /* Task parameters (pvParameters parameter) goes into register x10/a0 on the stack. */
|
||||||
|
|
||||||
|
addi a0, a0, -(6 * portWORD_SIZE) /* Space for registers x5-x9 + taskReturnAddress (register x1). */
|
||||||
|
load_x t0, xTaskReturnAddress
|
||||||
|
store_x t0, 0(a0) /* Return address onto the stack. */
|
||||||
|
|
||||||
|
csrr t0, mstatus /* Obtain current mstatus value. */
|
||||||
|
andi t0, t0, ~0x8 /* Ensure interrupts are disabled when the stack is restored within an ISR. Required when a task is created after the scheduler has been started, otherwise interrupts would be disabled anyway. */
|
||||||
|
addi t1, x0, 0x188 /* Generate the value 0x1880, which are the MPIE=1 and MPP=M_Mode in mstatus. */
|
||||||
|
slli t1, t1, 4
|
||||||
|
or t0, t0, t1 /* Set MPIE and MPP bits in mstatus value. */
|
||||||
|
|
||||||
|
#if( configENABLE_FPU == 1 )
|
||||||
|
/* Mark the FPU as clean in the mstatus value. */
|
||||||
|
li t1, ~MSTATUS_FS_MASK
|
||||||
|
and t0, t0, t1
|
||||||
|
li t1, MSTATUS_FS_CLEAN
|
||||||
|
or t0, t0, t1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if( configENABLE_VPU == 1 )
|
||||||
|
/* Mark the VPU as clean in the mstatus value. */
|
||||||
|
li t1, ~MSTATUS_VS_MASK
|
||||||
|
and t0, t0, t1
|
||||||
|
li t1, MSTATUS_VS_CLEAN
|
||||||
|
or t0, t0, t1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
addi a0, a0, -portWORD_SIZE
|
||||||
|
store_x t0, 0(a0) /* mstatus onto the stack. */
|
||||||
|
|
||||||
|
addi t0, x0, portasmADDITIONAL_CONTEXT_SIZE /* The number of chip specific additional registers. */
|
||||||
|
chip_specific_stack_frame: /* First add any chip specific registers to the stack frame being created. */
|
||||||
|
beq t0, x0, 1f /* No more chip specific registers to save. */
|
||||||
|
addi a0, a0, -portWORD_SIZE /* Make space for chip specific register. */
|
||||||
|
store_x x0, 0(a0) /* Give the chip specific register an initial value of zero. */
|
||||||
|
addi t0, t0, -1 /* Decrement the count of chip specific registers remaining. */
|
||||||
|
j chip_specific_stack_frame /* Until no more chip specific registers. */
|
||||||
|
1:
|
||||||
|
|
||||||
|
addi a0, a0, -portWORD_SIZE
|
||||||
|
store_x a1, 0(a0) /* mret value (pxCode parameter) onto the stack. */
|
||||||
|
ret
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
xPortStartFirstTask:
|
||||||
|
load_x sp, pxCurrentTCB /* Load pxCurrentTCB. */
|
||||||
|
load_x sp, 0( sp ) /* Read sp from first TCB member. */
|
||||||
|
|
||||||
|
load_x x1, 0( sp ) /* Note for starting the scheduler the exception return address is used as the function return address. */
|
||||||
|
|
||||||
|
portasmRESTORE_ADDITIONAL_REGISTERS /* Defined in freertos_risc_v_chip_specific_extensions.h to restore any registers unique to the RISC-V implementation. */
|
||||||
|
|
||||||
|
load_x x5, 1 * portWORD_SIZE( sp ) /* Initial mstatus into x5 (t0). */
|
||||||
|
addi x5, x5, 0x08 /* Set MIE bit so the first task starts with interrupts enabled - required as returns with ret not eret. */
|
||||||
|
csrw mstatus, x5 /* Interrupts enabled from here! */
|
||||||
|
|
||||||
|
load_x x7, 5 * portWORD_SIZE( sp ) /* t2 */
|
||||||
|
load_x x8, 6 * portWORD_SIZE( sp ) /* s0/fp */
|
||||||
|
load_x x9, 7 * portWORD_SIZE( sp ) /* s1 */
|
||||||
|
load_x x10, 8 * portWORD_SIZE( sp ) /* a0 */
|
||||||
|
load_x x11, 9 * portWORD_SIZE( sp ) /* a1 */
|
||||||
|
load_x x12, 10 * portWORD_SIZE( sp ) /* a2 */
|
||||||
|
load_x x13, 11 * portWORD_SIZE( sp ) /* a3 */
|
||||||
|
load_x x14, 12 * portWORD_SIZE( sp ) /* a4 */
|
||||||
|
load_x x15, 13 * portWORD_SIZE( sp ) /* a5 */
|
||||||
|
#ifndef __riscv_32e
|
||||||
|
load_x x16, 14 * portWORD_SIZE( sp ) /* a6 */
|
||||||
|
load_x x17, 15 * portWORD_SIZE( sp ) /* a7 */
|
||||||
|
load_x x18, 16 * portWORD_SIZE( sp ) /* s2 */
|
||||||
|
load_x x19, 17 * portWORD_SIZE( sp ) /* s3 */
|
||||||
|
load_x x20, 18 * portWORD_SIZE( sp ) /* s4 */
|
||||||
|
load_x x21, 19 * portWORD_SIZE( sp ) /* s5 */
|
||||||
|
load_x x22, 20 * portWORD_SIZE( sp ) /* s6 */
|
||||||
|
load_x x23, 21 * portWORD_SIZE( sp ) /* s7 */
|
||||||
|
load_x x24, 22 * portWORD_SIZE( sp ) /* s8 */
|
||||||
|
load_x x25, 23 * portWORD_SIZE( sp ) /* s9 */
|
||||||
|
load_x x26, 24 * portWORD_SIZE( sp ) /* s10 */
|
||||||
|
load_x x27, 25 * portWORD_SIZE( sp ) /* s11 */
|
||||||
|
load_x x28, 26 * portWORD_SIZE( sp ) /* t3 */
|
||||||
|
load_x x29, 27 * portWORD_SIZE( sp ) /* t4 */
|
||||||
|
load_x x30, 28 * portWORD_SIZE( sp ) /* t5 */
|
||||||
|
load_x x31, 29 * portWORD_SIZE( sp ) /* t6 */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
load_x x5, portCRITICAL_NESTING_OFFSET * portWORD_SIZE( sp ) /* Obtain xCriticalNesting value for this task from task's stack. */
|
||||||
|
load_x x6, pxCriticalNesting /* Load the address of xCriticalNesting into x6. */
|
||||||
|
store_x x5, 0( x6 ) /* Restore the critical nesting value for this task. */
|
||||||
|
|
||||||
|
load_x x5, 3 * portWORD_SIZE( sp ) /* Initial x5 (t0) value. */
|
||||||
|
load_x x6, 4 * portWORD_SIZE( sp ) /* Initial x6 (t1) value. */
|
||||||
|
|
||||||
|
addi sp, sp, portCONTEXT_SIZE
|
||||||
|
ret
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
freertos_risc_v_application_exception_handler:
|
||||||
|
csrr t0, mcause /* For viewing in the debugger only. */
|
||||||
|
csrr t1, mepc /* For viewing in the debugger only */
|
||||||
|
csrr t2, mstatus /* For viewing in the debugger only */
|
||||||
|
j .
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
freertos_risc_v_application_interrupt_handler:
|
||||||
|
csrr t0, mcause /* For viewing in the debugger only. */
|
||||||
|
csrr t1, mepc /* For viewing in the debugger only */
|
||||||
|
csrr t2, mstatus /* For viewing in the debugger only */
|
||||||
|
j .
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.section .text.freertos_risc_v_exception_handler
|
||||||
|
freertos_risc_v_exception_handler:
|
||||||
|
portcontextSAVE_EXCEPTION_CONTEXT
|
||||||
|
/* a0 now contains mcause. */
|
||||||
|
li t0, 11 /* 11 == environment call. */
|
||||||
|
bne a0, t0, other_exception /* Not an M environment call, so some other exception. */
|
||||||
|
call vTaskSwitchContext
|
||||||
|
portcontextRESTORE_CONTEXT
|
||||||
|
|
||||||
|
other_exception:
|
||||||
|
call freertos_risc_v_application_exception_handler
|
||||||
|
portcontextRESTORE_CONTEXT
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.section .text.freertos_risc_v_interrupt_handler
|
||||||
|
freertos_risc_v_interrupt_handler:
|
||||||
|
portcontextSAVE_INTERRUPT_CONTEXT
|
||||||
|
call freertos_risc_v_application_interrupt_handler
|
||||||
|
portcontextRESTORE_CONTEXT
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.section .text.freertos_risc_v_mtimer_interrupt_handler
|
||||||
|
freertos_risc_v_mtimer_interrupt_handler:
|
||||||
|
portcontextSAVE_INTERRUPT_CONTEXT
|
||||||
|
portUPDATE_MTIMER_COMPARE_REGISTER
|
||||||
|
call xTaskIncrementTick
|
||||||
|
beqz a0, exit_without_context_switch /* Don't switch context if incrementing tick didn't unblock a task. */
|
||||||
|
call vTaskSwitchContext
|
||||||
|
exit_without_context_switch:
|
||||||
|
portcontextRESTORE_CONTEXT
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.section .text.freertos_risc_v_trap_handler
|
||||||
|
.align 8
|
||||||
|
freertos_risc_v_trap_handler:
|
||||||
|
portcontextSAVE_CONTEXT_INTERNAL
|
||||||
|
|
||||||
|
csrr a0, mcause
|
||||||
|
csrr a1, mepc
|
||||||
|
|
||||||
|
bge a0, x0, synchronous_exception
|
||||||
|
|
||||||
|
asynchronous_interrupt:
|
||||||
|
store_x a1, 0( sp ) /* Asynchronous interrupt so save unmodified exception return address. */
|
||||||
|
load_x sp, xISRStackTop /* Switch to ISR stack. */
|
||||||
|
j handle_interrupt
|
||||||
|
|
||||||
|
synchronous_exception:
|
||||||
|
addi a1, a1, 4 /* Synchronous so update exception return address to the instruction after the instruction that generated the exeption. */
|
||||||
|
store_x a1, 0( sp ) /* Save updated exception return address. */
|
||||||
|
load_x sp, xISRStackTop /* Switch to ISR stack. */
|
||||||
|
j handle_exception
|
||||||
|
|
||||||
|
handle_interrupt:
|
||||||
|
#if( portasmHAS_MTIME != 0 )
|
||||||
|
|
||||||
|
test_if_mtimer: /* If there is a CLINT then the mtimer is used to generate the tick interrupt. */
|
||||||
|
addi t0, x0, 1
|
||||||
|
slli t0, t0, __riscv_xlen - 1 /* LSB is already set, shift into MSB. Shift 31 on 32-bit or 63 on 64-bit cores. */
|
||||||
|
addi t1, t0, 7 /* 0x8000[]0007 == machine timer interrupt. */
|
||||||
|
bne a0, t1, application_interrupt_handler
|
||||||
|
|
||||||
|
portUPDATE_MTIMER_COMPARE_REGISTER
|
||||||
|
call xTaskIncrementTick
|
||||||
|
beqz a0, processed_source /* Don't switch context if incrementing tick didn't unblock a task. */
|
||||||
|
call vTaskSwitchContext
|
||||||
|
j processed_source
|
||||||
|
|
||||||
|
#endif /* portasmHAS_MTIME */
|
||||||
|
|
||||||
|
application_interrupt_handler:
|
||||||
|
call freertos_risc_v_application_interrupt_handler
|
||||||
|
j processed_source
|
||||||
|
|
||||||
|
handle_exception:
|
||||||
|
/* a0 contains mcause. */
|
||||||
|
li t0, 11 /* 11 == environment call. */
|
||||||
|
bne a0, t0, application_exception_handler /* Not an M environment call, so some other exception. */
|
||||||
|
call vTaskSwitchContext
|
||||||
|
j processed_source
|
||||||
|
|
||||||
|
application_exception_handler:
|
||||||
|
call freertos_risc_v_application_exception_handler
|
||||||
|
j processed_source /* No other exceptions handled yet. */
|
||||||
|
|
||||||
|
processed_source:
|
||||||
|
portcontextRESTORE_CONTEXT
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
@@ -0,0 +1,468 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PORTCONTEXT_H
|
||||||
|
#define PORTCONTEXT_H
|
||||||
|
|
||||||
|
#ifndef configENABLE_FPU
|
||||||
|
#define configENABLE_FPU 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configENABLE_VPU
|
||||||
|
#define configENABLE_VPU 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __riscv_xlen == 64
|
||||||
|
#define portWORD_SIZE 8
|
||||||
|
#define store_x sd
|
||||||
|
#define load_x ld
|
||||||
|
#elif __riscv_xlen == 32
|
||||||
|
#define store_x sw
|
||||||
|
#define load_x lw
|
||||||
|
#define portWORD_SIZE 4
|
||||||
|
#else
|
||||||
|
#error Assembler did not define __riscv_xlen
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "freertos_risc_v_chip_specific_extensions.h"
|
||||||
|
|
||||||
|
/* Only the standard core registers are stored by default. Any additional
|
||||||
|
* registers must be saved by the portasmSAVE_ADDITIONAL_REGISTERS and
|
||||||
|
* portasmRESTORE_ADDITIONAL_REGISTERS macros - which can be defined in a chip
|
||||||
|
* specific version of freertos_risc_v_chip_specific_extensions.h. See the
|
||||||
|
* notes at the top of portASM.S file. */
|
||||||
|
#ifdef __riscv_32e
|
||||||
|
#define portCONTEXT_SIZE ( 15 * portWORD_SIZE )
|
||||||
|
#define portCRITICAL_NESTING_OFFSET 14
|
||||||
|
#else
|
||||||
|
#define portCONTEXT_SIZE ( 31 * portWORD_SIZE )
|
||||||
|
#define portCRITICAL_NESTING_OFFSET 30
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ( configENABLE_FPU == 1 )
|
||||||
|
/* Bit [14:13] in the mstatus encode the status of FPU state which is one of
|
||||||
|
* the following values:
|
||||||
|
* 1. Value: 0, Meaning: Off.
|
||||||
|
* 2. Value: 1, Meaning: Initial.
|
||||||
|
* 3. Value: 2, Meaning: Clean.
|
||||||
|
* 4. Value: 3, Meaning: Dirty.
|
||||||
|
*/
|
||||||
|
#define MSTATUS_FS_MASK 0x6000
|
||||||
|
#define MSTATUS_FS_INITIAL 0x2000
|
||||||
|
#define MSTATUS_FS_CLEAN 0x4000
|
||||||
|
#define MSTATUS_FS_DIRTY 0x6000
|
||||||
|
#define MSTATUS_FS_OFFSET 13
|
||||||
|
|
||||||
|
#ifdef __riscv_fdiv
|
||||||
|
#if __riscv_flen == 32
|
||||||
|
#define load_f flw
|
||||||
|
#define store_f fsw
|
||||||
|
#elif __riscv_flen == 64
|
||||||
|
#define load_f fld
|
||||||
|
#define store_f fsd
|
||||||
|
#else
|
||||||
|
#error Assembler did not define __riscv_flen
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define portFPU_REG_SIZE ( __riscv_flen / 8 )
|
||||||
|
#define portFPU_REG_COUNT 33 /* 32 Floating point registers plus one CSR. */
|
||||||
|
#define portFPU_REG_OFFSET( regIndex ) ( ( 2 * portWORD_SIZE ) + ( regIndex * portFPU_REG_SIZE ) )
|
||||||
|
#define portFPU_CONTEXT_SIZE ( portFPU_REG_SIZE * portFPU_REG_COUNT )
|
||||||
|
#else
|
||||||
|
#error configENABLE_FPU must not be set to 1 if the hardware does not have FPU
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ( configENABLE_VPU == 1 )
|
||||||
|
/* Bit [10:9] in the mstatus encode the status of VPU state which is one of
|
||||||
|
* the following values:
|
||||||
|
* 1. Value: 0, Meaning: Off.
|
||||||
|
* 2. Value: 1, Meaning: Initial.
|
||||||
|
* 3. Value: 2, Meaning: Clean.
|
||||||
|
* 4. Value: 3, Meaning: Dirty.
|
||||||
|
*/
|
||||||
|
#define MSTATUS_VS_MASK 0x600
|
||||||
|
#define MSTATUS_VS_INITIAL 0x200
|
||||||
|
#define MSTATUS_VS_CLEAN 0x400
|
||||||
|
#define MSTATUS_VS_DIRTY 0x600
|
||||||
|
#define MSTATUS_VS_OFFSET 9
|
||||||
|
|
||||||
|
#ifndef __riscv_vector
|
||||||
|
#error configENABLE_VPU must not be set to 1 if the hardware does not have VPU
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.extern pxCurrentTCB
|
||||||
|
.extern xISRStackTop
|
||||||
|
.extern xCriticalNesting
|
||||||
|
.extern pxCriticalNesting
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.macro portcontexSAVE_FPU_CONTEXT
|
||||||
|
addi sp, sp, -( portFPU_CONTEXT_SIZE )
|
||||||
|
/* Store the FPU registers. */
|
||||||
|
store_f f0, portFPU_REG_OFFSET( 0 )( sp )
|
||||||
|
store_f f1, portFPU_REG_OFFSET( 1 )( sp )
|
||||||
|
store_f f2, portFPU_REG_OFFSET( 2 )( sp )
|
||||||
|
store_f f3, portFPU_REG_OFFSET( 3 )( sp )
|
||||||
|
store_f f4, portFPU_REG_OFFSET( 4 )( sp )
|
||||||
|
store_f f5, portFPU_REG_OFFSET( 5 )( sp )
|
||||||
|
store_f f6, portFPU_REG_OFFSET( 6 )( sp )
|
||||||
|
store_f f7, portFPU_REG_OFFSET( 7 )( sp )
|
||||||
|
store_f f8, portFPU_REG_OFFSET( 8 )( sp )
|
||||||
|
store_f f9, portFPU_REG_OFFSET( 9 )( sp )
|
||||||
|
store_f f10, portFPU_REG_OFFSET( 10 )( sp )
|
||||||
|
store_f f11, portFPU_REG_OFFSET( 11 )( sp )
|
||||||
|
store_f f12, portFPU_REG_OFFSET( 12 )( sp )
|
||||||
|
store_f f13, portFPU_REG_OFFSET( 13 )( sp )
|
||||||
|
store_f f14, portFPU_REG_OFFSET( 14 )( sp )
|
||||||
|
store_f f15, portFPU_REG_OFFSET( 15 )( sp )
|
||||||
|
store_f f16, portFPU_REG_OFFSET( 16 )( sp )
|
||||||
|
store_f f17, portFPU_REG_OFFSET( 17 )( sp )
|
||||||
|
store_f f18, portFPU_REG_OFFSET( 18 )( sp )
|
||||||
|
store_f f19, portFPU_REG_OFFSET( 19 )( sp )
|
||||||
|
store_f f20, portFPU_REG_OFFSET( 20 )( sp )
|
||||||
|
store_f f21, portFPU_REG_OFFSET( 21 )( sp )
|
||||||
|
store_f f22, portFPU_REG_OFFSET( 22 )( sp )
|
||||||
|
store_f f23, portFPU_REG_OFFSET( 23 )( sp )
|
||||||
|
store_f f24, portFPU_REG_OFFSET( 24 )( sp )
|
||||||
|
store_f f25, portFPU_REG_OFFSET( 25 )( sp )
|
||||||
|
store_f f26, portFPU_REG_OFFSET( 26 )( sp )
|
||||||
|
store_f f27, portFPU_REG_OFFSET( 27 )( sp )
|
||||||
|
store_f f28, portFPU_REG_OFFSET( 28 )( sp )
|
||||||
|
store_f f29, portFPU_REG_OFFSET( 29 )( sp )
|
||||||
|
store_f f30, portFPU_REG_OFFSET( 30 )( sp )
|
||||||
|
store_f f31, portFPU_REG_OFFSET( 31 )( sp )
|
||||||
|
csrr t0, fcsr
|
||||||
|
store_x t0, portFPU_REG_OFFSET( 32 )( sp )
|
||||||
|
.endm
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.macro portcontextRESTORE_FPU_CONTEXT
|
||||||
|
/* Restore the FPU registers. */
|
||||||
|
load_f f0, portFPU_REG_OFFSET( 0 )( sp )
|
||||||
|
load_f f1, portFPU_REG_OFFSET( 1 )( sp )
|
||||||
|
load_f f2, portFPU_REG_OFFSET( 2 )( sp )
|
||||||
|
load_f f3, portFPU_REG_OFFSET( 3 )( sp )
|
||||||
|
load_f f4, portFPU_REG_OFFSET( 4 )( sp )
|
||||||
|
load_f f5, portFPU_REG_OFFSET( 5 )( sp )
|
||||||
|
load_f f6, portFPU_REG_OFFSET( 6 )( sp )
|
||||||
|
load_f f7, portFPU_REG_OFFSET( 7 )( sp )
|
||||||
|
load_f f8, portFPU_REG_OFFSET( 8 )( sp )
|
||||||
|
load_f f9, portFPU_REG_OFFSET( 9 )( sp )
|
||||||
|
load_f f10, portFPU_REG_OFFSET( 10 )( sp )
|
||||||
|
load_f f11, portFPU_REG_OFFSET( 11 )( sp )
|
||||||
|
load_f f12, portFPU_REG_OFFSET( 12 )( sp )
|
||||||
|
load_f f13, portFPU_REG_OFFSET( 13 )( sp )
|
||||||
|
load_f f14, portFPU_REG_OFFSET( 14 )( sp )
|
||||||
|
load_f f15, portFPU_REG_OFFSET( 15 )( sp )
|
||||||
|
load_f f16, portFPU_REG_OFFSET( 16 )( sp )
|
||||||
|
load_f f17, portFPU_REG_OFFSET( 17 )( sp )
|
||||||
|
load_f f18, portFPU_REG_OFFSET( 18 )( sp )
|
||||||
|
load_f f19, portFPU_REG_OFFSET( 19 )( sp )
|
||||||
|
load_f f20, portFPU_REG_OFFSET( 20 )( sp )
|
||||||
|
load_f f21, portFPU_REG_OFFSET( 21 )( sp )
|
||||||
|
load_f f22, portFPU_REG_OFFSET( 22 )( sp )
|
||||||
|
load_f f23, portFPU_REG_OFFSET( 23 )( sp )
|
||||||
|
load_f f24, portFPU_REG_OFFSET( 24 )( sp )
|
||||||
|
load_f f25, portFPU_REG_OFFSET( 25 )( sp )
|
||||||
|
load_f f26, portFPU_REG_OFFSET( 26 )( sp )
|
||||||
|
load_f f27, portFPU_REG_OFFSET( 27 )( sp )
|
||||||
|
load_f f28, portFPU_REG_OFFSET( 28 )( sp )
|
||||||
|
load_f f29, portFPU_REG_OFFSET( 29 )( sp )
|
||||||
|
load_f f30, portFPU_REG_OFFSET( 30 )( sp )
|
||||||
|
load_f f31, portFPU_REG_OFFSET( 31 )( sp )
|
||||||
|
load_x t0, portFPU_REG_OFFSET( 32 )( sp )
|
||||||
|
csrw fcsr, t0
|
||||||
|
addi sp, sp, ( portFPU_CONTEXT_SIZE )
|
||||||
|
.endm
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.macro portcontexSAVE_VPU_CONTEXT
|
||||||
|
/* Un-reserve the space reserved for mstatus and epc. */
|
||||||
|
add sp, sp, ( 2 * portWORD_SIZE )
|
||||||
|
|
||||||
|
csrr t0, vlenb /* t0 = vlenb. vlenb is the length of each vector register in bytes. */
|
||||||
|
slli t0, t0, 3 /* t0 = vlenb * 8. t0 now contains the space required to store 8 vector registers. */
|
||||||
|
neg t0, t0
|
||||||
|
|
||||||
|
/* Store the vector registers in group of 8. */
|
||||||
|
add sp, sp, t0
|
||||||
|
vs8r.v v24, (sp) /* Store v24-v31. */
|
||||||
|
add sp, sp, t0
|
||||||
|
vs8r.v v16, (sp) /* Store v16-v23. */
|
||||||
|
add sp, sp, t0
|
||||||
|
vs8r.v v8, (sp) /* Store v8-v15. */
|
||||||
|
add sp, sp, t0
|
||||||
|
vs8r.v v0, (sp) /* Store v0-v7. */
|
||||||
|
|
||||||
|
/* Store the VPU CSRs. */
|
||||||
|
addi sp, sp, -( 4 * portWORD_SIZE )
|
||||||
|
csrr t0, vstart
|
||||||
|
store_x t0, 0 * portWORD_SIZE( sp )
|
||||||
|
csrr t0, vcsr
|
||||||
|
store_x t0, 1 * portWORD_SIZE( sp )
|
||||||
|
csrr t0, vl
|
||||||
|
store_x t0, 2 * portWORD_SIZE( sp )
|
||||||
|
csrr t0, vtype
|
||||||
|
store_x t0, 3 * portWORD_SIZE( sp )
|
||||||
|
|
||||||
|
/* Re-reserve the space for mstatus and epc. */
|
||||||
|
add sp, sp, -( 2 * portWORD_SIZE )
|
||||||
|
.endm
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.macro portcontextRESTORE_VPU_CONTEXT
|
||||||
|
/* Un-reserve the space reserved for mstatus and epc. */
|
||||||
|
add sp, sp, ( 2 * portWORD_SIZE )
|
||||||
|
|
||||||
|
/* Restore the VPU CSRs. */
|
||||||
|
load_x t0, 0 * portWORD_SIZE( sp )
|
||||||
|
csrw vstart, t0
|
||||||
|
load_x t0, 1 * portWORD_SIZE( sp )
|
||||||
|
csrw vcsr, t0
|
||||||
|
load_x t0, 2 * portWORD_SIZE( sp )
|
||||||
|
load_x t1, 3 * portWORD_SIZE( sp )
|
||||||
|
vsetvl x0, t0, t1 /* vlen and vtype can only be updated by using vset*vl* instructions. */
|
||||||
|
addi sp, sp, ( 4 * portWORD_SIZE )
|
||||||
|
|
||||||
|
csrr t0, vlenb /* t0 = vlenb. vlenb is the length of each vector register in bytes. */
|
||||||
|
slli t0, t0, 3 /* t0 = vlenb * 8. t0 now contains the space required to store 8 vector registers. */
|
||||||
|
|
||||||
|
/* Restore the vector registers. */
|
||||||
|
vl8r.v v0, (sp) /* Restore v0-v7. */
|
||||||
|
add sp, sp, t0
|
||||||
|
vl8r.v v8, (sp) /* Restore v8-v15. */
|
||||||
|
add sp, sp, t0
|
||||||
|
vl8r.v v16, (sp) /* Restore v16-v23. */
|
||||||
|
add sp, sp, t0
|
||||||
|
vl8r.v v24, (sp) /* Restore v23-v31. */
|
||||||
|
add sp, sp, t0
|
||||||
|
|
||||||
|
/* Re-reserve the space for mstatus and epc. */
|
||||||
|
add sp, sp, -( 2 * portWORD_SIZE )
|
||||||
|
.endm
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.macro portcontextSAVE_CONTEXT_INTERNAL
|
||||||
|
addi sp, sp, -portCONTEXT_SIZE
|
||||||
|
store_x x1, 2 * portWORD_SIZE( sp )
|
||||||
|
store_x x5, 3 * portWORD_SIZE( sp )
|
||||||
|
store_x x6, 4 * portWORD_SIZE( sp )
|
||||||
|
store_x x7, 5 * portWORD_SIZE( sp )
|
||||||
|
store_x x8, 6 * portWORD_SIZE( sp )
|
||||||
|
store_x x9, 7 * portWORD_SIZE( sp )
|
||||||
|
store_x x10, 8 * portWORD_SIZE( sp )
|
||||||
|
store_x x11, 9 * portWORD_SIZE( sp )
|
||||||
|
store_x x12, 10 * portWORD_SIZE( sp )
|
||||||
|
store_x x13, 11 * portWORD_SIZE( sp )
|
||||||
|
store_x x14, 12 * portWORD_SIZE( sp )
|
||||||
|
store_x x15, 13 * portWORD_SIZE( sp )
|
||||||
|
#ifndef __riscv_32e
|
||||||
|
store_x x16, 14 * portWORD_SIZE( sp )
|
||||||
|
store_x x17, 15 * portWORD_SIZE( sp )
|
||||||
|
store_x x18, 16 * portWORD_SIZE( sp )
|
||||||
|
store_x x19, 17 * portWORD_SIZE( sp )
|
||||||
|
store_x x20, 18 * portWORD_SIZE( sp )
|
||||||
|
store_x x21, 19 * portWORD_SIZE( sp )
|
||||||
|
store_x x22, 20 * portWORD_SIZE( sp )
|
||||||
|
store_x x23, 21 * portWORD_SIZE( sp )
|
||||||
|
store_x x24, 22 * portWORD_SIZE( sp )
|
||||||
|
store_x x25, 23 * portWORD_SIZE( sp )
|
||||||
|
store_x x26, 24 * portWORD_SIZE( sp )
|
||||||
|
store_x x27, 25 * portWORD_SIZE( sp )
|
||||||
|
store_x x28, 26 * portWORD_SIZE( sp )
|
||||||
|
store_x x29, 27 * portWORD_SIZE( sp )
|
||||||
|
store_x x30, 28 * portWORD_SIZE( sp )
|
||||||
|
store_x x31, 29 * portWORD_SIZE( sp )
|
||||||
|
#endif /* ifndef __riscv_32e */
|
||||||
|
|
||||||
|
load_x t0, xCriticalNesting /* Load the value of xCriticalNesting into t0. */
|
||||||
|
store_x t0, portCRITICAL_NESTING_OFFSET * portWORD_SIZE( sp ) /* Store the critical nesting value to the stack. */
|
||||||
|
|
||||||
|
#if( configENABLE_FPU == 1 )
|
||||||
|
csrr t0, mstatus
|
||||||
|
srl t1, t0, MSTATUS_FS_OFFSET
|
||||||
|
andi t1, t1, 3
|
||||||
|
addi t2, x0, 3
|
||||||
|
bne t1, t2, 1f /* If FPU status is not dirty, do not save FPU registers. */
|
||||||
|
|
||||||
|
portcontexSAVE_FPU_CONTEXT
|
||||||
|
1:
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if( configENABLE_VPU == 1 )
|
||||||
|
csrr t0, mstatus
|
||||||
|
srl t1, t0, MSTATUS_VS_OFFSET
|
||||||
|
andi t1, t1, 3
|
||||||
|
addi t2, x0, 3
|
||||||
|
bne t1, t2, 2f /* If VPU status is not dirty, do not save VPU registers. */
|
||||||
|
|
||||||
|
portcontexSAVE_VPU_CONTEXT
|
||||||
|
2:
|
||||||
|
#endif
|
||||||
|
|
||||||
|
csrr t0, mstatus
|
||||||
|
store_x t0, 1 * portWORD_SIZE( sp )
|
||||||
|
|
||||||
|
portasmSAVE_ADDITIONAL_REGISTERS /* Defined in freertos_risc_v_chip_specific_extensions.h to save any registers unique to the RISC-V implementation. */
|
||||||
|
|
||||||
|
#if( configENABLE_FPU == 1 )
|
||||||
|
/* Mark the FPU as clean, if it was dirty and we saved FPU registers. */
|
||||||
|
srl t1, t0, MSTATUS_FS_OFFSET
|
||||||
|
andi t1, t1, 3
|
||||||
|
addi t2, x0, 3
|
||||||
|
bne t1, t2, 3f
|
||||||
|
|
||||||
|
li t1, ~MSTATUS_FS_MASK
|
||||||
|
and t0, t0, t1
|
||||||
|
li t1, MSTATUS_FS_CLEAN
|
||||||
|
or t0, t0, t1
|
||||||
|
csrw mstatus, t0
|
||||||
|
3:
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if( configENABLE_VPU == 1 )
|
||||||
|
/* Mark the VPU as clean, if it was dirty and we saved VPU registers. */
|
||||||
|
srl t1, t0, MSTATUS_VS_OFFSET
|
||||||
|
andi t1, t1, 3
|
||||||
|
addi t2, x0, 3
|
||||||
|
bne t1, t2, 4f
|
||||||
|
|
||||||
|
li t1, ~MSTATUS_VS_MASK
|
||||||
|
and t0, t0, t1
|
||||||
|
li t1, MSTATUS_VS_CLEAN
|
||||||
|
or t0, t0, t1
|
||||||
|
csrw mstatus, t0
|
||||||
|
4:
|
||||||
|
#endif
|
||||||
|
|
||||||
|
load_x t0, pxCurrentTCB /* Load pxCurrentTCB. */
|
||||||
|
store_x sp, 0 ( t0 ) /* Write sp to first TCB member. */
|
||||||
|
|
||||||
|
.endm
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.macro portcontextSAVE_EXCEPTION_CONTEXT
|
||||||
|
portcontextSAVE_CONTEXT_INTERNAL
|
||||||
|
csrr a0, mcause
|
||||||
|
csrr a1, mepc
|
||||||
|
addi a1, a1, 4 /* Synchronous so update exception return address to the instruction after the instruction that generated the exception. */
|
||||||
|
store_x a1, 0 ( sp ) /* Save updated exception return address. */
|
||||||
|
load_x sp, xISRStackTop /* Switch to ISR stack. */
|
||||||
|
.endm
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.macro portcontextSAVE_INTERRUPT_CONTEXT
|
||||||
|
portcontextSAVE_CONTEXT_INTERNAL
|
||||||
|
csrr a0, mcause
|
||||||
|
csrr a1, mepc
|
||||||
|
store_x a1, 0 ( sp ) /* Asynchronous interrupt so save unmodified exception return address. */
|
||||||
|
load_x sp, xISRStackTop /* Switch to ISR stack. */
|
||||||
|
.endm
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
.macro portcontextRESTORE_CONTEXT
|
||||||
|
load_x t1, pxCurrentTCB /* Load pxCurrentTCB. */
|
||||||
|
load_x sp, 0 ( t1 ) /* Read sp from first TCB member. */
|
||||||
|
|
||||||
|
/* Load mepc with the address of the instruction in the task to run next. */
|
||||||
|
load_x t0, 0 ( sp )
|
||||||
|
csrw mepc, t0
|
||||||
|
|
||||||
|
/* Defined in freertos_risc_v_chip_specific_extensions.h to restore any registers unique to the RISC-V implementation. */
|
||||||
|
portasmRESTORE_ADDITIONAL_REGISTERS
|
||||||
|
|
||||||
|
/* Restore mstatus register. It is important to use t3 (and not t0) here as t3
|
||||||
|
* is not clobbered by portcontextRESTORE_VPU_CONTEXT and
|
||||||
|
* portcontextRESTORE_FPU_CONTEXT. */
|
||||||
|
load_x t3, 1 * portWORD_SIZE( sp )
|
||||||
|
csrw mstatus, t3
|
||||||
|
|
||||||
|
#if( configENABLE_VPU == 1 )
|
||||||
|
srl t1, t3, MSTATUS_VS_OFFSET
|
||||||
|
andi t1, t1, 3
|
||||||
|
addi t2, x0, 3
|
||||||
|
bne t1, t2, 5f /* If VPU status is not dirty, do not restore VPU registers. */
|
||||||
|
|
||||||
|
portcontextRESTORE_VPU_CONTEXT
|
||||||
|
5:
|
||||||
|
#endif /* ifdef portasmSTORE_VPU_CONTEXT */
|
||||||
|
|
||||||
|
#if( configENABLE_FPU == 1 )
|
||||||
|
srl t1, t3, MSTATUS_FS_OFFSET
|
||||||
|
andi t1, t1, 3
|
||||||
|
addi t2, x0, 3
|
||||||
|
bne t1, t2, 6f /* If FPU status is not dirty, do not restore FPU registers. */
|
||||||
|
|
||||||
|
portcontextRESTORE_FPU_CONTEXT
|
||||||
|
6:
|
||||||
|
#endif /* ifdef portasmSTORE_FPU_CONTEXT */
|
||||||
|
|
||||||
|
load_x t0, portCRITICAL_NESTING_OFFSET * portWORD_SIZE( sp ) /* Obtain xCriticalNesting value for this task from task's stack. */
|
||||||
|
load_x t1, pxCriticalNesting /* Load the address of xCriticalNesting into t1. */
|
||||||
|
store_x t0, 0 ( t1 ) /* Restore the critical nesting value for this task. */
|
||||||
|
|
||||||
|
load_x x1, 2 * portWORD_SIZE( sp )
|
||||||
|
load_x x5, 3 * portWORD_SIZE( sp )
|
||||||
|
load_x x6, 4 * portWORD_SIZE( sp )
|
||||||
|
load_x x7, 5 * portWORD_SIZE( sp )
|
||||||
|
load_x x8, 6 * portWORD_SIZE( sp )
|
||||||
|
load_x x9, 7 * portWORD_SIZE( sp )
|
||||||
|
load_x x10, 8 * portWORD_SIZE( sp )
|
||||||
|
load_x x11, 9 * portWORD_SIZE( sp )
|
||||||
|
load_x x12, 10 * portWORD_SIZE( sp )
|
||||||
|
load_x x13, 11 * portWORD_SIZE( sp )
|
||||||
|
load_x x14, 12 * portWORD_SIZE( sp )
|
||||||
|
load_x x15, 13 * portWORD_SIZE( sp )
|
||||||
|
#ifndef __riscv_32e
|
||||||
|
load_x x16, 14 * portWORD_SIZE( sp )
|
||||||
|
load_x x17, 15 * portWORD_SIZE( sp )
|
||||||
|
load_x x18, 16 * portWORD_SIZE( sp )
|
||||||
|
load_x x19, 17 * portWORD_SIZE( sp )
|
||||||
|
load_x x20, 18 * portWORD_SIZE( sp )
|
||||||
|
load_x x21, 19 * portWORD_SIZE( sp )
|
||||||
|
load_x x22, 20 * portWORD_SIZE( sp )
|
||||||
|
load_x x23, 21 * portWORD_SIZE( sp )
|
||||||
|
load_x x24, 22 * portWORD_SIZE( sp )
|
||||||
|
load_x x25, 23 * portWORD_SIZE( sp )
|
||||||
|
load_x x26, 24 * portWORD_SIZE( sp )
|
||||||
|
load_x x27, 25 * portWORD_SIZE( sp )
|
||||||
|
load_x x28, 26 * portWORD_SIZE( sp )
|
||||||
|
load_x x29, 27 * portWORD_SIZE( sp )
|
||||||
|
load_x x30, 28 * portWORD_SIZE( sp )
|
||||||
|
load_x x31, 29 * portWORD_SIZE( sp )
|
||||||
|
#endif /* ifndef __riscv_32e */
|
||||||
|
addi sp, sp, portCONTEXT_SIZE
|
||||||
|
|
||||||
|
mret
|
||||||
|
.endm
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#endif /* PORTCONTEXT_H */
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef PORTMACRO_H
|
||||||
|
#define PORTMACRO_H
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------
|
||||||
|
* Port specific definitions.
|
||||||
|
*
|
||||||
|
* The settings in this file configure FreeRTOS correctly for the
|
||||||
|
* given hardware and compiler.
|
||||||
|
*
|
||||||
|
* These settings should not be altered.
|
||||||
|
*-----------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Type definitions. */
|
||||||
|
#if __riscv_xlen == 64
|
||||||
|
#define portSTACK_TYPE uint64_t
|
||||||
|
#define portBASE_TYPE int64_t
|
||||||
|
#define portUBASE_TYPE uint64_t
|
||||||
|
#define portMAX_DELAY ( TickType_t ) 0xffffffffffffffffUL
|
||||||
|
#define portPOINTER_SIZE_TYPE uint64_t
|
||||||
|
#elif __riscv_xlen == 32
|
||||||
|
#define portSTACK_TYPE uint32_t
|
||||||
|
#define portBASE_TYPE int32_t
|
||||||
|
#define portUBASE_TYPE uint32_t
|
||||||
|
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
|
||||||
|
#else /* if __riscv_xlen == 64 */
|
||||||
|
#error "Assembler did not define __riscv_xlen"
|
||||||
|
#endif /* if __riscv_xlen == 64 */
|
||||||
|
|
||||||
|
typedef portSTACK_TYPE StackType_t;
|
||||||
|
typedef portBASE_TYPE BaseType_t;
|
||||||
|
typedef portUBASE_TYPE UBaseType_t;
|
||||||
|
typedef portUBASE_TYPE TickType_t;
|
||||||
|
|
||||||
|
/* Legacy type definitions. */
|
||||||
|
#define portCHAR char
|
||||||
|
#define portFLOAT float
|
||||||
|
#define portDOUBLE double
|
||||||
|
#define portLONG long
|
||||||
|
#define portSHORT short
|
||||||
|
|
||||||
|
/* 32-bit tick type on a 32-bit architecture, so reads of the tick count do
|
||||||
|
* not need to be guarded with a critical section. */
|
||||||
|
#define portTICK_TYPE_IS_ATOMIC 1
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Architecture specifics. */
|
||||||
|
#define portSTACK_GROWTH ( -1 )
|
||||||
|
#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
|
||||||
|
#ifdef __riscv_32e
|
||||||
|
#define portBYTE_ALIGNMENT 8 /* RV32E uses RISC-V EABI with reduced stack alignment requirements */
|
||||||
|
#else
|
||||||
|
#define portBYTE_ALIGNMENT 16
|
||||||
|
#endif
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Scheduler utilities. */
|
||||||
|
extern void vTaskSwitchContext( void );
|
||||||
|
#define portYIELD() __asm volatile ( "ecall" );
|
||||||
|
#define portEND_SWITCHING_ISR( xSwitchRequired ) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if( xSwitchRequired != pdFALSE ) \
|
||||||
|
{ \
|
||||||
|
traceISR_EXIT_TO_SCHEDULER(); \
|
||||||
|
vTaskSwitchContext(); \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
traceISR_EXIT(); \
|
||||||
|
} \
|
||||||
|
} while( 0 )
|
||||||
|
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Critical section management. */
|
||||||
|
#define portCRITICAL_NESTING_IN_TCB 0
|
||||||
|
|
||||||
|
#define portDISABLE_INTERRUPTS() __asm volatile ( "csrc mstatus, 8" )
|
||||||
|
#define portENABLE_INTERRUPTS() __asm volatile ( "csrs mstatus, 8" )
|
||||||
|
|
||||||
|
extern size_t xCriticalNesting;
|
||||||
|
#define portENTER_CRITICAL() \
|
||||||
|
{ \
|
||||||
|
portDISABLE_INTERRUPTS(); \
|
||||||
|
xCriticalNesting++; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define portEXIT_CRITICAL() \
|
||||||
|
{ \
|
||||||
|
xCriticalNesting--; \
|
||||||
|
if( xCriticalNesting == 0 ) \
|
||||||
|
{ \
|
||||||
|
portENABLE_INTERRUPTS(); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Architecture specific optimisations. */
|
||||||
|
#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION
|
||||||
|
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 )
|
||||||
|
|
||||||
|
/* Check the configuration. */
|
||||||
|
#if ( configMAX_PRIORITIES > 32 )
|
||||||
|
#error "configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Store/clear the ready priorities in a bit map. */
|
||||||
|
#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
|
||||||
|
#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - __builtin_clz( uxReadyPriorities ) )
|
||||||
|
|
||||||
|
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Task function macros as described on the FreeRTOS.org WEB site. These are
|
||||||
|
* not necessary for to use this port. They are defined so the common demo
|
||||||
|
* files (which build with all the ports) will build. */
|
||||||
|
#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void * pvParameters )
|
||||||
|
#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void * pvParameters )
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define portNOP() __asm volatile ( " nop " )
|
||||||
|
#define portINLINE __inline
|
||||||
|
|
||||||
|
#ifndef portFORCE_INLINE
|
||||||
|
#define portFORCE_INLINE inline __attribute__( ( always_inline ) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define portMEMORY_BARRIER() __asm volatile ( "" ::: "memory" )
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* configCLINT_BASE_ADDRESS is a legacy definition that was replaced by the
|
||||||
|
* configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS definitions. For
|
||||||
|
* backward compatibility derive the newer definitions from the old if the old
|
||||||
|
* definition is found. */
|
||||||
|
#if defined( configCLINT_BASE_ADDRESS ) && !defined( configMTIME_BASE_ADDRESS ) && ( configCLINT_BASE_ADDRESS == 0 )
|
||||||
|
|
||||||
|
/* Legacy case where configCLINT_BASE_ADDRESS was defined as 0 to indicate
|
||||||
|
* there was no CLINT. Equivalent now is to set the MTIME and MTIMECMP
|
||||||
|
* addresses to 0. */
|
||||||
|
#define configMTIME_BASE_ADDRESS ( 0 )
|
||||||
|
#define configMTIMECMP_BASE_ADDRESS ( 0 )
|
||||||
|
#elif defined( configCLINT_BASE_ADDRESS ) && !defined( configMTIME_BASE_ADDRESS )
|
||||||
|
|
||||||
|
/* Legacy case where configCLINT_BASE_ADDRESS was set to the base address of
|
||||||
|
* the CLINT. Equivalent now is to derive the MTIME and MTIMECMP addresses
|
||||||
|
* from the CLINT address. */
|
||||||
|
#define configMTIME_BASE_ADDRESS ( ( configCLINT_BASE_ADDRESS ) + 0xBFF8UL )
|
||||||
|
#define configMTIMECMP_BASE_ADDRESS ( ( configCLINT_BASE_ADDRESS ) + 0x4000UL )
|
||||||
|
#elif !defined( configMTIME_BASE_ADDRESS ) || !defined( configMTIMECMP_BASE_ADDRESS )
|
||||||
|
#error "configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS must be defined in FreeRTOSConfig.h. Set them to zero if there is no MTIME (machine time) clock. See www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html"
|
||||||
|
#endif /* if defined( configCLINT_BASE_ADDRESS ) && !defined( configMTIME_BASE_ADDRESS ) && ( configCLINT_BASE_ADDRESS == 0 ) */
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
#endif /* PORTMACRO_H */
|
||||||
@@ -0,0 +1,638 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V11.3.0
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A sample implementation of pvPortMalloc() and vPortFree() that combines
|
||||||
|
* (coalescences) adjacent memory blocks as they are freed, and in so doing
|
||||||
|
* limits memory fragmentation.
|
||||||
|
*
|
||||||
|
* See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the
|
||||||
|
* memory management pages of https://www.FreeRTOS.org for more information.
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
|
||||||
|
* all the API functions to use the MPU wrappers. That should only be done when
|
||||||
|
* task.h is included from an application file. */
|
||||||
|
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||||
|
|
||||||
|
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
|
||||||
|
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configHEAP_CLEAR_MEMORY_ON_FREE
|
||||||
|
#define configHEAP_CLEAR_MEMORY_ON_FREE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Block sizes must not get too small. */
|
||||||
|
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) )
|
||||||
|
|
||||||
|
/* Assumes 8bit bytes! */
|
||||||
|
#define heapBITS_PER_BYTE ( ( size_t ) 8 )
|
||||||
|
|
||||||
|
/* Max value that fits in a size_t type. */
|
||||||
|
#define heapSIZE_MAX ( ~( ( size_t ) 0 ) )
|
||||||
|
|
||||||
|
/* Check if multiplying a and b will result in overflow. */
|
||||||
|
#define heapMULTIPLY_WILL_OVERFLOW( a, b ) ( ( ( a ) > 0 ) && ( ( b ) > ( heapSIZE_MAX / ( a ) ) ) )
|
||||||
|
|
||||||
|
/* Check if adding a and b will result in overflow. */
|
||||||
|
#define heapADD_WILL_OVERFLOW( a, b ) ( ( a ) > ( heapSIZE_MAX - ( b ) ) )
|
||||||
|
|
||||||
|
/* Check if the subtraction operation ( a - b ) will result in underflow. */
|
||||||
|
#define heapSUBTRACT_WILL_UNDERFLOW( a, b ) ( ( a ) < ( b ) )
|
||||||
|
|
||||||
|
/* MSB of the xBlockSize member of an BlockLink_t structure is used to track
|
||||||
|
* the allocation status of a block. When MSB of the xBlockSize member of
|
||||||
|
* an BlockLink_t structure is set then the block belongs to the application.
|
||||||
|
* When the bit is free the block is still part of the free heap space. */
|
||||||
|
#define heapBLOCK_ALLOCATED_BITMASK ( ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ) )
|
||||||
|
#define heapBLOCK_SIZE_IS_VALID( xBlockSize ) ( ( ( xBlockSize ) & heapBLOCK_ALLOCATED_BITMASK ) == 0 )
|
||||||
|
#define heapBLOCK_IS_ALLOCATED( pxBlock ) ( ( ( pxBlock->xBlockSize ) & heapBLOCK_ALLOCATED_BITMASK ) != 0 )
|
||||||
|
#define heapALLOCATE_BLOCK( pxBlock ) ( ( pxBlock->xBlockSize ) |= heapBLOCK_ALLOCATED_BITMASK )
|
||||||
|
#define heapFREE_BLOCK( pxBlock ) ( ( pxBlock->xBlockSize ) &= ~heapBLOCK_ALLOCATED_BITMASK )
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Allocate the memory for the heap. */
|
||||||
|
#if ( configAPPLICATION_ALLOCATED_HEAP == 1 )
|
||||||
|
|
||||||
|
/* The application writer has already defined the array used for the RTOS
|
||||||
|
* heap - probably so it can be placed in a special segment or address. */
|
||||||
|
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
|
||||||
|
#else
|
||||||
|
PRIVILEGED_DATA static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
|
||||||
|
#endif /* configAPPLICATION_ALLOCATED_HEAP */
|
||||||
|
|
||||||
|
/* Define the linked list structure. This is used to link free blocks in order
|
||||||
|
* of their memory address. */
|
||||||
|
typedef struct A_BLOCK_LINK
|
||||||
|
{
|
||||||
|
struct A_BLOCK_LINK * pxNextFreeBlock; /**< The next free block in the list. */
|
||||||
|
size_t xBlockSize; /**< The size of the free block. */
|
||||||
|
} BlockLink_t;
|
||||||
|
|
||||||
|
/* Setting configENABLE_HEAP_PROTECTOR to 1 enables heap block pointers
|
||||||
|
* protection using an application supplied canary value to catch heap
|
||||||
|
* corruption should a heap buffer overflow occur.
|
||||||
|
*/
|
||||||
|
#if ( configENABLE_HEAP_PROTECTOR == 1 )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Application provided function to get a random value to be used as canary.
|
||||||
|
*
|
||||||
|
* @param pxHeapCanary [out] Output parameter to return the canary value.
|
||||||
|
*/
|
||||||
|
extern void vApplicationGetRandomHeapCanary( portPOINTER_SIZE_TYPE * pxHeapCanary );
|
||||||
|
|
||||||
|
/* Canary value for protecting internal heap pointers. */
|
||||||
|
PRIVILEGED_DATA static portPOINTER_SIZE_TYPE xHeapCanary;
|
||||||
|
|
||||||
|
/* Macro to load/store BlockLink_t pointers to memory. By XORing the
|
||||||
|
* pointers with a random canary value, heap overflows will result
|
||||||
|
* in randomly unpredictable pointer values which will be caught by
|
||||||
|
* heapVALIDATE_BLOCK_POINTER assert. */
|
||||||
|
#define heapPROTECT_BLOCK_POINTER( pxBlock ) ( ( BlockLink_t * ) ( ( ( portPOINTER_SIZE_TYPE ) ( pxBlock ) ) ^ xHeapCanary ) )
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define heapPROTECT_BLOCK_POINTER( pxBlock ) ( pxBlock )
|
||||||
|
|
||||||
|
#endif /* configENABLE_HEAP_PROTECTOR */
|
||||||
|
|
||||||
|
/* Assert that a heap block pointer is within the heap bounds. */
|
||||||
|
#define heapVALIDATE_BLOCK_POINTER( pxBlock ) \
|
||||||
|
configASSERT( ( ( uint8_t * ) ( pxBlock ) >= &( ucHeap[ 0 ] ) ) && \
|
||||||
|
( ( uint8_t * ) ( pxBlock ) <= &( ucHeap[ configTOTAL_HEAP_SIZE - 1 ] ) ) )
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inserts a block of memory that is being freed into the correct position in
|
||||||
|
* the list of free memory blocks. The block being freed will be merged with
|
||||||
|
* the block in front it and/or the block behind it if the memory blocks are
|
||||||
|
* adjacent to each other.
|
||||||
|
*/
|
||||||
|
static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called automatically to setup the required heap structures the first time
|
||||||
|
* pvPortMalloc() is called.
|
||||||
|
*/
|
||||||
|
static void prvHeapInit( void ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* The size of the structure placed at the beginning of each allocated memory
|
||||||
|
* block must by correctly byte aligned. */
|
||||||
|
static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
|
||||||
|
|
||||||
|
/* Create a couple of list links to mark the start and end of the list. */
|
||||||
|
PRIVILEGED_DATA static BlockLink_t xStart;
|
||||||
|
PRIVILEGED_DATA static BlockLink_t * pxEnd = NULL;
|
||||||
|
|
||||||
|
/* Keeps track of the number of calls to allocate and free memory as well as the
|
||||||
|
* number of free bytes remaining, but says nothing about fragmentation. */
|
||||||
|
PRIVILEGED_DATA static size_t xFreeBytesRemaining = ( size_t ) 0U;
|
||||||
|
PRIVILEGED_DATA static size_t xMinimumEverFreeBytesRemaining = ( size_t ) 0U;
|
||||||
|
PRIVILEGED_DATA static size_t xNumberOfSuccessfulAllocations = ( size_t ) 0U;
|
||||||
|
PRIVILEGED_DATA static size_t xNumberOfSuccessfulFrees = ( size_t ) 0U;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void * pvPortMalloc( size_t xWantedSize )
|
||||||
|
{
|
||||||
|
BlockLink_t * pxBlock;
|
||||||
|
BlockLink_t * pxPreviousBlock;
|
||||||
|
BlockLink_t * pxNewBlockLink;
|
||||||
|
void * pvReturn = NULL;
|
||||||
|
size_t xAdditionalRequiredSize;
|
||||||
|
size_t xAllocatedBlockSize = 0;
|
||||||
|
|
||||||
|
if( xWantedSize > 0 )
|
||||||
|
{
|
||||||
|
/* The wanted size must be increased so it can contain a BlockLink_t
|
||||||
|
* structure in addition to the requested amount of bytes. */
|
||||||
|
if( heapADD_WILL_OVERFLOW( xWantedSize, xHeapStructSize ) == 0 )
|
||||||
|
{
|
||||||
|
xWantedSize += xHeapStructSize;
|
||||||
|
|
||||||
|
/* Ensure that blocks are always aligned to the required number
|
||||||
|
* of bytes. */
|
||||||
|
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
|
||||||
|
{
|
||||||
|
/* Byte alignment required. */
|
||||||
|
xAdditionalRequiredSize = portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK );
|
||||||
|
|
||||||
|
if( heapADD_WILL_OVERFLOW( xWantedSize, xAdditionalRequiredSize ) == 0 )
|
||||||
|
{
|
||||||
|
xWantedSize += xAdditionalRequiredSize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xWantedSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xWantedSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskSuspendAll();
|
||||||
|
{
|
||||||
|
/* If this is the first call to malloc then the heap will require
|
||||||
|
* initialisation to setup the list of free blocks. */
|
||||||
|
if( pxEnd == NULL )
|
||||||
|
{
|
||||||
|
prvHeapInit();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the block size we are trying to allocate is not so large that the
|
||||||
|
* top bit is set. The top bit of the block size member of the BlockLink_t
|
||||||
|
* structure is used to determine who owns the block - the application or
|
||||||
|
* the kernel, so it must be free. */
|
||||||
|
if( heapBLOCK_SIZE_IS_VALID( xWantedSize ) != 0 )
|
||||||
|
{
|
||||||
|
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
|
||||||
|
{
|
||||||
|
/* Traverse the list from the start (lowest address) block until
|
||||||
|
* one of adequate size is found. */
|
||||||
|
pxPreviousBlock = &xStart;
|
||||||
|
pxBlock = heapPROTECT_BLOCK_POINTER( xStart.pxNextFreeBlock );
|
||||||
|
heapVALIDATE_BLOCK_POINTER( pxBlock );
|
||||||
|
|
||||||
|
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != heapPROTECT_BLOCK_POINTER( NULL ) ) )
|
||||||
|
{
|
||||||
|
pxPreviousBlock = pxBlock;
|
||||||
|
pxBlock = heapPROTECT_BLOCK_POINTER( pxBlock->pxNextFreeBlock );
|
||||||
|
heapVALIDATE_BLOCK_POINTER( pxBlock );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the end marker was reached then a block of adequate size
|
||||||
|
* was not found. */
|
||||||
|
if( pxBlock != pxEnd )
|
||||||
|
{
|
||||||
|
/* Return the memory space pointed to - jumping over the
|
||||||
|
* BlockLink_t structure at its start. */
|
||||||
|
pvReturn = ( void * ) ( ( ( uint8_t * ) heapPROTECT_BLOCK_POINTER( pxPreviousBlock->pxNextFreeBlock ) ) + xHeapStructSize );
|
||||||
|
heapVALIDATE_BLOCK_POINTER( pvReturn );
|
||||||
|
|
||||||
|
/* This block is being returned for use so must be taken out
|
||||||
|
* of the list of free blocks. */
|
||||||
|
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
|
||||||
|
|
||||||
|
/* If the block is larger than required it can be split into
|
||||||
|
* two. */
|
||||||
|
configASSERT( heapSUBTRACT_WILL_UNDERFLOW( pxBlock->xBlockSize, xWantedSize ) == 0 );
|
||||||
|
|
||||||
|
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
|
||||||
|
{
|
||||||
|
/* This block is to be split into two. Create a new
|
||||||
|
* block following the number of bytes requested. The void
|
||||||
|
* cast is used to prevent byte alignment warnings from the
|
||||||
|
* compiler. */
|
||||||
|
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
|
||||||
|
configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );
|
||||||
|
|
||||||
|
/* Calculate the sizes of two blocks split from the
|
||||||
|
* single block. */
|
||||||
|
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
|
||||||
|
pxBlock->xBlockSize = xWantedSize;
|
||||||
|
|
||||||
|
/* Insert the new block into the list of free blocks. */
|
||||||
|
pxNewBlockLink->pxNextFreeBlock = pxPreviousBlock->pxNextFreeBlock;
|
||||||
|
pxPreviousBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxNewBlockLink );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
xFreeBytesRemaining -= pxBlock->xBlockSize;
|
||||||
|
|
||||||
|
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
|
||||||
|
{
|
||||||
|
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
xAllocatedBlockSize = pxBlock->xBlockSize;
|
||||||
|
|
||||||
|
/* The block is being returned - it is allocated and owned
|
||||||
|
* by the application and has no "next" block. */
|
||||||
|
heapALLOCATE_BLOCK( pxBlock );
|
||||||
|
pxBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( NULL );
|
||||||
|
xNumberOfSuccessfulAllocations++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
traceMALLOC( pvReturn, xAllocatedBlockSize );
|
||||||
|
|
||||||
|
/* Prevent compiler warnings when trace macros are not used. */
|
||||||
|
( void ) xAllocatedBlockSize;
|
||||||
|
}
|
||||||
|
( void ) xTaskResumeAll();
|
||||||
|
|
||||||
|
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
|
||||||
|
{
|
||||||
|
if( pvReturn == NULL )
|
||||||
|
{
|
||||||
|
vApplicationMallocFailedHook();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */
|
||||||
|
|
||||||
|
configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
|
||||||
|
return pvReturn;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vPortFree( void * pv )
|
||||||
|
{
|
||||||
|
uint8_t * puc = ( uint8_t * ) pv;
|
||||||
|
BlockLink_t * pxLink;
|
||||||
|
|
||||||
|
if( pv != NULL )
|
||||||
|
{
|
||||||
|
/* The memory being freed will have an BlockLink_t structure immediately
|
||||||
|
* before it. */
|
||||||
|
puc -= xHeapStructSize;
|
||||||
|
|
||||||
|
/* This casting is to keep the compiler from issuing warnings. */
|
||||||
|
pxLink = ( void * ) puc;
|
||||||
|
|
||||||
|
heapVALIDATE_BLOCK_POINTER( pxLink );
|
||||||
|
configASSERT( heapBLOCK_IS_ALLOCATED( pxLink ) != 0 );
|
||||||
|
configASSERT( pxLink->pxNextFreeBlock == heapPROTECT_BLOCK_POINTER( NULL ) );
|
||||||
|
|
||||||
|
if( heapBLOCK_IS_ALLOCATED( pxLink ) != 0 )
|
||||||
|
{
|
||||||
|
if( pxLink->pxNextFreeBlock == heapPROTECT_BLOCK_POINTER( NULL ) )
|
||||||
|
{
|
||||||
|
/* The block is being returned to the heap - it is no longer
|
||||||
|
* allocated. */
|
||||||
|
heapFREE_BLOCK( pxLink );
|
||||||
|
#if ( configHEAP_CLEAR_MEMORY_ON_FREE == 1 )
|
||||||
|
{
|
||||||
|
/* Check for underflow as this can occur if xBlockSize is
|
||||||
|
* overwritten in a heap block. */
|
||||||
|
if( heapSUBTRACT_WILL_UNDERFLOW( pxLink->xBlockSize, xHeapStructSize ) == 0 )
|
||||||
|
{
|
||||||
|
( void ) memset( puc + xHeapStructSize, 0, pxLink->xBlockSize - xHeapStructSize );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vTaskSuspendAll();
|
||||||
|
{
|
||||||
|
/* Add this block to the list of free blocks. */
|
||||||
|
xFreeBytesRemaining += pxLink->xBlockSize;
|
||||||
|
traceFREE( pv, pxLink->xBlockSize );
|
||||||
|
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
|
||||||
|
xNumberOfSuccessfulFrees++;
|
||||||
|
}
|
||||||
|
( void ) xTaskResumeAll();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
size_t xPortGetFreeHeapSize( void )
|
||||||
|
{
|
||||||
|
return xFreeBytesRemaining;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
size_t xPortGetMinimumEverFreeHeapSize( void )
|
||||||
|
{
|
||||||
|
return xMinimumEverFreeBytesRemaining;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void xPortResetHeapMinimumEverFreeHeapSize( void )
|
||||||
|
{
|
||||||
|
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vPortInitialiseBlocks( void )
|
||||||
|
{
|
||||||
|
/* This just exists to keep the linker quiet. */
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void * pvPortCalloc( size_t xNum,
|
||||||
|
size_t xSize )
|
||||||
|
{
|
||||||
|
void * pv = NULL;
|
||||||
|
|
||||||
|
if( heapMULTIPLY_WILL_OVERFLOW( xNum, xSize ) == 0 )
|
||||||
|
{
|
||||||
|
pv = pvPortMalloc( xNum * xSize );
|
||||||
|
|
||||||
|
if( pv != NULL )
|
||||||
|
{
|
||||||
|
( void ) memset( pv, 0, xNum * xSize );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pv;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */
|
||||||
|
{
|
||||||
|
BlockLink_t * pxFirstFreeBlock;
|
||||||
|
portPOINTER_SIZE_TYPE uxStartAddress, uxEndAddress;
|
||||||
|
size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;
|
||||||
|
|
||||||
|
/* Ensure the heap starts on a correctly aligned boundary. */
|
||||||
|
uxStartAddress = ( portPOINTER_SIZE_TYPE ) ucHeap;
|
||||||
|
|
||||||
|
if( ( uxStartAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
|
||||||
|
{
|
||||||
|
uxStartAddress += ( portBYTE_ALIGNMENT - 1 );
|
||||||
|
uxStartAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK );
|
||||||
|
xTotalHeapSize -= ( size_t ) ( uxStartAddress - ( portPOINTER_SIZE_TYPE ) ucHeap );
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ( configENABLE_HEAP_PROTECTOR == 1 )
|
||||||
|
{
|
||||||
|
vApplicationGetRandomHeapCanary( &( xHeapCanary ) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* xStart is used to hold a pointer to the first item in the list of free
|
||||||
|
* blocks. The void cast is used to prevent compiler warnings. */
|
||||||
|
xStart.pxNextFreeBlock = ( void * ) heapPROTECT_BLOCK_POINTER( uxStartAddress );
|
||||||
|
xStart.xBlockSize = ( size_t ) 0;
|
||||||
|
|
||||||
|
/* pxEnd is used to mark the end of the list of free blocks and is inserted
|
||||||
|
* at the end of the heap space. */
|
||||||
|
uxEndAddress = uxStartAddress + ( portPOINTER_SIZE_TYPE ) xTotalHeapSize;
|
||||||
|
uxEndAddress -= ( portPOINTER_SIZE_TYPE ) xHeapStructSize;
|
||||||
|
uxEndAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK );
|
||||||
|
pxEnd = ( BlockLink_t * ) uxEndAddress;
|
||||||
|
pxEnd->xBlockSize = 0;
|
||||||
|
pxEnd->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( NULL );
|
||||||
|
|
||||||
|
/* To start with there is a single free block that is sized to take up the
|
||||||
|
* entire heap space, minus the space taken by pxEnd. */
|
||||||
|
pxFirstFreeBlock = ( BlockLink_t * ) uxStartAddress;
|
||||||
|
pxFirstFreeBlock->xBlockSize = ( size_t ) ( uxEndAddress - ( portPOINTER_SIZE_TYPE ) pxFirstFreeBlock );
|
||||||
|
pxFirstFreeBlock->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxEnd );
|
||||||
|
|
||||||
|
/* Only one block exists - and it covers the entire usable heap space. */
|
||||||
|
xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
|
||||||
|
xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVILEGED_FUNCTION */
|
||||||
|
{
|
||||||
|
BlockLink_t * pxIterator;
|
||||||
|
uint8_t * puc;
|
||||||
|
|
||||||
|
/* Iterate through the list until a block is found that has a higher address
|
||||||
|
* than the block being inserted. */
|
||||||
|
for( pxIterator = &xStart; heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) < pxBlockToInsert; pxIterator = heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) )
|
||||||
|
{
|
||||||
|
/* Nothing to do here, just iterate to the right position. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pxIterator != &xStart )
|
||||||
|
{
|
||||||
|
heapVALIDATE_BLOCK_POINTER( pxIterator );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do the block being inserted, and the block it is being inserted after
|
||||||
|
* make a contiguous block of memory? */
|
||||||
|
puc = ( uint8_t * ) pxIterator;
|
||||||
|
|
||||||
|
if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
|
||||||
|
{
|
||||||
|
pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
|
||||||
|
pxBlockToInsert = pxIterator;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do the block being inserted, and the block it is being inserted before
|
||||||
|
* make a contiguous block of memory? */
|
||||||
|
puc = ( uint8_t * ) pxBlockToInsert;
|
||||||
|
|
||||||
|
if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) )
|
||||||
|
{
|
||||||
|
if( heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock ) != pxEnd )
|
||||||
|
{
|
||||||
|
/* Form one big block from the two blocks. */
|
||||||
|
pxBlockToInsert->xBlockSize += heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock )->xBlockSize;
|
||||||
|
pxBlockToInsert->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxIterator->pxNextFreeBlock )->pxNextFreeBlock;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pxBlockToInsert->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxEnd );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the block being inserted plugged a gap, so was merged with the block
|
||||||
|
* before and the block after, then it's pxNextFreeBlock pointer will have
|
||||||
|
* already been set, and should not be set here as that would make it point
|
||||||
|
* to itself. */
|
||||||
|
if( pxIterator != pxBlockToInsert )
|
||||||
|
{
|
||||||
|
pxIterator->pxNextFreeBlock = heapPROTECT_BLOCK_POINTER( pxBlockToInsert );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vPortGetHeapStats( HeapStats_t * pxHeapStats )
|
||||||
|
{
|
||||||
|
BlockLink_t * pxBlock;
|
||||||
|
size_t xBlocks = 0, xMaxSize = 0, xMinSize = SIZE_MAX;
|
||||||
|
|
||||||
|
vTaskSuspendAll();
|
||||||
|
{
|
||||||
|
pxBlock = heapPROTECT_BLOCK_POINTER( xStart.pxNextFreeBlock );
|
||||||
|
|
||||||
|
/* pxBlock will be NULL if the heap has not been initialised. The heap
|
||||||
|
* is initialised automatically when the first allocation is made. */
|
||||||
|
if( pxBlock != NULL )
|
||||||
|
{
|
||||||
|
while( pxBlock != pxEnd )
|
||||||
|
{
|
||||||
|
/* Increment the number of blocks and record the largest block seen
|
||||||
|
* so far. */
|
||||||
|
xBlocks++;
|
||||||
|
|
||||||
|
if( pxBlock->xBlockSize > xMaxSize )
|
||||||
|
{
|
||||||
|
xMaxSize = pxBlock->xBlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pxBlock->xBlockSize < xMinSize )
|
||||||
|
{
|
||||||
|
xMinSize = pxBlock->xBlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move to the next block in the chain until the last block is
|
||||||
|
* reached. */
|
||||||
|
pxBlock = heapPROTECT_BLOCK_POINTER( pxBlock->pxNextFreeBlock );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
( void ) xTaskResumeAll();
|
||||||
|
|
||||||
|
pxHeapStats->xSizeOfLargestFreeBlockInBytes = xMaxSize;
|
||||||
|
pxHeapStats->xSizeOfSmallestFreeBlockInBytes = xMinSize;
|
||||||
|
pxHeapStats->xNumberOfFreeBlocks = xBlocks;
|
||||||
|
|
||||||
|
taskENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
pxHeapStats->xAvailableHeapSpaceInBytes = xFreeBytesRemaining;
|
||||||
|
pxHeapStats->xNumberOfSuccessfulAllocations = xNumberOfSuccessfulAllocations;
|
||||||
|
pxHeapStats->xNumberOfSuccessfulFrees = xNumberOfSuccessfulFrees;
|
||||||
|
pxHeapStats->xMinimumEverFreeBytesRemaining = xMinimumEverFreeBytesRemaining;
|
||||||
|
}
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset the state in this file. This state is normally initialized at start up.
|
||||||
|
* This function must be called by the application before restarting the
|
||||||
|
* scheduler.
|
||||||
|
*/
|
||||||
|
void vPortHeapResetState( void )
|
||||||
|
{
|
||||||
|
pxEnd = NULL;
|
||||||
|
|
||||||
|
xFreeBytesRemaining = ( size_t ) 0U;
|
||||||
|
xMinimumEverFreeBytesRemaining = ( size_t ) 0U;
|
||||||
|
xNumberOfSuccessfulAllocations = ( size_t ) 0U;
|
||||||
|
xNumberOfSuccessfulFrees = ( size_t ) 0U;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
@@ -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
|
||||||