commit 1d44b1c8c6d6bd5f89771c3b99e7a43e176ffe90 Author: M. Pabiszczak <2+mpabi@noreply.zsl-gitea.mpabi.pl> Date: Sun Jul 19 16:36:03 2026 +0200 feat: publish FreeRTOS C FC09 card diff --git a/.gitea/workflows/render-pdf.yml b/.gitea/workflows/render-pdf.yml new file mode 100644 index 0000000..2362972 --- /dev/null +++ b/.gitea/workflows/render-pdf.yml @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..115dfd9 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..38959f4 --- /dev/null +++ b/Makefile @@ -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 + +H3_COMMON := vendor/Hazard3/test/sim/common +H3_INIT := $(H3_COMMON)/init.S +LDSCRIPT := $(H3_COMMON)/link_hazard3.ld +TB ?= vendor/Hazard3/test/sim/tb_verilator/tb +FREERTOS := vendor/FreeRTOS-Kernel +FREERTOS_PORT := $(FREERTOS)/portable/GCC/RISC-V +BUILD := build +TASK := task01_event_groups + +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 + +COMMON_OBJS := \ + $(BUILD)/common/init.o \ + $(BUILD)/common/crt0.o \ + $(BUILD)/common/lab_io.o \ + $(BUILD)/common/lab_freertos.o \ + $(BUILD)/common/hazard3_freertos_traps.o \ + $(BUILD)/common/memops.o +KERNEL_OBJS := \ + $(BUILD)/kernel/tasks.o \ + $(BUILD)/kernel/list.o \ + $(BUILD)/kernel/queue.o \ + $(BUILD)/kernel/event_groups.o \ + $(BUILD)/kernel/heap_4.o \ + $(BUILD)/kernel/port.o \ + $(BUILD)/kernel/portASM.o + +.PHONY: all tasks task1 t1 host-test check-abi sim check-determinism check pdf clean + +all tasks task1 t1: $(BUILD)/$(TASK)/prog.bin + +$(BUILD)/common/init.o: $(H3_INIT) + mkdir -p $(@D) + $(CC) $(ASFLAGS) -c $< -o $@ +$(BUILD)/common/crt0.o: src/common/crt0.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/hazard3_freertos_traps.o: src/common/hazard3_freertos_traps.S + mkdir -p $(@D) + $(CC) $(ASFLAGS) -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/queue.o: $(FREERTOS)/queue.c + mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ +$(BUILD)/kernel/event_groups.o: $(FREERTOS)/event_groups.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)/$(TASK)/app.o: src/tasks/$(TASK).c include/$(TASK).h include/$(TASK)_model.h + mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ +$(BUILD)/$(TASK)/$(TASK).s: src/tasks/$(TASK).c include/$(TASK).h include/$(TASK)_model.h + mkdir -p $(@D) + $(CC) $(CFLAGS) -S $< -o $@ +$(BUILD)/$(TASK)/prog.elf: $(COMMON_OBJS) $(KERNEL_OBJS) $(BUILD)/$(TASK)/app.o + $(CC) $(LDFLAGS) -Wl,-Map,$(BUILD)/$(TASK)/prog.map -o $@ $^ $(LIBS) + $(SIZE) -A -x $@ +$(BUILD)/$(TASK)/prog.bin: $(BUILD)/$(TASK)/prog.elf $(BUILD)/$(TASK)/$(TASK).s + $(OBJCOPY) -O binary $< $@ + $(OBJDUMP) -d -M no-aliases,numeric $< > $(BUILD)/$(TASK)/prog.lst + +host-test: + mkdir -p $(BUILD)/host + $(HOSTCC) -std=c11 -Wall -Wextra -Werror -Iinclude tests/event_groups_model_host.c -o $(BUILD)/host/event_groups_model_host + $(BUILD)/host/event_groups_model_host + +check-abi: $(BUILD)/$(TASK)/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: + ./scripts/render_pdf.sh + +clean: + rm -rf $(BUILD) diff --git a/README.md b/README.md new file mode 100644 index 0000000..4152685 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# FC09 — Event Groups and multi-event synchronization + +This deterministic card implements two distinct protocols on one genuine +FreeRTOS EventGroup: + +1. a priority-3 coordinator waits for **ALL** independent readiness bits; +2. coordinator, worker A and worker B cross a three-party + `xEventGroupSync()` barrier. + +READY (`0x01`, `0x02`) and SYNC (`0x10`, `0x20`, `0x40`) masks are disjoint. +The partial mask cannot release either protocol. Every participant receives a +return value containing `SYNC_ALL`, while the group value becomes zero after +the barrier's automatic clear. + +```sh +make task1 host-test check-abi sim check-determinism +``` + +The test links upstream FreeRTOS V11.3.0 `event_groups.c`, uses finite +timeouts, records eleven commit-last events and prints a stable digest from +three clean Hazard3 processes. + +Interactive views: A2 structure, A4 participant topology, A5 execution flow, +A6 mask/state machine, A7 runtime evidence and A8 synchronization patterns. + +Source basis: FreeRTOS Kernel Book, Chapter 9. diff --git a/doc/assets/a2-structure.png b/doc/assets/a2-structure.png new file mode 100644 index 0000000..1737b42 Binary files /dev/null and b/doc/assets/a2-structure.png differ diff --git a/doc/assets/a2-structure.puml b/doc/assets/a2-structure.puml new file mode 100644 index 0000000..c7c61b3 --- /dev/null +++ b/doc/assets/a2-structure.puml @@ -0,0 +1,20 @@ +@startuml +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 12 +skinparam rectangleBorderColor #2c7794 +skinparam rectangleBackgroundColor #edf6fa +title A2 STRUCTURE — one EventGroup and disjoint masks +rectangle "01 EventGroup\nEventBits_t value" as group +rectangle "02 READY mask\nA=0x01 B=0x02\nALL=0x03" as ready +rectangle "03 SYNC mask\nA=0x10 B=0x20 C=0x40\nALL=0x70" as sync +rectangle "04 coordinator p3\nwait ALL-ready\nsync C" as coordinator +rectangle "05 worker A p2\nset READY_A\nsync A" as a +rectangle "06 worker B p1\nset READY_B\nsync B" as b +ready --> group +sync --> group +coordinator --> group +a --> group +b --> group +@enduml diff --git a/doc/assets/a2-structure.svg b/doc/assets/a2-structure.svg new file mode 100644 index 0000000..c6c9714 --- /dev/null +++ b/doc/assets/a2-structure.svg @@ -0,0 +1,43 @@ +A2 STRUCTURE — one EventGroup and disjoint masks01 EventGroupEventBits_t value02 READY maskA=0x01 B=0x02ALL=0x0303 SYNC maskA=0x10 B=0x20 C=0x40ALL=0x7004 coordinator p3wait ALL-readysync C05 worker A p2set READY_Async A06 worker B p1set READY_Bsync B \ No newline at end of file diff --git a/doc/assets/a4-application.png b/doc/assets/a4-application.png new file mode 100644 index 0000000..9d40fcd Binary files /dev/null and b/doc/assets/a4-application.png differ diff --git a/doc/assets/a4-application.puml b/doc/assets/a4-application.puml new file mode 100644 index 0000000..7e87593 --- /dev/null +++ b/doc/assets/a4-application.puml @@ -0,0 +1,20 @@ +@startuml +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 12 +skinparam rectangleBorderColor #2c7794 +skinparam rectangleBackgroundColor #edf6fa +title A4 APPLICATION — deterministic participant topology +rectangle "01 coordinator p3\nBlocked: wait READY_ALL" as c +rectangle "02 worker A p2\nREADY_A then Blocked at barrier" as a +rectangle "03 worker B p1\nobserves partial mask\nsets final READY_B" as b +rectangle "04 coordinator\npreempts B\ncontributes SYNC_C" as cp +rectangle "05 worker B\ncontributes final SYNC_B" as bf +rectangle "06 verifier p0\nchecks masks, states, stacks, order" as v +c --> a +a --> b +b --> cp +cp --> bf +bf --> v +@enduml diff --git a/doc/assets/a4-application.svg b/doc/assets/a4-application.svg new file mode 100644 index 0000000..ea417e0 --- /dev/null +++ b/doc/assets/a4-application.svg @@ -0,0 +1,43 @@ +A4 APPLICATION — deterministic participant topology01 coordinator p3Blocked: wait READY_ALL02 worker A p2READY_A then Blocked at barrier03 worker B p1observes partial masksets final READY_B04 coordinatorpreempts Bcontributes SYNC_C05 worker Bcontributes final SYNC_B06 verifier p0checks masks, states, stacks, order \ No newline at end of file diff --git a/doc/assets/a5-flow.png b/doc/assets/a5-flow.png new file mode 100644 index 0000000..7fde842 Binary files /dev/null and b/doc/assets/a5-flow.png differ diff --git a/doc/assets/a5-flow.puml b/doc/assets/a5-flow.puml new file mode 100644 index 0000000..ee7adfd --- /dev/null +++ b/doc/assets/a5-flow.puml @@ -0,0 +1,29 @@ +@startuml +scale 520 height +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 11 +skinparam sequenceArrowColor #8095a1 +skinparam sequenceLifeLineBorderColor #8095a1 +title A5 FLOW — ALL-ready wait and reusable barrier +participant Coordinator as C +participant "Worker A" as A +participant "Worker B" as B +participant EventGroup as G +participant Verifier as V +C -> G : 02 wait READY_ALL; block +A -> G : 03 set READY_A +A -> G : 04 sync(SYNC_A); block +B -> B : 05 observe READY_A|SYNC_A; A Blocked +B -> G : set READY_B +G --> C : 06 READY_ALL; clear READY bits +C -> G : sync(SYNC_C); block +B -> B : 07 coordinator Blocked +B -> G : sync(SYNC_B) = final bit +G --> C : 08 SYNC_ALL +G --> A : 09 SYNC_ALL +G --> B : 10 SYNC_ALL; clear sync bits +B -> V : workers complete +V -> G : 11 final bits = 0; PASS +@enduml diff --git a/doc/assets/a5-flow.svg b/doc/assets/a5-flow.svg new file mode 100644 index 0000000..c823d98 --- /dev/null +++ b/doc/assets/a5-flow.svg @@ -0,0 +1,41 @@ +A5 FLOW — ALL-ready wait and reusable barrierCoordinatorCoordinatorWorker AWorker AWorker BWorker BEventGroupEventGroupVerifierVerifier02 wait READY_ALL; block03 set READY_A04 sync(SYNC_A); block05 observe READY_A|SYNC_A; A Blockedset READY_B06 READY_ALL; clear READY bitssync(SYNC_C); block07 coordinator Blockedsync(SYNC_B) = final bit08 SYNC_ALL09 SYNC_ALL10 SYNC_ALL; clear sync bitsworkers complete11 final bits = 0; PASS \ No newline at end of file diff --git a/doc/assets/a6-state.png b/doc/assets/a6-state.png new file mode 100644 index 0000000..ce3f332 Binary files /dev/null and b/doc/assets/a6-state.png differ diff --git a/doc/assets/a6-state.puml b/doc/assets/a6-state.puml new file mode 100644 index 0000000..5649de7 --- /dev/null +++ b/doc/assets/a6-state.puml @@ -0,0 +1,24 @@ +@startuml +scale 700 height +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 12 +skinparam stateBorderColor #2c7794 +skinparam stateBackgroundColor #edf6fa +title A6 STATE — bit mask and blocked participants +[*] --> Zero : 01 0x00 +Zero --> ReadyA : 03 set 0x01 +ReadyA --> Partial : 04 sync A +Partial : bits 0x11 +Partial : coordinator Blocked +Partial : worker A Blocked +Partial --> ReadyAll : set READY_B +ReadyAll : return 0x03 to coordinator +ReadyAll --> BarrierAC : clear READY; set SYNC_C +BarrierAC : bits 0x50 +BarrierAC --> Release : 10 set SYNC_B +Release : all return 0x70 +Release --> Zero : auto-clear sync mask +Zero --> [*] : 11 PASS +@enduml diff --git a/doc/assets/a6-state.svg b/doc/assets/a6-state.svg new file mode 100644 index 0000000..e7e8e54 --- /dev/null +++ b/doc/assets/a6-state.svg @@ -0,0 +1,44 @@ +A6 STATE — bit mask and blocked participantsZeroReadyAPartialbits 0x11coordinator Blockedworker A BlockedReadyAllreturn 0x03 to coordinatorBarrierACbits 0x50Releaseall return 0x7001 0x0003 set 0x0104 sync Aset READY_Bclear READY; set SYNC_C10 set SYNC_Bauto-clear sync mask11 PASS \ No newline at end of file diff --git a/doc/assets/a7-runtime.png b/doc/assets/a7-runtime.png new file mode 100644 index 0000000..a034852 Binary files /dev/null and b/doc/assets/a7-runtime.png differ diff --git a/doc/assets/a7-runtime.puml b/doc/assets/a7-runtime.puml new file mode 100644 index 0000000..f7b3626 --- /dev/null +++ b/doc/assets/a7-runtime.puml @@ -0,0 +1,22 @@ +@startuml +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 12 +skinparam rectangleBorderColor #2c7794 +skinparam rectangleBackgroundColor #edf6fa +title A7 RUNTIME — event value, wait lists and task stacks +rectangle "01 g_fc09.group\nreal EventGroup handle" as group +rectangle "02 event value\n0x00 → 0x01 → 0x11\n→ 0x70 → 0x00" as bits +rectangle "03 coordinator TCB/stack\nwait mask 0x03\nthen 0x70" as c +rectangle "04 worker A TCB/stack\nbarrier waiter" as a +rectangle "05 worker B TCB/stack\nfinal bit setter" as b +rectangle "06 g_fc09_events[11]\ncommit-last records\ndigest 2391788d" as log +group --> bits +group --> c +group --> a +group --> b +c --> log +a --> log +b --> log +@enduml diff --git a/doc/assets/a7-runtime.svg b/doc/assets/a7-runtime.svg new file mode 100644 index 0000000..312fe71 --- /dev/null +++ b/doc/assets/a7-runtime.svg @@ -0,0 +1,47 @@ +A7 RUNTIME — event value, wait lists and task stacks01 g_fc09.groupreal EventGroup handle02 event value0x00 → 0x01 → 0x11→ 0x70 → 0x0003 coordinator TCB/stackwait mask 0x03then 0x7004 worker A TCB/stackbarrier waiter05 worker B TCB/stackfinal bit setter06 g_fc09_events[11]commit-last recordsdigest 2391788d \ No newline at end of file diff --git a/doc/assets/a8-patterns.png b/doc/assets/a8-patterns.png new file mode 100644 index 0000000..9578b47 Binary files /dev/null and b/doc/assets/a8-patterns.png differ diff --git a/doc/assets/a8-patterns.puml b/doc/assets/a8-patterns.puml new file mode 100644 index 0000000..5dd1200 --- /dev/null +++ b/doc/assets/a8-patterns.puml @@ -0,0 +1,20 @@ +@startuml +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 12 +skinparam rectangleBorderColor #2c7794 +skinparam rectangleBackgroundColor #edf6fa +title A8 PATTERNS — event latch and reusable barrier +rectangle "01 ANY wait\nrelease on first selected bit" as any +rectangle "02 ALL wait\nrelease on complete mask" as all +rectangle "03 clear-on-exit\nconsume readiness epoch" as clear +rectangle "04 xEventGroupSync\nset own bit + wait ALL\nautomatic barrier clear" as barrier +any -[hidden]right- all +all --> clear +clear --> barrier +note bottom of barrier + FC09 uses disjoint READY and SYNC masks. + Reserved control bits are never selected. +end note +@enduml diff --git a/doc/assets/a8-patterns.svg b/doc/assets/a8-patterns.svg new file mode 100644 index 0000000..208692f --- /dev/null +++ b/doc/assets/a8-patterns.svg @@ -0,0 +1,39 @@ +A8 PATTERNS — event latch and reusable barrier01 ANY waitrelease on first selected bit02 ALL waitrelease on complete mask03 clear-on-exitconsume readiness epoch04 xEventGroupSyncset own bit + wait ALLautomatic barrier clearFC09 uses disjoint READY and SYNC masks.Reserved control bits are never selected. \ No newline at end of file diff --git a/doc/card-plan.md b/doc/card-plan.md new file mode 100644 index 0000000..9f81496 --- /dev/null +++ b/doc/card-plan.md @@ -0,0 +1,21 @@ +# FC09 card plan — Event Groups + +## Thesis + +An EventGroup is a bit protocol, not a counter. ALL/ANY, clear-on-exit and +barrier auto-clear are part of the protocol and must be visible in evidence. + +```text +FC09 · Event Groups +└── Task01 · READY latch + three-party barrier + ├── A2/A4 · disjoint masks and participants + ├── A5 · E01–E11 deterministic flow + ├── A6 · bit value and blocked-task states + ├── A7 · handle, wait lists, three stacks and digest + ├── A8 · ANY/ALL/clear/sync decisions + └── Exercise · compare ANY and ALL on the same trace +``` + +Acceptance requires a real EventGroup, finite timeouts, coordinator blocked on +the partial READY mask, worker A blocked at sync, all three `xEventGroupSync` +results containing `0x70`, final bits zero and deterministic E01–E11. diff --git a/doc/generated/main.tex b/doc/generated/main.tex new file mode 100644 index 0000000..ae02718 --- /dev/null +++ b/doc/generated/main.tex @@ -0,0 +1,333 @@ +% 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}{09} +\newcommand{\CardCount}{15} +\newcommand{\CardSlug}{event-groups} +\newcommand{\CardVersion}{v00.01} +\newcommand{\DocumentUUID}{e5c4c0f3-3dd0-5168-9945-14d48744ec6f} + +\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-event-groups}% + \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 Event Groups: ALL-ready i bariera}\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-19T00: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 09/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 e5c4c0f3-3dd0-5168-9945-14d48744ec6f}\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 3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f}\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-event-groups}{\nolinkurl{https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-c-event-groups}}}} \\% + \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/3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f}{\nolinkurl{https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f}}}} \\% + \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/3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f}% + \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~RTOS}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.09}{Uczeń pokazuje maskę częściową, dwa Blocked states, trzy wyniki 0x70, final zero, trzy SP i\textCR digest.}}\par% + \hspace*{0.54em}\textcolor{blue!70!black}{KW~LOCAL~RTOS}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.09}{Łączy kod, diagram i zweryfikowany dowód.}}\par% + \end{minipage}% + }% + \end{minipage}% + ]{} + \normalmarginpar + + \marginnote{% + \begin{minipage}{\marginparwidth}% + \raggedright + {\fontsize{3.55}{3.95}\selectfont\ttfamily + \hspace*{0.06cm}% + \begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}% + \raggedright + {\bfseries\textcolor{black!65}{OG}\par}% + \vspace{0.08em}% + \hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par% + \hspace*{0.28em}\textcolor{green!50!black}{EN~LOCAL~EN~RTOS}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.09}{Uczeń przypisuje właściciela i znaczenie każdemu bitowi oraz rozdziela wait ALL od barrier\textCR sync.}}\par% + \hspace*{0.54em}\textcolor{blue!70!black}{KW~LOCAL~KW~RTOS}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.09}{Łączy kod, diagram i zweryfikowany dowód.}}\par% + \end{minipage}% + }% + \end{minipage}% + } + +} + +\begin{document} +\sloppy + +\vspace{1.0em} +\noindent{\Large\bfseries Cel karty}\par +\vspace{0.35em} +Uczeń projektuje rozłączne maski EventGroup i dowodzi różnicy między oczekiwaniem ALL a trzyosobową barierą. + +\vspace{0.8em} +\noindent\textcolor{black!25}{\rule{\textwidth}{0.35pt}} +\vspace{0.7em} +\noindent{\Large\bfseries Zakres karty}\par +\vspace{0.35em} +Koordynator p3 blokuje się na READY_ALL. Worker A ustawia READY_A i blokuje się w xEventGroupSync. Worker B potwierdza maskę częściową, ustawia READY_B, po czym koordynator wnosi SYNC_C. Ostatni SYNC_B zwalnia wszystkich; każdy wynik zawiera 0x70, a automatycznie wyczyszczona grupa kończy z 0. +\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{FC09}}} & \multicolumn{1}{l|}{\textbf{\texttt{Task01}}} & \multicolumn{1}{l|}{\textbf{bit masks, wait semantics, automatic clear i xEventGroupSync}} & \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 + +\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.79149\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a2-structure.png}} + \caption{A2 STRUCTURE — READY and SYNC masks. — część 1/1 (r1 c1).} + \label{fig:a2-structure} +\end{figure} +\ESCSectionBlockEnd +\end{landscape} + +\clearpage +\ESCSectionBlockStart +\noindent{\small\bfseries A4 — Application \hfill część 1/1 \textperiodcentered\ r1 c1}\par +\vspace{0.35em} +\begin{figure}[H] + \centering + \includegraphics[width=0.47576\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a4-application.png}} + \caption{A4 APPLICATION — deterministic release topology. — część 1/1 (r1 c1).} + \label{fig:a4-application} +\end{figure} +\ESCSectionBlockEnd + +\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.60851\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a5-flow.png}} + \caption{A5 FLOW — ALL-ready followed by xEventGroupSync. — część 1/1 (r1 c1).} + \label{fig:a5-flow} +\end{figure} +\ESCSectionBlockEnd +\end{landscape} + +\clearpage +\ESCSectionBlockStart +\noindent{\small\bfseries A6 — State \hfill część 1/1 \textperiodcentered\ r1 c1}\par +\vspace{0.35em} +\begin{figure}[H] + \centering + \includegraphics[width=0.47121\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a6-state.png}} + \caption{A6 STATE — zero, partial, ready and barrier release. — część 1/1 (r1 c1).} + \label{fig:a6-state} +\end{figure} +\ESCSectionBlockEnd + +\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.75319\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a7-runtime.png}} + \caption{A7 RUNTIME — event value and waiting TCBs. — część 1/1 (r1 c1).} + \label{fig:a7-runtime} +\end{figure} +\ESCSectionBlockEnd +\end{landscape} + +\clearpage +\begin{landscape} +\ESCSectionBlockStart +\noindent{\small\bfseries A8 — Patterns \hfill część 1/1 \textperiodcentered\ r1 c1}\par +\vspace{0.35em} +\begin{figure}[H] + \centering + \includegraphics[width=0.46596\linewidth,trim=0.000bp 0.000bp 0.000bp 0.000bp,clip]{\detokenize{../assets/a8-patterns.png}} + \caption{A8 PATTERNS — ANY, ALL, consume and barrier. — część 1/1 (r1 c1).} + \label{fig:a8-patterns} +\end{figure} +\ESCSectionBlockEnd +\end{landscape} + +\clearpage +\ESCSectionBlockStart +\section{Task01 — EventGroup} + +\begin{ESCTaskFrame}{TASK01 · ALL-ready i trzyosobowa bariera} +{\scriptsize\ttfamily 3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f}\par +\begin{ESCBlockFrame}{A — mask table} +Przypisz READY i SYNC do właścicieli. +\begin{ESCStepFrame}{partial · Przewidź 0x11.} +Wskaż, które taski muszą być Blocked. +\end{ESCStepFrame} +\begin{ESCStepFrame}{clear · Przewidź final zero.} +Rozdziel clear-on-exit od barrier auto-clear. +\end{ESCStepFrame} +\end{ESCBlockFrame} +\begin{ESCBlockFrame}{B — replay Hazard3} +RUN zaczyna się od czystej RAM. +\begin{ESCStepFrame}{ready · Zbadaj E02–E06.} +Porównaj maski i task states. +\end{ESCStepFrame} +\begin{ESCStepFrame}{barrier · Zbadaj E07–E10.} +Porównaj trzy wartości zwrotne. +\end{ESCStepFrame} +\begin{ESCStepFrame}{pass · Zbadaj E11.} +Zapisz final bits, SP, PASS i digest. +\end{ESCStepFrame} +\end{ESCBlockFrame} +\begin{ESCBlockFrame}{Ćwiczenie · Ćwiczenie — ANY kontra ALL} +Uruchom kontrolowany wariant wait ANY na tej samej sekwencji READY_A/READY_B. Zachowaj osobne maski i porównaj moment release oraz wartość zwrotną. +\par\textbf{Evidence:} Dwa timestampowane ślady i tabela semantyki clear-on-exit. +\par\textbf{Acceptance:} Uczeń nie przypisuje API gwarancji kolejności równorzędnych waiterów. +\end{ESCBlockFrame} +\tcblower\textbf{Task acceptance:} PASS; partial=0x11; READY result contains 0x03; all SYNC results contain 0x70; final=0; deterministic digest. +\end{ESCTaskFrame} +\begin{ESCConclusionFrame} +EventGroup przechowuje bity stanu protokołu, nie historię zdarzeń. Rozłączne maski i jawne reguły clear są warunkiem poprawnej synchronizacji. +\end{ESCConclusionFrame} +\ESCSectionBlockEnd + +\end{document} diff --git a/doc/page-layout.md b/doc/page-layout.md new file mode 100644 index 0000000..239e979 --- /dev/null +++ b/doc/page-layout.md @@ -0,0 +1,12 @@ +# FC09 page layout + +1. Goal, mask table, prediction and acceptance criterion. +2. A2 STRUCTURE — landscape. +3. A4 APPLICATION — portrait. +4. A5 FLOW — landscape. +5. A6 STATE — portrait. +6. A7 RUNTIME — landscape. +7. A8 PATTERNS and exercise — landscape. + +Every diagram starts on a new page. RUN replays from clean RAM; CODE only +selects source or the protocol table. diff --git a/include/FreeRTOSConfig.h b/include/FreeRTOSConfig.h new file mode 100644 index 0000000..b1d63bb --- /dev/null +++ b/include/FreeRTOSConfig.h @@ -0,0 +1,65 @@ +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +#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 0 +#define configMAX_PRIORITIES 5 +#define configMINIMAL_STACK_SIZE 128 +#define configMAX_TASK_NAME_LEN 16 +#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 1 +#define INCLUDE_vTaskDelayUntil 0 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 0 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskPrioritySet 0 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xSemaphoreGetMutexHolder 0 + +#define configENABLE_FPU 0 +#define configENABLE_VPU 0 +#define configISR_STACK_SIZE_WORDS 128 + +#ifndef __ASSEMBLER__ +void lab_assert_fail( const char * file, int line ); +#define configASSERT( condition ) \ + do { if( !( condition ) ) { lab_assert_fail( __FILE__, __LINE__ ); } } while( 0 ) +#else +#define configASSERT( condition ) +#endif + +#endif diff --git a/include/freertos_risc_v_chip_specific_extensions.h b/include/freertos_risc_v_chip_specific_extensions.h new file mode 100644 index 0000000..5395250 --- /dev/null +++ b/include/freertos_risc_v_chip_specific_extensions.h @@ -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 diff --git a/include/stdlib.h b/include/stdlib.h new file mode 100644 index 0000000..5656725 --- /dev/null +++ b/include/stdlib.h @@ -0,0 +1,11 @@ +#ifndef LAB_FREESTANDING_STDLIB_H +#define LAB_FREESTANDING_STDLIB_H + +/* + * The selected FreeRTOS sources include for standard types, but the + * configuration used by this card does not call hosted stdlib functions. + * GCC's freestanding headers provide size_t through . + */ +#include + +#endif diff --git a/include/string.h b/include/string.h new file mode 100644 index 0000000..21ccce1 --- /dev/null +++ b/include/string.h @@ -0,0 +1,17 @@ +#ifndef LAB_FREESTANDING_STRING_H +#define LAB_FREESTANDING_STRING_H + +#include + +#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 diff --git a/include/task01_event_groups.h b/include/task01_event_groups.h new file mode 100644 index 0000000..50d2b33 --- /dev/null +++ b/include/task01_event_groups.h @@ -0,0 +1,63 @@ +#ifndef TASK01_EVENT_GROUPS_H +#define TASK01_EVENT_GROUPS_H + +#include + +#include "FreeRTOS.h" +#include "event_groups.h" +#include "task.h" +#include "task01_event_groups_model.h" + +#define FC09_READY_A ( ( EventBits_t ) 0x01U ) +#define FC09_READY_B ( ( EventBits_t ) 0x02U ) +#define FC09_READY_ALL ( FC09_READY_A | FC09_READY_B ) +#define FC09_SYNC_A ( ( EventBits_t ) 0x10U ) +#define FC09_SYNC_B ( ( EventBits_t ) 0x20U ) +#define FC09_SYNC_C ( ( EventBits_t ) 0x40U ) +#define FC09_SYNC_ALL ( FC09_SYNC_A | FC09_SYNC_B | FC09_SYNC_C ) +#define FC09_TASK_STACK_WORDS 256U +#define FC09_COORDINATOR_PRIORITY 3U +#define FC09_WORKER_A_PRIORITY 2U +#define FC09_WORKER_B_PRIORITY 1U +#define FC09_VERIFIER_PRIORITY 0U +#define FC09_TIMEOUT_TICKS 20U +#define FC09_EVENT_CAPACITY 12U + +typedef struct EventGroupExperiment +{ + EventGroupHandle_t group; + TaskHandle_t coordinator_handle; + TaskHandle_t worker_a_handle; + TaskHandle_t worker_b_handle; + TaskHandle_t verifier_handle; + EventBits_t ready_result; + EventBits_t sync_a_result; + EventBits_t sync_b_result; + EventBits_t sync_c_result; + EventBits_t partial_bits; + EventBits_t final_bits; + eTaskState worker_a_state_at_partial; + eTaskState coordinator_state_before_final_sync; + uintptr_t coordinator_sp; + uintptr_t worker_a_sp; + uintptr_t worker_b_sp; + uint32_t coordinator_done; + uint32_t worker_a_done; + uint32_t worker_b_done; + uint32_t pass; + uint32_t evidence_digest; +} EventGroupExperiment; + +extern EventGroupExperiment g_fc09; +extern EventGroupEvidence g_fc09_events[ FC09_EVENT_CAPACITY ]; +extern volatile uint32_t g_fc09_event_count; +extern volatile uint32_t g_fc09_last_checkpoint; +extern volatile uint32_t g_fc09_pass; + +void fc09_checkpoint_committed( uint32_t point, const void * subject ); +void fc09_coordinator_entry( void * context ); +void fc09_worker_a_entry( void * context ); +void fc09_worker_b_entry( void * context ); +void fc09_verifier_entry( void * context ); + +#endif diff --git a/include/task01_event_groups_model.h b/include/task01_event_groups_model.h new file mode 100644 index 0000000..685d273 --- /dev/null +++ b/include/task01_event_groups_model.h @@ -0,0 +1,59 @@ +#ifndef TASK01_EVENT_GROUPS_MODEL_H +#define TASK01_EVENT_GROUPS_MODEL_H + +#include +#include + +typedef struct EventGroupEvidence +{ + uint32_t sequence; + uint32_t event; + uint32_t tick; + uint32_t actor; + uint32_t value; + uint32_t committed; +} EventGroupEvidence; + +static inline uint32_t fc09_digest( const EventGroupEvidence * events, + size_t count ) +{ + uint32_t hash = 2166136261U; + size_t index; + + for( index = 0U; index < count; ++index ) + { + hash = ( hash ^ events[ index ].event ) * 16777619U; + hash = ( hash ^ events[ index ].actor ) * 16777619U; + hash = ( hash ^ events[ index ].value ) * 16777619U; + hash = ( hash ^ events[ index ].committed ) * 16777619U; + } + return hash; +} + +static inline int fc09_mask_contains( uint32_t value, uint32_t mask ) +{ + return ( value & mask ) == mask; +} + +static inline int fc09_order_is_valid( const EventGroupEvidence * events, + size_t count ) +{ + size_t index; + + if( events == NULL || count != 11U ) + { + return 0; + } + for( index = 0U; index < count; ++index ) + { + if( events[ index ].sequence != index + 1U || + events[ index ].event != index + 1U || + events[ index ].committed != ( 0xFC090000U | ( index + 1U ) ) ) + { + return 0; + } + } + return 1; +} + +#endif diff --git a/json/card_source.json b/json/card_source.json new file mode 100644 index 0000000..89b88ea --- /dev/null +++ b/json/card_source.json @@ -0,0 +1,1482 @@ +{ + "$schema": "../../../tools/card-layouts/schemas/card-source.schema.json", + "schema": "esc-card-source.v1", + "card": { + "id": "mpabi-freertos-c-09-event-groups", + "series": "freertos-c", + "series_title": "FreeRTOS C", + "number": "09", + "count": "15", + "slug": "event-groups", + "title": "Event Groups: ALL-ready i bariera", + "topic": "bit masks, wait semantics, automatic clear i xEventGroupSync", + "project": "Freestanding C nad FreeRTOS", + "subject": "Informatyka", + "level": "Rok 2 · L09 · RV32I/Hazard3", + "revision_date": "2026-07-19T00:00:00+02:00", + "status": "Gotowa", + "version": "v00.01", + "uuid": "e5c4c0f3-3dd0-5168-9945-14d48744ec6f", + "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": "Granice", + "status": "unavailable", + "reason": "Bez nowej granicy sprzętowej lub callbacku." + }, + { + "id": "A2", + "label": "STRUCTURE", + "subtitle": "EventGroup, masks i participants", + "status": "enabled", + "target_label": "fig:a2-structure" + }, + { + "id": "A3", + "label": "DISPATCH", + "subtitle": "Binding", + "status": "unavailable", + "reason": "Task-context EventGroup nie wprowadza callbacku." + }, + { + "id": "A4", + "label": "APPLICATION", + "subtitle": "Coordinator, A, B, verifier", + "status": "enabled", + "target_label": "fig:a4-application" + }, + { + "id": "A5", + "label": "FLOW", + "subtitle": "E01–E11", + "status": "enabled", + "target_label": "fig:a5-flow" + }, + { + "id": "A6", + "label": "STATE", + "subtitle": "Bit value i blocked tasks", + "status": "enabled", + "target_label": "fig:a6-state" + }, + { + "id": "A7", + "label": "RUNTIME", + "subtitle": "Handle, wait lists, stacks, digest", + "status": "enabled", + "target_label": "fig:a7-runtime" + }, + { + "id": "A8", + "label": "PATTERNS", + "subtitle": "ANY, ALL, clear i barrier", + "status": "enabled", + "target_label": "fig:a8-patterns" + } + ], + "debug_strategies": { + "code.protocol": { + "id": "code.protocol", + "kind": "code", + "action": "open", + "expected_observations": [ + "disjoint low-bit masks", + "finite waits" + ], + "assertions": [ + "READY_ALL=0x03", + "SYNC_ALL=0x70" + ], + "evidence_fields": [ + "mask", + "API", + "code_ref" + ], + "prerequisites": [ + "źródło FC09" + ], + "layout": [ + "mask table", + "EventGroupExperiment", + "kernel config" + ], + "commands": [ + "rg -n 'READY_|SYNC_|xEventGroup' include src" + ] + }, + "run.ready": { + "id": "run.ready", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ] + }, + "run.barrier": { + "id": "run.barrier", + "kind": "run", + "action": "replay", + "expected_observations": [ + "all contain 0x70", + "final=0" + ], + "assertions": [ + "each participant syncs once", + "auto clear" + ], + "evidence_fields": [ + "result masks", + "final bits", + "order" + ], + "prerequisites": [ + "READY_ALL consumed" + ], + "layout": [ + "three sync results", + "final group bits" + ], + "commands": [ + "print/x g_fc09.sync_a_result", + "print/x g_fc09.sync_b_result", + "print/x g_fc09.sync_c_result" + ] + }, + "run.pass": { + "id": "run.pass", + "kind": "run", + "action": "replay", + "expected_observations": [ + "PASS=1", + "11 committed records" + ], + "assertions": [ + "three-run deterministic", + "distinct stacks" + ], + "evidence_fields": [ + "PASS", + "digest", + "hashes", + "timestamp" + ], + "prerequisites": [ + "all participants done" + ], + "layout": [ + "PASS", + "bounded log", + "digest" + ], + "commands": [ + "print g_fc09_pass", + "print g_fc09.evidence_digest" + ] + } + }, + "template": "templates/karta-klasyczna.json", + "title_block": { + "category": "KARTA PRACY · INFORMATYKA", + "prepared_by": "M. Pabiszczak", + "prepared_on": "2026-07-19T00:00:00+02:00", + "title": "Event Groups: ALL-ready i bariera", + "url": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f", + "repository_url": "https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-c-event-groups", + "url_host_uuid": "dce7fb9d-7b2f-5d49-96a2-3a30d3070b84", + "url_domain": "mpabi.pl", + "doc_uuid": "3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f", + "revision": "v00.01", + "issued_on": "2026-07-19T00:00:00+02:00", + "series": "FREERTOS-C-09", + "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ń projektuje rozłączne maski EventGroup i dowodzi różnicy między oczekiwaniem ALL a trzyosobową barierą.", + "scope_title": "Zakres karty", + "scope_content_tex": "Koordynator p3 blokuje się na READY_ALL. Worker A ustawia READY_A i blokuje się w xEventGroupSync. Worker B potwierdza maskę częściową, ustawia READY_B, po czym koordynator wnosi SYNC_C. Ostatni SYNC_B zwalnia wszystkich; każdy wynik zawiera 0x70, a automatycznie wyczyszczona grupa kończy z 0.", + "scope_table": { + "headers": [ + "Lekcja", + "Task", + "Najważniejsza idea", + "Priorytet", + "Status", + "Version" + ], + "rows": [ + { + "chapter": "FC09", + "task": "Task01", + "idea_tex": "bit masks, wait semantics, automatic clear i xEventGroupSync", + "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 runtime." + }, + { + "id": "ogolne", + "label": "OG", + "side": "right", + "tree": "WE -> EN -> KW", + "description": "Model FreeRTOS C." + } + ] + }, + "learning_effects": { + "FC09.EN01": { + "bloom_level": "Analiza", + "label": "Model bit protocol", + "text": "Uczeń przypisuje właściciela i znaczenie każdemu bitowi oraz rozdziela wait ALL od barrier sync.", + "assessment_criteria": [ + "FC09.KW01" + ] + }, + "FC09.EK01": { + "bloom_level": "Zastosowanie", + "label": "Replay E01–E11", + "text": "Uczeń pokazuje maskę częściową, dwa Blocked states, trzy wyniki 0x70, final zero, trzy SP i digest.", + "assessment_criteria": [ + "FC09.KW01" + ] + } + }, + "assessment_criteria": { + "FC09.KW01": { + "text": "PASS; partial=0x11; READY result contains 0x03; all SYNC results contain 0x70; final=0; deterministic digest.", + "learning_effects": [ + "FC09.EN01", + "FC09.EK01" + ] + } + }, + "educational_requirements": { + "FC09.WE01": { + "text": "Zaprojektowanie i zbadanie wielozdarzeniowej synchronizacji FreeRTOS.", + "label": "bit masks, wait semantics, automatic clear i xEventGroupSync", + "learning_effects": [ + "FC09.EN01", + "FC09.EK01" + ], + "learning_tree": { + "schema": "we-learning-tree.v1", + "policy": "Każda aktywna kotwica wskazuje kod lub checkpoint.", + "ogolne": [ + { + "effect_ref": "FC09.EN01", + "display": "EN LOCAL RTOS.09", + "source": "LOCAL", + "official": "RTOS", + "local": "09", + "kind": "EN", + "tree_id": "FC09.WE01.OG.LOCAL.RTOS.09", + "text": "Uczeń przypisuje właściciela i znaczenie każdemu bitowi oraz rozdziela wait ALL od barrier sync.", + "kw": [ + { + "criterion_ref": "FC09.KW01", + "display": "KW LOCAL RTOS.09", + "source": "LOCAL", + "kind": "KW", + "official": "RTOS", + "local": "09", + "text": "Łączy kod, diagram i zweryfikowany dowód." + } + ] + } + ], + "zawodowe": [ + { + "effect_ref": "FC09.EK01", + "display": "EK LOCAL RTOS.09", + "source": "LOCAL", + "official": "RTOS", + "local": "09", + "kind": "EK", + "tree_id": "FC09.WE01.TECH.LOCAL.RTOS.09", + "text": "Uczeń pokazuje maskę częściową, dwa Blocked states, trzy wyniki 0x70, final zero, trzy SP i digest.", + "kw": [ + { + "criterion_ref": "FC09.KW01", + "display": "KW LOCAL RTOS.09", + "source": "LOCAL", + "kind": "KW", + "official": "RTOS", + "local": "09", + "text": "Łączy kod, diagram i zweryfikowany dowód." + } + ] + } + ] + } + } + }, + "debug_checkpoints": { + "schema": "stem-debug-checkpoints.v1", + "semantics": "deterministic-replay", + "artifact": { + "source": "src/tasks/task01_event_groups.c", + "source_git_blob": "a3902fd6f17b5d9e9c4fbda5a62f4aca74a7a379", + "elf": "build/task01_event_groups/prog.elf", + "hazard3_elf_sha256": "9b19890d7a1f58ffea884a948ddb6c5486fec898cca7a9c13edbb645acfd15e7", + "hazard3_image": "build/task01_event_groups/prog.bin", + "hazard3_image_sha256": "3571ca16c4139e38f9bae5c4db00f9e8595497ceb0d4537e84d08745fd1e2529" + }, + "targets": { + "hazard3-sim": { + "adapter": "hazard3-reset-load-replay.v1", + "baseline": "restart-backend", + "clean_ram": true + } + }, + "items": { + "task01.config": { + "event_id": "E01", + "stop": { + "symbol": "fc09_checkpoint_committed", + "offset": 0, + "condition": "$a0 == 1" + }, + "verify": { + "expressions": [ + { + "expr": "g_fc09_event_count", + "equals": 1 + }, + { + "expr": "g_fc09.group != 0", + "equals": 1 + } + ] + } + }, + "task01.coordinator-wait": { + "event_id": "E02", + "stop": { + "symbol": "fc09_checkpoint_committed", + "offset": 0, + "condition": "$a0 == 2" + }, + "verify": { + "expressions": [ + { + "expr": "g_fc09.coordinator_sp != 0", + "equals": 1 + } + ] + } + }, + "task01.ready-a": { + "event_id": "E03", + "stop": { + "symbol": "fc09_checkpoint_committed", + "offset": 0, + "condition": "$a0 == 3" + }, + "verify": { + "expressions": [ + { + "expr": "g_fc09_events[2].value", + "equals": 1 + } + ] + } + }, + "task01.sync-a": { + "event_id": "E04", + "stop": { + "symbol": "fc09_checkpoint_committed", + "offset": 0, + "condition": "$a0 == 4" + }, + "verify": { + "expressions": [ + { + "expr": "g_fc09_events[3].value", + "equals": 16 + } + ] + } + }, + "task01.partial": { + "event_id": "E05", + "stop": { + "symbol": "fc09_checkpoint_committed", + "offset": 0, + "condition": "$a0 == 5" + }, + "verify": { + "expressions": [ + { + "expr": "g_fc09.partial_bits", + "equals": 17 + }, + { + "expr": "g_fc09.worker_a_state_at_partial", + "equals": 2 + } + ] + } + }, + "task01.ready-all": { + "event_id": "E06", + "stop": { + "symbol": "fc09_checkpoint_committed", + "offset": 0, + "condition": "$a0 == 6" + }, + "verify": { + "expressions": [ + { + "expr": "g_fc09.ready_result & 3", + "equals": 3 + } + ] + } + }, + "task01.before-final": { + "event_id": "E07", + "stop": { + "symbol": "fc09_checkpoint_committed", + "offset": 0, + "condition": "$a0 == 7" + }, + "verify": { + "expressions": [ + { + "expr": "g_fc09.coordinator_state_before_final_sync", + "equals": 2 + } + ] + } + }, + "task01.coordinator-release": { + "event_id": "E08", + "stop": { + "symbol": "fc09_checkpoint_committed", + "offset": 0, + "condition": "$a0 == 8" + }, + "verify": { + "expressions": [ + { + "expr": "g_fc09.sync_c_result & 112", + "equals": 112 + } + ] + } + }, + "task01.a-release": { + "event_id": "E09", + "stop": { + "symbol": "fc09_checkpoint_committed", + "offset": 0, + "condition": "$a0 == 9" + }, + "verify": { + "expressions": [ + { + "expr": "g_fc09.sync_a_result & 112", + "equals": 112 + } + ] + } + }, + "task01.b-release": { + "event_id": "E10", + "stop": { + "symbol": "fc09_checkpoint_committed", + "offset": 0, + "condition": "$a0 == 10" + }, + "verify": { + "expressions": [ + { + "expr": "g_fc09.sync_b_result & 112", + "equals": 112 + } + ] + } + }, + "task01.pass": { + "event_id": "E11", + "stop": { + "symbol": "fc09_checkpoint_committed", + "offset": 0, + "condition": "$a0 == 11" + }, + "verify": { + "expressions": [ + { + "expr": "g_fc09_pass", + "equals": 1 + }, + { + "expr": "g_fc09.final_bits", + "equals": 0 + }, + { + "expr": "g_fc09_event_count", + "equals": 11 + } + ] + } + } + } + }, + "sections": [ + { + "title": "A2 — Structure", + "order": 20, + "content_kind": "prose", + "asset_page_mode": "one-per-page", + "page_orientation": "landscape", + "content_tex": "EventBits_t jest wspólną wartością bitową. Znaczenie każdego bitu wynika wyłącznie z jawnego kontraktu aplikacji.", + "assets": [ + { + "path": "assets/a2-structure.png", + "html_path": "assets/a2-structure.svg", + "source_path": "assets/a2-structure.puml", + "caption": "A2 STRUCTURE — READY and SYNC masks.", + "label": "fig:a2-structure", + "alt": "Struktura EventGroup.", + "kind": "diagram", + "width": 1.0, + "page_grid": { + "columns": 1, + "rows": 1, + "page_width": 940, + "page_height": 560, + "overview": true, + "step_tiles": { + "group": 1, + "ready": 1, + "sync": 1, + "coordinator": 1, + "a": 1, + "b": 1 + } + }, + "interactive": { + "kind": "uml-class", + "storage_key": "fc09-a2-structure", + "title": "A2 · masks and participants", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a2", + "label": "A2 STRUCTURE", + "description": "One group, two disjoint protocols and three participants." + }, + "phases": [ + { + "id": "structure", + "label": "GROUP / MASKS / PARTICIPANTS", + "steps": [ + { + "id": "group", + "number": 1, + "label": "one real EventGroup", + "mode": "CODE", + "strategy_ref": "code.protocol", + "svg_label": "01", + "code_ref": "src/tasks/task01_event_groups.c:261", + "description": "Kernel object holds bits and wait lists.", + "evidence": "xEventGroupCreate symbol." + }, + { + "id": "ready", + "number": 2, + "label": "READY bits are disjoint", + "mode": "CODE", + "strategy_ref": "code.protocol", + "svg_label": "02", + "code_ref": "include/task01_event_groups.h:11", + "description": "A and B own one bit each.", + "evidence": "ALL=0x03." + }, + { + "id": "sync", + "number": 3, + "label": "SYNC is a separate mask", + "mode": "CODE", + "strategy_ref": "code.protocol", + "svg_label": "03", + "code_ref": "include/task01_event_groups.h:14", + "description": "Barrier cannot consume readiness meaning.", + "evidence": "ALL=0x70." + }, + { + "id": "coordinator", + "number": 4, + "label": "coordinator waits ALL", + "mode": "RUN", + "event_id": "E02", + "strategy_ref": "run.ready", + "svg_label": "04", + "code_ref": "src/tasks/task01_event_groups.c:97", + "snapshot_ref": "task01.coordinator-wait", + "description": "Partial READY cannot release p3.", + "evidence": "Blocked path." + }, + { + "id": "a", + "number": 5, + "label": "worker A owns READY_A/SYNC_A", + "mode": "RUN", + "event_id": "E04", + "strategy_ref": "run.ready", + "svg_label": "05", + "code_ref": "src/tasks/task01_event_groups.c:140", + "snapshot_ref": "task01.sync-a", + "description": "A blocks at barrier.", + "evidence": "partial mask follows." + }, + { + "id": "b", + "number": 6, + "label": "worker B contributes final bits", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "06", + "code_ref": "src/tasks/task01_event_groups.c:183", + "snapshot_ref": "task01.b-release", + "description": "B completes READY then SYNC.", + "evidence": "returns 0x70." + } + ] + } + ] + } + } + ] + }, + { + "title": "A4 — Application", + "order": 30, + "content_kind": "prose", + "asset_page_mode": "one-per-page", + "page_orientation": "portrait", + "content_tex": "Koordynator blokuje się pierwszy, worker A drugi, a worker B może obserwować oba wait conditions przed ustawieniem brakujących bitów.", + "assets": [ + { + "path": "assets/a4-application.png", + "html_path": "assets/a4-application.svg", + "source_path": "assets/a4-application.puml", + "caption": "A4 APPLICATION — deterministic release topology.", + "label": "fig:a4-application", + "alt": "Topologia trzech tasków i verifiera.", + "kind": "diagram", + "width": 1.0, + "page_grid": { + "columns": 1, + "rows": 1, + "page_width": 660, + "page_height": 760, + "overview": true, + "step_tiles": { + "c": 1, + "a": 1, + "b": 1, + "cp": 1, + "bf": 1, + "v": 1 + } + }, + "interactive": { + "kind": "uml-class", + "storage_key": "fc09-a4-application", + "title": "A4 · participant topology", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a4", + "label": "A4 APPLICATION", + "description": "Priorities create a deterministic observation order." + }, + "phases": [ + { + "id": "topology", + "label": "BLOCK / SET / PREEMPT / VERIFY", + "steps": [ + { + "id": "c", + "number": 1, + "label": "coordinator blocks first", + "mode": "RUN", + "event_id": "E02", + "strategy_ref": "run.ready", + "svg_label": "01", + "code_ref": "src/tasks/task01_event_groups.c:96", + "snapshot_ref": "task01.coordinator-wait", + "description": "Priority 3.", + "evidence": "coordinator stack." + }, + { + "id": "a", + "number": 2, + "label": "A publishes partial protocols", + "mode": "RUN", + "event_id": "E04", + "strategy_ref": "run.ready", + "svg_label": "02", + "code_ref": "src/tasks/task01_event_groups.c:145", + "snapshot_ref": "task01.sync-a", + "description": "Priority 2 then block.", + "evidence": "READY_A and SYNC_A." + }, + { + "id": "b", + "number": 3, + "label": "B observes two blocked tasks", + "mode": "RUN", + "event_id": "E05", + "strategy_ref": "run.ready", + "svg_label": "03", + "code_ref": "src/tasks/task01_event_groups.c:175", + "snapshot_ref": "task01.partial", + "description": "No timing guess is needed.", + "evidence": "0x11 and eBlocked." + }, + { + "id": "cp", + "number": 4, + "label": "coordinator preempts on READY_ALL", + "mode": "RUN", + "event_id": "E06", + "strategy_ref": "run.ready", + "svg_label": "04", + "code_ref": "src/tasks/task01_event_groups.c:107", + "snapshot_ref": "task01.ready-all", + "description": "Higher p3 consumes READY epoch.", + "evidence": "ready_result contains 0x03." + }, + { + "id": "bf", + "number": 5, + "label": "B sets the final barrier bit", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "05", + "code_ref": "src/tasks/task01_event_groups.c:190", + "snapshot_ref": "task01.b-release", + "description": "All three waiters return.", + "evidence": "SYNC_ALL." + }, + { + "id": "v", + "number": 6, + "label": "p0 verifier observes final zero", + "mode": "RUN", + "event_id": "E11", + "strategy_ref": "run.pass", + "svg_label": "06", + "code_ref": "src/tasks/task01_event_groups.c:230", + "snapshot_ref": "task01.pass", + "description": "Auto-clear is part of acceptance.", + "evidence": "PASS and digest." + } + ] + } + ] + } + } + ] + }, + { + "title": "A5 — Flow", + "order": 40, + "content_kind": "prose", + "asset_page_mode": "one-per-page", + "page_orientation": "landscape", + "content_tex": "SetBits może uruchomić wyższy task przed powrotem callera. Diagram pokazuje logiczny porządek, nie gwarantowaną kolejność równorzędnych waiterów.", + "assets": [ + { + "path": "assets/a5-flow.png", + "html_path": "assets/a5-flow.svg", + "source_path": "assets/a5-flow.puml", + "caption": "A5 FLOW — ALL-ready followed by xEventGroupSync.", + "label": "fig:a5-flow", + "alt": "Sekwencja EventGroup.", + "kind": "diagram", + "width": 1.0, + "page_grid": { + "columns": 1, + "rows": 1, + "page_width": 940, + "page_height": 560, + "overview": true, + "step_tiles": { + "config": 1, + "wait": 1, + "seta": 1, + "synca": 1, + "partial": 1, + "ready": 1, + "before": 1, + "c": 1, + "a": 1, + "b": 1, + "pass": 1 + } + }, + "interactive": { + "kind": "uml-sequence", + "storage_key": "fc09-a5-flow", + "title": "A5 · ready and barrier flow", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a5", + "label": "A5 FLOW", + "description": "Complete E01–E11 protocol flow." + }, + "phases": [ + { + "id": "flow", + "label": "READY ALL / BARRIER / CLEAR", + "steps": [ + { + "id": "config", + "number": 1, + "label": "group starts at zero", + "mode": "RUN", + "event_id": "E01", + "strategy_ref": "run.ready", + "svg_label": "01", + "code_ref": "src/tasks/task01_event_groups.c:266", + "snapshot_ref": "task01.config", + "description": "Clean RAM.", + "evidence": "bits=0." + }, + { + "id": "wait", + "number": 2, + "label": "coordinator waits ALL", + "mode": "RUN", + "event_id": "E02", + "strategy_ref": "run.ready", + "svg_label": "02", + "code_ref": "src/tasks/task01_event_groups.c:98", + "snapshot_ref": "task01.coordinator-wait", + "description": "Finite timeout.", + "evidence": "p3 blocks." + }, + { + "id": "seta", + "number": 3, + "label": "A sets READY_A", + "mode": "RUN", + "event_id": "E03", + "strategy_ref": "run.ready", + "svg_label": "03", + "code_ref": "src/tasks/task01_event_groups.c:140", + "snapshot_ref": "task01.ready-a", + "description": "ALL remains false.", + "evidence": "bits 0x01." + }, + { + "id": "synca", + "number": 4, + "label": "A contributes SYNC_A", + "mode": "RUN", + "event_id": "E04", + "strategy_ref": "run.ready", + "svg_label": "04", + "code_ref": "src/tasks/task01_event_groups.c:146", + "snapshot_ref": "task01.sync-a", + "description": "A blocks.", + "evidence": "next partial=0x11." + }, + { + "id": "partial", + "number": 5, + "label": "B proves partial state", + "mode": "RUN", + "event_id": "E05", + "strategy_ref": "run.ready", + "svg_label": "05", + "code_ref": "src/tasks/task01_event_groups.c:175", + "snapshot_ref": "task01.partial", + "description": "Neither protocol is complete.", + "evidence": "0x11 and A Blocked." + }, + { + "id": "ready", + "number": 6, + "label": "coordinator receives READY_ALL", + "mode": "RUN", + "event_id": "E06", + "strategy_ref": "run.ready", + "svg_label": "06", + "code_ref": "src/tasks/task01_event_groups.c:107", + "snapshot_ref": "task01.ready-all", + "description": "Ready bits clear on exit.", + "evidence": "result 0x03." + }, + { + "id": "before", + "number": 7, + "label": "coordinator waits at barrier", + "mode": "RUN", + "event_id": "E07", + "strategy_ref": "run.barrier", + "svg_label": "07", + "code_ref": "src/tasks/task01_event_groups.c:184", + "snapshot_ref": "task01.before-final", + "description": "B sees p3 Blocked.", + "evidence": "state eBlocked." + }, + { + "id": "c", + "number": 8, + "label": "coordinator leaves barrier", + "mode": "RUN", + "event_id": "E08", + "strategy_ref": "run.barrier", + "svg_label": "08", + "code_ref": "src/tasks/task01_event_groups.c:120", + "snapshot_ref": "task01.coordinator-release", + "description": "Final B bit released it.", + "evidence": "result contains 0x70." + }, + { + "id": "a", + "number": 9, + "label": "A leaves barrier", + "mode": "RUN", + "event_id": "E09", + "strategy_ref": "run.barrier", + "svg_label": "09", + "code_ref": "src/tasks/task01_event_groups.c:155", + "snapshot_ref": "task01.a-release", + "description": "A returns same complete mask.", + "evidence": "0x70." + }, + { + "id": "b", + "number": 10, + "label": "B returns and sync bits clear", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "10", + "code_ref": "src/tasks/task01_event_groups.c:199", + "snapshot_ref": "task01.b-release", + "description": "Last contributor also receives full mask.", + "evidence": "0x70." + }, + { + "id": "pass", + "number": 11, + "label": "verifier commits PASS", + "mode": "RUN", + "event_id": "E11", + "strategy_ref": "run.pass", + "svg_label": "11", + "code_ref": "src/tasks/task01_event_groups.c:230", + "snapshot_ref": "task01.pass", + "description": "Final group value is zero.", + "evidence": "digest 2391788d." + } + ] + } + ] + } + } + ] + }, + { + "title": "A6 — State", + "order": 50, + "content_kind": "prose", + "asset_page_mode": "one-per-page", + "page_orientation": "portrait", + "content_tex": "ALL/ANY and clear-on-exit describe transitions of the shared bit value. They do not count event multiplicity.", + "assets": [ + { + "path": "assets/a6-state.png", + "html_path": "assets/a6-state.svg", + "source_path": "assets/a6-state.puml", + "caption": "A6 STATE — zero, partial, ready and barrier release.", + "label": "fig:a6-state", + "alt": "Maszyna stanu bitów.", + "kind": "diagram", + "width": 1.0, + "page_grid": { + "columns": 1, + "rows": 1, + "page_width": 660, + "page_height": 760, + "overview": true, + "step_tiles": { + "zero": 1, + "partial": 1, + "all": 1, + "barrier": 1 + } + }, + "interactive": { + "kind": "uml-class", + "storage_key": "fc09-a6-state", + "title": "A6 · bit-state machine", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a6", + "label": "A6 STATE", + "description": "The same integer carries two explicit protocols." + }, + "phases": [ + { + "id": "state", + "label": "ZERO / PARTIAL / COMPLETE / CLEAR", + "steps": [ + { + "id": "zero", + "number": 1, + "label": "zero initial and final", + "mode": "RUN", + "event_id": "E01", + "strategy_ref": "run.ready", + "svg_label": "01", + "code_ref": "src/tasks/task01_event_groups.c:261", + "snapshot_ref": "task01.config", + "description": "No retained epoch.", + "evidence": "0x00." + }, + { + "id": "partial", + "number": 3, + "label": "READY_A is insufficient", + "mode": "RUN", + "event_id": "E05", + "strategy_ref": "run.ready", + "svg_label": "03", + "code_ref": "src/tasks/task01_event_groups.c:175", + "snapshot_ref": "task01.partial", + "description": "Both waiters remain blocked.", + "evidence": "0x11." + }, + { + "id": "all", + "number": 6, + "label": "READY_ALL releases and clears", + "mode": "RUN", + "event_id": "E06", + "strategy_ref": "run.ready", + "svg_label": "06", + "code_ref": "src/tasks/task01_event_groups.c:97", + "snapshot_ref": "task01.ready-all", + "description": "Return contains pre-clear bits.", + "evidence": "0x03." + }, + { + "id": "barrier", + "number": 10, + "label": "SYNC_ALL releases and auto-clears", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "10", + "code_ref": "src/tasks/task01_event_groups.c:190", + "snapshot_ref": "task01.b-release", + "description": "All participants see complete mask.", + "evidence": "0x70 then 0." + } + ] + } + ] + } + } + ] + }, + { + "title": "A7 — Runtime", + "order": 60, + "content_kind": "prose", + "asset_page_mode": "one-per-page", + "page_orientation": "landscape", + "content_tex": "Public task states and returned masks are acceptance evidence. Internal wait lists may be inspected in GDB but are not application dependencies.", + "assets": [ + { + "path": "assets/a7-runtime.png", + "html_path": "assets/a7-runtime.svg", + "source_path": "assets/a7-runtime.puml", + "caption": "A7 RUNTIME — event value and waiting TCBs.", + "label": "fig:a7-runtime", + "alt": "Runtime EventGroup.", + "kind": "diagram", + "width": 1.0, + "page_grid": { + "columns": 1, + "rows": 1, + "page_width": 940, + "page_height": 560, + "overview": true, + "step_tiles": { + "group": 1, + "bits": 1, + "c": 1, + "a": 1, + "b": 1, + "log": 1 + } + }, + "interactive": { + "kind": "uml-class", + "storage_key": "fc09-a7-runtime", + "title": "A7 · runtime evidence", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a7", + "label": "A7 RUNTIME", + "description": "One handle, two wait masks, three stacks and one canonical log." + }, + "phases": [ + { + "id": "runtime", + "label": "GROUP / TCB / STACK / LOG", + "steps": [ + { + "id": "group", + "number": 1, + "label": "real group handle", + "mode": "RUN", + "event_id": "E01", + "strategy_ref": "run.ready", + "svg_label": "01", + "code_ref": "src/tasks/task01_event_groups.c:261", + "snapshot_ref": "task01.config", + "description": "Persistent kernel object.", + "evidence": "non-null handle." + }, + { + "id": "bits", + "number": 2, + "label": "value evolves by API calls", + "mode": "RUN", + "event_id": "E05", + "strategy_ref": "run.ready", + "svg_label": "02", + "code_ref": "src/tasks/task01_event_groups.c:175", + "snapshot_ref": "task01.partial", + "description": "No shadow counter.", + "evidence": "0x11." + }, + { + "id": "c", + "number": 3, + "label": "coordinator has distinct stack", + "mode": "RUN", + "event_id": "E02", + "strategy_ref": "run.ready", + "svg_label": "03", + "code_ref": "src/tasks/task01_event_groups.c:95", + "snapshot_ref": "task01.coordinator-wait", + "description": "Blocked context preserved.", + "evidence": "coordinator_sp." + }, + { + "id": "a", + "number": 4, + "label": "A waits at barrier", + "mode": "RUN", + "event_id": "E05", + "strategy_ref": "run.ready", + "svg_label": "04", + "code_ref": "src/tasks/task01_event_groups.c:176", + "snapshot_ref": "task01.partial", + "description": "Public eBlocked proof.", + "evidence": "worker_a_sp." + }, + { + "id": "b", + "number": 5, + "label": "B provides final bit", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "05", + "code_ref": "src/tasks/task01_event_groups.c:190", + "snapshot_ref": "task01.b-release", + "description": "Third stack.", + "evidence": "worker_b_sp." + }, + { + "id": "log", + "number": 6, + "label": "commit-last bounded evidence", + "mode": "RUN", + "event_id": "E11", + "strategy_ref": "run.pass", + "svg_label": "06", + "code_ref": "src/tasks/task01_event_groups.c:55", + "snapshot_ref": "task01.pass", + "description": "Exact E01–E11.", + "evidence": "digest." + } + ] + } + ] + } + } + ] + }, + { + "title": "A8 — Patterns", + "order": 70, + "content_kind": "prose", + "asset_page_mode": "one-per-page", + "page_orientation": "landscape", + "content_tex": "ANY, ALL, clear-on-exit and xEventGroupSync are distinct contracts. Reusing a bit for incompatible meanings makes the protocol ambiguous.", + "assets": [ + { + "path": "assets/a8-patterns.png", + "html_path": "assets/a8-patterns.svg", + "source_path": "assets/a8-patterns.puml", + "caption": "A8 PATTERNS — ANY, ALL, consume and barrier.", + "label": "fig:a8-patterns", + "alt": "Wzorce EventGroup.", + "kind": "diagram", + "width": 1.0, + "page_grid": { + "columns": 1, + "rows": 1, + "page_width": 940, + "page_height": 560, + "overview": true, + "step_tiles": { + "any": 1, + "all": 1, + "clear": 1, + "sync": 1 + } + }, + "interactive": { + "kind": "uml-class", + "storage_key": "fc09-a8-patterns", + "title": "A8 · event synchronization choices", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a8", + "label": "A8 PATTERNS", + "description": "Choose bit protocol semantics explicitly." + }, + "phases": [ + { + "id": "patterns", + "label": "ANY / ALL / CLEAR / SYNC", + "steps": [ + { + "id": "any", + "number": 1, + "label": "ANY releases on first selected bit", + "mode": "CODE", + "strategy_ref": "code.protocol", + "svg_label": "01", + "code_ref": "include/task01_event_groups.h:11", + "description": "Useful for alternatives.", + "evidence": "exercise variant." + }, + { + "id": "all", + "number": 2, + "label": "ALL joins independent readiness", + "mode": "RUN", + "event_id": "E06", + "strategy_ref": "run.ready", + "svg_label": "02", + "code_ref": "src/tasks/task01_event_groups.c:98", + "snapshot_ref": "task01.ready-all", + "description": "FC09 readiness contract.", + "evidence": "0x03." + }, + { + "id": "clear", + "number": 3, + "label": "clear consumes the readiness epoch", + "mode": "RUN", + "event_id": "E06", + "strategy_ref": "run.ready", + "svg_label": "03", + "code_ref": "src/tasks/task01_event_groups.c:100", + "snapshot_ref": "task01.ready-all", + "description": "Old readiness is not retained.", + "evidence": "final zero." + }, + { + "id": "sync", + "number": 4, + "label": "sync is a reusable barrier", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "04", + "code_ref": "src/tasks/task01_event_groups.c:190", + "snapshot_ref": "task01.b-release", + "description": "Set own bit and wait ALL atomically.", + "evidence": "three 0x70 results." + } + ] + } + ] + } + } + ] + }, + { + "title": "Task01 — EventGroup", + "order": 90, + "content_kind": "tasks", + "task_refs": [ + "task01" + ] + } + ], + "tasks": { + "task01": { + "title": "ALL-ready i trzyosobowa bariera", + "uuid": "3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f", + "prompt_tex": "Przejdź po A2, A4, A5, A6, A7 i A8. Odtwórz E01–E11 i udowodnij, że maska częściowa nie zwalnia tasków, a bariera zwalnia wszystkich dokładnie raz.", + "criterion": "PASS; partial=0x11; READY result contains 0x03; all SYNC results contain 0x70; final=0; deterministic digest.", + "conclusion_tex": "EventGroup przechowuje bity stanu protokołu, nie historię zdarzeń. Rozłączne maski i jawne reguły clear są warunkiem poprawnej synchronizacji.", + "flow": [ + { + "kind": "block", + "id": "predict", + "title": "A — mask table", + "content_tex": "Przypisz READY i SYNC do właścicieli.", + "steps": [ + { + "id": "partial", + "title": "Przewidź 0x11.", + "content_tex": "Wskaż, które taski muszą być Blocked." + }, + { + "id": "clear", + "title": "Przewidź final zero.", + "content_tex": "Rozdziel clear-on-exit od barrier auto-clear." + } + ] + }, + { + "kind": "block", + "id": "replay", + "title": "B — replay Hazard3", + "content_tex": "RUN zaczyna się od czystej RAM.", + "steps": [ + { + "id": "ready", + "title": "Zbadaj E02–E06.", + "content_tex": "Porównaj maski i task states." + }, + { + "id": "barrier", + "title": "Zbadaj E07–E10.", + "content_tex": "Porównaj trzy wartości zwrotne." + }, + { + "id": "pass", + "title": "Zbadaj E11.", + "content_tex": "Zapisz final bits, SP, PASS i digest." + } + ] + }, + { + "kind": "exercise", + "id": "any-all", + "title": "Ćwiczenie — ANY kontra ALL", + "prompt_tex": "Uruchom kontrolowany wariant wait ANY na tej samej sekwencji READY_A/READY_B. Zachowaj osobne maski i porównaj moment release oraz wartość zwrotną.", + "evidence_tex": "Dwa timestampowane ślady i tabela semantyki clear-on-exit.", + "criterion": "Uczeń nie przypisuje API gwarancji kolejności równorzędnych waiterów.", + "based_on": [ + "predict", + "replay" + ] + } + ], + "educational_requirement_refs": [ + "FC09.WE01" + ], + "learning_effect_refs": [ + "FC09.EN01", + "FC09.EK01" + ], + "assessment_criterion_ref": "FC09.KW01" + } + }, + "tasks_order": [ + "task01" + ] +} diff --git a/json/card_spec.json b/json/card_spec.json new file mode 100644 index 0000000..3146044 --- /dev/null +++ b/json/card_spec.json @@ -0,0 +1,91 @@ +{ + "card":{"number":"09","slug":"event-groups","title":"Event Groups: ALL-ready i bariera","topic":"bit masks, wait semantics, automatic clear i xEventGroupSync","status":"Gotowa","version":"v00.01"}, + "front":{"goal":"Uczeń projektuje rozłączne maski EventGroup i dowodzi różnicy między oczekiwaniem ALL a trzyosobową barierą.","scope":"Koordynator p3 blokuje się na READY_ALL. Worker A ustawia READY_A i blokuje się w xEventGroupSync. Worker B potwierdza maskę częściową, ustawia READY_B, po czym koordynator wnosi SYNC_C. Ostatni SYNC_B zwalnia wszystkich; każdy wynik zawiera 0x70, a automatycznie wyczyszczona grupa kończy z 0."}, + "viewpoints":[ + {"id":"A1","label":"CONTEXT","status":"unavailable","subtitle":"Granice","reason":"Bez nowej granicy sprzętowej lub callbacku."}, + {"id":"A2","label":"STRUCTURE","status":"enabled","subtitle":"EventGroup, masks i participants"}, + {"id":"A3","label":"DISPATCH","status":"unavailable","subtitle":"Binding","reason":"Task-context EventGroup nie wprowadza callbacku."}, + {"id":"A4","label":"APPLICATION","status":"enabled","subtitle":"Coordinator, A, B, verifier"}, + {"id":"A5","label":"FLOW","status":"enabled","subtitle":"E01–E11"}, + {"id":"A6","label":"STATE","status":"enabled","subtitle":"Bit value i blocked tasks"}, + {"id":"A7","label":"RUNTIME","status":"enabled","subtitle":"Handle, wait lists, stacks, digest"}, + {"id":"A8","label":"PATTERNS","status":"enabled","subtitle":"ANY, ALL, clear i barrier"} + ], + "artifact":{"source":"src/tasks/task01_event_groups.c","elf":"build/task01_event_groups/prog.elf","image":"build/task01_event_groups/prog.bin"}, + "strategies":[ + {"id":"code.protocol","kind":"code","prerequisites":["źródło FC09"],"layout":["mask table","EventGroupExperiment","kernel config"],"commands":["rg -n 'READY_|SYNC_|xEventGroup' include src"],"expected_observations":["disjoint low-bit masks","finite waits"],"assertions":["READY_ALL=0x03","SYNC_ALL=0x70"],"evidence_fields":["mask","API","code_ref"]}, + {"id":"run.ready","kind":"run","prerequisites":["clean Hazard3 RAM"],"layout":["group bits","coordinator/A states","event log"],"commands":["print g_fc09.partial_bits","print g_fc09.worker_a_state_at_partial"],"expected_observations":["partial=0x11","A Blocked","coordinator still Blocked"],"assertions":["ALL not satisfied by READY_A","reserved bits unused"],"evidence_fields":["bits","state","PC","SP","timestamp"]}, + {"id":"run.barrier","kind":"run","prerequisites":["READY_ALL consumed"],"layout":["three sync results","final group bits"],"commands":["print/x g_fc09.sync_a_result","print/x g_fc09.sync_b_result","print/x g_fc09.sync_c_result"],"expected_observations":["all contain 0x70","final=0"],"assertions":["each participant syncs once","auto clear"],"evidence_fields":["result masks","final bits","order"]}, + {"id":"run.pass","kind":"run","prerequisites":["all participants done"],"layout":["PASS","bounded log","digest"],"commands":["print g_fc09_pass","print g_fc09.evidence_digest"],"expected_observations":["PASS=1","11 committed records"],"assertions":["three-run deterministic","distinct stacks"],"evidence_fields":["PASS","digest","hashes","timestamp"]} + ], + "checkpoints":{ + "task01.config":{"event_id":"E01","stop":{"symbol":"fc09_checkpoint_committed","offset":0,"condition":"$a0 == 1"},"verify":{"expressions":[{"expr":"g_fc09_event_count","equals":1},{"expr":"g_fc09.group != 0","equals":1}]}}, + "task01.coordinator-wait":{"event_id":"E02","stop":{"symbol":"fc09_checkpoint_committed","offset":0,"condition":"$a0 == 2"},"verify":{"expressions":[{"expr":"g_fc09.coordinator_sp != 0","equals":1}]}}, + "task01.ready-a":{"event_id":"E03","stop":{"symbol":"fc09_checkpoint_committed","offset":0,"condition":"$a0 == 3"},"verify":{"expressions":[{"expr":"g_fc09_events[2].value","equals":1}]}}, + "task01.sync-a":{"event_id":"E04","stop":{"symbol":"fc09_checkpoint_committed","offset":0,"condition":"$a0 == 4"},"verify":{"expressions":[{"expr":"g_fc09_events[3].value","equals":16}]}}, + "task01.partial":{"event_id":"E05","stop":{"symbol":"fc09_checkpoint_committed","offset":0,"condition":"$a0 == 5"},"verify":{"expressions":[{"expr":"g_fc09.partial_bits","equals":17},{"expr":"g_fc09.worker_a_state_at_partial","equals":2}]}}, + "task01.ready-all":{"event_id":"E06","stop":{"symbol":"fc09_checkpoint_committed","offset":0,"condition":"$a0 == 6"},"verify":{"expressions":[{"expr":"g_fc09.ready_result & 3","equals":3}]}}, + "task01.before-final":{"event_id":"E07","stop":{"symbol":"fc09_checkpoint_committed","offset":0,"condition":"$a0 == 7"},"verify":{"expressions":[{"expr":"g_fc09.coordinator_state_before_final_sync","equals":2}]}}, + "task01.coordinator-release":{"event_id":"E08","stop":{"symbol":"fc09_checkpoint_committed","offset":0,"condition":"$a0 == 8"},"verify":{"expressions":[{"expr":"g_fc09.sync_c_result & 112","equals":112}]}}, + "task01.a-release":{"event_id":"E09","stop":{"symbol":"fc09_checkpoint_committed","offset":0,"condition":"$a0 == 9"},"verify":{"expressions":[{"expr":"g_fc09.sync_a_result & 112","equals":112}]}}, + "task01.b-release":{"event_id":"E10","stop":{"symbol":"fc09_checkpoint_committed","offset":0,"condition":"$a0 == 10"},"verify":{"expressions":[{"expr":"g_fc09.sync_b_result & 112","equals":112}]}}, + "task01.pass":{"event_id":"E11","stop":{"symbol":"fc09_checkpoint_committed","offset":0,"condition":"$a0 == 11"},"verify":{"expressions":[{"expr":"g_fc09_pass","equals":1},{"expr":"g_fc09.final_bits","equals":0},{"expr":"g_fc09_event_count","equals":11}]}} + }, + "sections":[ + {"id":"A2","label":"STRUCTURE","title":"Structure","order":20,"orientation":"landscape","description":"One group, two disjoint protocols and three participants.","content_tex":"EventBits_t jest wspólną wartością bitową. Znaczenie każdego bitu wynika wyłącznie z jawnego kontraktu aplikacji.","assets":[{"stem":"a2-structure","title":"A2 · masks and participants","caption":"A2 STRUCTURE — READY and SYNC masks.","label":"fig:a2-structure","alt":"Struktura EventGroup.","phases":[{"id":"structure","label":"GROUP / MASKS / PARTICIPANTS","steps":[ + {"id":"group","number":1,"label":"one real EventGroup","mode":"CODE","strategy_ref":"code.protocol","svg_label":"01","code_ref":"src/tasks/task01_event_groups.c:261","description":"Kernel object holds bits and wait lists.","evidence":"xEventGroupCreate symbol."}, + {"id":"ready","number":2,"label":"READY bits are disjoint","mode":"CODE","strategy_ref":"code.protocol","svg_label":"02","code_ref":"include/task01_event_groups.h:11","description":"A and B own one bit each.","evidence":"ALL=0x03."}, + {"id":"sync","number":3,"label":"SYNC is a separate mask","mode":"CODE","strategy_ref":"code.protocol","svg_label":"03","code_ref":"include/task01_event_groups.h:14","description":"Barrier cannot consume readiness meaning.","evidence":"ALL=0x70."}, + {"id":"coordinator","number":4,"label":"coordinator waits ALL","mode":"RUN","event_id":"E02","strategy_ref":"run.ready","svg_label":"04","code_ref":"src/tasks/task01_event_groups.c:97","snapshot_ref":"task01.coordinator-wait","description":"Partial READY cannot release p3.","evidence":"Blocked path."}, + {"id":"a","number":5,"label":"worker A owns READY_A/SYNC_A","mode":"RUN","event_id":"E04","strategy_ref":"run.ready","svg_label":"05","code_ref":"src/tasks/task01_event_groups.c:140","snapshot_ref":"task01.sync-a","description":"A blocks at barrier.","evidence":"partial mask follows."}, + {"id":"b","number":6,"label":"worker B contributes final bits","mode":"RUN","event_id":"E10","strategy_ref":"run.barrier","svg_label":"06","code_ref":"src/tasks/task01_event_groups.c:183","snapshot_ref":"task01.b-release","description":"B completes READY then SYNC.","evidence":"returns 0x70."} + ]}]}]}, + {"id":"A4","label":"APPLICATION","title":"Application","order":30,"orientation":"portrait","description":"Priorities create a deterministic observation order.","content_tex":"Koordynator blokuje się pierwszy, worker A drugi, a worker B może obserwować oba wait conditions przed ustawieniem brakujących bitów.","assets":[{"stem":"a4-application","title":"A4 · participant topology","caption":"A4 APPLICATION — deterministic release topology.","label":"fig:a4-application","alt":"Topologia trzech tasków i verifiera.","phases":[{"id":"topology","label":"BLOCK / SET / PREEMPT / VERIFY","steps":[ + {"id":"c","number":1,"label":"coordinator blocks first","mode":"RUN","event_id":"E02","strategy_ref":"run.ready","svg_label":"01","code_ref":"src/tasks/task01_event_groups.c:96","snapshot_ref":"task01.coordinator-wait","description":"Priority 3.","evidence":"coordinator stack."}, + {"id":"a","number":2,"label":"A publishes partial protocols","mode":"RUN","event_id":"E04","strategy_ref":"run.ready","svg_label":"02","code_ref":"src/tasks/task01_event_groups.c:145","snapshot_ref":"task01.sync-a","description":"Priority 2 then block.","evidence":"READY_A and SYNC_A."}, + {"id":"b","number":3,"label":"B observes two blocked tasks","mode":"RUN","event_id":"E05","strategy_ref":"run.ready","svg_label":"03","code_ref":"src/tasks/task01_event_groups.c:175","snapshot_ref":"task01.partial","description":"No timing guess is needed.","evidence":"0x11 and eBlocked."}, + {"id":"cp","number":4,"label":"coordinator preempts on READY_ALL","mode":"RUN","event_id":"E06","strategy_ref":"run.ready","svg_label":"04","code_ref":"src/tasks/task01_event_groups.c:107","snapshot_ref":"task01.ready-all","description":"Higher p3 consumes READY epoch.","evidence":"ready_result contains 0x03."}, + {"id":"bf","number":5,"label":"B sets the final barrier bit","mode":"RUN","event_id":"E10","strategy_ref":"run.barrier","svg_label":"05","code_ref":"src/tasks/task01_event_groups.c:190","snapshot_ref":"task01.b-release","description":"All three waiters return.","evidence":"SYNC_ALL."}, + {"id":"v","number":6,"label":"p0 verifier observes final zero","mode":"RUN","event_id":"E11","strategy_ref":"run.pass","svg_label":"06","code_ref":"src/tasks/task01_event_groups.c:230","snapshot_ref":"task01.pass","description":"Auto-clear is part of acceptance.","evidence":"PASS and digest."} + ]}]}]}, + {"id":"A5","label":"FLOW","title":"Flow","order":40,"orientation":"landscape","description":"Complete E01–E11 protocol flow.","content_tex":"SetBits może uruchomić wyższy task przed powrotem callera. Diagram pokazuje logiczny porządek, nie gwarantowaną kolejność równorzędnych waiterów.","assets":[{"stem":"a5-flow","diagram_kind":"sequence","title":"A5 · ready and barrier flow","caption":"A5 FLOW — ALL-ready followed by xEventGroupSync.","label":"fig:a5-flow","alt":"Sekwencja EventGroup.","phases":[{"id":"flow","label":"READY ALL / BARRIER / CLEAR","steps":[ + {"id":"config","number":1,"label":"group starts at zero","mode":"RUN","event_id":"E01","strategy_ref":"run.ready","svg_label":"01","code_ref":"src/tasks/task01_event_groups.c:266","snapshot_ref":"task01.config","description":"Clean RAM.","evidence":"bits=0."}, + {"id":"wait","number":2,"label":"coordinator waits ALL","mode":"RUN","event_id":"E02","strategy_ref":"run.ready","svg_label":"02","code_ref":"src/tasks/task01_event_groups.c:98","snapshot_ref":"task01.coordinator-wait","description":"Finite timeout.","evidence":"p3 blocks."}, + {"id":"seta","number":3,"label":"A sets READY_A","mode":"RUN","event_id":"E03","strategy_ref":"run.ready","svg_label":"03","code_ref":"src/tasks/task01_event_groups.c:140","snapshot_ref":"task01.ready-a","description":"ALL remains false.","evidence":"bits 0x01."}, + {"id":"synca","number":4,"label":"A contributes SYNC_A","mode":"RUN","event_id":"E04","strategy_ref":"run.ready","svg_label":"04","code_ref":"src/tasks/task01_event_groups.c:146","snapshot_ref":"task01.sync-a","description":"A blocks.","evidence":"next partial=0x11."}, + {"id":"partial","number":5,"label":"B proves partial state","mode":"RUN","event_id":"E05","strategy_ref":"run.ready","svg_label":"05","code_ref":"src/tasks/task01_event_groups.c:175","snapshot_ref":"task01.partial","description":"Neither protocol is complete.","evidence":"0x11 and A Blocked."}, + {"id":"ready","number":6,"label":"coordinator receives READY_ALL","mode":"RUN","event_id":"E06","strategy_ref":"run.ready","svg_label":"06","code_ref":"src/tasks/task01_event_groups.c:107","snapshot_ref":"task01.ready-all","description":"Ready bits clear on exit.","evidence":"result 0x03."}, + {"id":"before","number":7,"label":"coordinator waits at barrier","mode":"RUN","event_id":"E07","strategy_ref":"run.barrier","svg_label":"07","code_ref":"src/tasks/task01_event_groups.c:184","snapshot_ref":"task01.before-final","description":"B sees p3 Blocked.","evidence":"state eBlocked."}, + {"id":"c","number":8,"label":"coordinator leaves barrier","mode":"RUN","event_id":"E08","strategy_ref":"run.barrier","svg_label":"08","code_ref":"src/tasks/task01_event_groups.c:120","snapshot_ref":"task01.coordinator-release","description":"Final B bit released it.","evidence":"result contains 0x70."}, + {"id":"a","number":9,"label":"A leaves barrier","mode":"RUN","event_id":"E09","strategy_ref":"run.barrier","svg_label":"09","code_ref":"src/tasks/task01_event_groups.c:155","snapshot_ref":"task01.a-release","description":"A returns same complete mask.","evidence":"0x70."}, + {"id":"b","number":10,"label":"B returns and sync bits clear","mode":"RUN","event_id":"E10","strategy_ref":"run.barrier","svg_label":"10","code_ref":"src/tasks/task01_event_groups.c:199","snapshot_ref":"task01.b-release","description":"Last contributor also receives full mask.","evidence":"0x70."}, + {"id":"pass","number":11,"label":"verifier commits PASS","mode":"RUN","event_id":"E11","strategy_ref":"run.pass","svg_label":"11","code_ref":"src/tasks/task01_event_groups.c:230","snapshot_ref":"task01.pass","description":"Final group value is zero.","evidence":"digest 2391788d."} + ]}]}]}, + {"id":"A6","label":"STATE","title":"State","order":50,"orientation":"portrait","description":"The same integer carries two explicit protocols.","content_tex":"ALL/ANY and clear-on-exit describe transitions of the shared bit value. They do not count event multiplicity.","assets":[{"stem":"a6-state","title":"A6 · bit-state machine","caption":"A6 STATE — zero, partial, ready and barrier release.","label":"fig:a6-state","alt":"Maszyna stanu bitów.","phases":[{"id":"state","label":"ZERO / PARTIAL / COMPLETE / CLEAR","steps":[ + {"id":"zero","number":1,"label":"zero initial and final","mode":"RUN","event_id":"E01","strategy_ref":"run.ready","svg_label":"01","code_ref":"src/tasks/task01_event_groups.c:261","snapshot_ref":"task01.config","description":"No retained epoch.","evidence":"0x00."}, + {"id":"partial","number":3,"label":"READY_A is insufficient","mode":"RUN","event_id":"E05","strategy_ref":"run.ready","svg_label":"03","code_ref":"src/tasks/task01_event_groups.c:175","snapshot_ref":"task01.partial","description":"Both waiters remain blocked.","evidence":"0x11."}, + {"id":"all","number":6,"label":"READY_ALL releases and clears","mode":"RUN","event_id":"E06","strategy_ref":"run.ready","svg_label":"06","code_ref":"src/tasks/task01_event_groups.c:97","snapshot_ref":"task01.ready-all","description":"Return contains pre-clear bits.","evidence":"0x03."}, + {"id":"barrier","number":10,"label":"SYNC_ALL releases and auto-clears","mode":"RUN","event_id":"E10","strategy_ref":"run.barrier","svg_label":"10","code_ref":"src/tasks/task01_event_groups.c:190","snapshot_ref":"task01.b-release","description":"All participants see complete mask.","evidence":"0x70 then 0."} + ]}]}]}, + {"id":"A7","label":"RUNTIME","title":"Runtime","order":60,"orientation":"landscape","description":"One handle, two wait masks, three stacks and one canonical log.","content_tex":"Public task states and returned masks are acceptance evidence. Internal wait lists may be inspected in GDB but are not application dependencies.","assets":[{"stem":"a7-runtime","title":"A7 · runtime evidence","caption":"A7 RUNTIME — event value and waiting TCBs.","label":"fig:a7-runtime","alt":"Runtime EventGroup.","phases":[{"id":"runtime","label":"GROUP / TCB / STACK / LOG","steps":[ + {"id":"group","number":1,"label":"real group handle","mode":"RUN","event_id":"E01","strategy_ref":"run.ready","svg_label":"01","code_ref":"src/tasks/task01_event_groups.c:261","snapshot_ref":"task01.config","description":"Persistent kernel object.","evidence":"non-null handle."}, + {"id":"bits","number":2,"label":"value evolves by API calls","mode":"RUN","event_id":"E05","strategy_ref":"run.ready","svg_label":"02","code_ref":"src/tasks/task01_event_groups.c:175","snapshot_ref":"task01.partial","description":"No shadow counter.","evidence":"0x11."}, + {"id":"c","number":3,"label":"coordinator has distinct stack","mode":"RUN","event_id":"E02","strategy_ref":"run.ready","svg_label":"03","code_ref":"src/tasks/task01_event_groups.c:95","snapshot_ref":"task01.coordinator-wait","description":"Blocked context preserved.","evidence":"coordinator_sp."}, + {"id":"a","number":4,"label":"A waits at barrier","mode":"RUN","event_id":"E05","strategy_ref":"run.ready","svg_label":"04","code_ref":"src/tasks/task01_event_groups.c:176","snapshot_ref":"task01.partial","description":"Public eBlocked proof.","evidence":"worker_a_sp."}, + {"id":"b","number":5,"label":"B provides final bit","mode":"RUN","event_id":"E10","strategy_ref":"run.barrier","svg_label":"05","code_ref":"src/tasks/task01_event_groups.c:190","snapshot_ref":"task01.b-release","description":"Third stack.","evidence":"worker_b_sp."}, + {"id":"log","number":6,"label":"commit-last bounded evidence","mode":"RUN","event_id":"E11","strategy_ref":"run.pass","svg_label":"06","code_ref":"src/tasks/task01_event_groups.c:55","snapshot_ref":"task01.pass","description":"Exact E01–E11.","evidence":"digest."} + ]}]}]}, + {"id":"A8","label":"PATTERNS","title":"Patterns","order":70,"orientation":"landscape","description":"Choose bit protocol semantics explicitly.","content_tex":"ANY, ALL, clear-on-exit and xEventGroupSync are distinct contracts. Reusing a bit for incompatible meanings makes the protocol ambiguous.","assets":[{"stem":"a8-patterns","title":"A8 · event synchronization choices","caption":"A8 PATTERNS — ANY, ALL, consume and barrier.","label":"fig:a8-patterns","alt":"Wzorce EventGroup.","phases":[{"id":"patterns","label":"ANY / ALL / CLEAR / SYNC","steps":[ + {"id":"any","number":1,"label":"ANY releases on first selected bit","mode":"CODE","strategy_ref":"code.protocol","svg_label":"01","code_ref":"include/task01_event_groups.h:11","description":"Useful for alternatives.","evidence":"exercise variant."}, + {"id":"all","number":2,"label":"ALL joins independent readiness","mode":"RUN","event_id":"E06","strategy_ref":"run.ready","svg_label":"02","code_ref":"src/tasks/task01_event_groups.c:98","snapshot_ref":"task01.ready-all","description":"FC09 readiness contract.","evidence":"0x03."}, + {"id":"clear","number":3,"label":"clear consumes the readiness epoch","mode":"RUN","event_id":"E06","strategy_ref":"run.ready","svg_label":"03","code_ref":"src/tasks/task01_event_groups.c:100","snapshot_ref":"task01.ready-all","description":"Old readiness is not retained.","evidence":"final zero."}, + {"id":"sync","number":4,"label":"sync is a reusable barrier","mode":"RUN","event_id":"E10","strategy_ref":"run.barrier","svg_label":"04","code_ref":"src/tasks/task01_event_groups.c:190","snapshot_ref":"task01.b-release","description":"Set own bit and wait ALL atomically.","evidence":"three 0x70 results."} + ]}]}]} + ], + "learning":{"model_label":"Model bit protocol","model":"Uczeń przypisuje właściciela i znaczenie każdemu bitowi oraz rozdziela wait ALL od barrier sync.","replay_label":"Replay E01–E11","replay":"Uczeń pokazuje maskę częściową, dwa Blocked states, trzy wyniki 0x70, final zero, trzy SP i digest.","criterion":"PASS; partial=0x11; READY result contains 0x03; all SYNC results contain 0x70; final=0; deterministic digest.","requirement":"Zaprojektowanie i zbadanie wielozdarzeniowej synchronizacji FreeRTOS."}, + "task":{"short_label":"EventGroup","title":"ALL-ready i trzyosobowa bariera","prompt_tex":"Przejdź po A2, A4, A5, A6, A7 i A8. Odtwórz E01–E11 i udowodnij, że maska częściowa nie zwalnia tasków, a bariera zwalnia wszystkich dokładnie raz.","conclusion_tex":"EventGroup przechowuje bity stanu protokołu, nie historię zdarzeń. Rozłączne maski i jawne reguły clear są warunkiem poprawnej synchronizacji.","flow":[ + {"kind":"block","id":"predict","title":"A — mask table","content_tex":"Przypisz READY i SYNC do właścicieli.","steps":[{"id":"partial","title":"Przewidź 0x11.","content_tex":"Wskaż, które taski muszą być Blocked."},{"id":"clear","title":"Przewidź final zero.","content_tex":"Rozdziel clear-on-exit od barrier auto-clear."}]}, + {"kind":"block","id":"replay","title":"B — replay Hazard3","content_tex":"RUN zaczyna się od czystej RAM.","steps":[{"id":"ready","title":"Zbadaj E02–E06.","content_tex":"Porównaj maski i task states."},{"id":"barrier","title":"Zbadaj E07–E10.","content_tex":"Porównaj trzy wartości zwrotne."},{"id":"pass","title":"Zbadaj E11.","content_tex":"Zapisz final bits, SP, PASS i digest."}]}, + {"kind":"exercise","id":"any-all","title":"Ćwiczenie — ANY kontra ALL","prompt_tex":"Uruchom kontrolowany wariant wait ANY na tej samej sekwencji READY_A/READY_B. Zachowaj osobne maski i porównaj moment release oraz wartość zwrotną.","evidence_tex":"Dwa timestampowane ślady i tabela semantyki clear-on-exit.","criterion":"Uczeń nie przypisuje API gwarancji kolejności równorzędnych waiterów.","based_on":["predict","replay"]} + ]} +} diff --git a/scripts/check_abi.sh b/scripts/check_abi.sh new file mode 100755 index 0000000..455044e --- /dev/null +++ b/scripts/check_abi.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env sh +set -eu + +nm=$(printenv RISCV_NM || true) +readelf=$(printenv RISCV_READELF || true) +test -n "$nm" || nm=riscv64-unknown-elf-nm +test -n "$readelf" || readelf=riscv64-unknown-elf-readelf +elf=build/task01_event_groups/prog.elf + +if "$nm" --defined-only "$elf" | awk '{print $3}' | grep -Eq '^_Z|^__cxa_|^_Unwind_'; then + echo "unexpected C++ or unwind symbol in FC09" >&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 FC09" >&2 + exit 1 +fi +for symbol in fc09_coordinator_entry fc09_worker_a_entry fc09_worker_b_entry \ + fc09_verifier_entry fc09_checkpoint_committed xEventGroupCreate \ + xEventGroupSetBits xEventGroupWaitBits xEventGroupSync; do + "$nm" --defined-only "$elf" | grep -Eq "[[:space:]]$symbol$" || { + echo "required symbol is missing: $symbol" >&2 + exit 1 + } +done +echo "PASS ABI: FC09 is freestanding C11 with EventGroup wait and sync" diff --git a/scripts/check_determinism.sh b/scripts/check_determinism.sh new file mode 100755 index 0000000..5fb10e1 --- /dev/null +++ b/scripts/check_determinism.sh @@ -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_event_groups/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" diff --git a/scripts/render_pdf.sh b/scripts/render_pdf.sh new file mode 100755 index 0000000..fe3b1c4 --- /dev/null +++ b/scripts/render_pdf.sh @@ -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" diff --git a/scripts/run_sim.sh b/scripts/run_sim.sh new file mode 100755 index 0000000..3ad14ea --- /dev/null +++ b/scripts/run_sim.sh @@ -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_event_groups ==" +"$tb" --bin "$build_dir/task01_event_groups/prog.bin" --cycles 3000000 --cpuret diff --git a/src/common/crt0.S b/src/common/crt0.S new file mode 100644 index 0000000..1b329cf --- /dev/null +++ b/src/common/crt0.S @@ -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 diff --git a/src/common/hazard3_freertos_traps.S b/src/common/hazard3_freertos_traps.S new file mode 100644 index 0000000..b38a06a --- /dev/null +++ b/src/common/hazard3_freertos_traps.S @@ -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 diff --git a/src/common/lab_freertos.c b/src/common/lab_freertos.c new file mode 100644 index 0000000..5ad5607 --- /dev/null +++ b/src/common/lab_freertos.c @@ -0,0 +1,48 @@ +#include + +#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 ); +} diff --git a/src/common/lab_freertos.h b/src/common/lab_freertos.h new file mode 100644 index 0000000..370c069 --- /dev/null +++ b/src/common/lab_freertos.h @@ -0,0 +1,16 @@ +#ifndef LAB_FREERTOS_H +#define LAB_FREERTOS_H + +#include +#include + +#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 diff --git a/src/common/lab_io.c b/src/common/lab_io.c new file mode 100644 index 0000000..8829b28 --- /dev/null +++ b/src/common/lab_io.c @@ -0,0 +1,30 @@ +#include + +#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" ); + } +} diff --git a/src/common/lab_io.h b/src/common/lab_io.h new file mode 100644 index 0000000..bffcc61 --- /dev/null +++ b/src/common/lab_io.h @@ -0,0 +1,10 @@ +#ifndef LAB_IO_H +#define LAB_IO_H + +#include + +void lab_puts( const char * text ); +void lab_put_u32( uint32_t value ); +void lab_exit( uint32_t code ) __attribute__( ( noreturn ) ); + +#endif diff --git a/src/common/memops.c b/src/common/memops.c new file mode 100644 index 0000000..acf5a09 --- /dev/null +++ b/src/common/memops.c @@ -0,0 +1,34 @@ +#include + +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; +} diff --git a/src/tasks/task01_event_groups.c b/src/tasks/task01_event_groups.c new file mode 100644 index 0000000..a3902fd --- /dev/null +++ b/src/tasks/task01_event_groups.c @@ -0,0 +1,279 @@ +#include +#include + +#include "FreeRTOS.h" +#include "event_groups.h" +#include "task.h" +#include "lab_freertos.h" +#include "lab_io.h" +#include "task01_event_groups.h" +#include "task01_event_groups_model.h" + +#define FC09_ACTOR_MAIN 1U +#define FC09_ACTOR_COORDINATOR 2U +#define FC09_ACTOR_WORKER_A 3U +#define FC09_ACTOR_WORKER_B 4U +#define FC09_ACTOR_VERIFIER 5U + +EventGroupExperiment g_fc09; +EventGroupEvidence g_fc09_events[ FC09_EVENT_CAPACITY ]; +volatile uint32_t g_fc09_event_count; +volatile uint32_t g_fc09_last_checkpoint; +volatile uint32_t g_fc09_pass; + +static uintptr_t read_sp( void ) +{ + uintptr_t value; + __asm__ volatile( "mv %0, sp" : "=r"( value ) ); + return value; +} + +__attribute__( ( noinline, used ) ) +void fc09_checkpoint_committed( uint32_t point, const void * subject ) +{ + ( void ) point; + ( void ) subject; + __asm__ volatile( "" ::: "memory" ); +} + +static void fc09_publish( uint32_t point, + uint32_t actor, + uint32_t value, + const void * subject ) +{ + UBaseType_t critical_state; + uint32_t index; + EventGroupEvidence * event; + + critical_state = taskENTER_CRITICAL_FROM_ISR(); + index = g_fc09_event_count; + if( index >= FC09_EVENT_CAPACITY ) + { + taskEXIT_CRITICAL_FROM_ISR( critical_state ); + lab_fail( 0xFC090001U ); + } + event = &g_fc09_events[ index ]; + event->sequence = index + 1U; + event->event = point; + event->tick = ( uint32_t ) xTaskGetTickCount(); + event->actor = actor; + event->value = value; + __asm__ volatile( "" ::: "memory" ); + event->committed = 0xFC090000U | ( index + 1U ); + g_fc09_event_count = index + 1U; + g_fc09_last_checkpoint = point; + taskEXIT_CRITICAL_FROM_ISR( critical_state ); + fc09_checkpoint_committed( point, subject ); +} + +static TaskHandle_t fc09_create_task( TaskFunction_t entry, + const char * name, + UBaseType_t priority ) +{ + TaskHandle_t handle = NULL; + if( xTaskCreate( entry, + name, + FC09_TASK_STACK_WORDS, + &g_fc09, + priority, + &handle ) != pdPASS ) + { + lab_fail( 0xFC090100U + priority ); + } + return handle; +} + +__attribute__( ( noinline, used ) ) +void fc09_coordinator_entry( void * context ) +{ + EventGroupExperiment * experiment = ( EventGroupExperiment * ) context; + + if( experiment == NULL ) + { + lab_fail( 0xFC090201U ); + } + experiment->coordinator_sp = read_sp(); + fc09_publish( 2U, FC09_ACTOR_COORDINATOR, FC09_READY_ALL, experiment ); + experiment->ready_result = + xEventGroupWaitBits( experiment->group, + FC09_READY_ALL, + pdTRUE, + pdTRUE, + FC09_TIMEOUT_TICKS ); + if( fc09_mask_contains( experiment->ready_result, FC09_READY_ALL ) == 0 ) + { + lab_fail( 0xFC090202U ); + } + fc09_publish( 6U, + FC09_ACTOR_COORDINATOR, + experiment->ready_result, + experiment ); + experiment->sync_c_result = + xEventGroupSync( experiment->group, + FC09_SYNC_C, + FC09_SYNC_ALL, + FC09_TIMEOUT_TICKS ); + if( fc09_mask_contains( experiment->sync_c_result, FC09_SYNC_ALL ) == 0 ) + { + lab_fail( 0xFC090203U ); + } + fc09_publish( 8U, + FC09_ACTOR_COORDINATOR, + experiment->sync_c_result, + experiment ); + experiment->coordinator_done = 1U; + experiment->coordinator_handle = NULL; + vTaskDelete( NULL ); + lab_fail( 0xFC090204U ); +} + +__attribute__( ( noinline, used ) ) +void fc09_worker_a_entry( void * context ) +{ + EventGroupExperiment * experiment = ( EventGroupExperiment * ) context; + + if( experiment == NULL ) + { + lab_fail( 0xFC090301U ); + } + experiment->worker_a_sp = read_sp(); + ( void ) xEventGroupSetBits( experiment->group, FC09_READY_A ); + fc09_publish( 3U, + FC09_ACTOR_WORKER_A, + xEventGroupGetBits( experiment->group ), + experiment ); + fc09_publish( 4U, FC09_ACTOR_WORKER_A, FC09_SYNC_A, experiment ); + experiment->sync_a_result = + xEventGroupSync( experiment->group, + FC09_SYNC_A, + FC09_SYNC_ALL, + FC09_TIMEOUT_TICKS ); + if( fc09_mask_contains( experiment->sync_a_result, FC09_SYNC_ALL ) == 0 ) + { + lab_fail( 0xFC090302U ); + } + fc09_publish( 9U, + FC09_ACTOR_WORKER_A, + experiment->sync_a_result, + experiment ); + experiment->worker_a_done = 1U; + experiment->worker_a_handle = NULL; + vTaskDelete( NULL ); + lab_fail( 0xFC090303U ); +} + +__attribute__( ( noinline, used ) ) +void fc09_worker_b_entry( void * context ) +{ + EventGroupExperiment * experiment = ( EventGroupExperiment * ) context; + + if( experiment == NULL ) + { + lab_fail( 0xFC090401U ); + } + experiment->worker_b_sp = read_sp(); + experiment->partial_bits = xEventGroupGetBits( experiment->group ); + experiment->worker_a_state_at_partial = + eTaskGetState( experiment->worker_a_handle ); + fc09_publish( 5U, + FC09_ACTOR_WORKER_B, + experiment->partial_bits, + experiment ); + + ( void ) xEventGroupSetBits( experiment->group, FC09_READY_B ); + experiment->coordinator_state_before_final_sync = + eTaskGetState( experiment->coordinator_handle ); + fc09_publish( 7U, + FC09_ACTOR_WORKER_B, + ( uint32_t ) experiment->coordinator_state_before_final_sync, + experiment ); + experiment->sync_b_result = + xEventGroupSync( experiment->group, + FC09_SYNC_B, + FC09_SYNC_ALL, + FC09_TIMEOUT_TICKS ); + if( fc09_mask_contains( experiment->sync_b_result, FC09_SYNC_ALL ) == 0 ) + { + lab_fail( 0xFC090402U ); + } + fc09_publish( 10U, + FC09_ACTOR_WORKER_B, + experiment->sync_b_result, + experiment ); + experiment->worker_b_done = 1U; + experiment->worker_b_handle = NULL; + vTaskDelete( NULL ); + lab_fail( 0xFC090403U ); +} + +__attribute__( ( noinline, used ) ) +void fc09_verifier_entry( void * context ) +{ + EventGroupExperiment * experiment = ( EventGroupExperiment * ) context; + TickType_t started; + + if( experiment == NULL ) + { + lab_fail( 0xFC090501U ); + } + started = xTaskGetTickCount(); + while( experiment->coordinator_done == 0U || + experiment->worker_a_done == 0U || + experiment->worker_b_done == 0U ) + { + if( ( xTaskGetTickCount() - started ) > FC09_TIMEOUT_TICKS ) + { + lab_fail( 0xFC090502U ); + } + vTaskDelay( 1U ); + } + experiment->final_bits = xEventGroupGetBits( experiment->group ); + experiment->pass = + fc09_mask_contains( experiment->ready_result, FC09_READY_ALL ) != 0 && + fc09_mask_contains( experiment->partial_bits, + FC09_READY_A | FC09_SYNC_A ) != 0 && + experiment->worker_a_state_at_partial == eBlocked && + experiment->coordinator_state_before_final_sync == eBlocked && + fc09_mask_contains( experiment->sync_a_result, FC09_SYNC_ALL ) != 0 && + fc09_mask_contains( experiment->sync_b_result, FC09_SYNC_ALL ) != 0 && + fc09_mask_contains( experiment->sync_c_result, FC09_SYNC_ALL ) != 0 && + experiment->final_bits == 0U && + experiment->coordinator_sp != experiment->worker_a_sp && + experiment->worker_a_sp != experiment->worker_b_sp && + g_fc09_event_count == 10U; + g_fc09_pass = experiment->pass; + fc09_publish( 11U, FC09_ACTOR_VERIFIER, experiment->pass, experiment ); + experiment->evidence_digest = fc09_digest( g_fc09_events, + g_fc09_event_count ); + if( experiment->pass == 0U || + fc09_order_is_valid( g_fc09_events, g_fc09_event_count ) == 0 ) + { + lab_fail( 0xFC090503U ); + } + lab_puts( "PASS FC09: ALL-ready wait and three-party EventGroup barrier\n" ); + lab_put_u32( experiment->evidence_digest ); + lab_exit( 0U ); +} + +int main( void ) +{ + lab_heap_initialize(); + g_fc09.group = xEventGroupCreate(); + if( g_fc09.group == NULL ) + { + lab_fail( 0xFC090601U ); + } + fc09_publish( 1U, FC09_ACTOR_MAIN, xEventGroupGetBits( g_fc09.group ), &g_fc09 ); + g_fc09.verifier_handle = + fc09_create_task( fc09_verifier_entry, "verifier", FC09_VERIFIER_PRIORITY ); + g_fc09.worker_b_handle = + fc09_create_task( fc09_worker_b_entry, "workerB", FC09_WORKER_B_PRIORITY ); + g_fc09.worker_a_handle = + fc09_create_task( fc09_worker_a_entry, "workerA", FC09_WORKER_A_PRIORITY ); + g_fc09.coordinator_handle = + fc09_create_task( fc09_coordinator_entry, + "coordinator", + FC09_COORDINATOR_PRIORITY ); + vTaskStartScheduler(); + lab_fail( 0xFC090602U ); +} diff --git a/tests/event_groups_model_host.c b/tests/event_groups_model_host.c new file mode 100644 index 0000000..644d7a0 --- /dev/null +++ b/tests/event_groups_model_host.c @@ -0,0 +1,28 @@ +#include + +#include "task01_event_groups_model.h" + +int main( void ) +{ + EventGroupEvidence events[ 11 ]; + size_t index; + + assert( fc09_mask_contains( 0x73U, 0x03U ) != 0 ); + assert( fc09_mask_contains( 0x31U, 0x70U ) == 0 ); + for( index = 0U; index < 11U; ++index ) + { + events[ index ] = ( EventGroupEvidence ) { + ( uint32_t ) index + 1U, + ( uint32_t ) index + 1U, + 0U, + ( uint32_t ) index, + ( uint32_t ) index, + 0xFC090000U | ( ( uint32_t ) index + 1U ) + }; + } + assert( fc09_order_is_valid( events, 11U ) != 0 ); + assert( fc09_digest( events, 11U ) == fc09_digest( events, 11U ) ); + events[ 7 ].event = 9U; + assert( fc09_order_is_valid( events, 11U ) == 0 ); + return 0; +} diff --git a/vendor/FreeRTOS-Kernel/LICENSE.md b/vendor/FreeRTOS-Kernel/LICENSE.md new file mode 100644 index 0000000..9cf1062 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/LICENSE.md @@ -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. diff --git a/vendor/FreeRTOS-Kernel/UPSTREAM.md b/vendor/FreeRTOS-Kernel/UPSTREAM.md new file mode 100644 index 0000000..5661374 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/UPSTREAM.md @@ -0,0 +1,12 @@ +# Vendored FreeRTOS kernel subset + +- Upstream: +- 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/`. diff --git a/vendor/FreeRTOS-Kernel/event_groups.c b/vendor/FreeRTOS-Kernel/event_groups.c new file mode 100644 index 0000000..7c43fa5 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/event_groups.c @@ -0,0 +1,887 @@ +/* + * 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 + * + */ + +/* Standard includes. */ +#include + +/* 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 + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" +#include "event_groups.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 + +/* This entire source file will be skipped if the application is not configured + * to include event groups functionality. This #if is closed at the very bottom + * of this file. If you want to include event groups then ensure + * configUSE_EVENT_GROUPS is set to 1 in FreeRTOSConfig.h. */ +#if ( configUSE_EVENT_GROUPS == 1 ) + + typedef struct EventGroupDef_t + { + EventBits_t uxEventBits; + List_t xTasksWaitingForBits; /**< List of tasks waiting for a bit to be set. */ + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxEventGroupNumber; + #endif + + #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucStaticallyAllocated; /**< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */ + #endif + } EventGroup_t; + +/*-----------------------------------------------------------*/ + +/* + * Test the bits set in uxCurrentEventBits to see if the wait condition is met. + * The wait condition is defined by xWaitForAllBits. If xWaitForAllBits is + * pdTRUE then the wait condition is met if all the bits set in uxBitsToWaitFor + * are also set in uxCurrentEventBits. If xWaitForAllBits is pdFALSE then the + * wait condition is met if any of the bits set in uxBitsToWaitFor are also set + * in uxCurrentEventBits. + */ + static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, + const EventBits_t uxBitsToWaitFor, + const BaseType_t xWaitForAllBits ) PRIVILEGED_FUNCTION; + +/*-----------------------------------------------------------*/ + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ) + { + EventGroup_t * pxEventBits; + + traceENTER_xEventGroupCreateStatic( pxEventGroupBuffer ); + + /* A StaticEventGroup_t object must be provided. */ + configASSERT( pxEventGroupBuffer ); + + #if ( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + * variable of type StaticEventGroup_t equals the size of the real + * event group structure. */ + volatile size_t xSize = sizeof( StaticEventGroup_t ); + configASSERT( xSize == sizeof( EventGroup_t ) ); + } + #endif /* configASSERT_DEFINED */ + + /* The user has provided a statically allocated event group - use it. */ + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; + + if( pxEventBits != NULL ) + { + pxEventBits->uxEventBits = 0; + vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); + + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Both static and dynamic allocation can be used, so note that + * this event group was created statically in case the event group + * is later deleted. */ + pxEventBits->ucStaticallyAllocated = pdTRUE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + traceEVENT_GROUP_CREATE( pxEventBits ); + } + else + { + /* xEventGroupCreateStatic should only ever be called with + * pxEventGroupBuffer pointing to a pre-allocated (compile time + * allocated) StaticEventGroup_t variable. */ + traceEVENT_GROUP_CREATE_FAILED(); + } + + traceRETURN_xEventGroupCreateStatic( pxEventBits ); + + return pxEventBits; + } + + #endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + EventGroupHandle_t xEventGroupCreate( void ) + { + EventGroup_t * pxEventBits; + + traceENTER_xEventGroupCreate(); + + /* MISRA Ref 11.5.1 [Malloc memory assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); + + if( pxEventBits != NULL ) + { + pxEventBits->uxEventBits = 0; + vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* Both static and dynamic allocation can be used, so note this + * event group was allocated statically in case the event group is + * later deleted. */ + pxEventBits->ucStaticallyAllocated = pdFALSE; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + traceEVENT_GROUP_CREATE( pxEventBits ); + } + else + { + traceEVENT_GROUP_CREATE_FAILED(); + } + + traceRETURN_xEventGroupCreate( pxEventBits ); + + return pxEventBits; + } + + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + + EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet, + const EventBits_t uxBitsToWaitFor, + TickType_t xTicksToWait ) + { + EventBits_t uxOriginalBitValue, uxReturn; + EventGroup_t * pxEventBits = xEventGroup; + BaseType_t xAlreadyYielded; + BaseType_t xTimeoutOccurred = pdFALSE; + + traceENTER_xEventGroupSync( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTicksToWait ); + + configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + configASSERT( uxBitsToWaitFor != 0 ); + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + vTaskSuspendAll(); + { + uxOriginalBitValue = pxEventBits->uxEventBits; + + ( void ) xEventGroupSetBits( xEventGroup, uxBitsToSet ); + + if( ( ( uxOriginalBitValue | uxBitsToSet ) & uxBitsToWaitFor ) == uxBitsToWaitFor ) + { + /* All the rendezvous bits are now set - no need to block. */ + uxReturn = ( uxOriginalBitValue | uxBitsToSet ); + + /* Rendezvous always clear the bits. They will have been cleared + * already unless this is the only task in the rendezvous. */ + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + + xTicksToWait = 0; + } + else + { + if( xTicksToWait != ( TickType_t ) 0 ) + { + traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor ); + + /* Store the bits that the calling task is waiting for in the + * task's event list item so the kernel knows when a match is + * found. Then enter the blocked state. */ + vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait ); + + /* This assignment is obsolete as uxReturn will get set after + * the task unblocks, but some compilers mistakenly generate a + * warning about uxReturn being returned without being set if the + * assignment is omitted. */ + uxReturn = 0; + } + else + { + /* The rendezvous bits were not set, but no block time was + * specified - just return the current event bit value. */ + uxReturn = pxEventBits->uxEventBits; + xTimeoutOccurred = pdTRUE; + } + } + } + xAlreadyYielded = xTaskResumeAll(); + + if( xTicksToWait != ( TickType_t ) 0 ) + { + if( xAlreadyYielded == pdFALSE ) + { + taskYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The task blocked to wait for its required bits to be set - at this + * point either the required bits were set or the block time expired. If + * the required bits were set they will have been stored in the task's + * event list item, and they should now be retrieved then cleared. */ + uxReturn = uxTaskResetEventItemValue(); + + if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 ) + { + /* The task timed out, just return the current event bit value. */ + taskENTER_CRITICAL(); + { + uxReturn = pxEventBits->uxEventBits; + + /* Although the task got here because it timed out before the + * bits it was waiting for were set, it is possible that since it + * unblocked another task has set the bits. If this is the case + * then it needs to clear the bits before exiting. */ + if( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor ) + { + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + xTimeoutOccurred = pdTRUE; + } + else + { + /* The task unblocked because the bits were set. */ + } + + /* Control bits might be set as the task had blocked should not be + * returned. */ + uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES; + } + + traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred ); + + /* Prevent compiler warnings when trace macros are not used. */ + ( void ) xTimeoutOccurred; + + traceRETURN_xEventGroupSync( uxReturn ); + + return uxReturn; + } +/*-----------------------------------------------------------*/ + + EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToWaitFor, + const BaseType_t xClearOnExit, + const BaseType_t xWaitForAllBits, + TickType_t xTicksToWait ) + { + EventGroup_t * pxEventBits = xEventGroup; + EventBits_t uxReturn, uxControlBits = 0; + BaseType_t xWaitConditionMet, xAlreadyYielded; + BaseType_t xTimeoutOccurred = pdFALSE; + + traceENTER_xEventGroupWaitBits( xEventGroup, uxBitsToWaitFor, xClearOnExit, xWaitForAllBits, xTicksToWait ); + + /* Check the user is not attempting to wait on the bits used by the kernel + * itself, and that at least one bit is being requested. */ + configASSERT( xEventGroup ); + configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + configASSERT( uxBitsToWaitFor != 0 ); + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + vTaskSuspendAll(); + { + const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits; + + /* Check to see if the wait condition is already met or not. */ + xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits ); + + if( xWaitConditionMet != pdFALSE ) + { + /* The wait condition has already been met so there is no need to + * block. */ + uxReturn = uxCurrentEventBits; + xTicksToWait = ( TickType_t ) 0; + + /* Clear the wait bits if requested to do so. */ + if( xClearOnExit != pdFALSE ) + { + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The wait condition has not been met, but no block time was + * specified, so just return the current value. */ + uxReturn = uxCurrentEventBits; + xTimeoutOccurred = pdTRUE; + } + else + { + /* The task is going to block to wait for its required bits to be + * set. uxControlBits are used to remember the specified behaviour of + * this call to xEventGroupWaitBits() - for use when the event bits + * unblock the task. */ + if( xClearOnExit != pdFALSE ) + { + uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xWaitForAllBits != pdFALSE ) + { + uxControlBits |= eventWAIT_FOR_ALL_BITS; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Store the bits that the calling task is waiting for in the + * task's event list item so the kernel knows when a match is + * found. Then enter the blocked state. */ + vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait ); + + /* This is obsolete as it will get set after the task unblocks, but + * some compilers mistakenly generate a warning about the variable + * being returned without being set if it is not done. */ + uxReturn = 0; + + traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor ); + } + } + xAlreadyYielded = xTaskResumeAll(); + + if( xTicksToWait != ( TickType_t ) 0 ) + { + if( xAlreadyYielded == pdFALSE ) + { + taskYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The task blocked to wait for its required bits to be set - at this + * point either the required bits were set or the block time expired. If + * the required bits were set they will have been stored in the task's + * event list item, and they should now be retrieved then cleared. */ + uxReturn = uxTaskResetEventItemValue(); + + if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 ) + { + taskENTER_CRITICAL(); + { + /* The task timed out, just return the current event bit value. */ + uxReturn = pxEventBits->uxEventBits; + + /* It is possible that the event bits were updated between this + * task leaving the Blocked state and running again. */ + if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE ) + { + if( xClearOnExit != pdFALSE ) + { + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xTimeoutOccurred = pdTRUE; + } + taskEXIT_CRITICAL(); + } + else + { + /* The task unblocked because the bits were set. */ + } + + /* The task blocked so control bits may have been set. */ + uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES; + } + + traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred ); + + /* Prevent compiler warnings when trace macros are not used. */ + ( void ) xTimeoutOccurred; + + traceRETURN_xEventGroupWaitBits( uxReturn ); + + return uxReturn; + } +/*-----------------------------------------------------------*/ + + EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToClear ) + { + EventGroup_t * pxEventBits = xEventGroup; + EventBits_t uxReturn; + + traceENTER_xEventGroupClearBits( xEventGroup, uxBitsToClear ); + + /* Check the user is not attempting to clear the bits used by the kernel + * itself. */ + configASSERT( xEventGroup ); + configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + + taskENTER_CRITICAL(); + { + traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear ); + + /* The value returned is the event group value prior to the bits being + * cleared. */ + uxReturn = pxEventBits->uxEventBits; + + /* Clear the bits. */ + pxEventBits->uxEventBits &= ~uxBitsToClear; + } + taskEXIT_CRITICAL(); + + traceRETURN_xEventGroupClearBits( uxReturn ); + + return uxReturn; + } +/*-----------------------------------------------------------*/ + + #if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) + + BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToClear ) + { + BaseType_t xReturn; + + traceENTER_xEventGroupClearBitsFromISR( xEventGroup, uxBitsToClear ); + + traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ); + xReturn = xTimerPendFunctionCallFromISR( &vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ); + + traceRETURN_xEventGroupClearBitsFromISR( xReturn ); + + return xReturn; + } + + #endif /* if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */ +/*-----------------------------------------------------------*/ + + EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) + { + UBaseType_t uxSavedInterruptStatus; + EventGroup_t const * const pxEventBits = xEventGroup; + EventBits_t uxReturn; + + traceENTER_xEventGroupGetBitsFromISR( xEventGroup ); + + /* MISRA Ref 4.7.1 [Return value shall be checked] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ + /* coverity[misra_c_2012_directive_4_7_violation] */ + uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); + { + uxReturn = pxEventBits->uxEventBits; + } + taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_xEventGroupGetBitsFromISR( uxReturn ); + + return uxReturn; + } +/*-----------------------------------------------------------*/ + + EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet ) + { + ListItem_t * pxListItem; + ListItem_t * pxNext; + ListItem_t const * pxListEnd; + List_t const * pxList; + EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits, uxReturnBits; + EventGroup_t * pxEventBits = xEventGroup; + BaseType_t xMatchFound = pdFALSE; + + traceENTER_xEventGroupSetBits( xEventGroup, uxBitsToSet ); + + /* Check the user is not attempting to set the bits used by the kernel + * itself. */ + configASSERT( xEventGroup ); + configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + + pxList = &( pxEventBits->xTasksWaitingForBits ); + pxListEnd = listGET_END_MARKER( pxList ); + vTaskSuspendAll(); + { + traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ); + + pxListItem = listGET_HEAD_ENTRY( pxList ); + + /* Set the bits. */ + pxEventBits->uxEventBits |= uxBitsToSet; + + /* See if the new bit value should unblock any tasks. */ + while( pxListItem != pxListEnd ) + { + pxNext = listGET_NEXT( pxListItem ); + uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem ); + xMatchFound = pdFALSE; + + /* Split the bits waited for from the control bits. */ + uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES; + uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES; + + if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 ) + { + /* Just looking for single bit being set. */ + if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 ) + { + xMatchFound = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor ) + { + /* All bits are set. */ + xMatchFound = pdTRUE; + } + else + { + /* Need all bits to be set, but not all the bits were set. */ + } + + if( xMatchFound != pdFALSE ) + { + /* The bits match. Should the bits be cleared on exit? */ + if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 ) + { + uxBitsToClear |= uxBitsWaitedFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Store the actual event flag value in the task's event list + * item before removing the task from the event list. The + * eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows + * that is was unblocked due to its required bits matching, rather + * than because it timed out. */ + vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET ); + } + + /* Move onto the next list item. Note pxListItem->pxNext is not + * used here as the list item may have been removed from the event list + * and inserted into the ready/pending reading list. */ + pxListItem = pxNext; + } + + /* Clear any bits that matched when the eventCLEAR_EVENTS_ON_EXIT_BIT + * bit was set in the control word. */ + pxEventBits->uxEventBits &= ~uxBitsToClear; + + /* Snapshot resulting bits. */ + uxReturnBits = pxEventBits->uxEventBits; + } + ( void ) xTaskResumeAll(); + + traceRETURN_xEventGroupSetBits( uxReturnBits ); + + return uxReturnBits; + } +/*-----------------------------------------------------------*/ + + void vEventGroupDelete( EventGroupHandle_t xEventGroup ) + { + EventGroup_t * pxEventBits = xEventGroup; + const List_t * pxTasksWaitingForBits; + + traceENTER_vEventGroupDelete( xEventGroup ); + + configASSERT( pxEventBits ); + + pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); + + vTaskSuspendAll(); + { + traceEVENT_GROUP_DELETE( xEventGroup ); + + while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 ) + { + /* Unblock the task, returning 0 as the event list is being deleted + * and cannot therefore have any bits set. */ + configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( const ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) ); + vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); + } + } + ( void ) xTaskResumeAll(); + + #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) + { + /* The event group can only have been allocated dynamically - free + * it again. */ + vPortFree( pxEventBits ); + } + #elif ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + { + /* The event group could have been allocated statically or + * dynamically, so check before attempting to free the memory. */ + if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) + { + vPortFree( pxEventBits ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + traceRETURN_vEventGroupDelete(); + } +/*-----------------------------------------------------------*/ + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + BaseType_t xEventGroupGetStaticBuffer( EventGroupHandle_t xEventGroup, + StaticEventGroup_t ** ppxEventGroupBuffer ) + { + BaseType_t xReturn; + EventGroup_t * pxEventBits = xEventGroup; + + traceENTER_xEventGroupGetStaticBuffer( xEventGroup, ppxEventGroupBuffer ); + + configASSERT( pxEventBits ); + configASSERT( ppxEventGroupBuffer ); + + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Check if the event group was statically allocated. */ + if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdTRUE ) + { + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + *ppxEventGroupBuffer = ( StaticEventGroup_t * ) pxEventBits; + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + #else /* configSUPPORT_DYNAMIC_ALLOCATION */ + { + /* Event group must have been statically allocated. */ + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + *ppxEventGroupBuffer = ( StaticEventGroup_t * ) pxEventBits; + xReturn = pdTRUE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + traceRETURN_xEventGroupGetStaticBuffer( xReturn ); + + return xReturn; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +/* For internal use only - execute a 'set bits' command that was pended from + * an interrupt. */ + void vEventGroupSetBitsCallback( void * pvEventGroup, + uint32_t ulBitsToSet ) + { + traceENTER_vEventGroupSetBitsCallback( pvEventGroup, ulBitsToSet ); + + /* MISRA Ref 11.5.4 [Callback function parameter] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + ( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet ); + + traceRETURN_vEventGroupSetBitsCallback(); + } +/*-----------------------------------------------------------*/ + +/* For internal use only - execute a 'clear bits' command that was pended from + * an interrupt. */ + void vEventGroupClearBitsCallback( void * pvEventGroup, + uint32_t ulBitsToClear ) + { + traceENTER_vEventGroupClearBitsCallback( pvEventGroup, ulBitsToClear ); + + /* MISRA Ref 11.5.4 [Callback function parameter] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + ( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear ); + + traceRETURN_vEventGroupClearBitsCallback(); + } +/*-----------------------------------------------------------*/ + + static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, + const EventBits_t uxBitsToWaitFor, + const BaseType_t xWaitForAllBits ) + { + BaseType_t xWaitConditionMet = pdFALSE; + + if( xWaitForAllBits == pdFALSE ) + { + /* Task only has to wait for one bit within uxBitsToWaitFor to be + * set. Is one already set? */ + if( ( uxCurrentEventBits & uxBitsToWaitFor ) != ( EventBits_t ) 0 ) + { + xWaitConditionMet = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Task has to wait for all the bits in uxBitsToWaitFor to be set. + * Are they set already? */ + if( ( uxCurrentEventBits & uxBitsToWaitFor ) == uxBitsToWaitFor ) + { + xWaitConditionMet = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + return xWaitConditionMet; + } +/*-----------------------------------------------------------*/ + + #if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) + + BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet, + BaseType_t * pxHigherPriorityTaskWoken ) + { + BaseType_t xReturn; + + traceENTER_xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken ); + + traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ); + xReturn = xTimerPendFunctionCallFromISR( &vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); + + traceRETURN_xEventGroupSetBitsFromISR( xReturn ); + + return xReturn; + } + + #endif /* if ( ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) */ +/*-----------------------------------------------------------*/ + + #if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxEventGroupGetNumber( void * xEventGroup ) + { + UBaseType_t xReturn; + + /* MISRA Ref 11.5.2 [Opaque pointer] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + EventGroup_t const * pxEventBits = ( EventGroup_t * ) xEventGroup; + + traceENTER_uxEventGroupGetNumber( xEventGroup ); + + if( xEventGroup == NULL ) + { + xReturn = 0; + } + else + { + xReturn = pxEventBits->uxEventGroupNumber; + } + + traceRETURN_uxEventGroupGetNumber( xReturn ); + + return xReturn; + } + + #endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + + #if ( configUSE_TRACE_FACILITY == 1 ) + + void vEventGroupSetNumber( void * xEventGroup, + UBaseType_t uxEventGroupNumber ) + { + traceENTER_vEventGroupSetNumber( xEventGroup, uxEventGroupNumber ); + + /* MISRA Ref 11.5.2 [Opaque pointer] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + ( ( EventGroup_t * ) xEventGroup )->uxEventGroupNumber = uxEventGroupNumber; + + traceRETURN_vEventGroupSetNumber(); + } + + #endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +/* This entire source file will be skipped if the application is not configured + * to include event groups functionality. If you want to include event groups + * then ensure configUSE_EVENT_GROUPS is set to 1 in FreeRTOSConfig.h. */ +#endif /* configUSE_EVENT_GROUPS == 1 */ diff --git a/vendor/FreeRTOS-Kernel/include/FreeRTOS.h b/vendor/FreeRTOS-Kernel/include/FreeRTOS.h new file mode 100644 index 0000000..f31e72a --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/FreeRTOS.h @@ -0,0 +1,3359 @@ +/* + * 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_FREERTOS_H +#define INC_FREERTOS_H + +/* + * Include the generic headers required for the FreeRTOS port being used. + */ +#include + +/* + * If stdint.h cannot be located then: + * + If using GCC ensure the -nostdint options is *not* being used. + * + Ensure the project's include path includes the directory in which your + * compiler stores stdint.h. + * + Set any compiler options necessary for it to support C99, as technically + * stdint.h is only mandatory with C99 (FreeRTOS does not require C99 in any + * other way). + * + The FreeRTOS download includes a simple stdint.h definition that can be + * used in cases where none is provided by the compiler. The files only + * contains the typedefs required to build FreeRTOS. Read the instructions + * in FreeRTOS/source/stdint.readme for more information. + */ +#include /* READ COMMENT ABOVE. */ + +/* Acceptable values for configTICK_TYPE_WIDTH_IN_BITS. */ +#define TICK_TYPE_WIDTH_16_BITS 0 +#define TICK_TYPE_WIDTH_32_BITS 1 +#define TICK_TYPE_WIDTH_64_BITS 2 + +/* Application specific configuration options. */ +#include "FreeRTOSConfig.h" + +#if !defined( configUSE_16_BIT_TICKS ) && !defined( configTICK_TYPE_WIDTH_IN_BITS ) + #error Missing definition: One of configUSE_16_BIT_TICKS and configTICK_TYPE_WIDTH_IN_BITS must be defined in FreeRTOSConfig.h. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#if defined( configUSE_16_BIT_TICKS ) && defined( configTICK_TYPE_WIDTH_IN_BITS ) + #error Only one of configUSE_16_BIT_TICKS and configTICK_TYPE_WIDTH_IN_BITS must be defined in FreeRTOSConfig.h. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +/* Define configTICK_TYPE_WIDTH_IN_BITS according to the + * value of configUSE_16_BIT_TICKS for backward compatibility. */ +#ifndef configTICK_TYPE_WIDTH_IN_BITS + #if ( configUSE_16_BIT_TICKS == 1 ) + #define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_16_BITS + #else + #define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_32_BITS + #endif +#endif + +/* Set configUSE_MPU_WRAPPERS_V1 to 1 to use MPU wrappers v1. */ +#ifndef configUSE_MPU_WRAPPERS_V1 + #define configUSE_MPU_WRAPPERS_V1 0 +#endif + +/* Set configENABLE_ACCESS_CONTROL_LIST to 1 to enable access control list support. */ +#ifndef configENABLE_ACCESS_CONTROL_LIST + #define configENABLE_ACCESS_CONTROL_LIST 0 +#endif + +/* Set default value of configNUMBER_OF_CORES to 1 to use single core FreeRTOS. */ +#ifndef configNUMBER_OF_CORES + #define configNUMBER_OF_CORES 1 +#endif + +#ifndef configUSE_MALLOC_FAILED_HOOK + #define configUSE_MALLOC_FAILED_HOOK 0 +#endif + +#ifndef configASSERT + #define configASSERT( x ) + #define configASSERT_DEFINED 0 +#else + #define configASSERT_DEFINED 1 +#endif + +/* Set configENABLE_PAC and/or configENABLE_BTI to 1 to enable PAC and/or BTI + * support and 0 to disable them. These are currently used in ARMv8.1-M ports. */ +#ifndef configENABLE_PAC + #define configENABLE_PAC 0 +#endif + +#ifndef configENABLE_BTI + #define configENABLE_BTI 0 +#endif + +/* Basic FreeRTOS definitions. */ +#include "projdefs.h" + +/* Definitions specific to the port being used. */ +#include "portable.h" + +/* Must be defaulted before configUSE_NEWLIB_REENTRANT is used below. */ +#ifndef configUSE_NEWLIB_REENTRANT + #define configUSE_NEWLIB_REENTRANT 0 +#endif + +/* Required if struct _reent is used. */ +#if ( configUSE_NEWLIB_REENTRANT == 1 ) + + #include "newlib-freertos.h" + +#endif /* if ( configUSE_NEWLIB_REENTRANT == 1 ) */ + +/* Must be defaulted before configUSE_PICOLIBC_TLS is used below. */ +#ifndef configUSE_PICOLIBC_TLS + #define configUSE_PICOLIBC_TLS 0 +#endif + +#if ( configUSE_PICOLIBC_TLS == 1 ) + + #include "picolibc-freertos.h" + +#endif /* if ( configUSE_PICOLIBC_TLS == 1 ) */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +#ifndef configUSE_C_RUNTIME_TLS_SUPPORT + #define configUSE_C_RUNTIME_TLS_SUPPORT 0 +#endif + +#if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + + #ifndef configTLS_BLOCK_TYPE + #error Missing definition: configTLS_BLOCK_TYPE must be defined in FreeRTOSConfig.h when configUSE_C_RUNTIME_TLS_SUPPORT is set to 1. + #endif + + #ifndef configINIT_TLS_BLOCK + #error Missing definition: configINIT_TLS_BLOCK must be defined in FreeRTOSConfig.h when configUSE_C_RUNTIME_TLS_SUPPORT is set to 1. + #endif + + #ifndef configSET_TLS_BLOCK + #error Missing definition: configSET_TLS_BLOCK must be defined in FreeRTOSConfig.h when configUSE_C_RUNTIME_TLS_SUPPORT is set to 1. + #endif + + #ifndef configDEINIT_TLS_BLOCK + #error Missing definition: configDEINIT_TLS_BLOCK must be defined in FreeRTOSConfig.h when configUSE_C_RUNTIME_TLS_SUPPORT is set to 1. + #endif +#endif /* if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) */ + +/* + * Check all the required application specific macros have been defined. + * These macros are application specific and (as downloaded) are defined + * within FreeRTOSConfig.h. + */ + +#ifndef configMINIMAL_STACK_SIZE + #error Missing definition: configMINIMAL_STACK_SIZE must be defined in FreeRTOSConfig.h. configMINIMAL_STACK_SIZE defines the size (in words) of the stack allocated to the idle task. Refer to the demo project provided for your port for a suitable value. +#endif + +#ifndef configMAX_PRIORITIES + #error Missing definition: configMAX_PRIORITIES must be defined in FreeRTOSConfig.h. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#if configMAX_PRIORITIES < 1 + #error configMAX_PRIORITIES must be defined to be greater than or equal to 1. +#endif + +#ifndef configUSE_PREEMPTION + #error Missing definition: configUSE_PREEMPTION must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_IDLE_HOOK + #error Missing definition: configUSE_IDLE_HOOK must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#if ( configNUMBER_OF_CORES > 1 ) + #ifndef configUSE_PASSIVE_IDLE_HOOK + #error Missing definition: configUSE_PASSIVE_IDLE_HOOK must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. + #endif +#endif + +#ifndef configUSE_TICK_HOOK + #error Missing definition: configUSE_TICK_HOOK must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#if ( ( configTICK_TYPE_WIDTH_IN_BITS != TICK_TYPE_WIDTH_16_BITS ) && \ + ( configTICK_TYPE_WIDTH_IN_BITS != TICK_TYPE_WIDTH_32_BITS ) && \ + ( configTICK_TYPE_WIDTH_IN_BITS != TICK_TYPE_WIDTH_64_BITS ) ) + #error Macro configTICK_TYPE_WIDTH_IN_BITS is defined to incorrect value. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_CO_ROUTINES + #define configUSE_CO_ROUTINES 0 +#endif + +#ifndef INCLUDE_vTaskPrioritySet + #define INCLUDE_vTaskPrioritySet 0 +#endif + +#ifndef INCLUDE_uxTaskPriorityGet + #define INCLUDE_uxTaskPriorityGet 0 +#endif + +#ifndef INCLUDE_vTaskDelete + #define INCLUDE_vTaskDelete 0 +#endif + +#ifndef INCLUDE_vTaskSuspend + #define INCLUDE_vTaskSuspend 0 +#endif + +#ifdef INCLUDE_xTaskDelayUntil + #ifdef INCLUDE_vTaskDelayUntil + +/* INCLUDE_vTaskDelayUntil was replaced by INCLUDE_xTaskDelayUntil. Backward + * compatibility is maintained if only one or the other is defined, but + * there is a conflict if both are defined. */ + #error INCLUDE_vTaskDelayUntil and INCLUDE_xTaskDelayUntil are both defined. INCLUDE_vTaskDelayUntil is no longer required and should be removed + #endif +#endif + +#ifndef INCLUDE_xTaskDelayUntil + #ifdef INCLUDE_vTaskDelayUntil + +/* If INCLUDE_vTaskDelayUntil is set but INCLUDE_xTaskDelayUntil is not then + * the project's FreeRTOSConfig.h probably pre-dates the introduction of + * xTaskDelayUntil and setting INCLUDE_xTaskDelayUntil to whatever + * INCLUDE_vTaskDelayUntil is set to will ensure backward compatibility. + */ + #define INCLUDE_xTaskDelayUntil INCLUDE_vTaskDelayUntil + #endif +#endif + +#ifndef INCLUDE_xTaskDelayUntil + #define INCLUDE_xTaskDelayUntil 0 +#endif + +#ifndef INCLUDE_vTaskDelay + #define INCLUDE_vTaskDelay 0 +#endif + +#ifndef INCLUDE_xTaskGetIdleTaskHandle + #define INCLUDE_xTaskGetIdleTaskHandle 0 +#endif + +#ifndef INCLUDE_xTaskAbortDelay + #define INCLUDE_xTaskAbortDelay 0 +#endif + +#ifndef INCLUDE_xQueueGetMutexHolder + #define INCLUDE_xQueueGetMutexHolder 0 +#endif + +#ifndef INCLUDE_xSemaphoreGetMutexHolder + #define INCLUDE_xSemaphoreGetMutexHolder INCLUDE_xQueueGetMutexHolder +#endif + +#ifndef INCLUDE_xTaskGetHandle + #define INCLUDE_xTaskGetHandle 0 +#endif + +#ifndef INCLUDE_uxTaskGetStackHighWaterMark + #define INCLUDE_uxTaskGetStackHighWaterMark 0 +#endif + +#ifndef INCLUDE_uxTaskGetStackHighWaterMark2 + #define INCLUDE_uxTaskGetStackHighWaterMark2 0 +#endif + +#ifndef INCLUDE_eTaskGetState + #define INCLUDE_eTaskGetState 0 +#endif + +#ifndef INCLUDE_xTaskResumeFromISR + #define INCLUDE_xTaskResumeFromISR 1 +#endif + +#ifndef INCLUDE_xTimerPendFunctionCall + #define INCLUDE_xTimerPendFunctionCall 0 +#endif + +#ifndef INCLUDE_xTaskGetSchedulerState + #define INCLUDE_xTaskGetSchedulerState 0 +#endif + +#ifndef INCLUDE_xTaskGetCurrentTaskHandle + #define INCLUDE_xTaskGetCurrentTaskHandle 1 +#endif + +#if configUSE_CO_ROUTINES != 0 + #ifndef configMAX_CO_ROUTINE_PRIORITIES + #error configMAX_CO_ROUTINE_PRIORITIES must be greater than or equal to 1. + #endif +#endif + +#ifndef configUSE_APPLICATION_TASK_TAG + #define configUSE_APPLICATION_TASK_TAG 0 +#endif + +#ifndef configNUM_THREAD_LOCAL_STORAGE_POINTERS + #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 +#endif + +#ifndef configUSE_RECURSIVE_MUTEXES + #define configUSE_RECURSIVE_MUTEXES 0 +#endif + +#ifndef configUSE_MUTEXES + #define configUSE_MUTEXES 0 +#endif + +#ifndef configUSE_TIMERS + #define configUSE_TIMERS 0 +#endif + +#ifndef configUSE_EVENT_GROUPS + #define configUSE_EVENT_GROUPS 1 +#endif + +#ifndef configUSE_STREAM_BUFFERS + #define configUSE_STREAM_BUFFERS 1 +#endif + +#ifndef configUSE_DAEMON_TASK_STARTUP_HOOK + #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 +#endif + +#if ( configUSE_DAEMON_TASK_STARTUP_HOOK != 0 ) + #if ( configUSE_TIMERS == 0 ) + #error configUSE_DAEMON_TASK_STARTUP_HOOK is set, but the daemon task is not created because configUSE_TIMERS is 0. + #endif +#endif + +#ifndef configUSE_COUNTING_SEMAPHORES + #define configUSE_COUNTING_SEMAPHORES 0 +#endif + +#ifndef configUSE_TASK_PREEMPTION_DISABLE + #define configUSE_TASK_PREEMPTION_DISABLE 0 +#endif + +#ifndef configUSE_ALTERNATIVE_API + #define configUSE_ALTERNATIVE_API 0 +#endif + +#ifndef portCRITICAL_NESTING_IN_TCB + #define portCRITICAL_NESTING_IN_TCB 0 +#endif + +#ifndef configMAX_TASK_NAME_LEN + #define configMAX_TASK_NAME_LEN 16 +#endif + +#ifndef configIDLE_SHOULD_YIELD + #define configIDLE_SHOULD_YIELD 1 +#endif + +#if configMAX_TASK_NAME_LEN < 1 + #error configMAX_TASK_NAME_LEN must be set to a minimum of 1 in FreeRTOSConfig.h +#endif + +/* configPRECONDITION should be defined as configASSERT. + * The CBMC proofs need a way to track assumptions and assertions. + * A configPRECONDITION statement should express an implicit invariant or + * assumption made. A configASSERT statement should express an invariant that must + * hold explicit before calling the code. */ +#ifndef configPRECONDITION + #define configPRECONDITION( X ) configASSERT( X ) + #define configPRECONDITION_DEFINED 0 +#else + #define configPRECONDITION_DEFINED 1 +#endif + +#ifndef configCHECK_HANDLER_INSTALLATION + #define configCHECK_HANDLER_INSTALLATION 1 +#else + +/* The application has explicitly defined configCHECK_HANDLER_INSTALLATION + * to 1. The checks requires configASSERT() to be defined. */ + #if ( ( configCHECK_HANDLER_INSTALLATION == 1 ) && ( configASSERT_DEFINED == 0 ) ) + #error You must define configASSERT() when configCHECK_HANDLER_INSTALLATION is 1. + #endif +#endif + +#ifndef portMEMORY_BARRIER + #define portMEMORY_BARRIER() +#endif + +#ifndef portSOFTWARE_BARRIER + #define portSOFTWARE_BARRIER() +#endif + +#ifndef configRUN_MULTIPLE_PRIORITIES + #define configRUN_MULTIPLE_PRIORITIES 0 +#endif + +#ifndef portGET_CORE_ID + + #if ( configNUMBER_OF_CORES == 1 ) + #define portGET_CORE_ID() 0 + #else + #error configNUMBER_OF_CORES is set to more than 1 then portGET_CORE_ID must also be defined. + #endif /* configNUMBER_OF_CORES */ + +#endif /* portGET_CORE_ID */ + +#ifndef portYIELD_CORE + + #if ( configNUMBER_OF_CORES == 1 ) + #define portYIELD_CORE( x ) portYIELD() + #else + #error configNUMBER_OF_CORES is set to more than 1 then portYIELD_CORE must also be defined. + #endif /* configNUMBER_OF_CORES */ + +#endif /* portYIELD_CORE */ + +#ifndef portSET_INTERRUPT_MASK + + #if ( configNUMBER_OF_CORES > 1 ) + #error portSET_INTERRUPT_MASK is required in SMP + #endif + +#endif /* portSET_INTERRUPT_MASK */ + +#ifndef portCLEAR_INTERRUPT_MASK + + #if ( configNUMBER_OF_CORES > 1 ) + #error portCLEAR_INTERRUPT_MASK is required in SMP + #endif + +#endif /* portCLEAR_INTERRUPT_MASK */ + +#ifndef portRELEASE_TASK_LOCK + + #if ( configNUMBER_OF_CORES == 1 ) + #define portRELEASE_TASK_LOCK( xCoreID ) + #else + #error portRELEASE_TASK_LOCK is required in SMP + #endif + +#endif /* portRELEASE_TASK_LOCK */ + +#ifndef portGET_TASK_LOCK + + #if ( configNUMBER_OF_CORES == 1 ) + #define portGET_TASK_LOCK( xCoreID ) + #else + #error portGET_TASK_LOCK is required in SMP + #endif + +#endif /* portGET_TASK_LOCK */ + +#ifndef portRELEASE_ISR_LOCK + + #if ( configNUMBER_OF_CORES == 1 ) + #define portRELEASE_ISR_LOCK( xCoreID ) + #else + #error portRELEASE_ISR_LOCK is required in SMP + #endif + +#endif /* portRELEASE_ISR_LOCK */ + +#ifndef portGET_ISR_LOCK + + #if ( configNUMBER_OF_CORES == 1 ) + #define portGET_ISR_LOCK( xCoreID ) + #else + #error portGET_ISR_LOCK is required in SMP + #endif + +#endif /* portGET_ISR_LOCK */ + +#ifndef portENTER_CRITICAL_FROM_ISR + + #if ( configNUMBER_OF_CORES > 1 ) + #error portENTER_CRITICAL_FROM_ISR is required in SMP + #endif + +#endif + +#ifndef portEXIT_CRITICAL_FROM_ISR + + #if ( configNUMBER_OF_CORES > 1 ) + #error portEXIT_CRITICAL_FROM_ISR is required in SMP + #endif + +#endif + +#ifndef configUSE_CORE_AFFINITY + #define configUSE_CORE_AFFINITY 0 +#endif /* configUSE_CORE_AFFINITY */ + +#if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + #ifndef configTASK_DEFAULT_CORE_AFFINITY + #define configTASK_DEFAULT_CORE_AFFINITY tskNO_AFFINITY + #endif +#endif + +#ifndef configUSE_PASSIVE_IDLE_HOOK + #define configUSE_PASSIVE_IDLE_HOOK 0 +#endif /* configUSE_PASSIVE_IDLE_HOOK */ + +/* The timers module relies on xTaskGetSchedulerState(). */ +#if configUSE_TIMERS == 1 + + #ifndef configTIMER_TASK_PRIORITY + #error If configUSE_TIMERS is set to 1 then configTIMER_TASK_PRIORITY must also be defined. + #endif /* configTIMER_TASK_PRIORITY */ + + #ifndef configTIMER_QUEUE_LENGTH + #error If configUSE_TIMERS is set to 1 then configTIMER_QUEUE_LENGTH must also be defined. + #endif /* configTIMER_QUEUE_LENGTH */ + + #ifndef configTIMER_TASK_STACK_DEPTH + #error If configUSE_TIMERS is set to 1 then configTIMER_TASK_STACK_DEPTH must also be defined. + #endif /* configTIMER_TASK_STACK_DEPTH */ + + #ifndef portTIMER_CALLBACK_ATTRIBUTE + #define portTIMER_CALLBACK_ATTRIBUTE + #endif /* portTIMER_CALLBACK_ATTRIBUTE */ + +#endif /* configUSE_TIMERS */ + +#ifndef portHAS_NESTED_INTERRUPTS + #if defined( portSET_INTERRUPT_MASK_FROM_ISR ) && defined( portCLEAR_INTERRUPT_MASK_FROM_ISR ) + #define portHAS_NESTED_INTERRUPTS 1 + #else + #define portHAS_NESTED_INTERRUPTS 0 + #endif +#endif + +#ifndef portSET_INTERRUPT_MASK_FROM_ISR + #if ( portHAS_NESTED_INTERRUPTS == 1 ) + #error portSET_INTERRUPT_MASK_FROM_ISR must be defined for ports that support nested interrupts (i.e. portHAS_NESTED_INTERRUPTS is set to 1) + #else + #define portSET_INTERRUPT_MASK_FROM_ISR() 0 + #endif +#else + #if ( portHAS_NESTED_INTERRUPTS == 0 ) + #error portSET_INTERRUPT_MASK_FROM_ISR must not be defined for ports that do not support nested interrupts (i.e. portHAS_NESTED_INTERRUPTS is set to 0) + #endif +#endif + +#ifndef portCLEAR_INTERRUPT_MASK_FROM_ISR + #if ( portHAS_NESTED_INTERRUPTS == 1 ) + #error portCLEAR_INTERRUPT_MASK_FROM_ISR must be defined for ports that support nested interrupts (i.e. portHAS_NESTED_INTERRUPTS is set to 1) + #else + #define portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedStatusValue ) ( void ) ( uxSavedStatusValue ) + #endif +#else + #if ( portHAS_NESTED_INTERRUPTS == 0 ) + #error portCLEAR_INTERRUPT_MASK_FROM_ISR must not be defined for ports that do not support nested interrupts (i.e. portHAS_NESTED_INTERRUPTS is set to 0) + #endif +#endif + +#ifndef portCLEAN_UP_TCB + #define portCLEAN_UP_TCB( pxTCB ) ( void ) ( pxTCB ) +#endif + +#ifndef portPRE_TASK_DELETE_HOOK + #define portPRE_TASK_DELETE_HOOK( pvTaskToDelete, pxYieldPending ) +#endif + +#ifndef portSETUP_TCB + #define portSETUP_TCB( pxTCB ) ( void ) ( pxTCB ) +#endif + +#ifndef portTASK_SWITCH_HOOK + #define portTASK_SWITCH_HOOK( pxTCB ) ( void ) ( pxTCB ) +#endif + +#ifndef configQUEUE_REGISTRY_SIZE + #define configQUEUE_REGISTRY_SIZE 0U +#endif + +#if ( configQUEUE_REGISTRY_SIZE < 1 ) + #define vQueueAddToRegistry( xQueue, pcName ) + #define vQueueUnregisterQueue( xQueue ) + #define pcQueueGetName( xQueue ) +#endif + +#ifndef configUSE_MINI_LIST_ITEM + #define configUSE_MINI_LIST_ITEM 1 +#endif + +#ifndef portPOINTER_SIZE_TYPE + #define portPOINTER_SIZE_TYPE uint32_t +#endif + +/* Remove any unused trace macros. */ +#ifndef traceSTART + +/* Used to perform any necessary initialisation - for example, open a file + * into which trace is to be written. */ + #define traceSTART() +#endif + +#ifndef traceEND + +/* Use to close a trace, for example close a file into which trace has been + * written. */ + #define traceEND() +#endif + +#ifndef traceTASK_SWITCHED_IN + +/* Called after a task has been selected to run. pxCurrentTCB holds a pointer + * to the task control block of the selected task. */ + #define traceTASK_SWITCHED_IN() +#endif + +#ifndef traceSTARTING_SCHEDULER + +/* Called after all idle tasks and timer task (if enabled) have been created + * successfully, just before the scheduler is started. */ + #define traceSTARTING_SCHEDULER( xIdleTaskHandles ) +#endif + +#ifndef traceINCREASE_TICK_COUNT + +/* Called before stepping the tick count after waking from tickless idle + * sleep. */ + #define traceINCREASE_TICK_COUNT( x ) +#endif + +#ifndef traceLOW_POWER_IDLE_BEGIN + /* Called immediately before entering tickless idle. */ + #define traceLOW_POWER_IDLE_BEGIN() +#endif + +#ifndef traceLOW_POWER_IDLE_END + /* Called when returning to the Idle task after a tickless idle. */ + #define traceLOW_POWER_IDLE_END() +#endif + +#ifndef traceTASK_SWITCHED_OUT + +/* Called before a task has been selected to run. pxCurrentTCB holds a pointer + * to the task control block of the task being switched out. */ + #define traceTASK_SWITCHED_OUT() +#endif + +#ifndef traceTASK_PRIORITY_INHERIT + +/* Called when a task attempts to take a mutex that is already held by a + * lower priority task. pxTCBOfMutexHolder is a pointer to the TCB of the task + * that holds the mutex. uxInheritedPriority is the priority the mutex holder + * will inherit (the priority of the task that is attempting to obtain the + * muted. */ + #define traceTASK_PRIORITY_INHERIT( pxTCBOfMutexHolder, uxInheritedPriority ) +#endif + +#ifndef traceTASK_PRIORITY_DISINHERIT + +/* Called when a task releases a mutex, the holding of which had resulted in + * the task inheriting the priority of a higher priority task. + * pxTCBOfMutexHolder is a pointer to the TCB of the task that is releasing the + * mutex. uxOriginalPriority is the task's configured (base) priority. */ + #define traceTASK_PRIORITY_DISINHERIT( pxTCBOfMutexHolder, uxOriginalPriority ) +#endif + +#ifndef traceBLOCKING_ON_QUEUE_RECEIVE + +/* Task is about to block because it cannot read from a + * queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + * upon which the read was attempted. pxCurrentTCB points to the TCB of the + * task that attempted the read. */ + #define traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ) +#endif + +#ifndef traceBLOCKING_ON_QUEUE_PEEK + +/* Task is about to block because it cannot read from a + * queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + * upon which the read was attempted. pxCurrentTCB points to the TCB of the + * task that attempted the read. */ + #define traceBLOCKING_ON_QUEUE_PEEK( pxQueue ) +#endif + +#ifndef traceBLOCKING_ON_QUEUE_SEND + +/* Task is about to block because it cannot write to a + * queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + * upon which the write was attempted. pxCurrentTCB points to the TCB of the + * task that attempted the write. */ + #define traceBLOCKING_ON_QUEUE_SEND( pxQueue ) +#endif + +#ifndef configCHECK_FOR_STACK_OVERFLOW + #define configCHECK_FOR_STACK_OVERFLOW 0 +#endif + +#ifndef configRECORD_STACK_HIGH_ADDRESS + #define configRECORD_STACK_HIGH_ADDRESS 0 +#endif + +#ifndef configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H + #define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 0 +#endif + +/* The following event macros are embedded in the kernel API calls. */ + +#ifndef traceMOVED_TASK_TO_READY_STATE + #define traceMOVED_TASK_TO_READY_STATE( pxTCB ) +#endif + +#ifndef tracePOST_MOVED_TASK_TO_READY_STATE + #define tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ) +#endif + +#ifndef traceMOVED_TASK_TO_DELAYED_LIST + #define traceMOVED_TASK_TO_DELAYED_LIST() +#endif + +#ifndef traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST + #define traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST() +#endif + +#ifndef traceQUEUE_CREATE + #define traceQUEUE_CREATE( pxNewQueue ) +#endif + +#ifndef traceQUEUE_CREATE_FAILED + #define traceQUEUE_CREATE_FAILED( ucQueueType ) +#endif + +#ifndef traceCREATE_MUTEX + #define traceCREATE_MUTEX( pxNewQueue ) +#endif + +#ifndef traceCREATE_MUTEX_FAILED + #define traceCREATE_MUTEX_FAILED() +#endif + +#ifndef traceGIVE_MUTEX_RECURSIVE + #define traceGIVE_MUTEX_RECURSIVE( pxMutex ) +#endif + +#ifndef traceGIVE_MUTEX_RECURSIVE_FAILED + #define traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ) +#endif + +#ifndef traceTAKE_MUTEX_RECURSIVE + #define traceTAKE_MUTEX_RECURSIVE( pxMutex ) +#endif + +#ifndef traceTAKE_MUTEX_RECURSIVE_FAILED + #define traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ) +#endif + +#ifndef traceCREATE_COUNTING_SEMAPHORE + #define traceCREATE_COUNTING_SEMAPHORE() +#endif + +#ifndef traceCREATE_COUNTING_SEMAPHORE_FAILED + #define traceCREATE_COUNTING_SEMAPHORE_FAILED() +#endif + +#ifndef traceQUEUE_SET_SEND + #define traceQUEUE_SET_SEND traceQUEUE_SEND +#endif + +#ifndef traceQUEUE_SEND + #define traceQUEUE_SEND( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FAILED + #define traceQUEUE_SEND_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE + #define traceQUEUE_RECEIVE( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK + #define traceQUEUE_PEEK( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK_FAILED + #define traceQUEUE_PEEK_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK_FROM_ISR + #define traceQUEUE_PEEK_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FAILED + #define traceQUEUE_RECEIVE_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FROM_ISR + #define traceQUEUE_SEND_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FROM_ISR_FAILED + #define traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FROM_ISR + #define traceQUEUE_RECEIVE_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FROM_ISR_FAILED + #define traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK_FROM_ISR_FAILED + #define traceQUEUE_PEEK_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_DELETE + #define traceQUEUE_DELETE( pxQueue ) +#endif + +#ifndef traceTASK_CREATE + #define traceTASK_CREATE( pxNewTCB ) +#endif + +#ifndef traceTASK_CREATE_FAILED + #define traceTASK_CREATE_FAILED() +#endif + +#ifndef traceTASK_DELETE + #define traceTASK_DELETE( pxTaskToDelete ) +#endif + +#ifndef traceTASK_DELAY_UNTIL + #define traceTASK_DELAY_UNTIL( x ) +#endif + +#ifndef traceTASK_DELAY + #define traceTASK_DELAY() +#endif + +#ifndef traceTASK_PRIORITY_SET + #define traceTASK_PRIORITY_SET( pxTask, uxNewPriority ) +#endif + +#ifndef traceTASK_SUSPEND + #define traceTASK_SUSPEND( pxTaskToSuspend ) +#endif + +#ifndef traceTASK_RESUME + #define traceTASK_RESUME( pxTaskToResume ) +#endif + +#ifndef traceTASK_RESUME_FROM_ISR + #define traceTASK_RESUME_FROM_ISR( pxTaskToResume ) +#endif + +#ifndef traceTASK_INCREMENT_TICK + #define traceTASK_INCREMENT_TICK( xTickCount ) +#endif + +#ifndef traceTIMER_CREATE + #define traceTIMER_CREATE( pxNewTimer ) +#endif + +#ifndef traceTIMER_CREATE_FAILED + #define traceTIMER_CREATE_FAILED() +#endif + +#ifndef traceTIMER_COMMAND_SEND + #define traceTIMER_COMMAND_SEND( xTimer, xMessageID, xMessageValueValue, xReturn ) +#endif + +#ifndef traceTIMER_EXPIRED + #define traceTIMER_EXPIRED( pxTimer ) +#endif + +#ifndef traceTIMER_COMMAND_RECEIVED + #define traceTIMER_COMMAND_RECEIVED( pxTimer, xMessageID, xMessageValue ) +#endif + +#ifndef traceMALLOC + #define traceMALLOC( pvAddress, uiSize ) +#endif + +#ifndef traceFREE + #define traceFREE( pvAddress, uiSize ) +#endif + +#ifndef traceEVENT_GROUP_CREATE + #define traceEVENT_GROUP_CREATE( xEventGroup ) +#endif + +#ifndef traceEVENT_GROUP_CREATE_FAILED + #define traceEVENT_GROUP_CREATE_FAILED() +#endif + +#ifndef traceEVENT_GROUP_SYNC_BLOCK + #define traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor ) +#endif + +#ifndef traceEVENT_GROUP_SYNC_END + #define traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred ) ( void ) ( xTimeoutOccurred ) +#endif + +#ifndef traceEVENT_GROUP_WAIT_BITS_BLOCK + #define traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor ) +#endif + +#ifndef traceEVENT_GROUP_WAIT_BITS_END + #define traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred ) ( void ) ( xTimeoutOccurred ) +#endif + +#ifndef traceEVENT_GROUP_CLEAR_BITS + #define traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear ) +#endif + +#ifndef traceEVENT_GROUP_CLEAR_BITS_FROM_ISR + #define traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ) +#endif + +#ifndef traceEVENT_GROUP_SET_BITS + #define traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ) +#endif + +#ifndef traceEVENT_GROUP_SET_BITS_FROM_ISR + #define traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ) +#endif + +#ifndef traceEVENT_GROUP_DELETE + #define traceEVENT_GROUP_DELETE( xEventGroup ) +#endif + +#ifndef tracePEND_FUNC_CALL + #define tracePEND_FUNC_CALL( xFunctionToPend, pvParameter1, ulParameter2, ret ) +#endif + +#ifndef tracePEND_FUNC_CALL_FROM_ISR + #define tracePEND_FUNC_CALL_FROM_ISR( xFunctionToPend, pvParameter1, ulParameter2, ret ) +#endif + +#ifndef traceQUEUE_REGISTRY_ADD + #define traceQUEUE_REGISTRY_ADD( xQueue, pcQueueName ) +#endif + +#ifndef traceTASK_NOTIFY_TAKE_BLOCK + #define traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWait ) +#endif + +#ifndef traceTASK_NOTIFY_TAKE + #define traceTASK_NOTIFY_TAKE( uxIndexToWait ) +#endif + +#ifndef traceTASK_NOTIFY_WAIT_BLOCK + #define traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWait ) +#endif + +#ifndef traceTASK_NOTIFY_WAIT + #define traceTASK_NOTIFY_WAIT( uxIndexToWait ) +#endif + +#ifndef traceTASK_NOTIFY + #define traceTASK_NOTIFY( uxIndexToNotify ) +#endif + +#ifndef traceTASK_NOTIFY_FROM_ISR + #define traceTASK_NOTIFY_FROM_ISR( uxIndexToNotify ) +#endif + +#ifndef traceTASK_NOTIFY_GIVE_FROM_ISR + #define traceTASK_NOTIFY_GIVE_FROM_ISR( uxIndexToNotify ) +#endif + +#ifndef traceISR_EXIT_TO_SCHEDULER + #define traceISR_EXIT_TO_SCHEDULER() +#endif + +#ifndef traceISR_EXIT + #define traceISR_EXIT() +#endif + +#ifndef traceISR_ENTER + #define traceISR_ENTER() +#endif + +#ifndef traceSTREAM_BUFFER_CREATE_FAILED + #define traceSTREAM_BUFFER_CREATE_FAILED( xStreamBufferType ) +#endif + +#ifndef traceSTREAM_BUFFER_CREATE_STATIC_FAILED + #define traceSTREAM_BUFFER_CREATE_STATIC_FAILED( xReturn, xStreamBufferType ) +#endif + +#ifndef traceSTREAM_BUFFER_CREATE + #define traceSTREAM_BUFFER_CREATE( pxStreamBuffer, xStreamBufferType ) +#endif + +#ifndef traceSTREAM_BUFFER_DELETE + #define traceSTREAM_BUFFER_DELETE( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RESET + #define traceSTREAM_BUFFER_RESET( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RESET_FROM_ISR + #define traceSTREAM_BUFFER_RESET_FROM_ISR( xStreamBuffer ) +#endif + +#ifndef traceBLOCKING_ON_STREAM_BUFFER_SEND + #define traceBLOCKING_ON_STREAM_BUFFER_SEND( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND + #define traceSTREAM_BUFFER_SEND( xStreamBuffer, xBytesSent ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND_FAILED + #define traceSTREAM_BUFFER_SEND_FAILED( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND_FROM_ISR + #define traceSTREAM_BUFFER_SEND_FROM_ISR( xStreamBuffer, xBytesSent ) +#endif + +#ifndef traceBLOCKING_ON_STREAM_BUFFER_RECEIVE + #define traceBLOCKING_ON_STREAM_BUFFER_RECEIVE( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE + #define traceSTREAM_BUFFER_RECEIVE( xStreamBuffer, xReceivedLength ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE_FAILED + #define traceSTREAM_BUFFER_RECEIVE_FAILED( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE_FROM_ISR + #define traceSTREAM_BUFFER_RECEIVE_FROM_ISR( xStreamBuffer, xReceivedLength ) +#endif + +#ifndef traceENTER_xEventGroupCreateStatic + #define traceENTER_xEventGroupCreateStatic( pxEventGroupBuffer ) +#endif + +#ifndef traceRETURN_xEventGroupCreateStatic + #define traceRETURN_xEventGroupCreateStatic( pxEventBits ) +#endif + +#ifndef traceENTER_xEventGroupCreate + #define traceENTER_xEventGroupCreate() +#endif + +#ifndef traceRETURN_xEventGroupCreate + #define traceRETURN_xEventGroupCreate( pxEventBits ) +#endif + +#ifndef traceENTER_xEventGroupSync + #define traceENTER_xEventGroupSync( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTicksToWait ) +#endif + +#ifndef traceRETURN_xEventGroupSync + #define traceRETURN_xEventGroupSync( uxReturn ) +#endif + +#ifndef traceENTER_xEventGroupWaitBits + #define traceENTER_xEventGroupWaitBits( xEventGroup, uxBitsToWaitFor, xClearOnExit, xWaitForAllBits, xTicksToWait ) +#endif + +#ifndef traceRETURN_xEventGroupWaitBits + #define traceRETURN_xEventGroupWaitBits( uxReturn ) +#endif + +#ifndef traceENTER_xEventGroupClearBits + #define traceENTER_xEventGroupClearBits( xEventGroup, uxBitsToClear ) +#endif + +#ifndef traceRETURN_xEventGroupClearBits + #define traceRETURN_xEventGroupClearBits( uxReturn ) +#endif + +#ifndef traceENTER_xEventGroupClearBitsFromISR + #define traceENTER_xEventGroupClearBitsFromISR( xEventGroup, uxBitsToClear ) +#endif + +#ifndef traceRETURN_xEventGroupClearBitsFromISR + #define traceRETURN_xEventGroupClearBitsFromISR( xReturn ) +#endif + +#ifndef traceENTER_xEventGroupGetBitsFromISR + #define traceENTER_xEventGroupGetBitsFromISR( xEventGroup ) +#endif + +#ifndef traceRETURN_xEventGroupGetBitsFromISR + #define traceRETURN_xEventGroupGetBitsFromISR( uxReturn ) +#endif + +#ifndef traceENTER_xEventGroupSetBits + #define traceENTER_xEventGroupSetBits( xEventGroup, uxBitsToSet ) +#endif + +#ifndef traceRETURN_xEventGroupSetBits + #define traceRETURN_xEventGroupSetBits( uxEventBits ) +#endif + +#ifndef traceENTER_vEventGroupDelete + #define traceENTER_vEventGroupDelete( xEventGroup ) +#endif + +#ifndef traceRETURN_vEventGroupDelete + #define traceRETURN_vEventGroupDelete() +#endif + +#ifndef traceENTER_xEventGroupGetStaticBuffer + #define traceENTER_xEventGroupGetStaticBuffer( xEventGroup, ppxEventGroupBuffer ) +#endif + +#ifndef traceRETURN_xEventGroupGetStaticBuffer + #define traceRETURN_xEventGroupGetStaticBuffer( xReturn ) +#endif + +#ifndef traceENTER_vEventGroupSetBitsCallback + #define traceENTER_vEventGroupSetBitsCallback( pvEventGroup, ulBitsToSet ) +#endif + +#ifndef traceRETURN_vEventGroupSetBitsCallback + #define traceRETURN_vEventGroupSetBitsCallback() +#endif + +#ifndef traceENTER_vEventGroupClearBitsCallback + #define traceENTER_vEventGroupClearBitsCallback( pvEventGroup, ulBitsToClear ) +#endif + +#ifndef traceRETURN_vEventGroupClearBitsCallback + #define traceRETURN_vEventGroupClearBitsCallback() +#endif + +#ifndef traceENTER_xEventGroupSetBitsFromISR + #define traceENTER_xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken ) +#endif + +#ifndef traceRETURN_xEventGroupSetBitsFromISR + #define traceRETURN_xEventGroupSetBitsFromISR( xReturn ) +#endif + +#ifndef traceENTER_uxEventGroupGetNumber + #define traceENTER_uxEventGroupGetNumber( xEventGroup ) +#endif + +#ifndef traceRETURN_uxEventGroupGetNumber + #define traceRETURN_uxEventGroupGetNumber( xReturn ) +#endif + +#ifndef traceENTER_vEventGroupSetNumber + #define traceENTER_vEventGroupSetNumber( xEventGroup, uxEventGroupNumber ) +#endif + +#ifndef traceRETURN_vEventGroupSetNumber + #define traceRETURN_vEventGroupSetNumber() +#endif + +#ifndef traceENTER_xQueueGenericReset + #define traceENTER_xQueueGenericReset( xQueue, xNewQueue ) +#endif + +#ifndef traceRETURN_xQueueGenericReset + #define traceRETURN_xQueueGenericReset( xReturn ) +#endif + +#ifndef traceENTER_xQueueGenericCreateStatic + #define traceENTER_xQueueGenericCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxStaticQueue, ucQueueType ) +#endif + +#ifndef traceRETURN_xQueueGenericCreateStatic + #define traceRETURN_xQueueGenericCreateStatic( pxNewQueue ) +#endif + +#ifndef traceENTER_xQueueGenericGetStaticBuffers + #define traceENTER_xQueueGenericGetStaticBuffers( xQueue, ppucQueueStorage, ppxStaticQueue ) +#endif + +#ifndef traceRETURN_xQueueGenericGetStaticBuffers + #define traceRETURN_xQueueGenericGetStaticBuffers( xReturn ) +#endif + +#ifndef traceENTER_xQueueGenericCreate + #define traceENTER_xQueueGenericCreate( uxQueueLength, uxItemSize, ucQueueType ) +#endif + +#ifndef traceRETURN_xQueueGenericCreate + #define traceRETURN_xQueueGenericCreate( pxNewQueue ) +#endif + +#ifndef traceENTER_xQueueCreateMutex + #define traceENTER_xQueueCreateMutex( ucQueueType ) +#endif + +#ifndef traceRETURN_xQueueCreateMutex + #define traceRETURN_xQueueCreateMutex( xNewQueue ) +#endif + +#ifndef traceENTER_xQueueCreateMutexStatic + #define traceENTER_xQueueCreateMutexStatic( ucQueueType, pxStaticQueue ) +#endif + +#ifndef traceRETURN_xQueueCreateMutexStatic + #define traceRETURN_xQueueCreateMutexStatic( xNewQueue ) +#endif + +#ifndef traceENTER_xQueueGetMutexHolder + #define traceENTER_xQueueGetMutexHolder( xSemaphore ) +#endif + +#ifndef traceRETURN_xQueueGetMutexHolder + #define traceRETURN_xQueueGetMutexHolder( pxReturn ) +#endif + +#ifndef traceENTER_xQueueGetMutexHolderFromISR + #define traceENTER_xQueueGetMutexHolderFromISR( xSemaphore ) +#endif + +#ifndef traceRETURN_xQueueGetMutexHolderFromISR + #define traceRETURN_xQueueGetMutexHolderFromISR( pxReturn ) +#endif + +#ifndef traceENTER_xQueueGiveMutexRecursive + #define traceENTER_xQueueGiveMutexRecursive( xMutex ) +#endif + +#ifndef traceRETURN_xQueueGiveMutexRecursive + #define traceRETURN_xQueueGiveMutexRecursive( xReturn ) +#endif + +#ifndef traceENTER_xQueueTakeMutexRecursive + #define traceENTER_xQueueTakeMutexRecursive( xMutex, xTicksToWait ) +#endif + +#ifndef traceRETURN_xQueueTakeMutexRecursive + #define traceRETURN_xQueueTakeMutexRecursive( xReturn ) +#endif + +#ifndef traceENTER_xQueueCreateCountingSemaphoreStatic + #define traceENTER_xQueueCreateCountingSemaphoreStatic( uxMaxCount, uxInitialCount, pxStaticQueue ) +#endif + +#ifndef traceRETURN_xQueueCreateCountingSemaphoreStatic + #define traceRETURN_xQueueCreateCountingSemaphoreStatic( xHandle ) +#endif + +#ifndef traceENTER_xQueueCreateCountingSemaphore + #define traceENTER_xQueueCreateCountingSemaphore( uxMaxCount, uxInitialCount ) +#endif + +#ifndef traceRETURN_xQueueCreateCountingSemaphore + #define traceRETURN_xQueueCreateCountingSemaphore( xHandle ) +#endif + +#ifndef traceENTER_xQueueGenericSend + #define traceENTER_xQueueGenericSend( xQueue, pvItemToQueue, xTicksToWait, xCopyPosition ) +#endif + +#ifndef traceRETURN_xQueueGenericSend + #define traceRETURN_xQueueGenericSend( xReturn ) +#endif + +#ifndef traceENTER_xQueueGenericSendFromISR + #define traceENTER_xQueueGenericSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken, xCopyPosition ) +#endif + +#ifndef traceRETURN_xQueueGenericSendFromISR + #define traceRETURN_xQueueGenericSendFromISR( xReturn ) +#endif + +#ifndef traceENTER_xQueueGiveFromISR + #define traceENTER_xQueueGiveFromISR( xQueue, pxHigherPriorityTaskWoken ) +#endif + +#ifndef traceRETURN_xQueueGiveFromISR + #define traceRETURN_xQueueGiveFromISR( xReturn ) +#endif + +#ifndef traceENTER_xQueueReceive + #define traceENTER_xQueueReceive( xQueue, pvBuffer, xTicksToWait ) +#endif + +#ifndef traceRETURN_xQueueReceive + #define traceRETURN_xQueueReceive( xReturn ) +#endif + +#ifndef traceENTER_xQueueSemaphoreTake + #define traceENTER_xQueueSemaphoreTake( xQueue, xTicksToWait ) +#endif + +#ifndef traceRETURN_xQueueSemaphoreTake + #define traceRETURN_xQueueSemaphoreTake( xReturn ) +#endif + +#ifndef traceENTER_xQueuePeek + #define traceENTER_xQueuePeek( xQueue, pvBuffer, xTicksToWait ) +#endif + +#ifndef traceRETURN_xQueuePeek + #define traceRETURN_xQueuePeek( xReturn ) +#endif + +#ifndef traceENTER_xQueueReceiveFromISR + #define traceENTER_xQueueReceiveFromISR( xQueue, pvBuffer, pxHigherPriorityTaskWoken ) +#endif + +#ifndef traceRETURN_xQueueReceiveFromISR + #define traceRETURN_xQueueReceiveFromISR( xReturn ) +#endif + +#ifndef traceENTER_xQueuePeekFromISR + #define traceENTER_xQueuePeekFromISR( xQueue, pvBuffer ) +#endif + +#ifndef traceRETURN_xQueuePeekFromISR + #define traceRETURN_xQueuePeekFromISR( xReturn ) +#endif + +#ifndef traceENTER_uxQueueMessagesWaiting + #define traceENTER_uxQueueMessagesWaiting( xQueue ) +#endif + +#ifndef traceRETURN_uxQueueMessagesWaiting + #define traceRETURN_uxQueueMessagesWaiting( uxReturn ) +#endif + +#ifndef traceENTER_uxQueueSpacesAvailable + #define traceENTER_uxQueueSpacesAvailable( xQueue ) +#endif + +#ifndef traceRETURN_uxQueueSpacesAvailable + #define traceRETURN_uxQueueSpacesAvailable( uxReturn ) +#endif + +#ifndef traceENTER_uxQueueMessagesWaitingFromISR + #define traceENTER_uxQueueMessagesWaitingFromISR( xQueue ) +#endif + +#ifndef traceRETURN_uxQueueMessagesWaitingFromISR + #define traceRETURN_uxQueueMessagesWaitingFromISR( uxReturn ) +#endif + +#ifndef traceENTER_vQueueDelete + #define traceENTER_vQueueDelete( xQueue ) +#endif + +#ifndef traceRETURN_vQueueDelete + #define traceRETURN_vQueueDelete() +#endif + +#ifndef traceENTER_uxQueueGetQueueNumber + #define traceENTER_uxQueueGetQueueNumber( xQueue ) +#endif + +#ifndef traceRETURN_uxQueueGetQueueNumber + #define traceRETURN_uxQueueGetQueueNumber( uxQueueNumber ) +#endif + +#ifndef traceENTER_vQueueSetQueueNumber + #define traceENTER_vQueueSetQueueNumber( xQueue, uxQueueNumber ) +#endif + +#ifndef traceRETURN_vQueueSetQueueNumber + #define traceRETURN_vQueueSetQueueNumber() +#endif + +#ifndef traceENTER_ucQueueGetQueueType + #define traceENTER_ucQueueGetQueueType( xQueue ) +#endif + +#ifndef traceRETURN_ucQueueGetQueueType + #define traceRETURN_ucQueueGetQueueType( ucQueueType ) +#endif + +#ifndef traceENTER_uxQueueGetQueueItemSize + #define traceENTER_uxQueueGetQueueItemSize( xQueue ) +#endif + +#ifndef traceRETURN_uxQueueGetQueueItemSize + #define traceRETURN_uxQueueGetQueueItemSize( uxItemSize ) +#endif + +#ifndef traceENTER_uxQueueGetQueueLength + #define traceENTER_uxQueueGetQueueLength( xQueue ) +#endif + +#ifndef traceRETURN_uxQueueGetQueueLength + #define traceRETURN_uxQueueGetQueueLength( uxLength ) +#endif + +#ifndef traceENTER_xQueueIsQueueEmptyFromISR + #define traceENTER_xQueueIsQueueEmptyFromISR( xQueue ) +#endif + +#ifndef traceRETURN_xQueueIsQueueEmptyFromISR + #define traceRETURN_xQueueIsQueueEmptyFromISR( xReturn ) +#endif + +#ifndef traceENTER_xQueueIsQueueFullFromISR + #define traceENTER_xQueueIsQueueFullFromISR( xQueue ) +#endif + +#ifndef traceRETURN_xQueueIsQueueFullFromISR + #define traceRETURN_xQueueIsQueueFullFromISR( xReturn ) +#endif + +#ifndef traceENTER_xQueueCRSend + #define traceENTER_xQueueCRSend( xQueue, pvItemToQueue, xTicksToWait ) +#endif + +#ifndef traceRETURN_xQueueCRSend + #define traceRETURN_xQueueCRSend( xReturn ) +#endif + +#ifndef traceENTER_xQueueCRReceive + #define traceENTER_xQueueCRReceive( xQueue, pvBuffer, xTicksToWait ) +#endif + +#ifndef traceRETURN_xQueueCRReceive + #define traceRETURN_xQueueCRReceive( xReturn ) +#endif + +#ifndef traceENTER_xQueueCRSendFromISR + #define traceENTER_xQueueCRSendFromISR( xQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) +#endif + +#ifndef traceRETURN_xQueueCRSendFromISR + #define traceRETURN_xQueueCRSendFromISR( xCoRoutinePreviouslyWoken ) +#endif + +#ifndef traceENTER_xQueueCRReceiveFromISR + #define traceENTER_xQueueCRReceiveFromISR( xQueue, pvBuffer, pxCoRoutineWoken ) +#endif + +#ifndef traceRETURN_xQueueCRReceiveFromISR + #define traceRETURN_xQueueCRReceiveFromISR( xReturn ) +#endif + +#ifndef traceENTER_vQueueAddToRegistry + #define traceENTER_vQueueAddToRegistry( xQueue, pcQueueName ) +#endif + +#ifndef traceRETURN_vQueueAddToRegistry + #define traceRETURN_vQueueAddToRegistry() +#endif + +#ifndef traceENTER_pcQueueGetName + #define traceENTER_pcQueueGetName( xQueue ) +#endif + +#ifndef traceRETURN_pcQueueGetName + #define traceRETURN_pcQueueGetName( pcReturn ) +#endif + +#ifndef traceENTER_vQueueUnregisterQueue + #define traceENTER_vQueueUnregisterQueue( xQueue ) +#endif + +#ifndef traceRETURN_vQueueUnregisterQueue + #define traceRETURN_vQueueUnregisterQueue() +#endif + +#ifndef traceENTER_vQueueWaitForMessageRestricted + #define traceENTER_vQueueWaitForMessageRestricted( xQueue, xTicksToWait, xWaitIndefinitely ) +#endif + +#ifndef traceRETURN_vQueueWaitForMessageRestricted + #define traceRETURN_vQueueWaitForMessageRestricted() +#endif + +#ifndef traceENTER_xQueueCreateSet + #define traceENTER_xQueueCreateSet( uxEventQueueLength ) +#endif + +#ifndef traceRETURN_xQueueCreateSet + #define traceRETURN_xQueueCreateSet( pxQueue ) +#endif + +#ifndef traceENTER_xQueueCreateSetStatic + #define traceENTER_xQueueCreateSetStatic( uxEventQueueLength ) +#endif + +#ifndef traceRETURN_xQueueCreateSetStatic + #define traceRETURN_xQueueCreateSetStatic( pxQueue ) +#endif + +#ifndef traceENTER_xQueueAddToSet + #define traceENTER_xQueueAddToSet( xQueueOrSemaphore, xQueueSet ) +#endif + +#ifndef traceRETURN_xQueueAddToSet + #define traceRETURN_xQueueAddToSet( xReturn ) +#endif + +#ifndef traceENTER_xQueueRemoveFromSet + #define traceENTER_xQueueRemoveFromSet( xQueueOrSemaphore, xQueueSet ) +#endif + +#ifndef traceRETURN_xQueueRemoveFromSet + #define traceRETURN_xQueueRemoveFromSet( xReturn ) +#endif + +#ifndef traceENTER_xQueueSelectFromSet + #define traceENTER_xQueueSelectFromSet( xQueueSet, xTicksToWait ) +#endif + +#ifndef traceRETURN_xQueueSelectFromSet + #define traceRETURN_xQueueSelectFromSet( xReturn ) +#endif + +#ifndef traceENTER_xQueueSelectFromSetFromISR + #define traceENTER_xQueueSelectFromSetFromISR( xQueueSet ) +#endif + +#ifndef traceRETURN_xQueueSelectFromSetFromISR + #define traceRETURN_xQueueSelectFromSetFromISR( xReturn ) +#endif + +#ifndef traceENTER_xTimerCreateTimerTask + #define traceENTER_xTimerCreateTimerTask() +#endif + +#ifndef traceRETURN_xTimerCreateTimerTask + #define traceRETURN_xTimerCreateTimerTask( xReturn ) +#endif + +#ifndef traceENTER_xTimerCreate + #define traceENTER_xTimerCreate( pcTimerName, xTimerPeriodInTicks, xAutoReload, pvTimerID, pxCallbackFunction ) +#endif + +#ifndef traceRETURN_xTimerCreate + #define traceRETURN_xTimerCreate( pxNewTimer ) +#endif + +#ifndef traceENTER_xTimerCreateStatic + #define traceENTER_xTimerCreateStatic( pcTimerName, xTimerPeriodInTicks, xAutoReload, pvTimerID, pxCallbackFunction, pxTimerBuffer ) +#endif + +#ifndef traceRETURN_xTimerCreateStatic + #define traceRETURN_xTimerCreateStatic( pxNewTimer ) +#endif + +#ifndef traceENTER_xTimerGenericCommandFromTask + #define traceENTER_xTimerGenericCommandFromTask( xTimer, xCommandID, xOptionalValue, pxHigherPriorityTaskWoken, xTicksToWait ) +#endif + +#ifndef traceRETURN_xTimerGenericCommandFromTask + #define traceRETURN_xTimerGenericCommandFromTask( xReturn ) +#endif + +#ifndef traceENTER_xTimerGenericCommandFromISR + #define traceENTER_xTimerGenericCommandFromISR( xTimer, xCommandID, xOptionalValue, pxHigherPriorityTaskWoken, xTicksToWait ) +#endif + +#ifndef traceRETURN_xTimerGenericCommandFromISR + #define traceRETURN_xTimerGenericCommandFromISR( xReturn ) +#endif + +#ifndef traceENTER_xTimerGetTimerDaemonTaskHandle + #define traceENTER_xTimerGetTimerDaemonTaskHandle() +#endif + +#ifndef traceRETURN_xTimerGetTimerDaemonTaskHandle + #define traceRETURN_xTimerGetTimerDaemonTaskHandle( xTimerTaskHandle ) +#endif + +#ifndef traceENTER_xTimerGetPeriod + #define traceENTER_xTimerGetPeriod( xTimer ) +#endif + +#ifndef traceRETURN_xTimerGetPeriod + #define traceRETURN_xTimerGetPeriod( xTimerPeriodInTicks ) +#endif + +#ifndef traceENTER_vTimerSetReloadMode + #define traceENTER_vTimerSetReloadMode( xTimer, xAutoReload ) +#endif + +#ifndef traceRETURN_vTimerSetReloadMode + #define traceRETURN_vTimerSetReloadMode() +#endif + +#ifndef traceENTER_xTimerGetReloadMode + #define traceENTER_xTimerGetReloadMode( xTimer ) +#endif + +#ifndef traceRETURN_xTimerGetReloadMode + #define traceRETURN_xTimerGetReloadMode( xReturn ) +#endif + +#ifndef traceENTER_uxTimerGetReloadMode + #define traceENTER_uxTimerGetReloadMode( xTimer ) +#endif + +#ifndef traceRETURN_uxTimerGetReloadMode + #define traceRETURN_uxTimerGetReloadMode( uxReturn ) +#endif + +#ifndef traceENTER_xTimerGetExpiryTime + #define traceENTER_xTimerGetExpiryTime( xTimer ) +#endif + +#ifndef traceRETURN_xTimerGetExpiryTime + #define traceRETURN_xTimerGetExpiryTime( xReturn ) +#endif + +#ifndef traceENTER_xTimerGetStaticBuffer + #define traceENTER_xTimerGetStaticBuffer( xTimer, ppxTimerBuffer ) +#endif + +#ifndef traceRETURN_xTimerGetStaticBuffer + #define traceRETURN_xTimerGetStaticBuffer( xReturn ) +#endif + +#ifndef traceENTER_pcTimerGetName + #define traceENTER_pcTimerGetName( xTimer ) +#endif + +#ifndef traceRETURN_pcTimerGetName + #define traceRETURN_pcTimerGetName( pcTimerName ) +#endif + +#ifndef traceENTER_xTimerIsTimerActive + #define traceENTER_xTimerIsTimerActive( xTimer ) +#endif + +#ifndef traceRETURN_xTimerIsTimerActive + #define traceRETURN_xTimerIsTimerActive( xReturn ) +#endif + +#ifndef traceENTER_pvTimerGetTimerID + #define traceENTER_pvTimerGetTimerID( xTimer ) +#endif + +#ifndef traceRETURN_pvTimerGetTimerID + #define traceRETURN_pvTimerGetTimerID( pvReturn ) +#endif + +#ifndef traceENTER_vTimerSetTimerID + #define traceENTER_vTimerSetTimerID( xTimer, pvNewID ) +#endif + +#ifndef traceRETURN_vTimerSetTimerID + #define traceRETURN_vTimerSetTimerID() +#endif + +#ifndef traceENTER_xTimerPendFunctionCallFromISR + #define traceENTER_xTimerPendFunctionCallFromISR( xFunctionToPend, pvParameter1, ulParameter2, pxHigherPriorityTaskWoken ) +#endif + +#ifndef traceRETURN_xTimerPendFunctionCallFromISR + #define traceRETURN_xTimerPendFunctionCallFromISR( xReturn ) +#endif + +#ifndef traceENTER_xTimerPendFunctionCall + #define traceENTER_xTimerPendFunctionCall( xFunctionToPend, pvParameter1, ulParameter2, xTicksToWait ) +#endif + +#ifndef traceRETURN_xTimerPendFunctionCall + #define traceRETURN_xTimerPendFunctionCall( xReturn ) +#endif + +#ifndef traceENTER_uxTimerGetTimerNumber + #define traceENTER_uxTimerGetTimerNumber( xTimer ) +#endif + +#ifndef traceRETURN_uxTimerGetTimerNumber + #define traceRETURN_uxTimerGetTimerNumber( uxTimerNumber ) +#endif + +#ifndef traceENTER_vTimerSetTimerNumber + #define traceENTER_vTimerSetTimerNumber( xTimer, uxTimerNumber ) +#endif + +#ifndef traceRETURN_vTimerSetTimerNumber + #define traceRETURN_vTimerSetTimerNumber() +#endif + +#ifndef traceENTER_xTaskCreateStatic + #define traceENTER_xTaskCreateStatic( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, puxStackBuffer, pxTaskBuffer ) +#endif + +#ifndef traceRETURN_xTaskCreateStatic + #define traceRETURN_xTaskCreateStatic( xReturn ) +#endif + +#ifndef traceENTER_xTaskCreateStaticAffinitySet + #define traceENTER_xTaskCreateStaticAffinitySet( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, puxStackBuffer, pxTaskBuffer, uxCoreAffinityMask ) +#endif + +#ifndef traceRETURN_xTaskCreateStaticAffinitySet + #define traceRETURN_xTaskCreateStaticAffinitySet( xReturn ) +#endif + +#ifndef traceENTER_xTaskCreateRestrictedStatic + #define traceENTER_xTaskCreateRestrictedStatic( pxTaskDefinition, pxCreatedTask ) +#endif + +#ifndef traceRETURN_xTaskCreateRestrictedStatic + #define traceRETURN_xTaskCreateRestrictedStatic( xReturn ) +#endif + +#ifndef traceENTER_xTaskCreateRestrictedStaticAffinitySet + #define traceENTER_xTaskCreateRestrictedStaticAffinitySet( pxTaskDefinition, uxCoreAffinityMask, pxCreatedTask ) +#endif + +#ifndef traceRETURN_xTaskCreateRestrictedStaticAffinitySet + #define traceRETURN_xTaskCreateRestrictedStaticAffinitySet( xReturn ) +#endif + +#ifndef traceENTER_xTaskCreateRestricted + #define traceENTER_xTaskCreateRestricted( pxTaskDefinition, pxCreatedTask ) +#endif + +#ifndef traceRETURN_xTaskCreateRestricted + #define traceRETURN_xTaskCreateRestricted( xReturn ) +#endif + +#ifndef traceENTER_xTaskCreateRestrictedAffinitySet + #define traceENTER_xTaskCreateRestrictedAffinitySet( pxTaskDefinition, uxCoreAffinityMask, pxCreatedTask ) +#endif + +#ifndef traceRETURN_xTaskCreateRestrictedAffinitySet + #define traceRETURN_xTaskCreateRestrictedAffinitySet( xReturn ) +#endif + +#ifndef traceENTER_xTaskCreate + #define traceENTER_xTaskCreate( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask ) +#endif + +#ifndef traceRETURN_xTaskCreate + #define traceRETURN_xTaskCreate( xReturn ) +#endif + +#ifndef traceENTER_xTaskCreateAffinitySet + #define traceENTER_xTaskCreateAffinitySet( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, uxCoreAffinityMask, pxCreatedTask ) +#endif + +#ifndef traceRETURN_xTaskCreateAffinitySet + #define traceRETURN_xTaskCreateAffinitySet( xReturn ) +#endif + +#ifndef traceENTER_vTaskDelete + #define traceENTER_vTaskDelete( xTaskToDelete ) +#endif + +#ifndef traceRETURN_vTaskDelete + #define traceRETURN_vTaskDelete() +#endif + +#ifndef traceENTER_xTaskDelayUntil + #define traceENTER_xTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement ) +#endif + +#ifndef traceRETURN_xTaskDelayUntil + #define traceRETURN_xTaskDelayUntil( xShouldDelay ) +#endif + +#ifndef traceENTER_vTaskDelay + #define traceENTER_vTaskDelay( xTicksToDelay ) +#endif + +#ifndef traceRETURN_vTaskDelay + #define traceRETURN_vTaskDelay() +#endif + +#ifndef traceENTER_eTaskGetState + #define traceENTER_eTaskGetState( xTask ) +#endif + +#ifndef traceRETURN_eTaskGetState + #define traceRETURN_eTaskGetState( eReturn ) +#endif + +#ifndef traceENTER_uxTaskPriorityGet + #define traceENTER_uxTaskPriorityGet( xTask ) +#endif + +#ifndef traceRETURN_uxTaskPriorityGet + #define traceRETURN_uxTaskPriorityGet( uxReturn ) +#endif + +#ifndef traceENTER_uxTaskPriorityGetFromISR + #define traceENTER_uxTaskPriorityGetFromISR( xTask ) +#endif + +#ifndef traceRETURN_uxTaskPriorityGetFromISR + #define traceRETURN_uxTaskPriorityGetFromISR( uxReturn ) +#endif + +#ifndef traceENTER_uxTaskBasePriorityGet + #define traceENTER_uxTaskBasePriorityGet( xTask ) +#endif + +#ifndef traceRETURN_uxTaskBasePriorityGet + #define traceRETURN_uxTaskBasePriorityGet( uxReturn ) +#endif + +#ifndef traceENTER_uxTaskBasePriorityGetFromISR + #define traceENTER_uxTaskBasePriorityGetFromISR( xTask ) +#endif + +#ifndef traceRETURN_uxTaskBasePriorityGetFromISR + #define traceRETURN_uxTaskBasePriorityGetFromISR( uxReturn ) +#endif + +#ifndef traceENTER_vTaskPrioritySet + #define traceENTER_vTaskPrioritySet( xTask, uxNewPriority ) +#endif + +#ifndef traceRETURN_vTaskPrioritySet + #define traceRETURN_vTaskPrioritySet() +#endif + +#ifndef traceENTER_vTaskCoreAffinitySet + #define traceENTER_vTaskCoreAffinitySet( xTask, uxCoreAffinityMask ) +#endif + +#ifndef traceRETURN_vTaskCoreAffinitySet + #define traceRETURN_vTaskCoreAffinitySet() +#endif + +#ifndef traceENTER_vTaskCoreAffinityGet + #define traceENTER_vTaskCoreAffinityGet( xTask ) +#endif + +#ifndef traceRETURN_vTaskCoreAffinityGet + #define traceRETURN_vTaskCoreAffinityGet( uxCoreAffinityMask ) +#endif + +#ifndef traceENTER_vTaskPreemptionDisable + #define traceENTER_vTaskPreemptionDisable( xTask ) +#endif + +#ifndef traceRETURN_vTaskPreemptionDisable + #define traceRETURN_vTaskPreemptionDisable() +#endif + +#ifndef traceENTER_vTaskPreemptionEnable + #define traceENTER_vTaskPreemptionEnable( xTask ) +#endif + +#ifndef traceRETURN_vTaskPreemptionEnable + #define traceRETURN_vTaskPreemptionEnable() +#endif + +#ifndef traceENTER_vTaskSuspend + #define traceENTER_vTaskSuspend( xTaskToSuspend ) +#endif + +#ifndef traceRETURN_vTaskSuspend + #define traceRETURN_vTaskSuspend() +#endif + +#ifndef traceENTER_vTaskResume + #define traceENTER_vTaskResume( xTaskToResume ) +#endif + +#ifndef traceRETURN_vTaskResume + #define traceRETURN_vTaskResume() +#endif + +#ifndef traceENTER_xTaskResumeFromISR + #define traceENTER_xTaskResumeFromISR( xTaskToResume ) +#endif + +#ifndef traceRETURN_xTaskResumeFromISR + #define traceRETURN_xTaskResumeFromISR( xYieldRequired ) +#endif + +#ifndef traceENTER_vTaskStartScheduler + #define traceENTER_vTaskStartScheduler() +#endif + +#ifndef traceRETURN_vTaskStartScheduler + #define traceRETURN_vTaskStartScheduler() +#endif + +#ifndef traceENTER_vTaskEndScheduler + #define traceENTER_vTaskEndScheduler() +#endif + +#ifndef traceRETURN_vTaskEndScheduler + #define traceRETURN_vTaskEndScheduler() +#endif + +#ifndef traceENTER_vTaskSuspendAll + #define traceENTER_vTaskSuspendAll() +#endif + +#ifndef traceRETURN_vTaskSuspendAll + #define traceRETURN_vTaskSuspendAll() +#endif + +#ifndef traceENTER_xTaskResumeAll + #define traceENTER_xTaskResumeAll() +#endif + +#ifndef traceRETURN_xTaskResumeAll + #define traceRETURN_xTaskResumeAll( xAlreadyYielded ) +#endif + +#ifndef traceENTER_xTaskGetTickCount + #define traceENTER_xTaskGetTickCount() +#endif + +#ifndef traceRETURN_xTaskGetTickCount + #define traceRETURN_xTaskGetTickCount( xTicks ) +#endif + +#ifndef traceENTER_xTaskGetTickCountFromISR + #define traceENTER_xTaskGetTickCountFromISR() +#endif + +#ifndef traceRETURN_xTaskGetTickCountFromISR + #define traceRETURN_xTaskGetTickCountFromISR( xReturn ) +#endif + +#ifndef traceENTER_uxTaskGetNumberOfTasks + #define traceENTER_uxTaskGetNumberOfTasks() +#endif + +#ifndef traceRETURN_uxTaskGetNumberOfTasks + #define traceRETURN_uxTaskGetNumberOfTasks( uxCurrentNumberOfTasks ) +#endif + +#ifndef traceENTER_pcTaskGetName + #define traceENTER_pcTaskGetName( xTaskToQuery ) +#endif + +#ifndef traceRETURN_pcTaskGetName + #define traceRETURN_pcTaskGetName( pcTaskName ) +#endif + +#ifndef traceENTER_xTaskGetHandle + #define traceENTER_xTaskGetHandle( pcNameToQuery ) +#endif + +#ifndef traceRETURN_xTaskGetHandle + #define traceRETURN_xTaskGetHandle( pxTCB ) +#endif + +#ifndef traceENTER_xTaskGetStaticBuffers + #define traceENTER_xTaskGetStaticBuffers( xTask, ppuxStackBuffer, ppxTaskBuffer ) +#endif + +#ifndef traceRETURN_xTaskGetStaticBuffers + #define traceRETURN_xTaskGetStaticBuffers( xReturn ) +#endif + +#ifndef traceENTER_uxTaskGetSystemState + #define traceENTER_uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, pulTotalRunTime ) +#endif + +#ifndef traceRETURN_uxTaskGetSystemState + #define traceRETURN_uxTaskGetSystemState( uxTask ) +#endif + +#if ( configNUMBER_OF_CORES == 1 ) + #ifndef traceENTER_xTaskGetIdleTaskHandle + #define traceENTER_xTaskGetIdleTaskHandle() + #endif +#endif + +#if ( configNUMBER_OF_CORES == 1 ) + #ifndef traceRETURN_xTaskGetIdleTaskHandle + #define traceRETURN_xTaskGetIdleTaskHandle( xIdleTaskHandle ) + #endif +#endif + +#ifndef traceENTER_xTaskGetIdleTaskHandleForCore + #define traceENTER_xTaskGetIdleTaskHandleForCore( xCoreID ) +#endif + +#ifndef traceRETURN_xTaskGetIdleTaskHandleForCore + #define traceRETURN_xTaskGetIdleTaskHandleForCore( xIdleTaskHandle ) +#endif + +#ifndef traceENTER_vTaskStepTick + #define traceENTER_vTaskStepTick( xTicksToJump ) +#endif + +#ifndef traceRETURN_vTaskStepTick + #define traceRETURN_vTaskStepTick() +#endif + +#ifndef traceENTER_xTaskCatchUpTicks + #define traceENTER_xTaskCatchUpTicks( xTicksToCatchUp ) +#endif + +#ifndef traceRETURN_xTaskCatchUpTicks + #define traceRETURN_xTaskCatchUpTicks( xYieldOccurred ) +#endif + +#ifndef traceENTER_xTaskAbortDelay + #define traceENTER_xTaskAbortDelay( xTask ) +#endif + +#ifndef traceRETURN_xTaskAbortDelay + #define traceRETURN_xTaskAbortDelay( xReturn ) +#endif + +#ifndef traceENTER_xTaskIncrementTick + #define traceENTER_xTaskIncrementTick() +#endif + +#ifndef traceRETURN_xTaskIncrementTick + #define traceRETURN_xTaskIncrementTick( xSwitchRequired ) +#endif + +#ifndef traceENTER_vTaskSetApplicationTaskTag + #define traceENTER_vTaskSetApplicationTaskTag( xTask, pxHookFunction ) +#endif + +#ifndef traceRETURN_vTaskSetApplicationTaskTag + #define traceRETURN_vTaskSetApplicationTaskTag() +#endif + +#ifndef traceENTER_xTaskGetApplicationTaskTag + #define traceENTER_xTaskGetApplicationTaskTag( xTask ) +#endif + +#ifndef traceRETURN_xTaskGetApplicationTaskTag + #define traceRETURN_xTaskGetApplicationTaskTag( xReturn ) +#endif + +#ifndef traceENTER_xTaskGetApplicationTaskTagFromISR + #define traceENTER_xTaskGetApplicationTaskTagFromISR( xTask ) +#endif + +#ifndef traceRETURN_xTaskGetApplicationTaskTagFromISR + #define traceRETURN_xTaskGetApplicationTaskTagFromISR( xReturn ) +#endif + +#ifndef traceENTER_xTaskCallApplicationTaskHook + #define traceENTER_xTaskCallApplicationTaskHook( xTask, pvParameter ) +#endif + +#ifndef traceRETURN_xTaskCallApplicationTaskHook + #define traceRETURN_xTaskCallApplicationTaskHook( xReturn ) +#endif + +#ifndef traceENTER_vTaskSwitchContext + #define traceENTER_vTaskSwitchContext() +#endif + +#ifndef traceRETURN_vTaskSwitchContext + #define traceRETURN_vTaskSwitchContext() +#endif + +#ifndef traceENTER_vTaskPlaceOnEventList + #define traceENTER_vTaskPlaceOnEventList( pxEventList, xTicksToWait ) +#endif + +#ifndef traceRETURN_vTaskPlaceOnEventList + #define traceRETURN_vTaskPlaceOnEventList() +#endif + +#ifndef traceENTER_vTaskPlaceOnUnorderedEventList + #define traceENTER_vTaskPlaceOnUnorderedEventList( pxEventList, xItemValue, xTicksToWait ) +#endif + +#ifndef traceRETURN_vTaskPlaceOnUnorderedEventList + #define traceRETURN_vTaskPlaceOnUnorderedEventList() +#endif + +#ifndef traceENTER_vTaskPlaceOnEventListRestricted + #define traceENTER_vTaskPlaceOnEventListRestricted( pxEventList, xTicksToWait, xWaitIndefinitely ) +#endif + +#ifndef traceRETURN_vTaskPlaceOnEventListRestricted + #define traceRETURN_vTaskPlaceOnEventListRestricted() +#endif + +#ifndef traceENTER_xTaskRemoveFromEventList + #define traceENTER_xTaskRemoveFromEventList( pxEventList ) +#endif + +#ifndef traceRETURN_xTaskRemoveFromEventList + #define traceRETURN_xTaskRemoveFromEventList( xReturn ) +#endif + +#ifndef traceENTER_vTaskRemoveFromUnorderedEventList + #define traceENTER_vTaskRemoveFromUnorderedEventList( pxEventListItem, xItemValue ) +#endif + +#ifndef traceRETURN_vTaskRemoveFromUnorderedEventList + #define traceRETURN_vTaskRemoveFromUnorderedEventList() +#endif + +#ifndef traceENTER_vTaskSetTimeOutState + #define traceENTER_vTaskSetTimeOutState( pxTimeOut ) +#endif + +#ifndef traceRETURN_vTaskSetTimeOutState + #define traceRETURN_vTaskSetTimeOutState() +#endif + +#ifndef traceENTER_vTaskInternalSetTimeOutState + #define traceENTER_vTaskInternalSetTimeOutState( pxTimeOut ) +#endif + +#ifndef traceRETURN_vTaskInternalSetTimeOutState + #define traceRETURN_vTaskInternalSetTimeOutState() +#endif + +#ifndef traceENTER_xTaskCheckForTimeOut + #define traceENTER_xTaskCheckForTimeOut( pxTimeOut, pxTicksToWait ) +#endif + +#ifndef traceRETURN_xTaskCheckForTimeOut + #define traceRETURN_xTaskCheckForTimeOut( xReturn ) +#endif + +#ifndef traceENTER_vTaskMissedYield + #define traceENTER_vTaskMissedYield() +#endif + +#ifndef traceRETURN_vTaskMissedYield + #define traceRETURN_vTaskMissedYield() +#endif + +#ifndef traceENTER_uxTaskGetTaskNumber + #define traceENTER_uxTaskGetTaskNumber( xTask ) +#endif + +#ifndef traceRETURN_uxTaskGetTaskNumber + #define traceRETURN_uxTaskGetTaskNumber( uxReturn ) +#endif + +#ifndef traceENTER_vTaskSetTaskNumber + #define traceENTER_vTaskSetTaskNumber( xTask, uxHandle ) +#endif + +#ifndef traceRETURN_vTaskSetTaskNumber + #define traceRETURN_vTaskSetTaskNumber() +#endif + +#ifndef traceENTER_eTaskConfirmSleepModeStatus + #define traceENTER_eTaskConfirmSleepModeStatus() +#endif + +#ifndef traceRETURN_eTaskConfirmSleepModeStatus + #define traceRETURN_eTaskConfirmSleepModeStatus( eReturn ) +#endif + +#ifndef traceENTER_vTaskSetThreadLocalStoragePointer + #define traceENTER_vTaskSetThreadLocalStoragePointer( xTaskToSet, xIndex, pvValue ) +#endif + +#ifndef traceRETURN_vTaskSetThreadLocalStoragePointer + #define traceRETURN_vTaskSetThreadLocalStoragePointer() +#endif + +#ifndef traceENTER_pvTaskGetThreadLocalStoragePointer + #define traceENTER_pvTaskGetThreadLocalStoragePointer( xTaskToQuery, xIndex ) +#endif + +#ifndef traceRETURN_pvTaskGetThreadLocalStoragePointer + #define traceRETURN_pvTaskGetThreadLocalStoragePointer( pvReturn ) +#endif + +#ifndef traceENTER_vTaskAllocateMPURegions + #define traceENTER_vTaskAllocateMPURegions( xTaskToModify, pxRegions ) +#endif + +#ifndef traceRETURN_vTaskAllocateMPURegions + #define traceRETURN_vTaskAllocateMPURegions() +#endif + +#ifndef traceENTER_vTaskGetInfo + #define traceENTER_vTaskGetInfo( xTask, pxTaskStatus, xGetFreeStackSpace, eState ) +#endif + +#ifndef traceRETURN_vTaskGetInfo + #define traceRETURN_vTaskGetInfo() +#endif + +#ifndef traceENTER_uxTaskGetStackHighWaterMark2 + #define traceENTER_uxTaskGetStackHighWaterMark2( xTask ) +#endif + +#ifndef traceRETURN_uxTaskGetStackHighWaterMark2 + #define traceRETURN_uxTaskGetStackHighWaterMark2( uxReturn ) +#endif + +#ifndef traceENTER_uxTaskGetStackHighWaterMark + #define traceENTER_uxTaskGetStackHighWaterMark( xTask ) +#endif + +#ifndef traceRETURN_uxTaskGetStackHighWaterMark + #define traceRETURN_uxTaskGetStackHighWaterMark( uxReturn ) +#endif + +#ifndef traceENTER_xTaskGetCurrentTaskHandle + #define traceENTER_xTaskGetCurrentTaskHandle() +#endif + +#ifndef traceRETURN_xTaskGetCurrentTaskHandle + #define traceRETURN_xTaskGetCurrentTaskHandle( xReturn ) +#endif + +#ifndef traceENTER_xTaskGetCurrentTaskHandleForCore + #define traceENTER_xTaskGetCurrentTaskHandleForCore( xCoreID ) +#endif + +#ifndef traceRETURN_xTaskGetCurrentTaskHandleForCore + #define traceRETURN_xTaskGetCurrentTaskHandleForCore( xReturn ) +#endif + +#ifndef traceENTER_xTaskGetSchedulerState + #define traceENTER_xTaskGetSchedulerState() +#endif + +#ifndef traceRETURN_xTaskGetSchedulerState + #define traceRETURN_xTaskGetSchedulerState( xReturn ) +#endif + +#ifndef traceENTER_xTaskPriorityInherit + #define traceENTER_xTaskPriorityInherit( pxMutexHolder ) +#endif + +#ifndef traceRETURN_xTaskPriorityInherit + #define traceRETURN_xTaskPriorityInherit( xReturn ) +#endif + +#ifndef traceENTER_xTaskPriorityDisinherit + #define traceENTER_xTaskPriorityDisinherit( pxMutexHolder ) +#endif + +#ifndef traceRETURN_xTaskPriorityDisinherit + #define traceRETURN_xTaskPriorityDisinherit( xReturn ) +#endif + +#ifndef traceENTER_vTaskPriorityDisinheritAfterTimeout + #define traceENTER_vTaskPriorityDisinheritAfterTimeout( pxMutexHolder, uxHighestPriorityWaitingTask ) +#endif + +#ifndef traceRETURN_vTaskPriorityDisinheritAfterTimeout + #define traceRETURN_vTaskPriorityDisinheritAfterTimeout() +#endif + +#ifndef traceENTER_vTaskYieldWithinAPI + #define traceENTER_vTaskYieldWithinAPI() +#endif + +#ifndef traceRETURN_vTaskYieldWithinAPI + #define traceRETURN_vTaskYieldWithinAPI() +#endif + +#ifndef traceENTER_vTaskEnterCritical + #define traceENTER_vTaskEnterCritical() +#endif + +#ifndef traceRETURN_vTaskEnterCritical + #define traceRETURN_vTaskEnterCritical() +#endif + +#ifndef traceENTER_vTaskEnterCriticalFromISR + #define traceENTER_vTaskEnterCriticalFromISR() +#endif + +#ifndef traceRETURN_vTaskEnterCriticalFromISR + #define traceRETURN_vTaskEnterCriticalFromISR( uxSavedInterruptStatus ) +#endif + +#ifndef traceENTER_vTaskExitCritical + #define traceENTER_vTaskExitCritical() +#endif + +#ifndef traceRETURN_vTaskExitCritical + #define traceRETURN_vTaskExitCritical() +#endif + +#ifndef traceENTER_vTaskExitCriticalFromISR + #define traceENTER_vTaskExitCriticalFromISR( uxSavedInterruptStatus ) +#endif + +#ifndef traceRETURN_vTaskExitCriticalFromISR + #define traceRETURN_vTaskExitCriticalFromISR() +#endif + +#ifndef traceENTER_vTaskListTasks + #define traceENTER_vTaskListTasks( pcWriteBuffer, uxBufferLength ) +#endif + +#ifndef traceRETURN_vTaskListTasks + #define traceRETURN_vTaskListTasks() +#endif + +#ifndef traceENTER_vTaskGetRunTimeStatistics + #define traceENTER_vTaskGetRunTimeStatistics( pcWriteBuffer, uxBufferLength ) +#endif + +#ifndef traceRETURN_vTaskGetRunTimeStatistics + #define traceRETURN_vTaskGetRunTimeStatistics() +#endif + +#ifndef traceENTER_uxTaskResetEventItemValue + #define traceENTER_uxTaskResetEventItemValue() +#endif + +#ifndef traceRETURN_uxTaskResetEventItemValue + #define traceRETURN_uxTaskResetEventItemValue( uxReturn ) +#endif + +#ifndef traceENTER_pvTaskIncrementMutexHeldCount + #define traceENTER_pvTaskIncrementMutexHeldCount() +#endif + +#ifndef traceRETURN_pvTaskIncrementMutexHeldCount + #define traceRETURN_pvTaskIncrementMutexHeldCount( pxTCB ) +#endif + +#ifndef traceENTER_ulTaskGenericNotifyTake + #define traceENTER_ulTaskGenericNotifyTake( uxIndexToWaitOn, xClearCountOnExit, xTicksToWait ) +#endif + +#ifndef traceRETURN_ulTaskGenericNotifyTake + #define traceRETURN_ulTaskGenericNotifyTake( ulReturn ) +#endif + +#ifndef traceENTER_xTaskGenericNotifyWait + #define traceENTER_xTaskGenericNotifyWait( uxIndexToWaitOn, ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) +#endif + +#ifndef traceRETURN_xTaskGenericNotifyWait + #define traceRETURN_xTaskGenericNotifyWait( xReturn ) +#endif + +#ifndef traceENTER_xTaskGenericNotify + #define traceENTER_xTaskGenericNotify( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue ) +#endif + +#ifndef traceRETURN_xTaskGenericNotify + #define traceRETURN_xTaskGenericNotify( xReturn ) +#endif + +#ifndef traceENTER_xTaskGenericNotifyFromISR + #define traceENTER_xTaskGenericNotifyFromISR( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) +#endif + +#ifndef traceRETURN_xTaskGenericNotifyFromISR + #define traceRETURN_xTaskGenericNotifyFromISR( xReturn ) +#endif + +#ifndef traceENTER_vTaskGenericNotifyGiveFromISR + #define traceENTER_vTaskGenericNotifyGiveFromISR( xTaskToNotify, uxIndexToNotify, pxHigherPriorityTaskWoken ) +#endif + +#ifndef traceRETURN_vTaskGenericNotifyGiveFromISR + #define traceRETURN_vTaskGenericNotifyGiveFromISR() +#endif + +#ifndef traceENTER_xTaskGenericNotifyStateClear + #define traceENTER_xTaskGenericNotifyStateClear( xTask, uxIndexToClear ) +#endif + +#ifndef traceRETURN_xTaskGenericNotifyStateClear + #define traceRETURN_xTaskGenericNotifyStateClear( xReturn ) +#endif + +#ifndef traceENTER_ulTaskGenericNotifyValueClear + #define traceENTER_ulTaskGenericNotifyValueClear( xTask, uxIndexToClear, ulBitsToClear ) +#endif + +#ifndef traceRETURN_ulTaskGenericNotifyValueClear + #define traceRETURN_ulTaskGenericNotifyValueClear( ulReturn ) +#endif + +#ifndef traceENTER_ulTaskGetRunTimeCounter + #define traceENTER_ulTaskGetRunTimeCounter( xTask ) +#endif + +#ifndef traceRETURN_ulTaskGetRunTimeCounter + #define traceRETURN_ulTaskGetRunTimeCounter( ulRunTimeCounter ) +#endif + +#ifndef traceENTER_ulTaskGetRunTimePercent + #define traceENTER_ulTaskGetRunTimePercent( xTask ) +#endif + +#ifndef traceRETURN_ulTaskGetRunTimePercent + #define traceRETURN_ulTaskGetRunTimePercent( ulReturn ) +#endif + +#ifndef traceENTER_ulTaskGetIdleRunTimeCounter + #define traceENTER_ulTaskGetIdleRunTimeCounter() +#endif + +#ifndef traceRETURN_ulTaskGetIdleRunTimeCounter + #define traceRETURN_ulTaskGetIdleRunTimeCounter( ulReturn ) +#endif + +#ifndef traceENTER_ulTaskGetIdleRunTimePercent + #define traceENTER_ulTaskGetIdleRunTimePercent() +#endif + +#ifndef traceRETURN_ulTaskGetIdleRunTimePercent + #define traceRETURN_ulTaskGetIdleRunTimePercent( ulReturn ) +#endif + +#ifndef traceENTER_xTaskGetMPUSettings + #define traceENTER_xTaskGetMPUSettings( xTask ) +#endif + +#ifndef traceRETURN_xTaskGetMPUSettings + #define traceRETURN_xTaskGetMPUSettings( xMPUSettings ) +#endif + +#ifndef traceENTER_xStreamBufferGenericCreate + #define traceENTER_xStreamBufferGenericCreate( xBufferSizeBytes, xTriggerLevelBytes, xStreamBufferType, pxSendCompletedCallback, pxReceiveCompletedCallback ) +#endif + +#ifndef traceRETURN_xStreamBufferGenericCreate + #define traceRETURN_xStreamBufferGenericCreate( pvAllocatedMemory ) +#endif + +#ifndef traceENTER_xStreamBufferGenericCreateStatic + #define traceENTER_xStreamBufferGenericCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, xStreamBufferType, pucStreamBufferStorageArea, pxStaticStreamBuffer, pxSendCompletedCallback, pxReceiveCompletedCallback ) +#endif + +#ifndef traceRETURN_xStreamBufferGenericCreateStatic + #define traceRETURN_xStreamBufferGenericCreateStatic( xReturn ) +#endif + +#ifndef traceENTER_xStreamBufferGetStaticBuffers + #define traceENTER_xStreamBufferGetStaticBuffers( xStreamBuffer, ppucStreamBufferStorageArea, ppxStaticStreamBuffer ) +#endif + +#ifndef traceRETURN_xStreamBufferGetStaticBuffers + #define traceRETURN_xStreamBufferGetStaticBuffers( xReturn ) +#endif + +#ifndef traceENTER_vStreamBufferDelete + #define traceENTER_vStreamBufferDelete( xStreamBuffer ) +#endif + +#ifndef traceRETURN_vStreamBufferDelete + #define traceRETURN_vStreamBufferDelete() +#endif + +#ifndef traceENTER_xStreamBufferReset + #define traceENTER_xStreamBufferReset( xStreamBuffer ) +#endif + +#ifndef traceRETURN_xStreamBufferReset + #define traceRETURN_xStreamBufferReset( xReturn ) +#endif + +#ifndef traceENTER_xStreamBufferResetFromISR + #define traceENTER_xStreamBufferResetFromISR( xStreamBuffer ) +#endif + +#ifndef traceRETURN_xStreamBufferResetFromISR + #define traceRETURN_xStreamBufferResetFromISR( xReturn ) +#endif + +#ifndef traceENTER_xStreamBufferSetTriggerLevel + #define traceENTER_xStreamBufferSetTriggerLevel( xStreamBuffer, xTriggerLevel ) +#endif + +#ifndef traceRETURN_xStreamBufferSetTriggerLevel + #define traceRETURN_xStreamBufferSetTriggerLevel( xReturn ) +#endif + +#ifndef traceENTER_xStreamBufferSpacesAvailable + #define traceENTER_xStreamBufferSpacesAvailable( xStreamBuffer ) +#endif + +#ifndef traceRETURN_xStreamBufferSpacesAvailable + #define traceRETURN_xStreamBufferSpacesAvailable( xSpace ) +#endif + +#ifndef traceENTER_xStreamBufferBytesAvailable + #define traceENTER_xStreamBufferBytesAvailable( xStreamBuffer ) +#endif + +#ifndef traceRETURN_xStreamBufferBytesAvailable + #define traceRETURN_xStreamBufferBytesAvailable( xReturn ) +#endif + +#ifndef traceENTER_xStreamBufferSend + #define traceENTER_xStreamBufferSend( xStreamBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) +#endif + +#ifndef traceRETURN_xStreamBufferSend + #define traceRETURN_xStreamBufferSend( xReturn ) +#endif + +#ifndef traceENTER_xStreamBufferSendFromISR + #define traceENTER_xStreamBufferSendFromISR( xStreamBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) +#endif + +#ifndef traceRETURN_xStreamBufferSendFromISR + #define traceRETURN_xStreamBufferSendFromISR( xReturn ) +#endif + +#ifndef traceENTER_xStreamBufferReceive + #define traceENTER_xStreamBufferReceive( xStreamBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) +#endif + +#ifndef traceRETURN_xStreamBufferReceive + #define traceRETURN_xStreamBufferReceive( xReceivedLength ) +#endif + +#ifndef traceENTER_xStreamBufferNextMessageLengthBytes + #define traceENTER_xStreamBufferNextMessageLengthBytes( xStreamBuffer ) +#endif + +#ifndef traceRETURN_xStreamBufferNextMessageLengthBytes + #define traceRETURN_xStreamBufferNextMessageLengthBytes( xReturn ) +#endif + +#ifndef traceENTER_xStreamBufferReceiveFromISR + #define traceENTER_xStreamBufferReceiveFromISR( xStreamBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) +#endif + +#ifndef traceRETURN_xStreamBufferReceiveFromISR + #define traceRETURN_xStreamBufferReceiveFromISR( xReceivedLength ) +#endif + +#ifndef traceENTER_xStreamBufferIsEmpty + #define traceENTER_xStreamBufferIsEmpty( xStreamBuffer ) +#endif + +#ifndef traceRETURN_xStreamBufferIsEmpty + #define traceRETURN_xStreamBufferIsEmpty( xReturn ) +#endif + +#ifndef traceENTER_xStreamBufferIsFull + #define traceENTER_xStreamBufferIsFull( xStreamBuffer ) +#endif + +#ifndef traceRETURN_xStreamBufferIsFull + #define traceRETURN_xStreamBufferIsFull( xReturn ) +#endif + +#ifndef traceENTER_xStreamBufferSendCompletedFromISR + #define traceENTER_xStreamBufferSendCompletedFromISR( xStreamBuffer, pxHigherPriorityTaskWoken ) +#endif + +#ifndef traceRETURN_xStreamBufferSendCompletedFromISR + #define traceRETURN_xStreamBufferSendCompletedFromISR( xReturn ) +#endif + +#ifndef traceENTER_xStreamBufferReceiveCompletedFromISR + #define traceENTER_xStreamBufferReceiveCompletedFromISR( xStreamBuffer, pxHigherPriorityTaskWoken ) +#endif + +#ifndef traceRETURN_xStreamBufferReceiveCompletedFromISR + #define traceRETURN_xStreamBufferReceiveCompletedFromISR( xReturn ) +#endif + +#ifndef traceENTER_uxStreamBufferGetStreamBufferNotificationIndex + #define traceENTER_uxStreamBufferGetStreamBufferNotificationIndex( xStreamBuffer ) +#endif + +#ifndef traceRETURN_uxStreamBufferGetStreamBufferNotificationIndex + #define traceRETURN_uxStreamBufferGetStreamBufferNotificationIndex( uxNotificationIndex ) +#endif + +#ifndef traceENTER_vStreamBufferSetStreamBufferNotificationIndex + #define traceENTER_vStreamBufferSetStreamBufferNotificationIndex( xStreamBuffer, uxNotificationIndex ) +#endif + +#ifndef traceRETURN_vStreamBufferSetStreamBufferNotificationIndex + #define traceRETURN_vStreamBufferSetStreamBufferNotificationIndex() +#endif + +#ifndef traceENTER_uxStreamBufferGetStreamBufferNumber + #define traceENTER_uxStreamBufferGetStreamBufferNumber( xStreamBuffer ) +#endif + +#ifndef traceRETURN_uxStreamBufferGetStreamBufferNumber + #define traceRETURN_uxStreamBufferGetStreamBufferNumber( uxStreamBufferNumber ) +#endif + +#ifndef traceENTER_vStreamBufferSetStreamBufferNumber + #define traceENTER_vStreamBufferSetStreamBufferNumber( xStreamBuffer, uxStreamBufferNumber ) +#endif + +#ifndef traceRETURN_vStreamBufferSetStreamBufferNumber + #define traceRETURN_vStreamBufferSetStreamBufferNumber() +#endif + +#ifndef traceENTER_ucStreamBufferGetStreamBufferType + #define traceENTER_ucStreamBufferGetStreamBufferType( xStreamBuffer ) +#endif + +#ifndef traceRETURN_ucStreamBufferGetStreamBufferType + #define traceRETURN_ucStreamBufferGetStreamBufferType( ucStreamBufferType ) +#endif + +#ifndef traceENTER_vListInitialise + #define traceENTER_vListInitialise( pxList ) +#endif + +#ifndef traceRETURN_vListInitialise + #define traceRETURN_vListInitialise() +#endif + +#ifndef traceENTER_vListInitialiseItem + #define traceENTER_vListInitialiseItem( pxItem ) +#endif + +#ifndef traceRETURN_vListInitialiseItem + #define traceRETURN_vListInitialiseItem() +#endif + +#ifndef traceENTER_vListInsertEnd + #define traceENTER_vListInsertEnd( pxList, pxNewListItem ) +#endif + +#ifndef traceRETURN_vListInsertEnd + #define traceRETURN_vListInsertEnd() +#endif + +#ifndef traceENTER_vListInsert + #define traceENTER_vListInsert( pxList, pxNewListItem ) +#endif + +#ifndef traceRETURN_vListInsert + #define traceRETURN_vListInsert() +#endif + +#ifndef traceENTER_uxListRemove + #define traceENTER_uxListRemove( pxItemToRemove ) +#endif + +#ifndef traceRETURN_uxListRemove + #define traceRETURN_uxListRemove( uxNumberOfItems ) +#endif + +#ifndef traceENTER_xCoRoutineCreate + #define traceENTER_xCoRoutineCreate( pxCoRoutineCode, uxPriority, uxIndex ) +#endif + +#ifndef traceRETURN_xCoRoutineCreate + #define traceRETURN_xCoRoutineCreate( xReturn ) +#endif + +#ifndef traceENTER_vCoRoutineAddToDelayedList + #define traceENTER_vCoRoutineAddToDelayedList( xTicksToDelay, pxEventList ) +#endif + +#ifndef traceRETURN_vCoRoutineAddToDelayedList + #define traceRETURN_vCoRoutineAddToDelayedList() +#endif + +#ifndef traceENTER_vCoRoutineSchedule + #define traceENTER_vCoRoutineSchedule() +#endif + +#ifndef traceRETURN_vCoRoutineSchedule + #define traceRETURN_vCoRoutineSchedule() +#endif + +#ifndef traceENTER_xCoRoutineRemoveFromEventList + #define traceENTER_xCoRoutineRemoveFromEventList( pxEventList ) +#endif + +#ifndef traceRETURN_xCoRoutineRemoveFromEventList + #define traceRETURN_xCoRoutineRemoveFromEventList( xReturn ) +#endif + +#ifndef configGENERATE_RUN_TIME_STATS + #define configGENERATE_RUN_TIME_STATS 0 +#endif + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + #ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS + #error If configGENERATE_RUN_TIME_STATS is defined then portCONFIGURE_TIMER_FOR_RUN_TIME_STATS must also be defined. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS should call a port layer function to setup a peripheral timer/counter that can then be used as the run time counter time base. + #endif /* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS */ + + #ifndef portGET_RUN_TIME_COUNTER_VALUE + #ifndef portALT_GET_RUN_TIME_COUNTER_VALUE + #error If configGENERATE_RUN_TIME_STATS is defined then either portGET_RUN_TIME_COUNTER_VALUE or portALT_GET_RUN_TIME_COUNTER_VALUE must also be defined. See the examples provided and the FreeRTOS web site for more information. + #endif /* portALT_GET_RUN_TIME_COUNTER_VALUE */ + #endif /* portGET_RUN_TIME_COUNTER_VALUE */ + +#endif /* configGENERATE_RUN_TIME_STATS */ + +#ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS + #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#endif + +#ifndef portPRIVILEGE_BIT + #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 ) +#endif + +#ifndef portYIELD_WITHIN_API + #define portYIELD_WITHIN_API portYIELD +#endif + +#ifndef portSUPPRESS_TICKS_AND_SLEEP + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) +#endif + +#ifndef configEXPECTED_IDLE_TIME_BEFORE_SLEEP + #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 +#endif + +#if configEXPECTED_IDLE_TIME_BEFORE_SLEEP < 2 + #error configEXPECTED_IDLE_TIME_BEFORE_SLEEP must not be less than 2 +#endif + +#ifndef configUSE_TICKLESS_IDLE + #define configUSE_TICKLESS_IDLE 0 +#endif + +#ifndef configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING + #define configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( x ) +#endif + +#ifndef configPRE_SLEEP_PROCESSING + #define configPRE_SLEEP_PROCESSING( x ) +#endif + +#ifndef configPOST_SLEEP_PROCESSING + #define configPOST_SLEEP_PROCESSING( x ) +#endif + +#ifndef configUSE_QUEUE_SETS + #define configUSE_QUEUE_SETS 0 +#endif + +#ifndef portTASK_USES_FLOATING_POINT + #define portTASK_USES_FLOATING_POINT() +#endif + +#ifndef portALLOCATE_SECURE_CONTEXT + #define portALLOCATE_SECURE_CONTEXT( ulSecureStackSize ) +#endif + +#ifndef portDONT_DISCARD + #define portDONT_DISCARD +#endif + +#ifndef configUSE_TIME_SLICING + #define configUSE_TIME_SLICING 1 +#endif + +#ifndef configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS + #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0 +#endif + +#ifndef configUSE_STATS_FORMATTING_FUNCTIONS + #define configUSE_STATS_FORMATTING_FUNCTIONS 0 +#endif + +#ifndef portASSERT_IF_INTERRUPT_PRIORITY_INVALID + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() +#endif + +#ifndef configUSE_TRACE_FACILITY + #define configUSE_TRACE_FACILITY 0 +#endif + +#ifndef mtCOVERAGE_TEST_MARKER + #define mtCOVERAGE_TEST_MARKER() +#endif + +#ifndef mtCOVERAGE_TEST_DELAY + #define mtCOVERAGE_TEST_DELAY() +#endif + +#ifndef portASSERT_IF_IN_ISR + #define portASSERT_IF_IN_ISR() +#endif + +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +#endif + +#ifndef configAPPLICATION_ALLOCATED_HEAP + #define configAPPLICATION_ALLOCATED_HEAP 0 +#endif + +#ifndef configENABLE_HEAP_PROTECTOR + #define configENABLE_HEAP_PROTECTOR 0 +#endif + +#ifndef configUSE_TASK_NOTIFICATIONS + #define configUSE_TASK_NOTIFICATIONS 1 +#endif + +#ifndef configTASK_NOTIFICATION_ARRAY_ENTRIES + #define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 +#endif + +#if configTASK_NOTIFICATION_ARRAY_ENTRIES < 1 + #error configTASK_NOTIFICATION_ARRAY_ENTRIES must be at least 1 +#endif + +#ifndef configUSE_POSIX_ERRNO + #define configUSE_POSIX_ERRNO 0 +#endif + +#ifndef configUSE_SB_COMPLETED_CALLBACK + +/* By default per-instance callbacks are not enabled for stream buffer or message buffer. */ + #define configUSE_SB_COMPLETED_CALLBACK 0 +#endif + +#ifndef portTICK_TYPE_IS_ATOMIC + #define portTICK_TYPE_IS_ATOMIC 0 +#endif + +#ifndef configSUPPORT_STATIC_ALLOCATION + /* Defaults to 0 for backward compatibility. */ + #define configSUPPORT_STATIC_ALLOCATION 0 +#endif + +#ifndef configKERNEL_PROVIDED_STATIC_MEMORY + #define configKERNEL_PROVIDED_STATIC_MEMORY 0 +#endif + +#ifndef configSUPPORT_DYNAMIC_ALLOCATION + /* Defaults to 1 for backward compatibility. */ + #define configSUPPORT_DYNAMIC_ALLOCATION 1 +#endif + +#if ( ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION != 1 ) ) + #error configUSE_STATS_FORMATTING_FUNCTIONS cannot be used without dynamic allocation, but configSUPPORT_DYNAMIC_ALLOCATION is not set to 1. +#endif + +#if ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) + #if ( ( configUSE_TRACE_FACILITY != 1 ) && ( configGENERATE_RUN_TIME_STATS != 1 ) ) + #error configUSE_STATS_FORMATTING_FUNCTIONS is 1 but the functions it enables are not used because neither configUSE_TRACE_FACILITY or configGENERATE_RUN_TIME_STATS are 1. Set configUSE_STATS_FORMATTING_FUNCTIONS to 0 in FreeRTOSConfig.h. + #endif +#endif + +#ifndef configSTATS_BUFFER_MAX_LENGTH + #define configSTATS_BUFFER_MAX_LENGTH 0xFFFF +#endif + +#ifndef configSTACK_DEPTH_TYPE + +/* Defaults to StackType_t for backward compatibility, but can be overridden + * in FreeRTOSConfig.h if StackType_t is too restrictive. */ + #define configSTACK_DEPTH_TYPE StackType_t +#endif + +#ifndef configRUN_TIME_COUNTER_TYPE + +/* Defaults to uint32_t for backward compatibility, but can be overridden in + * FreeRTOSConfig.h if uint32_t is too restrictive. */ + + #define configRUN_TIME_COUNTER_TYPE uint32_t +#endif + +#ifndef configMESSAGE_BUFFER_LENGTH_TYPE + +/* Defaults to size_t for backward compatibility, but can be overridden + * in FreeRTOSConfig.h if lengths will always be less than the number of bytes + * in a size_t. */ + #define configMESSAGE_BUFFER_LENGTH_TYPE size_t +#endif + +/* Sanity check the configuration. */ +#if ( ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) ) + #error configSUPPORT_STATIC_ALLOCATION and configSUPPORT_DYNAMIC_ALLOCATION cannot both be 0, but can both be 1. +#endif + +#if ( ( configUSE_RECURSIVE_MUTEXES == 1 ) && ( configUSE_MUTEXES != 1 ) ) + #error configUSE_MUTEXES must be set to 1 to use recursive mutexes +#endif + +#if ( ( configRUN_MULTIPLE_PRIORITIES == 0 ) && ( configUSE_TASK_PREEMPTION_DISABLE != 0 ) ) + #error configRUN_MULTIPLE_PRIORITIES must be set to 1 to use task preemption disable +#endif + +#if ( ( configUSE_PREEMPTION == 0 ) && ( configUSE_TASK_PREEMPTION_DISABLE != 0 ) ) + #error configUSE_PREEMPTION must be set to 1 to use task preemption disable +#endif + +#if ( ( configNUMBER_OF_CORES == 1 ) && ( configUSE_TASK_PREEMPTION_DISABLE != 0 ) ) + #error configUSE_TASK_PREEMPTION_DISABLE is not supported in single core FreeRTOS +#endif + +#if ( ( configNUMBER_OF_CORES == 1 ) && ( configUSE_CORE_AFFINITY != 0 ) ) + #error configUSE_CORE_AFFINITY is not supported in single core FreeRTOS +#endif + +#if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_PORT_OPTIMISED_TASK_SELECTION != 0 ) ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION is not supported in SMP FreeRTOS +#endif + +#ifndef configINITIAL_TICK_COUNT + #define configINITIAL_TICK_COUNT 0 +#endif + +#if ( portTICK_TYPE_IS_ATOMIC == 0 ) + +/* Either variables of tick type cannot be read atomically, or + * portTICK_TYPE_IS_ATOMIC was not set - map the critical sections used when + * the tick count is returned to the standard critical section macros. */ + #define portTICK_TYPE_ENTER_CRITICAL() portENTER_CRITICAL() + #define portTICK_TYPE_EXIT_CRITICAL() portEXIT_CRITICAL() + #define portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() + #define portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( ( x ) ) +#else + +/* The tick type can be read atomically, so critical sections used when the + * tick count is returned can be defined away. */ + #define portTICK_TYPE_ENTER_CRITICAL() + #define portTICK_TYPE_EXIT_CRITICAL() + #define portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR() 0 + #define portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( x ) ( void ) ( x ) +#endif /* if ( portTICK_TYPE_IS_ATOMIC == 0 ) */ + +/* Definitions to allow backward compatibility with FreeRTOS versions prior to + * V8 if desired. */ +#ifndef configENABLE_BACKWARD_COMPATIBILITY + #define configENABLE_BACKWARD_COMPATIBILITY 1 +#endif + +#ifndef configPRINTF + +/* configPRINTF() was not defined, so define it away to nothing. To use + * configPRINTF() then define it as follows (where MyPrintFunction() is + * provided by the application writer): + * + * void MyPrintFunction(const char *pcFormat, ... ); + #define configPRINTF( X ) MyPrintFunction X + * + * Then call like a standard printf() function, but placing brackets around + * all parameters so they are passed as a single parameter. For example: + * configPRINTF( ("Value = %d", MyVariable) ); */ + #define configPRINTF( X ) +#endif + +#ifndef configMAX + +/* The application writer has not provided their own MAX macro, so define + * the following generic implementation. */ + #define configMAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#endif + +#ifndef configMIN + +/* The application writer has not provided their own MIN macro, so define + * the following generic implementation. */ + #define configMIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#endif + +#if configENABLE_BACKWARD_COMPATIBILITY == 1 + #define eTaskStateGet eTaskGetState + #define portTickType TickType_t + #define xTaskHandle TaskHandle_t + #define xQueueHandle QueueHandle_t + #define xSemaphoreHandle SemaphoreHandle_t + #define xQueueSetHandle QueueSetHandle_t + #define xQueueSetMemberHandle QueueSetMemberHandle_t + #define xTimeOutType TimeOut_t + #define xMemoryRegion MemoryRegion_t + #define xTaskParameters TaskParameters_t + #define xTaskStatusType TaskStatus_t + #define xTimerHandle TimerHandle_t + #define xCoRoutineHandle CoRoutineHandle_t + #define pdTASK_HOOK_CODE TaskHookFunction_t + #define portTICK_RATE_MS portTICK_PERIOD_MS + #define pcTaskGetTaskName pcTaskGetName + #define pcTimerGetTimerName pcTimerGetName + #define pcQueueGetQueueName pcQueueGetName + #define vTaskGetTaskInfo vTaskGetInfo + #define xTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter + +/* Backward compatibility within the scheduler code only - these definitions + * are not really required but are included for completeness. */ + #define tmrTIMER_CALLBACK TimerCallbackFunction_t + #define pdTASK_CODE TaskFunction_t + #define xListItem ListItem_t + #define xList List_t + +/* For libraries that break the list data hiding, and access list structure + * members directly (which is not supposed to be done). */ + #define pxContainer pvContainer +#endif /* configENABLE_BACKWARD_COMPATIBILITY */ + +#if ( configUSE_ALTERNATIVE_API != 0 ) + #error The alternative API was deprecated some time ago, and was removed in FreeRTOS V9.0 0 +#endif + +/* Set configUSE_TASK_FPU_SUPPORT to 0 to omit floating point support even + * if floating point hardware is otherwise supported by the FreeRTOS port in use. + * This constant is not supported by all FreeRTOS ports that include floating + * point support. */ +#ifndef configUSE_TASK_FPU_SUPPORT + #define configUSE_TASK_FPU_SUPPORT 1 +#endif + +/* Set configENABLE_MPU to 1 to enable MPU support and 0 to disable it. This is + * currently used in ARMv8M ports. */ +#ifndef configENABLE_MPU + #define configENABLE_MPU 0 +#endif + +/* Set configENABLE_FPU to 1 to enable FPU support and 0 to disable it. This is + * currently used in ARMv8M ports. */ +#ifndef configENABLE_FPU + #define configENABLE_FPU 1 +#endif + +/* Set configENABLE_MVE to 1 to enable MVE support and 0 to disable it. This is + * currently used in ARMv8M ports. */ +#ifndef configENABLE_MVE + #define configENABLE_MVE 0 +#endif + +/* Set configENABLE_TRUSTZONE to 1 enable TrustZone support and 0 to disable it. + * This is currently used in ARMv8M ports. */ +#ifndef configENABLE_TRUSTZONE + #define configENABLE_TRUSTZONE 1 +#endif + +/* Set configRUN_FREERTOS_SECURE_ONLY to 1 to run the FreeRTOS ARMv8M port on + * the Secure Side only. */ +#ifndef configRUN_FREERTOS_SECURE_ONLY + #define configRUN_FREERTOS_SECURE_ONLY 0 +#endif + +#ifndef configRUN_ADDITIONAL_TESTS + #define configRUN_ADDITIONAL_TESTS 0 +#endif + +/* The following config allows infinite loop control. For example, control the + * infinite loop in idle task function when performing unit tests. */ +#ifndef configCONTROL_INFINITE_LOOP + #define configCONTROL_INFINITE_LOOP() +#endif + +/* Sometimes the FreeRTOSConfig.h settings only allow a task to be created using + * dynamically allocated RAM, in which case when any task is deleted it is known + * that both the task's stack and TCB need to be freed. Sometimes the + * FreeRTOSConfig.h settings only allow a task to be created using statically + * allocated RAM, in which case when any task is deleted it is known that neither + * the task's stack or TCB should be freed. Sometimes the FreeRTOSConfig.h + * settings allow a task to be created using either statically or dynamically + * allocated RAM, in which case a member of the TCB is used to record whether the + * stack and/or TCB were allocated statically or dynamically, so when a task is + * deleted the RAM that was allocated dynamically is freed again and no attempt is + * made to free the RAM that was allocated statically. + * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE is only true if it is possible for a + * task to be created using either statically or dynamically allocated RAM. Note + * that if portUSING_MPU_WRAPPERS is 1 then a protected task can be created with + * a statically allocated stack and a dynamically allocated TCB. + * + * The following table lists various combinations of portUSING_MPU_WRAPPERS, + * configSUPPORT_DYNAMIC_ALLOCATION and configSUPPORT_STATIC_ALLOCATION and + * when it is possible to have both static and dynamic allocation: + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + * | MPU | Dynamic | Static | Available Functions | Possible Allocations | Both Dynamic and | Need Free | + * | | | | | | Static Possible | | + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + * | 0 | 0 | 1 | xTaskCreateStatic | TCB - Static, Stack - Static | No | No | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 0 | 1 | 0 | xTaskCreate | TCB - Dynamic, Stack - Dynamic | No | Yes | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 0 | 1 | 1 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateStatic | 2. TCB - Static, Stack - Static | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 0 | 1 | xTaskCreateStatic, | TCB - Static, Stack - Static | No | No | + * | | | | xTaskCreateRestrictedStatic | | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 1 | 0 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateRestricted | 2. TCB - Dynamic, Stack - Static | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 1 | 1 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateStatic, | 2. TCB - Dynamic, Stack - Static | | | + * | | | | xTaskCreateRestricted, | 3. TCB - Static, Stack - Static | | | + * | | | | xTaskCreateRestrictedStatic | | | | + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + */ +#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE \ + ( ( ( portUSING_MPU_WRAPPERS == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) || \ + ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) ) + +/* + * In line with software engineering best practice, FreeRTOS implements a strict + * data hiding policy, so the real structures used by FreeRTOS to maintain the + * state of tasks, queues, semaphores, etc. are not accessible to the application + * code. However, if the application writer wants to statically allocate such + * an object then the size of the object needs to be known. Dummy structures + * that are guaranteed to have the same size and alignment requirements of the + * real objects are used for this purpose. The dummy list and list item + * structures below are used for inclusion in such a dummy structure. + */ +struct xSTATIC_LIST_ITEM +{ + #if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + TickType_t xDummy2; + void * pvDummy3[ 4 ]; + #if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy4; + #endif +}; +typedef struct xSTATIC_LIST_ITEM StaticListItem_t; + +#if ( configUSE_MINI_LIST_ITEM == 1 ) + /* See the comments above the struct xSTATIC_LIST_ITEM definition. */ + struct xSTATIC_MINI_LIST_ITEM + { + #if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + TickType_t xDummy2; + void * pvDummy3[ 2 ]; + }; + typedef struct xSTATIC_MINI_LIST_ITEM StaticMiniListItem_t; +#else /* if ( configUSE_MINI_LIST_ITEM == 1 ) */ + typedef struct xSTATIC_LIST_ITEM StaticMiniListItem_t; +#endif /* if ( configUSE_MINI_LIST_ITEM == 1 ) */ + +/* See the comments above the struct xSTATIC_LIST_ITEM definition. */ +typedef struct xSTATIC_LIST +{ + #if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + UBaseType_t uxDummy2; + void * pvDummy3; + StaticMiniListItem_t xDummy4; + #if ( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy5; + #endif +} StaticList_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the Task structure used internally by + * FreeRTOS is not accessible to application code. However, if the application + * writer wants to statically allocate the memory required to create a task then + * the size of the task object needs to be known. The StaticTask_t structure + * below is provided for this purpose. Its sizes and alignment requirements are + * guaranteed to match those of the genuine structure, no matter which + * architecture is being used, and no matter how the values in FreeRTOSConfig.h + * are set. Its contents are somewhat obfuscated in the hope users will + * recognise that it would be unwise to make direct use of the structure members. + */ +typedef struct xSTATIC_TCB +{ + void * pxDummy1; + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xDummy2; + #endif + #if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) + UBaseType_t uxDummy26; + #endif + StaticListItem_t xDummy3[ 2 ]; + UBaseType_t uxDummy5; + void * pxDummy6; + #if ( configNUMBER_OF_CORES > 1 ) + BaseType_t xDummy23; + UBaseType_t uxDummy24; + #endif + uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; + #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + BaseType_t xDummy25; + #endif + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + void * pxDummy8; + #endif + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + UBaseType_t uxDummy9; + #endif + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy10[ 2 ]; + #endif + #if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxDummy12[ 2 ]; + #endif + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + void * pxDummy14; + #endif + #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + void * pvDummy15[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #endif + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + configRUN_TIME_COUNTER_TYPE ulDummy16; + #endif + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + configTLS_BLOCK_TYPE xDummy17; + #endif + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + uint32_t ulDummy18[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; + uint8_t ucDummy19[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; + #endif + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + uint8_t uxDummy20; + #endif + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + uint8_t ucDummy21; + #endif + #if ( configUSE_POSIX_ERRNO == 1 ) + int iDummy22; + #endif +} StaticTask_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the Queue structure used internally by + * FreeRTOS is not accessible to application code. However, if the application + * writer wants to statically allocate the memory required to create a queue + * then the size of the queue object needs to be known. The StaticQueue_t + * structure below is provided for this purpose. Its sizes and alignment + * requirements are guaranteed to match those of the genuine structure, no + * matter which architecture is being used, and no matter how the values in + * FreeRTOSConfig.h are set. Its contents are somewhat obfuscated in the hope + * users will recognise that it would be unwise to make direct use of the + * structure members. + */ +typedef struct xSTATIC_QUEUE +{ + void * pvDummy1[ 3 ]; + + union + { + void * pvDummy2; + UBaseType_t uxDummy2; + } u; + + StaticList_t xDummy3[ 2 ]; + UBaseType_t uxDummy4[ 3 ]; + uint8_t ucDummy5[ 2 ]; + + #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucDummy6; + #endif + + #if ( configUSE_QUEUE_SETS == 1 ) + void * pvDummy7; + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy8; + uint8_t ucDummy9; + #endif +} StaticQueue_t; +typedef StaticQueue_t StaticSemaphore_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the event group structure used + * internally by FreeRTOS is not accessible to application code. However, if + * the application writer wants to statically allocate the memory required to + * create an event group then the size of the event group object needs to be + * know. The StaticEventGroup_t structure below is provided for this purpose. + * Its sizes and alignment requirements are guaranteed to match those of the + * genuine structure, no matter which architecture is being used, and no matter + * how the values in FreeRTOSConfig.h are set. Its contents are somewhat + * obfuscated in the hope users will recognise that it would be unwise to make + * direct use of the structure members. + */ +typedef struct xSTATIC_EVENT_GROUP +{ + TickType_t xDummy1; + StaticList_t xDummy2; + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy3; + #endif + + #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucDummy4; + #endif +} StaticEventGroup_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the software timer structure used + * internally by FreeRTOS is not accessible to application code. However, if + * the application writer wants to statically allocate the memory required to + * create a software timer then the size of the queue object needs to be known. + * The StaticTimer_t structure below is provided for this purpose. Its sizes + * and alignment requirements are guaranteed to match those of the genuine + * structure, no matter which architecture is being used, and no matter how the + * values in FreeRTOSConfig.h are set. Its contents are somewhat obfuscated in + * the hope users will recognise that it would be unwise to make direct use of + * the structure members. + */ +typedef struct xSTATIC_TIMER +{ + void * pvDummy1; + StaticListItem_t xDummy2; + TickType_t xDummy3; + void * pvDummy5; + TaskFunction_t pvDummy6; + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy7; + #endif + uint8_t ucDummy8; +} StaticTimer_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the stream buffer structure used + * internally by FreeRTOS is not accessible to application code. However, if + * the application writer wants to statically allocate the memory required to + * create a stream buffer then the size of the stream buffer object needs to be + * known. The StaticStreamBuffer_t structure below is provided for this + * purpose. Its size and alignment requirements are guaranteed to match those + * of the genuine structure, no matter which architecture is being used, and + * no matter how the values in FreeRTOSConfig.h are set. Its contents are + * somewhat obfuscated in the hope users will recognise that it would be unwise + * to make direct use of the structure members. + */ +typedef struct xSTATIC_STREAM_BUFFER +{ + size_t uxDummy1[ 4 ]; + void * pvDummy2[ 3 ]; + uint8_t ucDummy3; + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy4; + #endif + #if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) + void * pvDummy5[ 2 ]; + #endif + UBaseType_t uxDummy6; +} StaticStreamBuffer_t; + +/* Message buffers are built on stream buffers. */ +typedef StaticStreamBuffer_t StaticMessageBuffer_t; + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ + +#endif /* INC_FREERTOS_H */ diff --git a/vendor/FreeRTOS-Kernel/include/StackMacros.h b/vendor/FreeRTOS-Kernel/include/StackMacros.h new file mode 100644 index 0000000..07282a2 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/StackMacros.h @@ -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" diff --git a/vendor/FreeRTOS-Kernel/include/atomic.h b/vendor/FreeRTOS-Kernel/include/atomic.h new file mode 100644 index 0000000..2fe6b43 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/atomic.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 + +/* *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 */ diff --git a/vendor/FreeRTOS-Kernel/include/croutine.h b/vendor/FreeRTOS-Kernel/include/croutine.h new file mode 100644 index 0000000..42e27b1 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/croutine.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 */ diff --git a/vendor/FreeRTOS-Kernel/include/deprecated_definitions.h b/vendor/FreeRTOS-Kernel/include/deprecated_definitions.h new file mode 100644 index 0000000..6038230 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/deprecated_definitions.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 */ diff --git a/vendor/FreeRTOS-Kernel/include/event_groups.h b/vendor/FreeRTOS-Kernel/include/event_groups.h new file mode 100644 index 0000000..d705aa1 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/event_groups.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 */ diff --git a/vendor/FreeRTOS-Kernel/include/list.h b/vendor/FreeRTOS-Kernel/include/list.h new file mode 100644 index 0000000..2b9e2a2 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/list.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 */ diff --git a/vendor/FreeRTOS-Kernel/include/message_buffer.h b/vendor/FreeRTOS-Kernel/include/message_buffer.h new file mode 100644 index 0000000..afc337f --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/message_buffer.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 ) */ diff --git a/vendor/FreeRTOS-Kernel/include/mpu_prototypes.h b/vendor/FreeRTOS-Kernel/include/mpu_prototypes.h new file mode 100644 index 0000000..7f1652d --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/mpu_prototypes.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 */ diff --git a/vendor/FreeRTOS-Kernel/include/mpu_syscall_numbers.h b/vendor/FreeRTOS-Kernel/include/mpu_syscall_numbers.h new file mode 100644 index 0000000..50142e4 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/mpu_syscall_numbers.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 */ diff --git a/vendor/FreeRTOS-Kernel/include/mpu_wrappers.h b/vendor/FreeRTOS-Kernel/include/mpu_wrappers.h new file mode 100644 index 0000000..560d2aa --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/mpu_wrappers.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 */ diff --git a/vendor/FreeRTOS-Kernel/include/newlib-freertos.h b/vendor/FreeRTOS-Kernel/include/newlib-freertos.h new file mode 100644 index 0000000..531fec9 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/newlib-freertos.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 + +#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 */ diff --git a/vendor/FreeRTOS-Kernel/include/picolibc-freertos.h b/vendor/FreeRTOS-Kernel/include/picolibc-freertos.h new file mode 100644 index 0000000..86ed9f8 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/picolibc-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 + +#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 */ diff --git a/vendor/FreeRTOS-Kernel/include/portable.h b/vendor/FreeRTOS-Kernel/include/portable.h new file mode 100644 index 0000000..14277ab --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/portable.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 */ diff --git a/vendor/FreeRTOS-Kernel/include/projdefs.h b/vendor/FreeRTOS-Kernel/include/projdefs.h new file mode 100644 index 0000000..aff862d --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/projdefs.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 */ diff --git a/vendor/FreeRTOS-Kernel/include/queue.h b/vendor/FreeRTOS-Kernel/include/queue.h new file mode 100644 index 0000000..b14f853 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/queue.h @@ -0,0 +1,1882 @@ +/* + * 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 QUEUE_H +#define QUEUE_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h" must appear in source files before "include queue.h" +#endif + +#include "task.h" + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/** + * Type by which queues are referenced. For example, a call to xQueueCreate() + * returns an QueueHandle_t variable that can then be used as a parameter to + * xQueueSend(), xQueueReceive(), etc. + */ +struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */ +typedef struct QueueDefinition * QueueHandle_t; + +/** + * Type by which queue sets are referenced. For example, a call to + * xQueueCreateSet() returns an xQueueSet variable that can then be used as a + * parameter to xQueueSelectFromSet(), xQueueAddToSet(), etc. + */ +typedef struct QueueDefinition * QueueSetHandle_t; + +/** + * Queue sets can contain both queues and semaphores, so the + * QueueSetMemberHandle_t is defined as a type to be used where a parameter or + * return value can be either an QueueHandle_t or an SemaphoreHandle_t. + */ +typedef struct QueueDefinition * QueueSetMemberHandle_t; + +/* For internal use only. */ +#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) +#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) +#define queueOVERWRITE ( ( BaseType_t ) 2 ) + +/* For internal use only. These definitions *must* match those in queue.c. */ +#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) +#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U ) +#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) +#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) +#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) +#define queueQUEUE_TYPE_SET ( ( uint8_t ) 5U ) + +/** + * queue. h + * @code{c} + * QueueHandle_t xQueueCreate( + * UBaseType_t uxQueueLength, + * UBaseType_t uxItemSize + * ); + * @endcode + * + * Creates a new queue instance, and returns a handle by which the new queue + * can be referenced. + * + * Internally, within the FreeRTOS implementation, queues use two blocks of + * memory. The first block is used to hold the queue's data structures. The + * second block is used to hold items placed into the queue. If a queue is + * created using xQueueCreate() then both blocks of memory are automatically + * dynamically allocated inside the xQueueCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a queue is created using + * xQueueCreateStatic() then the application writer must provide the memory that + * will get used by the queue. xQueueCreateStatic() therefore allows a queue to + * be created without using any dynamic memory allocation. + * + * https://www.FreeRTOS.org/Embedded-RTOS-Queues.html + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @return If the queue is successfully create then a handle to the newly + * created queue is returned. If the queue cannot be created then NULL is + * returned. + * + * Example usage: + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * }; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * if( xQueue1 == NULL ) + * { + * // Queue was not created and must not be used. + * } + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * if( xQueue2 == NULL ) + * { + * // Queue was not created and must not be used. + * } + * + * // ... Rest of task code. + * } + * @endcode + * \defgroup xQueueCreate xQueueCreate + * \ingroup QueueManagement + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) ) +#endif + +/** + * queue. h + * @code{c} + * QueueHandle_t xQueueCreateStatic( + * UBaseType_t uxQueueLength, + * UBaseType_t uxItemSize, + * uint8_t *pucQueueStorage, + * StaticQueue_t *pxQueueBuffer + * ); + * @endcode + * + * Creates a new queue instance, and returns a handle by which the new queue + * can be referenced. + * + * Internally, within the FreeRTOS implementation, queues use two blocks of + * memory. The first block is used to hold the queue's data structures. The + * second block is used to hold items placed into the queue. If a queue is + * created using xQueueCreate() then both blocks of memory are automatically + * dynamically allocated inside the xQueueCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a queue is created using + * xQueueCreateStatic() then the application writer must provide the memory that + * will get used by the queue. xQueueCreateStatic() therefore allows a queue to + * be created without using any dynamic memory allocation. + * + * https://www.FreeRTOS.org/Embedded-RTOS-Queues.html + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @param pucQueueStorage If uxItemSize is not zero then + * pucQueueStorage must point to a uint8_t array that is at least large + * enough to hold the maximum number of items that can be in the queue at any + * one time - which is ( uxQueueLength * uxItemsSize ) bytes. If uxItemSize is + * zero then pucQueueStorage can be NULL. + * + * @param pxQueueBuffer Must point to a variable of type StaticQueue_t, which + * will be used to hold the queue's data structure. + * + * @return If the queue is created then a handle to the created queue is + * returned. If pxQueueBuffer is NULL then NULL is returned. + * + * Example usage: + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * }; + * + * #define QUEUE_LENGTH 10 + * #define ITEM_SIZE sizeof( uint32_t ) + * + * // xQueueBuffer will hold the queue structure. + * StaticQueue_t xQueueBuffer; + * + * // ucQueueStorage will hold the items posted to the queue. Must be at least + * // [(queue length) * ( queue item size)] bytes long. + * uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ]; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreateStatic( QUEUE_LENGTH, // The number of items the queue can hold. + * ITEM_SIZE, // The size of each item in the queue. + * &( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue. + * &xQueueBuffer ); // The buffer that will hold the queue structure. + * + * // The queue is guaranteed to be created successfully as no dynamic memory + * // allocation is used. Therefore xQueue1 is now a handle to a valid queue. + * + * // ... Rest of task code. + * } + * @endcode + * \defgroup xQueueCreateStatic xQueueCreateStatic + * \ingroup QueueManagement + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer ) xQueueGenericCreateStatic( ( uxQueueLength ), ( uxItemSize ), ( pucQueueStorage ), ( pxQueueBuffer ), ( queueQUEUE_TYPE_BASE ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * queue. h + * @code{c} + * BaseType_t xQueueGetStaticBuffers( QueueHandle_t xQueue, + * uint8_t ** ppucQueueStorage, + * StaticQueue_t ** ppxStaticQueue ); + * @endcode + * + * Retrieve pointers to a statically created queue's data structure buffer + * and storage area buffer. These are the same buffers that are supplied + * at the time of creation. + * + * @param xQueue The queue for which to retrieve the buffers. + * + * @param ppucQueueStorage Used to return a pointer to the queue's storage + * area buffer. + * + * @param ppxStaticQueue Used to return a pointer to the queue's data + * structure buffer. + * + * @return pdTRUE if buffers were retrieved, pdFALSE otherwise. + * + * \defgroup xQueueGetStaticBuffers xQueueGetStaticBuffers + * \ingroup QueueManagement + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xQueueGetStaticBuffers( xQueue, ppucQueueStorage, ppxStaticQueue ) xQueueGenericGetStaticBuffers( ( xQueue ), ( ppucQueueStorage ), ( ppxStaticQueue ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * queue. h + * @code{c} + * BaseType_t xQueueSendToFront( + * QueueHandle_t xQueue, + * const void *pvItemToQueue, + * TickType_t xTicksToWait + * ); + * @endcode + * + * Post an item to the front of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @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 xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdPASS if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10U; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * } + * + * // ... Rest of task code. + * } + * @endcode + * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) \ + xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT ) + +/** + * queue. h + * @code{c} + * BaseType_t xQueueSendToBack( + * QueueHandle_t xQueue, + * const void *pvItemToQueue, + * TickType_t xTicksToWait + * ); + * @endcode + * + * This is a macro that calls xQueueGenericSend(). + * + * Post an item to the back of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @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 xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the queue + * is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdPASS if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10U; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueSendToBack( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * } + * + * // ... Rest of task code. + * } + * @endcode + * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) \ + xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) + +/** + * queue. h + * @code{c} + * BaseType_t xQueueSend( + * QueueHandle_t xQueue, + * const void * pvItemToQueue, + * TickType_t xTicksToWait + * ); + * @endcode + * + * This is a macro that calls xQueueGenericSend(). It is included for + * backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToFront() and xQueueSendToBack() macros. It is + * equivalent to xQueueSendToBack(). + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @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 xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdPASS if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10U; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * } + * + * // ... Rest of task code. + * } + * @endcode + * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \ + xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) + +/** + * queue. h + * @code{c} + * BaseType_t xQueueOverwrite( + * QueueHandle_t xQueue, + * const void * pvItemToQueue + * ); + * @endcode + * + * Only for use with queues that have a length of one - so the queue is either + * empty or full. + * + * Post an item on a queue. If the queue is already full then overwrite the + * value held in the queue. The item is queued by copy, not by reference. + * + * This function must not be called from an interrupt service routine. + * See xQueueOverwriteFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle of the queue to which the data is being sent. + * + * @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. + * + * @return xQueueOverwrite() is a macro that calls xQueueGenericSend(), and + * therefore has the same return values as xQueueSendToFront(). However, pdPASS + * is the only value that can be returned because xQueueOverwrite() will write + * to the queue even when the queue is already full. + * + * Example usage: + * @code{c} + * + * void vFunction( void *pvParameters ) + * { + * QueueHandle_t xQueue; + * uint32_t ulVarToSend, ulValReceived; + * + * // Create a queue to hold one uint32_t value. It is strongly + * // recommended *not* to use xQueueOverwrite() on queues that can + * // contain more than one value, and doing so will trigger an assertion + * // if configASSERT() is defined. + * xQueue = xQueueCreate( 1, sizeof( uint32_t ) ); + * + * // Write the value 10 to the queue using xQueueOverwrite(). + * ulVarToSend = 10; + * xQueueOverwrite( xQueue, &ulVarToSend ); + * + * // Peeking the queue should now return 10, but leave the value 10 in + * // the queue. A block time of zero is used as it is known that the + * // queue holds a value. + * ulValReceived = 0; + * xQueuePeek( xQueue, &ulValReceived, 0 ); + * + * if( ulValReceived != 10 ) + * { + * // Error unless the item was removed by a different task. + * } + * + * // The queue is still full. Use xQueueOverwrite() to overwrite the + * // value held in the queue with 100. + * ulVarToSend = 100; + * xQueueOverwrite( xQueue, &ulVarToSend ); + * + * // This time read from the queue, leaving the queue empty once more. + * // A block time of 0 is used again. + * xQueueReceive( xQueue, &ulValReceived, 0 ); + * + * // The value read should be the last value written, even though the + * // queue was already full when the value was written. + * if( ulValReceived != 100 ) + * { + * // Error! + * } + * + * // ... + * } + * @endcode + * \defgroup xQueueOverwrite xQueueOverwrite + * \ingroup QueueManagement + */ +#define xQueueOverwrite( xQueue, pvItemToQueue ) \ + xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE ) + + +/** + * queue. h + * @code{c} + * BaseType_t xQueueGenericSend( + * QueueHandle_t xQueue, + * const void * pvItemToQueue, + * TickType_t xTicksToWait + * BaseType_t xCopyPosition + * ); + * @endcode + * + * It is preferred that the macros xQueueSend(), xQueueSendToFront() and + * xQueueSendToBack() are used in place of calling this function directly. + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @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 xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdPASS if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * uint32_t ulVar = 10U; + * + * void vATask( void *pvParameters ) + * { + * QueueHandle_t xQueue1, xQueue2; + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 uint32_t values. + * xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) ); + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * + * // ... + * + * if( xQueue1 != 0 ) + * { + * // Send an uint32_t. Wait for 10 ticks for space to become + * // available if necessary. + * if( xQueueGenericSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10, queueSEND_TO_BACK ) != pdPASS ) + * { + * // Failed to post the message, even after 10 ticks. + * } + * } + * + * if( xQueue2 != 0 ) + * { + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueGenericSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0, queueSEND_TO_BACK ); + * } + * + * // ... Rest of task code. + * } + * @endcode + * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +BaseType_t xQueueGenericSend( QueueHandle_t xQueue, + const void * const pvItemToQueue, + TickType_t xTicksToWait, + const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; + +/** + * queue. h + * @code{c} + * BaseType_t xQueuePeek( + * QueueHandle_t xQueue, + * void * const pvBuffer, + * TickType_t xTicksToWait + * ); + * @endcode + * + * Receive an item from a queue without removing the item from the queue. + * The item is received by copy so a buffer of adequate size must be + * provided. The number of bytes copied into the buffer was defined when + * the queue was created. + * + * Successfully received items remain on the queue so will be returned again + * by the next call, or a call to xQueueReceive(). + * + * This macro must not be used in an interrupt service routine. See + * xQueuePeekFromISR() for an alternative that can be called from an interrupt + * service routine. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * xQueuePeek() will return immediately if xTicksToWait is 0 and the queue + * is empty. + * + * @return pdPASS if an item was successfully received from the queue, + * otherwise errQUEUE_EMPTY. + * + * Example usage: + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * QueueHandle_t xQueue; + * + * // Task to create a queue and post a value. + * void vATask( void *pvParameters ) + * { + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * if( xQueue == 0 ) + * { + * // Failed to create the queue. + * } + * + * // ... + * + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * + * // ... Rest of task code. + * } + * + * // Task to peek the data from the queue. + * void vADifferentTask( void *pvParameters ) + * { + * struct AMessage *pxRxedMessage; + * + * if( xQueue != 0 ) + * { + * // Peek a message on the created queue. Block for 10 ticks if a + * // message is not immediately available. + * if( xQueuePeek( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) ) + * { + * // pcRxedMessage now points to the struct AMessage variable posted + * // by vATask, but the item still remains on the queue. + * } + * } + * + * // ... Rest of task code. + * } + * @endcode + * \defgroup xQueuePeek xQueuePeek + * \ingroup QueueManagement + */ +BaseType_t xQueuePeek( QueueHandle_t xQueue, + void * const pvBuffer, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * queue. h + * @code{c} + * BaseType_t xQueuePeekFromISR( + * QueueHandle_t xQueue, + * void *pvBuffer, + * ); + * @endcode + * + * A version of xQueuePeek() that can be called from an interrupt service + * routine (ISR). + * + * Receive an item from a queue without removing the item from the queue. + * The item is received by copy so a buffer of adequate size must be + * provided. The number of bytes copied into the buffer was defined when + * the queue was created. + * + * Successfully received items remain on the queue so will be returned again + * by the next call, or a call to xQueueReceive(). + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @return pdPASS if an item was successfully received from the queue, + * otherwise pdFAIL. + * + * \defgroup xQueuePeekFromISR xQueuePeekFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, + void * const pvBuffer ) PRIVILEGED_FUNCTION; + +/** + * queue. h + * @code{c} + * BaseType_t xQueueReceive( + * QueueHandle_t xQueue, + * void *pvBuffer, + * TickType_t xTicksToWait + * ); + * @endcode + * + * Receive an item from a queue. The item is received by copy so a buffer of + * adequate size must be provided. The number of bytes copied into the buffer + * was defined when the queue was created. + * + * Successfully received items are removed from the queue. + * + * This function must not be used in an interrupt service routine. See + * xQueueReceiveFromISR for an alternative that can. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. xQueueReceive() will return immediately if xTicksToWait + * is zero and the queue is empty. The time is defined in tick periods so the + * constant portTICK_PERIOD_MS should be used to convert to real time if this is + * required. + * + * @return pdPASS if an item was successfully received from the queue, + * otherwise errQUEUE_EMPTY. + * + * Example usage: + * @code{c} + * struct AMessage + * { + * char ucMessageID; + * char ucData[ 20 ]; + * } xMessage; + * + * QueueHandle_t xQueue; + * + * // Task to create a queue and post a value. + * void vATask( void *pvParameters ) + * { + * struct AMessage *pxMessage; + * + * // Create a queue capable of containing 10 pointers to AMessage structures. + * // These should be passed by pointer as they contain a lot of data. + * xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) ); + * if( xQueue == 0 ) + * { + * // Failed to create the queue. + * } + * + * // ... + * + * // Send a pointer to a struct AMessage object. Don't block if the + * // queue is already full. + * pxMessage = & xMessage; + * xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 ); + * + * // ... Rest of task code. + * } + * + * // Task to receive from the queue. + * void vADifferentTask( void *pvParameters ) + * { + * struct AMessage *pxRxedMessage; + * + * if( xQueue != 0 ) + * { + * // Receive a message on the created queue. Block for 10 ticks if a + * // message is not immediately available. + * if( xQueueReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) ) + * { + * // pcRxedMessage now points to the struct AMessage variable posted + * // by vATask. + * } + * } + * + * // ... Rest of task code. + * } + * @endcode + * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +BaseType_t xQueueReceive( QueueHandle_t xQueue, + void * const pvBuffer, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * queue. h + * @code{c} + * UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ); + * @endcode + * + * Return the number of messages stored in a queue. + * + * @param xQueue A handle to the queue being queried. + * + * @return The number of messages available in the queue. + * + * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting + * \ingroup QueueManagement + */ +UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + * @code{c} + * UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ); + * @endcode + * + * Return the number of free spaces available in a queue. This is equal to the + * number of items that can be sent to the queue before the queue becomes full + * if no items are removed. + * + * @param xQueue A handle to the queue being queried. + * + * @return The number of spaces available in the queue. + * + * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting + * \ingroup QueueManagement + */ +UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + * @code{c} + * void vQueueDelete( QueueHandle_t xQueue ); + * @endcode + * + * Delete a queue - freeing all the memory allocated for storing of items + * placed on the queue. + * + * @param xQueue A handle to the queue to be deleted. + * + * \defgroup vQueueDelete vQueueDelete + * \ingroup QueueManagement + */ +void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + * @code{c} + * BaseType_t xQueueSendToFrontFromISR( + * QueueHandle_t xQueue, + * const void *pvItemToQueue, + * BaseType_t *pxHigherPriorityTaskWoken + * ); + * @endcode + * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the front of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @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 pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToFrontFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdPASS if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): + * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPriorityTaskWoken; + * + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWoken = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post the byte. + * xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. + * if( xHigherPriorityTaskWoken ) + * { + * // As 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 xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \ + xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT ) + + +/** + * queue. h + * @code{c} + * BaseType_t xQueueSendToBackFromISR( + * QueueHandle_t xQueue, + * const void *pvItemToQueue, + * BaseType_t *pxHigherPriorityTaskWoken + * ); + * @endcode + * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the back of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @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 pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToBackFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdPASS if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): + * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPriorityTaskWoken; + * + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWoken = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post the byte. + * xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. + * if( xHigherPriorityTaskWoken ) + * { + * // As 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 xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \ + xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * queue. h + * @code{c} + * BaseType_t xQueueOverwriteFromISR( + * QueueHandle_t xQueue, + * const void * pvItemToQueue, + * BaseType_t *pxHigherPriorityTaskWoken + * ); + * @endcode + * + * A version of xQueueOverwrite() that can be used in an interrupt service + * routine (ISR). + * + * Only for use with queues that can hold a single item - so the queue is either + * empty or full. + * + * Post an item on a queue. If the queue is already full then overwrite the + * value held in the queue. The item is queued by copy, not by reference. + * + * @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 pxHigherPriorityTaskWoken xQueueOverwriteFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueOverwriteFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return xQueueOverwriteFromISR() is a macro that calls + * xQueueGenericSendFromISR(), and therefore has the same return values as + * xQueueSendToFrontFromISR(). However, pdPASS is the only value that can be + * returned because xQueueOverwriteFromISR() will write to the queue even when + * the queue is already full. + * + * Example usage: + * @code{c} + * + * QueueHandle_t xQueue; + * + * void vFunction( void *pvParameters ) + * { + * // Create a queue to hold one uint32_t value. It is strongly + * // recommended *not* to use xQueueOverwriteFromISR() on queues that can + * // contain more than one value, and doing so will trigger an assertion + * // if configASSERT() is defined. + * xQueue = xQueueCreate( 1, sizeof( uint32_t ) ); + * } + * + * void vAnInterruptHandler( void ) + * { + * // xHigherPriorityTaskWoken must be set to pdFALSE before it is used. + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * uint32_t ulVarToSend, ulValReceived; + * + * // Write the value 10 to the queue using xQueueOverwriteFromISR(). + * ulVarToSend = 10; + * xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken ); + * + * // The queue is full, but calling xQueueOverwriteFromISR() again will still + * // pass because the value held in the queue will be overwritten with the + * // new value. + * ulVarToSend = 100; + * xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken ); + * + * // Reading from the queue will now return 100. + * + * // ... + * + * if( xHigherPrioritytaskWoken == pdTRUE ) + * { + * // Writing to the queue caused a task to unblock and the unblocked task + * // has a priority higher than or equal to the priority of the currently + * // executing task (the task this interrupt interrupted). Perform a context + * // switch so this interrupt returns directly to the unblocked task. + * // 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 xQueueOverwriteFromISR xQueueOverwriteFromISR + * \ingroup QueueManagement + */ +#define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \ + xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE ) + +/** + * queue. h + * @code{c} + * BaseType_t xQueueSendFromISR( + * QueueHandle_t xQueue, + * const void *pvItemToQueue, + * BaseType_t *pxHigherPriorityTaskWoken + * ); + * @endcode + * + * This is a macro that calls xQueueGenericSendFromISR(). It is included + * for backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToBackFromISR() and xQueueSendToFrontFromISR() + * macros. + * + * Post an item to the back of a queue. It is safe to use this function from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @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 pxHigherPriorityTaskWoken xQueueSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdPASS if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): + * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPriorityTaskWoken; + * + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWoken = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post the byte. + * xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. + * if( xHigherPriorityTaskWoken ) + * { + * // As 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 xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \ + xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * queue. h + * @code{c} + * BaseType_t xQueueGenericSendFromISR( + * QueueHandle_t xQueue, + * const void *pvItemToQueue, + * BaseType_t *pxHigherPriorityTaskWoken, + * BaseType_t xCopyPosition + * ); + * @endcode + * + * It is preferred that the macros xQueueSendFromISR(), + * xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() be used in place + * of calling this function directly. xQueueGiveFromISR() is an + * equivalent for use by semaphores that don't actually copy any data. + * + * Post an item on a queue. It is safe to use this function from within an + * interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @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 pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueGenericSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdPASS if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): + * @code{c} + * void vBufferISR( void ) + * { + * char cIn; + * BaseType_t xHigherPriorityTaskWokenByPost; + * + * // We have not woken a task at the start of the ISR. + * xHigherPriorityTaskWokenByPost = pdFALSE; + * + * // Loop until the buffer is empty. + * do + * { + * // Obtain a byte from the buffer. + * cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS ); + * + * // Post each byte. + * xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK ); + * + * } while( portINPUT_BYTE( BUFFER_COUNT ) ); + * + * // Now the buffer is empty we can switch context if necessary. + * if( xHigherPriorityTaskWokenByPost ) + * { + * // As xHigherPriorityTaskWokenByPost 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( xHigherPriorityTaskWokenByPost ); + * } + * } + * @endcode + * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, + const void * const pvItemToQueue, + BaseType_t * const pxHigherPriorityTaskWoken, + const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * queue. h + * @code{c} + * BaseType_t xQueueReceiveFromISR( + * QueueHandle_t xQueue, + * void *pvBuffer, + * BaseType_t *pxTaskWoken + * ); + * @endcode + * + * Receive an item from a queue. It is safe to use this function from within an + * interrupt service routine. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param pxHigherPriorityTaskWoken A task may be blocked waiting for space to + * become available on the queue. If xQueueReceiveFromISR causes such a task + * to unblock *pxTaskWoken will get set to pdTRUE, otherwise *pxTaskWoken will + * remain unchanged. + * + * @return pdPASS if an item was successfully received from the queue, + * otherwise pdFAIL. + * + * Example usage: + * @code{c} + * + * QueueHandle_t xQueue; + * + * // Function to create a queue and post some values. + * void vAFunction( void *pvParameters ) + * { + * char cValueToPost; + * const TickType_t xTicksToWait = ( TickType_t )0xff; + * + * // Create a queue capable of containing 10 characters. + * xQueue = xQueueCreate( 10, sizeof( char ) ); + * if( xQueue == 0 ) + * { + * // Failed to create the queue. + * } + * + * // ... + * + * // Post some characters that will be used within an ISR. If the queue + * // is full then this task will block for xTicksToWait ticks. + * cValueToPost = 'a'; + * xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait ); + * cValueToPost = 'b'; + * xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait ); + * + * // ... keep posting characters ... this task may block when the queue + * // becomes full. + * + * cValueToPost = 'c'; + * xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait ); + * } + * + * // ISR that outputs all the characters received on the queue. + * void vISR_Routine( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * char cRxedChar; + * + * while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xHigherPriorityTaskWoken) ) + * { + * // A character was received. Output the character now. + * vOutputCharacter( cRxedChar ); + * + * // If removing the character from the queue woke the task that was + * // posting onto the queue xHigherPriorityTaskWoken will have been set to + * // pdTRUE. No matter how many times this loop iterates only one + * // task will be woken. + * } + * + * if( xHigherPrioritytaskWoken == pdTRUE ); + * { + * // As 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 xQueueReceiveFromISR xQueueReceiveFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, + void * const pvBuffer, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/* + * Utilities to query queues that are safe to use from an ISR. These utilities + * should be used only from within an ISR, or within a critical section. + */ +BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +#if ( configUSE_CO_ROUTINES == 1 ) + +/* + * The functions defined above are for passing data to and from tasks. The + * functions below are the equivalents for passing data to and from + * co-routines. + * + * These functions are called from the co-routine macro implementation and + * should not be called directly from application code. Instead use the macro + * wrappers defined within croutine.h. + */ + BaseType_t xQueueCRSendFromISR( QueueHandle_t xQueue, + const void * pvItemToQueue, + BaseType_t xCoRoutinePreviouslyWoken ); + BaseType_t xQueueCRReceiveFromISR( QueueHandle_t xQueue, + void * pvBuffer, + BaseType_t * pxTaskWoken ); + BaseType_t xQueueCRSend( QueueHandle_t xQueue, + const void * pvItemToQueue, + TickType_t xTicksToWait ); + BaseType_t xQueueCRReceive( QueueHandle_t xQueue, + void * pvBuffer, + TickType_t xTicksToWait ); + +#endif /* if ( configUSE_CO_ROUTINES == 1 ) */ + +/* + * For internal use only. Use xSemaphoreCreateMutex(), + * xSemaphoreCreateCounting() or xSemaphoreGetMutexHolder() instead of calling + * these functions directly. + */ +QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, + StaticQueue_t * pxStaticQueue ) PRIVILEGED_FUNCTION; +#endif + +#if ( configUSE_COUNTING_SEMAPHORES == 1 ) + QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, + const UBaseType_t uxInitialCount ) PRIVILEGED_FUNCTION; +#endif + +#if ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, + const UBaseType_t uxInitialCount, + StaticQueue_t * pxStaticQueue ) PRIVILEGED_FUNCTION; +#endif + +BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) + TaskHandle_t xQueueGetMutexHolder( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; + TaskHandle_t xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; +#endif + +/* + * For internal use only. Use xSemaphoreTakeRecursive() or + * xSemaphoreGiveRecursive() instead of calling these functions directly. + */ +BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) PRIVILEGED_FUNCTION; + +/* + * Reset a queue back to its original empty state. The return value is now + * obsolete and is always set to pdPASS. + */ +#define xQueueReset( xQueue ) xQueueGenericReset( ( xQueue ), pdFALSE ) + +/* + * The registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add + * a queue, semaphore or mutex handle to the registry if you want the handle + * to be available to a kernel aware debugger. If you are not using a kernel + * aware debugger then this function can be ignored. + * + * configQUEUE_REGISTRY_SIZE defines the maximum number of handles the + * registry can hold. configQUEUE_REGISTRY_SIZE must be greater than 0 + * within FreeRTOSConfig.h for the registry to be available. Its value + * does not affect the number of queues, semaphores and mutexes that can be + * created - just the number that the registry can hold. + * + * If vQueueAddToRegistry is called more than once with the same xQueue + * parameter, the registry will store the pcQueueName parameter from the + * most recent call to vQueueAddToRegistry. + * + * @param xQueue The handle of the queue being added to the registry. This + * is the handle returned by a call to xQueueCreate(). Semaphore and mutex + * handles can also be passed in here. + * + * @param pcQueueName The name to be associated with the handle. This is the + * name that the kernel aware debugger will display. The queue registry only + * stores a pointer to the string - so the string must be persistent (global or + * preferably in ROM/Flash), not on the stack. + */ +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + void vQueueAddToRegistry( QueueHandle_t xQueue, + const char * pcQueueName ) PRIVILEGED_FUNCTION; +#endif + +/* + * The registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add + * a queue, semaphore or mutex handle to the registry if you want the handle + * to be available to a kernel aware debugger, and vQueueUnregisterQueue() to + * remove the queue, semaphore or mutex from the register. If you are not using + * a kernel aware debugger then this function can be ignored. + * + * @param xQueue The handle of the queue being removed from the registry. + */ +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + void vQueueUnregisterQueue( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +#endif + +/* + * The queue registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call pcQueueGetName() to look + * up and return the name of a queue in the queue registry from the queue's + * handle. + * + * @param xQueue The handle of the queue the name of which will be returned. + * @return If the queue is in the registry then a pointer to the name of the + * queue is returned. If the queue is not in the registry then NULL is + * returned. + */ +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + const char * pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +#endif + +/* + * Generic version of the function used to create a queue using dynamic memory + * allocation. This is called by other functions and macros that create other + * RTOS objects that use the queue structure as their base. + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#endif + +/* + * Generic version of the function used to create a queue using dynamic memory + * allocation. This is called by other functions and macros that create other + * RTOS objects that use the queue structure as their base. + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + uint8_t * pucQueueStorage, + StaticQueue_t * pxStaticQueue, + const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#endif + +/* + * Generic version of the function used to retrieve the buffers of statically + * created queues. This is called by other functions and macros that retrieve + * the buffers of other statically created RTOS objects that use the queue + * structure as their base. + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + BaseType_t xQueueGenericGetStaticBuffers( QueueHandle_t xQueue, + uint8_t ** ppucQueueStorage, + StaticQueue_t ** ppxStaticQueue ) PRIVILEGED_FUNCTION; +#endif + +/* + * Queue sets provide a mechanism to allow a task to block (pend) on a read + * operation from multiple queues or semaphores simultaneously. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * A queue set must be explicitly created using a call to xQueueCreateSet() or + * xQueueCreateSetStatic() before it can be used. Once created, standard + * FreeRTOS queues and semaphores can be added to the set using calls to + * xQueueAddToSet(). xQueueSelectFromSet() is then used to determine which, if + * any, of the queues or semaphores contained in the set is in a state where a + * queue read or semaphore take operation would be successful. + * + * Note 1: See the documentation on https://www.freertos.org/Documentation/02-Kernel/04-API-references/07-Queue-sets/00-RTOS-queue-sets + * for reasons why queue sets are very rarely needed in practice as there are + * simpler methods of blocking on multiple objects. + * + * Note 2: Blocking on a queue set that contains a mutex will not cause the + * mutex holder to inherit the priority of the blocked task. + * + * Note 3: An additional 4 bytes of RAM is required for each space in a every + * queue added to a queue set. Therefore counting semaphores that have a high + * maximum count value should not be added to a queue set. + * + * Note 4: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param uxEventQueueLength Queue sets store events that occur on + * the queues and semaphores contained in the set. uxEventQueueLength specifies + * the maximum number of events that can be queued at once. To be absolutely + * certain that events are not lost uxEventQueueLength should be set to the + * total sum of the length of the queues added to the set, where binary + * semaphores and mutexes have a length of 1, and counting semaphores have a + * length set by their maximum count value. Examples: + * + If a queue set is to hold a queue of length 5, another queue of length 12, + * and a binary semaphore, then uxEventQueueLength should be set to + * (5 + 12 + 1), or 18. + * + If a queue set is to hold three binary semaphores then uxEventQueueLength + * should be set to (1 + 1 + 1 ), or 3. + * + If a queue set is to hold a counting semaphore that has a maximum count of + * 5, and a counting semaphore that has a maximum count of 3, then + * uxEventQueueLength should be set to (5 + 3), or 8. + * + * @return If the queue set is created successfully then a handle to the created + * queue set is returned. Otherwise NULL is returned. + */ +#if ( ( configUSE_QUEUE_SETS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILEGED_FUNCTION; +#endif + +/* + * Queue sets provide a mechanism to allow a task to block (pend) on a read + * operation from multiple queues or semaphores simultaneously. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * A queue set must be explicitly created using a call to xQueueCreateSet() + * or xQueueCreateSetStatic() before it can be used. Once created, standard + * FreeRTOS queues and semaphores can be added to the set using calls to + * xQueueAddToSet(). xQueueSelectFromSet() is then used to determine which, if + * any, of the queues or semaphores contained in the set is in a state where a + * queue read or semaphore take operation would be successful. + * + * Note 1: See the documentation on https://www.freertos.org/Documentation/02-Kernel/04-API-references/07-Queue-sets/00-RTOS-queue-sets + * for reasons why queue sets are very rarely needed in practice as there are + * simpler methods of blocking on multiple objects. + * + * Note 2: Blocking on a queue set that contains a mutex will not cause the + * mutex holder to inherit the priority of the blocked task. + * + * Note 3: An additional 4 bytes of RAM is required for each space in a every + * queue added to a queue set. Therefore counting semaphores that have a high + * maximum count value should not be added to a queue set. + * + * Note 4: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param uxEventQueueLength Queue sets store events that occur on + * the queues and semaphores contained in the set. uxEventQueueLength specifies + * the maximum number of events that can be queued at once. To be absolutely + * certain that events are not lost uxEventQueueLength should be set to the + * total sum of the length of the queues added to the set, where binary + * semaphores and mutexes have a length of 1, and counting semaphores have a + * length set by their maximum count value. Examples: + * + If a queue set is to hold a queue of length 5, another queue of length 12, + * and a binary semaphore, then uxEventQueueLength should be set to + * (5 + 12 + 1), or 18. + * + If a queue set is to hold three binary semaphores then uxEventQueueLength + * should be set to (1 + 1 + 1 ), or 3. + * + If a queue set is to hold a counting semaphore that has a maximum count of + * 5, and a counting semaphore that has a maximum count of 3, then + * uxEventQueueLength should be set to (5 + 3), or 8. + * + * @param pucQueueStorage pucQueueStorage must point to a uint8_t array that is + * at least large enough to hold uxEventQueueLength events. + * + * @param pxQueueBuffer Must point to a variable of type StaticQueue_t, which + * will be used to hold the queue's data structure. + * + * @return If the queue set is created successfully then a handle to the created + * queue set is returned. If pxQueueBuffer is NULL then NULL is returned. + */ +#if ( ( configUSE_QUEUE_SETS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + QueueSetHandle_t xQueueCreateSetStatic( const UBaseType_t uxEventQueueLength, + uint8_t * pucQueueStorage, + StaticQueue_t * pxStaticQueue ) PRIVILEGED_FUNCTION; +#endif + +/* + * Adds a queue or semaphore to a queue set that was previously created by a + * call to xQueueCreateSet() or xQueueCreateSetStatic(). + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * Note 1: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param xQueueOrSemaphore The handle of the queue or semaphore being added to + * the queue set (cast to an QueueSetMemberHandle_t type). + * + * @param xQueueSet The handle of the queue set to which the queue or semaphore + * is being added. + * + * @return If the queue or semaphore was successfully added to the queue set + * then pdPASS is returned. If the queue could not be successfully added to the + * queue set because it is already a member of a different queue set then pdFAIL + * is returned. + */ +#if ( configUSE_QUEUE_SETS == 1 ) + BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; +#endif + +/* + * Removes a queue or semaphore from a queue set. A queue or semaphore can only + * be removed from a set if the queue or semaphore is empty. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * @param xQueueOrSemaphore The handle of the queue or semaphore being removed + * from the queue set (cast to an QueueSetMemberHandle_t type). + * + * @param xQueueSet The handle of the queue set in which the queue or semaphore + * is included. + * + * @return If the queue or semaphore was successfully removed from the queue set + * then pdPASS is returned. If the queue was not in the queue set, or the + * queue (or semaphore) was not empty, then pdFAIL is returned. + */ +#if ( configUSE_QUEUE_SETS == 1 ) + BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; +#endif + +/* + * xQueueSelectFromSet() selects from the members of a queue set a queue or + * semaphore that either contains data (in the case of a queue) or is available + * to take (in the case of a semaphore). xQueueSelectFromSet() effectively + * allows a task to block (pend) on a read operation on all the queues and + * semaphores in a queue set simultaneously. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * Note 1: See the documentation on https://www.freertos.org/Documentation/02-Kernel/04-API-references/07-Queue-sets/00-RTOS-queue-sets + * for reasons why queue sets are very rarely needed in practice as there are + * simpler methods of blocking on multiple objects. + * + * Note 2: Blocking on a queue set that contains a mutex will not cause the + * mutex holder to inherit the priority of the blocked task. + * + * Note 3: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param xQueueSet The queue set on which the task will (potentially) block. + * + * @param xTicksToWait The maximum time, in ticks, that the calling task will + * remain in the Blocked state (with other tasks executing) to wait for a member + * of the queue set to be ready for a successful queue read or semaphore take + * operation. + * + * @return xQueueSelectFromSet() will return the handle of a queue (cast to + * a QueueSetMemberHandle_t type) contained in the queue set that contains data, + * or the handle of a semaphore (cast to a QueueSetMemberHandle_t type) contained + * in the queue set that is available, or NULL if no such queue or semaphore + * exists before before the specified block time expires. + */ +#if ( configUSE_QUEUE_SETS == 1 ) + QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, + const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +#endif + +/* + * A version of xQueueSelectFromSet() that can be used from an ISR. + */ +#if ( configUSE_QUEUE_SETS == 1 ) + QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; +#endif + +/* Not public API functions. */ +void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, + TickType_t xTicksToWait, + const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGenericReset( QueueHandle_t xQueue, + BaseType_t xNewQueue ) PRIVILEGED_FUNCTION; + +#if ( configUSE_TRACE_FACILITY == 1 ) + void vQueueSetQueueNumber( QueueHandle_t xQueue, + UBaseType_t uxQueueNumber ) PRIVILEGED_FUNCTION; +#endif + +#if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +#endif + +#if ( configUSE_TRACE_FACILITY == 1 ) + uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +#endif + +UBaseType_t uxQueueGetQueueItemSize( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueGetQueueLength( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ + +#endif /* QUEUE_H */ diff --git a/vendor/FreeRTOS-Kernel/include/semphr.h b/vendor/FreeRTOS-Kernel/include/semphr.h new file mode 100644 index 0000000..c4b1a43 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/semphr.h @@ -0,0 +1,1215 @@ +/* + * 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 SEMAPHORE_H +#define SEMAPHORE_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h" must appear in source files before "include semphr.h" +#endif + +#include "queue.h" + +typedef QueueHandle_t SemaphoreHandle_t; + +#define semBINARY_SEMAPHORE_QUEUE_LENGTH ( ( UBaseType_t ) 1U ) +#define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( UBaseType_t ) 0U ) +#define semGIVE_BLOCK_TIME ( ( TickType_t ) 0U ) + + +/** + * semphr. h + * @code{c} + * vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore ); + * @endcode + * + * In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a binary semaphore! + * https://www.FreeRTOS.org/RTOS-task-notifications.html + * + * This old vSemaphoreCreateBinary() macro is now deprecated in favour of the + * xSemaphoreCreateBinary() function. Note that binary semaphores created using + * the vSemaphoreCreateBinary() macro are created in a state such that the + * first call to 'take' the semaphore would pass, whereas binary semaphores + * created using xSemaphoreCreateBinary() are created in a state such that the + * the semaphore must first be 'given' before it can be 'taken'. + * + * Macro that implements a semaphore by using the existing queue mechanism. + * The queue length is 1 as this is a binary semaphore. The data size is 0 + * as we don't want to actually store any data - we just want to know if the + * queue is empty or full. + * + * This type of semaphore can be used for pure synchronisation between tasks or + * between an interrupt and a task. The semaphore need not be given back once + * obtained, so one task/interrupt can continuously 'give' the semaphore while + * another continuously 'takes' the semaphore. For this reason this type of + * semaphore does not use a priority inheritance mechanism. For an alternative + * that does use priority inheritance see xSemaphoreCreateMutex(). + * + * @param xSemaphore Handle to the created semaphore. Should be of type SemaphoreHandle_t. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to vSemaphoreCreateBinary (). + * // This is a macro so pass the variable in directly. + * vSemaphoreCreateBinary( xSemaphore ); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode + * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary + * \ingroup Semaphores + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define vSemaphoreCreateBinary( xSemaphore ) \ + do { \ + ( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); \ + if( ( xSemaphore ) != NULL ) \ + { \ + ( void ) xSemaphoreGive( ( xSemaphore ) ); \ + } \ + } while( 0 ) +#endif + +/** + * semphr. h + * @code{c} + * SemaphoreHandle_t xSemaphoreCreateBinary( void ); + * @endcode + * + * Creates a new binary semaphore instance, and returns a handle by which the + * new semaphore can be referenced. + * + * In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a binary semaphore! + * https://www.FreeRTOS.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, binary semaphores use a block + * of memory, in which the semaphore structure is stored. If a binary semaphore + * is created using xSemaphoreCreateBinary() then the required memory is + * automatically dynamically allocated inside the xSemaphoreCreateBinary() + * function. (see https://www.FreeRTOS.org/a00111.html). If a binary semaphore + * is created using xSemaphoreCreateBinaryStatic() then the application writer + * must provide the memory. xSemaphoreCreateBinaryStatic() therefore allows a + * binary semaphore to be created without using any dynamic memory allocation. + * + * The old vSemaphoreCreateBinary() macro is now deprecated in favour of this + * xSemaphoreCreateBinary() function. Note that binary semaphores created using + * the vSemaphoreCreateBinary() macro are created in a state such that the + * first call to 'take' the semaphore would pass, whereas binary semaphores + * created using xSemaphoreCreateBinary() are created in a state such that the + * the semaphore must first be 'given' before it can be 'taken'. + * + * This type of semaphore can be used for pure synchronisation between tasks or + * between an interrupt and a task. The semaphore need not be given back once + * obtained, so one task/interrupt can continuously 'give' the semaphore while + * another continuously 'takes' the semaphore. For this reason this type of + * semaphore does not use a priority inheritance mechanism. For an alternative + * that does use priority inheritance see xSemaphoreCreateMutex(). + * + * @return Handle to the created semaphore, or NULL if the memory required to + * hold the semaphore's data structures could not be allocated. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to xSemaphoreCreateBinary(). + * // This is a macro so pass the variable in directly. + * xSemaphore = xSemaphoreCreateBinary(); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode + * \defgroup xSemaphoreCreateBinary xSemaphoreCreateBinary + * \ingroup Semaphores + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ) +#endif + +/** + * semphr. h + * @code{c} + * SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer ); + * @endcode + * + * Creates a new binary semaphore instance, and returns a handle by which the + * new semaphore can be referenced. + * + * NOTE: In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a binary semaphore! + * https://www.FreeRTOS.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, binary semaphores use a block + * of memory, in which the semaphore structure is stored. If a binary semaphore + * is created using xSemaphoreCreateBinary() then the required memory is + * automatically dynamically allocated inside the xSemaphoreCreateBinary() + * function. (see https://www.FreeRTOS.org/a00111.html). If a binary semaphore + * is created using xSemaphoreCreateBinaryStatic() then the application writer + * must provide the memory. xSemaphoreCreateBinaryStatic() therefore allows a + * binary semaphore to be created without using any dynamic memory allocation. + * + * This type of semaphore can be used for pure synchronisation between tasks or + * between an interrupt and a task. The semaphore need not be given back once + * obtained, so one task/interrupt can continuously 'give' the semaphore while + * another continuously 'takes' the semaphore. For this reason this type of + * semaphore does not use a priority inheritance mechanism. For an alternative + * that does use priority inheritance see xSemaphoreCreateMutex(). + * + * @param pxSemaphoreBuffer Must point to a variable of type StaticSemaphore_t, + * which will then be used to hold the semaphore's data structure, removing the + * need for the memory to be allocated dynamically. + * + * @return If the semaphore is created then a handle to the created semaphore is + * returned. If pxSemaphoreBuffer is NULL then NULL is returned. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * StaticSemaphore_t xSemaphoreBuffer; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to xSemaphoreCreateBinary(). + * // The semaphore's data structures will be placed in the xSemaphoreBuffer + * // variable, the address of which is passed into the function. The + * // function's parameter is not NULL, so the function will not attempt any + * // dynamic memory allocation, and therefore the function will not return + * // return NULL. + * xSemaphore = xSemaphoreCreateBinary( &xSemaphoreBuffer ); + * + * // Rest of task code goes here. + * } + * @endcode + * \defgroup xSemaphoreCreateBinaryStatic xSemaphoreCreateBinaryStatic + * \ingroup Semaphores + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreCreateBinaryStatic( pxStaticSemaphore ) xQueueGenericCreateStatic( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, ( pxStaticSemaphore ), queueQUEUE_TYPE_BINARY_SEMAPHORE ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * semphr. h + * @code{c} + * xSemaphoreTake( + * SemaphoreHandle_t xSemaphore, + * TickType_t xBlockTime + * ); + * @endcode + * + * Macro to obtain a semaphore. The semaphore must have previously been + * created with a call to xSemaphoreCreateBinary(), xSemaphoreCreateMutex() or + * xSemaphoreCreateCounting(). + * + * @param xSemaphore A handle to the semaphore being taken - obtained when + * the semaphore was created. + * + * @param xBlockTime The time in ticks to wait for the semaphore to become + * available. The macro portTICK_PERIOD_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the semaphore. A block + * time of portMAX_DELAY can be used to block indefinitely (provided + * INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h). + * + * @return pdTRUE if the semaphore was obtained. pdFALSE + * if xBlockTime expired without the semaphore becoming available. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * // A task that creates a semaphore. + * void vATask( void * pvParameters ) + * { + * // Create the semaphore to guard a shared resource. + * xSemaphore = xSemaphoreCreateBinary(); + * } + * + * // A task that uses the semaphore. + * void vAnotherTask( void * pvParameters ) + * { + * // ... Do other things. + * + * if( xSemaphore != NULL ) + * { + * // See if we can obtain the semaphore. If the semaphore is not available + * // wait 10 ticks to see if it becomes free. + * if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) + * { + * // We were able to obtain the semaphore and can now access the + * // shared resource. + * + * // ... + * + * // We have finished accessing the shared resource. Release the + * // semaphore. + * xSemaphoreGive( xSemaphore ); + * } + * else + * { + * // We could not obtain the semaphore and can therefore not access + * // the shared resource safely. + * } + * } + * } + * @endcode + * \defgroup xSemaphoreTake xSemaphoreTake + * \ingroup Semaphores + */ +#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) ) + +/** + * semphr. h + * @code{c} + * xSemaphoreTakeRecursive( + * SemaphoreHandle_t xMutex, + * TickType_t xBlockTime + * ); + * @endcode + * + * Macro to recursively obtain, or 'take', a mutex type semaphore. + * The mutex must have previously been created using a call to + * xSemaphoreCreateRecursiveMutex(); + * + * configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h for this + * macro to be available. + * + * This macro must not be used on mutexes created using xSemaphoreCreateMutex(). + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * @param xMutex A handle to the mutex being obtained. This is the + * handle returned by xSemaphoreCreateRecursiveMutex(); + * + * @param xBlockTime The time in ticks to wait for the semaphore to become + * available. The macro portTICK_PERIOD_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the semaphore. If + * the task already owns the semaphore then xSemaphoreTakeRecursive() will + * return immediately no matter what the value of xBlockTime. + * + * @return pdTRUE if the semaphore was obtained. pdFALSE if xBlockTime + * expired without the semaphore becoming available. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xMutex = NULL; + * + * // A task that creates a mutex. + * void vATask( void * pvParameters ) + * { + * // Create the mutex to guard a shared resource. + * xMutex = xSemaphoreCreateRecursiveMutex(); + * } + * + * // A task that uses the mutex. + * void vAnotherTask( void * pvParameters ) + * { + * // ... Do other things. + * + * if( xMutex != NULL ) + * { + * // See if we can obtain the mutex. If the mutex is not available + * // wait 10 ticks to see if it becomes free. + * if( xSemaphoreTakeRecursive( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) + * { + * // We were able to obtain the mutex and can now access the + * // shared resource. + * + * // ... + * // For some reason due to the nature of the code further calls to + * // xSemaphoreTakeRecursive() are made on the same mutex. In real + * // code these would not be just sequential calls as this would make + * // no sense. Instead the calls are likely to be buried inside + * // a more complex call structure. + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * + * // The mutex has now been 'taken' three times, so will not be + * // available to another task until it has also been given back + * // three times. Again it is unlikely that real code would have + * // these calls sequentially, but instead buried in a more complex + * // call structure. This is just for illustrative purposes. + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * + * // Now the mutex can be taken by other tasks. + * } + * else + * { + * // We could not obtain the mutex and can therefore not access + * // the shared resource safely. + * } + * } + * } + * @endcode + * \defgroup xSemaphoreTakeRecursive xSemaphoreTakeRecursive + * \ingroup Semaphores + */ +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) + #define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) ) +#endif + +/** + * semphr. h + * @code{c} + * xSemaphoreGive( SemaphoreHandle_t xSemaphore ); + * @endcode + * + * Macro to release a semaphore. The semaphore must have previously been + * created with a call to xSemaphoreCreateBinary(), xSemaphoreCreateMutex() or + * xSemaphoreCreateCounting(). and obtained using sSemaphoreTake(). + * + * This macro must not be used from an ISR. See xSemaphoreGiveFromISR () for + * an alternative which can be used from an ISR. + * + * This macro must also not be used on semaphores created using + * xSemaphoreCreateRecursiveMutex(). + * + * @param xSemaphore A handle to the semaphore being released. This is the + * handle returned when the semaphore was created. + * + * @return pdTRUE if the semaphore was released. pdFALSE if an error occurred. + * Semaphores are implemented using queues. An error can occur if there is + * no space on the queue to post a message - indicating that the + * semaphore was not first obtained correctly. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xSemaphore = NULL; + * + * void vATask( void * pvParameters ) + * { + * // Create the semaphore to guard a shared resource. + * xSemaphore = vSemaphoreCreateBinary(); + * + * if( xSemaphore != NULL ) + * { + * if( xSemaphoreGive( xSemaphore ) != pdTRUE ) + * { + * // We would expect this call to fail because we cannot give + * // a semaphore without first "taking" it! + * } + * + * // Obtain the semaphore - don't block if the semaphore is not + * // immediately available. + * if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ) + * { + * // We now have the semaphore and can access the shared resource. + * + * // ... + * + * // We have finished accessing the shared resource so can free the + * // semaphore. + * if( xSemaphoreGive( xSemaphore ) != pdTRUE ) + * { + * // We would not expect this call to fail because we must have + * // obtained the semaphore to get here. + * } + * } + * } + * } + * @endcode + * \defgroup xSemaphoreGive xSemaphoreGive + * \ingroup Semaphores + */ +#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) + +/** + * semphr. h + * @code{c} + * xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex ); + * @endcode + * + * Macro to recursively release, or 'give', a mutex type semaphore. + * The mutex must have previously been created using a call to + * xSemaphoreCreateRecursiveMutex(); + * + * configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h for this + * macro to be available. + * + * This macro must not be used on mutexes created using xSemaphoreCreateMutex(). + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * @param xMutex A handle to the mutex being released, or 'given'. This is the + * handle returned by xSemaphoreCreateMutex(); + * + * @return pdTRUE if the semaphore was given. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xMutex = NULL; + * + * // A task that creates a mutex. + * void vATask( void * pvParameters ) + * { + * // Create the mutex to guard a shared resource. + * xMutex = xSemaphoreCreateRecursiveMutex(); + * } + * + * // A task that uses the mutex. + * void vAnotherTask( void * pvParameters ) + * { + * // ... Do other things. + * + * if( xMutex != NULL ) + * { + * // See if we can obtain the mutex. If the mutex is not available + * // wait 10 ticks to see if it becomes free. + * if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE ) + * { + * // We were able to obtain the mutex and can now access the + * // shared resource. + * + * // ... + * // For some reason due to the nature of the code further calls to + * // xSemaphoreTakeRecursive() are made on the same mutex. In real + * // code these would not be just sequential calls as this would make + * // no sense. Instead the calls are likely to be buried inside + * // a more complex call structure. + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); + * + * // The mutex has now been 'taken' three times, so will not be + * // available to another task until it has also been given back + * // three times. Again it is unlikely that real code would have + * // these calls sequentially, it would be more likely that the calls + * // to xSemaphoreGiveRecursive() would be called as a call stack + * // unwound. This is just for demonstrative purposes. + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * xSemaphoreGiveRecursive( xMutex ); + * + * // Now the mutex can be taken by other tasks. + * } + * else + * { + * // We could not obtain the mutex and can therefore not access + * // the shared resource safely. + * } + * } + * } + * @endcode + * \defgroup xSemaphoreGiveRecursive xSemaphoreGiveRecursive + * \ingroup Semaphores + */ +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) + #define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) ) +#endif + +/** + * semphr. h + * @code{c} + * xSemaphoreGiveFromISR( + * SemaphoreHandle_t xSemaphore, + * BaseType_t *pxHigherPriorityTaskWoken + * ); + * @endcode + * + * Macro to release a semaphore. The semaphore must have previously been + * created with a call to xSemaphoreCreateBinary() or xSemaphoreCreateCounting(). + * + * Mutex type semaphores (those created using a call to xSemaphoreCreateMutex()) + * must not be used with this macro. + * + * This macro can be used from an ISR. + * + * @param xSemaphore A handle to the semaphore being released. This is the + * handle returned when the semaphore was created. + * + * @param pxHigherPriorityTaskWoken xSemaphoreGiveFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if giving the semaphore caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xSemaphoreGiveFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the semaphore was successfully given, otherwise errQUEUE_FULL. + * + * Example usage: + * @code{c} + \#define LONG_TIME 0xffff + \#define TICKS_TO_WAIT 10 + * SemaphoreHandle_t xSemaphore = NULL; + * + * // Repetitive task. + * void vATask( void * pvParameters ) + * { + * for( ;; ) + * { + * // We want this task to run every 10 ticks of a timer. The semaphore + * // was created before this task was started. + * + * // Block waiting for the semaphore to become available. + * if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE ) + * { + * // It is time to execute. + * + * // ... + * + * // We have finished our task. Return to the top of the loop where + * // we will block on the semaphore until it is time to execute + * // again. Note when using the semaphore for synchronisation with an + * // ISR in this manner there is no need to 'give' the semaphore back. + * } + * } + * } + * + * // Timer ISR + * void vTimerISR( void * pvParameters ) + * { + * static uint8_t ucLocalTickCount = 0; + * static BaseType_t xHigherPriorityTaskWoken; + * + * // A timer tick has occurred. + * + * // ... Do other time functions. + * + * // Is it time for vATask () to run? + * xHigherPriorityTaskWoken = pdFALSE; + * ucLocalTickCount++; + * if( ucLocalTickCount >= TICKS_TO_WAIT ) + * { + * // Unblock the task by releasing the semaphore. + * xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); + * + * // Reset the count so we release the semaphore again in 10 ticks time. + * ucLocalTickCount = 0; + * } + * + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // We can force a context switch here. Context switching from an + * // ISR uses port specific syntax. Check the demo task for your port + * // to find the syntax required. + * } + * } + * @endcode + * \defgroup xSemaphoreGiveFromISR xSemaphoreGiveFromISR + * \ingroup Semaphores + */ +#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) ) + +/** + * semphr. h + * @code{c} + * xSemaphoreTakeFromISR( + * SemaphoreHandle_t xSemaphore, + * BaseType_t *pxHigherPriorityTaskWoken + * ); + * @endcode + * + * Macro to take a semaphore from an ISR. The semaphore must have + * previously been created with a call to xSemaphoreCreateBinary() or + * xSemaphoreCreateCounting(). + * + * Mutex type semaphores (those created using a call to xSemaphoreCreateMutex()) + * must not be used with this macro. + * + * This macro can be used from an ISR, however taking a semaphore from an ISR + * is not a common operation. It is likely to only be useful when taking a + * counting semaphore when an interrupt is obtaining an object from a resource + * pool (when the semaphore count indicates the number of resources available). + * + * @param xSemaphore A handle to the semaphore being taken. This is the + * handle returned when the semaphore was created. + * + * @param pxHigherPriorityTaskWoken xSemaphoreTakeFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if taking the semaphore caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xSemaphoreTakeFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the semaphore was successfully taken, otherwise + * pdFALSE + */ +#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) ) + +/** + * semphr. h + * @code{c} + * SemaphoreHandle_t xSemaphoreCreateMutex( void ); + * @endcode + * + * Creates a new mutex type semaphore instance, and returns a handle by which + * the new mutex can be referenced. + * + * Internally, within the FreeRTOS implementation, mutex semaphores use a block + * of memory, in which the mutex structure is stored. If a mutex is created + * using xSemaphoreCreateMutex() then the required memory is automatically + * dynamically allocated inside the xSemaphoreCreateMutex() function. (see + * https://www.FreeRTOS.org/a00111.html). If a mutex is created using + * xSemaphoreCreateMutexStatic() then the application writer must provided the + * memory. xSemaphoreCreateMutexStatic() therefore allows a mutex to be created + * without using any dynamic memory allocation. + * + * Mutexes created using this function can be accessed using the xSemaphoreTake() + * and xSemaphoreGive() macros. The xSemaphoreTakeRecursive() and + * xSemaphoreGiveRecursive() macros must not be used. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See xSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @return If the mutex was successfully created then a handle to the created + * semaphore is returned. If there was not enough heap to allocate the mutex + * data structures then NULL is returned. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xSemaphore; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to xSemaphoreCreateMutex(). + * // This is a macro so pass the variable in directly. + * xSemaphore = xSemaphoreCreateMutex(); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode + * \defgroup xSemaphoreCreateMutex xSemaphoreCreateMutex + * \ingroup Semaphores + */ +#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_MUTEXES == 1 ) ) + #define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX ) +#endif + +/** + * semphr. h + * @code{c} + * SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer ); + * @endcode + * + * Creates a new mutex type semaphore instance, and returns a handle by which + * the new mutex can be referenced. + * + * Internally, within the FreeRTOS implementation, mutex semaphores use a block + * of memory, in which the mutex structure is stored. If a mutex is created + * using xSemaphoreCreateMutex() then the required memory is automatically + * dynamically allocated inside the xSemaphoreCreateMutex() function. (see + * https://www.FreeRTOS.org/a00111.html). If a mutex is created using + * xSemaphoreCreateMutexStatic() then the application writer must provided the + * memory. xSemaphoreCreateMutexStatic() therefore allows a mutex to be created + * without using any dynamic memory allocation. + * + * Mutexes created using this function can be accessed using the xSemaphoreTake() + * and xSemaphoreGive() macros. The xSemaphoreTakeRecursive() and + * xSemaphoreGiveRecursive() macros must not be used. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See xSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @param pxMutexBuffer Must point to a variable of type StaticSemaphore_t, + * which will be used to hold the mutex's data structure, removing the need for + * the memory to be allocated dynamically. + * + * @return If the mutex was successfully created then a handle to the created + * mutex is returned. If pxMutexBuffer was NULL then NULL is returned. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xSemaphore; + * StaticSemaphore_t xMutexBuffer; + * + * void vATask( void * pvParameters ) + * { + * // A mutex cannot be used before it has been created. xMutexBuffer is + * // into xSemaphoreCreateMutexStatic() so no dynamic memory allocation is + * // attempted. + * xSemaphore = xSemaphoreCreateMutexStatic( &xMutexBuffer ); + * + * // As no dynamic memory allocation was performed, xSemaphore cannot be NULL, + * // so there is no need to check it. + * } + * @endcode + * \defgroup xSemaphoreCreateMutexStatic xSemaphoreCreateMutexStatic + * \ingroup Semaphores + */ +#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_MUTEXES == 1 ) ) + #define xSemaphoreCreateMutexStatic( pxMutexBuffer ) xQueueCreateMutexStatic( queueQUEUE_TYPE_MUTEX, ( pxMutexBuffer ) ) +#endif + + +/** + * semphr. h + * @code{c} + * SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void ); + * @endcode + * + * Creates a new recursive mutex type semaphore instance, and returns a handle + * by which the new recursive mutex can be referenced. + * + * Internally, within the FreeRTOS implementation, recursive mutexes use a block + * of memory, in which the mutex structure is stored. If a recursive mutex is + * created using xSemaphoreCreateRecursiveMutex() then the required memory is + * automatically dynamically allocated inside the + * xSemaphoreCreateRecursiveMutex() function. (see + * https://www.FreeRTOS.org/a00111.html). If a recursive mutex is created using + * xSemaphoreCreateRecursiveMutexStatic() then the application writer must + * provide the memory that will get used by the mutex. + * xSemaphoreCreateRecursiveMutexStatic() therefore allows a recursive mutex to + * be created without using any dynamic memory allocation. + * + * Mutexes created using this macro can be accessed using the + * xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() macros. The + * xSemaphoreTake() and xSemaphoreGive() macros must not be used. + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See xSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @return xSemaphore Handle to the created mutex semaphore. Should be of type + * SemaphoreHandle_t. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xSemaphore; + * + * void vATask( void * pvParameters ) + * { + * // Semaphore cannot be used before a call to xSemaphoreCreateMutex(). + * // This is a macro so pass the variable in directly. + * xSemaphore = xSemaphoreCreateRecursiveMutex(); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode + * \defgroup xSemaphoreCreateRecursiveMutex xSemaphoreCreateRecursiveMutex + * \ingroup Semaphores + */ +#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) + #define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX ) +#endif + +/** + * semphr. h + * @code{c} + * SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer ); + * @endcode + * + * Creates a new recursive mutex type semaphore instance, and returns a handle + * by which the new recursive mutex can be referenced. + * + * Internally, within the FreeRTOS implementation, recursive mutexes use a block + * of memory, in which the mutex structure is stored. If a recursive mutex is + * created using xSemaphoreCreateRecursiveMutex() then the required memory is + * automatically dynamically allocated inside the + * xSemaphoreCreateRecursiveMutex() function. (see + * https://www.FreeRTOS.org/a00111.html). If a recursive mutex is created using + * xSemaphoreCreateRecursiveMutexStatic() then the application writer must + * provide the memory that will get used by the mutex. + * xSemaphoreCreateRecursiveMutexStatic() therefore allows a recursive mutex to + * be created without using any dynamic memory allocation. + * + * Mutexes created using this macro can be accessed using the + * xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() macros. The + * xSemaphoreTake() and xSemaphoreGive() macros must not be used. + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See xSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @param pxMutexBuffer Must point to a variable of type StaticSemaphore_t, + * which will then be used to hold the recursive mutex's data structure, + * removing the need for the memory to be allocated dynamically. + * + * @return If the recursive mutex was successfully created then a handle to the + * created recursive mutex is returned. If pxMutexBuffer was NULL then NULL is + * returned. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xSemaphore; + * StaticSemaphore_t xMutexBuffer; + * + * void vATask( void * pvParameters ) + * { + * // A recursive semaphore cannot be used before it is created. Here a + * // recursive mutex is created using xSemaphoreCreateRecursiveMutexStatic(). + * // The address of xMutexBuffer is passed into the function, and will hold + * // the mutexes data structures - so no dynamic memory allocation will be + * // attempted. + * xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer ); + * + * // As no dynamic memory allocation was performed, xSemaphore cannot be NULL, + * // so there is no need to check it. + * } + * @endcode + * \defgroup xSemaphoreCreateRecursiveMutexStatic xSemaphoreCreateRecursiveMutexStatic + * \ingroup Semaphores + */ +#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) ) + #define xSemaphoreCreateRecursiveMutexStatic( pxStaticSemaphore ) xQueueCreateMutexStatic( queueQUEUE_TYPE_RECURSIVE_MUTEX, ( pxStaticSemaphore ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * semphr. h + * @code{c} + * SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount ); + * @endcode + * + * Creates a new counting semaphore instance, and returns a handle by which the + * new counting semaphore can be referenced. + * + * In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a counting semaphore! + * https://www.FreeRTOS.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, counting semaphores use a + * block of memory, in which the counting semaphore structure is stored. If a + * counting semaphore is created using xSemaphoreCreateCounting() then the + * required memory is automatically dynamically allocated inside the + * xSemaphoreCreateCounting() function. (see + * https://www.FreeRTOS.org/a00111.html). If a counting semaphore is created + * using xSemaphoreCreateCountingStatic() then the application writer can + * instead optionally provide the memory that will get used by the counting + * semaphore. xSemaphoreCreateCountingStatic() therefore allows a counting + * semaphore to be created without using any dynamic memory allocation. + * + * Counting semaphores are typically used for two things: + * + * 1) Counting events. + * + * In this usage scenario an event handler will 'give' a semaphore each time + * an event occurs (incrementing the semaphore count value), and a handler + * task will 'take' a semaphore each time it processes an event + * (decrementing the semaphore count value). The count value is therefore + * the difference between the number of events that have occurred and the + * number that have been processed. In this case it is desirable for the + * initial count value to be zero. + * + * 2) Resource management. + * + * In this usage scenario the count value indicates the number of resources + * available. To obtain control of a resource a task must first obtain a + * semaphore - decrementing the semaphore count value. When the count value + * reaches zero there are no free resources. When a task finishes with the + * resource it 'gives' the semaphore back - incrementing the semaphore count + * value. In this case it is desirable for the initial count value to be + * equal to the maximum count value, indicating that all resources are free. + * + * @param uxMaxCount The maximum count value that can be reached. When the + * semaphore reaches this value it can no longer be 'given'. + * + * @param uxInitialCount The count value assigned to the semaphore when it is + * created. + * + * @return Handle to the created semaphore. Null if the semaphore could not be + * created. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xSemaphore; + * + * void vATask( void * pvParameters ) + * { + * SemaphoreHandle_t xSemaphore = NULL; + * + * // Semaphore cannot be used before a call to xSemaphoreCreateCounting(). + * // The max value to which the semaphore can count should be 10, and the + * // initial value assigned to the count should be 0. + * xSemaphore = xSemaphoreCreateCounting( 10, 0 ); + * + * if( xSemaphore != NULL ) + * { + * // The semaphore was created successfully. + * // The semaphore can now be used. + * } + * } + * @endcode + * \defgroup xSemaphoreCreateCounting xSemaphoreCreateCounting + * \ingroup Semaphores + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) ) +#endif + +/** + * semphr. h + * @code{c} + * SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer ); + * @endcode + * + * Creates a new counting semaphore instance, and returns a handle by which the + * new counting semaphore can be referenced. + * + * In many usage scenarios it is faster and more memory efficient to use a + * direct to task notification in place of a counting semaphore! + * https://www.FreeRTOS.org/RTOS-task-notifications.html + * + * Internally, within the FreeRTOS implementation, counting semaphores use a + * block of memory, in which the counting semaphore structure is stored. If a + * counting semaphore is created using xSemaphoreCreateCounting() then the + * required memory is automatically dynamically allocated inside the + * xSemaphoreCreateCounting() function. (see + * https://www.FreeRTOS.org/a00111.html). If a counting semaphore is created + * using xSemaphoreCreateCountingStatic() then the application writer must + * provide the memory. xSemaphoreCreateCountingStatic() therefore allows a + * counting semaphore to be created without using any dynamic memory allocation. + * + * Counting semaphores are typically used for two things: + * + * 1) Counting events. + * + * In this usage scenario an event handler will 'give' a semaphore each time + * an event occurs (incrementing the semaphore count value), and a handler + * task will 'take' a semaphore each time it processes an event + * (decrementing the semaphore count value). The count value is therefore + * the difference between the number of events that have occurred and the + * number that have been processed. In this case it is desirable for the + * initial count value to be zero. + * + * 2) Resource management. + * + * In this usage scenario the count value indicates the number of resources + * available. To obtain control of a resource a task must first obtain a + * semaphore - decrementing the semaphore count value. When the count value + * reaches zero there are no free resources. When a task finishes with the + * resource it 'gives' the semaphore back - incrementing the semaphore count + * value. In this case it is desirable for the initial count value to be + * equal to the maximum count value, indicating that all resources are free. + * + * @param uxMaxCount The maximum count value that can be reached. When the + * semaphore reaches this value it can no longer be 'given'. + * + * @param uxInitialCount The count value assigned to the semaphore when it is + * created. + * + * @param pxSemaphoreBuffer Must point to a variable of type StaticSemaphore_t, + * which will then be used to hold the semaphore's data structure, removing the + * need for the memory to be allocated dynamically. + * + * @return If the counting semaphore was successfully created then a handle to + * the created counting semaphore is returned. If pxSemaphoreBuffer was NULL + * then NULL is returned. + * + * Example usage: + * @code{c} + * SemaphoreHandle_t xSemaphore; + * StaticSemaphore_t xSemaphoreBuffer; + * + * void vATask( void * pvParameters ) + * { + * SemaphoreHandle_t xSemaphore = NULL; + * + * // Counting semaphore cannot be used before they have been created. Create + * // a counting semaphore using xSemaphoreCreateCountingStatic(). The max + * // value to which the semaphore can count is 10, and the initial value + * // assigned to the count will be 0. The address of xSemaphoreBuffer is + * // passed in and will be used to hold the semaphore structure, so no dynamic + * // memory allocation will be used. + * xSemaphore = xSemaphoreCreateCounting( 10, 0, &xSemaphoreBuffer ); + * + * // No memory allocation was attempted so xSemaphore cannot be NULL, so there + * // is no need to check its value. + * } + * @endcode + * \defgroup xSemaphoreCreateCountingStatic xSemaphoreCreateCountingStatic + * \ingroup Semaphores + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreCreateCountingStatic( uxMaxCount, uxInitialCount, pxSemaphoreBuffer ) xQueueCreateCountingSemaphoreStatic( ( uxMaxCount ), ( uxInitialCount ), ( pxSemaphoreBuffer ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * semphr. h + * @code{c} + * void vSemaphoreDelete( SemaphoreHandle_t xSemaphore ); + * @endcode + * + * Delete a semaphore. This function must be used with care. For example, + * do not delete a mutex type semaphore if the mutex is held by a task. + * + * @param xSemaphore A handle to the semaphore to be deleted. + * + * \defgroup vSemaphoreDelete vSemaphoreDelete + * \ingroup Semaphores + */ +#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) ) + +/** + * semphr.h + * @code{c} + * TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex ); + * @endcode + * + * If xMutex is indeed a mutex type semaphore, return the current mutex holder. + * If xMutex is not a mutex type semaphore, or the mutex is available (not held + * by a task), return NULL. + * + * Note: This is a good way of determining if the calling task is the mutex + * holder, but not a good way of determining the identity of the mutex holder as + * the holder may change between the function exiting and the returned value + * being tested. + */ +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) + #define xSemaphoreGetMutexHolder( xSemaphore ) xQueueGetMutexHolder( ( xSemaphore ) ) +#endif + +/** + * semphr.h + * @code{c} + * TaskHandle_t xSemaphoreGetMutexHolderFromISR( SemaphoreHandle_t xMutex ); + * @endcode + * + * If xMutex is indeed a mutex type semaphore, return the current mutex holder. + * If xMutex is not a mutex type semaphore, or the mutex is available (not held + * by a task), return NULL. + * + */ +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) + #define xSemaphoreGetMutexHolderFromISR( xSemaphore ) xQueueGetMutexHolderFromISR( ( xSemaphore ) ) +#endif + +/** + * semphr.h + * @code{c} + * UBaseType_t uxSemaphoreGetCount( SemaphoreHandle_t xSemaphore ); + * @endcode + * + * If the semaphore is a counting semaphore then uxSemaphoreGetCount() returns + * its current count value. If the semaphore is a binary semaphore then + * uxSemaphoreGetCount() returns 1 if the semaphore is available, and 0 if the + * semaphore is not available. + * + */ +#define uxSemaphoreGetCount( xSemaphore ) uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) ) + +/** + * semphr.h + * @code{c} + * UBaseType_t uxSemaphoreGetCountFromISR( SemaphoreHandle_t xSemaphore ); + * @endcode + * + * If the semaphore is a counting semaphore then uxSemaphoreGetCountFromISR() returns + * its current count value. If the semaphore is a binary semaphore then + * uxSemaphoreGetCountFromISR() returns 1 if the semaphore is available, and 0 if the + * semaphore is not available. + * + */ +#define uxSemaphoreGetCountFromISR( xSemaphore ) uxQueueMessagesWaitingFromISR( ( QueueHandle_t ) ( xSemaphore ) ) + +/** + * semphr.h + * @code{c} + * BaseType_t xSemaphoreGetStaticBuffer( SemaphoreHandle_t xSemaphore, + * StaticSemaphore_t ** ppxSemaphoreBuffer ); + * @endcode + * + * Retrieve pointer to a statically created binary semaphore, counting semaphore, + * or mutex semaphore's data structure buffer. This is the same buffer that is + * supplied at the time of creation. + * + * @param xSemaphore The semaphore for which to retrieve the buffer. + * + * @param ppxSemaphoreBuffer Used to return a pointer to the semaphore's + * data structure buffer. + * + * @return pdTRUE if buffer was retrieved, pdFALSE otherwise. + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xSemaphoreGetStaticBuffer( xSemaphore, ppxSemaphoreBuffer ) xQueueGenericGetStaticBuffers( ( QueueHandle_t ) ( xSemaphore ), NULL, ( ppxSemaphoreBuffer ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +#endif /* SEMAPHORE_H */ diff --git a/vendor/FreeRTOS-Kernel/include/stack_macros.h b/vendor/FreeRTOS-Kernel/include/stack_macros.h new file mode 100644 index 0000000..9b7dd77 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/stack_macros.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 */ diff --git a/vendor/FreeRTOS-Kernel/include/stream_buffer.h b/vendor/FreeRTOS-Kernel/include/stream_buffer.h new file mode 100644 index 0000000..bec895d --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/stream_buffer.h @@ -0,0 +1,1280 @@ +/* + * 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 + * + */ + +/* + * Stream buffers are used to send a continuous stream of data 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. + * + */ + +#ifndef STREAM_BUFFER_H +#define STREAM_BUFFER_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include stream_buffer.h" +#endif + +/* *INDENT-OFF* */ +#if defined( __cplusplus ) + extern "C" { +#endif +/* *INDENT-ON* */ + +/** + * Type of stream buffer. For internal use only. + */ +#define sbTYPE_STREAM_BUFFER ( ( BaseType_t ) 0 ) +#define sbTYPE_MESSAGE_BUFFER ( ( BaseType_t ) 1 ) +#define sbTYPE_STREAM_BATCHING_BUFFER ( ( BaseType_t ) 2 ) + +/** + * Type by which stream buffers are referenced. For example, a call to + * xStreamBufferCreate() returns an StreamBufferHandle_t variable that can + * then be used as a parameter to xStreamBufferSend(), xStreamBufferReceive(), + * etc. + */ +struct StreamBufferDef_t; +typedef struct StreamBufferDef_t * StreamBufferHandle_t; + +/** + * Type used as a stream buffer's optional callback. + */ +typedef void (* StreamBufferCallbackFunction_t)( StreamBufferHandle_t xStreamBuffer, + BaseType_t xIsInsideISR, + BaseType_t * const pxHigherPriorityTaskWoken ); + +/** + * stream_buffer.h + * + * @code{c} + * StreamBufferHandle_t xStreamBufferCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes ); + * @endcode + * + * Creates a new stream buffer using dynamically allocated memory. See + * xStreamBufferCreateStatic() 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 xStreamBufferCreate() to be available. + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferCreate() to be available. + * + * @param xBufferSizeBytes The total number of bytes the stream buffer will be + * able to hold at any one time. + * + * @param xTriggerLevelBytes The number of bytes that must be in the stream + * buffer before a task that is blocked on the stream buffer to wait for data is + * moved out of the blocked state. For example, if a task is blocked on a read + * of an empty stream buffer that has a trigger level of 1 then the task will be + * unblocked when a single byte is written to the buffer or the task's block + * time expires. As another example, if a task is blocked on a read of an empty + * stream buffer that has a trigger level of 10 then the task will not be + * unblocked until the stream buffer contains at least 10 bytes or the task's + * block time expires. If a reading task's block time expires before the + * trigger level is reached then the task will still receive however many bytes + * are actually available. Setting a trigger level of 0 will result in a + * trigger level of 1 being used. It is not valid to specify a trigger level + * that is greater than the buffer size. + * + * @param pxSendCompletedCallback Callback invoked when number of bytes at least equal to + * trigger level is sent to the stream buffer. If the parameter is NULL, 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 more than zero bytes are read from a + * stream buffer. If the parameter is NULL, 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 stream buffer cannot be created + * because there is insufficient heap memory available for FreeRTOS to allocate + * the stream buffer data structures and storage area. A non-NULL value being + * returned indicates that the stream buffer has been created successfully - + * the returned value should be stored as the handle to the created stream + * buffer. + * + * Example use: + * @code{c} + * + * void vAFunction( void ) + * { + * StreamBufferHandle_t xStreamBuffer; + * const size_t xStreamBufferSizeBytes = 100, xTriggerLevel = 10; + * + * // Create a stream buffer that can hold 100 bytes. The memory used to hold + * // both the stream buffer structure and the data in the stream buffer is + * // allocated dynamically. + * xStreamBuffer = xStreamBufferCreate( xStreamBufferSizeBytes, xTriggerLevel ); + * + * if( xStreamBuffer == NULL ) + * { + * // There was not enough heap memory space available to create the + * // stream buffer. + * } + * else + * { + * // The stream buffer was created successfully and can now be used. + * } + * } + * @endcode + * \defgroup xStreamBufferCreate xStreamBufferCreate + * \ingroup StreamBufferManagement + */ + +#define xStreamBufferCreate( xBufferSizeBytes, xTriggerLevelBytes ) \ + xStreamBufferGenericCreate( ( xBufferSizeBytes ), ( xTriggerLevelBytes ), sbTYPE_STREAM_BUFFER, NULL, NULL ) + +#if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) + #define xStreamBufferCreateWithCallback( xBufferSizeBytes, xTriggerLevelBytes, pxSendCompletedCallback, pxReceiveCompletedCallback ) \ + xStreamBufferGenericCreate( ( xBufferSizeBytes ), ( xTriggerLevelBytes ), sbTYPE_STREAM_BUFFER, ( pxSendCompletedCallback ), ( pxReceiveCompletedCallback ) ) +#endif + +/** + * stream_buffer.h + * + * @code{c} + * StreamBufferHandle_t xStreamBufferCreateStatic( size_t xBufferSizeBytes, + * size_t xTriggerLevelBytes, + * uint8_t *pucStreamBufferStorageArea, + * StaticStreamBuffer_t *pxStaticStreamBuffer ); + * @endcode + * Creates a new stream buffer using statically allocated memory. See + * xStreamBufferCreate() for a version that uses dynamically allocated memory. + * + * configSUPPORT_STATIC_ALLOCATION must be set to 1 in FreeRTOSConfig.h for + * xStreamBufferCreateStatic() to be available. configUSE_STREAM_BUFFERS must be + * set to 1 in for FreeRTOSConfig.h for xStreamBufferCreateStatic() to be + * available. + * + * @param xBufferSizeBytes The size, in bytes, of the buffer pointed to by the + * pucStreamBufferStorageArea parameter. + * + * @param xTriggerLevelBytes The number of bytes that must be in the stream + * buffer before a task that is blocked on the stream buffer to wait for data is + * moved out of the blocked state. For example, if a task is blocked on a read + * of an empty stream buffer that has a trigger level of 1 then the task will be + * unblocked when a single byte is written to the buffer or the task's block + * time expires. As another example, if a task is blocked on a read of an empty + * stream buffer that has a trigger level of 10 then the task will not be + * unblocked until the stream buffer contains at least 10 bytes or the task's + * block time expires. If a reading task's block time expires before the + * trigger level is reached then the task will still receive however many bytes + * are actually available. Setting a trigger level of 0 will result in a + * trigger level of 1 being used. It is not valid to specify a trigger level + * that is greater than the buffer size. + * + * @param pucStreamBufferStorageArea Must point to a uint8_t array that is at + * least xBufferSizeBytes big. This is the array to which streams are + * copied when they are written to the stream buffer. + * + * @param pxStaticStreamBuffer Must point to a variable of type + * StaticStreamBuffer_t, which will be used to hold the stream buffer's data + * structure. + * + * @param pxSendCompletedCallback Callback invoked when number of bytes at least equal to + * trigger level is sent to the stream buffer. If the parameter is NULL, 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 more than zero bytes are read from a + * stream buffer. If the parameter is NULL, 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 stream buffer is created successfully then a handle to the + * created stream buffer is returned. If either pucStreamBufferStorageArea or + * pxStaticstreamBuffer are NULL then NULL is returned. + * + * Example use: + * @code{c} + * + * // Used to dimension the array used to hold the streams. 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 streams within the stream + * // buffer. + * static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ]; + * + * // The variable used to hold the stream buffer structure. + * StaticStreamBuffer_t xStreamBufferStruct; + * + * void MyFunction( void ) + * { + * StreamBufferHandle_t xStreamBuffer; + * const size_t xTriggerLevel = 1; + * + * xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucStorageBuffer ), + * xTriggerLevel, + * ucStorageBuffer, + * &xStreamBufferStruct ); + * + * // As neither the pucStreamBufferStorageArea or pxStaticStreamBuffer + * // parameters were NULL, xStreamBuffer will not be NULL, and can be used to + * // reference the created stream buffer in other stream buffer API calls. + * + * // Other code that uses the stream buffer can go here. + * } + * + * @endcode + * \defgroup xStreamBufferCreateStatic xStreamBufferCreateStatic + * \ingroup StreamBufferManagement + */ + +#define xStreamBufferCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer ) \ + xStreamBufferGenericCreateStatic( ( xBufferSizeBytes ), ( xTriggerLevelBytes ), sbTYPE_STREAM_BUFFER, ( pucStreamBufferStorageArea ), ( pxStaticStreamBuffer ), NULL, NULL ) + +#if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) + #define xStreamBufferCreateStaticWithCallback( xBufferSizeBytes, xTriggerLevelBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer, pxSendCompletedCallback, pxReceiveCompletedCallback ) \ + xStreamBufferGenericCreateStatic( ( xBufferSizeBytes ), ( xTriggerLevelBytes ), sbTYPE_STREAM_BUFFER, ( pucStreamBufferStorageArea ), ( pxStaticStreamBuffer ), ( pxSendCompletedCallback ), ( pxReceiveCompletedCallback ) ) +#endif + +/** + * stream_buffer.h + * + * @code{c} + * StreamBufferHandle_t xStreamBatchingBufferCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes ); + * @endcode + * + * Creates a new stream batching buffer using dynamically allocated memory. See + * xStreamBatchingBufferCreateStatic() 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 xStreamBatchingBufferCreate() to be available. + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBatchingBufferCreate() to be available. + * + * The difference between a stream buffer and a stream batching buffer is when + * a task performs read on a non-empty buffer: + * - The task reading from a non-empty stream buffer returns immediately + * regardless of the amount of data in the buffer. + * - The task reading from a non-empty steam batching buffer blocks until the + * amount of data in the buffer exceeds the trigger level or the block time + * expires. + * + * @param xBufferSizeBytes The total number of bytes the stream batching buffer + * will be able to hold at any one time. + * + * @param xTriggerLevelBytes The number of bytes that must be in the stream + * batching buffer to unblock a task calling xStreamBufferReceive before the + * block time expires. + * + * @param pxSendCompletedCallback Callback invoked when number of bytes at least + * equal to trigger level is sent to the stream batching buffer. If the + * parameter is NULL, 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 more than zero bytes + * are read from a stream batching buffer. If the parameter is NULL, 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 stream batching buffer cannot be created + * because there is insufficient heap memory available for FreeRTOS to allocate + * the stream batching buffer data structures and storage area. A non-NULL value + * being returned indicates that the stream batching buffer has been created + * successfully - the returned value should be stored as the handle to the + * created stream batching buffer. + * + * Example use: + * @code{c} + * + * void vAFunction( void ) + * { + * StreamBufferHandle_t xStreamBatchingBuffer; + * const size_t xStreamBufferSizeBytes = 100, xTriggerLevel = 10; + * + * // Create a stream batching buffer that can hold 100 bytes. The memory used + * // to hold both the stream batching buffer structure and the data in the stream + * // batching buffer is allocated dynamically. + * xStreamBatchingBuffer = xStreamBatchingBufferCreate( xStreamBufferSizeBytes, xTriggerLevel ); + * + * if( xStreamBatchingBuffer == NULL ) + * { + * // There was not enough heap memory space available to create the + * // stream batching buffer. + * } + * else + * { + * // The stream batching buffer was created successfully and can now be used. + * } + * } + * @endcode + * \defgroup xStreamBatchingBufferCreate xStreamBatchingBufferCreate + * \ingroup StreamBatchingBufferManagement + */ + +#define xStreamBatchingBufferCreate( xBufferSizeBytes, xTriggerLevelBytes ) \ + xStreamBufferGenericCreate( ( xBufferSizeBytes ), ( xTriggerLevelBytes ), sbTYPE_STREAM_BATCHING_BUFFER, NULL, NULL ) + +#if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) + #define xStreamBatchingBufferCreateWithCallback( xBufferSizeBytes, xTriggerLevelBytes, pxSendCompletedCallback, pxReceiveCompletedCallback ) \ + xStreamBufferGenericCreate( ( xBufferSizeBytes ), ( xTriggerLevelBytes ), sbTYPE_STREAM_BATCHING_BUFFER, ( pxSendCompletedCallback ), ( pxReceiveCompletedCallback ) ) +#endif + +/** + * stream_buffer.h + * + * @code{c} + * StreamBufferHandle_t xStreamBatchingBufferCreateStatic( size_t xBufferSizeBytes, + * size_t xTriggerLevelBytes, + * uint8_t *pucStreamBufferStorageArea, + * StaticStreamBuffer_t *pxStaticStreamBuffer ); + * @endcode + * Creates a new stream batching buffer using statically allocated memory. See + * xStreamBatchingBufferCreate() for a version that uses dynamically allocated + * memory. + * + * configSUPPORT_STATIC_ALLOCATION must be set to 1 in FreeRTOSConfig.h for + * xStreamBatchingBufferCreateStatic() to be available. configUSE_STREAM_BUFFERS + * must be set to 1 in for FreeRTOSConfig.h for xStreamBatchingBufferCreateStatic() + * to be available. + * + * The difference between a stream buffer and a stream batching buffer is when + * a task performs read on a non-empty buffer: + * - The task reading from a non-empty stream buffer returns immediately + * regardless of the amount of data in the buffer. + * - The task reading from a non-empty steam batching buffer blocks until the + * amount of data in the buffer exceeds the trigger level or the block time + * expires. + * + * @param xBufferSizeBytes The size, in bytes, of the buffer pointed to by the + * pucStreamBufferStorageArea parameter. + * + * @param xTriggerLevelBytes The number of bytes that must be in the stream + * batching buffer to unblock a task calling xStreamBufferReceive before the + * block time expires. + * + * @param pucStreamBufferStorageArea Must point to a uint8_t array that is at + * least xBufferSizeBytes big. This is the array to which streams are + * copied when they are written to the stream batching buffer. + * + * @param pxStaticStreamBuffer Must point to a variable of type + * StaticStreamBuffer_t, which will be used to hold the stream batching buffer's + * data structure. + * + * @param pxSendCompletedCallback Callback invoked when number of bytes at least + * equal to trigger level is sent to the stream batching buffer. If the parameter + * is NULL, 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 more than zero bytes + * are read from a stream batching buffer. If the parameter is NULL, 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 stream batching buffer is created successfully then a handle + * to the created stream batching buffer is returned. If either pucStreamBufferStorageArea + * or pxStaticstreamBuffer are NULL then NULL is returned. + * + * Example use: + * @code{c} + * + * // Used to dimension the array used to hold the streams. 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 streams within the stream + * // batching buffer. + * static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ]; + * + * // The variable used to hold the stream batching buffer structure. + * StaticStreamBuffer_t xStreamBufferStruct; + * + * void MyFunction( void ) + * { + * StreamBufferHandle_t xStreamBatchingBuffer; + * const size_t xTriggerLevel = 1; + * + * xStreamBatchingBuffer = xStreamBatchingBufferCreateStatic( sizeof( ucStorageBuffer ), + * xTriggerLevel, + * ucStorageBuffer, + * &xStreamBufferStruct ); + * + * // As neither the pucStreamBufferStorageArea or pxStaticStreamBuffer + * // parameters were NULL, xStreamBatchingBuffer will not be NULL, and can be + * // used to reference the created stream batching buffer in other stream + * // buffer API calls. + * + * // Other code that uses the stream batching buffer can go here. + * } + * + * @endcode + * \defgroup xStreamBatchingBufferCreateStatic xStreamBatchingBufferCreateStatic + * \ingroup StreamBatchingBufferManagement + */ + +#define xStreamBatchingBufferCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer ) \ + xStreamBufferGenericCreateStatic( ( xBufferSizeBytes ), ( xTriggerLevelBytes ), sbTYPE_STREAM_BATCHING_BUFFER, ( pucStreamBufferStorageArea ), ( pxStaticStreamBuffer ), NULL, NULL ) + +#if ( configUSE_SB_COMPLETED_CALLBACK == 1 ) + #define xStreamBatchingBufferCreateStaticWithCallback( xBufferSizeBytes, xTriggerLevelBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer, pxSendCompletedCallback, pxReceiveCompletedCallback ) \ + xStreamBufferGenericCreateStatic( ( xBufferSizeBytes ), ( xTriggerLevelBytes ), sbTYPE_STREAM_BATCHING_BUFFER, ( pucStreamBufferStorageArea ), ( pxStaticStreamBuffer ), ( pxSendCompletedCallback ), ( pxReceiveCompletedCallback ) ) +#endif + +/** + * stream_buffer.h + * + * @code{c} + * BaseType_t xStreamBufferGetStaticBuffers( StreamBufferHandle_t xStreamBuffer, + * uint8_t ** ppucStreamBufferStorageArea, + * StaticStreamBuffer_t ** ppxStaticStreamBuffer ); + * @endcode + * + * Retrieve pointers to a statically created stream 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 + * xStreamBufferGetStaticBuffers() to be available. + * + * @param xStreamBuffer The stream buffer for which to retrieve the buffers. + * + * @param ppucStreamBufferStorageArea Used to return a pointer to the stream + * buffer's storage area buffer. + * + * @param ppxStaticStreamBuffer Used to return a pointer to the stream + * buffer's data structure buffer. + * + * @return pdTRUE if buffers were retrieved, pdFALSE otherwise. + * + * \defgroup xStreamBufferGetStaticBuffers xStreamBufferGetStaticBuffers + * \ingroup StreamBufferManagement + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + BaseType_t xStreamBufferGetStaticBuffers( StreamBufferHandle_t xStreamBuffer, + uint8_t ** ppucStreamBufferStorageArea, + StaticStreamBuffer_t ** ppxStaticStreamBuffer ) PRIVILEGED_FUNCTION; +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * stream_buffer.h + * + * @code{c} + * size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, + * const void *pvTxData, + * size_t xDataLengthBytes, + * TickType_t xTicksToWait ); + * @endcode + * + * Sends bytes to a stream buffer. The bytes are copied into the stream 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 xStreamBufferSend() to write to a stream buffer from a task. Use + * xStreamBufferSendFromISR() to write to a stream buffer from an interrupt + * service routine (ISR). + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferSend() to be available. + * + * @param xStreamBuffer The handle of the stream buffer to which a stream is + * being sent. + * + * @param pvTxData A pointer to the buffer that holds the bytes to be copied + * into the stream buffer. + * + * @param xDataLengthBytes The maximum number of bytes to copy from pvTxData + * into the stream buffer. + * + * @param xTicksToWait The maximum amount of time the task should remain in the + * Blocked state to wait for enough space to become available in the stream + * buffer, should the stream buffer contain too little space to hold the + * another xDataLengthBytes bytes. 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. If a task times out + * before it can write all xDataLengthBytes into the buffer it will still write + * as many bytes as possible. A task does not use any CPU time when it is in + * the blocked state. + * + * @return The number of bytes written to the stream buffer. If a task times + * out before it can write all xDataLengthBytes into the buffer it will still + * write as many bytes as possible. + * + * Example use: + * @code{c} + * void vAFunction( StreamBufferHandle_t xStreamBuffer ) + * { + * 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 stream buffer, blocking for a maximum of 100ms to + * // wait for enough space to be available in the stream buffer. + * xBytesSent = xStreamBufferSend( xStreamBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms ); + * + * if( xBytesSent != sizeof( ucArrayToSend ) ) + * { + * // The call to xStreamBufferSend() times out before there was enough + * // space in the buffer for the data to be written, but it did + * // successfully write xBytesSent bytes. + * } + * + * // Send the string to the stream buffer. Return immediately if there is not + * // enough space in the buffer. + * xBytesSent = xStreamBufferSend( xStreamBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 ); + * + * if( xBytesSent != strlen( pcStringToSend ) ) + * { + * // The entire string could not be added to the stream buffer because + * // there was not enough free space in the buffer, but xBytesSent bytes + * // were sent. Could try again to send the remaining bytes. + * } + * } + * @endcode + * \defgroup xStreamBufferSend xStreamBufferSend + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, + const void * pvTxData, + size_t xDataLengthBytes, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, + * const void *pvTxData, + * size_t xDataLengthBytes, + * BaseType_t *pxHigherPriorityTaskWoken ); + * @endcode + * + * Interrupt safe version of the API function that sends a stream of bytes to + * the stream 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 xStreamBufferSend() to write to a stream buffer from a task. Use + * xStreamBufferSendFromISR() to write to a stream buffer from an interrupt + * service routine (ISR). + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferSendFromISR() to be available. + * + * @param xStreamBuffer The handle of the stream buffer to which a stream is + * being sent. + * + * @param pvTxData A pointer to the data that is to be copied into the stream + * buffer. + * + * @param xDataLengthBytes The maximum number of bytes to copy from pvTxData + * into the stream buffer. + * + * @param pxHigherPriorityTaskWoken It is possible that a stream buffer will + * have a task blocked on it waiting for data. Calling + * xStreamBufferSendFromISR() can make data available, and so cause a task that + * was waiting for data to leave the Blocked state. If calling + * xStreamBufferSendFromISR() 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, xStreamBufferSendFromISR() + * will set *pxHigherPriorityTaskWoken to pdTRUE. If + * xStreamBufferSendFromISR() 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 example code below for an example. + * + * @return The number of bytes actually written to the stream buffer, which will + * be less than xDataLengthBytes if the stream buffer didn't have enough free + * space for all the bytes to be written. + * + * Example use: + * @code{c} + * // A stream buffer that has already been created. + * StreamBufferHandle_t xStreamBuffer; + * + * 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 stream buffer. + * xBytesSent = xStreamBufferSendFromISR( xStreamBuffer, + * ( void * ) pcStringToSend, + * strlen( pcStringToSend ), + * &xHigherPriorityTaskWoken ); + * + * if( xBytesSent != strlen( pcStringToSend ) ) + * { + * // There was not enough free space in the stream buffer for the entire + * // string to be written, ut xBytesSent bytes were written. + * } + * + * // If xHigherPriorityTaskWoken was set to pdTRUE inside + * // xStreamBufferSendFromISR() 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 xStreamBufferSendFromISR xStreamBufferSendFromISR + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferSendFromISR( StreamBufferHandle_t xStreamBuffer, + const void * pvTxData, + size_t xDataLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, + * void *pvRxData, + * size_t xBufferLengthBytes, + * TickType_t xTicksToWait ); + * @endcode + * + * Receives bytes from a stream 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 xStreamBufferReceive() to read from a stream buffer from a task. Use + * xStreamBufferReceiveFromISR() to read from a stream buffer from an + * interrupt service routine (ISR). + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferReceive() to be available. + * + * @param xStreamBuffer The handle of the stream buffer from which bytes are to + * be received. + * + * @param pvRxData A pointer to the buffer into which the received bytes will be + * copied. + * + * @param xBufferLengthBytes The length of the buffer pointed to by the + * pvRxData parameter. This sets the maximum number of bytes to receive in one + * call. xStreamBufferReceive will return as many bytes as possible up to a + * maximum set by xBufferLengthBytes. + * + * @param xTicksToWait The maximum amount of time the task should remain in the + * Blocked state to wait for data to become available if the stream buffer is + * empty. xStreamBufferReceive() will return immediately 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. A task does not use any CPU time when it is in the + * Blocked state. + * + * @return The number of bytes actually read from the stream buffer, which will + * be less than xBufferLengthBytes if the call to xStreamBufferReceive() timed + * out before xBufferLengthBytes were available. + * + * Example use: + * @code{c} + * void vAFunction( StreamBuffer_t xStreamBuffer ) + * { + * uint8_t ucRxData[ 20 ]; + * size_t xReceivedBytes; + * const TickType_t xBlockTime = pdMS_TO_TICKS( 20 ); + * + * // Receive up to another sizeof( ucRxData ) bytes from the stream buffer. + * // Wait in the Blocked state (so not using any CPU processing time) for a + * // maximum of 100ms for the full sizeof( ucRxData ) number of bytes to be + * // available. + * xReceivedBytes = xStreamBufferReceive( xStreamBuffer, + * ( void * ) ucRxData, + * sizeof( ucRxData ), + * xBlockTime ); + * + * if( xReceivedBytes > 0 ) + * { + * // A ucRxData contains another xReceivedBytes bytes of data, which can + * // be processed here.... + * } + * } + * @endcode + * \defgroup xStreamBufferReceive xStreamBufferReceive + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, + void * pvRxData, + size_t xBufferLengthBytes, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, + * void *pvRxData, + * size_t xBufferLengthBytes, + * BaseType_t *pxHigherPriorityTaskWoken ); + * @endcode + * + * An interrupt safe version of the API function that receives bytes from a + * stream buffer. + * + * Use xStreamBufferReceive() to read bytes from a stream buffer from a task. + * Use xStreamBufferReceiveFromISR() to read bytes from a stream buffer from an + * interrupt service routine (ISR). + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferReceiveFromISR() to be available. + * + * @param xStreamBuffer The handle of the stream buffer from which a stream + * is being received. + * + * @param pvRxData A pointer to the buffer into which the received bytes are + * copied. + * + * @param xBufferLengthBytes The length of the buffer pointed to by the + * pvRxData parameter. This sets the maximum number of bytes to receive in one + * call. xStreamBufferReceive will return as many bytes as possible up to a + * maximum set by xBufferLengthBytes. + * + * @param pxHigherPriorityTaskWoken It is possible that a stream buffer will + * have a task blocked on it waiting for space to become available. Calling + * xStreamBufferReceiveFromISR() can make space available, and so cause a task + * that is waiting for space to leave the Blocked state. If calling + * xStreamBufferReceiveFromISR() 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, + * xStreamBufferReceiveFromISR() will set *pxHigherPriorityTaskWoken to pdTRUE. + * If xStreamBufferReceiveFromISR() 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 number of bytes read from the stream buffer, if any. + * + * Example use: + * @code{c} + * // A stream buffer that has already been created. + * StreamBuffer_t xStreamBuffer; + * + * void vAnInterruptServiceRoutine( void ) + * { + * uint8_t ucRxData[ 20 ]; + * size_t xReceivedBytes; + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE. + * + * // Receive the next stream from the stream buffer. + * xReceivedBytes = xStreamBufferReceiveFromISR( xStreamBuffer, + * ( void * ) ucRxData, + * sizeof( ucRxData ), + * &xHigherPriorityTaskWoken ); + * + * if( xReceivedBytes > 0 ) + * { + * // ucRxData contains xReceivedBytes read from the stream buffer. + * // Process the stream here.... + * } + * + * // If xHigherPriorityTaskWoken was set to pdTRUE inside + * // xStreamBufferReceiveFromISR() 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 xStreamBufferReceiveFromISR xStreamBufferReceiveFromISR + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferReceiveFromISR( StreamBufferHandle_t xStreamBuffer, + void * pvRxData, + size_t xBufferLengthBytes, + BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ); + * @endcode + * + * Deletes a stream buffer that was previously created using a call to + * xStreamBufferCreate() or xStreamBufferCreateStatic(). If the stream + * buffer was created using dynamic memory (that is, by xStreamBufferCreate()), + * then the allocated memory is freed. + * + * A stream buffer handle must not be used after the stream buffer has been + * deleted. + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * vStreamBufferDelete() to be available. + * + * @param xStreamBuffer The handle of the stream buffer to be deleted. + * + * \defgroup vStreamBufferDelete vStreamBufferDelete + * \ingroup StreamBufferManagement + */ +void vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ); + * @endcode + * + * Queries a stream buffer to see if it is full. A stream buffer is full if it + * does not have any free space, and therefore cannot accept any more data. + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferIsFull() to be available. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return If the stream buffer is full then pdTRUE is returned. Otherwise + * pdFALSE is returned. + * + * \defgroup xStreamBufferIsFull xStreamBufferIsFull + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ); + * @endcode + * + * Queries a stream buffer to see if it is empty. A stream buffer is empty if + * it does not contain any data. + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferIsEmpty() to be available. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return If the stream buffer is empty then pdTRUE is returned. Otherwise + * pdFALSE is returned. + * + * \defgroup xStreamBufferIsEmpty xStreamBufferIsEmpty + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ); + * @endcode + * + * Resets a stream buffer to its initial, empty, state. Any data that was in + * the stream buffer is discarded. A stream buffer can only be reset if there + * are no tasks blocked waiting to either send to or receive from the stream + * buffer. + * + * Use xStreamBufferReset() to reset a stream buffer from a task. + * Use xStreamBufferResetFromISR() to reset a stream buffer from an + * interrupt service routine (ISR). + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferReset() to be available. + * + * @param xStreamBuffer The handle of the stream buffer being reset. + * + * @return If the stream buffer is reset then pdPASS is returned. If there was + * a task blocked waiting to send to or read from the stream buffer then the + * stream buffer is not reset and pdFAIL is returned. + * + * \defgroup xStreamBufferReset xStreamBufferReset + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * BaseType_t xStreamBufferResetFromISR( StreamBufferHandle_t xStreamBuffer ); + * @endcode + * + * An interrupt safe version of the API function that resets the stream buffer. + * + * Resets a stream buffer to its initial, empty, state. Any data that was in + * the stream buffer is discarded. A stream buffer can only be reset if there + * are no tasks blocked waiting to either send to or receive from the stream + * buffer. + * + * Use xStreamBufferReset() to reset a stream buffer from a task. + * Use xStreamBufferResetFromISR() to reset a stream buffer from an + * interrupt service routine (ISR). + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferResetFromISR() to be available. + * + * @param xStreamBuffer The handle of the stream buffer being reset. + * + * @return If the stream buffer is reset then pdPASS is returned. If there was + * a task blocked waiting to send to or read from the stream buffer then the + * stream buffer is not reset and pdFAIL is returned. + * + * \defgroup xStreamBufferResetFromISR xStreamBufferResetFromISR + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferResetFromISR( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ); + * @endcode + * + * Queries a stream buffer to see how much free space it contains, which is + * equal to the amount of data that can be sent to the stream buffer before it + * is full. + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferSpacesAvailable() to be available. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return The number of bytes that can be written to the stream buffer before + * the stream buffer would be full. + * + * \defgroup xStreamBufferSpacesAvailable xStreamBufferSpacesAvailable + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ); + * @endcode + * + * Queries a stream buffer to see how much data it contains, which is equal to + * the number of bytes that can be read from the stream buffer before the stream + * buffer would be empty. + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferBytesAvailable() to be available. + * + * @param xStreamBuffer The handle of the stream buffer being queried. + * + * @return The number of bytes that can be read from the stream buffer before + * the stream buffer would be empty. + * + * \defgroup xStreamBufferBytesAvailable xStreamBufferBytesAvailable + * \ingroup StreamBufferManagement + */ +size_t xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel ); + * @endcode + * + * A stream buffer's trigger level is the number of bytes that must be in the + * stream buffer before a task that is blocked on the stream buffer to + * wait for data is moved out of the blocked state. For example, if a task is + * blocked on a read of an empty stream buffer that has a trigger level of 1 + * then the task will be unblocked when a single byte is written to the buffer + * or the task's block time expires. As another example, if a task is blocked + * on a read of an empty stream buffer that has a trigger level of 10 then the + * task will not be unblocked until the stream buffer contains at least 10 bytes + * or the task's block time expires. If a reading task's block time expires + * before the trigger level is reached then the task will still receive however + * many bytes are actually available. Setting a trigger level of 0 will result + * in a trigger level of 1 being used. It is not valid to specify a trigger + * level that is greater than the buffer size. + * + * A trigger level is set when the stream buffer is created, and can be modified + * using xStreamBufferSetTriggerLevel(). + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * xStreamBufferSetTriggerLevel() to be available. + * + * @param xStreamBuffer The handle of the stream buffer being updated. + * + * @param xTriggerLevel The new trigger level for the stream buffer. + * + * @return If xTriggerLevel was less than or equal to the stream buffer's length + * then the trigger level will be updated and pdTRUE is returned. Otherwise + * pdFALSE is returned. + * + * \defgroup xStreamBufferSetTriggerLevel xStreamBufferSetTriggerLevel + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, + size_t xTriggerLevel ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, 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. xStreamBufferSendCompletedFromISR() 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 + * xStreamBufferSendCompletedFromISR() to be available. + * + * @param xStreamBuffer The handle of the stream buffer to which data was + * written. + * + * @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be + * initialised to pdFALSE before it is passed into + * xStreamBufferSendCompletedFromISR(). If calling + * xStreamBufferSendCompletedFromISR() 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 xStreamBufferSendCompletedFromISR xStreamBufferSendCompletedFromISR + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer, + BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, 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. xStreamBufferReceiveCompletedFromISR() + * 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 + * xStreamBufferReceiveCompletedFromISR() to be available. + * + * @param xStreamBuffer The handle of the stream buffer from which data was + * read. + * + * @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be + * initialised to pdFALSE before it is passed into + * xStreamBufferReceiveCompletedFromISR(). If calling + * xStreamBufferReceiveCompletedFromISR() 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 xStreamBufferReceiveCompletedFromISR xStreamBufferReceiveCompletedFromISR + * \ingroup StreamBufferManagement + */ +BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuffer, + BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * UBaseType_t uxStreamBufferGetStreamBufferNotificationIndex( StreamBufferHandle_t xStreamBuffer ); + * @endcode + * + * Get the task notification index used for the supplied stream buffer which can + * be set using vStreamBufferSetStreamBufferNotificationIndex. If the task + * notification index for the stream buffer is not changed using + * vStreamBufferSetStreamBufferNotificationIndex, this function returns the + * default value (tskDEFAULT_INDEX_TO_NOTIFY). + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * uxStreamBufferGetStreamBufferNotificationIndex() to be available. + * + * @param xStreamBuffer The handle of the stream buffer for which the task + * notification index is retrieved. + * + * @return The task notification index for the stream buffer. + * + * \defgroup uxStreamBufferGetStreamBufferNotificationIndex uxStreamBufferGetStreamBufferNotificationIndex + * \ingroup StreamBufferManagement + */ +UBaseType_t uxStreamBufferGetStreamBufferNotificationIndex( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +/** + * stream_buffer.h + * + * @code{c} + * void vStreamBufferSetStreamBufferNotificationIndex ( StreamBuffer_t xStreamBuffer, UBaseType_t uxNotificationIndex ); + * @endcode + * + * Set the task notification index used for the supplied stream buffer. + * Successive calls to stream buffer APIs (like xStreamBufferSend or + * xStreamBufferReceive) for this stream buffer will use this new index for + * their task notifications. + * + * If this function is not called, the default index (tskDEFAULT_INDEX_TO_NOTIFY) + * is used for task notifications. It is recommended to call this function + * before attempting to send or receive data from the stream buffer to avoid + * inconsistencies. + * + * configUSE_STREAM_BUFFERS must be set to 1 in for FreeRTOSConfig.h for + * vStreamBufferSetStreamBufferNotificationIndex() to be available. + * + * @param xStreamBuffer The handle of the stream buffer for which the task + * notification index is set. + * + * @param uxNotificationIndex The task notification index to set. + * + * \defgroup vStreamBufferSetStreamBufferNotificationIndex vStreamBufferSetStreamBufferNotificationIndex + * \ingroup StreamBufferManagement + */ +void vStreamBufferSetStreamBufferNotificationIndex( StreamBufferHandle_t xStreamBuffer, + UBaseType_t uxNotificationIndex ) PRIVILEGED_FUNCTION; + +/* Functions below here are not part of the public API. */ +StreamBufferHandle_t xStreamBufferGenericCreate( size_t xBufferSizeBytes, + size_t xTriggerLevelBytes, + BaseType_t xStreamBufferType, + StreamBufferCallbackFunction_t pxSendCompletedCallback, + StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) PRIVILEGED_FUNCTION; + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + StreamBufferHandle_t 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; +#endif + +size_t xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + +#if ( configUSE_TRACE_FACILITY == 1 ) + void vStreamBufferSetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer, + UBaseType_t uxStreamBufferNumber ) PRIVILEGED_FUNCTION; + UBaseType_t uxStreamBufferGetStreamBufferNumber( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; + uint8_t ucStreamBufferGetStreamBufferType( StreamBufferHandle_t xStreamBuffer ) PRIVILEGED_FUNCTION; +#endif + +/* *INDENT-OFF* */ +#if defined( __cplusplus ) + } +#endif +/* *INDENT-ON* */ + +#endif /* !defined( STREAM_BUFFER_H ) */ diff --git a/vendor/FreeRTOS-Kernel/include/task.h b/vendor/FreeRTOS-Kernel/include/task.h new file mode 100644 index 0000000..8192132 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/task.h @@ -0,0 +1,3800 @@ +/* + * FreeRTOS Kernel V11.3.0 + * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2025 Arm Limited and/or its affiliates + * + * 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_TASK_H +#define INC_TASK_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include task.h" +#endif + +#include "list.h" + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/*----------------------------------------------------------- +* MACROS AND DEFINITIONS +*----------------------------------------------------------*/ + +/* + * If tskKERNEL_VERSION_NUMBER ends with + it represents the version in development + * after the numbered release. + * + * The tskKERNEL_VERSION_MAJOR, tskKERNEL_VERSION_MINOR, tskKERNEL_VERSION_BUILD + * values will reflect the last released version number. + */ +#define tskKERNEL_VERSION_NUMBER "V11.3.0" +#define tskKERNEL_VERSION_MAJOR 11 +#define tskKERNEL_VERSION_MINOR 3 +#define tskKERNEL_VERSION_BUILD 0 + +/* MPU region parameters passed in ulParameters + * of MemoryRegion_t struct. */ +#define tskMPU_REGION_READ_ONLY ( 1U << 0U ) +#define tskMPU_REGION_READ_WRITE ( 1U << 1U ) +#define tskMPU_REGION_EXECUTE_NEVER ( 1U << 2U ) +#define tskMPU_REGION_NORMAL_MEMORY ( 1U << 3U ) +#define tskMPU_REGION_DEVICE_MEMORY ( 1U << 4U ) +#if defined( portARMV8M_MINOR_VERSION ) && ( portARMV8M_MINOR_VERSION >= 1 ) + #define tskMPU_REGION_PRIVILEGED_EXECUTE_NEVER ( 1U << 5U ) +#endif /* portARMV8M_MINOR_VERSION >= 1 */ +#define tskMPU_REGION_NON_SHAREABLE ( 1U << 6U ) +#define tskMPU_REGION_OUTER_SHAREABLE ( 1U << 7U ) +#define tskMPU_REGION_INNER_SHAREABLE ( 1U << 8U ) + +/* MPU region permissions stored in MPU settings to + * authorize access requests. */ +#define tskMPU_READ_PERMISSION ( 1U << 0U ) +#define tskMPU_WRITE_PERMISSION ( 1U << 1U ) + +/* The direct to task notification feature used to have only a single notification + * per task. Now there is an array of notifications per task that is dimensioned by + * configTASK_NOTIFICATION_ARRAY_ENTRIES. For backward compatibility, any use of the + * original direct to task notification defaults to using the first index in the + * array. */ +#define tskDEFAULT_INDEX_TO_NOTIFY ( 0 ) + +/** + * task. h + * + * Type by which tasks are referenced. For example, a call to xTaskCreate + * returns (via a pointer parameter) an TaskHandle_t variable that can then + * be used as a parameter to vTaskDelete to delete the task. + * + * \defgroup TaskHandle_t TaskHandle_t + * \ingroup Tasks + */ +struct tskTaskControlBlock; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +typedef struct tskTaskControlBlock * TaskHandle_t; +typedef const struct tskTaskControlBlock * ConstTaskHandle_t; + +/* + * Defines the prototype to which the application task hook function must + * conform. + */ +typedef BaseType_t (* TaskHookFunction_t)( void * arg ); + +/* Task states returned by eTaskGetState. */ +typedef enum +{ + eRunning = 0, /* A task is querying the state of itself, so must be running. */ + eReady, /* The task being queried is in a ready or pending ready list. */ + eBlocked, /* The task being queried is in the Blocked state. */ + eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ + eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */ + eInvalid /* Used as an 'invalid state' value. */ +} eTaskState; + +/* Actions that can be performed when vTaskNotify() is called. */ +typedef enum +{ + eNoAction = 0, /* Notify the task without updating its notify value. */ + eSetBits, /* Set bits in the task's notification value. */ + eIncrement, /* Increment the task's notification value. */ + eSetValueWithOverwrite, /* Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */ + eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task. */ +} eNotifyAction; + +/* + * Used internally only. + */ +typedef struct xTIME_OUT +{ + BaseType_t xOverflowCount; + TickType_t xTimeOnEntering; +} TimeOut_t; + +/* + * Defines the memory ranges allocated to the task when an MPU is used. + */ +typedef struct xMEMORY_REGION +{ + void * pvBaseAddress; + uint32_t ulLengthInBytes; + uint32_t ulParameters; +} MemoryRegion_t; + +/* + * Parameters required to create an MPU protected task. + */ +typedef struct xTASK_PARAMETERS +{ + TaskFunction_t pvTaskCode; + const char * pcName; + configSTACK_DEPTH_TYPE usStackDepth; + void * pvParameters; + UBaseType_t uxPriority; + StackType_t * puxStackBuffer; + MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ]; + #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + StaticTask_t * const pxTaskBuffer; + #endif +} TaskParameters_t; + +/* Used with the uxTaskGetSystemState() function to return the state of each task + * in the system. */ +typedef struct xTASK_STATUS +{ + TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */ + const char * pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ + UBaseType_t xTaskNumber; /* A number unique to the task. Note that this is not the task number that may be modified using vTaskSetTaskNumber() and uxTaskGetTaskNumber(), but a separate TCB-specific and unique identifier automatically assigned on task generation. */ + eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */ + UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */ + UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ + configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See https://www.FreeRTOS.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ + StackType_t * pxStackBase; /* Points to the lowest address of the task's stack area. */ + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + StackType_t * pxTopOfStack; /* Points to the top address of the task's stack area. */ + StackType_t * pxEndOfStack; /* Points to the end address of the task's stack area. */ + #endif + configSTACK_DEPTH_TYPE usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ + #if ( ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) + UBaseType_t uxCoreAffinityMask; /* The core affinity mask for the task */ + #endif +} TaskStatus_t; + +/* Possible return values for eTaskConfirmSleepModeStatus(). */ +typedef enum +{ + eAbortSleep = 0, /* A task has been made ready or a context switch pended since portSUPPRESS_TICKS_AND_SLEEP() was called - abort entering a sleep mode. */ + eStandardSleep /* Enter a sleep mode that will not last any longer than the expected idle time. */ + #if ( INCLUDE_vTaskSuspend == 1 ) + , + eNoTasksWaitingTimeout /* No tasks are waiting for a timeout so it is safe to enter a sleep mode that can only be exited by an external interrupt. */ + #endif /* INCLUDE_vTaskSuspend */ +} eSleepModeStatus; + +/** + * Defines the priority used by the idle task. This must not be modified. + * + * \ingroup TaskUtils + */ +#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U ) + +/** + * Defines affinity to all available cores. + * + * \ingroup TaskUtils + */ +#define tskNO_AFFINITY ( ( UBaseType_t ) -1 ) + +/** + * task. h + * + * Macro for forcing a context switch. + * + * \defgroup taskYIELD taskYIELD + * \ingroup SchedulerControl + */ +#define taskYIELD() portYIELD() + +/** + * task. h + * + * Macro to mark the start of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \defgroup taskENTER_CRITICAL taskENTER_CRITICAL + * \ingroup SchedulerControl + */ +#define taskENTER_CRITICAL() portENTER_CRITICAL() +#if ( configNUMBER_OF_CORES == 1 ) + #define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() +#else + #define taskENTER_CRITICAL_FROM_ISR() portENTER_CRITICAL_FROM_ISR() +#endif + +/** + * task. h + * + * Macro to mark the end of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL + * \ingroup SchedulerControl + */ +#define taskEXIT_CRITICAL() portEXIT_CRITICAL() +#if ( configNUMBER_OF_CORES == 1 ) + #define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) +#else + #define taskEXIT_CRITICAL_FROM_ISR( x ) portEXIT_CRITICAL_FROM_ISR( x ) +#endif + +/** + * task. h + * + * Macro to disable all maskable interrupts. + * + * \defgroup taskDISABLE_INTERRUPTS taskDISABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() + +/** + * task. h + * + * Macro to enable microcontroller interrupts. + * + * \defgroup taskENABLE_INTERRUPTS taskENABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() + +/* Definitions returned by xTaskGetSchedulerState(). taskSCHEDULER_SUSPENDED is + * 0 to generate more optimal code when configASSERT() is defined as the constant + * is used in assert() statements. */ +#define taskSCHEDULER_SUSPENDED ( ( BaseType_t ) 0 ) +#define taskSCHEDULER_NOT_STARTED ( ( BaseType_t ) 1 ) +#define taskSCHEDULER_RUNNING ( ( BaseType_t ) 2 ) + +/* Checks if core ID is valid. */ +#define taskVALID_CORE_ID( xCoreID ) ( ( ( ( ( BaseType_t ) 0 <= ( xCoreID ) ) && ( ( xCoreID ) < ( BaseType_t ) configNUMBER_OF_CORES ) ) ) ? ( pdTRUE ) : ( pdFALSE ) ) + +/*----------------------------------------------------------- +* TASK CREATION API +*----------------------------------------------------------*/ + +/** + * task. h + * @code{c} + * BaseType_t xTaskCreate( + * TaskFunction_t pxTaskCode, + * const char * const pcName, + * const configSTACK_DEPTH_TYPE uxStackDepth, + * void *pvParameters, + * UBaseType_t uxPriority, + * TaskHandle_t *pxCreatedTask + * ); + * @endcode + * + * Create a new task and add it to the list of tasks that are ready to run. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * See xTaskCreateStatic() for a version that does not use any dynamic memory + * allocation. + * + * xTaskCreate() can only be used to create a task that has unrestricted + * access to the entire microcontroller memory map. Systems that include MPU + * support can alternatively create an MPU constrained task using + * xTaskCreateRestricted(). + * + * @param pxTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN - default + * is 16. + * + * @param uxStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 16 bits wide and uxStackDepth is defined as 100, 200 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task should run. Systems that + * include MPU support can optionally create tasks in a privileged (system) + * mode by setting bit portPRIVILEGE_BIT of the priority parameter. For + * example, to create a privileged task at priority 2 the uxPriority parameter + * should be set to ( 2 | portPRIVILEGE_BIT ). + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: + * @code{c} + * // Task to be created. + * void vTaskCode( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * } + * } + * + * // Function that creates a task. + * void vOtherFunction( void ) + * { + * static uint8_t ucParameterToPass; + * TaskHandle_t xHandle = NULL; + * + * // Create the task, storing the handle. Note that the passed parameter ucParameterToPass + * // must exist for the lifetime of the task, so in this case is declared static. If it was just an + * // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time + * // the new task attempts to access it. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle ); + * configASSERT( xHandle ); + * + * // Use the handle to delete the task. + * if( xHandle != NULL ) + * { + * vTaskDelete( xHandle ); + * } + * } + * @endcode + * \defgroup xTaskCreate xTaskCreate + * \ingroup Tasks + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + BaseType_t 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; +#endif + +#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + BaseType_t xTaskCreateAffinitySet( TaskFunction_t pxTaskCode, + const char * const pcName, + const configSTACK_DEPTH_TYPE uxStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + UBaseType_t uxCoreAffinityMask, + TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, + * const char * const pcName, + * const configSTACK_DEPTH_TYPE uxStackDepth, + * void *pvParameters, + * UBaseType_t uxPriority, + * StackType_t *puxStackBuffer, + * StaticTask_t *pxTaskBuffer ); + * @endcode + * + * Create a new task and add it to the list of tasks that are ready to run. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * @param pxTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. The maximum length of the string is defined by + * configMAX_TASK_NAME_LEN in FreeRTOSConfig.h. + * + * @param uxStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 32-bits wide and uxStackDepth is defined as 100 then 400 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task will run. + * + * @param puxStackBuffer Must point to a StackType_t array that has at least + * uxStackDepth indexes - the array will then be used as the task's stack, + * removing the need for the stack to be allocated dynamically. + * + * @param pxTaskBuffer Must point to a variable of type StaticTask_t, which will + * then be used to hold the task's data structures, removing the need for the + * memory to be allocated dynamically. + * + * @return If neither puxStackBuffer nor pxTaskBuffer are NULL, then the task + * will be created and a handle to the created task is returned. If either + * puxStackBuffer or pxTaskBuffer are NULL then the task will not be created and + * NULL is returned. + * + * Example usage: + * @code{c} + * + * // Dimensions of the buffer that the task being created will use as its stack. + * // NOTE: This is the number of words the stack will hold, not the number of + * // bytes. For example, if each stack item is 32-bits, and this is set to 100, + * // then 400 bytes (100 * 32-bits) will be allocated. + #define STACK_SIZE 200 + * + * // Structure that will hold the TCB of the task being created. + * StaticTask_t xTaskBuffer; + * + * // Buffer that the task being created will use as its stack. Note this is + * // an array of StackType_t variables. The size of StackType_t is dependent on + * // the RTOS port. + * StackType_t xStack[ STACK_SIZE ]; + * + * // Function that implements the task being created. + * void vTaskCode( void * pvParameters ) + * { + * // The parameter value is expected to be 1 as 1 is passed in the + * // pvParameters value in the call to xTaskCreateStatic(). + * configASSERT( ( uint32_t ) pvParameters == 1U ); + * + * for( ;; ) + * { + * // Task code goes here. + * } + * } + * + * // Function that creates a task. + * void vOtherFunction( void ) + * { + * TaskHandle_t xHandle = NULL; + * + * // Create the task without using any dynamic memory allocation. + * xHandle = xTaskCreateStatic( + * vTaskCode, // Function that implements the task. + * "NAME", // Text name for the task. + * STACK_SIZE, // Stack size in words, not bytes. + * ( void * ) 1, // Parameter passed into the task. + * tskIDLE_PRIORITY,// Priority at which the task is created. + * xStack, // Array to use as the task's stack. + * &xTaskBuffer ); // Variable to hold the task's data structure. + * + * // puxStackBuffer and pxTaskBuffer were not NULL, so the task will have + * // been created, and xHandle will be the task's handle. Use the handle + * // to suspend the task. + * vTaskSuspend( xHandle ); + * } + * @endcode + * \defgroup xTaskCreateStatic xTaskCreateStatic + * \ingroup Tasks + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + TaskHandle_t 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; +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + TaskHandle_t xTaskCreateStaticAffinitySet( 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, + UBaseType_t uxCoreAffinityMask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * BaseType_t xTaskCreateRestricted( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask ); + * @endcode + * + * Only available when configSUPPORT_DYNAMIC_ALLOCATION is set to 1. + * + * xTaskCreateRestricted() should only be used in systems that include an MPU + * implementation. + * + * Create a new task and add it to the list of tasks that are ready to run. + * The function parameters define the memory regions and associated access + * permissions allocated to the task. + * + * See xTaskCreateRestrictedStatic() for a version that does not use any + * dynamic memory allocation. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: + * @code{c} + * // Create an TaskParameters_t structure that defines the task to be created. + * static const TaskParameters_t xCheckTaskParameters = + * { + * vATask, // pvTaskCode - the function that implements the task. + * "ATask", // pcName - just a text name for the task to assist debugging. + * 100, // uxStackDepth - the stack size DEFINED IN WORDS. + * NULL, // pvParameters - passed into the task function as the function parameters. + * ( 1U | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state. + * cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack. + * + * // xRegions - Allocate up to three separate memory regions for access by + * // the task, with appropriate access permissions. Different processors have + * // different memory alignment requirements - refer to the FreeRTOS documentation + * // for full information. + * { + * // Base address Length Parameters + * { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, + * { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, + * { cPrivilegedOnlyAccessArray, 128, portMPU_REGION_PRIVILEGED_READ_WRITE } + * } + * }; + * + * int main( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task from the const structure defined above. The task handle + * // is requested (the second parameter is not NULL) but in this case just for + * // demonstration purposes as its not actually used. + * xTaskCreateRestricted( &xRegTest1Parameters, &xHandle ); + * + * // Start the scheduler. + * vTaskStartScheduler(); + * + * // Will only get here if there was insufficient memory to create the idle + * // and/or timer task. + * for( ;; ); + * } + * @endcode + * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + BaseType_t xTaskCreateRestrictedAffinitySet( const TaskParameters_t * const pxTaskDefinition, + UBaseType_t uxCoreAffinityMask, + TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * BaseType_t xTaskCreateRestrictedStatic( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask ); + * @endcode + * + * Only available when configSUPPORT_STATIC_ALLOCATION is set to 1. + * + * xTaskCreateRestrictedStatic() should only be used in systems that include an + * MPU implementation. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreateRestricted() then the stack is provided by the application writer, + * and the memory used to hold the task's data structure is automatically + * dynamically allocated inside the xTaskCreateRestricted() function. If a task + * is created using xTaskCreateRestrictedStatic() then the application writer + * must provide the memory used to hold the task's data structures too. + * xTaskCreateRestrictedStatic() therefore allows a memory protected task to be + * created without using any dynamic memory allocation. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. If configSUPPORT_STATIC_ALLOCATION is set to 1 the structure + * contains an additional member, which is used to point to a variable of type + * StaticTask_t - which is then used to hold the task's data structure. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: + * @code{c} + * // Create an TaskParameters_t structure that defines the task to be created. + * // The StaticTask_t variable is only included in the structure when + * // configSUPPORT_STATIC_ALLOCATION is set to 1. The PRIVILEGED_DATA macro can + * // be used to force the variable into the RTOS kernel's privileged data area. + * static PRIVILEGED_DATA StaticTask_t xTaskBuffer; + * static const TaskParameters_t xCheckTaskParameters = + * { + * vATask, // pvTaskCode - the function that implements the task. + * "ATask", // pcName - just a text name for the task to assist debugging. + * 100, // uxStackDepth - the stack size DEFINED IN WORDS. + * NULL, // pvParameters - passed into the task function as the function parameters. + * ( 1U | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state. + * cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack. + * + * // xRegions - Allocate up to three separate memory regions for access by + * // the task, with appropriate access permissions. Different processors have + * // different memory alignment requirements - refer to the FreeRTOS documentation + * // for full information. + * { + * // Base address Length Parameters + * { cReadWriteArray, 32, portMPU_REGION_READ_WRITE }, + * { cReadOnlyArray, 32, portMPU_REGION_READ_ONLY }, + * { cPrivilegedOnlyAccessArray, 128, portMPU_REGION_PRIVILEGED_READ_WRITE } + * } + * + * &xTaskBuffer; // Holds the task's data structure. + * }; + * + * int main( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task from the const structure defined above. The task handle + * // is requested (the second parameter is not NULL) but in this case just for + * // demonstration purposes as its not actually used. + * xTaskCreateRestrictedStatic( &xRegTest1Parameters, &xHandle ); + * + * // Start the scheduler. + * vTaskStartScheduler(); + * + * // Will only get here if there was insufficient memory to create the idle + * // and/or timer task. + * for( ;; ); + * } + * @endcode + * \defgroup xTaskCreateRestrictedStatic xTaskCreateRestrictedStatic + * \ingroup Tasks + */ +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + BaseType_t xTaskCreateRestrictedStaticAffinitySet( const TaskParameters_t * const pxTaskDefinition, + UBaseType_t uxCoreAffinityMask, + TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ); + * @endcode + * + * Memory regions are assigned to a restricted task when the task is created by + * a call to xTaskCreateRestricted(). These regions can be redefined using + * vTaskAllocateMPURegions(). + * + * @param xTaskToModify The handle of the task being updated. + * + * @param[in] pxRegions A pointer to a MemoryRegion_t structure that contains the + * new memory region definitions. + * + * Example usage: + * @code{c} + * // Define an array of MemoryRegion_t structures that configures an MPU region + * // allowing read/write access for 1024 bytes starting at the beginning of the + * // ucOneKByte array. The other two of the maximum 3 definable regions are + * // unused so set to zero. + * static const MemoryRegion_t xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] = + * { + * // Base address Length Parameters + * { ucOneKByte, 1024, portMPU_REGION_READ_WRITE }, + * { 0, 0, 0 }, + * { 0, 0, 0 } + * }; + * + * void vATask( void *pvParameters ) + * { + * // This task was created such that it has access to certain regions of + * // memory as defined by the MPU configuration. At some point it is + * // desired that these MPU regions are replaced with that defined in the + * // xAltRegions const struct above. Use a call to vTaskAllocateMPURegions() + * // for this purpose. NULL is used as the task handle to indicate that this + * // function should modify the MPU regions of the calling task. + * vTaskAllocateMPURegions( NULL, xAltRegions ); + * + * // Now the task can continue its function, but from this point on can only + * // access its stack and the ucOneKByte array (unless any other statically + * // defined or shared regions have been declared elsewhere). + * } + * @endcode + * \defgroup vTaskAllocateMPURegions vTaskAllocateMPURegions + * \ingroup Tasks + */ +#if ( portUSING_MPU_WRAPPERS == 1 ) + void vTaskAllocateMPURegions( TaskHandle_t xTaskToModify, + const MemoryRegion_t * const pxRegions ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * void vTaskDelete( TaskHandle_t xTaskToDelete ); + * @endcode + * + * INCLUDE_vTaskDelete must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Remove a task from the RTOS real time kernel's management. The task being + * deleted will be removed from all ready, blocked, suspended and event lists. + * + * NOTE: The idle task is responsible for freeing the kernel allocated + * memory from tasks that have been deleted. It is therefore important that + * the idle task is not starved of microcontroller processing time if your + * application makes any calls to vTaskDelete (). Memory allocated by the + * task code is not automatically freed, and should be freed before the task + * is deleted. + * + * See the demo application file death.c for sample code that utilises + * vTaskDelete (). + * + * @param xTaskToDelete The handle of the task to be deleted. Passing NULL will + * cause the calling task to be deleted. + * + * Example usage: + * @code{c} + * void vOtherFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create the task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // Use the handle to delete the task. + * vTaskDelete( xHandle ); + * } + * @endcode + * \defgroup vTaskDelete vTaskDelete + * \ingroup Tasks + */ +void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- +* TASK CONTROL API +*----------------------------------------------------------*/ + +/** + * task. h + * @code{c} + * void vTaskDelay( const TickType_t xTicksToDelay ); + * @endcode + * + * Delay a task for a given number of ticks. The actual time that the + * task remains blocked depends on the tick rate. The constant + * portTICK_PERIOD_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * + * vTaskDelay() specifies a time at which the task wishes to unblock relative to + * the time at which vTaskDelay() is called. For example, specifying a block + * period of 100 ticks will cause the task to unblock 100 ticks after + * vTaskDelay() is called. vTaskDelay() does not therefore provide a good method + * of controlling the frequency of a periodic task as the path taken through the + * code, as well as other task and interrupt activity, will affect the frequency + * at which vTaskDelay() gets called and therefore the time at which the task + * next executes. See xTaskDelayUntil() for an alternative API function designed + * to facilitate fixed frequency execution. It does this by specifying an + * absolute time (rather than a relative time) at which the calling task should + * unblock. + * + * @param xTicksToDelay The amount of time, in tick periods, that + * the calling task should block. + * + * Example usage: + * + * void vTaskFunction( void * pvParameters ) + * { + * // Block for 500ms. + * const TickType_t xDelay = 500 / portTICK_PERIOD_MS; + * + * for( ;; ) + * { + * // Simply toggle the LED every 500ms, blocking between each toggle. + * vToggleLED(); + * vTaskDelay( xDelay ); + * } + * } + * + * \defgroup vTaskDelay vTaskDelay + * \ingroup TaskCtrl + */ +void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * BaseType_t xTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement ); + * @endcode + * + * INCLUDE_xTaskDelayUntil must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Delay a task until a specified time. This function can be used by periodic + * tasks to ensure a constant execution frequency. + * + * This function differs from vTaskDelay () in one important aspect: vTaskDelay () will + * cause a task to block for the specified number of ticks from the time vTaskDelay () is + * called. It is therefore difficult to use vTaskDelay () by itself to generate a fixed + * execution frequency as the time between a task starting to execute and that task + * calling vTaskDelay () may not be fixed [the task may take a different path though the + * code between calls, or may get interrupted or preempted a different number of times + * each time it executes]. + * + * Whereas vTaskDelay () specifies a wake time relative to the time at which the function + * is called, xTaskDelayUntil () specifies the absolute (exact) time at which it wishes to + * unblock. + * + * The macro pdMS_TO_TICKS() can be used to calculate the number of ticks from a + * time specified in milliseconds with a resolution of one tick period. + * + * @param pxPreviousWakeTime Pointer to a variable that holds the time at which the + * task was last unblocked. The variable must be initialised with the current time + * prior to its first use (see the example below). Following this the variable is + * automatically updated within xTaskDelayUntil (). + * + * @param xTimeIncrement The cycle time period. The task will be unblocked at + * time *pxPreviousWakeTime + xTimeIncrement. Calling xTaskDelayUntil with the + * same xTimeIncrement parameter value will cause the task to execute with + * a fixed interface period. + * + * @return Value which can be used to check whether the task was actually delayed. + * Will be pdTRUE if the task way delayed and pdFALSE otherwise. A task will not + * be delayed if the next expected wake time is in the past. This prevents periodic + * tasks from accumulating delays and allows them to resume their regular timing pattern. + * + * Example usage: + * @code{c} + * // Perform an action every 10 ticks. + * void vTaskFunction( void * pvParameters ) + * { + * TickType_t xLastWakeTime; + * const TickType_t xFrequency = 10; + * BaseType_t xWasDelayed; + * + * // Initialise the xLastWakeTime variable with the current time. + * xLastWakeTime = xTaskGetTickCount (); + * for( ;; ) + * { + * // Wait for the next cycle. + * xWasDelayed = xTaskDelayUntil( &xLastWakeTime, xFrequency ); + * + * // Perform action here. xWasDelayed value can be used to determine + * // whether a deadline was missed if the code here took too long. + * } + * } + * @endcode + * \defgroup xTaskDelayUntil xTaskDelayUntil + * \ingroup TaskCtrl + */ +BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, + const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION; + +/* + * vTaskDelayUntil() is the older version of xTaskDelayUntil() and does not + * return a value. + */ +#define vTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement ) \ + do { \ + ( void ) xTaskDelayUntil( ( pxPreviousWakeTime ), ( xTimeIncrement ) ); \ + } while( 0 ) + + +/** + * task. h + * @code{c} + * BaseType_t xTaskAbortDelay( TaskHandle_t xTask ); + * @endcode + * + * INCLUDE_xTaskAbortDelay must be defined as 1 in FreeRTOSConfig.h for this + * function to be available. + * + * A task will enter the Blocked state when it is waiting for an event. The + * event it is waiting for can be a temporal event (waiting for a time), such + * as when vTaskDelay() is called, or an event on an object, such as when + * xQueueReceive() or ulTaskNotifyTake() is called. If the handle of a task + * that is in the Blocked state is used in a call to xTaskAbortDelay() then the + * task will leave the Blocked state, and return from whichever function call + * placed the task into the Blocked state. + * + * There is no 'FromISR' version of this function as an interrupt would need to + * know which object a task was blocked on in order to know which actions to + * take. For example, if the task was blocked on a queue the interrupt handler + * would then need to know if the queue was locked. + * + * @param xTask The handle of the task to remove from the Blocked state. + * + * @return If the task referenced by xTask was not in the Blocked state then + * pdFAIL is returned. Otherwise pdPASS is returned. + * + * \defgroup xTaskAbortDelay xTaskAbortDelay + * \ingroup TaskCtrl + */ +#if ( INCLUDE_xTaskAbortDelay == 1 ) + BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ); + * @endcode + * + * INCLUDE_uxTaskPriorityGet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the priority of any task. + * + * @param xTask Handle of the task to be queried. Passing a NULL + * handle results in the priority of the calling task being returned. + * + * @return The priority of xTask. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to obtain the priority of the created task. + * // It was created with tskIDLE_PRIORITY, but may have changed + * // it itself. + * if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY ) + * { + * // The task has changed it's priority. + * } + * + * // ... + * + * // Is our priority higher than the created task? + * if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) ) + * { + * // Our priority (obtained using NULL handle) is higher. + * } + * } + * @endcode + * \defgroup uxTaskPriorityGet uxTaskPriorityGet + * \ingroup TaskCtrl + */ +UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ); + * @endcode + * + * A version of uxTaskPriorityGet() that can be used from an ISR. + */ +UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * UBaseType_t uxTaskBasePriorityGet( const TaskHandle_t xTask ); + * @endcode + * + * INCLUDE_uxTaskPriorityGet and configUSE_MUTEXES must be defined as 1 for this + * function to be available. See the configuration section for more information. + * + * Obtain the base priority of any task. + * + * @param xTask Handle of the task to be queried. Passing a NULL + * handle results in the base priority of the calling task being returned. + * + * @return The base priority of xTask. + * + * \defgroup uxTaskPriorityGet uxTaskBasePriorityGet + * \ingroup TaskCtrl + */ +UBaseType_t uxTaskBasePriorityGet( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * UBaseType_t uxTaskBasePriorityGetFromISR( const TaskHandle_t xTask ); + * @endcode + * + * A version of uxTaskBasePriorityGet() that can be used from an ISR. + */ +UBaseType_t uxTaskBasePriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * eTaskState eTaskGetState( TaskHandle_t xTask ); + * @endcode + * + * INCLUDE_eTaskGetState must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the state of any task. States are encoded by the eTaskState + * enumerated type. + * + * @param xTask Handle of the task to be queried. + * + * @return The state of xTask at the time the function was called. Note the + * state of the task might change between the function being called, and the + * functions return value being tested by the calling task. + */ +#if ( ( INCLUDE_eTaskGetState == 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_xTaskAbortDelay == 1 ) ) + eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ); + * @endcode + * + * configUSE_TRACE_FACILITY must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * Populates a TaskStatus_t structure with information about a task. + * + * @param xTask Handle of the task being queried. If xTask is NULL then + * information will be returned about the calling task. + * + * @param pxTaskStatus A pointer to the TaskStatus_t structure that will be + * filled with information about the task referenced by the handle passed using + * the xTask parameter. + * + * @param xGetFreeStackSpace The TaskStatus_t structure contains a member to report + * the stack high water mark of the task being queried. Calculating the stack + * high water mark takes a relatively long time, and can make the system + * temporarily unresponsive - so the xGetFreeStackSpace parameter is provided to + * allow the high water mark checking to be skipped. The high watermark value + * will only be written to the TaskStatus_t structure if xGetFreeStackSpace is + * not set to pdFALSE; + * + * @param eState The TaskStatus_t structure contains a member to report the + * state of the task being queried. Obtaining the task state is not as fast as + * a simple assignment - so the eState parameter is provided to allow the state + * information to be omitted from the TaskStatus_t structure. To obtain state + * information then set eState to eInvalid - otherwise the value passed in + * eState will be reported as the task state in the TaskStatus_t structure. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * TaskStatus_t xTaskDetails; + * + * // Obtain the handle of a task from its name. + * xHandle = xTaskGetHandle( "Task_Name" ); + * + * // Check the handle is not NULL. + * configASSERT( xHandle ); + * + * // Use the handle to obtain further information about the task. + * vTaskGetInfo( xHandle, + * &xTaskDetails, + * pdTRUE, // Include the high water mark in xTaskDetails. + * eInvalid ); // Include the task state in xTaskDetails. + * } + * @endcode + * \defgroup vTaskGetInfo vTaskGetInfo + * \ingroup TaskCtrl + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + void vTaskGetInfo( TaskHandle_t xTask, + TaskStatus_t * pxTaskStatus, + BaseType_t xGetFreeStackSpace, + eTaskState eState ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ); + * @endcode + * + * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Set the priority of any task. + * + * A context switch will occur before the function returns if the priority + * being set is higher than the currently executing task. + * + * @param xTask Handle to the task for which the priority is being set. + * Passing a NULL handle results in the priority of the calling task being set. + * + * @param uxNewPriority The priority to which the task will be set. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to raise the priority of the created task. + * vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 ); + * + * // ... + * + * // Use a NULL handle to raise our priority to the same value. + * vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 ); + * } + * @endcode + * \defgroup vTaskPrioritySet vTaskPrioritySet + * \ingroup TaskCtrl + */ +void vTaskPrioritySet( TaskHandle_t xTask, + UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskSuspend( TaskHandle_t xTaskToSuspend ); + * @endcode + * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Suspend any task. When suspended a task will never get any microcontroller + * processing time, no matter what its priority. + * + * Calls to vTaskSuspend are not accumulative - + * i.e. calling vTaskSuspend () twice on the same task still only requires one + * call to vTaskResume () to ready the suspended task. + * + * @param xTaskToSuspend Handle to the task being suspended. Passing a NULL + * handle will cause the calling task to be suspended. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to suspend the created task. + * vTaskSuspend( xHandle ); + * + * // ... + * + * // The created task will not run during this period, unless + * // another task calls vTaskResume( xHandle ). + * + * //... + * + * + * // Suspend ourselves. + * vTaskSuspend( NULL ); + * + * // We cannot get here unless another task calls vTaskResume + * // with our handle as the parameter. + * } + * @endcode + * \defgroup vTaskSuspend vTaskSuspend + * \ingroup TaskCtrl + */ +void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskResume( TaskHandle_t xTaskToResume ); + * @endcode + * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Resumes a suspended task. + * + * A task that has been suspended by one or more calls to vTaskSuspend () + * will be made available for running again by a single call to + * vTaskResume (). + * + * @param xTaskToResume Handle to the task being readied. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle ); + * + * // ... + * + * // Use the handle to suspend the created task. + * vTaskSuspend( xHandle ); + * + * // ... + * + * // The created task will not run during this period, unless + * // another task calls vTaskResume( xHandle ). + * + * //... + * + * + * // Resume the suspended task ourselves. + * vTaskResume( xHandle ); + * + * // The created task will once again get microcontroller processing + * // time in accordance with its priority within the system. + * } + * @endcode + * \defgroup vTaskResume vTaskResume + * \ingroup TaskCtrl + */ +void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void xTaskResumeFromISR( TaskHandle_t xTaskToResume ); + * @endcode + * + * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * An implementation of vTaskResume() that can be called from within an ISR. + * + * A task that has been suspended by one or more calls to vTaskSuspend () + * will be made available for running again by a single call to + * xTaskResumeFromISR (). + * + * xTaskResumeFromISR() should not be used to synchronise a task with an + * interrupt if there is a chance that the interrupt could arrive prior to the + * task being suspended - as this can lead to interrupts being missed. Use of a + * semaphore as a synchronisation mechanism would avoid this eventuality. + * + * @param xTaskToResume Handle to the task being readied. + * + * @return pdTRUE if resuming the task should result in a context switch, + * otherwise pdFALSE. This is used by the ISR to determine if a context switch + * may be required following the ISR. + * + * \defgroup vTaskResumeFromISR vTaskResumeFromISR + * \ingroup TaskCtrl + */ +BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; + +#if ( configUSE_CORE_AFFINITY == 1 ) + +/** + * @brief Sets the core affinity mask for a task. + * + * It sets the cores on which a task can run. configUSE_CORE_AFFINITY must + * be defined as 1 for this function to be available. + * + * @param xTask The handle of the task to set the core affinity mask for. + * Passing NULL will set the core affinity mask for the calling task. + * + * @param uxCoreAffinityMask A bitwise value that indicates the cores on + * which the task can run. Cores are numbered from 0 to configNUMBER_OF_CORES - 1. + * For example, to ensure that a task can run on core 0 and core 1, set + * uxCoreAffinityMask to 0x03. + * + * Example usage: + * + * // The function that creates task. + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * UBaseType_t uxCoreAffinityMask; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &( xHandle ) ); + * + * // Define the core affinity mask such that this task can only run + * // on core 0 and core 2. + * uxCoreAffinityMask = ( ( 1 << 0 ) | ( 1 << 2 ) ); + * + * //Set the core affinity mask for the task. + * vTaskCoreAffinitySet( xHandle, uxCoreAffinityMask ); + * } + */ + void vTaskCoreAffinitySet( const TaskHandle_t xTask, + UBaseType_t uxCoreAffinityMask ); +#endif + +#if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + +/** + * @brief Gets the core affinity mask for a task. + * + * configUSE_CORE_AFFINITY must be defined as 1 for this function to be + * available. + * + * @param xTask The handle of the task to get the core affinity mask for. + * Passing NULL will get the core affinity mask for the calling task. + * + * @return The core affinity mask which is a bitwise value that indicates + * the cores on which a task can run. Cores are numbered from 0 to + * configNUMBER_OF_CORES - 1. For example, if a task can run on core 0 and core 1, + * the core affinity mask is 0x03. + * + * Example usage: + * + * // Task handle of the networking task - it is populated elsewhere. + * TaskHandle_t xNetworkingTaskHandle; + * + * void vAFunction( void ) + * { + * TaskHandle_t xHandle; + * UBaseType_t uxNetworkingCoreAffinityMask; + * + * // Create a task, storing the handle. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &( xHandle ) ); + * + * //Get the core affinity mask for the networking task. + * uxNetworkingCoreAffinityMask = vTaskCoreAffinityGet( xNetworkingTaskHandle ); + * + * // Here is a hypothetical scenario, just for the example. Assume that we + * // have 2 cores - Core 0 and core 1. We want to pin the application task to + * // the core different than the networking task to ensure that the + * // application task does not interfere with networking. + * if( ( uxNetworkingCoreAffinityMask & ( 1 << 0 ) ) != 0 ) + * { + * // The networking task can run on core 0, pin our task to core 1. + * vTaskCoreAffinitySet( xHandle, ( 1 << 1 ) ); + * } + * else + * { + * // Otherwise, pin our task to core 0. + * vTaskCoreAffinitySet( xHandle, ( 1 << 0 ) ); + * } + * } + */ + UBaseType_t vTaskCoreAffinityGet( ConstTaskHandle_t xTask ); +#endif + +#if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + +/** + * @brief Disables preemption for a task. + * + * @param xTask The handle of the task to disable preemption. Passing NULL + * disables preemption for the calling task. + * + * Example usage: + * + * void vTaskCode( void *pvParameters ) + * { + * // Silence warnings about unused parameters. + * ( void ) pvParameters; + * + * for( ;; ) + * { + * // ... Perform some function here. + * + * // Disable preemption for this task. + * vTaskPreemptionDisable( NULL ); + * + * // The task will not be preempted when it is executing in this portion ... + * + * // ... until the preemption is enabled again. + * vTaskPreemptionEnable( NULL ); + * + * // The task can be preempted when it is executing in this portion. + * } + * } + */ + void vTaskPreemptionDisable( const TaskHandle_t xTask ); +#endif + +#if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + +/** + * @brief Enables preemption for a task. + * + * @param xTask The handle of the task to enable preemption. Passing NULL + * enables preemption for the calling task. + * + * Example usage: + * + * void vTaskCode( void *pvParameters ) + * { + * // Silence warnings about unused parameters. + * ( void ) pvParameters; + * + * for( ;; ) + * { + * // ... Perform some function here. + * + * // Disable preemption for this task. + * vTaskPreemptionDisable( NULL ); + * + * // The task will not be preempted when it is executing in this portion ... + * + * // ... until the preemption is enabled again. + * vTaskPreemptionEnable( NULL ); + * + * // The task can be preempted when it is executing in this portion. + * } + * } + */ + void vTaskPreemptionEnable( const TaskHandle_t xTask ); +#endif + +/*----------------------------------------------------------- +* SCHEDULER CONTROL +*----------------------------------------------------------*/ + +/** + * task. h + * @code{c} + * void vTaskStartScheduler( void ); + * @endcode + * + * Starts the real time kernel tick processing. After calling the kernel + * has control over which tasks are executed and when. + * + * See the demo application file main.c for an example of creating + * tasks and starting the kernel. + * + * Example usage: + * @code{c} + * void vAFunction( void ) + * { + * // Create at least one task before starting the kernel. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + * + * // Start the real time kernel with preemption. + * vTaskStartScheduler (); + * + * // Will not get here unless a task calls vTaskEndScheduler () + * } + * @endcode + * + * \defgroup vTaskStartScheduler vTaskStartScheduler + * \ingroup SchedulerControl + */ +void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskEndScheduler( void ); + * @endcode + * + * NOTE: At the time of writing only the x86 real mode port, which runs on a PC + * in place of DOS, implements this function. + * + * Stops the real time kernel tick. All created tasks will be automatically + * deleted and multitasking (either preemptive or cooperative) will + * stop. Execution then resumes from the point where vTaskStartScheduler () + * was called, as if vTaskStartScheduler () had just returned. + * + * See the demo application file main. c in the demo/PC directory for an + * example that uses vTaskEndScheduler (). + * + * vTaskEndScheduler () requires an exit function to be defined within the + * portable layer (see vPortEndScheduler () in port. c for the PC port). This + * performs hardware specific operations such as stopping the kernel tick. + * + * vTaskEndScheduler () will cause all of the resources allocated by the + * kernel to be freed - but will not free resources allocated by application + * tasks. + * + * Example usage: + * @code{c} + * void vTaskCode( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * + * // At some point we want to end the real time kernel processing + * // so call ... + * vTaskEndScheduler (); + * } + * } + * + * void vAFunction( void ) + * { + * // Create at least one task before starting the kernel. + * xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + * + * // Start the real time kernel with preemption. + * vTaskStartScheduler (); + * + * // Will only get here when the vTaskCode () task has called + * // vTaskEndScheduler (). When we get here we are back to single task + * // execution. + * } + * @endcode + * + * \defgroup vTaskEndScheduler vTaskEndScheduler + * \ingroup SchedulerControl + */ +void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * void vTaskSuspendAll( void ); + * @endcode + * + * Suspends the scheduler without disabling interrupts. Context switches will + * not occur while the scheduler is suspended. + * + * After calling vTaskSuspendAll () the calling task will continue to execute + * without risk of being swapped out until a call to xTaskResumeAll () has been + * made. + * + * API functions that have the potential to cause a context switch (for example, + * xTaskDelayUntil(), xQueueSend(), etc.) must not be called while the scheduler + * is suspended. + * + * Example usage: + * @code{c} + * void vTask1( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * + * // ... + * + * // At some point the task wants to perform a long operation during + * // which it does not want to get swapped out. It cannot use + * // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the + * // operation may cause interrupts to be missed - including the + * // ticks. + * + * // Prevent the real time kernel swapping out the task. + * vTaskSuspendAll (); + * + * // Perform the operation here. There is no need to use critical + * // sections as we have all the microcontroller processing time. + * // During this time interrupts will still operate and the kernel + * // tick count will be maintained. + * + * // ... + * + * // The operation is complete. Restart the kernel. + * xTaskResumeAll (); + * } + * } + * @endcode + * \defgroup vTaskSuspendAll vTaskSuspendAll + * \ingroup SchedulerControl + */ +void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * BaseType_t xTaskResumeAll( void ); + * @endcode + * + * Resumes scheduler activity after it was suspended by a call to + * vTaskSuspendAll(). + * + * xTaskResumeAll() only resumes the scheduler. It does not unsuspend tasks + * that were previously suspended by a call to vTaskSuspend(). + * + * @return If resuming the scheduler caused a context switch then pdTRUE is + * returned, otherwise pdFALSE is returned. + * + * Example usage: + * @code{c} + * void vTask1( void * pvParameters ) + * { + * for( ;; ) + * { + * // Task code goes here. + * + * // ... + * + * // At some point the task wants to perform a long operation during + * // which it does not want to get swapped out. It cannot use + * // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the + * // operation may cause interrupts to be missed - including the + * // ticks. + * + * // Prevent the real time kernel swapping out the task. + * vTaskSuspendAll (); + * + * // Perform the operation here. There is no need to use critical + * // sections as we have all the microcontroller processing time. + * // During this time interrupts will still operate and the real + * // time kernel tick count will be maintained. + * + * // ... + * + * // The operation is complete. Restart the kernel. We want to force + * // a context switch - but there is no point if resuming the scheduler + * // caused a context switch already. + * if( !xTaskResumeAll () ) + * { + * taskYIELD (); + * } + * } + * } + * @endcode + * \defgroup xTaskResumeAll xTaskResumeAll + * \ingroup SchedulerControl + */ +BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- +* TASK UTILITIES +*----------------------------------------------------------*/ + +/** + * task. h + * @code{c} + * TickType_t xTaskGetTickCount( void ); + * @endcode + * + * @return The count of ticks since vTaskStartScheduler was called. + * + * \defgroup xTaskGetTickCount xTaskGetTickCount + * \ingroup TaskUtils + */ +TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * TickType_t xTaskGetTickCountFromISR( void ); + * @endcode + * + * @return The count of ticks since vTaskStartScheduler was called. + * + * This is a version of xTaskGetTickCount() that is safe to be called from an + * ISR - provided that TickType_t is the natural word size of the + * microcontroller being used or interrupt nesting is either not supported or + * not being used. + * + * \defgroup xTaskGetTickCountFromISR xTaskGetTickCountFromISR + * \ingroup TaskUtils + */ +TickType_t xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * uint16_t uxTaskGetNumberOfTasks( void ); + * @endcode + * + * @return The number of tasks that the real time kernel is currently managing. + * This includes all ready, blocked and suspended tasks. A task that + * has been deleted but not yet freed by the idle task will also be + * included in the count. + * + * \defgroup uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks + * \ingroup TaskUtils + */ +UBaseType_t uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * char *pcTaskGetName( TaskHandle_t xTaskToQuery ); + * @endcode + * + * @return The text (human readable) name of the task referenced by the handle + * xTaskToQuery. A task can query its own name by either passing in its own + * handle, or by setting xTaskToQuery to NULL. + * + * \defgroup pcTaskGetName pcTaskGetName + * \ingroup TaskUtils + */ +char * pcTaskGetName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; + +/** + * task. h + * @code{c} + * TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ); + * @endcode + * + * NOTE: This function takes a relatively long time to complete and should be + * used sparingly. + * + * @return The handle of the task that has the human readable name pcNameToQuery. + * NULL is returned if no matching name is found. INCLUDE_xTaskGetHandle + * must be set to 1 in FreeRTOSConfig.h for pcTaskGetHandle() to be available. + * + * \defgroup pcTaskGetHandle pcTaskGetHandle + * \ingroup TaskUtils + */ +#if ( INCLUDE_xTaskGetHandle == 1 ) + TaskHandle_t xTaskGetHandle( const char * pcNameToQuery ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * BaseType_t xTaskGetStaticBuffers( TaskHandle_t xTask, + * StackType_t ** ppuxStackBuffer, + * StaticTask_t ** ppxTaskBuffer ); + * @endcode + * + * Retrieve pointers to a statically created task's data structure + * buffer and stack buffer. These are the same buffers that are supplied + * at the time of creation. + * + * @param xTask The task for which to retrieve the buffers. + * + * @param ppuxStackBuffer Used to return a pointer to the task's stack buffer. + * + * @param ppxTaskBuffer Used to return a pointer to the task's data structure + * buffer. + * + * @return pdTRUE if buffers were retrieved, pdFALSE otherwise. + * + * \defgroup xTaskGetStaticBuffers xTaskGetStaticBuffers + * \ingroup TaskUtils + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + BaseType_t xTaskGetStaticBuffers( TaskHandle_t xTask, + StackType_t ** ppuxStackBuffer, + StaticTask_t ** ppxTaskBuffer ) PRIVILEGED_FUNCTION; +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * task.h + * @code{c} + * UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ); + * @endcode + * + * INCLUDE_uxTaskGetStackHighWaterMark must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in words, so + * actual spaces on the stack rather than bytes) since the task referenced by + * xTask was created. + */ +#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) + UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task.h + * @code{c} + * configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ); + * @endcode + * + * INCLUDE_uxTaskGetStackHighWaterMark2 must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in words, so + * actual spaces on the stack rather than bytes) since the task referenced by + * xTask was created. + */ +#if ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) + configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; +#endif + +/* When using trace macros it is sometimes necessary to include task.h before + * FreeRTOS.h. When this is done TaskHookFunction_t will not yet have been defined, + * so the following two prototypes will cause a compilation error. This can be + * fixed by simply guarding against the inclusion of these two prototypes unless + * they are explicitly required by the configUSE_APPLICATION_TASK_TAG configuration + * constant. */ +#ifdef configUSE_APPLICATION_TASK_TAG + #if configUSE_APPLICATION_TASK_TAG == 1 + +/** + * task.h + * @code{c} + * void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ); + * @endcode + * + * Sets pxHookFunction to be the task hook function used by the task xTask. + * Passing xTask as NULL has the effect of setting the calling tasks hook + * function. + */ + void vTaskSetApplicationTaskTag( TaskHandle_t xTask, + TaskHookFunction_t pxHookFunction ) PRIVILEGED_FUNCTION; + +/** + * task.h + * @code{c} + * void xTaskGetApplicationTaskTag( TaskHandle_t xTask ); + * @endcode + * + * Returns the pxHookFunction value assigned to the task xTask. Do not + * call from an interrupt service routine - call + * xTaskGetApplicationTaskTagFromISR() instead. + */ + TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task.h + * @code{c} + * void xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ); + * @endcode + * + * Returns the pxHookFunction value assigned to the task xTask. Can + * be called from an interrupt service routine. + */ + TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + #endif /* configUSE_APPLICATION_TASK_TAG ==1 */ +#endif /* ifdef configUSE_APPLICATION_TASK_TAG */ + +#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + +/* Each task contains an array of pointers that is dimensioned by the + * configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. The + * kernel does not use the pointers itself, so the application writer can use + * the pointers for any purpose they wish. The following two functions are + * used to set and query a pointer respectively. */ + void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, + BaseType_t xIndex, + void * pvValue ) PRIVILEGED_FUNCTION; + void * pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, + BaseType_t xIndex ) PRIVILEGED_FUNCTION; + +#endif + +#if ( configCHECK_FOR_STACK_OVERFLOW > 0 ) + +/** + * task.h + * @code{c} + * void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName); + * @endcode + * + * The application stack overflow hook is called when a stack overflow is detected for a task. + * + * Details on stack overflow detection can be found here: https://www.FreeRTOS.org/Stacks-and-stack-overflow-checking.html + * + * @param xTask the task that just exceeded its stack boundaries. + * @param pcTaskName A character string containing the name of the offending task. + */ + /* MISRA Ref 8.6.1 [External linkage] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-86 */ + /* coverity[misra_c_2012_rule_8_6_violation] */ + void vApplicationStackOverflowHook( TaskHandle_t xTask, + char * pcTaskName ); + +#endif + +#if ( configUSE_IDLE_HOOK == 1 ) + +/** + * task.h + * @code{c} + * void vApplicationIdleHook( void ); + * @endcode + * + * The application idle hook is called by the idle task. + * This allows the application designer to add background functionality without + * the overhead of a separate task. + * NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, CALL A FUNCTION THAT MIGHT BLOCK. + */ + /* MISRA Ref 8.6.1 [External linkage] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-86 */ + /* coverity[misra_c_2012_rule_8_6_violation] */ + void vApplicationIdleHook( void ); + +#endif + + +#if ( configUSE_TICK_HOOK != 0 ) + +/** + * task.h + * @code{c} + * void vApplicationTickHook( void ); + * @endcode + * + * This hook function is called in the system tick handler after any OS work is completed. + */ + /* MISRA Ref 8.6.1 [External linkage] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-86 */ + /* coverity[misra_c_2012_rule_8_6_violation] */ + void vApplicationTickHook( void ); + +#endif + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + +/** + * task.h + * @code{c} + * void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, StackType_t ** ppxIdleTaskStackBuffer, configSTACK_DEPTH_TYPE * puxIdleTaskStackSize ) + * @endcode + * + * This function is used to provide a statically allocated block of memory to FreeRTOS to hold the Idle Task TCB. This function is required when + * configSUPPORT_STATIC_ALLOCATION is set. For more information see this URI: https://www.FreeRTOS.org/a00110.html#configSUPPORT_STATIC_ALLOCATION + * + * @param ppxIdleTaskTCBBuffer A handle to a statically allocated TCB buffer + * @param ppxIdleTaskStackBuffer A handle to a statically allocated Stack buffer for the idle task + * @param puxIdleTaskStackSize A pointer to the number of elements that will fit in the allocated stack buffer + */ + void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, + StackType_t ** ppxIdleTaskStackBuffer, + configSTACK_DEPTH_TYPE * puxIdleTaskStackSize ); + +/** + * task.h + * @code{c} + * void vApplicationGetPassiveIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, StackType_t ** ppxIdleTaskStackBuffer, configSTACK_DEPTH_TYPE * puxIdleTaskStackSize, BaseType_t xCoreID ) + * @endcode + * + * This function is used to provide a statically allocated block of memory to FreeRTOS to hold the Idle Tasks TCB. This function is required when + * configSUPPORT_STATIC_ALLOCATION is set. For more information see this URI: https://www.FreeRTOS.org/a00110.html#configSUPPORT_STATIC_ALLOCATION + * + * In the FreeRTOS SMP, there are a total of configNUMBER_OF_CORES idle tasks: + * 1. 1 Active idle task which does all the housekeeping. + * 2. ( configNUMBER_OF_CORES - 1 ) Passive idle tasks which do nothing. + * These idle tasks are created to ensure that each core has an idle task to run when + * no other task is available to run. + * + * The function vApplicationGetPassiveIdleTaskMemory is called with passive idle + * task index 0, 1 ... ( configNUMBER_OF_CORES - 2 ) to get memory for passive idle + * tasks. + * + * @param ppxIdleTaskTCBBuffer A handle to a statically allocated TCB buffer + * @param ppxIdleTaskStackBuffer A handle to a statically allocated Stack buffer for the idle task + * @param puxIdleTaskStackSize A pointer to the number of elements that will fit in the allocated stack buffer + * @param xPassiveIdleTaskIndex The passive idle task index of the idle task buffer + */ + #if ( configNUMBER_OF_CORES > 1 ) + void vApplicationGetPassiveIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, + StackType_t ** ppxIdleTaskStackBuffer, + configSTACK_DEPTH_TYPE * puxIdleTaskStackSize, + BaseType_t xPassiveIdleTaskIndex ); + #endif /* #if ( configNUMBER_OF_CORES > 1 ) */ +#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + +/** + * task.h + * @code{c} + * BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ); + * @endcode + * + * Calls the hook function associated with xTask. Passing xTask as NULL has + * the effect of calling the Running tasks (the calling task) hook function. + * + * pvParameter is passed to the hook function for the task to interpret as it + * wants. The return value is the value returned by the task hook function + * registered by the user. + */ +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, + void * pvParameter ) PRIVILEGED_FUNCTION; +#endif + +/** + * xTaskGetIdleTaskHandle() is only available if + * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h. + * + * In single-core FreeRTOS, this function simply returns the handle of the idle + * task. It is not valid to call xTaskGetIdleTaskHandle() before the scheduler + * has been started. + * + * In the FreeRTOS SMP, there are a total of configNUMBER_OF_CORES idle tasks: + * 1. 1 Active idle task which does all the housekeeping. + * 2. ( configNUMBER_OF_CORES - 1 ) Passive idle tasks which do nothing. + * These idle tasks are created to ensure that each core has an idle task to run when + * no other task is available to run. Call xTaskGetIdleTaskHandle() or + * xTaskGetIdleTaskHandleForCore() with xCoreID set to 0 to get the Active + * idle task handle. Call xTaskGetIdleTaskHandleForCore() with xCoreID set to + * 1,2 ... ( configNUMBER_OF_CORES - 1 ) to get the Passive idle task handles. + */ +#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) + #if ( configNUMBER_OF_CORES == 1 ) + TaskHandle_t xTaskGetIdleTaskHandle( void ) PRIVILEGED_FUNCTION; + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + + TaskHandle_t xTaskGetIdleTaskHandleForCore( BaseType_t xCoreID ) PRIVILEGED_FUNCTION; +#endif /* #if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) */ + +/** + * configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for + * uxTaskGetSystemState() to be available. + * + * uxTaskGetSystemState() populates an TaskStatus_t structure for each task in + * the system. TaskStatus_t structures contain, among other things, members + * for the task handle, task name, task priority, task state, and total amount + * of run time consumed by the task. See the TaskStatus_t structure + * definition in this file for the full member list. + * + * NOTE: This function is intended for debugging use only as its use results in + * the scheduler remaining suspended for an extended period. + * + * @param pxTaskStatusArray A pointer to an array of TaskStatus_t structures. + * The array must contain at least one TaskStatus_t structure for each task + * that is under the control of the RTOS. The number of tasks under the control + * of the RTOS can be determined using the uxTaskGetNumberOfTasks() API function. + * + * @param uxArraySize The size of the array pointed to by the pxTaskStatusArray + * parameter. The size is specified as the number of indexes in the array, or + * the number of TaskStatus_t structures contained in the array, not by the + * number of bytes in the array. + * + * @param pulTotalRunTime If configGENERATE_RUN_TIME_STATS is set to 1 in + * FreeRTOSConfig.h then *pulTotalRunTime is set by uxTaskGetSystemState() to the + * total run time (as defined by the run time stats clock, see + * https://www.FreeRTOS.org/rtos-run-time-stats.html) since the target booted. + * pulTotalRunTime can be set to NULL to omit the total run time information. + * + * @return The number of TaskStatus_t structures that were populated by + * uxTaskGetSystemState(). This should equal the number returned by the + * uxTaskGetNumberOfTasks() API function, but will be zero if the value passed + * in the uxArraySize parameter was too small. + * + * Example usage: + * @code{c} + * // This example demonstrates how a human readable table of run time stats + * // information is generated from raw data provided by uxTaskGetSystemState(). + * // The human readable table is written to pcWriteBuffer + * void vTaskGetRunTimeStats( char *pcWriteBuffer ) + * { + * TaskStatus_t *pxTaskStatusArray; + * volatile UBaseType_t uxArraySize, x; + * configRUN_TIME_COUNTER_TYPE ulTotalRunTime, ulStatsAsPercentage; + * + * // Make sure the write buffer does not contain a string. + * pcWriteBuffer = 0x00; + * + * // Take a snapshot of the number of tasks in case it changes while this + * // function is executing. + * uxArraySize = uxTaskGetNumberOfTasks(); + * + * // Allocate a TaskStatus_t structure for each task. An array could be + * // allocated statically at compile time. + * pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) ); + * + * if( pxTaskStatusArray != NULL ) + * { + * // Generate raw status information about each task. + * uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalRunTime ); + * + * // For percentage calculations. + * ulTotalRunTime /= 100U; + * + * // Avoid divide by zero errors. + * if( ulTotalRunTime > 0 ) + * { + * // For each populated position in the pxTaskStatusArray array, + * // format the raw data as human readable ASCII data + * for( x = 0; x < uxArraySize; x++ ) + * { + * // What percentage of the total run time has the task used? + * // This will always be rounded down to the nearest integer. + * // ulTotalRunTimeDiv100 has already been divided by 100. + * ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalRunTime; + * + * if( ulStatsAsPercentage > 0U ) + * { + * sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage ); + * } + * else + * { + * // If the percentage is zero here then the task has + * // consumed less than 1% of the total run time. + * sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter ); + * } + * + * pcWriteBuffer += strlen( ( char * ) pcWriteBuffer ); + * } + * } + * + * // The array is no longer needed, free the memory it consumes. + * vPortFree( pxTaskStatusArray ); + * } + * } + * @endcode + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, + const UBaseType_t uxArraySize, + configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * void vTaskListTasks( char *pcWriteBuffer, size_t uxBufferLength ); + * @endcode + * + * configUSE_TRACE_FACILITY and configUSE_STATS_FORMATTING_FUNCTIONS must + * both be defined as 1 for this function to be available. See the + * configuration section of the FreeRTOS.org website for more information. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Lists all the current tasks, along with their current state and stack + * usage high water mark. + * + * Tasks are reported as running ('X'), blocked ('B'), ready ('R'), deleted ('D') + * or suspended ('S'). + * + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many of the + * demo applications. Do not consider it to be part of the scheduler. + * + * vTaskListTasks() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that displays task + * information in the following format: + * Task Name, Task State, Task Priority, Task Stack High Watermak, Task Number. + * + * The following is a sample output: + * Task A X 2 67 2 + * Task B R 1 67 3 + * IDLE R 0 67 5 + * Tmr Svc B 6 137 6 + * + * Stack usage specified as the number of unused StackType_t words stack can hold + * on top of stack - not the number of bytes. + * + * vTaskListTasks() has a dependency on the snprintf() C library function that might + * bloat the code size, use a lot of stack, and provide different results on + * different platforms. An alternative, tiny, third party, and limited + * functionality implementation of snprintf() is provided in many of the + * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note + * printf-stdarg.c does not provide a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly through a + * call to vTaskListTasks(). + * + * @param pcWriteBuffer A buffer into which the above mentioned details + * will be written, in ASCII form. This buffer is assumed to be large + * enough to contain the generated report. Approximately 40 bytes per + * task should be sufficient. + * + * @param uxBufferLength Length of the pcWriteBuffer. + * + * \defgroup vTaskListTasks vTaskListTasks + * \ingroup TaskUtils + */ +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) + void vTaskListTasks( char * pcWriteBuffer, + size_t uxBufferLength ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * void vTaskList( char *pcWriteBuffer ); + * @endcode + * + * configUSE_TRACE_FACILITY and configUSE_STATS_FORMATTING_FUNCTIONS must + * both be defined as 1 for this function to be available. See the + * configuration section of the FreeRTOS.org website for more information. + * + * WARN: This function assumes that the pcWriteBuffer is of length + * configSTATS_BUFFER_MAX_LENGTH. This function is there only for + * backward compatibility. New applications are recommended to + * use vTaskListTasks and supply the length of the pcWriteBuffer explicitly. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Lists all the current tasks, along with their current state and stack + * usage high water mark. + * + * Tasks are reported as running ('X'), blocked ('B'), ready ('R'), deleted ('D') + * or suspended ('S'). + * + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many of the + * demo applications. Do not consider it to be part of the scheduler. + * + * vTaskList() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that displays task + * information in the following format: + * Task Name, Task State, Task Priority, Task Stack High Watermak, Task Number. + * + * The following is a sample output: + * Task A X 2 67 2 + * Task B R 1 67 3 + * IDLE R 0 67 5 + * Tmr Svc B 6 137 6 + * + * Stack usage specified as the number of unused StackType_t words stack can hold + * on top of stack - not the number of bytes. + * + * vTaskList() has a dependency on the snprintf() C library function that might + * bloat the code size, use a lot of stack, and provide different results on + * different platforms. An alternative, tiny, third party, and limited + * functionality implementation of snprintf() is provided in many of the + * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note + * printf-stdarg.c does not provide a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly through a + * call to vTaskList(). + * + * @param pcWriteBuffer A buffer into which the above mentioned details + * will be written, in ASCII form. This buffer is assumed to be large + * enough to contain the generated report. Approximately 40 bytes per + * task should be sufficient. + * + * \defgroup vTaskList vTaskList + * \ingroup TaskUtils + */ +#define vTaskList( pcWriteBuffer ) vTaskListTasks( ( pcWriteBuffer ), configSTATS_BUFFER_MAX_LENGTH ) + +/** + * task. h + * @code{c} + * void vTaskGetRunTimeStatistics( char *pcWriteBuffer, size_t uxBufferLength ); + * @endcode + * + * configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS + * must both be defined as 1 for this function to be available. The application + * must also then provide definitions for + * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() + * to configure a peripheral timer/counter and return the timers current count + * value respectively. The counter should be at least 10 times the frequency of + * the tick count. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * Calling vTaskGetRunTimeStatistics() writes the total execution time of each + * task into a buffer, both as an absolute count value and as a percentage + * of the total system execution time. + * + * NOTE 2: + * + * This function is provided for convenience only, and is used by many of the + * demo applications. Do not consider it to be part of the scheduler. + * + * vTaskGetRunTimeStatistics() calls uxTaskGetSystemState(), then formats part of + * the uxTaskGetSystemState() output into a human readable table that displays the + * amount of time each task has spent in the Running state in both absolute and + * percentage terms. + * + * vTaskGetRunTimeStatistics() has a dependency on the snprintf() C library function + * that might bloat the code size, use a lot of stack, and provide different + * results on different platforms. An alternative, tiny, third party, and + * limited functionality implementation of snprintf() is provided in many of the + * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note + * printf-stdarg.c does not provide a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() directly + * to get access to raw stats data, rather than indirectly through a call to + * vTaskGetRunTimeStatistics(). + * + * @param pcWriteBuffer A buffer into which the execution times will be + * written, in ASCII form. This buffer is assumed to be large enough to + * contain the generated report. Approximately 40 bytes per task should + * be sufficient. + * + * @param uxBufferLength Length of the pcWriteBuffer. + * + * \defgroup vTaskGetRunTimeStatistics vTaskGetRunTimeStatistics + * \ingroup TaskUtils + */ +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configUSE_TRACE_FACILITY == 1 ) ) + void vTaskGetRunTimeStatistics( char * pcWriteBuffer, + size_t uxBufferLength ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * void vTaskGetRunTimeStats( char *pcWriteBuffer ); + * @endcode + * + * configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS + * must both be defined as 1 for this function to be available. The application + * must also then provide definitions for + * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() + * to configure a peripheral timer/counter and return the timers current count + * value respectively. The counter should be at least 10 times the frequency of + * the tick count. + * + * WARN: This function assumes that the pcWriteBuffer is of length + * configSTATS_BUFFER_MAX_LENGTH. This function is there only for + * backward compatibility. New applications are recommended to use + * vTaskGetRunTimeStatistics and supply the length of the pcWriteBuffer + * explicitly. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * Calling vTaskGetRunTimeStats() writes the total execution time of each + * task into a buffer, both as an absolute count value and as a percentage + * of the total system execution time. + * + * NOTE 2: + * + * This function is provided for convenience only, and is used by many of the + * demo applications. Do not consider it to be part of the scheduler. + * + * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that displays the + * amount of time each task has spent in the Running state in both absolute and + * percentage terms. + * + * vTaskGetRunTimeStats() has a dependency on the snprintf() C library function + * that might bloat the code size, use a lot of stack, and provide different + * results on different platforms. An alternative, tiny, third party, and + * limited functionality implementation of snprintf() is provided in many of the + * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note + * printf-stdarg.c does not provide a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() directly + * to get access to raw stats data, rather than indirectly through a call to + * vTaskGetRunTimeStats(). + * + * @param pcWriteBuffer A buffer into which the execution times will be + * written, in ASCII form. This buffer is assumed to be large enough to + * contain the generated report. Approximately 40 bytes per task should + * be sufficient. + * + * \defgroup vTaskGetRunTimeStats vTaskGetRunTimeStats + * \ingroup TaskUtils + */ +#define vTaskGetRunTimeStats( pcWriteBuffer ) vTaskGetRunTimeStatistics( ( pcWriteBuffer ), configSTATS_BUFFER_MAX_LENGTH ) + +/** + * task. h + * @code{c} + * configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimeCounter( const TaskHandle_t xTask ); + * configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimePercent( const TaskHandle_t xTask ); + * @endcode + * + * configGENERATE_RUN_TIME_STATS must be defined as 1 for these functions to be + * available. The application must also then provide definitions for + * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE() to configure a peripheral timer/counter and + * return the timers current count value respectively. The counter should be + * at least 10 times the frequency of the tick count. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * While uxTaskGetSystemState() and vTaskGetRunTimeStats() writes the total + * execution time of each task into a buffer, ulTaskGetRunTimeCounter() + * returns the total execution time of just one task and + * ulTaskGetRunTimePercent() returns the percentage of the CPU time used by + * just one task. + * + * @return The total run time of the given task or the percentage of the total + * run time consumed by the given task. This is the amount of time the task + * has actually been executing. The unit of time is dependent on the frequency + * configured using the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE() macros. + * + * \defgroup ulTaskGetRunTimeCounter ulTaskGetRunTimeCounter + * \ingroup TaskUtils + */ +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimeCounter( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimePercent( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimeCounter( void ); + * configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimePercent( void ); + * @endcode + * + * configGENERATE_RUN_TIME_STATS must be defined as 1 for these functions to be + * available. The application must also then provide definitions for + * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE() to configure a peripheral timer/counter and + * return the timers current count value respectively. The counter should be + * at least 10 times the frequency of the tick count. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * While uxTaskGetSystemState() and vTaskGetRunTimeStats() writes the total + * execution time of each task into a buffer, ulTaskGetIdleRunTimeCounter() + * returns the total execution time of just the idle task and + * ulTaskGetIdleRunTimePercent() returns the percentage of the CPU time used by + * just the idle task. + * + * Note the amount of idle time is only a good measure of the slack time in a + * system if there are no other tasks executing at the idle priority, tickless + * idle is not used, and configIDLE_SHOULD_YIELD is set to 0. + * + * @return The total run time of the idle task or the percentage of the total + * run time consumed by the idle task. This is the amount of time the + * idle task has actually been executing. The unit of time is dependent on the + * frequency configured using the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE() macros. + * + * \defgroup ulTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter + * \ingroup TaskUtils + */ +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) + configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimeCounter( void ) PRIVILEGED_FUNCTION; + configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimePercent( void ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction ); + * BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Sends a direct to task notification to a task, with an optional value and + * action. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * A task can use xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() to + * [optionally] block to wait for a notification to be pending. The task does + * not consume any CPU time while it is in the Blocked state. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() (or their + * un-indexed equivalents). If the task was already in the Blocked state to + * wait for a notification when the notification arrives then the task will + * automatically be removed from the Blocked state (unblocked) and the + * notification cleared. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotify() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling xTaskNotify() is equivalent to calling xTaskNotifyIndexed() + * with the uxIndexToNotify parameter set to 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotify() does + * not have this parameter and always sends notifications to index 0. + * + * @param ulValue Data that can be sent with the notification. How the data is + * used depends on the value of the eAction parameter. + * + * @param eAction Specifies how the notification updates the task's notification + * value, if at all. Valid values for eAction are as follows: + * + * eSetBits - + * The target notification value is bitwise ORed with ulValue. + * xTaskNotifyIndexed() always returns pdPASS in this case. + * + * eIncrement - + * The target notification value is incremented. ulValue is not used and + * xTaskNotifyIndexed() always returns pdPASS in this case. + * + * eSetValueWithOverwrite - + * The target notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification at the + * same array index (the task already had a notification pending at that index). + * xTaskNotifyIndexed() always returns pdPASS in this case. + * + * eSetValueWithoutOverwrite - + * If the task being notified did not already have a notification pending at the + * same array index then the target notification value is set to ulValue and + * xTaskNotifyIndexed() will return pdPASS. If the task being notified already + * had a notification pending at the same array index then no action is + * performed and pdFAIL is returned. + * + * eNoAction - + * The task receives a notification at the specified array index without the + * notification value at that index being updated. ulValue is not used and + * xTaskNotifyIndexed() always returns pdPASS in this case. + * + * @param pulPreviousNotificationValue Can be used to pass out the subject + * task's notification value before any bits are modified by the notify function. + * + * @return Dependent on the value of eAction. See the description of the + * eAction parameter. + * + * \defgroup xTaskNotifyIndexed xTaskNotifyIndexed + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue ) PRIVILEGED_FUNCTION; +#define xTaskNotify( xTaskToNotify, ulValue, eAction ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL ) +#define xTaskNotifyIndexed( xTaskToNotify, uxIndexToNotify, ulValue, eAction ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), NULL ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyAndQueryIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue ); + * BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotifyValue ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * xTaskNotifyAndQueryIndexed() performs the same operation as + * xTaskNotifyIndexed() with the addition that it also returns the subject + * task's prior notification value (the notification value at the time the + * function is called rather than when the function returns) in the additional + * pulPreviousNotifyValue parameter. + * + * xTaskNotifyAndQuery() performs the same operation as xTaskNotify() with the + * addition that it also returns the subject task's prior notification value + * (the notification value as it was at the time the function is called, rather + * than when the function returns) in the additional pulPreviousNotifyValue + * parameter. + * + * \defgroup xTaskNotifyAndQueryIndexed xTaskNotifyAndQueryIndexed + * \ingroup TaskNotifications + */ +#define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) ) +#define xTaskNotifyAndQueryIndexed( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotifyValue ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyIndexedFromISR( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken ); + * BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * A version of xTaskNotifyIndexed() that can be used from an interrupt service + * routine (ISR). + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * A task can use xTaskNotifyWaitIndexed() to [optionally] block to wait for a + * notification to be pending, or ulTaskNotifyTakeIndexed() to [optionally] block + * to wait for a notification value to have a non-zero value. The task does + * not consume any CPU time while it is in the Blocked state. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() (or their + * un-indexed equivalents). If the task was already in the Blocked state to + * wait for a notification when the notification arrives then the task will + * automatically be removed from the Blocked state (unblocked) and the + * notification cleared. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyFromISR() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling xTaskNotifyFromISR() is equivalent to calling + * xTaskNotifyIndexedFromISR() with the uxIndexToNotify parameter set to 0. + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyFromISR() + * does not have this parameter and always sends notifications to index 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param ulValue Data that can be sent with the notification. How the data is + * used depends on the value of the eAction parameter. + * + * @param eAction Specifies how the notification updates the task's notification + * value, if at all. Valid values for eAction are as follows: + * + * eSetBits - + * The task's notification value is bitwise ORed with ulValue. xTaskNotify() + * always returns pdPASS in this case. + * + * eIncrement - + * The task's notification value is incremented. ulValue is not used and + * xTaskNotify() always returns pdPASS in this case. + * + * eSetValueWithOverwrite - + * The task's notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification (the + * task already had a notification pending). xTaskNotify() always returns + * pdPASS in this case. + * + * eSetValueWithoutOverwrite - + * If the task being notified did not already have a notification pending then + * the task's notification value is set to ulValue and xTaskNotify() will + * return pdPASS. If the task being notified already had a notification + * pending then no action is performed and pdFAIL is returned. + * + * eNoAction - + * The task receives a notification without its notification value being + * updated. ulValue is not used and xTaskNotify() always returns pdPASS in + * this case. + * + * @param pulPreviousNotificationValue Can be used to pass out the subject + * task's notification value before any bits are modified by the notify function. + * + * @param pxHigherPriorityTaskWoken xTaskNotifyFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the + * task to which the notification was sent to leave the Blocked state, and the + * unblocked task has a priority higher than the currently running task. If + * xTaskNotifyFromISR() sets this value to pdTRUE then a context switch should + * be requested before the interrupt is exited. How a context switch is + * requested from an ISR is dependent on the port - see the documentation page + * for the port in use. + * + * @return Dependent on the value of eAction. See the description of the + * eAction parameter. + * + * \defgroup xTaskNotifyIndexedFromISR xTaskNotifyIndexedFromISR + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; +#define xTaskNotifyFromISR( xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) ) +#define xTaskNotifyIndexedFromISR( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyAndQueryIndexedFromISR( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ); + * BaseType_t xTaskNotifyAndQueryFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * xTaskNotifyAndQueryIndexedFromISR() performs the same operation as + * xTaskNotifyIndexedFromISR() with the addition that it also returns the + * subject task's prior notification value (the notification value at the time + * the function is called rather than at the time the function returns) in the + * additional pulPreviousNotifyValue parameter. + * + * xTaskNotifyAndQueryFromISR() performs the same operation as + * xTaskNotifyFromISR() with the addition that it also returns the subject + * task's prior notification value (the notification value at the time the + * function is called rather than at the time the function returns) in the + * additional pulPreviousNotifyValue parameter. + * + * \defgroup xTaskNotifyAndQueryIndexedFromISR xTaskNotifyAndQueryIndexedFromISR + * \ingroup TaskNotifications + */ +#define xTaskNotifyAndQueryIndexedFromISR( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) ) +#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) \ + xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyWaitIndexed( UBaseType_t uxIndexToWaitOn, uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ); + * + * BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ); + * @endcode + * + * Waits for a direct to task notification to be pending at a given index within + * an array of direct to task notifications. + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this + * function to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWaitIndexed() or ulTaskNotifyTakeIndexed() (or their + * un-indexed equivalents). If the task was already in the Blocked state to + * wait for a notification when the notification arrives then the task will + * automatically be removed from the Blocked state (unblocked) and the + * notification cleared. + * + * A task can use xTaskNotifyWaitIndexed() to [optionally] block to wait for a + * notification to be pending, or ulTaskNotifyTakeIndexed() to [optionally] block + * to wait for a notification value to have a non-zero value. The task does + * not consume any CPU time while it is in the Blocked state. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyWait() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling xTaskNotifyWait() is equivalent to calling + * xTaskNotifyWaitIndexed() with the uxIndexToWaitOn parameter set to 0. + * + * @param uxIndexToWaitOn The index within the calling task's array of + * notification values on which the calling task will wait for a notification to + * be received. uxIndexToWaitOn must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyWait() does + * not have this parameter and always waits for notifications on index 0. + * + * @param ulBitsToClearOnEntry Bits that are set in ulBitsToClearOnEntry value + * will be cleared in the calling task's notification value before the task + * checks to see if any notifications are pending, and optionally blocks if no + * notifications are pending. Setting ulBitsToClearOnEntry to ULONG_MAX (if + * limits.h is included) or 0xffffffffU (if limits.h is not included) will have + * the effect of resetting the task's notification value to 0. Setting + * ulBitsToClearOnEntry to 0 will leave the task's notification value unchanged. + * + * @param ulBitsToClearOnExit If a notification is pending or received before + * the calling task exits the xTaskNotifyWait() function then the task's + * notification value (see the xTaskNotify() API function) is passed out using + * the pulNotificationValue parameter. Then any bits that are set in + * ulBitsToClearOnExit will be cleared in the task's notification value (note + * *pulNotificationValue is set before any bits are cleared). Setting + * ulBitsToClearOnExit to ULONG_MAX (if limits.h is included) or 0xffffffffUL + * (if limits.h is not included) will have the effect of resetting the task's + * notification value to 0 before the function exits. Setting + * ulBitsToClearOnExit to 0 will leave the task's notification value unchanged + * when the function exits (in which case the value passed out in + * pulNotificationValue will match the task's notification value). + * + * @param pulNotificationValue Used to pass the task's notification value out + * of the function. Note the value passed out will not be effected by the + * clearing of any bits caused by ulBitsToClearOnExit being non-zero. + * + * @param xTicksToWait The maximum amount of time that the task should wait in + * the Blocked state for a notification to be received, should a notification + * not already be pending when xTaskNotifyWait() was called. The task + * will not consume any processing time while it is in the Blocked state. This + * is specified in kernel ticks, the macro pdMS_TO_TICKS( value_in_ms ) can be + * used to convert a time specified in milliseconds to a time specified in + * ticks. + * + * @return If a notification was received (including notifications that were + * already pending when xTaskNotifyWait was called) then pdPASS is + * returned. Otherwise pdFAIL is returned. + * + * \defgroup xTaskNotifyWaitIndexed xTaskNotifyWaitIndexed + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +#define xTaskNotifyWait( ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) \ + xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY, ( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) ) +#define xTaskNotifyWaitIndexed( uxIndexToWaitOn, ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) \ + xTaskGenericNotifyWait( ( uxIndexToWaitOn ), ( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyGiveIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify ); + * BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); + * @endcode + * + * Sends a direct to task notification to a particular index in the target + * task's notification array in a manner similar to giving a counting semaphore. + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for more details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * macros to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * xTaskNotifyGiveIndexed() is a helper macro intended for use when task + * notifications are used as light weight and faster binary or counting + * semaphore equivalents. Actual FreeRTOS semaphores are given using the + * xSemaphoreGive() API function, the equivalent action that instead uses a task + * notification is xTaskNotifyGiveIndexed(). + * + * When task notifications are being used as a binary or counting semaphore + * equivalent then the task being notified should wait for the notification + * using the ulTaskNotifyTakeIndexed() API function rather than the + * xTaskNotifyWaitIndexed() API function. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyGive() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling xTaskNotifyGive() is equivalent to calling + * xTaskNotifyGiveIndexed() with the uxIndexToNotify parameter set to 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyGive() + * does not have this parameter and always sends notifications to index 0. + * + * @return xTaskNotifyGive() is a macro that calls xTaskNotify() with the + * eAction parameter set to eIncrement - so pdPASS is always returned. + * + * \defgroup xTaskNotifyGiveIndexed xTaskNotifyGiveIndexed + * \ingroup TaskNotifications + */ +#define xTaskNotifyGive( xTaskToNotify ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( 0 ), eIncrement, NULL ) +#define xTaskNotifyGiveIndexed( xTaskToNotify, uxIndexToNotify ) \ + xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( 0 ), eIncrement, NULL ) + +/** + * task. h + * @code{c} + * void vTaskNotifyGiveIndexedFromISR( TaskHandle_t xTaskHandle, UBaseType_t uxIndexToNotify, BaseType_t *pxHigherPriorityTaskWoken ); + * void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken ); + * @endcode + * + * A version of xTaskNotifyGiveIndexed() that can be called from an interrupt + * service routine (ISR). + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for more details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro + * to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * vTaskNotifyGiveIndexedFromISR() is intended for use when task notifications + * are used as light weight and faster binary or counting semaphore equivalents. + * Actual FreeRTOS semaphores are given from an ISR using the + * xSemaphoreGiveFromISR() API function, the equivalent action that instead uses + * a task notification is vTaskNotifyGiveIndexedFromISR(). + * + * When task notifications are being used as a binary or counting semaphore + * equivalent then the task being notified should wait for the notification + * using the ulTaskNotifyTakeIndexed() API function rather than the + * xTaskNotifyWaitIndexed() API function. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyFromISR() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling xTaskNotifyGiveFromISR() is equivalent to calling + * xTaskNotifyGiveIndexedFromISR() with the uxIndexToNotify parameter set to 0. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param uxIndexToNotify The index within the target task's array of + * notification values to which the notification is to be sent. uxIndexToNotify + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * xTaskNotifyGiveFromISR() does not have this parameter and always sends + * notifications to index 0. + * + * @param pxHigherPriorityTaskWoken vTaskNotifyGiveFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the + * task to which the notification was sent to leave the Blocked state, and the + * unblocked task has a priority higher than the currently running task. If + * vTaskNotifyGiveFromISR() sets this value to pdTRUE then a context switch + * should be requested before the interrupt is exited. How a context switch is + * requested from an ISR is dependent on the port - see the documentation page + * for the port in use. + * + * \defgroup vTaskNotifyGiveIndexedFromISR vTaskNotifyGiveIndexedFromISR + * \ingroup TaskNotifications + */ +void vTaskGenericNotifyGiveFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; +#define vTaskNotifyGiveFromISR( xTaskToNotify, pxHigherPriorityTaskWoken ) \ + vTaskGenericNotifyGiveFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( pxHigherPriorityTaskWoken ) ) +#define vTaskNotifyGiveIndexedFromISR( xTaskToNotify, uxIndexToNotify, pxHigherPriorityTaskWoken ) \ + vTaskGenericNotifyGiveFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( pxHigherPriorityTaskWoken ) ) + +/** + * task. h + * @code{c} + * uint32_t ulTaskNotifyTakeIndexed( UBaseType_t uxIndexToWaitOn, BaseType_t xClearCountOnExit, TickType_t xTicksToWait ); + * + * uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ); + * @endcode + * + * Waits for a direct to task notification on a particular index in the calling + * task's notification array in a manner similar to taking a counting semaphore. + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this + * function to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment one of the task's notification values. In + * that way task notifications can be used to send data to a task, or be used as + * light weight and fast binary or counting semaphores. + * + * ulTaskNotifyTakeIndexed() is intended for use when a task notification is + * used as a faster and lighter weight binary or counting semaphore alternative. + * Actual FreeRTOS semaphores are taken using the xSemaphoreTake() API function, + * the equivalent action that instead uses a task notification is + * ulTaskNotifyTakeIndexed(). + * + * When a task is using its notification value as a binary or counting semaphore + * other tasks should send notifications to it using the xTaskNotifyGiveIndexed() + * macro, or xTaskNotifyIndex() function with the eAction parameter set to + * eIncrement. + * + * ulTaskNotifyTakeIndexed() can either clear the task's notification value at + * the array index specified by the uxIndexToWaitOn parameter to zero on exit, + * in which case the notification value acts like a binary semaphore, or + * decrement the notification value on exit, in which case the notification + * value acts like a counting semaphore. + * + * A task can use ulTaskNotifyTakeIndexed() to [optionally] block to wait for + * a notification. The task does not consume any CPU time while it is in the + * Blocked state. + * + * Where as xTaskNotifyWaitIndexed() will return when a notification is pending, + * ulTaskNotifyTakeIndexed() will return when the task's notification value is + * not zero. + * + * **NOTE** Each notification within the array operates independently - a task + * can only block on one notification within the array at a time and will not be + * unblocked by a notification sent to any other array index. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. ulTaskNotifyTake() is the original API function, and remains backward + * compatible by always operating on the notification value at index 0 in the + * array. Calling ulTaskNotifyTake() is equivalent to calling + * ulTaskNotifyTakeIndexed() with the uxIndexToWaitOn parameter set to 0. + * + * @param uxIndexToWaitOn The index within the calling task's array of + * notification values on which the calling task will wait for a notification to + * be non-zero. uxIndexToWaitOn must be less than + * configTASK_NOTIFICATION_ARRAY_ENTRIES. xTaskNotifyTake() does + * not have this parameter and always waits for notifications on index 0. + * + * @param xClearCountOnExit if xClearCountOnExit is pdFALSE then the task's + * notification value is decremented when the function exits. In this way the + * notification value acts like a counting semaphore. If xClearCountOnExit is + * not pdFALSE then the task's notification value is cleared to zero when the + * function exits. In this way the notification value acts like a binary + * semaphore. + * + * @param xTicksToWait The maximum amount of time that the task should wait in + * the Blocked state for the task's notification value to be greater than zero, + * should the count not already be greater than zero when + * ulTaskNotifyTake() was called. The task will not consume any processing + * time while it is in the Blocked state. This is specified in kernel ticks, + * the macro pdMS_TO_TICKS( value_in_ms ) can be used to convert a time + * specified in milliseconds to a time specified in ticks. + * + * @return The task's notification count before it is either cleared to zero or + * decremented (see the xClearCountOnExit parameter). + * + * \defgroup ulTaskNotifyTakeIndexed ulTaskNotifyTakeIndexed + * \ingroup TaskNotifications + */ +uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn, + BaseType_t xClearCountOnExit, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +#define ulTaskNotifyTake( xClearCountOnExit, xTicksToWait ) \ + ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ), ( xClearCountOnExit ), ( xTicksToWait ) ) +#define ulTaskNotifyTakeIndexed( uxIndexToWaitOn, xClearCountOnExit, xTicksToWait ) \ + ulTaskGenericNotifyTake( ( uxIndexToWaitOn ), ( xClearCountOnExit ), ( xTicksToWait ) ) + +/** + * task. h + * @code{c} + * BaseType_t xTaskNotifyStateClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToCLear ); + * + * BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * If a notification is sent to an index within the array of notifications then + * the notification at that index is said to be 'pending' until it is read or + * explicitly cleared by the receiving task. xTaskNotifyStateClearIndexed() + * is the function that clears a pending notification without reading the + * notification value. The notification value at the same array index is not + * altered. Set xTask to NULL to clear the notification state of the calling + * task. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. xTaskNotifyStateClear() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling xTaskNotifyStateClear() is equivalent to calling + * xTaskNotifyStateClearIndexed() with the uxIndexToNotify parameter set to 0. + * + * @param xTask The handle of the RTOS task that will have a notification state + * cleared. Set xTask to NULL to clear a notification state in the calling + * task. To obtain a task's handle create the task using xTaskCreate() and + * make use of the pxCreatedTask parameter, or create the task using + * xTaskCreateStatic() and store the returned value, or use the task's name in + * a call to xTaskGetHandle(). + * + * @param uxIndexToClear The index within the target task's array of + * notification values to act upon. For example, setting uxIndexToClear to 1 + * will clear the state of the notification at index 1 within the array. + * uxIndexToClear must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * ulTaskNotifyStateClear() does not have this parameter and always acts on the + * notification at index 0. + * + * @return pdTRUE if the task's notification state was set to + * eNotWaitingNotification, otherwise pdFALSE. + * + * \defgroup xTaskNotifyStateClearIndexed xTaskNotifyStateClearIndexed + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotifyStateClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear ) PRIVILEGED_FUNCTION; +#define xTaskNotifyStateClear( xTask ) \ + xTaskGenericNotifyStateClear( ( xTask ), ( tskDEFAULT_INDEX_TO_NOTIFY ) ) +#define xTaskNotifyStateClearIndexed( xTask, uxIndexToClear ) \ + xTaskGenericNotifyStateClear( ( xTask ), ( uxIndexToClear ) ) + +/** + * task. h + * @code{c} + * uint32_t ulTaskNotifyValueClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToClear, uint32_t ulBitsToClear ); + * + * uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ); + * @endcode + * + * See https://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for these + * functions to be available. + * + * Each task has a private array of "notification values" (or 'notifications'), + * each of which is a 32-bit unsigned integer (uint32_t). The constant + * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the + * array, and (for backward compatibility) defaults to 1 if left undefined. + * Prior to FreeRTOS V10.4.0 there was only one notification value per task. + * + * ulTaskNotifyValueClearIndexed() clears the bits specified by the + * ulBitsToClear bit mask in the notification value at array index uxIndexToClear + * of the task referenced by xTask. + * + * Backward compatibility information: + * Prior to FreeRTOS V10.4.0 each task had a single "notification value", and + * all task notification API functions operated on that value. Replacing the + * single notification value with an array of notification values necessitated a + * new set of API functions that could address specific notifications within the + * array. ulTaskNotifyValueClear() is the original API function, and remains + * backward compatible by always operating on the notification value at index 0 + * within the array. Calling ulTaskNotifyValueClear() is equivalent to calling + * ulTaskNotifyValueClearIndexed() with the uxIndexToClear parameter set to 0. + * + * @param xTask The handle of the RTOS task that will have bits in one of its + * notification values cleared. Set xTask to NULL to clear bits in a + * notification value of the calling task. To obtain a task's handle create the + * task using xTaskCreate() and make use of the pxCreatedTask parameter, or + * create the task using xTaskCreateStatic() and store the returned value, or + * use the task's name in a call to xTaskGetHandle(). + * + * @param uxIndexToClear The index within the target task's array of + * notification values in which to clear the bits. uxIndexToClear + * must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES. + * ulTaskNotifyValueClear() does not have this parameter and always clears bits + * in the notification value at index 0. + * + * @param ulBitsToClear Bit mask of the bits to clear in the notification value of + * xTask. Set a bit to 1 to clear the corresponding bits in the task's notification + * value. Set ulBitsToClear to 0xffffffff (UINT_MAX on 32-bit architectures) to clear + * the notification value to 0. Set ulBitsToClear to 0 to query the task's + * notification value without clearing any bits. + * + * + * @return The value of the target task's notification value before the bits + * specified by ulBitsToClear were cleared. + * \defgroup ulTaskNotifyValueClear ulTaskNotifyValueClear + * \ingroup TaskNotifications + */ +uint32_t ulTaskGenericNotifyValueClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear, + uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION; +#define ulTaskNotifyValueClear( xTask, ulBitsToClear ) \ + ulTaskGenericNotifyValueClear( ( xTask ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulBitsToClear ) ) +#define ulTaskNotifyValueClearIndexed( xTask, uxIndexToClear, ulBitsToClear ) \ + ulTaskGenericNotifyValueClear( ( xTask ), ( uxIndexToClear ), ( ulBitsToClear ) ) + +/** + * task.h + * @code{c} + * void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ); + * @endcode + * + * Capture the current time for future use with xTaskCheckForTimeOut(). + * + * @param pxTimeOut Pointer to a timeout object into which the current time + * is to be captured. The captured time includes the tick count and the number + * of times the tick count has overflowed since the system first booted. + * \defgroup vTaskSetTimeOutState vTaskSetTimeOutState + * \ingroup TaskCtrl + */ +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + +/** + * task.h + * @code{c} + * BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ); + * @endcode + * + * Determines if pxTicksToWait ticks has passed since a time was captured + * using a call to vTaskSetTimeOutState(). The captured time includes the tick + * count and the number of times the tick count has overflowed. + * + * @param pxTimeOut The time status as captured previously using + * vTaskSetTimeOutState. If the timeout has not yet occurred, it is updated + * to reflect the current time status. + * @param pxTicksToWait The number of ticks to check for timeout i.e. if + * pxTicksToWait ticks have passed since pxTimeOut was last updated (either by + * vTaskSetTimeOutState() or xTaskCheckForTimeOut()), the timeout has occurred. + * If the timeout has not occurred, pxTicksToWait is updated to reflect the + * number of remaining ticks. + * + * @return If timeout has occurred, pdTRUE is returned. Otherwise pdFALSE is + * returned and pxTicksToWait is updated to reflect the number of remaining + * ticks. + * + * @see https://www.FreeRTOS.org/xTaskCheckForTimeOut.html + * + * Example Usage: + * @code{c} + * // Driver library function used to receive uxWantedBytes from an Rx buffer + * // that is filled by a UART interrupt. If there are not enough bytes in the + * // Rx buffer then the task enters the Blocked state until it is notified that + * // more data has been placed into the buffer. If there is still not enough + * // data then the task re-enters the Blocked state, and xTaskCheckForTimeOut() + * // is used to re-calculate the Block time to ensure the total amount of time + * // spent in the Blocked state does not exceed MAX_TIME_TO_WAIT. This + * // continues until either the buffer contains at least uxWantedBytes bytes, + * // or the total amount of time spent in the Blocked state reaches + * // MAX_TIME_TO_WAIT - at which point the task reads however many bytes are + * // available up to a maximum of uxWantedBytes. + * + * size_t xUART_Receive( uint8_t *pucBuffer, size_t uxWantedBytes ) + * { + * size_t uxReceived = 0; + * TickType_t xTicksToWait = MAX_TIME_TO_WAIT; + * TimeOut_t xTimeOut; + * + * // Initialize xTimeOut. This records the time at which this function + * // was entered. + * vTaskSetTimeOutState( &xTimeOut ); + * + * // Loop until the buffer contains the wanted number of bytes, or a + * // timeout occurs. + * while( UART_bytes_in_rx_buffer( pxUARTInstance ) < uxWantedBytes ) + * { + * // The buffer didn't contain enough data so this task is going to + * // enter the Blocked state. Adjusting xTicksToWait to account for + * // any time that has been spent in the Blocked state within this + * // function so far to ensure the total amount of time spent in the + * // Blocked state does not exceed MAX_TIME_TO_WAIT. + * if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) != pdFALSE ) + * { + * //Timed out before the wanted number of bytes were available, + * // exit the loop. + * break; + * } + * + * // Wait for a maximum of xTicksToWait ticks to be notified that the + * // receive interrupt has placed more data into the buffer. + * ulTaskNotifyTake( pdTRUE, xTicksToWait ); + * } + * + * // Attempt to read uxWantedBytes from the receive buffer into pucBuffer. + * // The actual number of bytes read (which might be less than + * // uxWantedBytes) is returned. + * uxReceived = UART_read_from_receive_buffer( pxUARTInstance, + * pucBuffer, + * uxWantedBytes ); + * + * return uxReceived; + * } + * @endcode + * \defgroup xTaskCheckForTimeOut xTaskCheckForTimeOut + * \ingroup TaskCtrl + */ +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, + TickType_t * const pxTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * task.h + * @code{c} + * BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ); + * @endcode + * + * This function corrects the tick count value after the application code has held + * interrupts disabled for an extended period resulting in tick interrupts having + * been missed. + * + * This function is similar to vTaskStepTick(), however, unlike + * vTaskStepTick(), xTaskCatchUpTicks() may move the tick count forward past a + * time at which a task should be removed from the blocked state. That means + * tasks may have to be removed from the blocked state as the tick count is + * moved. + * + * @param xTicksToCatchUp The number of tick interrupts that have been missed due to + * interrupts being disabled. Its value is not computed automatically, so must be + * computed by the application writer. + * + * @return pdTRUE if moving the tick count forward resulted in a task leaving the + * blocked state and a context switch being performed. Otherwise pdFALSE. + * + * \defgroup xTaskCatchUpTicks xTaskCatchUpTicks + * \ingroup TaskCtrl + */ +BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) PRIVILEGED_FUNCTION; + +/** + * task.h + * @code{c} + * void vTaskResetState( void ); + * @endcode + * + * This function resets the internal state of the task. It must be called by the + * application before restarting the scheduler. + * + * \defgroup vTaskResetState vTaskResetState + * \ingroup SchedulerControl + */ +void vTaskResetState( void ) PRIVILEGED_FUNCTION; + + +/*----------------------------------------------------------- +* SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES +*----------------------------------------------------------*/ + +#if ( configNUMBER_OF_CORES == 1 ) + #define taskYIELD_WITHIN_API() portYIELD_WITHIN_API() +#else /* #if ( configNUMBER_OF_CORES == 1 ) */ + #define taskYIELD_WITHIN_API() vTaskYieldWithinAPI() +#endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Called from the real time kernel tick (either preemptive or cooperative), + * this increments the tick count and checks if any tasks that are blocked + * for a finite period required removing from a blocked list and placing on + * a ready list. If a non-zero value is returned then a context switch is + * required because either: + * + A task was removed from a blocked list because its timeout had expired, + * or + * + Time slicing is in use and there is a task of equal priority to the + * currently running task. + */ +BaseType_t xTaskIncrementTick( void ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes the calling task from the ready list and places it both + * on the list of tasks waiting for a particular event, and the + * list of delayed tasks. The task will be removed from both lists + * and replaced on the ready list should either the event occur (and + * there be no higher priority tasks waiting on the same event) or + * the delay period expires. + * + * The 'unordered' version replaces the event list item value with the + * xItemValue value, and inserts the list item at the end of the list. + * + * The 'ordered' version uses the existing event list item value (which is the + * owning task's priority) to insert the list item into the event list in task + * priority order. + * + * @param pxEventList The list containing tasks that are blocked waiting + * for the event to occur. + * + * @param xItemValue The item value to use for the event list item when the + * event list is not ordered by task priority. + * + * @param xTicksToWait The maximum amount of time that the task should wait + * for the event to occur. This is specified in kernel ticks, the constant + * portTICK_PERIOD_MS can be used to convert kernel ticks into a real time + * period. + */ +void vTaskPlaceOnEventList( List_t * const pxEventList, + const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, + const TickType_t xItemValue, + const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * This function performs nearly the same function as vTaskPlaceOnEventList(). + * The difference being that this function does not permit tasks to block + * indefinitely, whereas vTaskPlaceOnEventList() does. + * + */ +void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, + TickType_t xTicksToWait, + const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes a task from both the specified event list and the list of blocked + * tasks, and places it on a ready queue. + * + * xTaskRemoveFromEventList()/vTaskRemoveFromUnorderedEventList() will be called + * if either an event occurs to unblock a task, or the block timeout period + * expires. + * + * xTaskRemoveFromEventList() is used when the event list is in task priority + * order. It removes the list item from the head of the event list as that will + * have the highest priority owning task of all the tasks on the event list. + * vTaskRemoveFromUnorderedEventList() is used when the event list is not + * ordered and the event list items hold something other than the owning tasks + * priority. In this case the event list item value is updated to the value + * passed in the xItemValue parameter. + * + * @return pdTRUE if the task being removed has a higher priority than the task + * making the call, otherwise pdFALSE. + */ +BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) PRIVILEGED_FUNCTION; +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, + const TickType_t xItemValue ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Sets the pointer to the current TCB to the TCB of the highest priority task + * that is ready to run. + */ +#if ( configNUMBER_OF_CORES == 1 ) + portDONT_DISCARD void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION; +#else + portDONT_DISCARD void vTaskSwitchContext( BaseType_t xCoreID ) PRIVILEGED_FUNCTION; +#endif + +/* + * THESE FUNCTIONS MUST NOT BE USED FROM APPLICATION CODE. THEY ARE USED BY + * THE EVENT BITS MODULE. + */ +TickType_t uxTaskResetEventItemValue( void ) PRIVILEGED_FUNCTION; + +/* + * Return the handle of the calling task. + */ +TaskHandle_t xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +/* + * Return the handle of the task running on specified core. + */ +TaskHandle_t xTaskGetCurrentTaskHandleForCore( BaseType_t xCoreID ) PRIVILEGED_FUNCTION; + +/* + * Shortcut used by the queue implementation to prevent unnecessary call to + * taskYIELD(); + */ +void vTaskMissedYield( void ) PRIVILEGED_FUNCTION; + +/* + * Returns the scheduler state as taskSCHEDULER_RUNNING, + * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED. + */ +BaseType_t xTaskGetSchedulerState( void ) PRIVILEGED_FUNCTION; + +/* + * Raises the priority of the mutex holder to that of the calling task should + * the mutex holder have a priority less than the calling task. + */ +BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * Set the priority of a task back to its proper priority in the case that it + * inherited a higher priority while it was holding a semaphore. + */ +BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * If a higher priority task attempting to obtain a mutex caused a lower + * priority task to inherit the higher priority task's priority - but the higher + * priority task then timed out without obtaining the mutex, then the lower + * priority task will disinherit the priority again - but only down as far as + * the highest priority task that is still waiting for the mutex (if there were + * more than one task waiting for the mutex). + */ +void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, + UBaseType_t uxHighestPriorityWaitingTask ) PRIVILEGED_FUNCTION; + +/* + * Get the uxTaskNumber assigned to the task referenced by the xTask parameter. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; +#endif + +/* + * Set the uxTaskNumber of the task referenced by the xTask parameter to + * uxHandle. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + void vTaskSetTaskNumber( TaskHandle_t xTask, + const UBaseType_t uxHandle ) PRIVILEGED_FUNCTION; +#endif + +/* + * Only available when configUSE_TICKLESS_IDLE is set to 1. + * If tickless mode is being used, or a low power mode is implemented, then + * the tick interrupt will not execute during idle periods. When this is the + * case, the tick count value maintained by the scheduler needs to be kept up + * to date with the actual execution time by being skipped forward by a time + * equal to the idle period. + */ +#if ( configUSE_TICKLESS_IDLE != 0 ) + void vTaskStepTick( TickType_t xTicksToJump ) PRIVILEGED_FUNCTION; +#endif + +/* + * Only available when configUSE_TICKLESS_IDLE is set to 1. + * Provided for use within portSUPPRESS_TICKS_AND_SLEEP() to allow the port + * specific sleep function to determine if it is ok to proceed with the sleep, + * and if it is ok to proceed, if it is ok to sleep indefinitely. + * + * This function is necessary because portSUPPRESS_TICKS_AND_SLEEP() is only + * called with the scheduler suspended, not from within a critical section. It + * is therefore possible for an interrupt to request a context switch between + * portSUPPRESS_TICKS_AND_SLEEP() and the low power mode actually being + * entered. eTaskConfirmSleepModeStatus() should be called from a short + * critical section between the timer being stopped and the sleep mode being + * entered to ensure it is ok to proceed into the sleep mode. + */ +#if ( configUSE_TICKLESS_IDLE != 0 ) + eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; +#endif + +/* + * For internal use only. Increment the mutex held count when a mutex is + * taken and return the handle of the task that has taken the mutex. + */ +TaskHandle_t pvTaskIncrementMutexHeldCount( void ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Same as vTaskSetTimeOutState(), but without a critical + * section. + */ +void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Same as portYIELD_WITHIN_API() in single core FreeRTOS. + * For SMP this is not defined by the port. + */ +#if ( configNUMBER_OF_CORES > 1 ) + void vTaskYieldWithinAPI( void ); +#endif + +/* + * This function is only intended for use when implementing a port of the scheduler + * and is only available when portCRITICAL_NESTING_IN_TCB is set to 1 or configNUMBER_OF_CORES + * is greater than 1. This function can be used in the implementation of portENTER_CRITICAL + * if port wants to maintain critical nesting count in TCB in single core FreeRTOS. + * It should be used in the implementation of portENTER_CRITICAL if port is running a + * multiple core FreeRTOS. + */ +#if ( ( portCRITICAL_NESTING_IN_TCB == 1 ) || ( configNUMBER_OF_CORES > 1 ) ) + void vTaskEnterCritical( void ); +#endif + +/* + * This function is only intended for use when implementing a port of the scheduler + * and is only available when portCRITICAL_NESTING_IN_TCB is set to 1 or configNUMBER_OF_CORES + * is greater than 1. This function can be used in the implementation of portEXIT_CRITICAL + * if port wants to maintain critical nesting count in TCB in single core FreeRTOS. + * It should be used in the implementation of portEXIT_CRITICAL if port is running a + * multiple core FreeRTOS. + */ +#if ( ( portCRITICAL_NESTING_IN_TCB == 1 ) || ( configNUMBER_OF_CORES > 1 ) ) + void vTaskExitCritical( void ); +#endif + +/* + * This function is only intended for use when implementing a port of the scheduler + * and is only available when configNUMBER_OF_CORES is greater than 1. This function + * should be used in the implementation of portENTER_CRITICAL_FROM_ISR if port is + * running a multiple core FreeRTOS. + */ +#if ( configNUMBER_OF_CORES > 1 ) + UBaseType_t vTaskEnterCriticalFromISR( void ); +#endif + +/* + * This function is only intended for use when implementing a port of the scheduler + * and is only available when configNUMBER_OF_CORES is greater than 1. This function + * should be used in the implementation of portEXIT_CRITICAL_FROM_ISR if port is + * running a multiple core FreeRTOS. + */ +#if ( configNUMBER_OF_CORES > 1 ) + void vTaskExitCriticalFromISR( UBaseType_t uxSavedInterruptStatus ); +#endif + +#if ( portUSING_MPU_WRAPPERS == 1 ) + +/* + * For internal use only. Get MPU settings associated with a task. + */ + xMPU_SETTINGS * xTaskGetMPUSettings( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +#endif /* portUSING_MPU_WRAPPERS */ + + +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) && ( configENABLE_ACCESS_CONTROL_LIST == 1 ) ) + +/* + * For internal use only. Grant/Revoke a task's access to a kernel object. + */ + void vGrantAccessToKernelObject( TaskHandle_t xExternalTaskHandle, + int32_t lExternalKernelObjectHandle ) PRIVILEGED_FUNCTION; + void vRevokeAccessToKernelObject( TaskHandle_t xExternalTaskHandle, + int32_t lExternalKernelObjectHandle ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Grant/Revoke a task's access to a kernel object. + */ + void vPortGrantAccessToKernelObject( TaskHandle_t xInternalTaskHandle, + int32_t lInternalIndexOfKernelObject ) PRIVILEGED_FUNCTION; + void vPortRevokeAccessToKernelObject( TaskHandle_t xInternalTaskHandle, + int32_t lInternalIndexOfKernelObject ) PRIVILEGED_FUNCTION; + +#endif /* #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) && ( configENABLE_ACCESS_CONTROL_LIST == 1 ) ) */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ +#endif /* INC_TASK_H */ diff --git a/vendor/FreeRTOS-Kernel/include/timers.h b/vendor/FreeRTOS-Kernel/include/timers.h new file mode 100644 index 0000000..480f31c --- /dev/null +++ b/vendor/FreeRTOS-Kernel/include/timers.h @@ -0,0 +1,1434 @@ +/* + * 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 TIMERS_H +#define TIMERS_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include timers.h" +#endif + +#include "task.h" + + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/*----------------------------------------------------------- +* MACROS AND DEFINITIONS +*----------------------------------------------------------*/ + +/* IDs for commands that can be sent/received on the timer queue. These are to + * be used solely through the macros that make up the public software timer API, + * as defined below. The commands that are sent from interrupts must use the + * highest numbers as tmrFIRST_FROM_ISR_COMMAND is used to determine if the task + * or interrupt version of the queue send function should be used. */ +#define tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR ( ( BaseType_t ) -2 ) +#define tmrCOMMAND_EXECUTE_CALLBACK ( ( BaseType_t ) -1 ) +#define tmrCOMMAND_START_DONT_TRACE ( ( BaseType_t ) 0 ) +#define tmrCOMMAND_START ( ( BaseType_t ) 1 ) +#define tmrCOMMAND_RESET ( ( BaseType_t ) 2 ) +#define tmrCOMMAND_STOP ( ( BaseType_t ) 3 ) +#define tmrCOMMAND_CHANGE_PERIOD ( ( BaseType_t ) 4 ) +#define tmrCOMMAND_DELETE ( ( BaseType_t ) 5 ) + +#define tmrFIRST_FROM_ISR_COMMAND ( ( BaseType_t ) 6 ) +#define tmrCOMMAND_START_FROM_ISR ( ( BaseType_t ) 6 ) +#define tmrCOMMAND_RESET_FROM_ISR ( ( BaseType_t ) 7 ) +#define tmrCOMMAND_STOP_FROM_ISR ( ( BaseType_t ) 8 ) +#define tmrCOMMAND_CHANGE_PERIOD_FROM_ISR ( ( BaseType_t ) 9 ) + + +/** + * Type by which software timers are referenced. For example, a call to + * xTimerCreate() returns an TimerHandle_t variable that can then be used to + * reference the subject timer in calls to other software timer API functions + * (for example, xTimerStart(), xTimerReset(), etc.). + */ +struct tmrTimerControl; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +typedef struct tmrTimerControl * TimerHandle_t; + +/* + * Defines the prototype to which timer callback functions must conform. + */ +typedef void (* TimerCallbackFunction_t)( TimerHandle_t xTimer ); + +/* + * Defines the prototype to which functions used with the + * xTimerPendFunctionCallFromISR() function must conform. + */ +typedef void (* PendedFunction_t)( void * arg1, + uint32_t arg2 ); + +/** + * TimerHandle_t xTimerCreate( const char * const pcTimerName, + * TickType_t xTimerPeriodInTicks, + * BaseType_t xAutoReload, + * void * pvTimerID, + * TimerCallbackFunction_t pxCallbackFunction ); + * + * Creates a new software timer instance, and returns a handle by which the + * created software timer can be referenced. + * + * Internally, within the FreeRTOS implementation, software timers use a block + * of memory, in which the timer data structure is stored. If a software timer + * is created using xTimerCreate() then the required memory is automatically + * dynamically allocated inside the xTimerCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a software timer is created using + * xTimerCreateStatic() then the application writer must provide the memory that + * will get used by the software timer. xTimerCreateStatic() therefore allows a + * software timer to be created without using any dynamic memory allocation. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a + * timer into the active state. + * + * @param pcTimerName A text name that is assigned to the timer. This is done + * purely to assist debugging. The kernel itself only ever references a timer + * by its handle, and never by its name. + * + * @param xTimerPeriodInTicks The timer period. The time is defined in tick + * periods so the constant portTICK_PERIOD_MS can be used to convert a time that + * has been specified in milliseconds. For example, if the timer must expire + * after 100 ticks, then xTimerPeriodInTicks should be set to 100. + * Alternatively, if the timer must expire after 500ms, then xPeriod can be set + * to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than or + * equal to 1000. Time timer period must be greater than 0. + * + * @param xAutoReload If xAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the xTimerPeriodInTicks parameter. + * If xAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + * + * @param pvTimerID An identifier that is assigned to the timer being created. + * Typically this would be used in the timer callback function to identify which + * timer expired when the same callback function is assigned to more than one + * timer. + * + * @param pxCallbackFunction The function to call when the timer expires. + * Callback functions must have the prototype defined by TimerCallbackFunction_t, + * which is "void vCallbackFunction( TimerHandle_t xTimer );". + * + * @return If the timer is successfully created then a handle to the newly + * created timer is returned. If the timer cannot be created because there is + * insufficient FreeRTOS heap remaining to allocate the timer + * structures then NULL is returned. + * + * Example usage: + * @verbatim + * #define NUM_TIMERS 5 + * + * // An array to hold handles to the created timers. + * TimerHandle_t xTimers[ NUM_TIMERS ]; + * + * // An array to hold a count of the number of times each timer expires. + * int32_t lExpireCounters[ NUM_TIMERS ] = { 0 }; + * + * // Define a callback function that will be used by multiple timer instances. + * // The callback function does nothing but count the number of times the + * // associated timer expires, and stop the timer once the timer has expired + * // 10 times. + * void vTimerCallback( TimerHandle_t pxTimer ) + * { + * int32_t lArrayIndex; + * const int32_t xMaxExpiryCountBeforeStopping = 10; + * + * // Optionally do something if the pxTimer parameter is NULL. + * configASSERT( pxTimer ); + * + * // Which timer expired? + * lArrayIndex = ( int32_t ) pvTimerGetTimerID( pxTimer ); + * + * // Increment the number of times that pxTimer has expired. + * lExpireCounters[ lArrayIndex ] += 1; + * + * // If the timer has expired 10 times then stop it from running. + * if( lExpireCounters[ lArrayIndex ] == xMaxExpiryCountBeforeStopping ) + * { + * // Do not use a block time if calling a timer API function from a + * // timer callback function, as doing so could cause a deadlock! + * xTimerStop( pxTimer, 0 ); + * } + * } + * + * void main( void ) + * { + * int32_t x; + * + * // Create then start some timers. Starting the timers before the scheduler + * // has been started means the timers will start running immediately that + * // the scheduler starts. + * for( x = 0; x < NUM_TIMERS; x++ ) + * { + * xTimers[ x ] = xTimerCreate( "Timer", // Just a text name, not used by the kernel. + * ( 100 * ( x + 1 ) ), // The timer period in ticks. + * pdTRUE, // The timers will auto-reload themselves when they expire. + * ( void * ) x, // Assign each timer a unique id equal to its array index. + * vTimerCallback // Each timer calls the same callback when it expires. + * ); + * + * if( xTimers[ x ] == NULL ) + * { + * // The timer was not created. + * } + * else + * { + * // Start the timer. No block time is specified, and even if one was + * // it would be ignored because the scheduler has not yet been + * // started. + * if( xTimerStart( xTimers[ x ], 0 ) != pdPASS ) + * { + * // The timer could not be set into the Active state. + * } + * } + * } + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timers running as they have already + * // been set into the active state. + * vTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + TimerHandle_t xTimerCreate( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const BaseType_t xAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; +#endif + +/** + * TimerHandle_t xTimerCreateStatic(const char * const pcTimerName, + * TickType_t xTimerPeriodInTicks, + * BaseType_t xAutoReload, + * void * pvTimerID, + * TimerCallbackFunction_t pxCallbackFunction, + * StaticTimer_t *pxTimerBuffer ); + * + * Creates a new software timer instance, and returns a handle by which the + * created software timer can be referenced. + * + * Internally, within the FreeRTOS implementation, software timers use a block + * of memory, in which the timer data structure is stored. If a software timer + * is created using xTimerCreate() then the required memory is automatically + * dynamically allocated inside the xTimerCreate() function. (see + * https://www.FreeRTOS.org/a00111.html). If a software timer is created using + * xTimerCreateStatic() then the application writer must provide the memory that + * will get used by the software timer. xTimerCreateStatic() therefore allows a + * software timer to be created without using any dynamic memory allocation. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a + * timer into the active state. + * + * @param pcTimerName A text name that is assigned to the timer. This is done + * purely to assist debugging. The kernel itself only ever references a timer + * by its handle, and never by its name. + * + * @param xTimerPeriodInTicks The timer period. The time is defined in tick + * periods so the constant portTICK_PERIOD_MS can be used to convert a time that + * has been specified in milliseconds. For example, if the timer must expire + * after 100 ticks, then xTimerPeriodInTicks should be set to 100. + * Alternatively, if the timer must expire after 500ms, then xPeriod can be set + * to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than or + * equal to 1000. The timer period must be greater than 0. + * + * @param xAutoReload If xAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the xTimerPeriodInTicks parameter. + * If xAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + * + * @param pvTimerID An identifier that is assigned to the timer being created. + * Typically this would be used in the timer callback function to identify which + * timer expired when the same callback function is assigned to more than one + * timer. + * + * @param pxCallbackFunction The function to call when the timer expires. + * Callback functions must have the prototype defined by TimerCallbackFunction_t, + * which is "void vCallbackFunction( TimerHandle_t xTimer );". + * + * @param pxTimerBuffer Must point to a variable of type StaticTimer_t, which + * will be then be used to hold the software timer's data structures, removing + * the need for the memory to be allocated dynamically. + * + * @return If the timer is created then a handle to the created timer is + * returned. If pxTimerBuffer was NULL then NULL is returned. + * + * Example usage: + * @verbatim + * + * // The buffer used to hold the software timer's data structure. + * static StaticTimer_t xTimerBuffer; + * + * // A variable that will be incremented by the software timer's callback + * // function. + * UBaseType_t uxVariableToIncrement = 0; + * + * // A software timer callback function that increments a variable passed to + * // it when the software timer was created. After the 5th increment the + * // callback function stops the software timer. + * static void prvTimerCallback( TimerHandle_t xExpiredTimer ) + * { + * UBaseType_t *puxVariableToIncrement; + * BaseType_t xReturned; + * + * // Obtain the address of the variable to increment from the timer ID. + * puxVariableToIncrement = ( UBaseType_t * ) pvTimerGetTimerID( xExpiredTimer ); + * + * // Increment the variable to show the timer callback has executed. + * ( *puxVariableToIncrement )++; + * + * // If this callback has executed the required number of times, stop the + * // timer. + * if( *puxVariableToIncrement == 5 ) + * { + * // This is called from a timer callback so must not block. + * xTimerStop( xExpiredTimer, staticDONT_BLOCK ); + * } + * } + * + * + * void main( void ) + * { + * // Create the software time. xTimerCreateStatic() has an extra parameter + * // than the normal xTimerCreate() API function. The parameter is a pointer + * // to the StaticTimer_t structure that will hold the software timer + * // structure. If the parameter is passed as NULL then the structure will be + * // allocated dynamically, just as if xTimerCreate() had been called. + * xTimer = xTimerCreateStatic( "T1", // Text name for the task. Helps debugging only. Not used by FreeRTOS. + * xTimerPeriod, // The period of the timer in ticks. + * pdTRUE, // This is an auto-reload timer. + * ( void * ) &uxVariableToIncrement, // A variable incremented by the software timer's callback function + * prvTimerCallback, // The function to execute when the timer expires. + * &xTimerBuffer ); // The buffer that will hold the software timer structure. + * + * // The scheduler has not started yet so a block time is not used. + * xReturned = xTimerStart( xTimer, 0 ); + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timers running as they have already + * // been set into the active state. + * vTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const BaseType_t xAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t * pxTimerBuffer ) PRIVILEGED_FUNCTION; +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * void *pvTimerGetTimerID( TimerHandle_t xTimer ); + * + * Returns the ID assigned to the timer. + * + * IDs are assigned to timers using the pvTimerID parameter of the call to + * xTimerCreated() that was used to create the timer, and by calling the + * vTimerSetTimerID() API function. + * + * If the same callback function is assigned to multiple timers then the timer + * ID can be used as time specific (timer local) storage. + * + * @param xTimer The timer being queried. + * + * @return The ID assigned to the timer being queried. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + */ +void * pvTimerGetTimerID( const TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); + * + * Sets the ID assigned to the timer. + * + * IDs are assigned to timers using the pvTimerID parameter of the call to + * xTimerCreated() that was used to create the timer. + * + * If the same callback function is assigned to multiple timers then the timer + * ID can be used as time specific (timer local) storage. + * + * @param xTimer The timer being updated. + * + * @param pvNewID The ID to assign to the timer. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + */ +void vTimerSetTimerID( TimerHandle_t xTimer, + void * pvNewID ) PRIVILEGED_FUNCTION; + +/** + * BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); + * + * Queries a timer to see if it is active or dormant. + * + * A timer will be dormant if: + * 1) It has been created but not started, or + * 2) It is an expired one-shot timer that has not been restarted. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a timer into the + * active state. + * + * @param xTimer The timer being queried. + * + * @return pdFALSE will be returned if the timer is dormant. A value other than + * pdFALSE will be returned if the timer is active. + * + * Example usage: + * @verbatim + * // This function assumes xTimer has already been created. + * void vAFunction( TimerHandle_t xTimer ) + * { + * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" + * { + * // xTimer is active, do something. + * } + * else + * { + * // xTimer is not active, do something else. + * } + * } + * @endverbatim + */ +BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); + * + * Simply returns the handle of the timer service/daemon task. It it not valid + * to call xTimerGetTimerDaemonTaskHandle() before the scheduler has been started. + */ +TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) PRIVILEGED_FUNCTION; + +/** + * BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerStart() starts a timer that was previously created using the + * xTimerCreate() API function. If the timer had already been started and was + * already in the active state, then xTimerStart() has equivalent functionality + * to the xTimerReset() API function. + * + * Starting a timer ensures the timer is in the active state. If the timer + * is not stopped, deleted, or reset in the mean time, the callback function + * associated with the timer will get called 'n' ticks after xTimerStart() was + * called, where 'n' is the timers defined period. + * + * It is valid to call xTimerStart() before the scheduler has been started, but + * when this is done the timer will not actually start until the scheduler is + * started, and the timers expiry time will be relative to when the scheduler is + * started, not relative to when xTimerStart() was called. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStart() + * to be available. + * + * @param xTimer The handle of the timer being started/restarted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the start command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerStart() was called. xTicksToWait is ignored if xTimerStart() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the start command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system, although the + * timers expiry time is relative to when xTimerStart() is actually called. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + * + */ +#define xTimerStart( xTimer, xTicksToWait ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerStop() stops a timer that was previously started using either of the + * The xTimerStart(), xTimerReset(), xTimerStartFromISR(), xTimerResetFromISR(), + * xTimerChangePeriod() or xTimerChangePeriodFromISR() API functions. + * + * Stopping a timer ensures the timer is not in the active state. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStop() + * to be available. + * + * @param xTimer The handle of the timer being stopped. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the stop command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerStop() was called. xTicksToWait is ignored if xTimerStop() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the stop command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + * + */ +#define xTimerStop( xTimer, xTicksToWait ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, + * TickType_t xNewPeriod, + * TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerChangePeriod() changes the period of a timer that was previously + * created using the xTimerCreate() API function. + * + * xTimerChangePeriod() can be called to change the period of an active or + * dormant state timer. + * + * The configUSE_TIMERS configuration constant must be set to 1 for + * xTimerChangePeriod() to be available. + * + * @param xTimer The handle of the timer that is having its period changed. + * + * @param xNewPeriod The new period for xTimer. Timer periods are specified in + * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a time + * that has been specified in milliseconds. For example, if the timer must + * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, + * if the timer must expire after 500ms, then xNewPeriod can be set to + * ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than + * or equal to 1000. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the change period command to be + * successfully sent to the timer command queue, should the queue already be + * full when xTimerChangePeriod() was called. xTicksToWait is ignored if + * xTimerChangePeriod() is called before the scheduler is started. + * + * @return pdFAIL will be returned if the change period command could not be + * sent to the timer command queue even after xTicksToWait ticks had passed. + * pdPASS will be returned if the command was successfully sent to the timer + * command queue. When the command is actually processed will depend on the + * priority of the timer service/daemon task relative to other tasks in the + * system. The timer service/daemon task priority is set by the + * configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This function assumes xTimer has already been created. If the timer + * // referenced by xTimer is already active when it is called, then the timer + * // is deleted. If the timer referenced by xTimer is not active when it is + * // called, then the period of the timer is set to 500ms and the timer is + * // started. + * void vAFunction( TimerHandle_t xTimer ) + * { + * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" + * { + * // xTimer is already active - delete it. + * xTimerDelete( xTimer ); + * } + * else + * { + * // xTimer is not active, change its period to 500ms. This will also + * // cause the timer to start. Block for a maximum of 100 ticks if the + * // change period command cannot immediately be sent to the timer + * // command queue. + * if( xTimerChangePeriod( xTimer, 500 / portTICK_PERIOD_MS, 100 ) == pdPASS ) + * { + * // The command was successfully sent. + * } + * else + * { + * // The command could not be sent, even after waiting for 100 ticks + * // to pass. Take appropriate action here. + * } + * } + * } + * @endverbatim + */ +#define xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerDelete() deletes a timer that was previously created using the + * xTimerCreate() API function. + * + * The configUSE_TIMERS configuration constant must be set to 1 for + * xTimerDelete() to be available. + * + * @param xTimer The handle of the timer being deleted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the delete command to be + * successfully sent to the timer command queue, should the queue already be + * full when xTimerDelete() was called. xTicksToWait is ignored if xTimerDelete() + * is called before the scheduler is started. + * + * @return pdFAIL will be returned if the delete command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerChangePeriod() API function example usage scenario. + */ +#define xTimerDelete( xTimer, xTicksToWait ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerReset() re-starts a timer that was previously created using the + * xTimerCreate() API function. If the timer had already been started and was + * already in the active state, then xTimerReset() will cause the timer to + * re-evaluate its expiry time so that it is relative to when xTimerReset() was + * called. If the timer was in the dormant state then xTimerReset() has + * equivalent functionality to the xTimerStart() API function. + * + * Resetting a timer ensures the timer is in the active state. If the timer + * is not stopped, deleted, or reset in the mean time, the callback function + * associated with the timer will get called 'n' ticks after xTimerReset() was + * called, where 'n' is the timers defined period. + * + * It is valid to call xTimerReset() before the scheduler has been started, but + * when this is done the timer will not actually start until the scheduler is + * started, and the timers expiry time will be relative to when the scheduler is + * started, not relative to when xTimerReset() was called. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerReset() + * to be available. + * + * @param xTimer The handle of the timer being reset/started/restarted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the reset command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerReset() was called. xTicksToWait is ignored if xTimerReset() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the reset command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system, although the + * timers expiry time is relative to when xTimerStart() is actually called. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * @verbatim + * // When a key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer. + * + * TimerHandle_t xBacklightTimer = NULL; + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press event handler. + * void vKeyPressEventHandler( char cKey ) + * { + * // Reset the timer that is responsible for turning the back-light off after + * // 5 seconds of key inactivity. Wait 10 ticks for the command to be + * // successfully sent if it cannot be sent immediately. + * if( xTimerReset( xBacklightTimer, 10 ) == pdPASS ) + * { + * // Turn on the LCD back-light. It will be turned off in the + * // vBacklightTimerCallback after 5 seconds of key inactivity. + * vSetBacklightState( BACKLIGHT_ON ); + * } + * else + * { + * // The reset command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * } + * + * void main( void ) + * { + * + * // Create then start the one-shot timer that is responsible for turning + * // the back-light off if no keys are pressed within a 5 second period. + * xBacklightTimer = xTimerCreate( "BacklightTimer", // Just a text name, not used by the kernel. + * pdMS_TO_TICKS( 5000 ), // The timer period in ticks. + * pdFALSE, // The timer is a one-shot timer. + * 0, // The id is not used by the callback so can take any value. + * vBacklightTimerCallback // The callback function that switches the LCD back-light off. + * ); + * + * if( xBacklightTimer == NULL ) + * { + * // The timer was not created. + * } + * else + * { + * // Start the timer. No block time is specified, and even if one was + * // it would be ignored because the scheduler has not yet been + * // started. + * if( xTimerStart( xBacklightTimer, 0 ) != pdPASS ) + * { + * // The timer could not be set into the Active state. + * } + * } + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timer running as it has already + * // been set into the active state. + * vTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ +#define xTimerReset( xTimer, xTicksToWait ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerStart() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer being started/restarted. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerStartFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerStartFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerStartFromISR() function. If + * xTimerStartFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the start command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system, although the timers expiry time is + * relative to when xTimerStartFromISR() is actually called. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xBacklightTimer has already been created. When a + * // key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer, and unlike the example given for + * // the xTimerReset() function, the key press event handler is an interrupt + * // service routine. + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press interrupt service routine. + * void vKeyPressEventInterruptHandler( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // Ensure the LCD back-light is on, then restart the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. This is an interrupt service routine so can only + * // call FreeRTOS API functions that end in "FromISR". + * vSetBacklightState( BACKLIGHT_ON ); + * + * // xTimerStartFromISR() or xTimerResetFromISR() could be called here + * // as both cause the timer to re-calculate its expiry time. + * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was + * // declared (in this function). + * if( xTimerStartFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The start command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerStopFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerStop() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer being stopped. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerStopFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerStopFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerStopFromISR() function. If + * xTimerStopFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the stop command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system. The timer service/daemon task + * priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xTimer has already been created and started. When + * // an interrupt occurs, the timer should be simply stopped. + * + * // The interrupt service routine that stops the timer. + * void vAnExampleInterruptServiceRoutine( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // The interrupt has occurred - simply stop the timer. + * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined + * // (within this function). As this is an interrupt service routine, only + * // FreeRTOS API functions that end in "FromISR" can be used. + * if( xTimerStopFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The stop command was not executed successfully. Take appropriate + * // action here. + * } + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer, + * TickType_t xNewPeriod, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerChangePeriod() that can be called from an interrupt + * service routine. + * + * @param xTimer The handle of the timer that is having its period changed. + * + * @param xNewPeriod The new period for xTimer. Timer periods are specified in + * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a time + * that has been specified in milliseconds. For example, if the timer must + * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, + * if the timer must expire after 500ms, then xNewPeriod can be set to + * ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than + * or equal to 1000. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerChangePeriodFromISR() writes a message to the + * timer command queue, so has the potential to transition the timer service/ + * daemon task out of the Blocked state. If calling xTimerChangePeriodFromISR() + * causes the timer service/daemon task to leave the Blocked state, and the + * timer service/daemon task has a priority equal to or greater than the + * currently executing task (the task that was interrupted), then + * *pxHigherPriorityTaskWoken will get set to pdTRUE internally within the + * xTimerChangePeriodFromISR() function. If xTimerChangePeriodFromISR() sets + * this value to pdTRUE then a context switch should be performed before the + * interrupt exits. + * + * @return pdFAIL will be returned if the command to change the timers period + * could not be sent to the timer command queue. pdPASS will be returned if the + * command was successfully sent to the timer command queue. When the command + * is actually processed will depend on the priority of the timer service/daemon + * task relative to other tasks in the system. The timer service/daemon task + * priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xTimer has already been created and started. When + * // an interrupt occurs, the period of xTimer should be changed to 500ms. + * + * // The interrupt service routine that changes the period of xTimer. + * void vAnExampleInterruptServiceRoutine( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // The interrupt has occurred - change the period of xTimer to 500ms. + * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined + * // (within this function). As this is an interrupt service routine, only + * // FreeRTOS API functions that end in "FromISR" can be used. + * if( xTimerChangePeriodFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The command to change the timers period was not executed + * // successfully. Take appropriate action here. + * } + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD_FROM_ISR, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerResetFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerReset() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer that is to be started, reset, or + * restarted. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerResetFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerResetFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerResetFromISR() function. If + * xTimerResetFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the reset command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system, although the timers expiry time is + * relative to when xTimerResetFromISR() is actually called. The timer service/daemon + * task priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xBacklightTimer has already been created. When a + * // key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer, and unlike the example given for + * // the xTimerReset() function, the key press event handler is an interrupt + * // service routine. + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press interrupt service routine. + * void vKeyPressEventInterruptHandler( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // Ensure the LCD back-light is on, then reset the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. This is an interrupt service routine so can only + * // call FreeRTOS API functions that end in "FromISR". + * vSetBacklightState( BACKLIGHT_ON ); + * + * // xTimerStartFromISR() or xTimerResetFromISR() could be called here + * // as both cause the timer to re-calculate its expiry time. + * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was + * // declared (in this function). + * if( xTimerResetFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The reset command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) \ + xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) + + +/** + * BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, + * void *pvParameter1, + * uint32_t ulParameter2, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * + * Used from application interrupt service routines to defer the execution of a + * function to the RTOS daemon task (the timer service task, hence this function + * is implemented in timers.c and is prefixed with 'Timer'). + * + * Ideally an interrupt service routine (ISR) is kept as short as possible, but + * sometimes an ISR either has a lot of processing to do, or needs to perform + * processing that is not deterministic. In these cases + * xTimerPendFunctionCallFromISR() can be used to defer processing of a function + * to the RTOS daemon task. + * + * A mechanism is provided that allows the interrupt to return directly to the + * task that will subsequently execute the pended callback function. This + * allows the callback function to execute contiguously in time with the + * interrupt - just as if the callback had executed in the interrupt itself. + * + * @param xFunctionToPend The function to execute from the timer service/ + * daemon task. The function must conform to the PendedFunction_t + * prototype. + * + * @param pvParameter1 The value of the callback function's first parameter. + * The parameter has a void * type to allow it to be used to pass any type. + * For example, unsigned longs can be cast to a void *, or the void * can be + * used to point to a structure. + * + * @param ulParameter2 The value of the callback function's second parameter. + * + * @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 (which is set using + * configTIMER_TASK_PRIORITY in FreeRTOSConfig.h) is higher than the priority of + * the currently running task (the task the interrupt interrupted) then + * *pxHigherPriorityTaskWoken will be set to pdTRUE within + * xTimerPendFunctionCallFromISR(), 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 pdPASS is returned if the message was successfully sent to the + * timer daemon task, otherwise pdFALSE is returned. + * + * Example usage: + * @verbatim + * + * // The callback function that will execute in the context of the daemon task. + * // Note callback functions must all use this same prototype. + * void vProcessInterface( void *pvParameter1, uint32_t ulParameter2 ) + * { + * BaseType_t xInterfaceToService; + * + * // The interface that requires servicing is passed in the second + * // parameter. The first parameter is not used in this case. + * xInterfaceToService = ( BaseType_t ) ulParameter2; + * + * // ...Perform the processing here... + * } + * + * // An ISR that receives data packets from multiple interfaces + * void vAnISR( void ) + * { + * BaseType_t xInterfaceToService, xHigherPriorityTaskWoken; + * + * // Query the hardware to determine which interface needs processing. + * xInterfaceToService = prvCheckInterfaces(); + * + * // The actual processing is to be deferred to a task. Request the + * // vProcessInterface() callback function is executed, passing in the + * // number of the interface that needs processing. The interface to + * // service is passed in the second parameter. The first parameter is + * // not used in this case. + * xHigherPriorityTaskWoken = pdFALSE; + * xTimerPendFunctionCallFromISR( vProcessInterface, NULL, ( uint32_t ) xInterfaceToService, &xHigherPriorityTaskWoken ); + * + * // 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 ); + * + * } + * @endverbatim + */ +#if ( INCLUDE_xTimerPendFunctionCall == 1 ) + BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, + void * pvParameter1, + uint32_t ulParameter2, + BaseType_t * pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; +#endif + +/** + * BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, + * void *pvParameter1, + * uint32_t ulParameter2, + * TickType_t xTicksToWait ); + * + * + * Used to defer the execution of a function to the RTOS daemon task (the timer + * service task, hence this function is implemented in timers.c and is prefixed + * with 'Timer'). + * + * @param xFunctionToPend The function to execute from the timer service/ + * daemon task. The function must conform to the PendedFunction_t + * prototype. + * + * @param pvParameter1 The value of the callback function's first parameter. + * The parameter has a void * type to allow it to be used to pass any type. + * For example, unsigned longs can be cast to a void *, or the void * can be + * used to point to a structure. + * + * @param ulParameter2 The value of the callback function's second parameter. + * + * @param xTicksToWait Calling this function will result in a message being + * sent to the timer daemon task on a queue. xTicksToWait is the amount of + * time the calling task should remain in the Blocked state (so not using any + * processing time) for space to become available on the timer queue if the + * queue is found to be full. + * + * @return pdPASS is returned if the message was successfully sent to the + * timer daemon task, otherwise pdFALSE is returned. + * + */ +#if ( INCLUDE_xTimerPendFunctionCall == 1 ) + BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, + void * pvParameter1, + uint32_t ulParameter2, + TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +#endif + +/** + * const char * const pcTimerGetName( TimerHandle_t xTimer ); + * + * Returns the name that was assigned to a timer when the timer was created. + * + * @param xTimer The handle of the timer being queried. + * + * @return The name assigned to the timer specified by the xTimer parameter. + */ +const char * pcTimerGetName( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * void vTimerSetReloadMode( TimerHandle_t xTimer, const BaseType_t xAutoReload ); + * + * Updates a timer to be either an auto-reload timer, in which case the timer + * automatically resets itself each time it expires, or a one-shot timer, in + * which case the timer will only expire once unless it is manually restarted. + * + * @param xTimer The handle of the timer being updated. + * + * @param xAutoReload If xAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the timer's period (see the + * xTimerPeriodInTicks parameter of the xTimerCreate() API function). If + * xAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + */ +void vTimerSetReloadMode( TimerHandle_t xTimer, + const BaseType_t xAutoReload ) PRIVILEGED_FUNCTION; + +/** + * BaseType_t xTimerGetReloadMode( TimerHandle_t xTimer ); + * + * Queries a timer to determine if it is an auto-reload timer, in which case the timer + * automatically resets itself each time it expires, or a one-shot timer, in + * which case the timer will only expire once unless it is manually restarted. + * + * @param xTimer The handle of the timer being queried. + * + * @return If the timer is an auto-reload timer then pdTRUE is returned, otherwise + * pdFALSE is returned. + */ +BaseType_t xTimerGetReloadMode( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ); + * + * Queries a timer to determine if it is an auto-reload timer, in which case the timer + * automatically resets itself each time it expires, or a one-shot timer, in + * which case the timer will only expire once unless it is manually restarted. + * + * @param xTimer The handle of the timer being queried. + * + * @return If the timer is an auto-reload timer then pdTRUE is returned, otherwise + * pdFALSE is returned. + */ +UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); + * + * Returns the period of a timer. + * + * @param xTimer The handle of the timer being queried. + * + * @return The period of the timer in ticks. + */ +TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ); + * + * Returns the time in ticks at which the timer will expire. If this is less + * than the current tick count then the expiry time has overflowed from the + * current time. + * + * @param xTimer The handle of the timer being queried. + * + * @return If the timer is running then the time in ticks at which the timer + * will next expire is returned. If the timer is not running then the return + * value is undefined. + */ +TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * BaseType_t xTimerGetStaticBuffer( TimerHandle_t xTimer, + * StaticTimer_t ** ppxTimerBuffer ); + * + * Retrieve pointer to a statically created timer's data structure + * buffer. This is the same buffer that is supplied at the time of + * creation. + * + * @param xTimer The timer for which to retrieve the buffer. + * + * @param ppxTaskBuffer Used to return a pointer to the timers's data + * structure buffer. + * + * @return pdTRUE if the buffer was retrieved, pdFALSE otherwise. + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + BaseType_t xTimerGetStaticBuffer( TimerHandle_t xTimer, + StaticTimer_t ** ppxTimerBuffer ) PRIVILEGED_FUNCTION; +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/* + * Functions beyond this part are not part of the public API and are intended + * for use by the kernel only. + */ +BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION; + +/* + * Splitting the xTimerGenericCommand into two sub functions and making it a macro + * removes a recursion path when called from ISRs. This is primarily for the XCore + * XCC port which detects the recursion path and throws an error during compilation + * when this is not split. + */ +BaseType_t xTimerGenericCommandFromTask( TimerHandle_t xTimer, + const BaseType_t xCommandID, + const TickType_t xOptionalValue, + BaseType_t * const pxHigherPriorityTaskWoken, + const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +BaseType_t xTimerGenericCommandFromISR( TimerHandle_t xTimer, + const BaseType_t xCommandID, + const TickType_t xOptionalValue, + BaseType_t * const pxHigherPriorityTaskWoken, + const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +#define xTimerGenericCommand( xTimer, xCommandID, xOptionalValue, pxHigherPriorityTaskWoken, xTicksToWait ) \ + ( ( xCommandID ) < tmrFIRST_FROM_ISR_COMMAND ? \ + xTimerGenericCommandFromTask( xTimer, xCommandID, xOptionalValue, pxHigherPriorityTaskWoken, xTicksToWait ) : \ + xTimerGenericCommandFromISR( xTimer, xCommandID, xOptionalValue, pxHigherPriorityTaskWoken, xTicksToWait ) ) +#if ( configUSE_TRACE_FACILITY == 1 ) + void vTimerSetTimerNumber( TimerHandle_t xTimer, + UBaseType_t uxTimerNumber ) PRIVILEGED_FUNCTION; + UBaseType_t uxTimerGetTimerNumber( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; +#endif + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + +/** + * task.h + * @code{c} + * void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer, StackType_t ** ppxTimerTaskStackBuffer, configSTACK_DEPTH_TYPE * puxTimerTaskStackSize ) + * @endcode + * + * This function is used to provide a statically allocated block of memory to FreeRTOS to hold the Timer Task TCB. This function is required when + * configSUPPORT_STATIC_ALLOCATION is set. For more information see this URI: https://www.FreeRTOS.org/a00110.html#configSUPPORT_STATIC_ALLOCATION + * + * @param ppxTimerTaskTCBBuffer A handle to a statically allocated TCB buffer + * @param ppxTimerTaskStackBuffer A handle to a statically allocated Stack buffer for the idle task + * @param puxTimerTaskStackSize A pointer to the number of elements that will fit in the allocated stack buffer + */ + void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer, + StackType_t ** ppxTimerTaskStackBuffer, + configSTACK_DEPTH_TYPE * puxTimerTaskStackSize ); + +#endif + +#if ( configUSE_DAEMON_TASK_STARTUP_HOOK != 0 ) + +/** + * timers.h + * @code{c} + * void vApplicationDaemonTaskStartupHook( void ); + * @endcode + * + * This hook function is called form the timer task once when the task starts running. + */ + /* MISRA Ref 8.6.1 [External linkage] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-86 */ + /* coverity[misra_c_2012_rule_8_6_violation] */ + void vApplicationDaemonTaskStartupHook( void ); + +#endif + +/* + * This function resets the internal state of the timer module. It must be called + * by the application before restarting the scheduler. + */ +void vTimerResetState( void ) PRIVILEGED_FUNCTION; + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ +#endif /* TIMERS_H */ diff --git a/vendor/FreeRTOS-Kernel/list.c b/vendor/FreeRTOS-Kernel/list.c new file mode 100644 index 0000000..5cdc862 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/list.c @@ -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 + +/* 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; +} +/*-----------------------------------------------------------*/ diff --git a/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/port.c b/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/port.c new file mode 100644 index 0000000..3efa1c5 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/port.c @@ -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( ; ; ) + { + } +} +/*-----------------------------------------------------------*/ diff --git a/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portASM.S b/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portASM.S new file mode 100644 index 0000000..949ace3 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portASM.S @@ -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 +/*-----------------------------------------------------------*/ diff --git a/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portContext.h b/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portContext.h new file mode 100644 index 0000000..b211df4 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portContext.h @@ -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 */ diff --git a/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portmacro.h b/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portmacro.h new file mode 100644 index 0000000..5ba2f8e --- /dev/null +++ b/vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portmacro.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 */ diff --git a/vendor/FreeRTOS-Kernel/portable/MemMang/heap_4.c b/vendor/FreeRTOS-Kernel/portable/MemMang/heap_4.c new file mode 100644 index 0000000..a2b93af --- /dev/null +++ b/vendor/FreeRTOS-Kernel/portable/MemMang/heap_4.c @@ -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 +#include + +/* 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; +} +/*-----------------------------------------------------------*/ diff --git a/vendor/FreeRTOS-Kernel/queue.c b/vendor/FreeRTOS-Kernel/queue.c new file mode 100644 index 0000000..dd302c9 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/queue.c @@ -0,0 +1,3364 @@ +/* + * FreeRTOS Kernel + * 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 +#include + +/* 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" +#include "queue.h" + +#if ( configUSE_CO_ROUTINES == 1 ) + #include "croutine.h" +#endif + +/* 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 + + +/* Constants used with the cRxLock and cTxLock structure members. */ +#define queueUNLOCKED ( ( int8_t ) -1 ) +#define queueLOCKED_UNMODIFIED ( ( int8_t ) 0 ) +#define queueINT8_MAX ( ( int8_t ) 127 ) + +/* When the Queue_t structure is used to represent a base queue its pcHead and + * pcTail members are used as pointers into the queue storage area. When the + * Queue_t structure is used to represent a mutex pcHead and pcTail pointers are + * not necessary, and the pcHead pointer is set to NULL to indicate that the + * structure instead holds a pointer to the mutex holder (if any). Map alternative + * names to the pcHead and structure member to ensure the readability of the code + * is maintained. The QueuePointers_t and SemaphoreData_t types are used to form + * a union as their usage is mutually exclusive dependent on what the queue is + * being used for. */ +#define uxQueueType pcHead +#define queueQUEUE_IS_MUTEX NULL + +typedef struct QueuePointers +{ + int8_t * pcTail; /**< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ + int8_t * pcReadFrom; /**< Points to the last place that a queued item was read from when the structure is used as a queue. */ +} QueuePointers_t; + +typedef struct SemaphoreData +{ + TaskHandle_t xMutexHolder; /**< The handle of the task that holds the mutex. */ + UBaseType_t uxRecursiveCallCount; /**< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */ +} SemaphoreData_t; + +/* Semaphores do not actually store or copy data, so have an item size of + * zero. */ +#define queueSEMAPHORE_QUEUE_ITEM_LENGTH ( ( UBaseType_t ) 0 ) +#define queueMUTEX_GIVE_BLOCK_TIME ( ( TickType_t ) 0U ) + +#if ( configUSE_PREEMPTION == 0 ) + +/* If the cooperative scheduler is being used then a yield should not be + * performed just because a higher priority task has been woken. */ + #define queueYIELD_IF_USING_PREEMPTION() +#else + #if ( configNUMBER_OF_CORES == 1 ) + #define queueYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + #define queueYIELD_IF_USING_PREEMPTION() vTaskYieldWithinAPI() + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ +#endif + +/* + * Definition of the queue used by the scheduler. + * Items are queued by copy, not reference. See the following link for the + * rationale: https://www.FreeRTOS.org/Embedded-RTOS-Queues.html + */ +typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +{ + int8_t * pcHead; /**< Points to the beginning of the queue storage area. */ + int8_t * pcWriteTo; /**< Points to the free next place in the storage area. */ + + union + { + QueuePointers_t xQueue; /**< Data required exclusively when this structure is used as a queue. */ + SemaphoreData_t xSemaphore; /**< Data required exclusively when this structure is used as a semaphore. */ + } u; + + List_t xTasksWaitingToSend; /**< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ + List_t xTasksWaitingToReceive; /**< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */ + + volatile UBaseType_t uxMessagesWaiting; /**< The number of items currently in the queue. */ + UBaseType_t uxLength; /**< The length of the queue defined as the number of items it will hold, not the number of bytes. */ + UBaseType_t uxItemSize; /**< The size of each items that the queue will hold. */ + + volatile int8_t cRxLock; /**< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + volatile int8_t cTxLock; /**< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + + #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucStaticallyAllocated; /**< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */ + #endif + + #if ( configUSE_QUEUE_SETS == 1 ) + struct QueueDefinition * pxQueueSetContainer; + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxQueueNumber; + uint8_t ucQueueType; + #endif +} xQUEUE; + +/* The old xQUEUE name is maintained above then typedefed to the new Queue_t + * name below to enable the use of older kernel aware debuggers. */ +typedef xQUEUE Queue_t; + +/*-----------------------------------------------------------*/ + +/* + * The queue registry is just a means for kernel aware debuggers to locate + * queue structures. It has no other purpose so is an optional component. + */ +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + +/* The type stored within the queue registry array. This allows a name + * to be assigned to each queue making kernel aware debugging a little + * more user friendly. */ + typedef struct QUEUE_REGISTRY_ITEM + { + const char * pcQueueName; + QueueHandle_t xHandle; + } xQueueRegistryItem; + +/* The old xQueueRegistryItem name is maintained above then typedefed to the + * new xQueueRegistryItem name below to enable the use of older kernel aware + * debuggers. */ + typedef xQueueRegistryItem QueueRegistryItem_t; + +/* The queue registry is simply an array of QueueRegistryItem_t structures. + * The pcQueueName member of a structure being NULL is indicative of the + * array position being vacant. */ + +/* MISRA Ref 8.4.2 [Declaration shall be visible] */ +/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-84 */ +/* coverity[misra_c_2012_rule_8_4_violation] */ + PRIVILEGED_DATA QueueRegistryItem_t xQueueRegistry[ configQUEUE_REGISTRY_SIZE ]; + +#endif /* configQUEUE_REGISTRY_SIZE */ + +/* + * Unlocks a queue locked by a call to prvLockQueue. Locking a queue does not + * prevent an ISR from adding or removing items to the queue, but does prevent + * an ISR from removing tasks from the queue event lists. If an ISR finds a + * queue is locked it will instead increment the appropriate queue lock count + * to indicate that a task may require unblocking. When the queue in unlocked + * these lock counts are inspected, and the appropriate action taken. + */ +static void prvUnlockQueue( Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Uses a critical section to determine if there is any data in a queue. + * + * @return pdTRUE if the queue contains no items, otherwise pdFALSE. + */ +static BaseType_t prvIsQueueEmpty( const Queue_t * pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Uses a critical section to determine if there is any space in a queue. + * + * @return pdTRUE if there is no space, otherwise pdFALSE; + */ +static BaseType_t prvIsQueueFull( const Queue_t * pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Copies an item into the queue, either at the front of the queue or the + * back of the queue. + */ +static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, + const void * pvItemToQueue, + const BaseType_t xPosition ) PRIVILEGED_FUNCTION; + +/* + * Copies an item out of a queue. + */ +static void prvCopyDataFromQueue( Queue_t * const pxQueue, + void * const pvBuffer ) PRIVILEGED_FUNCTION; + +#if ( configUSE_QUEUE_SETS == 1 ) + +/* + * Checks to see if a queue is a member of a queue set, and if so, notifies + * the queue set that the queue contains data. + */ + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; +#endif + +/* + * Called after a Queue_t structure has been allocated either statically or + * dynamically to fill in the structure's members. + */ +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + uint8_t * pucQueueStorage, + const uint8_t ucQueueType, + Queue_t * pxNewQueue ) PRIVILEGED_FUNCTION; + +/* + * Mutexes are a special type of queue. When a mutex is created, first the + * queue is created, then prvInitialiseMutex() is called to configure the queue + * as a mutex. + */ +#if ( configUSE_MUTEXES == 1 ) + static void prvInitialiseMutex( Queue_t * pxNewQueue ) PRIVILEGED_FUNCTION; +#endif + +#if ( configUSE_MUTEXES == 1 ) + +/* + * If a task waiting for a mutex causes the mutex holder to inherit a + * priority, but the waiting task times out, then the holder should + * disinherit the priority - but only down to the highest priority of any + * other tasks that are waiting for the same mutex. This function returns + * that priority. + */ + static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; +#endif +/*-----------------------------------------------------------*/ + +/* + * Macro to mark a queue as locked. Locking a queue prevents an ISR from + * accessing the queue event lists. + */ +#define prvLockQueue( pxQueue ) \ + taskENTER_CRITICAL(); \ + { \ + if( ( pxQueue )->cRxLock == queueUNLOCKED ) \ + { \ + ( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED; \ + } \ + if( ( pxQueue )->cTxLock == queueUNLOCKED ) \ + { \ + ( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED; \ + } \ + } \ + taskEXIT_CRITICAL() + +/* + * Macro to increment cTxLock member of the queue data structure. It is + * capped at the number of tasks in the system as we cannot unblock more + * tasks than the number of tasks in the system. + */ +#define prvIncrementQueueTxLock( pxQueue, cTxLock ) \ + do { \ + const UBaseType_t uxNumberOfTasks = uxTaskGetNumberOfTasks(); \ + if( ( UBaseType_t ) ( cTxLock ) < uxNumberOfTasks ) \ + { \ + configASSERT( ( cTxLock ) != queueINT8_MAX ); \ + ( pxQueue )->cTxLock = ( int8_t ) ( ( cTxLock ) + ( int8_t ) 1 ); \ + } \ + } while( 0 ) + +/* + * Macro to increment cRxLock member of the queue data structure. It is + * capped at the number of tasks in the system as we cannot unblock more + * tasks than the number of tasks in the system. + */ +#define prvIncrementQueueRxLock( pxQueue, cRxLock ) \ + do { \ + const UBaseType_t uxNumberOfTasks = uxTaskGetNumberOfTasks(); \ + if( ( UBaseType_t ) ( cRxLock ) < uxNumberOfTasks ) \ + { \ + configASSERT( ( cRxLock ) != queueINT8_MAX ); \ + ( pxQueue )->cRxLock = ( int8_t ) ( ( cRxLock ) + ( int8_t ) 1 ); \ + } \ + } while( 0 ) +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGenericReset( QueueHandle_t xQueue, + BaseType_t xNewQueue ) +{ + BaseType_t xReturn = pdPASS; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueGenericReset( xQueue, xNewQueue ); + + configASSERT( pxQueue ); + + if( ( pxQueue != NULL ) && + ( pxQueue->uxLength >= 1U ) && + /* Check for multiplication overflow. */ + ( ( SIZE_MAX / pxQueue->uxLength ) >= pxQueue->uxItemSize ) ) + { + taskENTER_CRITICAL(); + { + pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); + pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; + pxQueue->pcWriteTo = pxQueue->pcHead; + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 1U ) * pxQueue->uxItemSize ); + pxQueue->cRxLock = queueUNLOCKED; + pxQueue->cTxLock = queueUNLOCKED; + + if( xNewQueue == pdFALSE ) + { + /* If there are tasks blocked waiting to read from the queue, then + * the tasks will remain blocked as after this function exits the queue + * will still be empty. If there are tasks blocked waiting to write to + * the queue, then one should be unblocked as after this function exits + * it will be possible to write to it. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Ensure the event queues start in the correct state. */ + vListInitialise( &( pxQueue->xTasksWaitingToSend ) ); + vListInitialise( &( pxQueue->xTasksWaitingToReceive ) ); + } + } + taskEXIT_CRITICAL(); + } + else + { + xReturn = pdFAIL; + } + + configASSERT( xReturn != pdFAIL ); + + /* A value is returned for calling semantic consistency with previous + * versions. */ + traceRETURN_xQueueGenericReset( xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + uint8_t * pucQueueStorage, + StaticQueue_t * pxStaticQueue, + const uint8_t ucQueueType ) + { + Queue_t * pxNewQueue = NULL; + + traceENTER_xQueueGenericCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxStaticQueue, ucQueueType ); + + /* The StaticQueue_t structure and the queue storage area must be + * supplied. */ + configASSERT( pxStaticQueue ); + + if( ( uxQueueLength > ( UBaseType_t ) 0 ) && + ( pxStaticQueue != NULL ) && + + /* A queue storage area should be provided if the item size is not 0, and + * should not be provided if the item size is 0. */ + ( !( ( pucQueueStorage != NULL ) && ( uxItemSize == 0U ) ) ) && + ( !( ( pucQueueStorage == NULL ) && ( uxItemSize != 0U ) ) ) ) + { + #if ( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + * variable of type StaticQueue_t or StaticSemaphore_t equals the size of + * the real queue and semaphore structures. */ + volatile size_t xSize = sizeof( StaticQueue_t ); + + /* This assertion cannot be branch covered in unit tests */ + configASSERT( xSize == sizeof( Queue_t ) ); /* LCOV_EXCL_BR_LINE */ + ( void ) xSize; /* Prevent unused variable warning when configASSERT() is not defined. */ + } + #endif /* configASSERT_DEFINED */ + + /* The address of a statically allocated queue was passed in, use it. + * The address of a statically allocated storage area was also passed in + * but is already set. */ + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxNewQueue = ( Queue_t * ) pxStaticQueue; + + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Queues can be allocated wither statically or dynamically, so + * note this queue was allocated statically in case the queue is + * later deleted. */ + pxNewQueue->ucStaticallyAllocated = pdTRUE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); + } + else + { + configASSERT( pxNewQueue ); + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_xQueueGenericCreateStatic( pxNewQueue ); + + return pxNewQueue; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + BaseType_t xQueueGenericGetStaticBuffers( QueueHandle_t xQueue, + uint8_t ** ppucQueueStorage, + StaticQueue_t ** ppxStaticQueue ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueGenericGetStaticBuffers( xQueue, ppucQueueStorage, ppxStaticQueue ); + + configASSERT( pxQueue ); + configASSERT( ppxStaticQueue ); + + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Check if the queue was statically allocated. */ + if( pxQueue->ucStaticallyAllocated == ( uint8_t ) pdTRUE ) + { + if( ppucQueueStorage != NULL ) + { + *ppucQueueStorage = ( uint8_t * ) pxQueue->pcHead; + } + + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + *ppxStaticQueue = ( StaticQueue_t * ) pxQueue; + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + #else /* configSUPPORT_DYNAMIC_ALLOCATION */ + { + /* Queue must have been statically allocated. */ + if( ppucQueueStorage != NULL ) + { + *ppucQueueStorage = ( uint8_t * ) pxQueue->pcHead; + } + + *ppxStaticQueue = ( StaticQueue_t * ) pxQueue; + xReturn = pdTRUE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + traceRETURN_xQueueGenericGetStaticBuffers( xReturn ); + + return xReturn; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + const uint8_t ucQueueType ) + { + Queue_t * pxNewQueue = NULL; + size_t xQueueSizeInBytes; + uint8_t * pucQueueStorage; + + traceENTER_xQueueGenericCreate( uxQueueLength, uxItemSize, ucQueueType ); + + if( ( uxQueueLength > ( UBaseType_t ) 0 ) && + /* Check for multiplication overflow. */ + ( ( SIZE_MAX / uxQueueLength ) >= uxItemSize ) && + /* Check for addition overflow. */ + ( ( UBaseType_t ) ( SIZE_MAX - sizeof( Queue_t ) ) >= ( uxQueueLength * uxItemSize ) ) ) + { + /* Allocate enough space to hold the maximum number of items that + * can be in the queue at any time. It is valid for uxItemSize to be + * zero in the case the queue is used as a semaphore. */ + xQueueSizeInBytes = ( size_t ) ( ( size_t ) uxQueueLength * ( size_t ) uxItemSize ); + + /* MISRA Ref 11.5.1 [Malloc memory assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); + + if( pxNewQueue != NULL ) + { + /* Jump past the queue structure to find the location of the queue + * storage area. */ + pucQueueStorage = ( uint8_t * ) pxNewQueue; + pucQueueStorage += sizeof( Queue_t ); + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* Queues can be created either statically or dynamically, so + * note this task was created dynamically in case it is later + * deleted. */ + pxNewQueue->ucStaticallyAllocated = pdFALSE; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); + } + else + { + traceQUEUE_CREATE_FAILED( ucQueueType ); + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + configASSERT( pxNewQueue ); + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_xQueueGenericCreate( pxNewQueue ); + + return pxNewQueue; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, + const UBaseType_t uxItemSize, + uint8_t * pucQueueStorage, + const uint8_t ucQueueType, + Queue_t * pxNewQueue ) +{ + /* Remove compiler warnings about unused parameters should + * configUSE_TRACE_FACILITY not be set to 1. */ + ( void ) ucQueueType; + + if( uxItemSize == ( UBaseType_t ) 0 ) + { + /* No RAM was allocated for the queue storage area, but PC head cannot + * be set to NULL because NULL is used as a key to say the queue is used as + * a mutex. Therefore just set pcHead to point to the queue as a benign + * value that is known to be within the memory map. */ + pxNewQueue->pcHead = ( int8_t * ) pxNewQueue; + } + else + { + /* Set the head to the start of the queue storage area. */ + pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage; + } + + /* Initialise the queue members as described where the queue type is + * defined. */ + pxNewQueue->uxLength = uxQueueLength; + pxNewQueue->uxItemSize = uxItemSize; + ( void ) xQueueGenericReset( pxNewQueue, pdTRUE ); + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + pxNewQueue->ucQueueType = ucQueueType; + } + #endif /* configUSE_TRACE_FACILITY */ + + #if ( configUSE_QUEUE_SETS == 1 ) + { + pxNewQueue->pxQueueSetContainer = NULL; + } + #endif /* configUSE_QUEUE_SETS */ + + traceQUEUE_CREATE( pxNewQueue ); +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + static void prvInitialiseMutex( Queue_t * pxNewQueue ) + { + if( pxNewQueue != NULL ) + { + /* The queue create function will set all the queue structure members + * correctly for a generic queue, but this function is creating a + * mutex. Overwrite those members that need to be set differently - + * in particular the information required for priority inheritance. */ + pxNewQueue->u.xSemaphore.xMutexHolder = NULL; + pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; + + /* In case this is a recursive mutex. */ + pxNewQueue->u.xSemaphore.uxRecursiveCallCount = 0; + + traceCREATE_MUTEX( pxNewQueue ); + + /* Start with the semaphore in the expected state. */ + ( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK ); + } + else + { + traceCREATE_MUTEX_FAILED(); + } + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) + { + QueueHandle_t xNewQueue; + const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; + + traceENTER_xQueueCreateMutex( ucQueueType ); + + xNewQueue = xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); + prvInitialiseMutex( ( Queue_t * ) xNewQueue ); + + traceRETURN_xQueueCreateMutex( xNewQueue ); + + return xNewQueue; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, + StaticQueue_t * pxStaticQueue ) + { + QueueHandle_t xNewQueue; + const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; + + traceENTER_xQueueCreateMutexStatic( ucQueueType, pxStaticQueue ); + + /* Prevent compiler warnings about unused parameters if + * configUSE_TRACE_FACILITY does not equal 1. */ + ( void ) ucQueueType; + + xNewQueue = xQueueGenericCreateStatic( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType ); + prvInitialiseMutex( ( Queue_t * ) xNewQueue ); + + traceRETURN_xQueueCreateMutexStatic( xNewQueue ); + + return xNewQueue; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) + + TaskHandle_t xQueueGetMutexHolder( QueueHandle_t xSemaphore ) + { + TaskHandle_t pxReturn; + Queue_t * const pxSemaphore = ( Queue_t * ) xSemaphore; + + traceENTER_xQueueGetMutexHolder( xSemaphore ); + + configASSERT( xSemaphore ); + + /* This function is called by xSemaphoreGetMutexHolder(), and should not + * be called directly. Note: This is a good way of determining if the + * calling task is the mutex holder, but not a good way of determining the + * identity of the mutex holder, as the holder may change between the + * following critical section exiting and the function returning. */ + taskENTER_CRITICAL(); + { + if( pxSemaphore->uxQueueType == queueQUEUE_IS_MUTEX ) + { + pxReturn = pxSemaphore->u.xSemaphore.xMutexHolder; + } + else + { + pxReturn = NULL; + } + } + taskEXIT_CRITICAL(); + + traceRETURN_xQueueGetMutexHolder( pxReturn ); + + return pxReturn; + } + +#endif /* if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) + + TaskHandle_t xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) + { + TaskHandle_t pxReturn; + + traceENTER_xQueueGetMutexHolderFromISR( xSemaphore ); + + configASSERT( xSemaphore ); + + /* Mutexes cannot be used in interrupt service routines, so the mutex + * holder should not change in an ISR, and therefore a critical section is + * not required here. */ + if( ( ( Queue_t * ) xSemaphore )->uxQueueType == queueQUEUE_IS_MUTEX ) + { + pxReturn = ( ( Queue_t * ) xSemaphore )->u.xSemaphore.xMutexHolder; + } + else + { + pxReturn = NULL; + } + + traceRETURN_xQueueGetMutexHolderFromISR( pxReturn ); + + return pxReturn; + } + +#endif /* if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) + + BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) + { + BaseType_t xReturn; + Queue_t * const pxMutex = ( Queue_t * ) xMutex; + + traceENTER_xQueueGiveMutexRecursive( xMutex ); + + configASSERT( pxMutex ); + + /* If this is the task that holds the mutex then xMutexHolder will not + * change outside of this task. If this task does not hold the mutex then + * pxMutexHolder can never coincidentally equal the tasks handle, and as + * this is the only condition we are interested in it does not matter if + * pxMutexHolder is accessed simultaneously by another task. Therefore no + * mutual exclusion is required to test the pxMutexHolder variable. */ + if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) + { + traceGIVE_MUTEX_RECURSIVE( pxMutex ); + + /* uxRecursiveCallCount cannot be zero if xMutexHolder is equal to + * the task handle, therefore no underflow check is required. Also, + * uxRecursiveCallCount is only modified by the mutex holder, and as + * there can only be one, no mutual exclusion is required to modify the + * uxRecursiveCallCount member. */ + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )--; + + /* Has the recursive call count unwound to 0? */ + if( pxMutex->u.xSemaphore.uxRecursiveCallCount == ( UBaseType_t ) 0 ) + { + /* Return the mutex. This will automatically unblock any other + * task that might be waiting to access the mutex. */ + ( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xReturn = pdPASS; + } + else + { + /* The mutex cannot be given because the calling task is not the + * holder. */ + xReturn = pdFAIL; + + traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ); + } + + traceRETURN_xQueueGiveMutexRecursive( xReturn ); + + return xReturn; + } + +#endif /* configUSE_RECURSIVE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) + + BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, + TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxMutex = ( Queue_t * ) xMutex; + + traceENTER_xQueueTakeMutexRecursive( xMutex, xTicksToWait ); + + configASSERT( pxMutex ); + + /* Comments regarding mutual exclusion as per those within + * xQueueGiveMutexRecursive(). */ + + traceTAKE_MUTEX_RECURSIVE( pxMutex ); + + if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) + { + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++; + xReturn = pdPASS; + } + else + { + xReturn = xQueueSemaphoreTake( pxMutex, xTicksToWait ); + + /* pdPASS will only be returned if the mutex was successfully + * obtained. The calling task may have entered the Blocked state + * before reaching here. */ + if( xReturn != pdFAIL ) + { + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++; + } + else + { + traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ); + } + } + + traceRETURN_xQueueTakeMutexRecursive( xReturn ); + + return xReturn; + } + +#endif /* configUSE_RECURSIVE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, + const UBaseType_t uxInitialCount, + StaticQueue_t * pxStaticQueue ) + { + QueueHandle_t xHandle = NULL; + + traceENTER_xQueueCreateCountingSemaphoreStatic( uxMaxCount, uxInitialCount, pxStaticQueue ); + + if( ( uxMaxCount != 0U ) && + ( uxInitialCount <= uxMaxCount ) ) + { + xHandle = xQueueGenericCreateStatic( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticQueue, queueQUEUE_TYPE_COUNTING_SEMAPHORE ); + + if( xHandle != NULL ) + { + ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount; + + traceCREATE_COUNTING_SEMAPHORE(); + } + else + { + traceCREATE_COUNTING_SEMAPHORE_FAILED(); + } + } + else + { + configASSERT( xHandle ); + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_xQueueCreateCountingSemaphoreStatic( xHandle ); + + return xHandle; + } + +#endif /* ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, + const UBaseType_t uxInitialCount ) + { + QueueHandle_t xHandle = NULL; + + traceENTER_xQueueCreateCountingSemaphore( uxMaxCount, uxInitialCount ); + + if( ( uxMaxCount != 0U ) && + ( uxInitialCount <= uxMaxCount ) ) + { + xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE ); + + if( xHandle != NULL ) + { + ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount; + + traceCREATE_COUNTING_SEMAPHORE(); + } + else + { + traceCREATE_COUNTING_SEMAPHORE_FAILED(); + } + } + else + { + configASSERT( xHandle ); + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_xQueueCreateCountingSemaphore( xHandle ); + + return xHandle; + } + +#endif /* ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGenericSend( QueueHandle_t xQueue, + const void * const pvItemToQueue, + TickType_t xTicksToWait, + const BaseType_t xCopyPosition ) +{ + BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired; + TimeOut_t xTimeOut; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueGenericSend( xQueue, pvItemToQueue, xTicksToWait, xCopyPosition ); + + configASSERT( pxQueue ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) ); + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + for( ; ; ) + { + taskENTER_CRITICAL(); + { + /* Is there room on the queue now? The running task must be the + * highest priority task wanting to access the queue. If the head item + * in the queue is to be overwritten then it does not matter if the + * queue is full. */ + if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) + { + traceQUEUE_SEND( pxQueue ); + + #if ( configUSE_QUEUE_SETS == 1 ) + { + const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; + + xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) ) + { + /* Do not notify the queue set as an existing item + * was overwritten in the queue so the number of items + * in the queue has not changed. */ + mtCOVERAGE_TEST_MARKER(); + } + else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The queue is a member of a queue set, and posting + * to the queue set caused a higher priority task to + * unblock. A context switch is required. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* If there was a task waiting for data to arrive on the + * queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The unblocked task has a priority higher than + * our own so yield immediately. Yes it is ok to + * do this from within the critical section - the + * kernel takes care of that. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( xYieldRequired != pdFALSE ) + { + /* This path is a special case that will only get + * executed if the task was holding multiple mutexes + * and the mutexes were given back in an order that is + * different to that in which they were taken. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* If there was a task waiting for data to arrive on the + * queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The unblocked task has a priority higher than + * our own so yield immediately. Yes it is ok to do + * this from within the critical section - the kernel + * takes care of that. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( xYieldRequired != pdFALSE ) + { + /* This path is a special case that will only get + * executed if the task was holding multiple mutexes and + * the mutexes were given back in an order that is + * different to that in which they were taken. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_QUEUE_SETS */ + + taskEXIT_CRITICAL(); + + traceRETURN_xQueueGenericSend( pdPASS ); + + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was full and no block time is specified (or + * the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + + /* Return to the original privilege level before exiting + * the function. */ + traceQUEUE_SEND_FAILED( pxQueue ); + traceRETURN_xQueueGenericSend( errQUEUE_FULL ); + + return errQUEUE_FULL; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was full and a block time was specified so + * configure the timeout structure. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + * now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_SEND( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); + + /* Unlocking the queue means queue events can effect the + * event list. It is possible that interrupts occurring now + * remove this task from the event list again - but as the + * scheduler is suspended the task will go onto the pending + * ready list instead of the actual ready list. */ + prvUnlockQueue( pxQueue ); + + /* Resuming the scheduler will move tasks from the pending + * ready list into the ready list - so it is feasible that this + * task is already in the ready list before it yields - in which + * case the yield will not cause a context switch unless there + * is also a higher priority task in the pending ready list. */ + if( xTaskResumeAll() == pdFALSE ) + { + taskYIELD_WITHIN_API(); + } + } + else + { + /* Try again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* The timeout has expired. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + traceQUEUE_SEND_FAILED( pxQueue ); + traceRETURN_xQueueGenericSend( errQUEUE_FULL ); + + return errQUEUE_FULL; + } + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, + const void * const pvItemToQueue, + BaseType_t * const pxHigherPriorityTaskWoken, + const BaseType_t xCopyPosition ) +{ + BaseType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueGenericSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken, xCopyPosition ); + + configASSERT( pxQueue ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + * system call (or maximum API call) interrupt priority. Interrupts that are + * above the maximum system call priority are kept permanently enabled, even + * when the RTOS kernel is in a critical section, but cannot make any calls to + * FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + * then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has been + * assigned a priority above the configured maximum system call priority. + * Only FreeRTOS functions that end in FromISR can be called from interrupts + * that have been assigned a priority at or (logically) below the maximum + * system call interrupt priority. FreeRTOS maintains a separate interrupt + * safe API to ensure interrupt entry is as fast and as simple as possible. + * More information (albeit Cortex-M specific) is provided on the following + * link: https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + /* Similar to xQueueGenericSend, except without blocking if there is no room + * in the queue. Also don't directly wake a task that was blocked on a queue + * read, instead return a flag to say whether a context switch is required or + * not (i.e. has a task with a higher priority than us been woken by this + * post). */ + /* MISRA Ref 4.7.1 [Return value shall be checked] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ + /* coverity[misra_c_2012_directive_4_7_violation] */ + uxSavedInterruptStatus = ( UBaseType_t ) taskENTER_CRITICAL_FROM_ISR(); + { + if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) + { + const int8_t cTxLock = pxQueue->cTxLock; + const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; + + traceQUEUE_SEND_FROM_ISR( pxQueue ); + + /* Semaphores use xQueueGiveFromISR(), so pxQueue will not be a + * semaphore or mutex. That means prvCopyDataToQueue() cannot result + * in a task disinheriting a priority and prvCopyDataToQueue() can be + * called here even though the disinherit function does not check if + * the scheduler is suspended before accessing the ready lists. */ + ( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* The event list is not altered if the queue is locked. This will + * be done when the queue is unlocked later. */ + if( cTxLock == queueUNLOCKED ) + { + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) ) + { + /* Do not notify the queue set as an existing item + * was overwritten in the queue so the number of items + * in the queue has not changed. */ + mtCOVERAGE_TEST_MARKER(); + } + else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The queue is a member of a queue set, and posting + * to the queue set caused a higher priority task to + * unblock. A context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so + * record that a context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + * context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Not used in this path. */ + ( void ) uxPreviousMessagesWaiting; + } + #endif /* configUSE_QUEUE_SETS */ + } + else + { + /* Increment the lock count so the task that unlocks the queue + * knows that data was posted while it was locked. */ + prvIncrementQueueTxLock( pxQueue, cTxLock ); + } + + xReturn = pdPASS; + } + else + { + traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ); + xReturn = errQUEUE_FULL; + } + } + taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_xQueueGenericSendFromISR( xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, + BaseType_t * const pxHigherPriorityTaskWoken ) +{ + BaseType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueGiveFromISR( xQueue, pxHigherPriorityTaskWoken ); + + /* Similar to xQueueGenericSendFromISR() but used with semaphores where the + * item size is 0. Don't directly wake a task that was blocked on a queue + * read, instead return a flag to say whether a context switch is required or + * not (i.e. has a task with a higher priority than us been woken by this + * post). */ + + configASSERT( pxQueue ); + + /* xQueueGenericSendFromISR() should be used instead of xQueueGiveFromISR() + * if the item size is not 0. */ + configASSERT( pxQueue->uxItemSize == 0 ); + + /* Normally a mutex would not be given from an interrupt, especially if + * there is a mutex holder, as priority inheritance makes no sense for an + * interrupts, only tasks. */ + configASSERT( !( ( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) && ( pxQueue->u.xSemaphore.xMutexHolder != NULL ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + * system call (or maximum API call) interrupt priority. Interrupts that are + * above the maximum system call priority are kept permanently enabled, even + * when the RTOS kernel is in a critical section, but cannot make any calls to + * FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + * then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has been + * assigned a priority above the configured maximum system call priority. + * Only FreeRTOS functions that end in FromISR can be called from interrupts + * that have been assigned a priority at or (logically) below the maximum + * system call interrupt priority. FreeRTOS maintains a separate interrupt + * safe API to ensure interrupt entry is as fast and as simple as possible. + * More information (albeit Cortex-M specific) is provided on the following + * link: https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + /* MISRA Ref 4.7.1 [Return value shall be checked] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ + /* coverity[misra_c_2012_directive_4_7_violation] */ + uxSavedInterruptStatus = ( UBaseType_t ) taskENTER_CRITICAL_FROM_ISR(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + /* When the queue is used to implement a semaphore no data is ever + * moved through the queue but it is still valid to see if the queue 'has + * space'. */ + if( uxMessagesWaiting < pxQueue->uxLength ) + { + const int8_t cTxLock = pxQueue->cTxLock; + + traceQUEUE_SEND_FROM_ISR( pxQueue ); + + /* A task can only have an inherited priority if it is a mutex + * holder - and if there is a mutex holder then the mutex cannot be + * given from an ISR. As this is the ISR version of the function it + * can be assumed there is no mutex holder and no need to determine if + * priority disinheritance is needed. Simply increase the count of + * messages (semaphores) available. */ + pxQueue->uxMessagesWaiting = ( UBaseType_t ) ( uxMessagesWaiting + ( UBaseType_t ) 1 ); + + /* The event list is not altered if the queue is locked. This will + * be done when the queue is unlocked later. */ + if( cTxLock == queueUNLOCKED ) + { + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The semaphore is a member of a queue set, and + * posting to the queue set caused a higher priority + * task to unblock. A context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so + * record that a context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + * context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_QUEUE_SETS */ + } + else + { + /* Increment the lock count so the task that unlocks the queue + * knows that data was posted while it was locked. */ + prvIncrementQueueTxLock( pxQueue, cTxLock ); + } + + xReturn = pdPASS; + } + else + { + traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ); + xReturn = errQUEUE_FULL; + } + } + taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_xQueueGiveFromISR( xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueReceive( QueueHandle_t xQueue, + void * const pvBuffer, + TickType_t xTicksToWait ) +{ + BaseType_t xEntryTimeSet = pdFALSE; + TimeOut_t xTimeOut; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueReceive( xQueue, pvBuffer, xTicksToWait ); + + /* Check the pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* The buffer into which data is received can only be NULL if the data size + * is zero (so no data is copied into the buffer). */ + configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + for( ; ; ) + { + taskENTER_CRITICAL(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + /* Is there data in the queue now? To be running the calling task + * must be the highest priority task wanting to access the queue. */ + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Data available, remove one item. */ + prvCopyDataFromQueue( pxQueue, pvBuffer ); + traceQUEUE_RECEIVE( pxQueue ); + pxQueue->uxMessagesWaiting = ( UBaseType_t ) ( uxMessagesWaiting - ( UBaseType_t ) 1 ); + + /* There is now space in the queue, were any tasks waiting to + * post to the queue? If so, unblock the highest priority waiting + * task. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + + traceRETURN_xQueueReceive( pdPASS ); + + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was empty and no block time is specified (or + * the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + + traceQUEUE_RECEIVE_FAILED( pxQueue ); + traceRETURN_xQueueReceive( errQUEUE_EMPTY ); + + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + * configure the timeout structure. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + * now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* The timeout has not expired. If the queue is still empty place + * the task on the list of tasks waiting to receive from the queue. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + + if( xTaskResumeAll() == pdFALSE ) + { + taskYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The queue contains data again. Loop back to try and read the + * data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* Timed out. If there is no data in the queue exit, otherwise loop + * back and attempt to read the data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceQUEUE_RECEIVE_FAILED( pxQueue ); + traceRETURN_xQueueReceive( errQUEUE_EMPTY ); + + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, + TickType_t xTicksToWait ) +{ + BaseType_t xEntryTimeSet = pdFALSE; + TimeOut_t xTimeOut; + Queue_t * const pxQueue = xQueue; + + #if ( configUSE_MUTEXES == 1 ) + BaseType_t xInheritanceOccurred = pdFALSE; + #endif + + traceENTER_xQueueSemaphoreTake( xQueue, xTicksToWait ); + + /* Check the queue pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* Check this really is a semaphore, in which case the item size will be + * 0. */ + configASSERT( pxQueue->uxItemSize == 0 ); + + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + for( ; ; ) + { + taskENTER_CRITICAL(); + { + /* Semaphores are queues with an item size of 0, and where the + * number of messages in the queue is the semaphore's count value. */ + const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting; + + /* Is there data in the queue now? To be running the calling task + * must be the highest priority task wanting to access the queue. */ + if( uxSemaphoreCount > ( UBaseType_t ) 0 ) + { + traceQUEUE_RECEIVE( pxQueue ); + + /* Semaphores are queues with a data size of zero and where the + * messages waiting is the semaphore's count. Reduce the count. */ + pxQueue->uxMessagesWaiting = ( UBaseType_t ) ( uxSemaphoreCount - ( UBaseType_t ) 1 ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* Record the information required to implement + * priority inheritance should it become necessary. */ + pxQueue->u.xSemaphore.xMutexHolder = pvTaskIncrementMutexHeldCount(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_MUTEXES */ + + /* Check to see if other tasks are blocked waiting to give the + * semaphore, and if so, unblock the highest priority such task. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + + traceRETURN_xQueueSemaphoreTake( pdPASS ); + + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The semaphore count was 0 and no block time is specified + * (or the block time has expired) so exit now. */ + taskEXIT_CRITICAL(); + + traceQUEUE_RECEIVE_FAILED( pxQueue ); + traceRETURN_xQueueSemaphoreTake( errQUEUE_EMPTY ); + + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The semaphore count was 0 and a block time was specified + * so configure the timeout structure ready to block. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can give to and take from the semaphore + * now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* A block time is specified and not expired. If the semaphore + * count is 0 then enter the Blocked state to wait for a semaphore to + * become available. As semaphores are implemented with queues the + * queue being empty is equivalent to the semaphore count being 0. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + taskENTER_CRITICAL(); + { + xInheritanceOccurred = xTaskPriorityInherit( pxQueue->u.xSemaphore.xMutexHolder ); + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* if ( configUSE_MUTEXES == 1 ) */ + + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + + if( xTaskResumeAll() == pdFALSE ) + { + taskYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* There was no timeout and the semaphore count was not 0, so + * attempt to take the semaphore again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* Timed out. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + /* If the semaphore count is 0 exit now as the timeout has + * expired. Otherwise return to attempt to take the semaphore that is + * known to be available. As semaphores are implemented by queues the + * queue being empty is equivalent to the semaphore count being 0. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + #if ( configUSE_MUTEXES == 1 ) + { + /* xInheritanceOccurred could only have be set if + * pxQueue->uxQueueType == queueQUEUE_IS_MUTEX so no need to + * test the mutex type again to check it is actually a mutex. */ + if( xInheritanceOccurred != pdFALSE ) + { + taskENTER_CRITICAL(); + { + UBaseType_t uxHighestWaitingPriority; + + /* This task blocking on the mutex caused another + * task to inherit this task's priority. Now this task + * has timed out the priority should be disinherited + * again, but only as low as the next highest priority + * task that is waiting for the same mutex. */ + uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout( pxQueue ); + + /* vTaskPriorityDisinheritAfterTimeout uses the uxHighestWaitingPriority + * parameter to index pxReadyTasksLists when adding the task holding + * mutex to the ready list for its new priority. Coverity thinks that + * it can result in out-of-bounds access which is not true because + * uxHighestWaitingPriority, as returned by prvGetDisinheritPriorityAfterTimeout, + * is capped at ( configMAX_PRIORITIES - 1 ). */ + /* coverity[overrun] */ + vTaskPriorityDisinheritAfterTimeout( pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority ); + } + taskEXIT_CRITICAL(); + } + } + #endif /* configUSE_MUTEXES */ + + traceQUEUE_RECEIVE_FAILED( pxQueue ); + traceRETURN_xQueueSemaphoreTake( errQUEUE_EMPTY ); + + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueuePeek( QueueHandle_t xQueue, + void * const pvBuffer, + TickType_t xTicksToWait ) +{ + BaseType_t xEntryTimeSet = pdFALSE; + TimeOut_t xTimeOut; + int8_t * pcOriginalReadPosition; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueuePeek( xQueue, pvBuffer, xTicksToWait ); + + /* Check the pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* The buffer into which data is received can only be NULL if the data size + * is zero (so no data is copied into the buffer. */ + configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + for( ; ; ) + { + taskENTER_CRITICAL(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + /* Is there data in the queue now? To be running the calling task + * must be the highest priority task wanting to access the queue. */ + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Remember the read position so it can be reset after the data + * is read from the queue as this function is only peeking the + * data, not removing it. */ + pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + traceQUEUE_PEEK( pxQueue ); + + /* The data is not being removed, so reset the read pointer. */ + pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; + + /* The data is being left in the queue, so see if there are + * any other tasks waiting for the data. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than this task. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + + traceRETURN_xQueuePeek( pdPASS ); + + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was empty and no block time is specified (or + * the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + + traceQUEUE_PEEK_FAILED( pxQueue ); + traceRETURN_xQueuePeek( errQUEUE_EMPTY ); + + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + * configure the timeout structure ready to enter the blocked + * state. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + * now that the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* Timeout has not expired yet, check to see if there is data in the + * queue now, and if not enter the Blocked state to wait for data. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_PEEK( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + + if( xTaskResumeAll() == pdFALSE ) + { + taskYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* There is data in the queue now, so don't enter the blocked + * state, instead return to try and obtain the data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* The timeout has expired. If there is still no data in the queue + * exit, otherwise go back and try to read the data again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceQUEUE_PEEK_FAILED( pxQueue ); + traceRETURN_xQueuePeek( errQUEUE_EMPTY ); + + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, + void * const pvBuffer, + BaseType_t * const pxHigherPriorityTaskWoken ) +{ + BaseType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueReceiveFromISR( xQueue, pvBuffer, pxHigherPriorityTaskWoken ); + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + * system call (or maximum API call) interrupt priority. Interrupts that are + * above the maximum system call priority are kept permanently enabled, even + * when the RTOS kernel is in a critical section, but cannot make any calls to + * FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + * then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has been + * assigned a priority above the configured maximum system call priority. + * Only FreeRTOS functions that end in FromISR can be called from interrupts + * that have been assigned a priority at or (logically) below the maximum + * system call interrupt priority. FreeRTOS maintains a separate interrupt + * safe API to ensure interrupt entry is as fast and as simple as possible. + * More information (albeit Cortex-M specific) is provided on the following + * link: https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + /* MISRA Ref 4.7.1 [Return value shall be checked] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ + /* coverity[misra_c_2012_directive_4_7_violation] */ + uxSavedInterruptStatus = ( UBaseType_t ) taskENTER_CRITICAL_FROM_ISR(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + /* Cannot block in an ISR, so check there is data available. */ + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + const int8_t cRxLock = pxQueue->cRxLock; + + traceQUEUE_RECEIVE_FROM_ISR( pxQueue ); + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + pxQueue->uxMessagesWaiting = ( UBaseType_t ) ( uxMessagesWaiting - ( UBaseType_t ) 1 ); + + /* If the queue is locked the event list will not be modified. + * Instead update the lock count so the task that unlocks the queue + * will know that an ISR has removed data while the queue was + * locked. */ + if( cRxLock == queueUNLOCKED ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than us so + * force a context switch. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Increment the lock count so the task that unlocks the queue + * knows that data was removed while it was locked. */ + prvIncrementQueueRxLock( pxQueue, cRxLock ); + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ); + } + } + taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_xQueueReceiveFromISR( xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, + void * const pvBuffer ) +{ + BaseType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + int8_t * pcOriginalReadPosition; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueuePeekFromISR( xQueue, pvBuffer ); + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + configASSERT( pxQueue->uxItemSize != 0 ); /* Can't peek a semaphore. */ + + /* RTOS ports that support interrupt nesting have the concept of a maximum + * system call (or maximum API call) interrupt priority. Interrupts that are + * above the maximum system call priority are kept permanently enabled, even + * when the RTOS kernel is in a critical section, but cannot make any calls to + * FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + * then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has been + * assigned a priority above the configured maximum system call priority. + * Only FreeRTOS functions that end in FromISR can be called from interrupts + * that have been assigned a priority at or (logically) below the maximum + * system call interrupt priority. FreeRTOS maintains a separate interrupt + * safe API to ensure interrupt entry is as fast and as simple as possible. + * More information (albeit Cortex-M specific) is provided on the following + * link: https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + /* MISRA Ref 4.7.1 [Return value shall be checked] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ + /* coverity[misra_c_2012_directive_4_7_violation] */ + uxSavedInterruptStatus = ( UBaseType_t ) taskENTER_CRITICAL_FROM_ISR(); + { + /* Cannot block in an ISR, so check there is data available. */ + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + traceQUEUE_PEEK_FROM_ISR( pxQueue ); + + /* Remember the read position so it can be reset as nothing is + * actually being removed from the queue. */ + pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; + prvCopyDataFromQueue( pxQueue, pvBuffer ); + pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + traceQUEUE_PEEK_FROM_ISR_FAILED( pxQueue ); + } + } + taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_xQueuePeekFromISR( xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) +{ + UBaseType_t uxReturn; + + traceENTER_uxQueueMessagesWaiting( xQueue ); + + configASSERT( xQueue ); + + taskENTER_CRITICAL(); + { + uxReturn = ( ( Queue_t * ) xQueue )->uxMessagesWaiting; + } + taskEXIT_CRITICAL(); + + traceRETURN_uxQueueMessagesWaiting( uxReturn ); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) +{ + UBaseType_t uxReturn; + Queue_t * const pxQueue = xQueue; + + traceENTER_uxQueueSpacesAvailable( xQueue ); + + configASSERT( pxQueue ); + + taskENTER_CRITICAL(); + { + uxReturn = ( UBaseType_t ) ( pxQueue->uxLength - pxQueue->uxMessagesWaiting ); + } + taskEXIT_CRITICAL(); + + traceRETURN_uxQueueSpacesAvailable( uxReturn ); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) +{ + UBaseType_t uxReturn; + Queue_t * const pxQueue = xQueue; + + traceENTER_uxQueueMessagesWaitingFromISR( xQueue ); + + configASSERT( pxQueue ); + uxReturn = pxQueue->uxMessagesWaiting; + + traceRETURN_uxQueueMessagesWaitingFromISR( uxReturn ); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +void vQueueDelete( QueueHandle_t xQueue ) +{ + Queue_t * const pxQueue = xQueue; + + traceENTER_vQueueDelete( xQueue ); + + configASSERT( pxQueue ); + traceQUEUE_DELETE( pxQueue ); + + #if ( configQUEUE_REGISTRY_SIZE > 0 ) + { + vQueueUnregisterQueue( pxQueue ); + } + #endif + + #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) + { + /* The queue can only have been allocated dynamically - free it + * again. */ + vPortFree( pxQueue ); + } + #elif ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + { + /* The queue could have been allocated statically or dynamically, so + * check before attempting to free the memory. */ + if( pxQueue->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) + { + vPortFree( pxQueue ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else /* if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) */ + { + /* The queue must have been statically allocated, so is not going to be + * deleted. Avoid compiler warnings about the unused parameter. */ + ( void ) pxQueue; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + traceRETURN_vQueueDelete(); +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) + { + traceENTER_uxQueueGetQueueNumber( xQueue ); + + traceRETURN_uxQueueGetQueueNumber( ( ( Queue_t * ) xQueue )->uxQueueNumber ); + + return ( ( Queue_t * ) xQueue )->uxQueueNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vQueueSetQueueNumber( QueueHandle_t xQueue, + UBaseType_t uxQueueNumber ) + { + traceENTER_vQueueSetQueueNumber( xQueue, uxQueueNumber ); + + ( ( Queue_t * ) xQueue )->uxQueueNumber = uxQueueNumber; + + traceRETURN_vQueueSetQueueNumber(); + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) + { + traceENTER_ucQueueGetQueueType( xQueue ); + + traceRETURN_ucQueueGetQueueType( ( ( Queue_t * ) xQueue )->ucQueueType ); + + return ( ( Queue_t * ) xQueue )->ucQueueType; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +UBaseType_t uxQueueGetQueueItemSize( QueueHandle_t xQueue ) /* PRIVILEGED_FUNCTION */ +{ + traceENTER_uxQueueGetQueueItemSize( xQueue ); + + traceRETURN_uxQueueGetQueueItemSize( ( ( Queue_t * ) xQueue )->uxItemSize ); + + return ( ( Queue_t * ) xQueue )->uxItemSize; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxQueueGetQueueLength( QueueHandle_t xQueue ) /* PRIVILEGED_FUNCTION */ +{ + traceENTER_uxQueueGetQueueLength( xQueue ); + + traceRETURN_uxQueueGetQueueLength( ( ( Queue_t * ) xQueue )->uxLength ); + + return ( ( Queue_t * ) xQueue )->uxLength; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue ) + { + UBaseType_t uxHighestPriorityOfWaitingTasks; + + /* If a task waiting for a mutex causes the mutex holder to inherit a + * priority, but the waiting task times out, then the holder should + * disinherit the priority - but only down to the highest priority of any + * other tasks that are waiting for the same mutex. For this purpose, + * return the priority of the highest priority task that is waiting for the + * mutex. */ + if( listCURRENT_LIST_LENGTH( &( pxQueue->xTasksWaitingToReceive ) ) > 0U ) + { + uxHighestPriorityOfWaitingTasks = ( UBaseType_t ) ( ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) listGET_ITEM_VALUE_OF_HEAD_ENTRY( &( pxQueue->xTasksWaitingToReceive ) ) ); + } + else + { + uxHighestPriorityOfWaitingTasks = tskIDLE_PRIORITY; + } + + return uxHighestPriorityOfWaitingTasks; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, + const void * pvItemToQueue, + const BaseType_t xPosition ) +{ + BaseType_t xReturn = pdFALSE; + UBaseType_t uxMessagesWaiting; + + /* This function is called from a critical section. */ + + uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + if( pxQueue->uxItemSize == ( UBaseType_t ) 0 ) + { + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* The mutex is no longer being held. */ + xReturn = xTaskPriorityDisinherit( pxQueue->u.xSemaphore.xMutexHolder ); + pxQueue->u.xSemaphore.xMutexHolder = NULL; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_MUTEXES */ + } + else if( xPosition == queueSEND_TO_BACK ) + { + ( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); + pxQueue->pcWriteTo += pxQueue->uxItemSize; + + if( pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail ) + { + pxQueue->pcWriteTo = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + ( void ) memcpy( ( void * ) pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); + pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize; + + if( pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead ) + { + pxQueue->u.xQueue.pcReadFrom = ( pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xPosition == queueOVERWRITE ) + { + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* An item is not being added but overwritten, so subtract + * one from the recorded number of items in the queue so when + * one is added again below the number of recorded items remains + * correct. */ + --uxMessagesWaiting; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + pxQueue->uxMessagesWaiting = ( UBaseType_t ) ( uxMessagesWaiting + ( UBaseType_t ) 1 ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void prvCopyDataFromQueue( Queue_t * const pxQueue, + void * const pvBuffer ) +{ + if( pxQueue->uxItemSize != ( UBaseType_t ) 0 ) + { + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; + + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) + { + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); + } +} +/*-----------------------------------------------------------*/ + +static void prvUnlockQueue( Queue_t * const pxQueue ) +{ + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */ + + /* The lock counts contains the number of extra data items placed or + * removed from the queue while the queue was locked. When a queue is + * locked items can be added or removed, but the event lists cannot be + * updated. */ + taskENTER_CRITICAL(); + { + int8_t cTxLock = pxQueue->cTxLock; + + /* See if data was added to the queue while it was locked. */ + while( cTxLock > queueLOCKED_UNMODIFIED ) + { + /* Data was posted while the queue was locked. Are any tasks + * blocked waiting for data to become available? */ + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The queue is a member of a queue set, and posting to + * the queue set caused a higher priority task to unblock. + * A context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Tasks that are removed from the event list will get + * added to the pending ready list as the scheduler is still + * suspended. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + * context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + break; + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + /* Tasks that are removed from the event list will get added to + * the pending ready list as the scheduler is still suspended. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that + * a context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + break; + } + } + #endif /* configUSE_QUEUE_SETS */ + + --cTxLock; + } + + pxQueue->cTxLock = queueUNLOCKED; + } + taskEXIT_CRITICAL(); + + /* Do the same for the Rx lock. */ + taskENTER_CRITICAL(); + { + int8_t cRxLock = pxQueue->cRxLock; + + while( cRxLock > queueLOCKED_UNMODIFIED ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + --cRxLock; + } + else + { + break; + } + } + + pxQueue->cRxLock = queueUNLOCKED; + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvIsQueueEmpty( const Queue_t * pxQueue ) +{ + BaseType_t xReturn; + + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) +{ + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueIsQueueEmptyFromISR( xQueue ); + + configASSERT( pxQueue ); + + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + traceRETURN_xQueueIsQueueEmptyFromISR( xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvIsQueueFull( const Queue_t * pxQueue ) +{ + BaseType_t xReturn; + + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting == pxQueue->uxLength ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) +{ + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueIsQueueFullFromISR( xQueue ); + + configASSERT( pxQueue ); + + if( pxQueue->uxMessagesWaiting == pxQueue->uxLength ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + traceRETURN_xQueueIsQueueFullFromISR( xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRSend( QueueHandle_t xQueue, + const void * pvItemToQueue, + TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueCRSend( xQueue, pvItemToQueue, xTicksToWait ); + + /* If the queue is already full we may have to block. A critical section + * is required to prevent an interrupt removing something from the queue + * between the check to see if the queue is full and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + /* The queue is full - do we want to block or just leave without + * posting? */ + if( xTicksToWait > ( TickType_t ) 0 ) + { + /* As this is called from a coroutine we cannot block directly, but + * return indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToSend ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + } + portENABLE_INTERRUPTS(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + /* There is room in the queue, copy the data into the queue. */ + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + xReturn = pdPASS; + + /* Were any co-routines waiting for data to become available? */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + /* In this instance the co-routine could be placed directly + * into the ready list as we are within a critical section. + * Instead the same pending ready list mechanism is used as if + * the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The co-routine waiting has a higher priority so record + * that a yield might be appropriate. */ + xReturn = errQUEUE_YIELD; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + xReturn = errQUEUE_FULL; + } + } + portENABLE_INTERRUPTS(); + + traceRETURN_xQueueCRSend( xReturn ); + + return xReturn; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRReceive( QueueHandle_t xQueue, + void * pvBuffer, + TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueCRReceive( xQueue, pvBuffer, xTicksToWait ); + + /* If the queue is already empty we may have to block. A critical section + * is required to prevent an interrupt adding something to the queue + * between the check to see if the queue is empty and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + /* There are no messages in the queue, do we want to block or just + * leave with nothing? */ + if( xTicksToWait > ( TickType_t ) 0 ) + { + /* As this is a co-routine we cannot block directly, but return + * indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToReceive ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + portENABLE_INTERRUPTS(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Data is available from the queue. */ + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; + + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) + { + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + --( pxQueue->uxMessagesWaiting ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + xReturn = pdPASS; + + /* Were any co-routines waiting for space to become available? */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + /* In this instance the co-routine could be placed directly + * into the ready list as we are within a critical section. + * Instead the same pending ready list mechanism is used as if + * the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + xReturn = errQUEUE_YIELD; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + xReturn = pdFAIL; + } + } + portENABLE_INTERRUPTS(); + + traceRETURN_xQueueCRReceive( xReturn ); + + return xReturn; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRSendFromISR( QueueHandle_t xQueue, + const void * pvItemToQueue, + BaseType_t xCoRoutinePreviouslyWoken ) + { + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueCRSendFromISR( xQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ); + + /* Cannot block within an ISR so if there is no space on the queue then + * exit without doing anything. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + + /* We only want to wake one co-routine per ISR, so check that a + * co-routine has not already been woken. */ + if( xCoRoutinePreviouslyWoken == pdFALSE ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + return pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_xQueueCRSendFromISR( xCoRoutinePreviouslyWoken ); + + return xCoRoutinePreviouslyWoken; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRReceiveFromISR( QueueHandle_t xQueue, + void * pvBuffer, + BaseType_t * pxCoRoutineWoken ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; + + traceENTER_xQueueCRReceiveFromISR( xQueue, pvBuffer, pxCoRoutineWoken ); + + /* We cannot block from an ISR, so check there is data available. If + * not then just leave without doing anything. */ + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Copy the data from the queue. */ + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; + + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) + { + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + --( pxQueue->uxMessagesWaiting ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + if( ( *pxCoRoutineWoken ) == pdFALSE ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + *pxCoRoutineWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + traceRETURN_xQueueCRReceiveFromISR( xReturn ); + + return xReturn; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + + void vQueueAddToRegistry( QueueHandle_t xQueue, + const char * pcQueueName ) + { + UBaseType_t ux; + QueueRegistryItem_t * pxEntryToWrite = NULL; + + traceENTER_vQueueAddToRegistry( xQueue, pcQueueName ); + + configASSERT( xQueue ); + + if( pcQueueName != NULL ) + { + /* See if there is an empty space in the registry. A NULL name denotes + * a free slot. */ + for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + /* Replace an existing entry if the queue is already in the registry. */ + if( xQueue == xQueueRegistry[ ux ].xHandle ) + { + pxEntryToWrite = &( xQueueRegistry[ ux ] ); + break; + } + /* Otherwise, store in the next empty location */ + else if( ( pxEntryToWrite == NULL ) && ( xQueueRegistry[ ux ].pcQueueName == NULL ) ) + { + pxEntryToWrite = &( xQueueRegistry[ ux ] ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + + if( pxEntryToWrite != NULL ) + { + /* Store the information on this queue. */ + pxEntryToWrite->pcQueueName = pcQueueName; + pxEntryToWrite->xHandle = xQueue; + + traceQUEUE_REGISTRY_ADD( xQueue, pcQueueName ); + } + + traceRETURN_vQueueAddToRegistry(); + } + +#endif /* configQUEUE_REGISTRY_SIZE */ +/*-----------------------------------------------------------*/ + +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + + const char * pcQueueGetName( QueueHandle_t xQueue ) + { + UBaseType_t ux; + const char * pcReturn = NULL; + + traceENTER_pcQueueGetName( xQueue ); + + configASSERT( xQueue ); + + /* Note there is nothing here to protect against another task adding or + * removing entries from the registry while it is being searched. */ + + for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].xHandle == xQueue ) + { + pcReturn = xQueueRegistry[ ux ].pcQueueName; + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + traceRETURN_pcQueueGetName( pcReturn ); + + return pcReturn; + } + +#endif /* configQUEUE_REGISTRY_SIZE */ +/*-----------------------------------------------------------*/ + +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + + void vQueueUnregisterQueue( QueueHandle_t xQueue ) + { + UBaseType_t ux; + + traceENTER_vQueueUnregisterQueue( xQueue ); + + configASSERT( xQueue ); + + /* See if the handle of the queue being unregistered in actually in the + * registry. */ + for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].xHandle == xQueue ) + { + /* Set the name to NULL to show that this slot if free again. */ + xQueueRegistry[ ux ].pcQueueName = NULL; + + /* Set the handle to NULL to ensure the same queue handle cannot + * appear in the registry twice if it is added, removed, then + * added again. */ + xQueueRegistry[ ux ].xHandle = ( QueueHandle_t ) 0; + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + traceRETURN_vQueueUnregisterQueue(); + } + +#endif /* configQUEUE_REGISTRY_SIZE */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TIMERS == 1 ) + + void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, + TickType_t xTicksToWait, + const BaseType_t xWaitIndefinitely ) + { + Queue_t * const pxQueue = xQueue; + + traceENTER_vQueueWaitForMessageRestricted( xQueue, xTicksToWait, xWaitIndefinitely ); + + /* This function should not be called by application code hence the + * 'Restricted' in its name. It is not part of the public API. It is + * designed for use by kernel code, and has special calling requirements. + * It can result in vListInsert() being called on a list that can only + * possibly ever have one item in it, so the list will be fast, but even + * so it should be called with the scheduler locked and not from a critical + * section. */ + + /* Only do anything if there are no messages in the queue. This function + * will not actually cause the task to block, just place it on a blocked + * list. It will not block until the scheduler is unlocked - at which + * time a yield will be performed. If an item is added to the queue while + * the queue is locked, and the calling task blocks on the queue, then the + * calling task will be immediately unblocked when the queue is unlocked. */ + prvLockQueue( pxQueue ); + + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0U ) + { + /* There is nothing in the queue, block for the specified period. */ + vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait, xWaitIndefinitely ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvUnlockQueue( pxQueue ); + + traceRETURN_vQueueWaitForMessageRestricted(); + } + +#endif /* configUSE_TIMERS */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_QUEUE_SETS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) + { + QueueSetHandle_t pxQueue; + + traceENTER_xQueueCreateSet( uxEventQueueLength ); + + pxQueue = xQueueGenericCreate( uxEventQueueLength, ( UBaseType_t ) sizeof( Queue_t * ), queueQUEUE_TYPE_SET ); + + traceRETURN_xQueueCreateSet( pxQueue ); + + return pxQueue; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet ) + { + BaseType_t xReturn; + + traceENTER_xQueueAddToSet( xQueueOrSemaphore, xQueueSet ); + + taskENTER_CRITICAL(); + { + if( ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer != NULL ) + { + /* Cannot add a queue/semaphore to more than one queue set. */ + xReturn = pdFAIL; + } + else if( ( ( Queue_t * ) xQueueOrSemaphore )->uxMessagesWaiting != ( UBaseType_t ) 0 ) + { + /* Cannot add a queue/semaphore to a queue set if there are already + * items in the queue/semaphore. */ + xReturn = pdFAIL; + } + else + { + ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer = xQueueSet; + xReturn = pdPASS; + } + } + taskEXIT_CRITICAL(); + + traceRETURN_xQueueAddToSet( xReturn ); + + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet ) + { + BaseType_t xReturn; + Queue_t * const pxQueueOrSemaphore = ( Queue_t * ) xQueueOrSemaphore; + + traceENTER_xQueueRemoveFromSet( xQueueOrSemaphore, xQueueSet ); + + if( pxQueueOrSemaphore->pxQueueSetContainer != xQueueSet ) + { + /* The queue was not a member of the set. */ + xReturn = pdFAIL; + } + else if( pxQueueOrSemaphore->uxMessagesWaiting != ( UBaseType_t ) 0 ) + { + /* It is dangerous to remove a queue from a set when the queue is + * not empty because the queue set will still hold pending events for + * the queue. */ + xReturn = pdFAIL; + } + else + { + taskENTER_CRITICAL(); + { + /* The queue is no longer contained in the set. */ + pxQueueOrSemaphore->pxQueueSetContainer = NULL; + } + taskEXIT_CRITICAL(); + xReturn = pdPASS; + } + + traceRETURN_xQueueRemoveFromSet( xReturn ); + + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, + TickType_t const xTicksToWait ) + { + QueueSetMemberHandle_t xReturn = NULL; + + traceENTER_xQueueSelectFromSet( xQueueSet, xTicksToWait ); + + ( void ) xQueueReceive( ( QueueHandle_t ) xQueueSet, &xReturn, xTicksToWait ); + + traceRETURN_xQueueSelectFromSet( xReturn ); + + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) + { + QueueSetMemberHandle_t xReturn = NULL; + + traceENTER_xQueueSelectFromSetFromISR( xQueueSet ); + + ( void ) xQueueReceiveFromISR( ( QueueHandle_t ) xQueueSet, &xReturn, NULL ); + + traceRETURN_xQueueSelectFromSetFromISR( xReturn ); + + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue ) + { + Queue_t * pxQueueSetContainer = pxQueue->pxQueueSetContainer; + BaseType_t xReturn = pdFALSE; + + /* This function must be called form a critical section. */ + + /* The following line is not reachable in unit tests because every call + * to prvNotifyQueueSetContainer is preceded by a check that + * pxQueueSetContainer != NULL */ + configASSERT( pxQueueSetContainer ); /* LCOV_EXCL_BR_LINE */ + configASSERT( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength ); + + if( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength ) + { + const int8_t cTxLock = pxQueueSetContainer->cTxLock; + + traceQUEUE_SET_SEND( pxQueueSetContainer ); + + /* The data copied is the handle of the queue that contains data. */ + xReturn = prvCopyDataToQueue( pxQueueSetContainer, &pxQueue, queueSEND_TO_BACK ); + + if( cTxLock == queueUNLOCKED ) + { + if( listLIST_IS_EMPTY( &( pxQueueSetContainer->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueueSetContainer->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + prvIncrementQueueTxLock( pxQueueSetContainer, cTxLock ); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ diff --git a/vendor/FreeRTOS-Kernel/tasks.c b/vendor/FreeRTOS-Kernel/tasks.c new file mode 100644 index 0000000..2b7d311 --- /dev/null +++ b/vendor/FreeRTOS-Kernel/tasks.c @@ -0,0 +1,8872 @@ +/* + * FreeRTOS Kernel V11.3.0 + * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2026 Arm Limited and/or its affiliates + * + * 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 + * + */ + +/* Standard includes. */ +#include +#include + +/* 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 + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" +#include "stack_macros.h" + +/* The default definitions are only available for non-MPU ports. The + * reason is that the stack alignment requirements vary for different + * architectures.*/ +#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configKERNEL_PROVIDED_STATIC_MEMORY == 1 ) && ( portUSING_MPU_WRAPPERS != 0 ) ) + #error configKERNEL_PROVIDED_STATIC_MEMORY cannot be set to 1 when using an MPU port. The vApplicationGet*TaskMemory() functions must be provided manually. +#endif + +/* 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 + +/* Set configUSE_STATS_FORMATTING_FUNCTIONS to 2 to include the stats formatting + * functions but without including stdio.h here. */ +#if ( configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) + +/* At the bottom of this file are two optional functions that can be used + * to generate human readable text from the raw data generated by the + * uxTaskGetSystemState() function. Note the formatting functions are provided + * for convenience only, and are NOT considered part of the kernel. */ + #include +#endif /* configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) */ + +#if ( configUSE_PREEMPTION == 0 ) + +/* If the cooperative scheduler is being used then a yield should not be + * performed just because a higher priority task has been woken. */ + #define taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxTCB ) + #define taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB ) +#else + + #if ( configNUMBER_OF_CORES == 1 ) + +/* This macro requests the running task pxTCB to yield. In single core + * scheduler, a running task always runs on core 0 and portYIELD_WITHIN_API() + * can be used to request the task running on core 0 to yield. Therefore, pxTCB + * is not used in this macro. */ + #define taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxTCB ) \ + do { \ + ( void ) ( pxTCB ); \ + portYIELD_WITHIN_API(); \ + } while( 0 ) + + #define taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB ) \ + do { \ + if( pxCurrentTCB->uxPriority < ( pxTCB )->uxPriority ) \ + { \ + portYIELD_WITHIN_API(); \ + } \ + else \ + { \ + mtCOVERAGE_TEST_MARKER(); \ + } \ + } while( 0 ) + + #else /* if ( configNUMBER_OF_CORES == 1 ) */ + +/* Yield the core on which this task is running. */ + #define taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxTCB ) prvYieldCore( ( pxTCB )->xTaskRunState ) + +/* Yield for the task if a running task has priority lower than this task. */ + #define taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB ) prvYieldForTask( pxTCB ) + + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + +#endif /* if ( configUSE_PREEMPTION == 0 ) */ + +/* Values that can be assigned to the ucNotifyState member of the TCB. */ +#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) /* Must be zero as it is the initialised value. */ +#define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 ) +#define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 ) + +/* + * The value used to fill the stack of a task when the task is created. This + * is used purely for checking the high water mark for tasks. + */ +#define tskSTACK_FILL_BYTE ( 0xa5U ) + +/* Bits used to record how a task's stack and TCB were allocated. */ +#define tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 0 ) +#define tskSTATICALLY_ALLOCATED_STACK_ONLY ( ( uint8_t ) 1 ) +#define tskSTATICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 2 ) + +/* If any of the following are set then task stacks are filled with a known + * value so the high water mark can be determined. If none of the following are + * set then don't fill the stack so there is no unnecessary dependency on memset. */ +#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 1 +#else + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 0 +#endif + +/* + * Macros used by vListTask to indicate which state a task is in. + */ +#define tskRUNNING_CHAR ( 'X' ) +#define tskBLOCKED_CHAR ( 'B' ) +#define tskREADY_CHAR ( 'R' ) +#define tskDELETED_CHAR ( 'D' ) +#define tskSUSPENDED_CHAR ( 'S' ) + +/* + * Some kernel aware debuggers require the data the debugger needs access to be + * global, rather than file scope. + */ +#ifdef portREMOVE_STATIC_QUALIFIER + #define static +#endif + +/* The name allocated to the Idle task. This can be overridden by defining + * configIDLE_TASK_NAME in FreeRTOSConfig.h. */ +#ifndef configIDLE_TASK_NAME + #define configIDLE_TASK_NAME "IDLE" +#endif + +/* Reserve space for Core ID and null termination. */ +#if ( configNUMBER_OF_CORES > 1 ) + /* Multi-core systems with up to 9 cores require 1 character for core ID and 1 for null termination. */ + #if ( configMAX_TASK_NAME_LEN < 2U ) + #error Minimum required task name length is 2. Please increase configMAX_TASK_NAME_LEN. + #endif + #define taskRESERVED_TASK_NAME_LENGTH 2U + +#else /* if ( configNUMBER_OF_CORES > 1 ) */ + /* Reserve space for null termination. */ + #if ( configMAX_TASK_NAME_LEN < 1U ) + #error Minimum required task name length is 1. Please increase configMAX_TASK_NAME_LEN. + #endif + #define taskRESERVED_TASK_NAME_LENGTH 1U +#endif /* if ( ( configNUMBER_OF_CORES > 1 ) */ + +#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) + +/* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 then task selection is + * performed in a generic way that is not optimised to any particular + * microcontroller architecture. */ + +/* uxTopReadyPriority holds the priority of the highest priority ready + * state task. */ + #define taskRECORD_READY_PRIORITY( uxPriority ) \ + do { \ + if( ( uxPriority ) > uxTopReadyPriority ) \ + { \ + uxTopReadyPriority = ( uxPriority ); \ + } \ + } while( 0 ) /* taskRECORD_READY_PRIORITY */ + +/*-----------------------------------------------------------*/ + + #if ( configNUMBER_OF_CORES == 1 ) + #define taskSELECT_HIGHEST_PRIORITY_TASK() \ + do { \ + UBaseType_t uxTopPriority = uxTopReadyPriority; \ + \ + /* Find the highest priority queue that contains ready tasks. */ \ + while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) != pdFALSE ) \ + { \ + configASSERT( uxTopPriority ); \ + --uxTopPriority; \ + } \ + \ + /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \ + * the same priority get an equal share of the processor time. */ \ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + uxTopReadyPriority = uxTopPriority; \ + } while( 0 ) /* taskSELECT_HIGHEST_PRIORITY_TASK */ + #else /* if ( configNUMBER_OF_CORES == 1 ) */ + + #define taskSELECT_HIGHEST_PRIORITY_TASK( xCoreID ) prvSelectHighestPriorityTask( xCoreID ) + + #endif /* if ( configNUMBER_OF_CORES == 1 ) */ + +/*-----------------------------------------------------------*/ + +/* Define away taskRESET_READY_PRIORITY() and portRESET_READY_PRIORITY() as + * they are only required when a port optimised method of task selection is + * being used. */ + #define taskRESET_READY_PRIORITY( uxPriority ) + #define portRESET_READY_PRIORITY( uxPriority, uxTopReadyPriority ) + +#else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 1 then task selection is + * performed in a way that is tailored to the particular microcontroller + * architecture being used. */ + +/* A port optimised version is provided. Call the port defined macros. */ + #define taskRECORD_READY_PRIORITY( uxPriority ) portRECORD_READY_PRIORITY( ( uxPriority ), uxTopReadyPriority ) + +/*-----------------------------------------------------------*/ + + #define taskSELECT_HIGHEST_PRIORITY_TASK() \ + do { \ + UBaseType_t uxTopPriority; \ + \ + /* Find the highest priority list that contains ready tasks. */ \ + portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \ + configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + } while( 0 ) + +/*-----------------------------------------------------------*/ + +/* A port optimised version is provided, call it only if the TCB being reset + * is being referenced from a ready list. If it is referenced from a delayed + * or suspended list then it won't be in a ready list. */ + #define taskRESET_READY_PRIORITY( uxPriority ) \ + do { \ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 ) \ + { \ + portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \ + } \ + } while( 0 ) + +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/*-----------------------------------------------------------*/ + +/* pxDelayedTaskList and pxOverflowDelayedTaskList are switched when the tick + * count overflows. */ +#define taskSWITCH_DELAYED_LISTS() \ + do { \ + List_t * pxTemp; \ + \ + /* The delayed tasks list should be empty when the lists are switched. */ \ + configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) ); \ + \ + pxTemp = pxDelayedTaskList; \ + pxDelayedTaskList = pxOverflowDelayedTaskList; \ + pxOverflowDelayedTaskList = pxTemp; \ + xNumOfOverflows = ( BaseType_t ) ( xNumOfOverflows + 1 ); \ + prvResetNextTaskUnblockTime(); \ + } while( 0 ) + +/*-----------------------------------------------------------*/ + +/* + * Place the task represented by pxTCB into the appropriate ready list for + * the task. It is inserted at the end of the list. + */ +#define prvAddTaskToReadyList( pxTCB ) \ + do { \ + traceMOVED_TASK_TO_READY_STATE( pxTCB ); \ + taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \ + listINSERT_END( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \ + tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ); \ + } while( 0 ) +/*-----------------------------------------------------------*/ + +/* + * Several functions take a TaskHandle_t parameter that can optionally be NULL, + * where NULL is used to indicate that the handle of the currently executing + * task should be used in place of the parameter. This macro simply checks to + * see if the parameter is NULL and returns a pointer to the appropriate TCB. + */ +#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) ) + +/* The item value of the event list item is normally used to hold the priority + * of the task to which it belongs (coded to allow it to be held in reverse + * priority order). However, it is occasionally borrowed for other purposes. It + * is important its value is not updated due to a task priority change while it is + * being used for another purpose. The following bit definition is used to inform + * the scheduler that the value should not be changed - in which case it is the + * responsibility of whichever module is using the value to ensure it gets set back + * to its original value when it is released. */ +#if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS ) + #define taskEVENT_LIST_ITEM_VALUE_IN_USE ( ( uint16_t ) 0x8000U ) +#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS ) + #define taskEVENT_LIST_ITEM_VALUE_IN_USE ( ( uint32_t ) 0x80000000U ) +#elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_64_BITS ) + #define taskEVENT_LIST_ITEM_VALUE_IN_USE ( ( uint64_t ) 0x8000000000000000U ) +#endif + +/* Indicates that the task is not actively running on any core. */ +#define taskTASK_NOT_RUNNING ( ( BaseType_t ) ( -1 ) ) + +/* Indicates that the task is actively running but scheduled to yield. */ +#define taskTASK_SCHEDULED_TO_YIELD ( ( BaseType_t ) ( -2 ) ) + +/* Returns pdTRUE if the task is actively running and not scheduled to yield. */ +#if ( configNUMBER_OF_CORES == 1 ) + #define taskTASK_IS_RUNNING( pxTCB ) ( ( ( pxTCB ) == pxCurrentTCB ) ? ( pdTRUE ) : ( pdFALSE ) ) + #define taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD( pxTCB ) ( ( ( pxTCB ) == pxCurrentTCB ) ? ( pdTRUE ) : ( pdFALSE ) ) +#else + #define taskTASK_IS_RUNNING( pxTCB ) ( ( ( ( pxTCB )->xTaskRunState >= ( BaseType_t ) 0 ) && ( ( pxTCB )->xTaskRunState < ( BaseType_t ) configNUMBER_OF_CORES ) ) ? ( pdTRUE ) : ( pdFALSE ) ) + #define taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD( pxTCB ) ( ( ( pxTCB )->xTaskRunState != taskTASK_NOT_RUNNING ) ? ( pdTRUE ) : ( pdFALSE ) ) +#endif + +/* Indicates that the task is an Idle task. */ +#define taskATTRIBUTE_IS_IDLE ( UBaseType_t ) ( 1U << 0U ) + +#if ( ( configNUMBER_OF_CORES > 1 ) && ( portCRITICAL_NESTING_IN_TCB == 1 ) ) + #define portGET_CRITICAL_NESTING_COUNT( xCoreID ) ( pxCurrentTCBs[ ( xCoreID ) ]->uxCriticalNesting ) + #define portSET_CRITICAL_NESTING_COUNT( xCoreID, x ) ( pxCurrentTCBs[ ( xCoreID ) ]->uxCriticalNesting = ( x ) ) + #define portINCREMENT_CRITICAL_NESTING_COUNT( xCoreID ) ( pxCurrentTCBs[ ( xCoreID ) ]->uxCriticalNesting++ ) + #define portDECREMENT_CRITICAL_NESTING_COUNT( xCoreID ) ( pxCurrentTCBs[ ( xCoreID ) ]->uxCriticalNesting-- ) +#endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( portCRITICAL_NESTING_IN_TCB == 1 ) ) */ + +#define taskBITS_PER_BYTE ( ( size_t ) 8 ) + +#if ( configNUMBER_OF_CORES > 1 ) + +/* Yields the given core. This must be called from a critical section and xCoreID + * must be valid. This macro is not required in single core since there is only + * one core to yield. */ + #define prvYieldCore( xCoreID ) \ + do { \ + if( ( xCoreID ) == ( BaseType_t ) portGET_CORE_ID() ) \ + { \ + /* Pending a yield for this core since it is in the critical section. */ \ + xYieldPendings[ ( xCoreID ) ] = pdTRUE; \ + } \ + else \ + { \ + /* Request other core to yield if it is not requested before. */ \ + if( pxCurrentTCBs[ ( xCoreID ) ]->xTaskRunState != taskTASK_SCHEDULED_TO_YIELD ) \ + { \ + portYIELD_CORE( xCoreID ); \ + pxCurrentTCBs[ ( xCoreID ) ]->xTaskRunState = taskTASK_SCHEDULED_TO_YIELD; \ + } \ + } \ + } while( 0 ) +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ +/*-----------------------------------------------------------*/ + +/* + * Task control block. A task control block (TCB) is allocated for each task, + * and stores task state information, including a pointer to the task's context + * (the task's run time environment, including register values) + */ +typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +{ + volatile StackType_t * pxTopOfStack; /**< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xMPUSettings; /**< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */ + #endif + + #if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) + UBaseType_t uxCoreAffinityMask; /**< Used to link the task to certain cores. UBaseType_t must have greater than or equal to the number of bits as configNUMBER_OF_CORES. */ + #endif + + ListItem_t xStateListItem; /**< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ + ListItem_t xEventListItem; /**< Used to reference a task from an event list. */ + UBaseType_t uxPriority; /**< The priority of the task. 0 is the lowest priority. */ + StackType_t * pxStack; /**< Points to the start of the stack. */ + #if ( configNUMBER_OF_CORES > 1 ) + volatile BaseType_t xTaskRunState; /**< Used to identify the core the task is running on, if the task is running. Otherwise, identifies the task's state - not running or yielding. */ + UBaseType_t uxTaskAttributes; /**< Task's attributes - currently used to identify the idle tasks. */ + #endif + char pcTaskName[ configMAX_TASK_NAME_LEN ]; /**< Descriptive name given to the task when created. Facilitates debugging only. */ + + #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + BaseType_t xPreemptionDisable; /**< Used to prevent the task from being preempted. */ + #endif + + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + StackType_t * pxEndOfStack; /**< Points to the highest valid address for the stack. */ + #endif + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + UBaseType_t uxCriticalNesting; /**< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */ + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTCBNumber; /**< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. */ + UBaseType_t uxTaskNumber; /**< Stores a number specifically for use by third party trace code. */ + #endif + + #if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxBasePriority; /**< The priority last assigned to the task - used by the priority inheritance mechanism. */ + UBaseType_t uxMutexesHeld; + #endif + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + TaskHookFunction_t pxTaskTag; + #endif + + #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /**< Stores the amount of time the task has spent in the Running state. */ + #endif + + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + configTLS_BLOCK_TYPE xTLSBlock; /**< Memory block used as Thread Local Storage (TLS) Block for the task. */ + #endif + + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; + volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ]; + #endif + + /* See the comments in FreeRTOS.h with the definition of + * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */ + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + uint8_t ucStaticallyAllocated; /**< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */ + #endif + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + uint8_t ucDelayAborted; + #endif + + #if ( configUSE_POSIX_ERRNO == 1 ) + int iTaskErrno; + #endif +} tskTCB; + +/* The old tskTCB name is maintained above then typedefed to the new TCB_t name + * below to enable the use of older kernel aware debuggers. */ +typedef tskTCB TCB_t; + +#if ( configNUMBER_OF_CORES == 1 ) + /* MISRA Ref 8.4.1 [Declaration shall be visible] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-84 */ + /* coverity[misra_c_2012_rule_8_4_violation] */ + portDONT_DISCARD PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; +#else + /* MISRA Ref 8.4.1 [Declaration shall be visible] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-84 */ + /* coverity[misra_c_2012_rule_8_4_violation] */ + portDONT_DISCARD PRIVILEGED_DATA TCB_t * volatile pxCurrentTCBs[ configNUMBER_OF_CORES ]; + #define pxCurrentTCB xTaskGetCurrentTaskHandle() +#endif + +/* Lists for ready and blocked tasks. -------------------- + * xDelayedTaskList1 and xDelayedTaskList2 could be moved to function scope but + * doing so breaks some kernel aware debuggers and debuggers that rely on removing + * the static qualifier. */ +PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ]; /**< Prioritised ready tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList1; /**< Delayed tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList2; /**< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /**< Points to the delayed task list currently being used. */ +PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /**< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t xPendingReadyList; /**< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ + +#if ( INCLUDE_vTaskDelete == 1 ) + + PRIVILEGED_DATA static List_t xTasksWaitingTermination; /**< Tasks that have been deleted - but their memory not yet freed. */ + PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; + +#endif + +#if ( INCLUDE_vTaskSuspend == 1 ) + + PRIVILEGED_DATA static List_t xSuspendedTaskList; /**< Tasks that are currently suspended. */ + +#endif + +/* Global POSIX errno. Its value is changed upon context switching to match + * the errno of the currently running task. */ +#if ( configUSE_POSIX_ERRNO == 1 ) + int FreeRTOS_errno = 0; +#endif + +/* Other file private variables. --------------------------------*/ +PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; +PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; +PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; +PRIVILEGED_DATA static volatile TickType_t xPendedTicks = ( TickType_t ) 0U; +PRIVILEGED_DATA static volatile BaseType_t xYieldPendings[ configNUMBER_OF_CORES ] = { pdFALSE }; +PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; +PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = ( TickType_t ) 0U; /* Initialised to portMAX_DELAY before the scheduler starts. */ +PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandles[ configNUMBER_OF_CORES ]; /**< Holds the handles of the idle tasks. The idle tasks are created automatically when the scheduler is started. */ + +/* Improve support for OpenOCD. The kernel tracks Ready tasks via priority lists. + * For tracking the state of remote threads, OpenOCD uses uxTopUsedPriority + * to determine the number of priority lists to read back from the remote target. */ +static const volatile UBaseType_t uxTopUsedPriority = configMAX_PRIORITIES - 1U; + +/* Context switches are held pending while the scheduler is suspended. Also, + * interrupts must not manipulate the xStateListItem of a TCB, or any of the + * lists the xStateListItem can be referenced from, if the scheduler is suspended. + * If an interrupt needs to unblock a task while the scheduler is suspended then it + * moves the task's event list item into the xPendingReadyList, ready for the + * kernel to move the task from the pending ready list into the real ready list + * when the scheduler is unsuspended. The pending ready list itself can only be + * accessed from a critical section. + * + * Updates to uxSchedulerSuspended must be protected by both the task lock and the ISR lock + * and must not be done from an ISR. Reads must be protected by either lock and may be done + * from either an ISR or a task. */ +PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) 0U; + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + +/* Do not move these variables to function scope as doing so prevents the + * code working with debuggers that need to remove the static qualifier. */ +PRIVILEGED_DATA static configRUN_TIME_COUNTER_TYPE ulTaskSwitchedInTime[ configNUMBER_OF_CORES ] = { 0U }; /**< Holds the value of a timer/counter the last time a task was switched in. */ +PRIVILEGED_DATA static volatile configRUN_TIME_COUNTER_TYPE ulTotalRunTime[ configNUMBER_OF_CORES ] = { 0U }; /**< Holds the total amount of execution time as defined by the run time counter clock. */ + +#endif + +/*-----------------------------------------------------------*/ + +/* File private functions. --------------------------------*/ + +/* + * Creates the idle tasks during scheduler start. + */ +static BaseType_t prvCreateIdleTasks( void ); + +#if ( configNUMBER_OF_CORES > 1 ) + +/* + * Checks to see if another task moved the current task out of the ready + * list while it was waiting to enter a critical section and yields, if so. + */ + static void prvCheckForRunStateChange( void ); +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + +#if ( configNUMBER_OF_CORES > 1 ) + +/* + * Yields a core, or cores if multiple priorities are not allowed to run + * simultaneously, to allow the task pxTCB to run. + */ + static void prvYieldForTask( const TCB_t * pxTCB ); +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + +#if ( configNUMBER_OF_CORES > 1 ) + +/* + * Selects the highest priority available task for the given core. + */ + static void prvSelectHighestPriorityTask( BaseType_t xCoreID ); +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + +/** + * Utility task that simply returns pdTRUE if the task referenced by xTask is + * currently in the Suspended state, or pdFALSE if the task referenced by xTask + * is in any other state. + */ +#if ( INCLUDE_vTaskSuspend == 1 ) + + static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +#endif /* INCLUDE_vTaskSuspend */ + +/* + * Utility to ready all the lists used by the scheduler. This is called + * automatically upon the creation of the first task. + */ +static void prvInitialiseTaskLists( void ) PRIVILEGED_FUNCTION; + +/* + * The idle task, which as all tasks is implemented as a never ending loop. + * The idle task is automatically created and added to the ready lists upon + * creation of the first user task. + * + * In the FreeRTOS SMP, configNUMBER_OF_CORES - 1 passive idle tasks are also + * created to ensure that each core has an idle task to run when no other + * task is available to run. + * + * The portTASK_FUNCTION_PROTO() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for these functions are: + * + * void prvIdleTask( void *pvParameters ); + * void prvPassiveIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ) PRIVILEGED_FUNCTION; +#if ( configNUMBER_OF_CORES > 1 ) + static portTASK_FUNCTION_PROTO( prvPassiveIdleTask, pvParameters ) PRIVILEGED_FUNCTION; +#endif + +/* + * Utility to free all memory allocated by the scheduler to hold a TCB, + * including the stack pointed to by the TCB. + * + * This does not free memory allocated by the task itself (i.e. memory + * allocated by calls to pvPortMalloc from within the tasks application code). + */ +#if ( INCLUDE_vTaskDelete == 1 ) + + static void prvDeleteTCB( TCB_t * pxTCB ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Used only by the idle task. This checks to see if anything has been placed + * in the list of tasks waiting to be deleted. If so the task is cleaned up + * and its TCB deleted. + */ +static void prvCheckTasksWaitingTermination( void ) PRIVILEGED_FUNCTION; + +/* + * The currently executing task is entering the Blocked state. Add the task to + * either the current or the overflow delayed task list. + */ +static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, + const BaseType_t xCanBlockIndefinitely ) PRIVILEGED_FUNCTION; + +/* + * Fills an TaskStatus_t structure with information on each task that is + * referenced from the pxList list (which may be a ready list, a delayed list, + * a suspended list, etc.). + * + * THIS FUNCTION IS INTENDED FOR DEBUGGING ONLY, AND SHOULD NOT BE CALLED FROM + * NORMAL APPLICATION CODE. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t * pxTaskStatusArray, + List_t * pxList, + eTaskState eState ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Searches pxList for a task with name pcNameToQuery - returning a handle to + * the task if it is found, or NULL if the task is not found. + */ +#if ( INCLUDE_xTaskGetHandle == 1 ) + + static TCB_t * prvSearchForNameWithinSingleList( List_t * pxList, + const char pcNameToQuery[] ) PRIVILEGED_FUNCTION; + +#endif + +/* + * When a task is created, the stack of the task is filled with a known value. + * This function determines the 'high water mark' of the task stack by + * determining how much of the stack remains at the original preset value. + */ +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Return the amount of time, in ticks, that will pass before the kernel will + * next move a task from the Blocked state to the Running state or before the + * tick count overflows (whichever is earlier). + * + * This conditional compilation should use inequality to 0, not equality to 1. + * This is to ensure portSUPPRESS_TICKS_AND_SLEEP() can be called when user + * defined low power mode implementations require configUSE_TICKLESS_IDLE to be + * set to a value other than 1. + */ +#if ( configUSE_TICKLESS_IDLE != 0 ) + + static TickType_t prvGetExpectedIdleTime( void ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Set xNextTaskUnblockTime to the time at which the next Blocked state task + * will exit the Blocked state. + */ +static void prvResetNextTaskUnblockTime( void ) PRIVILEGED_FUNCTION; + +#if ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) + +/* + * Helper function used to pad task names with spaces when printing out + * human readable tables of task information. + */ + static char * prvWriteNameToBuffer( char * pcBuffer, + const char * pcTaskName ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Called after a Task_t structure has been allocated either statically or + * dynamically to fill in the structure's members. + */ +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, + const configSTACK_DEPTH_TYPE uxStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t * pxNewTCB, + const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION; + +/* + * Called after a new task has been created and initialised to place the task + * under the control of the scheduler. + */ +static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) PRIVILEGED_FUNCTION; + +/* + * Create a task with static buffer for both TCB and stack. Returns a handle to + * the task if it is created successfully. Otherwise, returns NULL. + */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + static TCB_t * prvCreateStaticTask( 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, + TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif /* #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + +/* + * Create a restricted task with static buffer for both TCB and stack. Returns + * a handle to the task if it is created successfully. Otherwise, returns NULL. + */ +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + static TCB_t * prvCreateRestrictedStaticTask( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif /* #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) */ + +/* + * Create a restricted task with static buffer for task stack and allocated buffer + * for TCB. Returns a handle to the task if it is created successfully. Otherwise, + * returns NULL. + */ +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + static TCB_t * prvCreateRestrictedTask( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif /* #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ + +/* + * Create a task with allocated buffer for both TCB and stack. Returns a handle to + * the task if it is created successfully. Otherwise, returns NULL. + */ +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + static TCB_t * prvCreateTask( TaskFunction_t pxTaskCode, + const char * const pcName, + const configSTACK_DEPTH_TYPE uxStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif /* #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ + +/* + * freertos_tasks_c_additions_init() should only be called if the user definable + * macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is the only macro + * called by the function. + */ +#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + + static void freertos_tasks_c_additions_init( void ) PRIVILEGED_FUNCTION; + +#endif + +#if ( configUSE_PASSIVE_IDLE_HOOK == 1 ) + extern void vApplicationPassiveIdleHook( void ); +#endif /* #if ( configUSE_PASSIVE_IDLE_HOOK == 1 ) */ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) + +/* + * Convert the snprintf return value to the number of characters + * written. The following are the possible cases: + * + * 1. The buffer supplied to snprintf is large enough to hold the + * generated string. The return value in this case is the number + * of characters actually written, not counting the terminating + * null character. + * 2. The buffer supplied to snprintf is NOT large enough to hold + * the generated string. The return value in this case is the + * number of characters that would have been written if the + * buffer had been sufficiently large, not counting the + * terminating null character. + * 3. Encoding error. The return value in this case is a negative + * number. + * + * From 1 and 2 above ==> Only when the return value is non-negative + * and less than the supplied buffer length, the string has been + * completely written. + */ + static size_t prvSnprintfReturnValueToCharsWritten( int iSnprintfReturnValue, + size_t n ); + +#endif /* #if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( configNUMBER_OF_CORES > 1 ) + static void prvCheckForRunStateChange( void ) + { + UBaseType_t uxPrevCriticalNesting; + const TCB_t * pxThisTCB; + BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID(); + + /* This must only be called from within a task. */ + portASSERT_IF_IN_ISR(); + + /* This function is always called with interrupts disabled + * so this is safe. */ + pxThisTCB = pxCurrentTCBs[ xCoreID ]; + + while( pxThisTCB->xTaskRunState == taskTASK_SCHEDULED_TO_YIELD ) + { + /* We are only here if we just entered a critical section + * or if we just suspended the scheduler, and another task + * has requested that we yield. + * + * This is slightly complicated since we need to save and restore + * the suspension and critical nesting counts, as well as release + * and reacquire the correct locks. And then, do it all over again + * if our state changed again during the reacquisition. */ + uxPrevCriticalNesting = portGET_CRITICAL_NESTING_COUNT( xCoreID ); + + if( uxPrevCriticalNesting > 0U ) + { + portSET_CRITICAL_NESTING_COUNT( xCoreID, 0U ); + portRELEASE_ISR_LOCK( xCoreID ); + } + else + { + /* The scheduler is suspended. uxSchedulerSuspended is updated + * only when the task is not requested to yield. */ + mtCOVERAGE_TEST_MARKER(); + } + + portRELEASE_TASK_LOCK( xCoreID ); + portMEMORY_BARRIER(); + configASSERT( pxThisTCB->xTaskRunState == taskTASK_SCHEDULED_TO_YIELD ); + + portENABLE_INTERRUPTS(); + + /* Enabling interrupts should cause this core to immediately service + * the pending interrupt and yield. After servicing the pending interrupt, + * the task needs to re-evaluate its run state within this loop, as + * other cores may have requested this task to yield, potentially altering + * its run state. */ + + portDISABLE_INTERRUPTS(); + + xCoreID = ( BaseType_t ) portGET_CORE_ID(); + portGET_TASK_LOCK( xCoreID ); + portGET_ISR_LOCK( xCoreID ); + + portSET_CRITICAL_NESTING_COUNT( xCoreID, uxPrevCriticalNesting ); + + if( uxPrevCriticalNesting == 0U ) + { + portRELEASE_ISR_LOCK( xCoreID ); + } + } + } +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + +/*-----------------------------------------------------------*/ + +#if ( configNUMBER_OF_CORES > 1 ) + static void prvYieldForTask( const TCB_t * pxTCB ) + { + BaseType_t xLowestPriorityToPreempt; + BaseType_t xCurrentCoreTaskPriority; + BaseType_t xLowestPriorityCore = ( BaseType_t ) -1; + BaseType_t xCoreID; + const BaseType_t xCurrentCoreID = ( BaseType_t ) portGET_CORE_ID(); + + #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) + BaseType_t xYieldCount = 0; + #endif /* #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) */ + + /* This must be called from a critical section. */ + configASSERT( portGET_CRITICAL_NESTING_COUNT( xCurrentCoreID ) > 0U ); + + #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) + + /* No task should yield for this one if it is a lower priority + * than priority level of currently ready tasks. */ + if( pxTCB->uxPriority >= uxTopReadyPriority ) + #else + /* Yield is not required for a task which is already running. */ + if( taskTASK_IS_RUNNING( pxTCB ) == pdFALSE ) + #endif + { + xLowestPriorityToPreempt = ( BaseType_t ) pxTCB->uxPriority; + + /* xLowestPriorityToPreempt will be decremented to -1 if the priority of pxTCB + * is 0. This is ok as we will give system idle tasks a priority of -1 below. */ + --xLowestPriorityToPreempt; + + for( xCoreID = ( BaseType_t ) 0; xCoreID < ( BaseType_t ) configNUMBER_OF_CORES; xCoreID++ ) + { + xCurrentCoreTaskPriority = ( BaseType_t ) pxCurrentTCBs[ xCoreID ]->uxPriority; + + /* System idle tasks are being assigned a priority of tskIDLE_PRIORITY - 1 here. */ + if( ( pxCurrentTCBs[ xCoreID ]->uxTaskAttributes & taskATTRIBUTE_IS_IDLE ) != 0U ) + { + xCurrentCoreTaskPriority = ( BaseType_t ) ( xCurrentCoreTaskPriority - 1 ); + } + + if( ( taskTASK_IS_RUNNING( pxCurrentTCBs[ xCoreID ] ) != pdFALSE ) && ( xYieldPendings[ xCoreID ] == pdFALSE ) ) + { + #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) + if( taskTASK_IS_RUNNING( pxTCB ) == pdFALSE ) + #endif + { + if( xCurrentCoreTaskPriority <= xLowestPriorityToPreempt ) + { + #if ( configUSE_CORE_AFFINITY == 1 ) + if( ( pxTCB->uxCoreAffinityMask & ( ( UBaseType_t ) 1U << ( UBaseType_t ) xCoreID ) ) != 0U ) + #endif + { + #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + if( pxCurrentTCBs[ xCoreID ]->xPreemptionDisable == pdFALSE ) + #endif + { + xLowestPriorityToPreempt = xCurrentCoreTaskPriority; + xLowestPriorityCore = xCoreID; + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) + { + /* Yield all currently running non-idle tasks with a priority lower than + * the task that needs to run. */ + if( ( xCurrentCoreTaskPriority > ( ( BaseType_t ) tskIDLE_PRIORITY - 1 ) ) && + ( xCurrentCoreTaskPriority < ( BaseType_t ) pxTCB->uxPriority ) ) + { + prvYieldCore( xCoreID ); + xYieldCount++; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) + if( ( xYieldCount == 0 ) && ( xLowestPriorityCore >= 0 ) ) + #else /* #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) */ + if( xLowestPriorityCore >= 0 ) + #endif /* #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) */ + { + prvYieldCore( xLowestPriorityCore ); + } + + #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) + /* Verify that the calling core always yields to higher priority tasks. */ + if( ( ( pxCurrentTCBs[ xCurrentCoreID ]->uxTaskAttributes & taskATTRIBUTE_IS_IDLE ) == 0U ) && + ( pxTCB->uxPriority > pxCurrentTCBs[ xCurrentCoreID ]->uxPriority ) ) + { + configASSERT( ( xYieldPendings[ xCurrentCoreID ] == pdTRUE ) || + ( taskTASK_IS_RUNNING( pxCurrentTCBs[ xCurrentCoreID ] ) == pdFALSE ) ); + } + #endif + } + } +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( configNUMBER_OF_CORES > 1 ) + static void prvSelectHighestPriorityTask( BaseType_t xCoreID ) + { + UBaseType_t uxCurrentPriority = uxTopReadyPriority; + BaseType_t xTaskScheduled = pdFALSE; + BaseType_t xDecrementTopPriority = pdTRUE; + TCB_t * pxTCB = NULL; + + #if ( configUSE_CORE_AFFINITY == 1 ) + const TCB_t * pxPreviousTCB = NULL; + #endif + #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) + BaseType_t xPriorityDropped = pdFALSE; + #endif + + /* This function should be called when scheduler is running. */ + configASSERT( xSchedulerRunning == pdTRUE ); + + /* A new task is created and a running task with the same priority yields + * itself to run the new task. When a running task yields itself, it is still + * in the ready list. This running task will be selected before the new task + * since the new task is always added to the end of the ready list. + * The other problem is that the running task still in the same position of + * the ready list when it yields itself. It is possible that it will be selected + * earlier then other tasks which waits longer than this task. + * + * To fix these problems, the running task should be put to the end of the + * ready list before searching for the ready task in the ready list. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxCurrentTCBs[ xCoreID ]->uxPriority ] ), + &pxCurrentTCBs[ xCoreID ]->xStateListItem ) == pdTRUE ) + { + ( void ) uxListRemove( &pxCurrentTCBs[ xCoreID ]->xStateListItem ); + vListInsertEnd( &( pxReadyTasksLists[ pxCurrentTCBs[ xCoreID ]->uxPriority ] ), + &pxCurrentTCBs[ xCoreID ]->xStateListItem ); + } + + while( xTaskScheduled == pdFALSE ) + { + #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) + { + if( uxCurrentPriority < uxTopReadyPriority ) + { + /* We can't schedule any tasks, other than idle, that have a + * priority lower than the priority of a task currently running + * on another core. */ + uxCurrentPriority = tskIDLE_PRIORITY; + } + } + #endif + + if( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxCurrentPriority ] ) ) == pdFALSE ) + { + const List_t * const pxReadyList = &( pxReadyTasksLists[ uxCurrentPriority ] ); + const ListItem_t * pxEndMarker = listGET_END_MARKER( pxReadyList ); + ListItem_t * pxIterator; + + /* The ready task list for uxCurrentPriority is not empty, so uxTopReadyPriority + * must not be decremented any further. */ + xDecrementTopPriority = pdFALSE; + + for( pxIterator = listGET_HEAD_ENTRY( pxReadyList ); pxIterator != pxEndMarker; pxIterator = listGET_NEXT( pxIterator ) ) + { + /* MISRA Ref 11.5.3 [Void pointer assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxTCB = ( TCB_t * ) listGET_LIST_ITEM_OWNER( pxIterator ); + + #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) + { + /* When falling back to the idle priority because only one priority + * level is allowed to run at a time, we should ONLY schedule the true + * idle tasks, not user tasks at the idle priority. */ + if( uxCurrentPriority < uxTopReadyPriority ) + { + if( ( pxTCB->uxTaskAttributes & taskATTRIBUTE_IS_IDLE ) == 0U ) + { + continue; + } + } + } + #endif /* #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) */ + + if( pxTCB->xTaskRunState == taskTASK_NOT_RUNNING ) + { + #if ( configUSE_CORE_AFFINITY == 1 ) + if( ( pxTCB->uxCoreAffinityMask & ( ( UBaseType_t ) 1U << ( UBaseType_t ) xCoreID ) ) != 0U ) + #endif + { + /* If the task is not being executed by any core swap it in. */ + pxCurrentTCBs[ xCoreID ]->xTaskRunState = taskTASK_NOT_RUNNING; + #if ( configUSE_CORE_AFFINITY == 1 ) + pxPreviousTCB = pxCurrentTCBs[ xCoreID ]; + #endif + pxTCB->xTaskRunState = xCoreID; + pxCurrentTCBs[ xCoreID ] = pxTCB; + xTaskScheduled = pdTRUE; + } + } + else if( pxTCB == pxCurrentTCBs[ xCoreID ] ) + { + configASSERT( ( pxTCB->xTaskRunState == xCoreID ) || ( pxTCB->xTaskRunState == taskTASK_SCHEDULED_TO_YIELD ) ); + + #if ( configUSE_CORE_AFFINITY == 1 ) + if( ( pxTCB->uxCoreAffinityMask & ( ( UBaseType_t ) 1U << ( UBaseType_t ) xCoreID ) ) != 0U ) + #endif + { + /* The task is already running on this core, mark it as scheduled. */ + pxTCB->xTaskRunState = xCoreID; + xTaskScheduled = pdTRUE; + } + } + else + { + /* This task is running on the core other than xCoreID. */ + mtCOVERAGE_TEST_MARKER(); + } + + if( xTaskScheduled != pdFALSE ) + { + /* A task has been selected to run on this core. */ + break; + } + } + } + else + { + if( xDecrementTopPriority != pdFALSE ) + { + uxTopReadyPriority--; + #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) + { + xPriorityDropped = pdTRUE; + } + #endif + } + } + + /* There are configNUMBER_OF_CORES Idle tasks created when scheduler started. + * The scheduler should be able to select a task to run when uxCurrentPriority + * is tskIDLE_PRIORITY. uxCurrentPriority is never decreased to value blow + * tskIDLE_PRIORITY. */ + if( uxCurrentPriority > tskIDLE_PRIORITY ) + { + uxCurrentPriority--; + } + else + { + /* This function is called when idle task is not created. Break the + * loop to prevent uxCurrentPriority overrun. */ + break; + } + } + + #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) + { + if( xTaskScheduled == pdTRUE ) + { + if( xPriorityDropped != pdFALSE ) + { + /* There may be several ready tasks that were being prevented from running because there was + * a higher priority task running. Now that the last of the higher priority tasks is no longer + * running, make sure all the other idle tasks yield. */ + BaseType_t x; + + for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configNUMBER_OF_CORES; x++ ) + { + if( ( pxCurrentTCBs[ x ]->uxTaskAttributes & taskATTRIBUTE_IS_IDLE ) != 0U ) + { + prvYieldCore( x ); + } + } + } + } + } + #endif /* #if ( configRUN_MULTIPLE_PRIORITIES == 0 ) */ + + #if ( configUSE_CORE_AFFINITY == 1 ) + { + if( xTaskScheduled == pdTRUE ) + { + if( ( pxPreviousTCB != NULL ) && ( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxPreviousTCB->uxPriority ] ), &( pxPreviousTCB->xStateListItem ) ) != pdFALSE ) ) + { + /* A ready task was just evicted from this core. See if it can be + * scheduled on any other core. */ + UBaseType_t uxCoreMap = pxPreviousTCB->uxCoreAffinityMask; + BaseType_t xLowestPriority = ( BaseType_t ) pxPreviousTCB->uxPriority; + BaseType_t xLowestPriorityCore = -1; + BaseType_t x; + + if( ( pxPreviousTCB->uxTaskAttributes & taskATTRIBUTE_IS_IDLE ) != 0U ) + { + xLowestPriority = xLowestPriority - 1; + } + + if( ( uxCoreMap & ( ( UBaseType_t ) 1U << ( UBaseType_t ) xCoreID ) ) != 0U ) + { + /* pxPreviousTCB was removed from this core and this core is not excluded + * from it's core affinity mask. + * + * pxPreviousTCB is preempted by the new higher priority task + * pxCurrentTCBs[ xCoreID ]. When searching a new core for pxPreviousTCB, + * we do not need to look at the cores on which pxCurrentTCBs[ xCoreID ] + * is allowed to run. The reason is - when more than one cores are + * eligible for an incoming task, we preempt the core with the minimum + * priority task. Because this core (i.e. xCoreID) was preempted for + * pxCurrentTCBs[ xCoreID ], this means that all the others cores + * where pxCurrentTCBs[ xCoreID ] can run, are running tasks with priority + * no lower than pxPreviousTCB's priority. Therefore, the only cores where + * which can be preempted for pxPreviousTCB are the ones where + * pxCurrentTCBs[ xCoreID ] is not allowed to run (and obviously, + * pxPreviousTCB is allowed to run). + * + * This is an optimization which reduces the number of cores needed to be + * searched for pxPreviousTCB to run. */ + uxCoreMap &= ~( pxCurrentTCBs[ xCoreID ]->uxCoreAffinityMask ); + } + else + { + /* pxPreviousTCB's core affinity mask is changed and it is no longer + * allowed to run on this core. Searching all the cores in pxPreviousTCB's + * new core affinity mask to find a core on which it can run. */ + } + + uxCoreMap &= ( ( 1U << configNUMBER_OF_CORES ) - 1U ); + + for( x = ( ( BaseType_t ) configNUMBER_OF_CORES - 1 ); x >= ( BaseType_t ) 0; x-- ) + { + UBaseType_t uxCore = ( UBaseType_t ) x; + BaseType_t xTaskPriority; + + if( ( uxCoreMap & ( ( UBaseType_t ) 1U << uxCore ) ) != 0U ) + { + xTaskPriority = ( BaseType_t ) pxCurrentTCBs[ uxCore ]->uxPriority; + + if( ( pxCurrentTCBs[ uxCore ]->uxTaskAttributes & taskATTRIBUTE_IS_IDLE ) != 0U ) + { + xTaskPriority = xTaskPriority - ( BaseType_t ) 1; + } + + uxCoreMap &= ~( ( UBaseType_t ) 1U << uxCore ); + + if( ( xTaskPriority < xLowestPriority ) && + ( taskTASK_IS_RUNNING( pxCurrentTCBs[ uxCore ] ) != pdFALSE ) && + ( xYieldPendings[ uxCore ] == pdFALSE ) ) + { + #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + if( pxCurrentTCBs[ uxCore ]->xPreemptionDisable == pdFALSE ) + #endif + { + xLowestPriority = xTaskPriority; + xLowestPriorityCore = ( BaseType_t ) uxCore; + } + } + } + } + + if( xLowestPriorityCore >= 0 ) + { + prvYieldCore( xLowestPriorityCore ); + } + } + } + } + #endif /* #if ( configUSE_CORE_AFFINITY == 1 ) */ + } + +#endif /* ( configNUMBER_OF_CORES > 1 ) */ + +/*-----------------------------------------------------------*/ + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + static TCB_t * prvCreateStaticTask( 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, + TaskHandle_t * const pxCreatedTask ) + { + TCB_t * pxNewTCB; + + configASSERT( puxStackBuffer != NULL ); + configASSERT( pxTaskBuffer != NULL ); + + #if ( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + * variable of type StaticTask_t equals the size of the real task + * structure. */ + volatile size_t xSize = sizeof( StaticTask_t ); + configASSERT( xSize == sizeof( TCB_t ) ); + ( void ) xSize; /* Prevent unused variable warning when configASSERT() is not used. */ + } + #endif /* configASSERT_DEFINED */ + + if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ) + { + /* The memory used for the task's TCB and stack are passed into this + * function - use them. */ + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + pxNewTCB = ( TCB_t * ) pxTaskBuffer; + ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; + + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + * task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); + } + else + { + pxNewTCB = NULL; + } + + return pxNewTCB; + } +/*-----------------------------------------------------------*/ + + TaskHandle_t 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 ) + { + TaskHandle_t xReturn = NULL; + TCB_t * pxNewTCB; + + traceENTER_xTaskCreateStatic( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, puxStackBuffer, pxTaskBuffer ); + + pxNewTCB = prvCreateStaticTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, puxStackBuffer, pxTaskBuffer, &xReturn ); + + if( pxNewTCB != NULL ) + { + #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + { + /* Set the task's affinity before scheduling it. */ + pxNewTCB->uxCoreAffinityMask = configTASK_DEFAULT_CORE_AFFINITY; + } + #endif + + prvAddNewTaskToReadyList( pxNewTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_xTaskCreateStatic( xReturn ); + + return xReturn; + } +/*-----------------------------------------------------------*/ + + #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + TaskHandle_t xTaskCreateStaticAffinitySet( 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, + UBaseType_t uxCoreAffinityMask ) + { + TaskHandle_t xReturn = NULL; + TCB_t * pxNewTCB; + + traceENTER_xTaskCreateStaticAffinitySet( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, puxStackBuffer, pxTaskBuffer, uxCoreAffinityMask ); + + pxNewTCB = prvCreateStaticTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, puxStackBuffer, pxTaskBuffer, &xReturn ); + + if( pxNewTCB != NULL ) + { + /* Set the task's affinity before scheduling it. */ + pxNewTCB->uxCoreAffinityMask = uxCoreAffinityMask; + + prvAddNewTaskToReadyList( pxNewTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_xTaskCreateStaticAffinitySet( xReturn ); + + return xReturn; + } + #endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ + +#endif /* SUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + static TCB_t * prvCreateRestrictedStaticTask( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * const pxCreatedTask ) + { + TCB_t * pxNewTCB; + + configASSERT( pxTaskDefinition->puxStackBuffer != NULL ); + configASSERT( pxTaskDefinition->pxTaskBuffer != NULL ); + + if( ( pxTaskDefinition->puxStackBuffer != NULL ) && ( pxTaskDefinition->pxTaskBuffer != NULL ) ) + { + /* Allocate space for the TCB. Where the memory comes from depends + * on the implementation of the port malloc function and whether or + * not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pxTaskDefinition->pxTaskBuffer; + ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + * task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions ); + } + else + { + pxNewTCB = NULL; + } + + return pxNewTCB; + } +/*-----------------------------------------------------------*/ + + BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn; + + traceENTER_xTaskCreateRestrictedStatic( pxTaskDefinition, pxCreatedTask ); + + configASSERT( pxTaskDefinition != NULL ); + + pxNewTCB = prvCreateRestrictedStaticTask( pxTaskDefinition, pxCreatedTask ); + + if( pxNewTCB != NULL ) + { + #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + { + /* Set the task's affinity before scheduling it. */ + pxNewTCB->uxCoreAffinityMask = configTASK_DEFAULT_CORE_AFFINITY; + } + #endif + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + traceRETURN_xTaskCreateRestrictedStatic( xReturn ); + + return xReturn; + } +/*-----------------------------------------------------------*/ + + #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + BaseType_t xTaskCreateRestrictedStaticAffinitySet( const TaskParameters_t * const pxTaskDefinition, + UBaseType_t uxCoreAffinityMask, + TaskHandle_t * pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn; + + traceENTER_xTaskCreateRestrictedStaticAffinitySet( pxTaskDefinition, uxCoreAffinityMask, pxCreatedTask ); + + configASSERT( pxTaskDefinition != NULL ); + + pxNewTCB = prvCreateRestrictedStaticTask( pxTaskDefinition, pxCreatedTask ); + + if( pxNewTCB != NULL ) + { + /* Set the task's affinity before scheduling it. */ + pxNewTCB->uxCoreAffinityMask = uxCoreAffinityMask; + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + traceRETURN_xTaskCreateRestrictedStaticAffinitySet( xReturn ); + + return xReturn; + } + #endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ + +#endif /* ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + static TCB_t * prvCreateRestrictedTask( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * const pxCreatedTask ) + { + TCB_t * pxNewTCB; + + configASSERT( pxTaskDefinition->puxStackBuffer ); + + if( pxTaskDefinition->puxStackBuffer != NULL ) + { + /* MISRA Ref 11.5.1 [Malloc memory assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note + * this task had a statically allocated stack in case it is + * later deleted. The TCB was allocated dynamically. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions ); + } + } + else + { + pxNewTCB = NULL; + } + + return pxNewTCB; + } +/*-----------------------------------------------------------*/ + + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, + TaskHandle_t * pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn; + + traceENTER_xTaskCreateRestricted( pxTaskDefinition, pxCreatedTask ); + + pxNewTCB = prvCreateRestrictedTask( pxTaskDefinition, pxCreatedTask ); + + if( pxNewTCB != NULL ) + { + #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + { + /* Set the task's affinity before scheduling it. */ + pxNewTCB->uxCoreAffinityMask = configTASK_DEFAULT_CORE_AFFINITY; + } + #endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ + + prvAddNewTaskToReadyList( pxNewTCB ); + + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + traceRETURN_xTaskCreateRestricted( xReturn ); + + return xReturn; + } +/*-----------------------------------------------------------*/ + + #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + BaseType_t xTaskCreateRestrictedAffinitySet( const TaskParameters_t * const pxTaskDefinition, + UBaseType_t uxCoreAffinityMask, + TaskHandle_t * pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn; + + traceENTER_xTaskCreateRestrictedAffinitySet( pxTaskDefinition, uxCoreAffinityMask, pxCreatedTask ); + + pxNewTCB = prvCreateRestrictedTask( pxTaskDefinition, pxCreatedTask ); + + if( pxNewTCB != NULL ) + { + /* Set the task's affinity before scheduling it. */ + pxNewTCB->uxCoreAffinityMask = uxCoreAffinityMask; + + prvAddNewTaskToReadyList( pxNewTCB ); + + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + traceRETURN_xTaskCreateRestrictedAffinitySet( xReturn ); + + return xReturn; + } + #endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ + + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + static TCB_t * prvCreateTask( TaskFunction_t pxTaskCode, + const char * const pcName, + const configSTACK_DEPTH_TYPE uxStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask ) + { + TCB_t * pxNewTCB; + + /* If the stack grows down then allocate the stack then the TCB so the stack + * does not grow into the TCB. Likewise if the stack grows up then allocate + * the TCB then the stack. */ + #if ( portSTACK_GROWTH > 0 ) + { + /* Allocate space for the TCB. Where the memory comes from depends on + * the implementation of the port malloc function and whether or not static + * allocation is being used. */ + /* MISRA Ref 11.5.1 [Malloc memory assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + + /* Allocate space for the stack used by the task being created. + * The base of the stack memory stored in the TCB so the task can + * be deleted later if required. */ + /* MISRA Ref 11.5.1 [Malloc memory assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) ); + + if( pxNewTCB->pxStack == NULL ) + { + /* Could not allocate the stack. Delete the allocated TCB. */ + vPortFree( pxNewTCB ); + pxNewTCB = NULL; + } + } + } + #else /* portSTACK_GROWTH */ + { + StackType_t * pxStack; + + /* Allocate space for the stack used by the task being created. */ + /* MISRA Ref 11.5.1 [Malloc memory assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxStack = ( StackType_t * ) pvPortMallocStack( ( ( ( size_t ) uxStackDepth ) * sizeof( StackType_t ) ) ); + + if( pxStack != NULL ) + { + /* Allocate space for the TCB. */ + /* MISRA Ref 11.5.1 [Malloc memory assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + ( void ) memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxStack; + } + else + { + /* The stack cannot be used as the TCB was not created. Free + * it again. */ + vPortFreeStack( pxStack ); + } + } + else + { + pxNewTCB = NULL; + } + } + #endif /* portSTACK_GROWTH */ + + if( pxNewTCB != NULL ) + { + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + * task was created dynamically in case it is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); + } + + return pxNewTCB; + } +/*-----------------------------------------------------------*/ + + BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, + const char * const pcName, + const configSTACK_DEPTH_TYPE uxStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn; + + traceENTER_xTaskCreate( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask ); + + pxNewTCB = prvCreateTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask ); + + if( pxNewTCB != NULL ) + { + #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + { + /* Set the task's affinity before scheduling it. */ + pxNewTCB->uxCoreAffinityMask = configTASK_DEFAULT_CORE_AFFINITY; + } + #endif + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + traceRETURN_xTaskCreate( xReturn ); + + return xReturn; + } +/*-----------------------------------------------------------*/ + + #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + BaseType_t xTaskCreateAffinitySet( TaskFunction_t pxTaskCode, + const char * const pcName, + const configSTACK_DEPTH_TYPE uxStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + UBaseType_t uxCoreAffinityMask, + TaskHandle_t * const pxCreatedTask ) + { + TCB_t * pxNewTCB; + BaseType_t xReturn; + + traceENTER_xTaskCreateAffinitySet( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, uxCoreAffinityMask, pxCreatedTask ); + + pxNewTCB = prvCreateTask( pxTaskCode, pcName, uxStackDepth, pvParameters, uxPriority, pxCreatedTask ); + + if( pxNewTCB != NULL ) + { + /* Set the task's affinity before scheduling it. */ + pxNewTCB->uxCoreAffinityMask = uxCoreAffinityMask; + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + traceRETURN_xTaskCreateAffinitySet( xReturn ); + + return xReturn; + } + #endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, + const configSTACK_DEPTH_TYPE uxStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t * pxNewTCB, + const MemoryRegion_t * const xRegions ) +{ + StackType_t * pxTopOfStack; + UBaseType_t x; + + #if ( portUSING_MPU_WRAPPERS == 1 ) + /* Should the task be created in privileged mode? */ + BaseType_t xRunPrivileged; + + if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) + { + xRunPrivileged = pdTRUE; + } + else + { + xRunPrivileged = pdFALSE; + } + uxPriority &= ~portPRIVILEGE_BIT; + #endif /* portUSING_MPU_WRAPPERS == 1 */ + + /* Avoid dependency on memset() if it is not required. */ + #if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 ) + { + /* Fill the stack with a known value to assist debugging. */ + ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) uxStackDepth * sizeof( StackType_t ) ); + } + #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */ + + /* Calculate the top of stack address. This depends on whether the stack + * grows from high memory to low (as per the 80x86) or vice versa. + * portSTACK_GROWTH is used to make the result positive or negative as required + * by the port. */ + #if ( portSTACK_GROWTH < 0 ) + { + pxTopOfStack = &( pxNewTCB->pxStack[ uxStackDepth - ( configSTACK_DEPTH_TYPE ) 1 ] ); + pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); + + /* Check the alignment of the calculated top of stack is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0U ) ); + + #if ( configRECORD_STACK_HIGH_ADDRESS == 1 ) + { + /* Also record the stack's high address, which may assist + * debugging. */ + pxNewTCB->pxEndOfStack = pxTopOfStack; + } + #endif /* configRECORD_STACK_HIGH_ADDRESS */ + } + #else /* portSTACK_GROWTH */ + { + pxTopOfStack = pxNewTCB->pxStack; + pxTopOfStack = ( StackType_t * ) ( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) + portBYTE_ALIGNMENT_MASK ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); + + /* Check the alignment of the calculated top of stack is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0U ) ); + + /* The other extreme of the stack space is required if stack checking is + * performed. */ + pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( uxStackDepth - ( configSTACK_DEPTH_TYPE ) 1 ); + } + #endif /* portSTACK_GROWTH */ + + /* Store the task name in the TCB. */ + if( pcName != NULL ) + { + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + pxNewTCB->pcTaskName[ x ] = pcName[ x ]; + + /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than + * configMAX_TASK_NAME_LEN characters just in case the memory after the + * string is not accessible (extremely unlikely). */ + if( pcName[ x ] == ( char ) 0x00 ) + { + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Ensure the name string is terminated in the case that the string length + * was greater or equal to configMAX_TASK_NAME_LEN. */ + pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1U ] = '\0'; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* This is used as an array index so must ensure it's not too large. */ + configASSERT( uxPriority < configMAX_PRIORITIES ); + + if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxNewTCB->uxPriority = uxPriority; + #if ( configUSE_MUTEXES == 1 ) + { + pxNewTCB->uxBasePriority = uxPriority; + } + #endif /* configUSE_MUTEXES */ + + vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); + vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); + + /* Set the pxNewTCB as a link back from the ListItem_t. This is so we can get + * back to the containing TCB from a generic item in a list. */ + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); + + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, uxStackDepth ); + } + #else + { + /* Avoid compiler warning about unreferenced parameter. */ + ( void ) xRegions; + } + #endif + + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + { + /* Allocate and initialize memory for the task's TLS Block. */ + configINIT_TLS_BLOCK( pxNewTCB->xTLSBlock, pxTopOfStack ); + } + #endif + + /* Initialize the TCB stack to look as if the task was already running, + * but had been interrupted by the scheduler. The return address is set + * to the start of the task function. Once the stack has been initialised + * the top of stack variable is updated. */ + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + /* If the port has capability to detect stack overflow, + * pass the stack end address to the stack initialization + * function as well. */ + #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if ( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged, &( pxNewTCB->xMPUSettings ) ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ + } + #else /* portUSING_MPU_WRAPPERS */ + { + /* If the port has capability to detect stack overflow, + * pass the stack end address to the stack initialization + * function as well. */ + #if ( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if ( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ + + #if ( portSTACK_GROWTH < 0 ) + { + configASSERT( ( ( portPOINTER_SIZE_TYPE ) ( pxTopOfStack - pxNewTCB->pxTopOfStack ) ) < ( ( portPOINTER_SIZE_TYPE ) uxStackDepth ) ); + } + #else /* portSTACK_GROWTH */ + { + configASSERT( ( ( portPOINTER_SIZE_TYPE ) ( pxNewTCB->pxTopOfStack - pxTopOfStack ) ) < ( ( portPOINTER_SIZE_TYPE ) uxStackDepth ) ); + } + #endif /* portSTACK_GROWTH */ + } + #endif /* portUSING_MPU_WRAPPERS */ + + /* Initialize task state and task attributes. */ + #if ( configNUMBER_OF_CORES > 1 ) + { + pxNewTCB->xTaskRunState = taskTASK_NOT_RUNNING; + + /* Is this an idle task? */ + if( ( ( TaskFunction_t ) pxTaskCode == ( TaskFunction_t ) ( &prvIdleTask ) ) || ( ( TaskFunction_t ) pxTaskCode == ( TaskFunction_t ) ( &prvPassiveIdleTask ) ) ) + { + pxNewTCB->uxTaskAttributes |= taskATTRIBUTE_IS_IDLE; + } + } + #endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + + if( pxCreatedTask != NULL ) + { + /* Pass the handle out in an anonymous way. The handle can be used to + * change the created task's priority, delete the created task, etc.*/ + *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } +} +/*-----------------------------------------------------------*/ + +#if ( configNUMBER_OF_CORES == 1 ) + + static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) + { + /* Ensure interrupts don't access the task lists while the lists are being + * updated. */ + taskENTER_CRITICAL(); + { + uxCurrentNumberOfTasks = ( UBaseType_t ) ( uxCurrentNumberOfTasks + 1U ); + + if( pxCurrentTCB == NULL ) + { + /* There are no other tasks, or all the other tasks are in + * the suspended state - make this the current task. */ + pxCurrentTCB = pxNewTCB; + + if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) + { + /* This is the first task to be created so do the preliminary + * initialisation required. We will not recover if this call + * fails, but we will report the failure. */ + prvInitialiseTaskLists(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* If the scheduler is not already running, make this task the + * current task if it is the highest priority task to be created + * so far. */ + if( xSchedulerRunning == pdFALSE ) + { + if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) + { + pxCurrentTCB = pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + uxTaskNumber++; + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + /* Add a counter into the TCB for tracing only. */ + pxNewTCB->uxTCBNumber = uxTaskNumber; + } + #endif /* configUSE_TRACE_FACILITY */ + traceTASK_CREATE( pxNewTCB ); + + prvAddTaskToReadyList( pxNewTCB ); + + portSETUP_TCB( pxNewTCB ); + } + taskEXIT_CRITICAL(); + + if( xSchedulerRunning != pdFALSE ) + { + /* If the created task is of a higher priority than the current task + * then it should run now. */ + taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxNewTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#else /* #if ( configNUMBER_OF_CORES == 1 ) */ + + static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) + { + /* Ensure interrupts don't access the task lists while the lists are being + * updated. */ + taskENTER_CRITICAL(); + { + uxCurrentNumberOfTasks++; + + if( xSchedulerRunning == pdFALSE ) + { + if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) + { + /* This is the first task to be created so do the preliminary + * initialisation required. We will not recover if this call + * fails, but we will report the failure. */ + prvInitialiseTaskLists(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* All the cores start with idle tasks before the SMP scheduler + * is running. Idle tasks are assigned to cores when they are + * created in prvCreateIdleTasks(). */ + } + + uxTaskNumber++; + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + /* Add a counter into the TCB for tracing only. */ + pxNewTCB->uxTCBNumber = uxTaskNumber; + } + #endif /* configUSE_TRACE_FACILITY */ + traceTASK_CREATE( pxNewTCB ); + + prvAddTaskToReadyList( pxNewTCB ); + + portSETUP_TCB( pxNewTCB ); + + if( xSchedulerRunning != pdFALSE ) + { + /* If the created task is of a higher priority than another + * currently running task and preemption is on then it should + * run now. */ + taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxNewTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + } + +#endif /* #if ( configNUMBER_OF_CORES == 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) + + static size_t prvSnprintfReturnValueToCharsWritten( int iSnprintfReturnValue, + size_t n ) + { + size_t uxCharsWritten; + + if( iSnprintfReturnValue < 0 ) + { + /* Encoding error - Return 0 to indicate that nothing + * was written to the buffer. */ + uxCharsWritten = 0; + } + else if( iSnprintfReturnValue >= ( int ) n ) + { + /* This is the case when the supplied buffer is not + * large to hold the generated string. Return the + * number of characters actually written without + * counting the terminating NULL character. */ + uxCharsWritten = n - 1U; + } + else + { + /* Complete string was written to the buffer. */ + uxCharsWritten = ( size_t ) iSnprintfReturnValue; + } + + return uxCharsWritten; + } + +#endif /* #if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + void vTaskDelete( TaskHandle_t xTaskToDelete ) + { + TCB_t * pxTCB; + BaseType_t xDeleteTCBInIdleTask = pdFALSE; + BaseType_t xTaskIsRunningOrYielding; + + traceENTER_vTaskDelete( xTaskToDelete ); + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the calling task that is + * being deleted. */ + pxTCB = prvGetTCBFromHandle( xTaskToDelete ); + configASSERT( pxTCB != NULL ); + + /* Remove task from the ready/delayed list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Is the task waiting on an event also? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Increment the uxTaskNumber also so kernel aware debuggers can + * detect that the task lists need re-generating. This is done before + * portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will + * not return. */ + uxTaskNumber++; + + /* Use temp variable as distinct sequence points for reading volatile + * variables prior to a logical operator to ensure compliance with + * MISRA C 2012 Rule 13.5. */ + xTaskIsRunningOrYielding = taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD( pxTCB ); + + /* If the task is running (or yielding), we must add it to the + * termination list so that an idle task can delete it when it is + * no longer running. */ + if( ( xSchedulerRunning != pdFALSE ) && ( xTaskIsRunningOrYielding != pdFALSE ) ) + { + /* A running task or a task which is scheduled to yield is being + * deleted. This cannot complete when the task is still running + * on a core, as a context switch to another task is required. + * Place the task in the termination list. The idle task will check + * the termination list and free up any memory allocated by the + * scheduler for the TCB and stack of the deleted task. */ + vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) ); + + /* Increment the ucTasksDeleted variable so the idle task knows + * there is a task that has been deleted and that it should therefore + * check the xTasksWaitingTermination list. */ + ++uxDeletedTasksWaitingCleanUp; + + /* Call the delete hook before portPRE_TASK_DELETE_HOOK() as + * portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */ + traceTASK_DELETE( pxTCB ); + + /* Delete the task TCB in idle task. */ + xDeleteTCBInIdleTask = pdTRUE; + + /* The pre-delete hook is primarily for the Windows simulator, + * in which Windows specific clean up operations are performed, + * after which it is not possible to yield away from this task - + * hence xYieldPending is used to latch that a context switch is + * required. */ + #if ( configNUMBER_OF_CORES == 1 ) + portPRE_TASK_DELETE_HOOK( pxTCB, &( xYieldPendings[ 0 ] ) ); + #else + portPRE_TASK_DELETE_HOOK( pxTCB, &( xYieldPendings[ pxTCB->xTaskRunState ] ) ); + #endif + + /* In the case of SMP, it is possible that the task being deleted + * is running on another core. We must evict the task before + * exiting the critical section to ensure that the task cannot + * take an action which puts it back on ready/state/event list, + * thereby nullifying the delete operation. Once evicted, the + * task won't be scheduled ever as it will no longer be on the + * ready list. */ + #if ( configNUMBER_OF_CORES > 1 ) + { + if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) + { + if( pxTCB->xTaskRunState == ( BaseType_t ) portGET_CORE_ID() ) + { + configASSERT( uxSchedulerSuspended == 0 ); + taskYIELD_WITHIN_API(); + } + else + { + prvYieldCore( pxTCB->xTaskRunState ); + } + } + } + #endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + } + else + { + --uxCurrentNumberOfTasks; + traceTASK_DELETE( pxTCB ); + + /* Reset the next expected unblock time in case it referred to + * the task that has just been deleted. */ + prvResetNextTaskUnblockTime(); + } + } + taskEXIT_CRITICAL(); + + /* If the task is not deleting itself, call prvDeleteTCB from outside of + * critical section. If a task deletes itself, prvDeleteTCB is called + * from prvCheckTasksWaitingTermination which is called from Idle task. */ + if( xDeleteTCBInIdleTask != pdTRUE ) + { + prvDeleteTCB( pxTCB ); + } + + /* Force a reschedule if it is the currently running task that has just + * been deleted. */ + #if ( configNUMBER_OF_CORES == 1 ) + { + if( xSchedulerRunning != pdFALSE ) + { + if( pxTCB == pxCurrentTCB ) + { + configASSERT( uxSchedulerSuspended == 0 ); + taskYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + + traceRETURN_vTaskDelete(); + } + +#endif /* INCLUDE_vTaskDelete */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskDelayUntil == 1 ) + + BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, + const TickType_t xTimeIncrement ) + { + TickType_t xTimeToWake; + BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE; + + traceENTER_xTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement ); + + configASSERT( pxPreviousWakeTime ); + configASSERT( ( xTimeIncrement > 0U ) ); + + vTaskSuspendAll(); + { + /* Minor optimisation. The tick count cannot change in this + * block. */ + const TickType_t xConstTickCount = xTickCount; + + configASSERT( uxSchedulerSuspended == 1U ); + + /* Generate the tick time at which the task wants to wake. */ + xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; + + if( xConstTickCount < *pxPreviousWakeTime ) + { + /* The tick count has overflowed since this function was + * lasted called. In this case the only time we should ever + * actually delay is if the wake time has also overflowed, + * and the wake time is greater than the tick time. When this + * is the case it is as if neither time had overflowed. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) ) + { + xShouldDelay = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The tick time has not overflowed. In this case we will + * delay if either the wake time has overflowed, and/or the + * tick time is less than the wake time. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) ) + { + xShouldDelay = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Update the wake time ready for the next call. */ + *pxPreviousWakeTime = xTimeToWake; + + if( xShouldDelay != pdFALSE ) + { + traceTASK_DELAY_UNTIL( xTimeToWake ); + + /* prvAddCurrentTaskToDelayedList() needs the block time, not + * the time to wake, so subtract the current tick count. */ + prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + xAlreadyYielded = xTaskResumeAll(); + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + * have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + taskYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_xTaskDelayUntil( xShouldDelay ); + + return xShouldDelay; + } + +#endif /* INCLUDE_xTaskDelayUntil */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelay == 1 ) + + void vTaskDelay( const TickType_t xTicksToDelay ) + { + BaseType_t xAlreadyYielded = pdFALSE; + + traceENTER_vTaskDelay( xTicksToDelay ); + + /* A delay time of zero just forces a reschedule. */ + if( xTicksToDelay > ( TickType_t ) 0U ) + { + vTaskSuspendAll(); + { + configASSERT( uxSchedulerSuspended == 1U ); + + traceTASK_DELAY(); + + /* A task that is removed from the event list while the + * scheduler is suspended will not get placed in the ready + * list or removed from the blocked list until the scheduler + * is resumed. + * + * This task cannot be in an event list as it is the currently + * executing task. */ + prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); + } + xAlreadyYielded = xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + * have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + taskYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_vTaskDelay(); + } + +#endif /* INCLUDE_vTaskDelay */ +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_eTaskGetState == 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_xTaskAbortDelay == 1 ) ) + + eTaskState eTaskGetState( TaskHandle_t xTask ) + { + eTaskState eReturn; + List_t const * pxStateList; + List_t const * pxEventList; + List_t const * pxDelayedList; + List_t const * pxOverflowedDelayedList; + const TCB_t * const pxTCB = xTask; + + traceENTER_eTaskGetState( xTask ); + + configASSERT( pxTCB != NULL ); + + #if ( configNUMBER_OF_CORES == 1 ) + if( pxTCB == pxCurrentTCB ) + { + /* The task calling this function is querying its own state. */ + eReturn = eRunning; + } + else + #endif + { + taskENTER_CRITICAL(); + { + pxStateList = listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) ); + pxEventList = listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ); + pxDelayedList = pxDelayedTaskList; + pxOverflowedDelayedList = pxOverflowDelayedTaskList; + } + taskEXIT_CRITICAL(); + + if( pxEventList == &xPendingReadyList ) + { + /* The task has been placed on the pending ready list, so its + * state is eReady regardless of what list the task's state list + * item is currently placed on. */ + eReturn = eReady; + } + else if( ( pxStateList == pxDelayedList ) || ( pxStateList == pxOverflowedDelayedList ) ) + { + /* The task being queried is referenced from one of the Blocked + * lists. */ + eReturn = eBlocked; + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + else if( pxStateList == &xSuspendedTaskList ) + { + /* The task being queried is referenced from the suspended + * list. Is it genuinely suspended or is it blocked + * indefinitely? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ) + { + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + BaseType_t x; + + /* The task does not appear on the event list item of + * and of the RTOS objects, but could still be in the + * blocked state if it is waiting on its notification + * rather than waiting on an object. If not, is + * suspended. */ + eReturn = eSuspended; + + for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) + { + if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) + { + eReturn = eBlocked; + break; + } + } + } + #else /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + { + eReturn = eSuspended; + } + #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + } + else + { + eReturn = eBlocked; + } + } + #endif /* if ( INCLUDE_vTaskSuspend == 1 ) */ + + #if ( INCLUDE_vTaskDelete == 1 ) + else if( ( pxStateList == &xTasksWaitingTermination ) || ( pxStateList == NULL ) ) + { + /* The task being queried is referenced from the deleted + * tasks list, or it is not referenced from any lists at + * all. */ + eReturn = eDeleted; + } + #endif + + else + { + #if ( configNUMBER_OF_CORES == 1 ) + { + /* If the task is not in any other state, it must be in the + * Ready (including pending ready) state. */ + eReturn = eReady; + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) + { + /* Is it actively running on a core? */ + eReturn = eRunning; + } + else + { + /* If the task is not in any other state, it must be in the + * Ready (including pending ready) state. */ + eReturn = eReady; + } + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + } + } + + traceRETURN_eTaskGetState( eReturn ); + + return eReturn; + } + +#endif /* INCLUDE_eTaskGetState */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskPriorityGet == 1 ) + + UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) + { + TCB_t const * pxTCB; + UBaseType_t uxReturn; + + traceENTER_uxTaskPriorityGet( xTask ); + + portBASE_TYPE_ENTER_CRITICAL(); + { + /* If null is passed in here then it is the priority of the task + * that called uxTaskPriorityGet() that is being queried. */ + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + uxReturn = pxTCB->uxPriority; + } + portBASE_TYPE_EXIT_CRITICAL(); + + traceRETURN_uxTaskPriorityGet( uxReturn ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskPriorityGet */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskPriorityGet == 1 ) + + UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) + { + TCB_t const * pxTCB; + UBaseType_t uxReturn; + UBaseType_t uxSavedInterruptStatus; + + traceENTER_uxTaskPriorityGetFromISR( xTask ); + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + /* MISRA Ref 4.7.1 [Return value shall be checked] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ + /* coverity[misra_c_2012_directive_4_7_violation] */ + uxSavedInterruptStatus = ( UBaseType_t ) taskENTER_CRITICAL_FROM_ISR(); + { + /* If null is passed in here then it is the priority of the calling + * task that is being queried. */ + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + uxReturn = pxTCB->uxPriority; + } + taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_uxTaskPriorityGetFromISR( uxReturn ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskPriorityGet */ +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_uxTaskPriorityGet == 1 ) && ( configUSE_MUTEXES == 1 ) ) + + UBaseType_t uxTaskBasePriorityGet( const TaskHandle_t xTask ) + { + TCB_t const * pxTCB; + UBaseType_t uxReturn; + + traceENTER_uxTaskBasePriorityGet( xTask ); + + portBASE_TYPE_ENTER_CRITICAL(); + { + /* If null is passed in here then it is the base priority of the task + * that called uxTaskBasePriorityGet() that is being queried. */ + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + uxReturn = pxTCB->uxBasePriority; + } + portBASE_TYPE_EXIT_CRITICAL(); + + traceRETURN_uxTaskBasePriorityGet( uxReturn ); + + return uxReturn; + } + +#endif /* #if ( ( INCLUDE_uxTaskPriorityGet == 1 ) && ( configUSE_MUTEXES == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_uxTaskPriorityGet == 1 ) && ( configUSE_MUTEXES == 1 ) ) + + UBaseType_t uxTaskBasePriorityGetFromISR( const TaskHandle_t xTask ) + { + TCB_t const * pxTCB; + UBaseType_t uxReturn; + UBaseType_t uxSavedInterruptStatus; + + traceENTER_uxTaskBasePriorityGetFromISR( xTask ); + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + /* MISRA Ref 4.7.1 [Return value shall be checked] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ + /* coverity[misra_c_2012_directive_4_7_violation] */ + uxSavedInterruptStatus = ( UBaseType_t ) taskENTER_CRITICAL_FROM_ISR(); + { + /* If null is passed in here then it is the base priority of the calling + * task that is being queried. */ + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + uxReturn = pxTCB->uxBasePriority; + } + taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_uxTaskBasePriorityGetFromISR( uxReturn ); + + return uxReturn; + } + +#endif /* #if ( ( INCLUDE_uxTaskPriorityGet == 1 ) && ( configUSE_MUTEXES == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskPrioritySet == 1 ) + + void vTaskPrioritySet( TaskHandle_t xTask, + UBaseType_t uxNewPriority ) + { + TCB_t * pxTCB; + UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry; + BaseType_t xYieldRequired = pdFALSE; + + #if ( configNUMBER_OF_CORES > 1 ) + BaseType_t xYieldForTask = pdFALSE; + #endif + + traceENTER_vTaskPrioritySet( xTask, uxNewPriority ); + + configASSERT( uxNewPriority < configMAX_PRIORITIES ); + + /* Ensure the new priority is valid. */ + if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the priority of the calling + * task that is being changed. */ + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + traceTASK_PRIORITY_SET( pxTCB, uxNewPriority ); + + #if ( configUSE_MUTEXES == 1 ) + { + uxCurrentBasePriority = pxTCB->uxBasePriority; + } + #else + { + uxCurrentBasePriority = pxTCB->uxPriority; + } + #endif + + if( uxCurrentBasePriority != uxNewPriority ) + { + /* The priority change may have readied a task of higher + * priority than a running task. */ + if( uxNewPriority > uxCurrentBasePriority ) + { + #if ( configNUMBER_OF_CORES == 1 ) + { + if( pxTCB != pxCurrentTCB ) + { + /* The priority of a task other than the currently + * running task is being raised. Is the priority being + * raised above that of the running task? */ + if( uxNewPriority > pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The priority of the running task is being raised, + * but the running task must already be the highest + * priority task able to run so no yield is required. */ + } + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + /* The priority of a task is being raised so + * perform a yield for this task later. */ + xYieldForTask = pdTRUE; + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + } + else if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) + { + /* Setting the priority of a running task down means + * there may now be another task of higher priority that + * is ready to execute. */ + #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + if( pxTCB->xPreemptionDisable == pdFALSE ) + #endif + { + xYieldRequired = pdTRUE; + } + } + else + { + /* Setting the priority of any other task down does not + * require a yield as the running task must be above the + * new priority of the task being modified. */ + } + + /* Remember the ready list the task might be referenced from + * before its uxPriority member is changed so the + * taskRESET_READY_PRIORITY() macro can function correctly. */ + uxPriorityUsedOnEntry = pxTCB->uxPriority; + + #if ( configUSE_MUTEXES == 1 ) + { + /* Only change the priority being used if the task is not + * currently using an inherited priority or the new priority + * is bigger than the inherited priority. */ + if( ( pxTCB->uxBasePriority == pxTCB->uxPriority ) || ( uxNewPriority > pxTCB->uxPriority ) ) + { + pxTCB->uxPriority = uxNewPriority; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The base priority gets set whatever. */ + pxTCB->uxBasePriority = uxNewPriority; + } + #else /* if ( configUSE_MUTEXES == 1 ) */ + { + pxTCB->uxPriority = uxNewPriority; + } + #endif /* if ( configUSE_MUTEXES == 1 ) */ + + /* Only reset the event list item value if the value is not + * being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == ( ( TickType_t ) 0U ) ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the task is in the blocked or suspended list we need do + * nothing more than change its priority variable. However, if + * the task is in a ready list it needs to be removed and placed + * in the list appropriate to its new priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + /* The task is currently in its ready list - remove before + * adding it to its new ready list. As we are in a critical + * section we can do this even if the scheduler is suspended. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + * there is no need to check again and the port level + * reset macro can be called directly. */ + portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvAddTaskToReadyList( pxTCB ); + } + else + { + #if ( configNUMBER_OF_CORES == 1 ) + { + mtCOVERAGE_TEST_MARKER(); + } + #else + { + /* It's possible that xYieldForTask was already set to pdTRUE because + * its priority is being raised. However, since it is not in a ready list + * we don't actually need to yield for it. */ + xYieldForTask = pdFALSE; + } + #endif + } + + if( xYieldRequired != pdFALSE ) + { + /* The running task priority is set down. Request the task to yield. */ + taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxTCB ); + } + else + { + #if ( configNUMBER_OF_CORES > 1 ) + if( xYieldForTask != pdFALSE ) + { + /* The priority of the task is being raised. If a running + * task has priority lower than this task, it should yield + * for this task. */ + taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB ); + } + else + #endif /* if ( configNUMBER_OF_CORES > 1 ) */ + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Remove compiler warning about unused variables when the port + * optimised task selection is not being used. */ + ( void ) uxPriorityUsedOnEntry; + } + } + taskEXIT_CRITICAL(); + + traceRETURN_vTaskPrioritySet(); + } + +#endif /* INCLUDE_vTaskPrioritySet */ +/*-----------------------------------------------------------*/ + +#if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + void vTaskCoreAffinitySet( const TaskHandle_t xTask, + UBaseType_t uxCoreAffinityMask ) + { + TCB_t * pxTCB; + BaseType_t xCoreID; + + traceENTER_vTaskCoreAffinitySet( xTask, uxCoreAffinityMask ); + + taskENTER_CRITICAL(); + { + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + pxTCB->uxCoreAffinityMask = uxCoreAffinityMask; + + if( xSchedulerRunning != pdFALSE ) + { + if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) + { + xCoreID = ( BaseType_t ) pxTCB->xTaskRunState; + + /* If the task can no longer run on the core it was running, + * request the core to yield. */ + if( ( uxCoreAffinityMask & ( ( UBaseType_t ) 1U << ( UBaseType_t ) xCoreID ) ) == 0U ) + { + prvYieldCore( xCoreID ); + } + } + else + { + #if ( configUSE_PREEMPTION == 1 ) + { + /* The SMP scheduler requests a core to yield when a ready + * task is able to run. It is possible that the core affinity + * of the ready task is changed before the requested core + * can select it to run. In that case, the task may not be + * selected by the previously requested core due to core affinity + * constraint and the SMP scheduler must select a new core to + * yield for the task. */ + prvYieldForTask( xTask ); + } + #else /* #if( configUSE_PREEMPTION == 1 ) */ + { + mtCOVERAGE_TEST_MARKER(); + } + #endif /* #if( configUSE_PREEMPTION == 1 ) */ + } + } + } + taskEXIT_CRITICAL(); + + traceRETURN_vTaskCoreAffinitySet(); + } +#endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) + UBaseType_t vTaskCoreAffinityGet( ConstTaskHandle_t xTask ) + { + const TCB_t * pxTCB; + UBaseType_t uxCoreAffinityMask; + + traceENTER_vTaskCoreAffinityGet( xTask ); + + portBASE_TYPE_ENTER_CRITICAL(); + { + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + uxCoreAffinityMask = pxTCB->uxCoreAffinityMask; + } + portBASE_TYPE_EXIT_CRITICAL(); + + traceRETURN_vTaskCoreAffinityGet( uxCoreAffinityMask ); + + return uxCoreAffinityMask; + } +#endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_CORE_AFFINITY == 1 ) ) */ + +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + + void vTaskPreemptionDisable( const TaskHandle_t xTask ) + { + TCB_t * pxTCB; + + traceENTER_vTaskPreemptionDisable( xTask ); + + taskENTER_CRITICAL(); + { + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + pxTCB->xPreemptionDisable = pdTRUE; + } + taskEXIT_CRITICAL(); + + traceRETURN_vTaskPreemptionDisable(); + } + +#endif /* #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + + void vTaskPreemptionEnable( const TaskHandle_t xTask ) + { + TCB_t * pxTCB; + BaseType_t xCoreID; + + traceENTER_vTaskPreemptionEnable( xTask ); + + taskENTER_CRITICAL(); + { + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + pxTCB->xPreemptionDisable = pdFALSE; + + if( xSchedulerRunning != pdFALSE ) + { + if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) + { + xCoreID = ( BaseType_t ) pxTCB->xTaskRunState; + prvYieldCore( xCoreID ); + } + } + } + taskEXIT_CRITICAL(); + + traceRETURN_vTaskPreemptionEnable(); + } + +#endif /* #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskSuspend( TaskHandle_t xTaskToSuspend ) + { + TCB_t * pxTCB; + + traceENTER_vTaskSuspend( xTaskToSuspend ); + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the running task that is + * being suspended. */ + pxTCB = prvGetTCBFromHandle( xTaskToSuspend ); + configASSERT( pxTCB != NULL ); + + traceTASK_SUSPEND( pxTCB ); + + /* Remove task from the ready/delayed list and place in the + * suspended list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Is the task waiting on an event also? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); + + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + BaseType_t x; + + for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) + { + if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) + { + /* The task was blocked to wait for a notification, but is + * now suspended, so no notification was received. */ + pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION; + } + } + } + #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + + /* In the case of SMP, it is possible that the task being suspended + * is running on another core. We must evict the task before + * exiting the critical section to ensure that the task cannot + * take an action which puts it back on ready/state/event list, + * thereby nullifying the suspend operation. Once evicted, the + * task won't be scheduled before it is resumed as it will no longer + * be on the ready list. */ + #if ( configNUMBER_OF_CORES > 1 ) + { + if( xSchedulerRunning != pdFALSE ) + { + /* Reset the next expected unblock time in case it referred to the + * task that is now in the Suspended state. */ + prvResetNextTaskUnblockTime(); + + if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) + { + if( pxTCB->xTaskRunState == ( BaseType_t ) portGET_CORE_ID() ) + { + /* The current task has just been suspended. */ + configASSERT( uxSchedulerSuspended == 0 ); + vTaskYieldWithinAPI(); + } + else + { + prvYieldCore( pxTCB->xTaskRunState ); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + } + taskEXIT_CRITICAL(); + + #if ( configNUMBER_OF_CORES == 1 ) + { + UBaseType_t uxCurrentListLength; + + if( xSchedulerRunning != pdFALSE ) + { + /* Reset the next expected unblock time in case it referred to the + * task that is now in the Suspended state. */ + taskENTER_CRITICAL(); + { + prvResetNextTaskUnblockTime(); + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( pxTCB == pxCurrentTCB ) + { + if( xSchedulerRunning != pdFALSE ) + { + /* The current task has just been suspended. */ + configASSERT( uxSchedulerSuspended == 0 ); + portYIELD_WITHIN_API(); + } + else + { + /* The scheduler is not running, but the task that was pointed + * to by pxCurrentTCB has just been suspended and pxCurrentTCB + * must be adjusted to point to a different task. */ + + /* Use a temp variable as a distinct sequence point for reading + * volatile variables prior to a comparison to ensure compliance + * with MISRA C 2012 Rule 13.2. */ + uxCurrentListLength = listCURRENT_LIST_LENGTH( &xSuspendedTaskList ); + + if( uxCurrentListLength == uxCurrentNumberOfTasks ) + { + /* No other tasks are ready, so set pxCurrentTCB back to + * NULL so when the next task is created pxCurrentTCB will + * be set to point to it no matter what its relative priority + * is. */ + pxCurrentTCB = NULL; + } + else + { + vTaskSwitchContext(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + + traceRETURN_vTaskSuspend(); + } + +#endif /* INCLUDE_vTaskSuspend */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) + { + BaseType_t xReturn = pdFALSE; + const TCB_t * const pxTCB = xTask; + + /* Accesses xPendingReadyList so must be called from a critical + * section. */ + + /* It does not make sense to check if the calling task is suspended. */ + configASSERT( xTask ); + + /* Is the task being resumed actually in the suspended list? */ + if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + /* Has the task already been resumed from within an ISR? */ + if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE ) + { + /* Is it in the suspended list because it is in the Suspended + * state, or because it is blocked with no timeout? */ + if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) + { + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + BaseType_t x; + + /* The task does not appear on the event list item of + * and of the RTOS objects, but could still be in the + * blocked state if it is waiting on its notification + * rather than waiting on an object. If not, is + * suspended. */ + xReturn = pdTRUE; + + for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) + { + if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) + { + xReturn = pdFALSE; + break; + } + } + } + #else /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + { + xReturn = pdTRUE; + } + #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } + +#endif /* INCLUDE_vTaskSuspend */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskResume( TaskHandle_t xTaskToResume ) + { + TCB_t * const pxTCB = xTaskToResume; + + traceENTER_vTaskResume( xTaskToResume ); + + /* It does not make sense to resume the calling task. */ + configASSERT( xTaskToResume ); + + #if ( configNUMBER_OF_CORES == 1 ) + + /* The parameter cannot be NULL as it is impossible to resume the + * currently executing task. */ + if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) ) + #else + + /* The parameter cannot be NULL as it is impossible to resume the + * currently executing task. It is also impossible to resume a task + * that is actively running on another core but it is not safe + * to check their run state here. Therefore, we get into a critical + * section and check if the task is actually suspended or not. */ + if( pxTCB != NULL ) + #endif + { + taskENTER_CRITICAL(); + { + if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) + { + traceTASK_RESUME( pxTCB ); + + /* The ready list can be accessed even if the scheduler is + * suspended because this is inside a critical section. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* This yield may not cause the task just resumed to run, + * but will leave the lists in the correct state for the + * next yield. */ + taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_vTaskResume(); + } + +#endif /* INCLUDE_vTaskSuspend */ + +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) + + BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) + { + BaseType_t xYieldRequired = pdFALSE; + TCB_t * const pxTCB = xTaskToResume; + UBaseType_t uxSavedInterruptStatus; + + traceENTER_xTaskResumeFromISR( xTaskToResume ); + + configASSERT( xTaskToResume ); + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + /* MISRA Ref 4.7.1 [Return value shall be checked] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ + /* coverity[misra_c_2012_directive_4_7_violation] */ + uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); + { + if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) + { + traceTASK_RESUME_FROM_ISR( pxTCB ); + + /* Check the ready lists can be accessed. */ + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + #if ( configNUMBER_OF_CORES == 1 ) + { + /* Ready lists can be accessed so move the task from the + * suspended list to the ready list directly. */ + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + + /* Mark that a yield is pending in case the user is not + * using the return value to initiate a context switch + * from the ISR using the port specific portYIELD_FROM_ISR(). */ + xYieldPendings[ 0 ] = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* The delayed or ready lists cannot be accessed so the task + * is held in the pending ready list until the scheduler is + * unsuspended. */ + vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + + #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_PREEMPTION == 1 ) ) + { + prvYieldForTask( pxTCB ); + + if( xYieldPendings[ portGET_CORE_ID() ] != pdFALSE ) + { + xYieldRequired = pdTRUE; + } + } + #endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_PREEMPTION == 1 ) ) */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_xTaskResumeFromISR( xYieldRequired ); + + return xYieldRequired; + } + +#endif /* ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) */ +/*-----------------------------------------------------------*/ + +static BaseType_t prvCreateIdleTasks( void ) +{ + BaseType_t xReturn = pdPASS; + BaseType_t xCoreID; + char cIdleName[ configMAX_TASK_NAME_LEN ] = { 0 }; + TaskFunction_t pxIdleTaskFunction = NULL; + UBaseType_t xIdleTaskNameIndex; + + /* MISRA Ref 14.3.1 [Configuration dependent invariant] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-143. */ + /* coverity[misra_c_2012_rule_14_3_violation] */ + for( xIdleTaskNameIndex = 0U; xIdleTaskNameIndex < ( configMAX_TASK_NAME_LEN - taskRESERVED_TASK_NAME_LENGTH ); xIdleTaskNameIndex++ ) + { + /* MISRA Ref 18.1.1 [Configuration dependent bounds checking] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-181. */ + /* coverity[misra_c_2012_rule_18_1_violation] */ + cIdleName[ xIdleTaskNameIndex ] = configIDLE_TASK_NAME[ xIdleTaskNameIndex ]; + + if( cIdleName[ xIdleTaskNameIndex ] == ( char ) 0x00 ) + { + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Ensure null termination. */ + cIdleName[ xIdleTaskNameIndex ] = '\0'; + + /* Add each idle task at the lowest priority. */ + for( xCoreID = ( BaseType_t ) 0; xCoreID < ( BaseType_t ) configNUMBER_OF_CORES; xCoreID++ ) + { + #if ( configNUMBER_OF_CORES == 1 ) + { + pxIdleTaskFunction = &prvIdleTask; + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + /* In the FreeRTOS SMP, configNUMBER_OF_CORES - 1 passive idle tasks + * are also created to ensure that each core has an idle task to + * run when no other task is available to run. */ + if( xCoreID == 0 ) + { + pxIdleTaskFunction = &prvIdleTask; + } + else + { + pxIdleTaskFunction = &prvPassiveIdleTask; + } + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + + /* Update the idle task name with suffix to differentiate the idle tasks. + * This function is not required in single core FreeRTOS since there is + * only one idle task. */ + #if ( configNUMBER_OF_CORES > 1 ) + { + /* Append the idle task number to the end of the name. + * + * Note: Idle task name index only supports single-character + * core IDs (0-9). If the core ID exceeds 9, the idle task + * name will contain an incorrect ASCII character. This is + * acceptable as the task name is used mainly for debugging. */ + cIdleName[ xIdleTaskNameIndex ] = ( char ) ( xCoreID + '0' ); + cIdleName[ xIdleTaskNameIndex + 1U ] = '\0'; + } + #endif /* if ( configNUMBER_OF_CORES > 1 ) */ + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + StaticTask_t * pxIdleTaskTCBBuffer = NULL; + StackType_t * pxIdleTaskStackBuffer = NULL; + configSTACK_DEPTH_TYPE uxIdleTaskStackSize; + + /* The Idle task is created using user provided RAM - obtain the + * address of the RAM then create the idle task. */ + #if ( configNUMBER_OF_CORES == 1 ) + { + vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &uxIdleTaskStackSize ); + } + #else + { + if( xCoreID == 0 ) + { + vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &uxIdleTaskStackSize ); + } + else + { + vApplicationGetPassiveIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &uxIdleTaskStackSize, ( BaseType_t ) ( xCoreID - 1 ) ); + } + } + #endif /* if ( configNUMBER_OF_CORES == 1 ) */ + xIdleTaskHandles[ xCoreID ] = xTaskCreateStatic( pxIdleTaskFunction, + cIdleName, + uxIdleTaskStackSize, + ( void * ) NULL, + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ + pxIdleTaskStackBuffer, + pxIdleTaskTCBBuffer ); + + if( xIdleTaskHandles[ xCoreID ] != NULL ) + { + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + } + #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + { + /* The Idle task is being created using dynamically allocated RAM. */ + xReturn = xTaskCreate( pxIdleTaskFunction, + cIdleName, + configMINIMAL_STACK_SIZE, + ( void * ) NULL, + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ + &xIdleTaskHandles[ xCoreID ] ); + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + /* Break the loop if any of the idle task is failed to be created. */ + if( xReturn != pdPASS ) + { + break; + } + else + { + #if ( configNUMBER_OF_CORES == 1 ) + { + mtCOVERAGE_TEST_MARKER(); + } + #else + { + /* Assign idle task to each core before SMP scheduler is running. */ + xIdleTaskHandles[ xCoreID ]->xTaskRunState = xCoreID; + pxCurrentTCBs[ xCoreID ] = xIdleTaskHandles[ xCoreID ]; + } + #endif + } + } + + return xReturn; +} + +/*-----------------------------------------------------------*/ + +void vTaskStartScheduler( void ) +{ + BaseType_t xReturn; + + traceENTER_vTaskStartScheduler(); + + #if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) + { + /* Sanity check that the UBaseType_t must have greater than or equal to + * the number of bits as confNUMBER_OF_CORES. */ + configASSERT( ( sizeof( UBaseType_t ) * taskBITS_PER_BYTE ) >= configNUMBER_OF_CORES ); + } + #endif /* #if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) */ + + xReturn = prvCreateIdleTasks(); + + #if ( configUSE_TIMERS == 1 ) + { + if( xReturn == pdPASS ) + { + xReturn = xTimerCreateTimerTask(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TIMERS */ + + if( xReturn == pdPASS ) + { + /* freertos_tasks_c_additions_init() should only be called if the user + * definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is + * the only macro called by the function. */ + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + { + freertos_tasks_c_additions_init(); + } + #endif + + /* Interrupts are turned off here, to ensure a tick does not occur + * before or during the call to xPortStartScheduler(). The stacks of + * the created tasks contain a status word with interrupts switched on + * so interrupts will automatically get re-enabled when the first task + * starts to run. */ + portDISABLE_INTERRUPTS(); + + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + { + /* Switch C-Runtime's TLS Block to point to the TLS + * block specific to the task that will run first. */ + configSET_TLS_BLOCK( pxCurrentTCB->xTLSBlock ); + } + #endif + + xNextTaskUnblockTime = portMAX_DELAY; + xSchedulerRunning = pdTRUE; + xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; + + /* If configGENERATE_RUN_TIME_STATS is defined then the following + * macro must be defined to configure the timer/counter used to generate + * the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS + * is set to 0 and the following line fails to build then ensure you do not + * have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your + * FreeRTOSConfig.h file. */ + portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); + + traceTASK_SWITCHED_IN(); + + traceSTARTING_SCHEDULER( xIdleTaskHandles ); + + /* Setting up the timer tick is hardware specific and thus in the + * portable interface. */ + + /* The return value for xPortStartScheduler is not required + * hence using a void datatype. */ + ( void ) xPortStartScheduler(); + + /* In most cases, xPortStartScheduler() will not return. If it + * returns pdTRUE then there was not enough heap memory available + * to create either the Idle or the Timer task. If it returned + * pdFALSE, then the application called xTaskEndScheduler(). + * Most ports don't implement xTaskEndScheduler() as there is + * nothing to return to. */ + } + else + { + /* This line will only be reached if the kernel could not be started, + * because there was not enough FreeRTOS heap to create the idle task + * or the timer task. */ + configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ); + } + + /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0, + * meaning xIdleTaskHandles are not used anywhere else. */ + ( void ) xIdleTaskHandles; + + /* OpenOCD makes use of uxTopUsedPriority for thread debugging. Prevent uxTopUsedPriority + * from getting optimized out as it is no longer used by the kernel. */ + ( void ) uxTopUsedPriority; + + traceRETURN_vTaskStartScheduler(); +} +/*-----------------------------------------------------------*/ + +void vTaskEndScheduler( void ) +{ + traceENTER_vTaskEndScheduler(); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + BaseType_t xCoreID; + + #if ( configUSE_TIMERS == 1 ) + { + /* Delete the timer task created by the kernel. */ + vTaskDelete( xTimerGetTimerDaemonTaskHandle() ); + } + #endif /* #if ( configUSE_TIMERS == 1 ) */ + + /* Delete Idle tasks created by the kernel.*/ + for( xCoreID = 0; xCoreID < ( BaseType_t ) configNUMBER_OF_CORES; xCoreID++ ) + { + vTaskDelete( xIdleTaskHandles[ xCoreID ] ); + } + + /* Idle task is responsible for reclaiming the resources of the tasks in + * xTasksWaitingTermination list. Since the idle task is now deleted and + * no longer going to run, we need to reclaim resources of all the tasks + * in the xTasksWaitingTermination list. */ + prvCheckTasksWaitingTermination(); + } + #endif /* #if ( INCLUDE_vTaskDelete == 1 ) */ + + /* Stop the scheduler interrupts and call the portable scheduler end + * routine so the original ISRs can be restored if necessary. The port + * layer must ensure interrupts enable bit is left in the correct state. */ + portDISABLE_INTERRUPTS(); + xSchedulerRunning = pdFALSE; + + /* This function must be called from a task and the application is + * responsible for deleting that task after the scheduler is stopped. */ + vPortEndScheduler(); + + traceRETURN_vTaskEndScheduler(); +} +/*----------------------------------------------------------*/ + +void vTaskSuspendAll( void ) +{ + traceENTER_vTaskSuspendAll(); + + #if ( configNUMBER_OF_CORES == 1 ) + { + /* A critical section is not required as the variable is of type + * BaseType_t. Each task maintains its own context, and a context switch + * cannot occur if the variable is non zero. So, as long as the writing + * from the register back into the memory is atomic, it is not a + * problem. + * + * Consider the following scenario, which starts with + * uxSchedulerSuspended at zero. + * + * 1. load uxSchedulerSuspended into register. + * 2. Now a context switch causes another task to run, and the other + * task uses the same variable. The other task will see the variable + * as zero because the variable has not yet been updated by the + * original task. Eventually the original task runs again. **That can + * only happen when uxSchedulerSuspended is once again zero**. When + * the original task runs again, the contents of the CPU registers + * are restored to exactly how they were when it was switched out - + * therefore the value it read into the register still matches the + * value of the uxSchedulerSuspended variable. + * + * 3. increment register. + * 4. store register into uxSchedulerSuspended. The value restored to + * uxSchedulerSuspended will be the correct value of 1, even though + * the variable was used by other tasks in the mean time. + */ + + /* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that + * do not otherwise exhibit real time behaviour. */ + portSOFTWARE_BARRIER(); + + /* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment + * is used to allow calls to vTaskSuspendAll() to nest. */ + uxSchedulerSuspended = ( UBaseType_t ) ( uxSchedulerSuspended + 1U ); + + /* Enforces ordering for ports and optimised compilers that may otherwise place + * the above increment elsewhere. */ + portMEMORY_BARRIER(); + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + UBaseType_t ulState; + BaseType_t xCoreID; + + /* This must only be called from within a task. */ + portASSERT_IF_IN_ISR(); + + if( xSchedulerRunning != pdFALSE ) + { + /* Writes to uxSchedulerSuspended must be protected by both the task AND ISR locks. + * We must disable interrupts before we grab the locks in the event that this task is + * interrupted and switches context before incrementing uxSchedulerSuspended. + * It is safe to re-enable interrupts after releasing the ISR lock and incrementing + * uxSchedulerSuspended since that will prevent context switches. */ + ulState = portSET_INTERRUPT_MASK(); + + xCoreID = ( BaseType_t ) portGET_CORE_ID(); + + /* This must never be called from inside a critical section. */ + configASSERT( portGET_CRITICAL_NESTING_COUNT( xCoreID ) == 0 ); + + /* portSOFTWARE_BARRIER() is only implemented for emulated/simulated ports that + * do not otherwise exhibit real time behaviour. */ + portSOFTWARE_BARRIER(); + + portGET_TASK_LOCK( xCoreID ); + + /* uxSchedulerSuspended is increased after prvCheckForRunStateChange. The + * purpose is to prevent altering the variable when fromISR APIs are readying + * it. */ + if( uxSchedulerSuspended == 0U ) + { + prvCheckForRunStateChange(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Query the coreID again as prvCheckForRunStateChange may have + * caused the task to get scheduled on a different core. The correct + * task lock for the core is acquired in prvCheckForRunStateChange. */ + xCoreID = ( BaseType_t ) portGET_CORE_ID(); + + portGET_ISR_LOCK( xCoreID ); + + /* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment + * is used to allow calls to vTaskSuspendAll() to nest. */ + ++uxSchedulerSuspended; + portRELEASE_ISR_LOCK( xCoreID ); + + portCLEAR_INTERRUPT_MASK( ulState ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + + traceRETURN_vTaskSuspendAll(); +} + +/*----------------------------------------------------------*/ + +#if ( configUSE_TICKLESS_IDLE != 0 ) + + static TickType_t prvGetExpectedIdleTime( void ) + { + TickType_t xReturn; + BaseType_t xHigherPriorityReadyTasks = pdFALSE; + + /* xHigherPriorityReadyTasks takes care of the case where + * configUSE_PREEMPTION is 0, so there may be tasks above the idle priority + * task that are in the Ready state, even though the idle task is + * running. */ + #if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) + { + if( uxTopReadyPriority > tskIDLE_PRIORITY ) + { + xHigherPriorityReadyTasks = pdTRUE; + } + } + #else + { + const UBaseType_t uxLeastSignificantBit = ( UBaseType_t ) 0x01; + + /* When port optimised task selection is used the uxTopReadyPriority + * variable is used as a bit map. If bits other than the least + * significant bit are set then there are tasks that have a priority + * above the idle priority that are in the Ready state. This takes + * care of the case where the co-operative scheduler is in use. */ + if( uxTopReadyPriority > uxLeastSignificantBit ) + { + xHigherPriorityReadyTasks = pdTRUE; + } + } + #endif /* if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) */ + + if( pxCurrentTCB->uxPriority > tskIDLE_PRIORITY ) + { + xReturn = 0; + } + else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1U ) + { + /* There are other idle priority tasks in the ready state. If + * time slicing is used then the very next tick interrupt must be + * processed. */ + xReturn = 0; + } + else if( xHigherPriorityReadyTasks != pdFALSE ) + { + /* There are tasks in the Ready state that have a priority above the + * idle priority. This path can only be reached if + * configUSE_PREEMPTION is 0. */ + xReturn = 0; + } + else + { + xReturn = xNextTaskUnblockTime; + xReturn -= xTickCount; + } + + return xReturn; + } + +#endif /* configUSE_TICKLESS_IDLE */ +/*----------------------------------------------------------*/ + +BaseType_t xTaskResumeAll( void ) +{ + TCB_t * pxTCB = NULL; + BaseType_t xAlreadyYielded = pdFALSE; + + traceENTER_xTaskResumeAll(); + + #if ( configNUMBER_OF_CORES > 1 ) + if( xSchedulerRunning != pdFALSE ) + #endif + { + /* It is possible that an ISR caused a task to be removed from an event + * list while the scheduler was suspended. If this was the case then the + * removed task will have been added to the xPendingReadyList. Once the + * scheduler has been resumed it is safe to move all the pending ready + * tasks from this list into their appropriate ready list. */ + taskENTER_CRITICAL(); + { + const BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID(); + + /* If uxSchedulerSuspended is zero then this function does not match a + * previous call to vTaskSuspendAll(). */ + configASSERT( uxSchedulerSuspended != 0U ); + + uxSchedulerSuspended = ( UBaseType_t ) ( uxSchedulerSuspended - 1U ); + portRELEASE_TASK_LOCK( xCoreID ); + + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U ) + { + /* Move any readied tasks from the pending list into the + * appropriate ready list. */ + while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) + { + /* MISRA Ref 11.5.3 [Void pointer assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); + listREMOVE_ITEM( &( pxTCB->xEventListItem ) ); + portMEMORY_BARRIER(); + listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + #if ( configNUMBER_OF_CORES == 1 ) + { + /* If the moved task has a priority higher than the current + * task then a yield must be performed. */ + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + xYieldPendings[ xCoreID ] = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + /* All appropriate tasks yield at the moment a task is added to xPendingReadyList. + * If the current core yielded then vTaskSwitchContext() has already been called + * which sets xYieldPendings for the current core to pdTRUE. */ + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + } + + if( pxTCB != NULL ) + { + /* A task was unblocked while the scheduler was suspended, + * which may have prevented the next unblock time from being + * re-calculated, in which case re-calculate it now. Mainly + * important for low power tickless implementations, where + * this can prevent an unnecessary exit from low power + * state. */ + prvResetNextTaskUnblockTime(); + } + + /* If any ticks occurred while the scheduler was suspended then + * they should be processed now. This ensures the tick count does + * not slip, and that any delayed tasks are resumed at the correct + * time. + * + * It should be safe to call xTaskIncrementTick here from any core + * since we are in a critical section and xTaskIncrementTick itself + * protects itself within a critical section. Suspending the scheduler + * from any core causes xTaskIncrementTick to increment uxPendedCounts. */ + { + TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */ + + if( xPendedCounts > ( TickType_t ) 0U ) + { + do + { + if( xTaskIncrementTick() != pdFALSE ) + { + /* Other cores are interrupted from + * within xTaskIncrementTick(). */ + xYieldPendings[ xCoreID ] = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + --xPendedCounts; + } while( xPendedCounts > ( TickType_t ) 0U ); + + xPendedTicks = 0; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + if( xYieldPendings[ xCoreID ] != pdFALSE ) + { + #if ( configUSE_PREEMPTION != 0 ) + { + xAlreadyYielded = pdTRUE; + } + #endif /* #if ( configUSE_PREEMPTION != 0 ) */ + + #if ( configNUMBER_OF_CORES == 1 ) + { + taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxCurrentTCB ); + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + } + + traceRETURN_xTaskResumeAll( xAlreadyYielded ); + + return xAlreadyYielded; +} +/*-----------------------------------------------------------*/ + +TickType_t xTaskGetTickCount( void ) +{ + TickType_t xTicks; + + traceENTER_xTaskGetTickCount(); + + /* Critical section required if running on a 16 bit processor. */ + portTICK_TYPE_ENTER_CRITICAL(); + { + xTicks = xTickCount; + } + portTICK_TYPE_EXIT_CRITICAL(); + + traceRETURN_xTaskGetTickCount( xTicks ); + + return xTicks; +} +/*-----------------------------------------------------------*/ + +TickType_t xTaskGetTickCountFromISR( void ) +{ + TickType_t xReturn; + UBaseType_t uxSavedInterruptStatus; + + traceENTER_xTaskGetTickCountFromISR(); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + * system call (or maximum API call) interrupt priority. Interrupts that are + * above the maximum system call priority are kept permanently enabled, even + * when the RTOS kernel is in a critical section, but cannot make any calls to + * FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + * then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has been + * assigned a priority above the configured maximum system call priority. + * Only FreeRTOS functions that end in FromISR can be called from interrupts + * that have been assigned a priority at or (logically) below the maximum + * system call interrupt priority. FreeRTOS maintains a separate interrupt + * safe API to ensure interrupt entry is as fast and as simple as possible. + * More information (albeit Cortex-M specific) is provided on the following + * link: https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR(); + { + xReturn = xTickCount; + } + portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_xTaskGetTickCountFromISR( xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxTaskGetNumberOfTasks( void ) +{ + traceENTER_uxTaskGetNumberOfTasks(); + + /* A critical section is not required because the variables are of type + * BaseType_t. */ + traceRETURN_uxTaskGetNumberOfTasks( uxCurrentNumberOfTasks ); + + return uxCurrentNumberOfTasks; +} +/*-----------------------------------------------------------*/ + +char * pcTaskGetName( TaskHandle_t xTaskToQuery ) +{ + TCB_t * pxTCB; + + traceENTER_pcTaskGetName( xTaskToQuery ); + + /* If null is passed in here then the name of the calling task is being + * queried. */ + pxTCB = prvGetTCBFromHandle( xTaskToQuery ); + configASSERT( pxTCB != NULL ); + + traceRETURN_pcTaskGetName( &( pxTCB->pcTaskName[ 0 ] ) ); + + return &( pxTCB->pcTaskName[ 0 ] ); +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetHandle == 1 ) + static TCB_t * prvSearchForNameWithinSingleList( List_t * pxList, + const char pcNameToQuery[] ) + { + TCB_t * pxReturn = NULL; + TCB_t * pxTCB = NULL; + UBaseType_t x; + char cNextChar; + BaseType_t xBreakLoop; + const ListItem_t * pxEndMarker = listGET_END_MARKER( pxList ); + ListItem_t * pxIterator; + + /* This function is called with the scheduler suspended. */ + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + for( pxIterator = listGET_HEAD_ENTRY( pxList ); pxIterator != pxEndMarker; pxIterator = listGET_NEXT( pxIterator ) ) + { + /* MISRA Ref 11.5.3 [Void pointer assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxTCB = listGET_LIST_ITEM_OWNER( pxIterator ); + + /* Check each character in the name looking for a match or + * mismatch. */ + xBreakLoop = pdFALSE; + + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + cNextChar = pxTCB->pcTaskName[ x ]; + + if( cNextChar != pcNameToQuery[ x ] ) + { + /* Characters didn't match. */ + xBreakLoop = pdTRUE; + } + else if( cNextChar == ( char ) 0x00 ) + { + /* Both strings terminated, a match must have been + * found. */ + pxReturn = pxTCB; + xBreakLoop = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xBreakLoop != pdFALSE ) + { + break; + } + } + + if( pxReturn != NULL ) + { + /* The handle has been found. */ + break; + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return pxReturn; + } + +#endif /* INCLUDE_xTaskGetHandle */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetHandle == 1 ) + + TaskHandle_t xTaskGetHandle( const char * pcNameToQuery ) + { + UBaseType_t uxQueue = configMAX_PRIORITIES; + TCB_t * pxTCB; + + traceENTER_xTaskGetHandle( pcNameToQuery ); + + /* Task names will be truncated to configMAX_TASK_NAME_LEN - 1 bytes. */ + configASSERT( strlen( pcNameToQuery ) < configMAX_TASK_NAME_LEN ); + + vTaskSuspendAll(); + { + /* Search the ready lists. */ + do + { + uxQueue--; + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) &( pxReadyTasksLists[ uxQueue ] ), pcNameToQuery ); + + if( pxTCB != NULL ) + { + /* Found the handle. */ + break; + } + } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); + + /* Search the delayed lists. */ + if( pxTCB == NULL ) + { + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) pxDelayedTaskList, pcNameToQuery ); + } + + if( pxTCB == NULL ) + { + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) pxOverflowDelayedTaskList, pcNameToQuery ); + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( pxTCB == NULL ) + { + /* Search the suspended list. */ + pxTCB = prvSearchForNameWithinSingleList( &xSuspendedTaskList, pcNameToQuery ); + } + } + #endif + + #if ( INCLUDE_vTaskDelete == 1 ) + { + if( pxTCB == NULL ) + { + /* Search the deleted list. */ + pxTCB = prvSearchForNameWithinSingleList( &xTasksWaitingTermination, pcNameToQuery ); + } + } + #endif + } + ( void ) xTaskResumeAll(); + + traceRETURN_xTaskGetHandle( pxTCB ); + + return pxTCB; + } + +#endif /* INCLUDE_xTaskGetHandle */ +/*-----------------------------------------------------------*/ + +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + + BaseType_t xTaskGetStaticBuffers( TaskHandle_t xTask, + StackType_t ** ppuxStackBuffer, + StaticTask_t ** ppxTaskBuffer ) + { + BaseType_t xReturn; + TCB_t * pxTCB; + + traceENTER_xTaskGetStaticBuffers( xTask, ppuxStackBuffer, ppxTaskBuffer ); + + configASSERT( ppuxStackBuffer != NULL ); + configASSERT( ppxTaskBuffer != NULL ); + + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 ) + { + if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ) + { + *ppuxStackBuffer = pxTCB->pxStack; + /* MISRA Ref 11.3.1 [Misaligned access] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-113 */ + /* coverity[misra_c_2012_rule_11_3_violation] */ + *ppxTaskBuffer = ( StaticTask_t * ) pxTCB; + xReturn = pdTRUE; + } + else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ) + { + *ppuxStackBuffer = pxTCB->pxStack; + *ppxTaskBuffer = NULL; + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + #else /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 */ + { + *ppuxStackBuffer = pxTCB->pxStack; + *ppxTaskBuffer = ( StaticTask_t * ) pxTCB; + xReturn = pdTRUE; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE == 1 */ + + traceRETURN_xTaskGetStaticBuffers( xReturn ); + + return xReturn; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, + const UBaseType_t uxArraySize, + configRUN_TIME_COUNTER_TYPE * const pulTotalRunTime ) + { + UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES; + + traceENTER_uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, pulTotalRunTime ); + + vTaskSuspendAll(); + { + /* Is there a space in the array for each task in the system? */ + if( uxArraySize >= uxCurrentNumberOfTasks ) + { + /* Fill in an TaskStatus_t structure with information on each + * task in the Ready state. */ + do + { + uxQueue--; + uxTask = ( UBaseType_t ) ( uxTask + prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &( pxReadyTasksLists[ uxQueue ] ), eReady ) ); + } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); + + /* Fill in an TaskStatus_t structure with information on each + * task in the Blocked state. */ + uxTask = ( UBaseType_t ) ( uxTask + prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxDelayedTaskList, eBlocked ) ); + uxTask = ( UBaseType_t ) ( uxTask + prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxOverflowDelayedTaskList, eBlocked ) ); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + /* Fill in an TaskStatus_t structure with information on + * each task that has been deleted but not yet cleaned up. */ + uxTask = ( UBaseType_t ) ( uxTask + prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xTasksWaitingTermination, eDeleted ) ); + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* Fill in an TaskStatus_t structure with information on + * each task in the Suspended state. */ + uxTask = ( UBaseType_t ) ( uxTask + prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xSuspendedTaskList, eSuspended ) ); + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + if( pulTotalRunTime != NULL ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ( *pulTotalRunTime ) ); + #else + *pulTotalRunTime = ( configRUN_TIME_COUNTER_TYPE ) portGET_RUN_TIME_COUNTER_VALUE(); + #endif + } + } + #else /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */ + { + if( pulTotalRunTime != NULL ) + { + *pulTotalRunTime = 0; + } + } + #endif /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + ( void ) xTaskResumeAll(); + + traceRETURN_uxTaskGetSystemState( uxTask ); + + return uxTask; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) + + #if ( configNUMBER_OF_CORES == 1 ) + TaskHandle_t xTaskGetIdleTaskHandle( void ) + { + traceENTER_xTaskGetIdleTaskHandle(); + + /* If xTaskGetIdleTaskHandle() is called before the scheduler has been + * started, then xIdleTaskHandles will be NULL. */ + configASSERT( ( xIdleTaskHandles[ 0 ] != NULL ) ); + + traceRETURN_xTaskGetIdleTaskHandle( xIdleTaskHandles[ 0 ] ); + + return xIdleTaskHandles[ 0 ]; + } + #endif /* if ( configNUMBER_OF_CORES == 1 ) */ + + TaskHandle_t xTaskGetIdleTaskHandleForCore( BaseType_t xCoreID ) + { + traceENTER_xTaskGetIdleTaskHandleForCore( xCoreID ); + + /* Ensure the core ID is valid. */ + configASSERT( taskVALID_CORE_ID( xCoreID ) == pdTRUE ); + + /* If xTaskGetIdleTaskHandle() is called before the scheduler has been + * started, then xIdleTaskHandles will be NULL. */ + configASSERT( ( xIdleTaskHandles[ xCoreID ] != NULL ) ); + + traceRETURN_xTaskGetIdleTaskHandleForCore( xIdleTaskHandles[ xCoreID ] ); + + return xIdleTaskHandles[ xCoreID ]; + } + +#endif /* INCLUDE_xTaskGetIdleTaskHandle */ +/*----------------------------------------------------------*/ + +/* This conditional compilation should use inequality to 0, not equality to 1. + * This is to ensure vTaskStepTick() is available when user defined low power mode + * implementations require configUSE_TICKLESS_IDLE to be set to a value other than + * 1. */ +#if ( configUSE_TICKLESS_IDLE != 0 ) + + void vTaskStepTick( TickType_t xTicksToJump ) + { + TickType_t xUpdatedTickCount; + + traceENTER_vTaskStepTick( xTicksToJump ); + + /* Correct the tick count value after a period during which the tick + * was suppressed. Note this does *not* call the tick hook function for + * each stepped tick. */ + xUpdatedTickCount = xTickCount + xTicksToJump; + configASSERT( xUpdatedTickCount <= xNextTaskUnblockTime ); + + if( xUpdatedTickCount == xNextTaskUnblockTime ) + { + /* Arrange for xTickCount to reach xNextTaskUnblockTime in + * xTaskIncrementTick() when the scheduler resumes. This ensures + * that any delayed tasks are resumed at the correct time. */ + configASSERT( uxSchedulerSuspended != ( UBaseType_t ) 0U ); + configASSERT( xTicksToJump != ( TickType_t ) 0 ); + + /* Prevent the tick interrupt modifying xPendedTicks simultaneously. */ + taskENTER_CRITICAL(); + { + xPendedTicks++; + } + taskEXIT_CRITICAL(); + xTicksToJump--; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xTickCount += xTicksToJump; + + traceINCREASE_TICK_COUNT( xTicksToJump ); + traceRETURN_vTaskStepTick(); + } + +#endif /* configUSE_TICKLESS_IDLE */ +/*----------------------------------------------------------*/ + +BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) +{ + BaseType_t xYieldOccurred; + + traceENTER_xTaskCatchUpTicks( xTicksToCatchUp ); + + /* Must not be called with the scheduler suspended as the implementation + * relies on xPendedTicks being wound down to 0 in xTaskResumeAll(). */ + configASSERT( uxSchedulerSuspended == ( UBaseType_t ) 0U ); + + /* Use xPendedTicks to mimic xTicksToCatchUp number of ticks occurring when + * the scheduler is suspended so the ticks are executed in xTaskResumeAll(). */ + vTaskSuspendAll(); + + /* Prevent the tick interrupt modifying xPendedTicks simultaneously. */ + taskENTER_CRITICAL(); + { + xPendedTicks += xTicksToCatchUp; + } + taskEXIT_CRITICAL(); + xYieldOccurred = xTaskResumeAll(); + + traceRETURN_xTaskCatchUpTicks( xYieldOccurred ); + + return xYieldOccurred; +} +/*----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskAbortDelay == 1 ) + + BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) + { + TCB_t * pxTCB = xTask; + BaseType_t xReturn; + + traceENTER_xTaskAbortDelay( xTask ); + + configASSERT( pxTCB != NULL ); + + vTaskSuspendAll(); + { + /* A task can only be prematurely removed from the Blocked state if + * it is actually in the Blocked state. */ + if( eTaskGetState( xTask ) == eBlocked ) + { + xReturn = pdPASS; + + /* Remove the reference to the task from the blocked list. An + * interrupt won't touch the xStateListItem because the + * scheduler is suspended. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + + /* Is the task waiting on an event also? If so remove it from + * the event list too. Interrupts can touch the event list item, + * even though the scheduler is suspended, so a critical section + * is used. */ + taskENTER_CRITICAL(); + { + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + + /* This lets the task know it was forcibly removed from the + * blocked state so it should not re-evaluate its block time and + * then block again. */ + pxTCB->ucDelayAborted = ( uint8_t ) pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + /* Place the unblocked task into the appropriate ready list. */ + prvAddTaskToReadyList( pxTCB ); + + /* A task being unblocked cannot cause an immediate context + * switch if preemption is turned off. */ + #if ( configUSE_PREEMPTION == 1 ) + { + #if ( configNUMBER_OF_CORES == 1 ) + { + /* Preemption is on, but a context switch should only be + * performed if the unblocked task has a priority that is + * higher than the currently executing task. */ + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* Pend the yield to be performed when the scheduler + * is unsuspended. */ + xYieldPendings[ 0 ] = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + taskENTER_CRITICAL(); + { + prvYieldForTask( pxTCB ); + } + taskEXIT_CRITICAL(); + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + } + #endif /* #if ( configUSE_PREEMPTION == 1 ) */ + } + else + { + xReturn = pdFAIL; + } + } + ( void ) xTaskResumeAll(); + + traceRETURN_xTaskAbortDelay( xReturn ); + + return xReturn; + } + +#endif /* INCLUDE_xTaskAbortDelay */ +/*----------------------------------------------------------*/ + +BaseType_t xTaskIncrementTick( void ) +{ + TCB_t * pxTCB; + TickType_t xItemValue; + BaseType_t xSwitchRequired = pdFALSE; + + traceENTER_xTaskIncrementTick(); + + /* Called by the portable layer each time a tick interrupt occurs. + * Increments the tick then checks to see if the new tick value will cause any + * tasks to be unblocked. */ + traceTASK_INCREMENT_TICK( xTickCount ); + + /* Tick increment should occur on every kernel timer event. Core 0 has the + * responsibility to increment the tick, or increment the pended ticks if the + * scheduler is suspended. If pended ticks is greater than zero, the core that + * calls xTaskResumeAll has the responsibility to increment the tick. */ + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + /* Minor optimisation. The tick count cannot change in this + * block. */ + const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1; + + /* Increment the RTOS tick, switching the delayed and overflowed + * delayed lists if it wraps to 0. */ + xTickCount = xConstTickCount; + + if( xConstTickCount == ( TickType_t ) 0U ) + { + taskSWITCH_DELAYED_LISTS(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* See if this tick has made a timeout expire. Tasks are stored in + * the queue in the order of their wake time - meaning once one task + * has been found whose block time has not expired there is no need to + * look any further down the list. */ + if( xConstTickCount >= xNextTaskUnblockTime ) + { + for( ; ; ) + { + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) + { + /* The delayed list is empty. Set xNextTaskUnblockTime + * to the maximum possible value so it is extremely + * unlikely that the + * if( xTickCount >= xNextTaskUnblockTime ) test will pass + * next time through. */ + xNextTaskUnblockTime = portMAX_DELAY; + break; + } + else + { + /* The delayed list is not empty, get the value of the + * item at the head of the delayed list. This is the time + * at which the task at the head of the delayed list must + * be removed from the Blocked state. */ + /* MISRA Ref 11.5.3 [Void pointer assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); + xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) ); + + if( xConstTickCount < xItemValue ) + { + /* It is not time to unblock this item yet, but the + * item value is the time at which the task at the head + * of the blocked list must be removed from the Blocked + * state - so record the item value in + * xNextTaskUnblockTime. */ + xNextTaskUnblockTime = xItemValue; + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* It is time to remove the item from the Blocked state. */ + listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); + + /* Is the task waiting on an event also? If so remove + * it from the event list. */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + listREMOVE_ITEM( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Place the unblocked task into the appropriate ready + * list. */ + prvAddTaskToReadyList( pxTCB ); + + /* A task being unblocked cannot cause an immediate + * context switch if preemption is turned off. */ + #if ( configUSE_PREEMPTION == 1 ) + { + #if ( configNUMBER_OF_CORES == 1 ) + { + /* Preemption is on, but a context switch should + * only be performed if the unblocked task's + * priority is higher than the currently executing + * task. + * The case of equal priority tasks sharing + * processing time (which happens when both + * preemption and time slicing are on) is + * handled below.*/ + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else /* #if( configNUMBER_OF_CORES == 1 ) */ + { + prvYieldForTask( pxTCB ); + } + #endif /* #if( configNUMBER_OF_CORES == 1 ) */ + } + #endif /* #if ( configUSE_PREEMPTION == 1 ) */ + } + } + } + + /* Tasks of equal priority to the currently running task will share + * processing time (time slice) if preemption is on, and the application + * writer has not explicitly turned time slicing off. */ + #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) + { + #if ( configNUMBER_OF_CORES == 1 ) + { + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > 1U ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + BaseType_t xCoreID; + + for( xCoreID = 0; xCoreID < ( ( BaseType_t ) configNUMBER_OF_CORES ); xCoreID++ ) + { + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCBs[ xCoreID ]->uxPriority ] ) ) > 1U ) + { + xYieldPendings[ xCoreID ] = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + } + #endif /* #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */ + + #if ( configUSE_TICK_HOOK == 1 ) + { + /* Guard against the tick hook being called when the pended tick + * count is being unwound (when the scheduler is being unlocked). */ + if( xPendedTicks == ( TickType_t ) 0 ) + { + vApplicationTickHook(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TICK_HOOK */ + + #if ( configUSE_PREEMPTION == 1 ) + { + #if ( configNUMBER_OF_CORES == 1 ) + { + /* For single core the core ID is always 0. */ + if( xYieldPendings[ 0 ] != pdFALSE ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + BaseType_t xCoreID, xCurrentCoreID; + xCurrentCoreID = ( BaseType_t ) portGET_CORE_ID(); + + for( xCoreID = 0; xCoreID < ( BaseType_t ) configNUMBER_OF_CORES; xCoreID++ ) + { + #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) + if( pxCurrentTCBs[ xCoreID ]->xPreemptionDisable == pdFALSE ) + #endif + { + if( xYieldPendings[ xCoreID ] != pdFALSE ) + { + if( xCoreID == xCurrentCoreID ) + { + xSwitchRequired = pdTRUE; + } + else + { + prvYieldCore( xCoreID ); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + } + #endif /* #if ( configUSE_PREEMPTION == 1 ) */ + } + else + { + xPendedTicks += 1U; + + /* The tick hook gets called at regular intervals, even if the + * scheduler is locked. */ + #if ( configUSE_TICK_HOOK == 1 ) + { + vApplicationTickHook(); + } + #endif + } + + traceRETURN_xTaskIncrementTick( xSwitchRequired ); + + return xSwitchRequired; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + void vTaskSetApplicationTaskTag( TaskHandle_t xTask, + TaskHookFunction_t pxHookFunction ) + { + TCB_t * xTCB; + + traceENTER_vTaskSetApplicationTaskTag( xTask, pxHookFunction ); + + /* If xTask is NULL then it is the task hook of the calling task that is + * getting set. */ + if( xTask == NULL ) + { + xTCB = ( TCB_t * ) pxCurrentTCB; + } + else + { + xTCB = xTask; + } + + /* Save the hook function in the TCB. A critical section is required as + * the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + { + xTCB->pxTaskTag = pxHookFunction; + } + taskEXIT_CRITICAL(); + + traceRETURN_vTaskSetApplicationTaskTag(); + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + TaskHookFunction_t xReturn; + + traceENTER_xTaskGetApplicationTaskTag( xTask ); + + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + /* Save the hook function in the TCB. A critical section is required as + * the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + { + xReturn = pxTCB->pxTaskTag; + } + taskEXIT_CRITICAL(); + + traceRETURN_xTaskGetApplicationTaskTag( xReturn ); + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + TaskHookFunction_t xReturn; + UBaseType_t uxSavedInterruptStatus; + + traceENTER_xTaskGetApplicationTaskTagFromISR( xTask ); + + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + /* Save the hook function in the TCB. A critical section is required as + * the value can be accessed from an interrupt. */ + /* MISRA Ref 4.7.1 [Return value shall be checked] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ + /* coverity[misra_c_2012_directive_4_7_violation] */ + uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); + { + xReturn = pxTCB->pxTaskTag; + } + taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_xTaskGetApplicationTaskTagFromISR( xReturn ); + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, + void * pvParameter ) + { + TCB_t * xTCB; + BaseType_t xReturn; + + traceENTER_xTaskCallApplicationTaskHook( xTask, pvParameter ); + + /* If xTask is NULL then we are calling our own task hook. */ + if( xTask == NULL ) + { + xTCB = pxCurrentTCB; + } + else + { + xTCB = xTask; + } + + if( xTCB->pxTaskTag != NULL ) + { + xReturn = xTCB->pxTaskTag( pvParameter ); + } + else + { + xReturn = pdFAIL; + } + + traceRETURN_xTaskCallApplicationTaskHook( xReturn ); + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configNUMBER_OF_CORES == 1 ) + void vTaskSwitchContext( void ) + { + traceENTER_vTaskSwitchContext(); + + if( uxSchedulerSuspended != ( UBaseType_t ) 0U ) + { + /* The scheduler is currently suspended - do not allow a context + * switch. */ + xYieldPendings[ 0 ] = pdTRUE; + } + else + { + xYieldPendings[ 0 ] = pdFALSE; + traceTASK_SWITCHED_OUT(); + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime[ 0 ] ); + #else + ulTotalRunTime[ 0 ] = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + /* Add the amount of time the task has been running to the + * accumulated time so far. The time the task started running was + * stored in ulTaskSwitchedInTime. Note that there is no overflow + * protection here so count values are only valid until the timer + * overflows. The guard against negative values is to protect + * against suspect run time stat counter implementations - which + * are provided by the application, not the kernel. */ + if( ulTotalRunTime[ 0 ] > ulTaskSwitchedInTime[ 0 ] ) + { + pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime[ 0 ] - ulTaskSwitchedInTime[ 0 ] ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + ulTaskSwitchedInTime[ 0 ] = ulTotalRunTime[ 0 ]; + } + #endif /* configGENERATE_RUN_TIME_STATS */ + + /* Check for stack overflow, if configured. */ + taskCHECK_FOR_STACK_OVERFLOW(); + + /* Before the currently running task is switched out, save its errno. */ + #if ( configUSE_POSIX_ERRNO == 1 ) + { + pxCurrentTCB->iTaskErrno = FreeRTOS_errno; + } + #endif + + /* Select a new task to run using either the generic C or port + * optimised asm code. */ + /* MISRA Ref 11.5.3 [Void pointer assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + taskSELECT_HIGHEST_PRIORITY_TASK(); + traceTASK_SWITCHED_IN(); + + /* Macro to inject port specific behaviour immediately after + * switching tasks, such as setting an end of stack watchpoint + * or reconfiguring the MPU. */ + portTASK_SWITCH_HOOK( pxCurrentTCB ); + + /* After the new task is switched in, update the global errno. */ + #if ( configUSE_POSIX_ERRNO == 1 ) + { + FreeRTOS_errno = pxCurrentTCB->iTaskErrno; + } + #endif + + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + { + /* Switch C-Runtime's TLS Block to point to the TLS + * Block specific to this task. */ + configSET_TLS_BLOCK( pxCurrentTCB->xTLSBlock ); + } + #endif + } + + traceRETURN_vTaskSwitchContext(); + } +#else /* if ( configNUMBER_OF_CORES == 1 ) */ + void vTaskSwitchContext( BaseType_t xCoreID ) + { + traceENTER_vTaskSwitchContext(); + + /* Acquire both locks: + * - The ISR lock protects the ready list from simultaneous access by + * both other ISRs and tasks. + * - We also take the task lock to pause here in case another core has + * suspended the scheduler. We don't want to simply set xYieldPending + * and move on if another core suspended the scheduler. We should only + * do that if the current core has suspended the scheduler. */ + + portGET_TASK_LOCK( xCoreID ); /* Must always acquire the task lock first. */ + portGET_ISR_LOCK( xCoreID ); + { + /* vTaskSwitchContext() must never be called from within a critical section. + * This is not necessarily true for single core FreeRTOS, but it is for this + * SMP port. */ + configASSERT( portGET_CRITICAL_NESTING_COUNT( xCoreID ) == 0 ); + + if( uxSchedulerSuspended != ( UBaseType_t ) 0U ) + { + /* The scheduler is currently suspended - do not allow a context + * switch. */ + xYieldPendings[ xCoreID ] = pdTRUE; + } + else + { + xYieldPendings[ xCoreID ] = pdFALSE; + traceTASK_SWITCHED_OUT(); + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime[ xCoreID ] ); + #else + ulTotalRunTime[ xCoreID ] = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + /* Add the amount of time the task has been running to the + * accumulated time so far. The time the task started running was + * stored in ulTaskSwitchedInTime. Note that there is no overflow + * protection here so count values are only valid until the timer + * overflows. The guard against negative values is to protect + * against suspect run time stat counter implementations - which + * are provided by the application, not the kernel. */ + if( ulTotalRunTime[ xCoreID ] > ulTaskSwitchedInTime[ xCoreID ] ) + { + pxCurrentTCBs[ xCoreID ]->ulRunTimeCounter += ( ulTotalRunTime[ xCoreID ] - ulTaskSwitchedInTime[ xCoreID ] ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + ulTaskSwitchedInTime[ xCoreID ] = ulTotalRunTime[ xCoreID ]; + } + #endif /* configGENERATE_RUN_TIME_STATS */ + + /* Check for stack overflow, if configured. */ + taskCHECK_FOR_STACK_OVERFLOW(); + + /* Before the currently running task is switched out, save its errno. */ + #if ( configUSE_POSIX_ERRNO == 1 ) + { + pxCurrentTCBs[ xCoreID ]->iTaskErrno = FreeRTOS_errno; + } + #endif + + /* Select a new task to run. */ + taskSELECT_HIGHEST_PRIORITY_TASK( xCoreID ); + traceTASK_SWITCHED_IN(); + + /* Macro to inject port specific behaviour immediately after + * switching tasks, such as setting an end of stack watchpoint + * or reconfiguring the MPU. */ + portTASK_SWITCH_HOOK( pxCurrentTCBs[ portGET_CORE_ID() ] ); + + /* After the new task is switched in, update the global errno. */ + #if ( configUSE_POSIX_ERRNO == 1 ) + { + FreeRTOS_errno = pxCurrentTCBs[ xCoreID ]->iTaskErrno; + } + #endif + + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + { + /* Switch C-Runtime's TLS Block to point to the TLS + * Block specific to this task. */ + configSET_TLS_BLOCK( pxCurrentTCBs[ xCoreID ]->xTLSBlock ); + } + #endif + } + } + portRELEASE_ISR_LOCK( xCoreID ); + portRELEASE_TASK_LOCK( xCoreID ); + + traceRETURN_vTaskSwitchContext(); + } +#endif /* if ( configNUMBER_OF_CORES > 1 ) */ +/*-----------------------------------------------------------*/ + +void vTaskPlaceOnEventList( List_t * const pxEventList, + const TickType_t xTicksToWait ) +{ + traceENTER_vTaskPlaceOnEventList( pxEventList, xTicksToWait ); + + configASSERT( pxEventList ); + + /* THIS FUNCTION MUST BE CALLED WITH THE + * SCHEDULER SUSPENDED AND THE QUEUE BEING ACCESSED LOCKED. */ + + /* Place the event list item of the TCB in the appropriate event list. + * This is placed in the list in priority order so the highest priority task + * is the first to be woken by the event. + * + * Note: Lists are sorted in ascending order by ListItem_t.xItemValue. + * Normally, the xItemValue of a TCB's ListItem_t members is: + * xItemValue = ( configMAX_PRIORITIES - uxPriority ) + * Therefore, the event list is sorted in descending priority order. + * + * The queue that contains the event list is locked, preventing + * simultaneous access from interrupts. */ + vListInsert( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + + traceRETURN_vTaskPlaceOnEventList(); +} +/*-----------------------------------------------------------*/ + +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, + const TickType_t xItemValue, + const TickType_t xTicksToWait ) +{ + traceENTER_vTaskPlaceOnUnorderedEventList( pxEventList, xItemValue, xTicksToWait ); + + configASSERT( pxEventList ); + + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by + * the event groups implementation. */ + configASSERT( uxSchedulerSuspended != ( UBaseType_t ) 0U ); + + /* Store the item value in the event list item. It is safe to access the + * event list item here as interrupts won't access the event list item of a + * task that is not in the Blocked state. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); + + /* Place the event list item of the TCB at the end of the appropriate event + * list. It is safe to access the event list here because it is part of an + * event group implementation - and interrupts don't access event groups + * directly (instead they access them indirectly by pending function calls to + * the task level). */ + listINSERT_END( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + + traceRETURN_vTaskPlaceOnUnorderedEventList(); +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TIMERS == 1 ) + + void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, + TickType_t xTicksToWait, + const BaseType_t xWaitIndefinitely ) + { + traceENTER_vTaskPlaceOnEventListRestricted( pxEventList, xTicksToWait, xWaitIndefinitely ); + + configASSERT( pxEventList ); + + /* This function should not be called by application code hence the + * 'Restricted' in its name. It is not part of the public API. It is + * designed for use by kernel code, and has special calling requirements - + * it should be called with the scheduler suspended. */ + + + /* Place the event list item of the TCB in the appropriate event list. + * In this case it is assume that this is the only task that is going to + * be waiting on this event list, so the faster vListInsertEnd() function + * can be used in place of vListInsert. */ + listINSERT_END( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + /* If the task should block indefinitely then set the block time to a + * value that will be recognised as an indefinite delay inside the + * prvAddCurrentTaskToDelayedList() function. */ + if( xWaitIndefinitely != pdFALSE ) + { + xTicksToWait = portMAX_DELAY; + } + + traceTASK_DELAY_UNTIL( ( xTickCount + xTicksToWait ) ); + prvAddCurrentTaskToDelayedList( xTicksToWait, xWaitIndefinitely ); + + traceRETURN_vTaskPlaceOnEventListRestricted(); + } + +#endif /* configUSE_TIMERS */ +/*-----------------------------------------------------------*/ + +BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) +{ + TCB_t * pxUnblockedTCB; + BaseType_t xReturn; + + traceENTER_xTaskRemoveFromEventList( pxEventList ); + + /* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION. It can also be + * called from a critical section within an ISR. */ + + /* The event list is sorted in priority order, so the first in the list can + * be removed as it is known to be the highest priority. Remove the TCB from + * the delayed list, and add it to the ready list. + * + * If an event is for a queue that is locked then this function will never + * get called - the lock count on the queue will get modified instead. This + * means exclusive access to the event list is guaranteed here. + * + * This function assumes that a check has already been made to ensure that + * pxEventList is not empty. */ + /* MISRA Ref 11.5.3 [Void pointer assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxUnblockedTCB = listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + configASSERT( pxUnblockedTCB ); + listREMOVE_ITEM( &( pxUnblockedTCB->xEventListItem ) ); + + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + listREMOVE_ITEM( &( pxUnblockedTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxUnblockedTCB ); + + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked on a kernel object then xNextTaskUnblockTime + * might be set to the blocked task's time out time. If the task is + * unblocked for a reason other than a timeout xNextTaskUnblockTime is + * normally left unchanged, because it is automatically reset to a new + * value when the tick count equals xNextTaskUnblockTime. However if + * tickless idling is used it might be more important to enter sleep mode + * at the earliest possible time - so reset xNextTaskUnblockTime here to + * ensure it is updated at the earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + } + else + { + /* The delayed and ready lists cannot be accessed, so hold this task + * pending until the scheduler is resumed. */ + listINSERT_END( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); + } + + #if ( configNUMBER_OF_CORES == 1 ) + { + if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* Return true if the task removed from the event list has a higher + * priority than the calling task. This allows the calling task to know if + * it should force a context switch now. */ + xReturn = pdTRUE; + + /* Mark that a yield is pending in case the user is not using the + * "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + xYieldPendings[ 0 ] = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + xReturn = pdFALSE; + + #if ( configUSE_PREEMPTION == 1 ) + { + prvYieldForTask( pxUnblockedTCB ); + + if( xYieldPendings[ portGET_CORE_ID() ] != pdFALSE ) + { + xReturn = pdTRUE; + } + } + #endif /* #if ( configUSE_PREEMPTION == 1 ) */ + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + + traceRETURN_xTaskRemoveFromEventList( xReturn ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, + const TickType_t xItemValue ) +{ + TCB_t * pxUnblockedTCB; + + traceENTER_vTaskRemoveFromUnorderedEventList( pxEventListItem, xItemValue ); + + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by + * the event flags implementation. */ + configASSERT( uxSchedulerSuspended != ( UBaseType_t ) 0U ); + + /* Store the new item value in the event list. */ + listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); + + /* Remove the event list form the event flag. Interrupts do not access + * event flags. */ + /* MISRA Ref 11.5.3 [Void pointer assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxUnblockedTCB = listGET_LIST_ITEM_OWNER( pxEventListItem ); + configASSERT( pxUnblockedTCB ); + listREMOVE_ITEM( pxEventListItem ); + + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked on a kernel object then xNextTaskUnblockTime + * might be set to the blocked task's time out time. If the task is + * unblocked for a reason other than a timeout xNextTaskUnblockTime is + * normally left unchanged, because it is automatically reset to a new + * value when the tick count equals xNextTaskUnblockTime. However if + * tickless idling is used it might be more important to enter sleep mode + * at the earliest possible time - so reset xNextTaskUnblockTime here to + * ensure it is updated at the earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + + /* Remove the task from the delayed list and add it to the ready list. The + * scheduler is suspended so interrupts will not be accessing the ready + * lists. */ + listREMOVE_ITEM( &( pxUnblockedTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxUnblockedTCB ); + + #if ( configNUMBER_OF_CORES == 1 ) + { + if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The unblocked task has a priority above that of the calling task, so + * a context switch is required. This function is called with the + * scheduler suspended so xYieldPending is set so the context switch + * occurs immediately that the scheduler is resumed (unsuspended). */ + xYieldPendings[ 0 ] = pdTRUE; + } + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + #if ( configUSE_PREEMPTION == 1 ) + { + taskENTER_CRITICAL(); + { + prvYieldForTask( pxUnblockedTCB ); + } + taskEXIT_CRITICAL(); + } + #endif + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + + traceRETURN_vTaskRemoveFromUnorderedEventList(); +} +/*-----------------------------------------------------------*/ + +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) +{ + traceENTER_vTaskSetTimeOutState( pxTimeOut ); + + configASSERT( pxTimeOut ); + taskENTER_CRITICAL(); + { + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; + } + taskEXIT_CRITICAL(); + + traceRETURN_vTaskSetTimeOutState(); +} +/*-----------------------------------------------------------*/ + +void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) +{ + traceENTER_vTaskInternalSetTimeOutState( pxTimeOut ); + + /* For internal use only as it does not use a critical section. */ + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; + + traceRETURN_vTaskInternalSetTimeOutState(); +} +/*-----------------------------------------------------------*/ + +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, + TickType_t * const pxTicksToWait ) +{ + BaseType_t xReturn; + + traceENTER_xTaskCheckForTimeOut( pxTimeOut, pxTicksToWait ); + + configASSERT( pxTimeOut ); + configASSERT( pxTicksToWait ); + + taskENTER_CRITICAL(); + { + /* Minor optimisation. The tick count cannot change in this block. */ + const TickType_t xConstTickCount = xTickCount; + const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering; + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + if( pxCurrentTCB->ucDelayAborted != ( uint8_t ) pdFALSE ) + { + /* The delay was aborted, which is not the same as a time out, + * but has the same result. */ + pxCurrentTCB->ucDelayAborted = ( uint8_t ) pdFALSE; + xReturn = pdTRUE; + } + else + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + if( *pxTicksToWait == portMAX_DELAY ) + { + /* If INCLUDE_vTaskSuspend is set to 1 and the block time + * specified is the maximum block time then the task should block + * indefinitely, and therefore never time out. */ + xReturn = pdFALSE; + } + else + #endif + + if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( xConstTickCount >= pxTimeOut->xTimeOnEntering ) ) + { + /* The tick count is greater than the time at which + * vTaskSetTimeout() was called, but has also overflowed since + * vTaskSetTimeOut() was called. It must have wrapped all the way + * around and gone past again. This passed since vTaskSetTimeout() + * was called. */ + xReturn = pdTRUE; + *pxTicksToWait = ( TickType_t ) 0; + } + else if( xElapsedTime < *pxTicksToWait ) + { + /* Not a genuine timeout. Adjust parameters for time remaining. */ + *pxTicksToWait -= xElapsedTime; + vTaskInternalSetTimeOutState( pxTimeOut ); + xReturn = pdFALSE; + } + else + { + *pxTicksToWait = ( TickType_t ) 0; + xReturn = pdTRUE; + } + } + taskEXIT_CRITICAL(); + + traceRETURN_xTaskCheckForTimeOut( xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskMissedYield( void ) +{ + traceENTER_vTaskMissedYield(); + + /* Must be called from within a critical section. */ + xYieldPendings[ portGET_CORE_ID() ] = pdTRUE; + + traceRETURN_vTaskMissedYield(); +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) + { + UBaseType_t uxReturn; + TCB_t const * pxTCB; + + traceENTER_uxTaskGetTaskNumber( xTask ); + + if( xTask != NULL ) + { + pxTCB = xTask; + uxReturn = pxTCB->uxTaskNumber; + } + else + { + uxReturn = 0U; + } + + traceRETURN_uxTaskGetTaskNumber( uxReturn ); + + return uxReturn; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskSetTaskNumber( TaskHandle_t xTask, + const UBaseType_t uxHandle ) + { + TCB_t * pxTCB; + + traceENTER_vTaskSetTaskNumber( xTask, uxHandle ); + + if( xTask != NULL ) + { + pxTCB = xTask; + pxTCB->uxTaskNumber = uxHandle; + } + + traceRETURN_vTaskSetTaskNumber(); + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +/* + * ----------------------------------------------------------- + * The passive idle task. + * ---------------------------------------------------------- + * + * The passive idle task is used for all the additional cores in a SMP + * system. There must be only 1 active idle task and the rest are passive + * idle tasks. + * + * The portTASK_FUNCTION() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvPassiveIdleTask( void *pvParameters ); + */ + +#if ( configNUMBER_OF_CORES > 1 ) + static portTASK_FUNCTION( prvPassiveIdleTask, pvParameters ) + { + ( void ) pvParameters; + + taskYIELD(); + + for( ; configCONTROL_INFINITE_LOOP(); ) + { + #if ( configUSE_PREEMPTION == 0 ) + { + /* If we are not using preemption we keep forcing a task switch to + * see if any other task has become available. If we are using + * preemption we don't need to do this as any task becoming available + * will automatically get the processor anyway. */ + taskYIELD(); + } + #endif /* configUSE_PREEMPTION */ + + #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) + { + /* When using preemption tasks of equal priority will be + * timesliced. If a task that is sharing the idle priority is ready + * to run then the idle task should yield before the end of the + * timeslice. + * + * A critical region is not required here as we are just reading from + * the list, and an occasional incorrect value will not matter. If + * the ready list at the idle priority contains one more task than the + * number of idle tasks, which is equal to the configured numbers of cores + * then a task other than the idle task is ready to execute. */ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) configNUMBER_OF_CORES ) + { + taskYIELD(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */ + + #if ( configUSE_PASSIVE_IDLE_HOOK == 1 ) + { + /* Call the user defined function from within the idle task. This + * allows the application designer to add background functionality + * without the overhead of a separate task. + * + * This hook is intended to manage core activity such as disabling cores that go idle. + * + * NOTE: vApplicationPassiveIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, + * CALL A FUNCTION THAT MIGHT BLOCK. */ + vApplicationPassiveIdleHook(); + } + #endif /* configUSE_PASSIVE_IDLE_HOOK */ + } + } +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + +/* + * ----------------------------------------------------------- + * The idle task. + * ---------------------------------------------------------- + * + * The portTASK_FUNCTION() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ + +static portTASK_FUNCTION( prvIdleTask, pvParameters ) +{ + /* Stop warnings. */ + ( void ) pvParameters; + + /** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE + * SCHEDULER IS STARTED. **/ + + /* In case a task that has a secure context deletes itself, in which case + * the idle task is responsible for deleting the task's secure context, if + * any. */ + portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE ); + + #if ( configNUMBER_OF_CORES > 1 ) + { + /* SMP all cores start up in the idle task. This initial yield gets the application + * tasks started. */ + taskYIELD(); + } + #endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + + for( ; configCONTROL_INFINITE_LOOP(); ) + { + /* See if any tasks have deleted themselves - if so then the idle task + * is responsible for freeing the deleted task's TCB and stack. */ + prvCheckTasksWaitingTermination(); + + #if ( configUSE_PREEMPTION == 0 ) + { + /* If we are not using preemption we keep forcing a task switch to + * see if any other task has become available. If we are using + * preemption we don't need to do this as any task becoming available + * will automatically get the processor anyway. */ + taskYIELD(); + } + #endif /* configUSE_PREEMPTION */ + + #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) + { + /* When using preemption tasks of equal priority will be + * timesliced. If a task that is sharing the idle priority is ready + * to run then the idle task should yield before the end of the + * timeslice. + * + * A critical region is not required here as we are just reading from + * the list, and an occasional incorrect value will not matter. If + * the ready list at the idle priority contains one more task than the + * number of idle tasks, which is equal to the configured numbers of cores + * then a task other than the idle task is ready to execute. */ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) configNUMBER_OF_CORES ) + { + taskYIELD(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */ + + #if ( configUSE_IDLE_HOOK == 1 ) + { + /* Call the user defined function from within the idle task. */ + vApplicationIdleHook(); + } + #endif /* configUSE_IDLE_HOOK */ + + /* This conditional compilation should use inequality to 0, not equality + * to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when + * user defined low power mode implementations require + * configUSE_TICKLESS_IDLE to be set to a value other than 1. */ + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + TickType_t xExpectedIdleTime; + + /* It is not desirable to suspend then resume the scheduler on + * each iteration of the idle task. Therefore, a preliminary + * test of the expected idle time is performed without the + * scheduler suspended. The result here is not necessarily + * valid. */ + xExpectedIdleTime = prvGetExpectedIdleTime(); + + if( xExpectedIdleTime >= ( TickType_t ) configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) + { + vTaskSuspendAll(); + { + /* Now the scheduler is suspended, the expected idle + * time can be sampled again, and this time its value can + * be used. */ + configASSERT( xNextTaskUnblockTime >= xTickCount ); + xExpectedIdleTime = prvGetExpectedIdleTime(); + + /* Define the following macro to set xExpectedIdleTime to 0 + * if the application does not want + * portSUPPRESS_TICKS_AND_SLEEP() to be called. */ + configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime ); + + if( xExpectedIdleTime >= ( TickType_t ) configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) + { + traceLOW_POWER_IDLE_BEGIN(); + portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ); + traceLOW_POWER_IDLE_END(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + ( void ) xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TICKLESS_IDLE */ + + #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_PASSIVE_IDLE_HOOK == 1 ) ) + { + /* Call the user defined function from within the idle task. This + * allows the application designer to add background functionality + * without the overhead of a separate task. + * + * This hook is intended to manage core activity such as disabling cores that go idle. + * + * NOTE: vApplicationPassiveIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, + * CALL A FUNCTION THAT MIGHT BLOCK. */ + vApplicationPassiveIdleHook(); + } + #endif /* #if ( ( configNUMBER_OF_CORES > 1 ) && ( configUSE_PASSIVE_IDLE_HOOK == 1 ) ) */ + } +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TICKLESS_IDLE != 0 ) + + eSleepModeStatus eTaskConfirmSleepModeStatus( void ) + { + #if ( INCLUDE_vTaskSuspend == 1 ) + /* The idle task exists in addition to the application tasks. */ + const UBaseType_t uxNonApplicationTasks = configNUMBER_OF_CORES; + #endif /* INCLUDE_vTaskSuspend */ + + eSleepModeStatus eReturn = eStandardSleep; + + traceENTER_eTaskConfirmSleepModeStatus(); + + /* This function must be called from a critical section. */ + + if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0U ) + { + /* A task was made ready while the scheduler was suspended. */ + eReturn = eAbortSleep; + } + else if( xYieldPendings[ portGET_CORE_ID() ] != pdFALSE ) + { + /* A yield was pended while the scheduler was suspended. */ + eReturn = eAbortSleep; + } + else if( xPendedTicks != 0U ) + { + /* A tick interrupt has already occurred but was held pending + * because the scheduler is suspended. */ + eReturn = eAbortSleep; + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + else if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == ( uxCurrentNumberOfTasks - uxNonApplicationTasks ) ) + { + /* If all the tasks are in the suspended list (which might mean they + * have an infinite block time rather than actually being suspended) + * then it is safe to turn all clocks off and just wait for external + * interrupts. */ + eReturn = eNoTasksWaitingTimeout; + } + #endif /* INCLUDE_vTaskSuspend */ + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_eTaskConfirmSleepModeStatus( eReturn ); + + return eReturn; + } + +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) + + void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, + BaseType_t xIndex, + void * pvValue ) + { + TCB_t * pxTCB; + + traceENTER_vTaskSetThreadLocalStoragePointer( xTaskToSet, xIndex, pvValue ); + + if( ( xIndex >= 0 ) && + ( xIndex < ( BaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS ) ) + { + pxTCB = prvGetTCBFromHandle( xTaskToSet ); + configASSERT( pxTCB != NULL ); + pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; + } + + traceRETURN_vTaskSetThreadLocalStoragePointer(); + } + +#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS */ +/*-----------------------------------------------------------*/ + +#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) + + void * pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, + BaseType_t xIndex ) + { + void * pvReturn = NULL; + TCB_t * pxTCB; + + traceENTER_pvTaskGetThreadLocalStoragePointer( xTaskToQuery, xIndex ); + + if( ( xIndex >= 0 ) && + ( xIndex < ( BaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS ) ) + { + pxTCB = prvGetTCBFromHandle( xTaskToQuery ); + configASSERT( pxTCB != NULL ); + + pvReturn = pxTCB->pvThreadLocalStoragePointers[ xIndex ]; + } + else + { + pvReturn = NULL; + } + + traceRETURN_pvTaskGetThreadLocalStoragePointer( pvReturn ); + + return pvReturn; + } + +#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS */ +/*-----------------------------------------------------------*/ + +#if ( portUSING_MPU_WRAPPERS == 1 ) + + void vTaskAllocateMPURegions( TaskHandle_t xTaskToModify, + const MemoryRegion_t * const pxRegions ) + { + TCB_t * pxTCB; + + traceENTER_vTaskAllocateMPURegions( xTaskToModify, pxRegions ); + + /* If null is passed in here then we are modifying the MPU settings of + * the calling task. */ + pxTCB = prvGetTCBFromHandle( xTaskToModify ); + configASSERT( pxTCB != NULL ); + + vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), pxRegions, NULL, 0 ); + + traceRETURN_vTaskAllocateMPURegions(); + } + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseTaskLists( void ) +{ + UBaseType_t uxPriority; + + for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ) + { + vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); + } + + vListInitialise( &xDelayedTaskList1 ); + vListInitialise( &xDelayedTaskList2 ); + vListInitialise( &xPendingReadyList ); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + vListInitialise( &xTasksWaitingTermination ); + } + #endif /* INCLUDE_vTaskDelete */ + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + vListInitialise( &xSuspendedTaskList ); + } + #endif /* INCLUDE_vTaskSuspend */ + + /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList + * using list2. */ + pxDelayedTaskList = &xDelayedTaskList1; + pxOverflowDelayedTaskList = &xDelayedTaskList2; +} +/*-----------------------------------------------------------*/ + +static void prvCheckTasksWaitingTermination( void ) +{ + /** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/ + + #if ( INCLUDE_vTaskDelete == 1 ) + { + TCB_t * pxTCB; + + /* uxDeletedTasksWaitingCleanUp is used to prevent taskENTER_CRITICAL() + * being called too often in the idle task. */ + while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U ) + { + #if ( configNUMBER_OF_CORES == 1 ) + { + taskENTER_CRITICAL(); + { + { + /* MISRA Ref 11.5.3 [Void pointer assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + --uxCurrentNumberOfTasks; + --uxDeletedTasksWaitingCleanUp; + } + } + taskEXIT_CRITICAL(); + + prvDeleteTCB( pxTCB ); + } + #else /* #if( configNUMBER_OF_CORES == 1 ) */ + { + pxTCB = NULL; + + taskENTER_CRITICAL(); + { + /* For SMP, multiple idles can be running simultaneously + * and we need to check that other idles did not cleanup while we were + * waiting to enter the critical section. */ + if( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U ) + { + /* MISRA Ref 11.5.3 [Void pointer assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); + + if( pxTCB->xTaskRunState == taskTASK_NOT_RUNNING ) + { + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + --uxCurrentNumberOfTasks; + --uxDeletedTasksWaitingCleanUp; + } + else + { + /* The TCB to be deleted still has not yet been switched out + * by the scheduler, so we will just exit this loop early and + * try again next time. */ + taskEXIT_CRITICAL(); + break; + } + } + } + taskEXIT_CRITICAL(); + + if( pxTCB != NULL ) + { + prvDeleteTCB( pxTCB ); + } + } + #endif /* #if( configNUMBER_OF_CORES == 1 ) */ + } + } + #endif /* INCLUDE_vTaskDelete */ +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskGetInfo( TaskHandle_t xTask, + TaskStatus_t * pxTaskStatus, + BaseType_t xGetFreeStackSpace, + eTaskState eState ) + { + TCB_t * pxTCB; + + traceENTER_vTaskGetInfo( xTask, pxTaskStatus, xGetFreeStackSpace, eState ); + + /* xTask is NULL then get the state of the calling task. */ + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + pxTaskStatus->xHandle = pxTCB; + pxTaskStatus->pcTaskName = ( const char * ) &( pxTCB->pcTaskName[ 0 ] ); + pxTaskStatus->uxCurrentPriority = pxTCB->uxPriority; + pxTaskStatus->pxStackBase = pxTCB->pxStack; + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + pxTaskStatus->pxTopOfStack = ( StackType_t * ) pxTCB->pxTopOfStack; + pxTaskStatus->pxEndOfStack = pxTCB->pxEndOfStack; + #endif + pxTaskStatus->xTaskNumber = pxTCB->uxTCBNumber; + + #if ( ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) + { + pxTaskStatus->uxCoreAffinityMask = pxTCB->uxCoreAffinityMask; + } + #endif + + #if ( configUSE_MUTEXES == 1 ) + { + pxTaskStatus->uxBasePriority = pxTCB->uxBasePriority; + } + #else + { + pxTaskStatus->uxBasePriority = 0; + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxTaskStatus->ulRunTimeCounter = ulTaskGetRunTimeCounter( xTask ); + } + #else + { + pxTaskStatus->ulRunTimeCounter = ( configRUN_TIME_COUNTER_TYPE ) 0; + } + #endif + + /* Obtaining the task state is a little fiddly, so is only done if the + * value of eState passed into this function is eInvalid - otherwise the + * state is just set to whatever is passed in. */ + if( eState != eInvalid ) + { + if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) + { + pxTaskStatus->eCurrentState = eRunning; + } + else + { + pxTaskStatus->eCurrentState = eState; + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* If the task is in the suspended list then there is a + * chance it is actually just blocked indefinitely - so really + * it should be reported as being in the Blocked state. */ + if( eState == eSuspended ) + { + vTaskSuspendAll(); + { + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + pxTaskStatus->eCurrentState = eBlocked; + } + else + { + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + BaseType_t x; + + /* The task does not appear on the event list item of + * and of the RTOS objects, but could still be in the + * blocked state if it is waiting on its notification + * rather than waiting on an object. If not, is + * suspended. */ + for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) + { + if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) + { + pxTaskStatus->eCurrentState = eBlocked; + break; + } + } + } + #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ + } + } + ( void ) xTaskResumeAll(); + } + } + #endif /* INCLUDE_vTaskSuspend */ + + /* Tasks can be in pending ready list and other state list at the + * same time. These tasks are in ready state no matter what state + * list the task is in. */ + taskENTER_CRITICAL(); + { + if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) != pdFALSE ) + { + pxTaskStatus->eCurrentState = eReady; + } + } + taskEXIT_CRITICAL(); + } + } + else + { + pxTaskStatus->eCurrentState = eTaskGetState( pxTCB ); + } + + /* Obtaining the stack space takes some time, so the xGetFreeStackSpace + * parameter is provided to allow it to be skipped. */ + if( xGetFreeStackSpace != pdFALSE ) + { + #if ( portSTACK_GROWTH > 0 ) + { + pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxEndOfStack ); + } + #else + { + pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxStack ); + } + #endif + } + else + { + pxTaskStatus->usStackHighWaterMark = 0; + } + + traceRETURN_vTaskGetInfo(); + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t * pxTaskStatusArray, + List_t * pxList, + eTaskState eState ) + { + UBaseType_t uxTask = 0; + const ListItem_t * pxEndMarker = listGET_END_MARKER( pxList ); + ListItem_t * pxIterator; + TCB_t * pxTCB = NULL; + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + /* Populate an TaskStatus_t structure within the + * pxTaskStatusArray array for each task that is referenced from + * pxList. See the definition of TaskStatus_t in task.h for the + * meaning of each TaskStatus_t structure member. */ + for( pxIterator = listGET_HEAD_ENTRY( pxList ); pxIterator != pxEndMarker; pxIterator = listGET_NEXT( pxIterator ) ) + { + /* MISRA Ref 11.5.3 [Void pointer assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxTCB = listGET_LIST_ITEM_OWNER( pxIterator ); + + vTaskGetInfo( ( TaskHandle_t ) pxTCB, &( pxTaskStatusArray[ uxTask ] ), pdTRUE, eState ); + uxTask++; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return uxTask; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) + { + configSTACK_DEPTH_TYPE uxCount = 0U; + + while( *pucStackByte == ( uint8_t ) tskSTACK_FILL_BYTE ) + { + pucStackByte -= portSTACK_GROWTH; + uxCount++; + } + + uxCount /= ( configSTACK_DEPTH_TYPE ) sizeof( StackType_t ); + + return uxCount; + } + +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) + +/* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. */ + configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + uint8_t * pucEndOfStack; + configSTACK_DEPTH_TYPE uxReturn; + + traceENTER_uxTaskGetStackHighWaterMark2( xTask ); + + /* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are + * the same except for their return type. Using configSTACK_DEPTH_TYPE + * allows the user to determine the return type. It gets around the + * problem of the value overflowing on 8-bit types without breaking + * backward compatibility for applications that expect an 8-bit return + * type. */ + + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + #if portSTACK_GROWTH < 0 + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; + } + #else + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = prvTaskCheckFreeStackSpace( pucEndOfStack ); + + traceRETURN_uxTaskGetStackHighWaterMark2( uxReturn ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskGetStackHighWaterMark2 */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) + + UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + uint8_t * pucEndOfStack; + UBaseType_t uxReturn; + + traceENTER_uxTaskGetStackHighWaterMark( xTask ); + + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + #if portSTACK_GROWTH < 0 + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; + } + #else + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = ( UBaseType_t ) prvTaskCheckFreeStackSpace( pucEndOfStack ); + + traceRETURN_uxTaskGetStackHighWaterMark( uxReturn ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskGetStackHighWaterMark */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + static void prvDeleteTCB( TCB_t * pxTCB ) + { + /* This call is required specifically for the TriCore port. It must be + * above the vPortFree() calls. The call is also used by ports/demos that + * want to allocate and clean RAM statically. */ + portCLEAN_UP_TCB( pxTCB ); + + #if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ) + { + /* Free up the memory allocated for the task's TLS Block. */ + configDEINIT_TLS_BLOCK( pxTCB->xTLSBlock ); + } + #endif + + #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) + { + /* The task can only have been allocated dynamically - free both + * the stack and TCB. */ + vPortFreeStack( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + #elif ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* The task could have been allocated statically or dynamically, so + * check what was statically allocated before trying to free the + * memory. */ + if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ) + { + /* Both the stack and TCB were allocated dynamically, so both + * must be freed. */ + vPortFreeStack( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ) + { + /* Only the stack was statically allocated, so the TCB is the + * only memory that must be freed. */ + vPortFree( pxTCB ); + } + else + { + /* Neither the stack nor the TCB were allocated dynamically, so + * nothing needs to be freed. */ + configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ); + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + } + +#endif /* INCLUDE_vTaskDelete */ +/*-----------------------------------------------------------*/ + +static void prvResetNextTaskUnblockTime( void ) +{ + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) + { + /* The new current delayed list is empty. Set xNextTaskUnblockTime to + * the maximum possible value so it is extremely unlikely that the + * if( xTickCount >= xNextTaskUnblockTime ) test will pass until + * there is an item in the delayed list. */ + xNextTaskUnblockTime = portMAX_DELAY; + } + else + { + /* The new current delayed list is not empty, get the value of + * the item at the head of the delayed list. This is the time at + * which the task at the head of the delayed list should be removed + * from the Blocked state. */ + xNextTaskUnblockTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxDelayedTaskList ); + } +} +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_RECURSIVE_MUTEXES == 1 ) ) || ( configNUMBER_OF_CORES > 1 ) + + #if ( configNUMBER_OF_CORES == 1 ) + TaskHandle_t xTaskGetCurrentTaskHandle( void ) + { + TaskHandle_t xReturn; + + traceENTER_xTaskGetCurrentTaskHandle(); + + /* A critical section is not required as this is not called from + * an interrupt and the current TCB will always be the same for any + * individual execution thread. */ + xReturn = pxCurrentTCB; + + traceRETURN_xTaskGetCurrentTaskHandle( xReturn ); + + return xReturn; + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + TaskHandle_t xTaskGetCurrentTaskHandle( void ) + { + TaskHandle_t xReturn; + UBaseType_t uxSavedInterruptStatus; + + traceENTER_xTaskGetCurrentTaskHandle(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK(); + { + xReturn = pxCurrentTCBs[ portGET_CORE_ID() ]; + } + portCLEAR_INTERRUPT_MASK( uxSavedInterruptStatus ); + + traceRETURN_xTaskGetCurrentTaskHandle( xReturn ); + + return xReturn; + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + + TaskHandle_t xTaskGetCurrentTaskHandleForCore( BaseType_t xCoreID ) + { + TaskHandle_t xReturn = NULL; + + traceENTER_xTaskGetCurrentTaskHandleForCore( xCoreID ); + + if( taskVALID_CORE_ID( xCoreID ) != pdFALSE ) + { + #if ( configNUMBER_OF_CORES == 1 ) + xReturn = pxCurrentTCB; + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + xReturn = pxCurrentTCBs[ xCoreID ]; + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + } + + traceRETURN_xTaskGetCurrentTaskHandleForCore( xReturn ); + + return xReturn; + } + +#endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_RECURSIVE_MUTEXES == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + + BaseType_t xTaskGetSchedulerState( void ) + { + BaseType_t xReturn; + + traceENTER_xTaskGetSchedulerState(); + + if( xSchedulerRunning == pdFALSE ) + { + xReturn = taskSCHEDULER_NOT_STARTED; + } + else + { + #if ( configNUMBER_OF_CORES > 1 ) + taskENTER_CRITICAL(); + #endif + { + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + xReturn = taskSCHEDULER_RUNNING; + } + else + { + xReturn = taskSCHEDULER_SUSPENDED; + } + } + #if ( configNUMBER_OF_CORES > 1 ) + taskEXIT_CRITICAL(); + #endif + } + + traceRETURN_xTaskGetSchedulerState( xReturn ); + + return xReturn; + } + +#endif /* ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) + { + TCB_t * const pxMutexHolderTCB = pxMutexHolder; + BaseType_t xReturn = pdFALSE; + + traceENTER_xTaskPriorityInherit( pxMutexHolder ); + + /* If the mutex is taken by an interrupt, the mutex holder is NULL. Priority + * inheritance is not applied in this scenario. */ + if( pxMutexHolder != NULL ) + { + /* If the holder of the mutex has a priority below the priority of + * the task attempting to obtain the mutex then it will temporarily + * inherit the priority of the task attempting to obtain the mutex. */ + if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority ) + { + /* Adjust the mutex holder state to account for its new + * priority. Only reset the event list item value if the value is + * not being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == ( ( TickType_t ) 0U ) ) + { + listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the task being modified is in the ready state it will need + * to be moved into a new list. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE ) + { + if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + * there is no need to check again and the port level + * reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Inherit the priority before being moved into the new list. */ + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; + prvAddTaskToReadyList( pxMutexHolderTCB ); + #if ( configNUMBER_OF_CORES > 1 ) + { + /* The priority of the task is raised. Yield for this task + * if it is not running. */ + if( taskTASK_IS_RUNNING( pxMutexHolderTCB ) != pdTRUE ) + { + prvYieldForTask( pxMutexHolderTCB ); + } + } + #endif /* if ( configNUMBER_OF_CORES > 1 ) */ + } + else + { + /* Just inherit the priority. */ + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; + } + + traceTASK_PRIORITY_INHERIT( pxMutexHolderTCB, pxCurrentTCB->uxPriority ); + + /* Inheritance occurred. */ + xReturn = pdTRUE; + } + else + { + if( pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority ) + { + /* The base priority of the mutex holder is lower than the + * priority of the task attempting to take the mutex, but the + * current priority of the mutex holder is not lower than the + * priority of the task attempting to take the mutex. + * Therefore the mutex holder must have already inherited a + * priority, but inheritance would have occurred if that had + * not been the case. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_xTaskPriorityInherit( xReturn ); + + return xReturn; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) + { + TCB_t * const pxTCB = pxMutexHolder; + BaseType_t xReturn = pdFALSE; + + traceENTER_xTaskPriorityDisinherit( pxMutexHolder ); + + if( pxMutexHolder != NULL ) + { + /* A task can only have an inherited priority if it holds the mutex. + * If the mutex is held by a task then it cannot be given from an + * interrupt, and if a mutex is given by the holding task then it must + * be the running state task. */ + configASSERT( pxTCB == pxCurrentTCB ); + configASSERT( pxTCB->uxMutexesHeld ); + ( pxTCB->uxMutexesHeld )--; + + /* Has the holder of the mutex inherited the priority of another + * task? */ + if( pxTCB->uxPriority != pxTCB->uxBasePriority ) + { + /* Only disinherit if no other mutexes are held. */ + if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ) + { + /* A task can only have an inherited priority if it holds + * the mutex. If the mutex is held by a task then it cannot be + * given from an interrupt, and if a mutex is given by the + * holding task then it must be the running state task. Remove + * the holding task from the ready list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Disinherit the priority before adding the task into the + * new ready list. */ + traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority ); + pxTCB->uxPriority = pxTCB->uxBasePriority; + + /* Reset the event list item value. It cannot be in use for + * any other purpose if this task is running, and it must be + * running to give back the mutex. */ + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); + prvAddTaskToReadyList( pxTCB ); + #if ( configNUMBER_OF_CORES > 1 ) + { + /* The priority of the task is dropped. Yield the core on + * which the task is running. */ + if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) + { + prvYieldCore( pxTCB->xTaskRunState ); + } + } + #endif /* if ( configNUMBER_OF_CORES > 1 ) */ + + /* Return true to indicate that a context switch is required. + * This is only actually required in the corner case whereby + * multiple mutexes were held and the mutexes were given back + * in an order different to that in which they were taken. + * If a context switch did not occur when the first mutex was + * returned, even if a task was waiting on it, then a context + * switch should occur when the last mutex is returned whether + * a task is waiting on it or not. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_xTaskPriorityDisinherit( xReturn ); + + return xReturn; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, + UBaseType_t uxHighestPriorityWaitingTask ) + { + TCB_t * const pxTCB = pxMutexHolder; + UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse; + const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1; + + traceENTER_vTaskPriorityDisinheritAfterTimeout( pxMutexHolder, uxHighestPriorityWaitingTask ); + + if( pxMutexHolder != NULL ) + { + /* If pxMutexHolder is not NULL then the holder must hold at least + * one mutex. */ + configASSERT( pxTCB->uxMutexesHeld ); + + /* Determine the priority to which the priority of the task that + * holds the mutex should be set. This will be the greater of the + * holding task's base priority and the priority of the highest + * priority task that is waiting to obtain the mutex. */ + if( pxTCB->uxBasePriority < uxHighestPriorityWaitingTask ) + { + uxPriorityToUse = uxHighestPriorityWaitingTask; + } + else + { + uxPriorityToUse = pxTCB->uxBasePriority; + } + + /* Does the priority need to change? */ + if( pxTCB->uxPriority != uxPriorityToUse ) + { + /* Only disinherit if no other mutexes are held. This is a + * simplification in the priority inheritance implementation. If + * the task that holds the mutex is also holding other mutexes then + * the other mutexes may have caused the priority inheritance. */ + if( pxTCB->uxMutexesHeld == uxOnlyOneMutexHeld ) + { + /* If a task has timed out because it already holds the + * mutex it was trying to obtain then it cannot of inherited + * its own priority. */ + configASSERT( pxTCB != pxCurrentTCB ); + + /* Disinherit the priority, remembering the previous + * priority to facilitate determining the subject task's + * state. */ + traceTASK_PRIORITY_DISINHERIT( pxTCB, uxPriorityToUse ); + uxPriorityUsedOnEntry = pxTCB->uxPriority; + pxTCB->uxPriority = uxPriorityToUse; + + /* Only reset the event list item value if the value is not + * being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == ( ( TickType_t ) 0U ) ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriorityToUse ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the running task is not the task that holds the mutex + * then the task that holds the mutex could be in either the + * Ready, Blocked or Suspended states. Only remove the task + * from its current state list if it is in the Ready state as + * the task's priority is going to change and there is one + * Ready list per priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + * there is no need to check again and the port level + * reset macro can be called directly. */ + portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvAddTaskToReadyList( pxTCB ); + #if ( configNUMBER_OF_CORES > 1 ) + { + /* The priority of the task is dropped. Yield the core on + * which the task is running. */ + if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) + { + prvYieldCore( pxTCB->xTaskRunState ); + } + } + #endif /* if ( configNUMBER_OF_CORES > 1 ) */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_vTaskPriorityDisinheritAfterTimeout(); + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configNUMBER_OF_CORES > 1 ) + +/* If not in a critical section then yield immediately. + * Otherwise set xYieldPendings to true to wait to + * yield until exiting the critical section. + */ + void vTaskYieldWithinAPI( void ) + { + UBaseType_t ulState; + + traceENTER_vTaskYieldWithinAPI(); + + ulState = portSET_INTERRUPT_MASK(); + { + const BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID(); + + if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) == 0U ) + { + portYIELD(); + } + else + { + xYieldPendings[ xCoreID ] = pdTRUE; + } + } + portCLEAR_INTERRUPT_MASK( ulState ); + + traceRETURN_vTaskYieldWithinAPI(); + } +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + +/*-----------------------------------------------------------*/ + +#if ( ( portCRITICAL_NESTING_IN_TCB == 1 ) && ( configNUMBER_OF_CORES == 1 ) ) + + void vTaskEnterCritical( void ) + { + traceENTER_vTaskEnterCritical(); + + portDISABLE_INTERRUPTS(); + + if( xSchedulerRunning != pdFALSE ) + { + ( pxCurrentTCB->uxCriticalNesting )++; + + /* This is not the interrupt safe version of the enter critical + * function so assert() if it is being called from an interrupt + * context. Only API functions that end in "FromISR" can be used in an + * interrupt. Only assert if the critical nesting count is 1 to + * protect against recursive calls if the assert function also uses a + * critical section. */ + if( pxCurrentTCB->uxCriticalNesting == 1U ) + { + portASSERT_IF_IN_ISR(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_vTaskEnterCritical(); + } + +#endif /* #if ( ( portCRITICAL_NESTING_IN_TCB == 1 ) && ( configNUMBER_OF_CORES == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( configNUMBER_OF_CORES > 1 ) + + void vTaskEnterCritical( void ) + { + traceENTER_vTaskEnterCritical(); + + portDISABLE_INTERRUPTS(); + { + const BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID(); + + if( xSchedulerRunning != pdFALSE ) + { + if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) == 0U ) + { + portGET_TASK_LOCK( xCoreID ); + portGET_ISR_LOCK( xCoreID ); + } + + portINCREMENT_CRITICAL_NESTING_COUNT( xCoreID ); + + /* This is not the interrupt safe version of the enter critical + * function so assert() if it is being called from an interrupt + * context. Only API functions that end in "FromISR" can be used in an + * interrupt. Only assert if the critical nesting count is 1 to + * protect against recursive calls if the assert function also uses a + * critical section. */ + if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) == 1U ) + { + portASSERT_IF_IN_ISR(); + + if( uxSchedulerSuspended == 0U ) + { + /* The only time there would be a problem is if this is called + * before a context switch and vTaskExitCritical() is called + * after pxCurrentTCB changes. Therefore this should not be + * used within vTaskSwitchContext(). */ + prvCheckForRunStateChange(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + traceRETURN_vTaskEnterCritical(); + } + +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + +/*-----------------------------------------------------------*/ + +#if ( configNUMBER_OF_CORES > 1 ) + + UBaseType_t vTaskEnterCriticalFromISR( void ) + { + UBaseType_t uxSavedInterruptStatus = 0; + const BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID(); + + traceENTER_vTaskEnterCriticalFromISR(); + + if( xSchedulerRunning != pdFALSE ) + { + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + + if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) == 0U ) + { + portGET_ISR_LOCK( xCoreID ); + } + + portINCREMENT_CRITICAL_NESTING_COUNT( xCoreID ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_vTaskEnterCriticalFromISR( uxSavedInterruptStatus ); + + return uxSavedInterruptStatus; + } + +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( ( portCRITICAL_NESTING_IN_TCB == 1 ) && ( configNUMBER_OF_CORES == 1 ) ) + + void vTaskExitCritical( void ) + { + traceENTER_vTaskExitCritical(); + + if( xSchedulerRunning != pdFALSE ) + { + /* If pxCurrentTCB->uxCriticalNesting is zero then this function + * does not match a previous call to vTaskEnterCritical(). */ + configASSERT( pxCurrentTCB->uxCriticalNesting > 0U ); + + /* This function should not be called in ISR. Use vTaskExitCriticalFromISR + * to exit critical section from ISR. */ + portASSERT_IF_IN_ISR(); + + if( pxCurrentTCB->uxCriticalNesting > 0U ) + { + ( pxCurrentTCB->uxCriticalNesting )--; + + if( pxCurrentTCB->uxCriticalNesting == 0U ) + { + portENABLE_INTERRUPTS(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_vTaskExitCritical(); + } + +#endif /* #if ( ( portCRITICAL_NESTING_IN_TCB == 1 ) && ( configNUMBER_OF_CORES == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( configNUMBER_OF_CORES > 1 ) + + void vTaskExitCritical( void ) + { + const BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID(); + + traceENTER_vTaskExitCritical(); + + if( xSchedulerRunning != pdFALSE ) + { + /* If critical nesting count is zero then this function + * does not match a previous call to vTaskEnterCritical(). */ + configASSERT( portGET_CRITICAL_NESTING_COUNT( xCoreID ) > 0U ); + + /* This function should not be called in ISR. Use vTaskExitCriticalFromISR + * to exit critical section from ISR. */ + portASSERT_IF_IN_ISR(); + + if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) > 0U ) + { + portDECREMENT_CRITICAL_NESTING_COUNT( xCoreID ); + + if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) == 0U ) + { + BaseType_t xYieldCurrentTask; + + /* Get the xYieldPending stats inside the critical section. */ + xYieldCurrentTask = xYieldPendings[ xCoreID ]; + + portRELEASE_ISR_LOCK( xCoreID ); + portRELEASE_TASK_LOCK( xCoreID ); + portENABLE_INTERRUPTS(); + + /* When a task yields in a critical section it just sets + * xYieldPending to true. So now that we have exited the + * critical section check if xYieldPending is true, and + * if so yield. */ + if( xYieldCurrentTask != pdFALSE ) + { + portYIELD(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_vTaskExitCritical(); + } + +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( configNUMBER_OF_CORES > 1 ) + + void vTaskExitCriticalFromISR( UBaseType_t uxSavedInterruptStatus ) + { + BaseType_t xCoreID; + + traceENTER_vTaskExitCriticalFromISR( uxSavedInterruptStatus ); + + if( xSchedulerRunning != pdFALSE ) + { + xCoreID = ( BaseType_t ) portGET_CORE_ID(); + + /* If critical nesting count is zero then this function + * does not match a previous call to vTaskEnterCritical(). */ + configASSERT( portGET_CRITICAL_NESTING_COUNT( xCoreID ) > 0U ); + + if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) > 0U ) + { + portDECREMENT_CRITICAL_NESTING_COUNT( xCoreID ); + + if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) == 0U ) + { + portRELEASE_ISR_LOCK( xCoreID ); + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_vTaskExitCriticalFromISR(); + } + +#endif /* #if ( configNUMBER_OF_CORES > 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) + + static char * prvWriteNameToBuffer( char * pcBuffer, + const char * pcTaskName ) + { + size_t x; + + /* Start by copying the entire string. */ + ( void ) strcpy( pcBuffer, pcTaskName ); + + /* Pad the end of the string with spaces to ensure columns line up when + * printed out. */ + for( x = strlen( pcBuffer ); x < ( size_t ) ( ( size_t ) configMAX_TASK_NAME_LEN - 1U ); x++ ) + { + pcBuffer[ x ] = ' '; + } + + /* Terminate. */ + pcBuffer[ x ] = ( char ) 0x00; + + /* Return the new end of string. */ + return &( pcBuffer[ x ] ); + } + +#endif /* ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) + + void vTaskListTasks( char * pcWriteBuffer, + size_t uxBufferLength ) + { + TaskStatus_t * pxTaskStatusArray; + size_t uxConsumedBufferLength = 0; + size_t uxCharsWrittenBySnprintf; + int iSnprintfReturnValue; + BaseType_t xOutputBufferFull = pdFALSE; + UBaseType_t uxArraySize, x; + char cStatus; + + traceENTER_vTaskListTasks( pcWriteBuffer, uxBufferLength ); + + /* + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many + * of the demo applications. Do not consider it to be part of the + * scheduler. + * + * vTaskListTasks() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that + * displays task: names, states, priority, stack usage and task number. + * Stack usage specified as the number of unused StackType_t words stack can hold + * on top of stack - not the number of bytes. + * + * vTaskListTasks() has a dependency on the snprintf() C library function that + * might bloat the code size, use a lot of stack, and provide different + * results on different platforms. An alternative, tiny, third party, + * and limited functionality implementation of snprintf() is provided in + * many of the FreeRTOS/Demo sub-directories in a file called + * printf-stdarg.c (note printf-stdarg.c does not provide a full + * snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly + * through a call to vTaskListTasks(). + */ + + + /* Make sure the write buffer does not contain a string. */ + *pcWriteBuffer = ( char ) 0x00; + + /* Take a snapshot of the number of tasks in case it changes while this + * function is executing. */ + uxArraySize = uxCurrentNumberOfTasks; + + /* Allocate an array index for each task. NOTE! if + * configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + * equate to NULL. */ + /* MISRA Ref 11.5.1 [Malloc memory assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); + + if( pxTaskStatusArray != NULL ) + { + /* Generate the (binary) data. */ + uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, NULL ); + + /* Create a human readable table from the binary data. */ + for( x = 0; x < uxArraySize; x++ ) + { + switch( pxTaskStatusArray[ x ].eCurrentState ) + { + case eRunning: + cStatus = tskRUNNING_CHAR; + break; + + case eReady: + cStatus = tskREADY_CHAR; + break; + + case eBlocked: + cStatus = tskBLOCKED_CHAR; + break; + + case eSuspended: + cStatus = tskSUSPENDED_CHAR; + break; + + case eDeleted: + cStatus = tskDELETED_CHAR; + break; + + case eInvalid: /* Fall through. */ + default: /* Should not get here, but it is included + * to prevent static checking errors. */ + cStatus = ( char ) 0x00; + break; + } + + /* Is there enough space in the buffer to hold task name? */ + if( ( uxConsumedBufferLength + configMAX_TASK_NAME_LEN ) <= uxBufferLength ) + { + /* Write the task name to the string, padding with spaces so it + * can be printed in tabular form more easily. */ + pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); + /* Do not count the terminating null character. */ + uxConsumedBufferLength = uxConsumedBufferLength + ( configMAX_TASK_NAME_LEN - 1U ); + + /* Is there space left in the buffer? -1 is done because snprintf + * writes a terminating null character. So we are essentially + * checking if the buffer has space to write at least one non-null + * character. */ + if( uxConsumedBufferLength < ( uxBufferLength - 1U ) ) + { + /* Write the rest of the string. */ + #if ( ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) + /* MISRA Ref 21.6.1 [snprintf for utility] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-216 */ + /* coverity[misra_c_2012_rule_21_6_violation] */ + iSnprintfReturnValue = snprintf( pcWriteBuffer, + uxBufferLength - uxConsumedBufferLength, + "\t%c\t%u\t%u\t%u\t0x%x\r\n", + cStatus, + ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, + ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, + ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber, + ( unsigned int ) pxTaskStatusArray[ x ].uxCoreAffinityMask ); + #else /* ( ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */ + /* MISRA Ref 21.6.1 [snprintf for utility] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-216 */ + /* coverity[misra_c_2012_rule_21_6_violation] */ + iSnprintfReturnValue = snprintf( pcWriteBuffer, + uxBufferLength - uxConsumedBufferLength, + "\t%c\t%u\t%u\t%u\r\n", + cStatus, + ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, + ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, + ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber ); + #endif /* ( ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */ + uxCharsWrittenBySnprintf = prvSnprintfReturnValueToCharsWritten( iSnprintfReturnValue, uxBufferLength - uxConsumedBufferLength ); + + uxConsumedBufferLength += uxCharsWrittenBySnprintf; + pcWriteBuffer += uxCharsWrittenBySnprintf; + } + else + { + xOutputBufferFull = pdTRUE; + } + } + else + { + xOutputBufferFull = pdTRUE; + } + + if( xOutputBufferFull == pdTRUE ) + { + break; + } + } + + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + * is 0 then vPortFree() will be #defined to nothing. */ + vPortFree( pxTaskStatusArray ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_vTaskListTasks(); + } + +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */ +/*----------------------------------------------------------*/ + +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configUSE_TRACE_FACILITY == 1 ) ) + + void vTaskGetRunTimeStatistics( char * pcWriteBuffer, + size_t uxBufferLength ) + { + TaskStatus_t * pxTaskStatusArray; + size_t uxConsumedBufferLength = 0; + size_t uxCharsWrittenBySnprintf; + int iSnprintfReturnValue; + BaseType_t xOutputBufferFull = pdFALSE; + UBaseType_t uxArraySize, x; + configRUN_TIME_COUNTER_TYPE ulTotalTime = 0; + configRUN_TIME_COUNTER_TYPE ulStatsAsPercentage; + + traceENTER_vTaskGetRunTimeStatistics( pcWriteBuffer, uxBufferLength ); + + /* + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many + * of the demo applications. Do not consider it to be part of the + * scheduler. + * + * vTaskGetRunTimeStatistics() calls uxTaskGetSystemState(), then formats part + * of the uxTaskGetSystemState() output into a human readable table that + * displays the amount of time each task has spent in the Running state + * in both absolute and percentage terms. + * + * vTaskGetRunTimeStatistics() has a dependency on the snprintf() C library + * function that might bloat the code size, use a lot of stack, and + * provide different results on different platforms. An alternative, + * tiny, third party, and limited functionality implementation of + * snprintf() is provided in many of the FreeRTOS/Demo sub-directories in + * a file called printf-stdarg.c (note printf-stdarg.c does not provide + * a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly + * through a call to vTaskGetRunTimeStatistics(). + */ + + /* Make sure the write buffer does not contain a string. */ + *pcWriteBuffer = ( char ) 0x00; + + /* Take a snapshot of the number of tasks in case it changes while this + * function is executing. */ + uxArraySize = uxCurrentNumberOfTasks; + + /* Allocate an array index for each task. NOTE! If + * configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + * equate to NULL. */ + /* MISRA Ref 11.5.1 [Malloc memory assignment] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 */ + /* coverity[misra_c_2012_rule_11_5_violation] */ + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); + + if( pxTaskStatusArray != NULL ) + { + /* Generate the (binary) data. */ + uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime ); + + /* For percentage calculations. */ + ulTotalTime /= ( ( configRUN_TIME_COUNTER_TYPE ) 100U ); + + /* Avoid divide by zero errors. */ + if( ulTotalTime > 0U ) + { + /* Create a human readable table from the binary data. */ + for( x = 0; x < uxArraySize; x++ ) + { + /* What percentage of the total run time has the task used? + * This will always be rounded down to the nearest integer. + * ulTotalRunTime has already been divided by 100. */ + ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime; + + /* Is there enough space in the buffer to hold task name? */ + if( ( uxConsumedBufferLength + configMAX_TASK_NAME_LEN ) <= uxBufferLength ) + { + /* Write the task name to the string, padding with + * spaces so it can be printed in tabular form more + * easily. */ + pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); + /* Do not count the terminating null character. */ + uxConsumedBufferLength = uxConsumedBufferLength + ( configMAX_TASK_NAME_LEN - 1U ); + + /* Is there space left in the buffer? -1 is done because snprintf + * writes a terminating null character. So we are essentially + * checking if the buffer has space to write at least one non-null + * character. */ + if( uxConsumedBufferLength < ( uxBufferLength - 1U ) ) + { + if( ulStatsAsPercentage > 0U ) + { + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { + /* MISRA Ref 21.6.1 [snprintf for utility] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-216 */ + /* coverity[misra_c_2012_rule_21_6_violation] */ + iSnprintfReturnValue = snprintf( pcWriteBuffer, + uxBufferLength - uxConsumedBufferLength, + "\t%lu\t\t%lu%%\r\n", + pxTaskStatusArray[ x ].ulRunTimeCounter, + ulStatsAsPercentage ); + } + #else /* ifdef portLU_PRINTF_SPECIFIER_REQUIRED */ + { + /* sizeof( int ) == sizeof( long ) so a smaller + * printf() library can be used. */ + /* MISRA Ref 21.6.1 [snprintf for utility] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-216 */ + /* coverity[misra_c_2012_rule_21_6_violation] */ + iSnprintfReturnValue = snprintf( pcWriteBuffer, + uxBufferLength - uxConsumedBufferLength, + "\t%u\t\t%u%%\r\n", + ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, + ( unsigned int ) ulStatsAsPercentage ); + } + #endif /* ifdef portLU_PRINTF_SPECIFIER_REQUIRED */ + } + else + { + /* If the percentage is zero here then the task has + * consumed less than 1% of the total run time. */ + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { + /* MISRA Ref 21.6.1 [snprintf for utility] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-216 */ + /* coverity[misra_c_2012_rule_21_6_violation] */ + iSnprintfReturnValue = snprintf( pcWriteBuffer, + uxBufferLength - uxConsumedBufferLength, + "\t%lu\t\t<1%%\r\n", + pxTaskStatusArray[ x ].ulRunTimeCounter ); + } + #else + { + /* sizeof( int ) == sizeof( long ) so a smaller + * printf() library can be used. */ + /* MISRA Ref 21.6.1 [snprintf for utility] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-216 */ + /* coverity[misra_c_2012_rule_21_6_violation] */ + iSnprintfReturnValue = snprintf( pcWriteBuffer, + uxBufferLength - uxConsumedBufferLength, + "\t%u\t\t<1%%\r\n", + ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter ); + } + #endif /* ifdef portLU_PRINTF_SPECIFIER_REQUIRED */ + } + + uxCharsWrittenBySnprintf = prvSnprintfReturnValueToCharsWritten( iSnprintfReturnValue, uxBufferLength - uxConsumedBufferLength ); + uxConsumedBufferLength += uxCharsWrittenBySnprintf; + pcWriteBuffer += uxCharsWrittenBySnprintf; + } + else + { + xOutputBufferFull = pdTRUE; + } + } + else + { + xOutputBufferFull = pdTRUE; + } + + if( xOutputBufferFull == pdTRUE ) + { + break; + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + * is 0 then vPortFree() will be #defined to nothing. */ + vPortFree( pxTaskStatusArray ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceRETURN_vTaskGetRunTimeStatistics(); + } + +#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) */ +/*-----------------------------------------------------------*/ + +TickType_t uxTaskResetEventItemValue( void ) +{ + TickType_t uxReturn; + + traceENTER_uxTaskResetEventItemValue(); + + uxReturn = listGET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ) ); + + /* Reset the event list item to its normal value - so it can be used with + * queues and semaphores. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ) ); + + traceRETURN_uxTaskResetEventItemValue( uxReturn ); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + TaskHandle_t pvTaskIncrementMutexHeldCount( void ) + { + TCB_t * pxTCB; + + traceENTER_pvTaskIncrementMutexHeldCount(); + + pxTCB = pxCurrentTCB; + + /* If xSemaphoreCreateMutex() is called before any tasks have been created + * then pxCurrentTCB will be NULL. */ + if( pxTCB != NULL ) + { + ( pxTCB->uxMutexesHeld )++; + } + + traceRETURN_pvTaskIncrementMutexHeldCount( pxTCB ); + + return pxTCB; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn, + BaseType_t xClearCountOnExit, + TickType_t xTicksToWait ) + { + uint32_t ulReturn; + BaseType_t xAlreadyYielded, xShouldBlock = pdFALSE; + + traceENTER_ulTaskGenericNotifyTake( uxIndexToWaitOn, xClearCountOnExit, xTicksToWait ); + + configASSERT( uxIndexToWaitOn < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* If the notification count is zero, and if we are willing to wait for a + * notification, then block the task and wait. */ + if( ( pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] == 0U ) && ( xTicksToWait > ( TickType_t ) 0 ) ) + { + /* We suspend the scheduler here as prvAddCurrentTaskToDelayedList is a + * non-deterministic operation. */ + vTaskSuspendAll(); + { + /* We MUST enter a critical section to atomically check if a notification + * has occurred and set the flag to indicate that we are waiting for + * a notification. If we do not do so, a notification sent from an ISR + * will get lost. */ + taskENTER_CRITICAL(); + { + /* Only block if the notification count is not already non-zero. */ + if( pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] == 0U ) + { + /* Mark this task as waiting for a notification. */ + pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION; + + /* Arrange to wait for a notification. */ + xShouldBlock = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + /* We are now out of the critical section but the scheduler is still + * suspended, so we are safe to do non-deterministic operations such + * as prvAddCurrentTaskToDelayedList. */ + if( xShouldBlock == pdTRUE ) + { + traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWaitOn ); + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + xAlreadyYielded = xTaskResumeAll(); + + /* Force a reschedule if xTaskResumeAll has not already done so. */ + if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) ) + { + taskYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + taskENTER_CRITICAL(); + { + traceTASK_NOTIFY_TAKE( uxIndexToWaitOn ); + ulReturn = pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ]; + + if( ulReturn != 0U ) + { + if( xClearCountOnExit != pdFALSE ) + { + pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] = ( uint32_t ) 0U; + } + else + { + pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] = ulReturn - ( uint32_t ) 1; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskNOT_WAITING_NOTIFICATION; + } + taskEXIT_CRITICAL(); + + traceRETURN_ulTaskGenericNotifyTake( ulReturn ); + + return ulReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn, + uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t * pulNotificationValue, + TickType_t xTicksToWait ) + { + BaseType_t xReturn, xAlreadyYielded, xShouldBlock = pdFALSE; + + traceENTER_xTaskGenericNotifyWait( uxIndexToWaitOn, ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ); + + configASSERT( uxIndexToWaitOn < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* If the task hasn't received a notification, and if we are willing to wait + * for it, then block the task and wait. */ + if( ( pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] != taskNOTIFICATION_RECEIVED ) && ( xTicksToWait > ( TickType_t ) 0 ) ) + { + /* We suspend the scheduler here as prvAddCurrentTaskToDelayedList is a + * non-deterministic operation. */ + vTaskSuspendAll(); + { + /* We MUST enter a critical section to atomically check and update the + * task notification value. If we do not do so, a notification from + * an ISR will get lost. */ + taskENTER_CRITICAL(); + { + /* Only block if a notification is not already pending. */ + if( pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] != taskNOTIFICATION_RECEIVED ) + { + /* Clear bits in the task's notification value as bits may get + * set by the notifying task or interrupt. This can be used + * to clear the value to zero. */ + pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] &= ~ulBitsToClearOnEntry; + + /* Mark this task as waiting for a notification. */ + pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION; + + /* Arrange to wait for a notification. */ + xShouldBlock = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + /* We are now out of the critical section but the scheduler is still + * suspended, so we are safe to do non-deterministic operations such + * as prvAddCurrentTaskToDelayedList. */ + if( xShouldBlock == pdTRUE ) + { + traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWaitOn ); + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + xAlreadyYielded = xTaskResumeAll(); + + /* Force a reschedule if xTaskResumeAll has not already done so. */ + if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) ) + { + taskYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + taskENTER_CRITICAL(); + { + traceTASK_NOTIFY_WAIT( uxIndexToWaitOn ); + + if( pulNotificationValue != NULL ) + { + /* Output the current notification value, which may or may not + * have changed. */ + *pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ]; + } + + /* If ucNotifyValue is set then either the task never entered the + * blocked state (because a notification was already pending) or the + * task unblocked because of a notification. Otherwise the task + * unblocked because of a timeout. */ + if( pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] != taskNOTIFICATION_RECEIVED ) + { + /* A notification was not received. */ + xReturn = pdFALSE; + } + else + { + /* A notification was already pending or a notification was + * received while the task was waiting. */ + pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] &= ~ulBitsToClearOnExit; + xReturn = pdTRUE; + } + + pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskNOT_WAITING_NOTIFICATION; + } + taskEXIT_CRITICAL(); + + traceRETURN_xTaskGenericNotifyWait( xReturn ); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue ) + { + TCB_t * pxTCB; + BaseType_t xReturn = pdPASS; + uint8_t ucOriginalNotifyState; + + traceENTER_xTaskGenericNotify( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue ); + + configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + configASSERT( xTaskToNotify ); + pxTCB = xTaskToNotify; + + taskENTER_CRITICAL(); + { + if( pulPreviousNotificationValue != NULL ) + { + *pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ]; + } + + ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ]; + + pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED; + + switch( eAction ) + { + case eSetBits: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue; + break; + + case eIncrement: + ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++; + break; + + case eSetValueWithOverwrite: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + break; + + case eSetValueWithoutOverwrite: + + if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) + { + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + } + else + { + /* The value could not be written to the task. */ + xReturn = pdFAIL; + } + + break; + + case eNoAction: + + /* The task is being notified without its notify value being + * updated. */ + break; + + default: + + /* Should not get here if all enums are handled. + * Artificially force an assert by testing a value the + * compiler can't assume is const. */ + configASSERT( xTickCount == ( TickType_t ) 0 ); + + break; + } + + traceTASK_NOTIFY( uxIndexToNotify ); + + /* If the task is in the blocked state specifically to wait for a + * notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked waiting for a notification then + * xNextTaskUnblockTime might be set to the blocked task's time + * out time. If the task is unblocked for a reason other than + * a timeout xNextTaskUnblockTime is normally left unchanged, + * because it will automatically get reset to a new value when + * the tick count equals xNextTaskUnblockTime. However if + * tickless idling is used it might be more important to enter + * sleep mode at the earliest possible time - so reset + * xNextTaskUnblockTime here to ensure it is updated at the + * earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + + /* Check if the notified task has a priority above the currently + * executing task. */ + taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + traceRETURN_xTaskGenericNotify( xReturn ); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + uint32_t ulValue, + eNotifyAction eAction, + uint32_t * pulPreviousNotificationValue, + BaseType_t * pxHigherPriorityTaskWoken ) + { + TCB_t * pxTCB; + uint8_t ucOriginalNotifyState; + BaseType_t xReturn = pdPASS; + UBaseType_t uxSavedInterruptStatus; + + traceENTER_xTaskGenericNotifyFromISR( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ); + + configASSERT( xTaskToNotify ); + configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + pxTCB = xTaskToNotify; + + /* MISRA Ref 4.7.1 [Return value shall be checked] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ + /* coverity[misra_c_2012_directive_4_7_violation] */ + uxSavedInterruptStatus = ( UBaseType_t ) taskENTER_CRITICAL_FROM_ISR(); + { + if( pulPreviousNotificationValue != NULL ) + { + *pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ]; + } + + ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ]; + pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED; + + switch( eAction ) + { + case eSetBits: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue; + break; + + case eIncrement: + ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++; + break; + + case eSetValueWithOverwrite: + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + break; + + case eSetValueWithoutOverwrite: + + if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) + { + pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue; + } + else + { + /* The value could not be written to the task. */ + xReturn = pdFAIL; + } + + break; + + case eNoAction: + + /* The task is being notified without its notify value being + * updated. */ + break; + + default: + + /* Should not get here if all enums are handled. + * Artificially force an assert by testing a value the + * compiler can't assume is const. */ + configASSERT( xTickCount == ( TickType_t ) 0 ); + break; + } + + traceTASK_NOTIFY_FROM_ISR( uxIndexToNotify ); + + /* If the task is in the blocked state specifically to wait for a + * notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked waiting for a notification then + * xNextTaskUnblockTime might be set to the blocked task's time + * out time. If the task is unblocked for a reason other than + * a timeout xNextTaskUnblockTime is normally left unchanged, + * because it will automatically get reset to a new value when + * the tick count equals xNextTaskUnblockTime. However if + * tickless idling is used it might be more important to enter + * sleep mode at the earliest possible time - so reset + * xNextTaskUnblockTime here to ensure it is updated at the + * earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + } + else + { + /* The delayed and ready lists cannot be accessed, so hold + * this task pending until the scheduler is resumed. */ + listINSERT_END( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + + #if ( configNUMBER_OF_CORES == 1 ) + { + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The notified task has a priority above the currently + * executing task so a yield is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + + /* Mark that a yield is pending in case the user is not + * using the "xHigherPriorityTaskWoken" parameter to an ISR + * safe FreeRTOS function. */ + xYieldPendings[ 0 ] = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + #if ( configUSE_PREEMPTION == 1 ) + { + prvYieldForTask( pxTCB ); + + if( xYieldPendings[ portGET_CORE_ID() ] == pdTRUE ) + { + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + } + } + #endif /* if ( configUSE_PREEMPTION == 1 ) */ + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + } + } + taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_xTaskGenericNotifyFromISR( xReturn ); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + void vTaskGenericNotifyGiveFromISR( TaskHandle_t xTaskToNotify, + UBaseType_t uxIndexToNotify, + BaseType_t * pxHigherPriorityTaskWoken ) + { + TCB_t * pxTCB; + uint8_t ucOriginalNotifyState; + UBaseType_t uxSavedInterruptStatus; + + traceENTER_vTaskGenericNotifyGiveFromISR( xTaskToNotify, uxIndexToNotify, pxHigherPriorityTaskWoken ); + + configASSERT( xTaskToNotify ); + configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* RTOS ports that support interrupt nesting have the concept of a + * maximum system call (or maximum API call) interrupt priority. + * Interrupts that are above the maximum system call priority are keep + * permanently enabled, even when the RTOS kernel is in a critical section, + * but cannot make any calls to FreeRTOS API functions. If configASSERT() + * is defined in FreeRTOSConfig.h then + * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + * failure if a FreeRTOS API function is called from an interrupt that has + * been assigned a priority above the configured maximum system call + * priority. Only FreeRTOS functions that end in FromISR can be called + * from interrupts that have been assigned a priority at or (logically) + * below the maximum system call interrupt priority. FreeRTOS maintains a + * separate interrupt safe API to ensure interrupt entry is as fast and as + * simple as possible. More information (albeit Cortex-M specific) is + * provided on the following link: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + pxTCB = xTaskToNotify; + + /* MISRA Ref 4.7.1 [Return value shall be checked] */ + /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ + /* coverity[misra_c_2012_directive_4_7_violation] */ + uxSavedInterruptStatus = ( UBaseType_t ) taskENTER_CRITICAL_FROM_ISR(); + { + ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ]; + pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED; + + /* 'Giving' is equivalent to incrementing a count in a counting + * semaphore. */ + ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++; + + traceTASK_NOTIFY_GIVE_FROM_ISR( uxIndexToNotify ); + + /* If the task is in the blocked state specifically to wait for a + * notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + if( uxSchedulerSuspended == ( UBaseType_t ) 0U ) + { + listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked waiting for a notification then + * xNextTaskUnblockTime might be set to the blocked task's time + * out time. If the task is unblocked for a reason other than + * a timeout xNextTaskUnblockTime is normally left unchanged, + * because it will automatically get reset to a new value when + * the tick count equals xNextTaskUnblockTime. However if + * tickless idling is used it might be more important to enter + * sleep mode at the earliest possible time - so reset + * xNextTaskUnblockTime here to ensure it is updated at the + * earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + } + else + { + /* The delayed and ready lists cannot be accessed, so hold + * this task pending until the scheduler is resumed. */ + listINSERT_END( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + + #if ( configNUMBER_OF_CORES == 1 ) + { + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The notified task has a priority above the currently + * executing task so a yield is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + + /* Mark that a yield is pending in case the user is not + * using the "xHigherPriorityTaskWoken" parameter in an ISR + * safe FreeRTOS function. */ + xYieldPendings[ 0 ] = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else /* #if ( configNUMBER_OF_CORES == 1 ) */ + { + #if ( configUSE_PREEMPTION == 1 ) + { + prvYieldForTask( pxTCB ); + + if( xYieldPendings[ portGET_CORE_ID() ] == pdTRUE ) + { + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + } + } + #endif /* #if ( configUSE_PREEMPTION == 1 ) */ + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + } + } + taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); + + traceRETURN_vTaskGenericNotifyGiveFromISR(); + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotifyStateClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear ) + { + TCB_t * pxTCB; + BaseType_t xReturn; + + traceENTER_xTaskGenericNotifyStateClear( xTask, uxIndexToClear ); + + configASSERT( uxIndexToClear < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* If null is passed in here then it is the calling task that is having + * its notification state cleared. */ + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + taskENTER_CRITICAL(); + { + if( pxTCB->ucNotifyState[ uxIndexToClear ] == taskNOTIFICATION_RECEIVED ) + { + pxTCB->ucNotifyState[ uxIndexToClear ] = taskNOT_WAITING_NOTIFICATION; + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + } + taskEXIT_CRITICAL(); + + traceRETURN_xTaskGenericNotifyStateClear( xReturn ); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TASK_NOTIFICATIONS == 1 ) + + uint32_t ulTaskGenericNotifyValueClear( TaskHandle_t xTask, + UBaseType_t uxIndexToClear, + uint32_t ulBitsToClear ) + { + TCB_t * pxTCB; + uint32_t ulReturn; + + traceENTER_ulTaskGenericNotifyValueClear( xTask, uxIndexToClear, ulBitsToClear ); + + configASSERT( uxIndexToClear < configTASK_NOTIFICATION_ARRAY_ENTRIES ); + + /* If null is passed in here then it is the calling task that is having + * its notification state cleared. */ + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + taskENTER_CRITICAL(); + { + /* Return the notification as it was before the bits were cleared, + * then clear the bit mask. */ + ulReturn = pxTCB->ulNotifiedValue[ uxIndexToClear ]; + pxTCB->ulNotifiedValue[ uxIndexToClear ] &= ~ulBitsToClear; + } + taskEXIT_CRITICAL(); + + traceRETURN_ulTaskGenericNotifyValueClear( ulReturn ); + + return ulReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimeCounter( const TaskHandle_t xTask ) + { + TCB_t * pxTCB; + configRUN_TIME_COUNTER_TYPE ulTotalTime = 0, ulTimeSinceLastSwitchedIn = 0, ulTaskRunTime = 0; + + traceENTER_ulTaskGetRunTimeCounter( xTask ); + + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + taskENTER_CRITICAL(); + { + if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalTime ); + #else + ulTotalTime = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + #if ( configNUMBER_OF_CORES == 1 ) + ulTimeSinceLastSwitchedIn = ulTotalTime - ulTaskSwitchedInTime[ 0 ]; + #else + ulTimeSinceLastSwitchedIn = ulTotalTime - ulTaskSwitchedInTime[ pxTCB->xTaskRunState ]; + #endif + } + + ulTaskRunTime = pxTCB->ulRunTimeCounter + ulTimeSinceLastSwitchedIn; + } + taskEXIT_CRITICAL(); + + traceRETURN_ulTaskGetRunTimeCounter( ulTaskRunTime ); + + return ulTaskRunTime; + } + +#endif /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + configRUN_TIME_COUNTER_TYPE ulTaskGetRunTimePercent( const TaskHandle_t xTask ) + { + TCB_t * pxTCB; + configRUN_TIME_COUNTER_TYPE ulTotalTime, ulReturn, ulTaskRunTime; + + traceENTER_ulTaskGetRunTimePercent( xTask ); + + ulTaskRunTime = ulTaskGetRunTimeCounter( xTask ); + + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalTime ); + #else + ulTotalTime = ( configRUN_TIME_COUNTER_TYPE ) portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + /* For percentage calculations. */ + ulTotalTime /= ( configRUN_TIME_COUNTER_TYPE ) 100; + + /* Avoid divide by zero errors. */ + if( ulTotalTime > ( configRUN_TIME_COUNTER_TYPE ) 0 ) + { + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + ulReturn = ulTaskRunTime / ulTotalTime; + } + else + { + ulReturn = 0; + } + + traceRETURN_ulTaskGetRunTimePercent( ulReturn ); + + return ulReturn; + } + +#endif /* if ( configGENERATE_RUN_TIME_STATS == 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) + + configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimeCounter( void ) + { + configRUN_TIME_COUNTER_TYPE ulTotalTime = 0, ulTimeSinceLastSwitchedIn = 0, ulIdleTaskRunTime = 0; + BaseType_t i; + + traceENTER_ulTaskGetIdleRunTimeCounter(); + + taskENTER_CRITICAL(); + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalTime ); + #else + ulTotalTime = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + for( i = 0; i < ( BaseType_t ) configNUMBER_OF_CORES; i++ ) + { + if( taskTASK_IS_RUNNING( xIdleTaskHandles[ i ] ) == pdTRUE ) + { + #if ( configNUMBER_OF_CORES == 1 ) + ulTimeSinceLastSwitchedIn = ulTotalTime - ulTaskSwitchedInTime[ 0 ]; + #else + ulTimeSinceLastSwitchedIn = ulTotalTime - ulTaskSwitchedInTime[ xIdleTaskHandles[ i ]->xTaskRunState ]; + #endif + } + else + { + ulTimeSinceLastSwitchedIn = 0; + } + + ulIdleTaskRunTime += ( xIdleTaskHandles[ i ]->ulRunTimeCounter + ulTimeSinceLastSwitchedIn ); + } + } + taskEXIT_CRITICAL(); + + traceRETURN_ulTaskGetIdleRunTimeCounter( ulIdleTaskRunTime ); + + return ulIdleTaskRunTime; + } + +#endif /* if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) + + configRUN_TIME_COUNTER_TYPE ulTaskGetIdleRunTimePercent( void ) + { + configRUN_TIME_COUNTER_TYPE ulTotalTime, ulReturn; + configRUN_TIME_COUNTER_TYPE ulRunTimeCounter = 0; + + traceENTER_ulTaskGetIdleRunTimePercent(); + + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalTime ); + #else + ulTotalTime = ( configRUN_TIME_COUNTER_TYPE ) portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + ulTotalTime *= configNUMBER_OF_CORES; + + /* For percentage calculations. */ + ulTotalTime /= ( configRUN_TIME_COUNTER_TYPE ) 100; + + /* Avoid divide by zero errors. */ + if( ulTotalTime > ( configRUN_TIME_COUNTER_TYPE ) 0 ) + { + ulRunTimeCounter = ulTaskGetIdleRunTimeCounter(); + ulReturn = ulRunTimeCounter / ulTotalTime; + } + else + { + ulReturn = 0; + } + + traceRETURN_ulTaskGetIdleRunTimePercent( ulReturn ); + + return ulReturn; + } + +#endif /* if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) */ +/*-----------------------------------------------------------*/ + +static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, + const BaseType_t xCanBlockIndefinitely ) +{ + TickType_t xTimeToWake; + const TickType_t xConstTickCount = xTickCount; + List_t * const pxDelayedList = pxDelayedTaskList; + List_t * const pxOverflowDelayedList = pxOverflowDelayedTaskList; + + #if ( INCLUDE_xTaskAbortDelay == 1 ) + { + /* About to enter a delayed list, so ensure the ucDelayAborted flag is + * reset to pdFALSE so it can be detected as having been set to pdTRUE + * when the task leaves the Blocked state. */ + pxCurrentTCB->ucDelayAborted = ( uint8_t ) pdFALSE; + } + #endif + + /* Remove the task from the ready list before adding it to the blocked list + * as the same list item is used for both lists. */ + if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* The current task must be in a ready list, so there is no need to + * check, and the port reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) ) + { + /* Add the task to the suspended task list instead of a delayed task + * list to ensure it is not woken by a timing event. It will block + * indefinitely. */ + listINSERT_END( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + /* Calculate the time at which the task should be woken if the event + * does not occur. This may overflow but this doesn't matter, the + * kernel will manage it correctly. */ + xTimeToWake = xConstTickCount + xTicksToWait; + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); + + if( xTimeToWake < xConstTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow + * list. */ + traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST(); + vListInsert( pxOverflowDelayedList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + /* The wake time has not overflowed, so the current block list + * is used. */ + traceMOVED_TASK_TO_DELAYED_LIST(); + vListInsert( pxDelayedList, &( pxCurrentTCB->xStateListItem ) ); + + /* If the task entering the blocked state was placed at the + * head of the list of blocked tasks then xNextTaskUnblockTime + * needs to be updated too. */ + if( xTimeToWake < xNextTaskUnblockTime ) + { + xNextTaskUnblockTime = xTimeToWake; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + } + #else /* INCLUDE_vTaskSuspend */ + { + /* Calculate the time at which the task should be woken if the event + * does not occur. This may overflow but this doesn't matter, the kernel + * will manage it correctly. */ + xTimeToWake = xConstTickCount + xTicksToWait; + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); + + if( xTimeToWake < xConstTickCount ) + { + traceMOVED_TASK_TO_OVERFLOW_DELAYED_LIST(); + /* Wake time has overflowed. Place this item in the overflow list. */ + vListInsert( pxOverflowDelayedList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + traceMOVED_TASK_TO_DELAYED_LIST(); + /* The wake time has not overflowed, so the current block list is used. */ + vListInsert( pxDelayedList, &( pxCurrentTCB->xStateListItem ) ); + + /* If the task entering the blocked state was placed at the head of the + * list of blocked tasks then xNextTaskUnblockTime needs to be updated + * too. */ + if( xTimeToWake < xNextTaskUnblockTime ) + { + xNextTaskUnblockTime = xTimeToWake; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Avoid compiler warning when INCLUDE_vTaskSuspend is not 1. */ + ( void ) xCanBlockIndefinitely; + } + #endif /* INCLUDE_vTaskSuspend */ +} +/*-----------------------------------------------------------*/ + +#if ( portUSING_MPU_WRAPPERS == 1 ) + + xMPU_SETTINGS * xTaskGetMPUSettings( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + + traceENTER_xTaskGetMPUSettings( xTask ); + + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + traceRETURN_xTaskGetMPUSettings( &( pxTCB->xMPUSettings ) ); + + return &( pxTCB->xMPUSettings ); + } + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +/* Code below here allows additional code to be inserted into this source file, + * especially where access to file scope functions and data is needed (for example + * when performing module tests). */ + +#ifdef FREERTOS_MODULE_TEST + #include "tasks_test_access_functions.h" +#endif + + +#if ( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) + + #include "freertos_tasks_c_additions.h" + + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + static void freertos_tasks_c_additions_init( void ) + { + FREERTOS_TASKS_C_ADDITIONS_INIT(); + } + #endif + +#endif /* if ( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) */ +/*-----------------------------------------------------------*/ + +#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configKERNEL_PROVIDED_STATIC_MEMORY == 1 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) + +/* + * This is the kernel provided implementation of vApplicationGetIdleTaskMemory() + * to provide the memory that is used by the Idle task. It is used when + * configKERNEL_PROVIDED_STATIC_MEMORY is set to 1. The application can provide + * it's own implementation of vApplicationGetIdleTaskMemory by setting + * configKERNEL_PROVIDED_STATIC_MEMORY to 0 or leaving it undefined. + */ + void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, + StackType_t ** ppxIdleTaskStackBuffer, + configSTACK_DEPTH_TYPE * puxIdleTaskStackSize ) + { + static StaticTask_t xIdleTaskTCB; + static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ]; + + *ppxIdleTaskTCBBuffer = &( xIdleTaskTCB ); + *ppxIdleTaskStackBuffer = &( uxIdleTaskStack[ 0 ] ); + *puxIdleTaskStackSize = configMINIMAL_STACK_SIZE; + } + + #if ( configNUMBER_OF_CORES > 1 ) + + void vApplicationGetPassiveIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer, + StackType_t ** ppxIdleTaskStackBuffer, + configSTACK_DEPTH_TYPE * puxIdleTaskStackSize, + BaseType_t xPassiveIdleTaskIndex ) + { + static StaticTask_t xIdleTaskTCBs[ configNUMBER_OF_CORES - 1 ]; + static StackType_t uxIdleTaskStacks[ configNUMBER_OF_CORES - 1 ][ configMINIMAL_STACK_SIZE ]; + + *ppxIdleTaskTCBBuffer = &( xIdleTaskTCBs[ xPassiveIdleTaskIndex ] ); + *ppxIdleTaskStackBuffer = &( uxIdleTaskStacks[ xPassiveIdleTaskIndex ][ 0 ] ); + *puxIdleTaskStackSize = configMINIMAL_STACK_SIZE; + } + + #endif /* #if ( configNUMBER_OF_CORES > 1 ) */ + +#endif /* #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configKERNEL_PROVIDED_STATIC_MEMORY == 1 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configKERNEL_PROVIDED_STATIC_MEMORY == 1 ) && ( portUSING_MPU_WRAPPERS == 0 ) && ( configUSE_TIMERS == 1 ) ) + +/* + * This is the kernel provided implementation of vApplicationGetTimerTaskMemory() + * to provide the memory that is used by the Timer service task. It is used when + * configKERNEL_PROVIDED_STATIC_MEMORY is set to 1. The application can provide + * it's own implementation of vApplicationGetTimerTaskMemory by setting + * configKERNEL_PROVIDED_STATIC_MEMORY to 0 or leaving it undefined. + */ + void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer, + StackType_t ** ppxTimerTaskStackBuffer, + configSTACK_DEPTH_TYPE * puxTimerTaskStackSize ) + { + static StaticTask_t xTimerTaskTCB; + static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ]; + + *ppxTimerTaskTCBBuffer = &( xTimerTaskTCB ); + *ppxTimerTaskStackBuffer = &( uxTimerTaskStack[ 0 ] ); + *puxTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; + } + +#endif /* #if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configKERNEL_PROVIDED_STATIC_MEMORY == 1 ) && ( portUSING_MPU_WRAPPERS == 0 ) && ( configUSE_TIMERS == 1 ) ) */ +/*-----------------------------------------------------------*/ + +/* + * 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 vTaskResetState( void ) +{ + BaseType_t xCoreID; + + /* Task control block. */ + #if ( configNUMBER_OF_CORES == 1 ) + { + pxCurrentTCB = NULL; + } + #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ + + #if ( INCLUDE_vTaskDelete == 1 ) + { + uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; + } + #endif /* #if ( INCLUDE_vTaskDelete == 1 ) */ + + #if ( configUSE_POSIX_ERRNO == 1 ) + { + FreeRTOS_errno = 0; + } + #endif /* #if ( configUSE_POSIX_ERRNO == 1 ) */ + + /* Other file private variables. */ + uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; + xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; + uxTopReadyPriority = tskIDLE_PRIORITY; + xSchedulerRunning = pdFALSE; + xPendedTicks = ( TickType_t ) 0U; + + for( xCoreID = 0; xCoreID < configNUMBER_OF_CORES; xCoreID++ ) + { + xYieldPendings[ xCoreID ] = pdFALSE; + } + + xNumOfOverflows = ( BaseType_t ) 0; + uxTaskNumber = ( UBaseType_t ) 0U; + xNextTaskUnblockTime = ( TickType_t ) 0U; + + uxSchedulerSuspended = ( UBaseType_t ) 0U; + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + for( xCoreID = 0; xCoreID < configNUMBER_OF_CORES; xCoreID++ ) + { + ulTaskSwitchedInTime[ xCoreID ] = 0U; + ulTotalRunTime[ xCoreID ] = 0U; + } + } + #endif /* #if ( configGENERATE_RUN_TIME_STATS == 1 ) */ +} +/*-----------------------------------------------------------*/ diff --git a/vendor/Hazard3/hdl/arith/hazard3_alu.v b/vendor/Hazard3/hdl/arith/hazard3_alu.v new file mode 100644 index 0000000..1b798e5 --- /dev/null +++ b/vendor/Hazard3/hdl/arith/hazard3_alu.v @@ -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 diff --git a/vendor/Hazard3/hdl/arith/hazard3_branchcmp.v b/vendor/Hazard3/hdl/arith/hazard3_branchcmp.v new file mode 100644 index 0000000..8672715 --- /dev/null +++ b/vendor/Hazard3/hdl/arith/hazard3_branchcmp.v @@ -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 diff --git a/vendor/Hazard3/hdl/arith/hazard3_mul_fast.v b/vendor/Hazard3/hdl/arith/hazard3_mul_fast.v new file mode 100644 index 0000000..c9fde6e --- /dev/null +++ b/vendor/Hazard3/hdl/arith/hazard3_mul_fast.v @@ -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 diff --git a/vendor/Hazard3/hdl/arith/hazard3_muldiv_seq.v b/vendor/Hazard3/hdl/arith/hazard3_muldiv_seq.v new file mode 100644 index 0000000..1fa4bdb --- /dev/null +++ b/vendor/Hazard3/hdl/arith/hazard3_muldiv_seq.v @@ -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 diff --git a/vendor/Hazard3/hdl/arith/hazard3_onehot_encode.v b/vendor/Hazard3/hdl/arith/hazard3_onehot_encode.v new file mode 100644 index 0000000..b33dd9a --- /dev/null +++ b/vendor/Hazard3/hdl/arith/hazard3_onehot_encode.v @@ -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 diff --git a/vendor/Hazard3/hdl/arith/hazard3_onehot_priority.v b/vendor/Hazard3/hdl/arith/hazard3_onehot_priority.v new file mode 100644 index 0000000..238fac2 --- /dev/null +++ b/vendor/Hazard3/hdl/arith/hazard3_onehot_priority.v @@ -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 diff --git a/vendor/Hazard3/hdl/arith/hazard3_onehot_priority_dynamic.v b/vendor/Hazard3/hdl/arith/hazard3_onehot_priority_dynamic.v new file mode 100644 index 0000000..123cdf8 --- /dev/null +++ b/vendor/Hazard3/hdl/arith/hazard3_onehot_priority_dynamic.v @@ -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 diff --git a/vendor/Hazard3/hdl/arith/hazard3_priority_encode.v b/vendor/Hazard3/hdl/arith/hazard3_priority_encode.v new file mode 100644 index 0000000..172e72a --- /dev/null +++ b/vendor/Hazard3/hdl/arith/hazard3_priority_encode.v @@ -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 diff --git a/vendor/Hazard3/hdl/arith/hazard3_shift_barrel.v b/vendor/Hazard3/hdl/arith/hazard3_shift_barrel.v new file mode 100644 index 0000000..9fb51be --- /dev/null +++ b/vendor/Hazard3/hdl/arith/hazard3_shift_barrel.v @@ -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 diff --git a/vendor/Hazard3/hdl/arith/muldiv_model.py b/vendor/Hazard3/hdl/arith/muldiv_model.py new file mode 100755 index 0000000..2515786 --- /dev/null +++ b/vendor/Hazard3/hdl/arith/muldiv_model.py @@ -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() diff --git a/vendor/Hazard3/hdl/debug/cdc/hazard3_apb_async_bridge.v b/vendor/Hazard3/hdl/debug/cdc/hazard3_apb_async_bridge.v new file mode 100644 index 0000000..a0236f9 --- /dev/null +++ b/vendor/Hazard3/hdl/debug/cdc/hazard3_apb_async_bridge.v @@ -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 diff --git a/vendor/Hazard3/hdl/debug/cdc/hazard3_reset_sync.v b/vendor/Hazard3/hdl/debug/cdc/hazard3_reset_sync.v new file mode 100644 index 0000000..3dc8c32 --- /dev/null +++ b/vendor/Hazard3/hdl/debug/cdc/hazard3_reset_sync.v @@ -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 diff --git a/vendor/Hazard3/hdl/debug/cdc/hazard3_sync_1bit.v b/vendor/Hazard3/hdl/debug/cdc/hazard3_sync_1bit.v new file mode 100644 index 0000000..1e6af4d --- /dev/null +++ b/vendor/Hazard3/hdl/debug/cdc/hazard3_sync_1bit.v @@ -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 diff --git a/vendor/Hazard3/hdl/debug/dm/hazard3_dm.f b/vendor/Hazard3/hdl/debug/dm/hazard3_dm.f new file mode 100644 index 0000000..155edad --- /dev/null +++ b/vendor/Hazard3/hdl/debug/dm/hazard3_dm.f @@ -0,0 +1 @@ +file hazard3_dm.v diff --git a/vendor/Hazard3/hdl/debug/dm/hazard3_dm.v b/vendor/Hazard3/hdl/debug/dm/hazard3_dm.v new file mode 100644 index 0000000..713c38f --- /dev/null +++ b/vendor/Hazard3/hdl/debug/dm/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 diff --git a/vendor/Hazard3/hdl/debug/dm/hazard3_sbus_to_ahb.v b/vendor/Hazard3/hdl/debug/dm/hazard3_sbus_to_ahb.v new file mode 100644 index 0000000..ab6949b --- /dev/null +++ b/vendor/Hazard3/hdl/debug/dm/hazard3_sbus_to_ahb.v @@ -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 diff --git a/vendor/Hazard3/hdl/debug/dtm/hazard3_ecp5_jtag_dtm.f b/vendor/Hazard3/hdl/debug/dtm/hazard3_ecp5_jtag_dtm.f new file mode 100644 index 0000000..0447ef8 --- /dev/null +++ b/vendor/Hazard3/hdl/debug/dtm/hazard3_ecp5_jtag_dtm.f @@ -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 diff --git a/vendor/Hazard3/hdl/debug/dtm/hazard3_ecp5_jtag_dtm.v b/vendor/Hazard3/hdl/debug/dtm/hazard3_ecp5_jtag_dtm.v new file mode 100644 index 0000000..bfaca49 --- /dev/null +++ b/vendor/Hazard3/hdl/debug/dtm/hazard3_ecp5_jtag_dtm.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 diff --git a/vendor/Hazard3/hdl/debug/dtm/hazard3_jtag_dtm.f b/vendor/Hazard3/hdl/debug/dtm/hazard3_jtag_dtm.f new file mode 100644 index 0000000..aa9e2eb --- /dev/null +++ b/vendor/Hazard3/hdl/debug/dtm/hazard3_jtag_dtm.f @@ -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 diff --git a/vendor/Hazard3/hdl/debug/dtm/hazard3_jtag_dtm.v b/vendor/Hazard3/hdl/debug/dtm/hazard3_jtag_dtm.v new file mode 100644 index 0000000..8cc76ab --- /dev/null +++ b/vendor/Hazard3/hdl/debug/dtm/hazard3_jtag_dtm.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 diff --git a/vendor/Hazard3/hdl/debug/dtm/hazard3_jtag_dtm_core.v b/vendor/Hazard3/hdl/debug/dtm/hazard3_jtag_dtm_core.v new file mode 100644 index 0000000..58744b0 --- /dev/null +++ b/vendor/Hazard3/hdl/debug/dtm/hazard3_jtag_dtm_core.v @@ -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 diff --git a/vendor/Hazard3/hdl/debug/dtm/hazard3_xilinx7_jtag_dtm.v b/vendor/Hazard3/hdl/debug/dtm/hazard3_xilinx7_jtag_dtm.v new file mode 100644 index 0000000..ac75737 --- /dev/null +++ b/vendor/Hazard3/hdl/debug/dtm/hazard3_xilinx7_jtag_dtm.v @@ -0,0 +1,162 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2025 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// Implement a RISC-V JTAG DTM tunnelled through a Xilinx BSCANE2 primitive. +// +// Xilinx allows up to four custom DRs to be added to the FPGA TAP controller. +// A JTAG-DTM only needs two: DTMCS and DMI. +// +// With the correct config, OpenOCD can treat the FPGA TAP as a JTAG DTM and +// access RISC-V debug directly. This allows you to debug internal RISC-V +// cores with the same JTAG interface you use to load the FPGA. +// +// CHAIN_DTMCS and CHAIN_DMI select which JTAG IR values are used to access +// these DRs. Values 1 through 4 correspond to Xilinx USER1 through USER4 +// instructions, which have IR values 0x02, 0x03, 0x22, 0x23. + +`default_nettype none + +module hazard3_xilinx7_jtag_dtm #( + parameter SEL_DTMCS = 3, + parameter SEL_DMI = 4, + parameter DTMCS_IDLE_HINT = 3'd4, + parameter W_PADDR = 9, + parameter ABITS = W_PADDR - 2 // do not modify +) ( + // This is synchronous to TCK and asserted for one TCK cycle only + output wire dmihardreset_req, + + // Bus clock + reset for Debug Module Interface + input wire clk_dmi, + input wire rst_n_dmi, + + // Debug Module Interface (APB) + output wire dmi_psel, + output wire dmi_penable, + output wire dmi_pwrite, + output wire [W_PADDR-1:0] dmi_paddr, + output wire [31:0] dmi_pwdata, + input wire [31:0] dmi_prdata, + input wire dmi_pready, + input wire dmi_pslverr +); + +// Signals to/from the Xilinx TAP + +wire jtck_unbuf; +wire jtck; +wire jtdo2; +wire jtdo1; +wire jtdi; +wire jshift; +wire jupdate; +wire jcapture; +wire jrst; +wire jrst_n = !jrst; +wire jce2; +wire jce1; + +BSCANE2 #( + .JTAG_CHAIN (SEL_DTMCS) // Value for USER command. +) bscan_dtmcs ( + .CAPTURE (jcapture), // CAPTURE output from TAP controller. + .DRCK (/* unused */), // Gated TCK output. When SEL is asserted, DRCK toggles when CAPTURE or SHIFT are asserted. + .RESET (jrst), // Reset output for TAP controller. + .RUNTEST (/* unused */), // Output asserted when TAP controller is in Run Test/Idle state. + .SEL (jce1), // USER instruction active output. + .SHIFT (jshift), // SHIFT output from TAP controller. + .TCK (jtck_unbuf), // Test Clock output. Fabric connection to TAP Clock pin. + .TDI (jtdi), // Test Data Input (TDI) output from TAP controller. + .TMS (/* unused */), // Test Mode Select output. Fabric connection to TAP. + .UPDATE (jupdate), // UPDATE output from TAP controller + .TDO (jtdo1) // Test Data Output (TDO) input for USER function. +); + +BSCANE2 #( + .JTAG_CHAIN (SEL_DMI) +) bscan_dmi ( + .CAPTURE (/* unused */), + .DRCK (/* unused */), + .RESET (/* unused */), + .RUNTEST (/* unused */), + .SEL (jce2), + .SHIFT (/* unused */), + .TCK (/* unused */), + .TDI (/* unused */), + .TMS (/* unused */), + .UPDATE (/* unused */), + .TDO (jtdo2) +); + +BUFG bufg_jtck ( + .I (jtck_unbuf), + .O (jtck) +); + +localparam W_DR_SHIFT = ABITS + 32 + 2; + +wire core_dr_wen = jupdate; +wire core_dr_ren = jcapture; +wire core_dr_sel_dmi_ndtmcs = !jce1; +wire dr_shift_en = jshift; +wire [W_DR_SHIFT-1:0] core_dr_wdata; +wire [W_DR_SHIFT-1:0] core_dr_rdata; + +reg [W_DR_SHIFT-1:0] dr_shift; +assign core_dr_wdata = dr_shift; + +always @ (posedge jtck or negedge jrst_n) begin + if (!jrst_n) begin + dr_shift <= {W_DR_SHIFT{1'b0}}; + end else if (core_dr_ren) begin + dr_shift <= core_dr_rdata; + end else if (dr_shift_en) begin + dr_shift <= {jtdi, dr_shift[W_DR_SHIFT-1:1]}; + if (!core_dr_sel_dmi_ndtmcs) + dr_shift[31] <= jtdi; + end +end + +// We have only a single shifter for the two DRs, so these are tied together: +assign jtdo1 = dr_shift[0]; +assign jtdo2 = dr_shift[0]; + +// The actual DTM is in here: + +hazard3_jtag_dtm_core #( + .DTMCS_IDLE_HINT (DTMCS_IDLE_HINT), + .W_ADDR (ABITS) +) inst_hazard3_jtag_dtm_core ( + .tck (jtck), + .trst_n (jrst_n), + + .clk_dmi (clk_dmi), + .rst_n_dmi (rst_n_dmi), + + .dr_wen (core_dr_wen), + .dr_ren (core_dr_ren), + .dr_sel_dmi_ndtmcs (core_dr_sel_dmi_ndtmcs), + .dr_wdata (core_dr_wdata), + .dr_rdata (core_dr_rdata), + + .dmihardreset_req (dmihardreset_req), + + .dmi_psel (dmi_psel), + .dmi_penable (dmi_penable), + .dmi_pwrite (dmi_pwrite), + .dmi_paddr (dmi_paddr[W_PADDR-1:2]), + .dmi_pwdata (dmi_pwdata), + .dmi_prdata (dmi_prdata), + .dmi_pready (dmi_pready), + .dmi_pslverr (dmi_pslverr) +); + +assign dmi_paddr[1:0] = 2'b00; + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3.f b/vendor/Hazard3/hdl/hazard3.f new file mode 100644 index 0000000..3a143d5 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3.f @@ -0,0 +1,22 @@ +file hazard3_core.v +file hazard3_cpu_1port.v +file hazard3_cpu_2port.v +file arith/hazard3_alu.v +file arith/hazard3_branchcmp.v +file arith/hazard3_mul_fast.v +file arith/hazard3_muldiv_seq.v +file arith/hazard3_onehot_encode.v +file arith/hazard3_onehot_priority.v +file arith/hazard3_onehot_priority_dynamic.v +file arith/hazard3_priority_encode.v +file arith/hazard3_shift_barrel.v +file hazard3_csr.v +file hazard3_decode.v +file hazard3_frontend.v +file hazard3_instr_decompress.v +file hazard3_irq_ctrl.v +file hazard3_pmp.v +file hazard3_power_ctrl.v +file hazard3_regfile_1w2r.v +file hazard3_triggers.v +include . diff --git a/vendor/Hazard3/hdl/hazard3_config.vh b/vendor/Hazard3/hdl/hazard3_config.vh new file mode 100644 index 0000000..e52c4c5 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_config.vh @@ -0,0 +1,259 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// Hazard3 CPU configuration parameters + +// To configure Hazard3 you can either edit this file, or set parameters on +// your top-level instantiation, it's up to you. These parameters are all +// plumbed through Hazard3's internal hierarchy to the appropriate places. + +// If you add a parameter here, you should add a matching line to +// hazard3_config_inst.vh to propagate the parameter through module +// instantiations. + +// ---------------------------------------------------------------------------- +// Reset state configuration + +// RESET_VECTOR: Address of first instruction executed. +parameter RESET_VECTOR = 32'h00000000, + +// MTVEC_INIT: Initial value of trap vector base. Bits clear in MTVEC_WMASK +// will never change from this initial value. Bits set in MTVEC_WMASK can be +// written/set/cleared as normal. +// +// Note that mtvec bits 1:0 do not affect the trap base (as per RISC-V spec). +// Bit 1 is don't care, bit 0 selects the vectoring mode: unvectored if == 0 +// (all traps go to mtvec), vectored if == 1 (exceptions go to mtvec, IRQs to +// mtvec + mcause * 4). This means MTVEC_INIT also sets the initial vectoring +// mode. +parameter MTVEC_INIT = 32'h00000000, + +// ---------------------------------------------------------------------------- +// Standard RISC-V ISA support + +// EXTENSION_A: Support for atomic read/modify/write instructions +parameter EXTENSION_A = 1, + +// EXTENSION_C: Support for compressed (variable-width) instructions +parameter EXTENSION_C = 1, + +// EXTENSION_E: Implement the RV32E base extension rather than RV32I. This +// reduces the number of integer registers from 31 to 15. +parameter EXTENSION_E = 0, + +// EXTENSION_M: Support for hardware multiply/divide/modulo instructions +parameter EXTENSION_M = 1, + +// EXTENSION_ZBA: Support for Zba address generation instructions +parameter EXTENSION_ZBA = 0, + +// EXTENSION_ZBB: Support for Zbb basic bit manipulation instructions +parameter EXTENSION_ZBB = 0, + +// EXTENSION_ZBC: Support for Zbc carry-less multiplication instructions +parameter EXTENSION_ZBC = 0, + +// EXTENSION_ZBKB: Support for Zbkb basic bit manipulation for cryptography +// Requires: Zbb. (This flag enables instructions in Zbkb which aren't in Zbb.) +parameter EXTENSION_ZBKB = 0, + +// EXTENSION_ZBKX: support for Zbkx crossbar permutation instructions +parameter EXTENSION_ZBKX = 0, + +// EXTENSION_ZBS: Support for Zbs single-bit manipulation instructions +parameter EXTENSION_ZBS = 0, + +// EXTENSION_ZCB: Support for Zcb basic additional compressed instructions +// Requires: EXTENSION_C. (Some Zcb instructions also require Zbb or M.) +// Note Zca is equivalent to C, as we do not support the F extension. +parameter EXTENSION_ZCB = 0, + +// EXTENSION_ZCLSD: Support for Zclsd compressed load/store pair instructions +// Requires: EXTENSION_ZILSD, EXTENSION_C. +parameter EXTENSION_ZCLSD = 0, + +// EXTENSION_ZCMP: Support for Zcmp push/pop instructions. +// Requires: EXTENSION_C. +parameter EXTENSION_ZCMP = 0, + +// EXTENSION_ZIFENCEI: Support for the fence.i instruction +// Optional, since a plain branch/jump will also flush the prefetch queue. +parameter EXTENSION_ZIFENCEI = 0, + +// EXTENSION_ZILSD: Support for Zilsd load/store pair instructions +parameter EXTENSION_ZILSD = 0, + +// ---------------------------------------------------------------------------- +// Custom RISC-V extensions + +// EXTENSION_XH3B: Custom bit-extract-multiple instructions for Hazard3 +parameter EXTENSION_XH3BEXTM = 0, + +// EXTENSION_XH3IRQ: Custom preemptive, prioritised interrupt support. Can be +// disabled if an external interrupt controller (e.g. PLIC) is used. If +// disabled, and NUM_IRQS > 1, the external interrupts are simply OR'd into +// mip.meip. +parameter EXTENSION_XH3IRQ = 0, + +// EXTENSION_XH3PMPM: PMPCFGMx CSRs to enforce PMP regions in M-mode without +// locking. Unlike ePMP mseccfg.rlb, locked and unlocked regions can coexist +parameter EXTENSION_XH3PMPM = 0, + +// EXTENSION_XH3POWER: Custom power management controls for Hazard3 +parameter EXTENSION_XH3POWER = 0, + +// ---------------------------------------------------------------------------- +// Standard CSR support + +// Note the Zicsr extension is implied by any of CSR_M_MANDATORY, CSR_M_TRAP, +// CSR_COUNTER. + +// CSR_M_MANDATORY: Bare minimum CSR support e.g. misa. Spec says must = 1 if +// CSRs are present, but I won't tell anyone. +parameter CSR_M_MANDATORY = 1, + +// CSR_M_TRAP: Include M-mode trap-handling CSRs, and enable trap support. +parameter CSR_M_TRAP = 1, + +// CSR_COUNTER: Include performance counters and Zicntr CSRs +parameter CSR_COUNTER = 0, + +// U_MODE: Support the U (user) execution mode. In U mode, the core performs +// unprivileged bus accesses, and software's access to CSRs is restricted. +// Additionally, if the PMP is included, the core may restrict U-mode +// software's access to memory. +// Requires: CSR_M_TRAP. +parameter U_MODE = 0, + +// PMP_REGIONS: Number of physical memory protection regions, or 0 for no PMP. +// PMP is more useful if U mode is supported, but this is not a requirement. +parameter PMP_REGIONS = 0, + +// PMP_GRAIN: This is the "G" parameter in the privileged spec. Minimum PMP +// region size is 1 << (G + 2) bytes. If G > 0, PMCFG.A can not be set to +// NA4 (will get set to OFF instead). If G > 1, the G - 1 LSBs of pmpaddr are +// read-only-0 when PMPCFG.A is OFF, and read-only-1 when PMPCFG.A is NAPOT. +parameter PMP_GRAIN = 0, + +// PMP_MATCH_NAPOT: Enable PMP support for the NAPOT (naturally-aligned +// power-of-two) and NA4 (naturally-aligned four-byte) matching modes. When +// disabled, attempting to select these modes will set the PMP region to OFF. +parameter PMP_MATCH_NAPOT = 1, + +// PMP_MATCH_TOR: Enable PMP support for the TOR (top-of-range) matching mode. +// When disabled, attempting to select this mode will set the region to OFF. +parameter PMP_MATCH_TOR = 0, + +// PMPADDR_HARDWIRED: If a bit is 1, the corresponding region's pmpaddr and +// pmpcfg registers are read-only. PMP_GRAIN is ignored on hardwired regions. +// It's recommended to make hardwired regions the highest-numbered, so they +// can be overridden by lower-numbered regions. +parameter PMP_HARDWIRED = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){1'b0}}, + +// PMPADDR_HARDWIRED_ADDR: Values of pmpaddr registers whose PMP_HARDWIRED +// bits are set to 1. Non-hardwired regions reset to all-zeroes. +parameter PMP_HARDWIRED_ADDR = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){32'h0}}, + +// PMPCFG_RESET_VAL: Values of pmpcfg registers whose PMP_HARDWIRED bits are +// set to 1. Non-hardwired regions reset to all zeroes. +parameter PMP_HARDWIRED_CFG = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){8'h00}}, + +// DEBUG_SUPPORT: Support for run/halt and instruction injection from an +// external Debug Module, support for Debug Mode, and Debug Mode CSRs. +// Requires: CSR_M_MANDATORY, CSR_M_TRAP. +parameter DEBUG_SUPPORT = 0, + +// BREAKPOINT_TRIGGERS: Number of triggers which support type=2 execute=1 +// (but not store/load=1, i.e. not a watchpoint). Requires: DEBUG_SUPPORT +parameter BREAKPOINT_TRIGGERS = 0, + +// ---------------------------------------------------------------------------- +// External interrupt support + +// NUM_IRQS: Number of external IRQs. Minimum 1, maximum 512. Note that if +// EXTENSION_XH3IRQ (Hazard3 interrupt controller) is disabled then multiple +// external interrupts are simply OR'd into mip.meip. +parameter NUM_IRQS = 1, + +// IRQ_PRIORITY_BITS: Number of priority bits implemented for each interrupt +// in meipra, if EXTENSION_XH3IRQ is enabled. The number of distinct levels +// is (1 << IRQ_PRIORITY_BITS). Minimum 0, max 4. Note that multiple priority +// levels with a large number of IRQs will have a severe effect on timing. +parameter IRQ_PRIORITY_BITS = 0, + +// IRQ_INPUT_BYPASS: disable the input registers on the external interrupts, +// to reduce latency by one cycle. Can be applied on an IRQ-by-IRQ basis. +// Ignored if EXTENSION_XH3IRQ is disabled. +parameter IRQ_INPUT_BYPASS = {(NUM_IRQS > 0 ? NUM_IRQS : 1){1'b0}}, + +// ---------------------------------------------------------------------------- +// ID registers + +// JEDEC JEP106-compliant vendor ID, can be left at 0 if "not implemented or +// [...] this is a non-commercial implementation" (RISC-V spec). +// 31:7 is continuation code count, 6:0 is ID. Parity bit is not stored. +parameter MVENDORID_VAL = 32'h0, + +// Pointer to configuration structure blob, or all-zeroes. Must be at least +// 4-byte-aligned. +parameter MCONFIGPTR_VAL = 32'h0, + +// ---------------------------------------------------------------------------- +// Performance/size options + +// REDUCED_BYPASS: Remove all forwarding paths except X->X (so back-to-back +// ALU ops can still run at 1 CPI), to save area. +parameter REDUCED_BYPASS = 0, + +// MULDIV_UNROLL: Bits per clock for multiply/divide circuit, if present. Must +// be a power of 2. +parameter MULDIV_UNROLL = 1, + +// MUL_FAST: Use single-cycle multiply circuit for MUL instructions, retiring +// to stage 3. The sequential multiply/divide circuit is still used for MULH* +parameter MUL_FAST = 0, + +// MUL_FASTER: Retire fast multiply results to stage 2 instead of stage 3. +// Throughput is the same, but latency is reduced from 2 cycles to 1 cycle. +// Requires: MUL_FAST. +parameter MUL_FASTER = 0, + +// MULH_FAST: extend the fast multiply circuit to also cover MULH*, and remove +// the multiply functionality from the sequential multiply/divide circuit. +// Requires: MUL_FAST +parameter MULH_FAST = 0, + +// FAST_BRANCHCMP: Instantiate a separate comparator (eq/lt/ltu) for branch +// comparisons, rather than using the ALU. Improves fetch address delay, +// especially if Zba extension is enabled. Disabling may save area. +parameter FAST_BRANCHCMP = 1, + +// RESET_REGFILE: whether to support reset of the general purpose registers. +// There are around 1k bits in the register file, so the reset can be +// disabled e.g. to permit block-RAM inference on FPGA. +parameter RESET_REGFILE = 0, + +// BRANCH_PREDICTOR: enable branch prediction. The branch predictor consists +// of a single BTB entry which is allocated on a taken backward branch, and +// cleared on a mispredicted nontaken branch, a fence.i or a trap. Successful +// prediction eliminates the 1-cyle fetch bubble on a taken branch, usually +// making tight loops faster. +parameter BRANCH_PREDICTOR = 0, + +// MTVEC_WMASK: Mask of which bits in mtvec are writable. Full writability is +// recommended, because a common idiom in setup code is to set mtvec just +// past code that may trap, as a hardware "try {...} catch" block. +// +// - The vectoring mode can be made fixed by clearing the LSB of MTVEC_WMASK +// +// - In vectored mode, the vector table must be aligned to its size, rounded +// up to a power of two. +parameter MTVEC_WMASK = 32'hfffffffd, + +// ---------------------------------------------------------------------------- +// Port size parameters (do not modify) + +parameter W_ADDR = 32, // Do not modify +parameter W_DATA = 32 // Do not modify diff --git a/vendor/Hazard3/hdl/hazard3_config_inst.vh b/vendor/Hazard3/hdl/hazard3_config_inst.vh new file mode 100644 index 0000000..e1118ff --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_config_inst.vh @@ -0,0 +1,59 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// Pass-through of parameters defined in hazard3_config.vh, so that these can +// be set at instantiation rather than editing the config file, and will flow +// correctly down through the hierarchy. + +.RESET_VECTOR (RESET_VECTOR), +.MTVEC_INIT (MTVEC_INIT), +.EXTENSION_A (EXTENSION_A), +.EXTENSION_C (EXTENSION_C), +.EXTENSION_E (EXTENSION_E), +.EXTENSION_M (EXTENSION_M), +.EXTENSION_ZBA (EXTENSION_ZBA), +.EXTENSION_ZBB (EXTENSION_ZBB), +.EXTENSION_ZBC (EXTENSION_ZBC), +.EXTENSION_ZBKB (EXTENSION_ZBKB), +.EXTENSION_ZBKX (EXTENSION_ZBKX), +.EXTENSION_ZBS (EXTENSION_ZBS), +.EXTENSION_ZCB (EXTENSION_ZCB), +.EXTENSION_ZCLSD (EXTENSION_ZCLSD), +.EXTENSION_ZCMP (EXTENSION_ZCMP), +.EXTENSION_ZIFENCEI (EXTENSION_ZIFENCEI), +.EXTENSION_ZILSD (EXTENSION_ZILSD), +.EXTENSION_XH3BEXTM (EXTENSION_XH3BEXTM), +.EXTENSION_XH3IRQ (EXTENSION_XH3IRQ), +.EXTENSION_XH3PMPM (EXTENSION_XH3PMPM), +.EXTENSION_XH3POWER (EXTENSION_XH3POWER), +.CSR_M_MANDATORY (CSR_M_MANDATORY), +.CSR_M_TRAP (CSR_M_TRAP), +.CSR_COUNTER (CSR_COUNTER), +.U_MODE (U_MODE), +.PMP_REGIONS (PMP_REGIONS), +.PMP_GRAIN (PMP_GRAIN), +.PMP_MATCH_NAPOT (PMP_MATCH_NAPOT), +.PMP_MATCH_TOR (PMP_MATCH_TOR), +.PMP_HARDWIRED (PMP_HARDWIRED), +.PMP_HARDWIRED_ADDR (PMP_HARDWIRED_ADDR), +.PMP_HARDWIRED_CFG (PMP_HARDWIRED_CFG), +.DEBUG_SUPPORT (DEBUG_SUPPORT), +.BREAKPOINT_TRIGGERS (BREAKPOINT_TRIGGERS), +.NUM_IRQS (NUM_IRQS), +.IRQ_PRIORITY_BITS (IRQ_PRIORITY_BITS), +.IRQ_INPUT_BYPASS (IRQ_INPUT_BYPASS), +.MVENDORID_VAL (MVENDORID_VAL), +.MCONFIGPTR_VAL (MCONFIGPTR_VAL), +.REDUCED_BYPASS (REDUCED_BYPASS), +.MULDIV_UNROLL (MULDIV_UNROLL), +.MUL_FAST (MUL_FAST), +.MUL_FASTER (MUL_FASTER), +.MULH_FAST (MULH_FAST), +.FAST_BRANCHCMP (FAST_BRANCHCMP), +.BRANCH_PREDICTOR (BRANCH_PREDICTOR), +.MTVEC_WMASK (MTVEC_WMASK), +.RESET_REGFILE (RESET_REGFILE), +.W_ADDR (W_ADDR), +.W_DATA (W_DATA) diff --git a/vendor/Hazard3/hdl/hazard3_core.v b/vendor/Hazard3/hdl/hazard3_core.v new file mode 100644 index 0000000..1809b6c --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_core.v @@ -0,0 +1,1590 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2025 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +`ifdef HAZARD3_RVFI_STANDALONE +`include "hazard3_rvfi_standalone_defs.vh" +`endif + +`default_nettype none + +module hazard3_core #( +`include "hazard3_config.vh" +, +`include "hazard3_width_const.vh" +) ( + // Global signals + input wire clk, + input wire clk_always_on, + input wire rst_n, + + // Power control signals + output wire pwrup_req, + input wire pwrup_ack, + output wire clk_en, + output reg unblock_out, + input wire unblock_in, + + `ifdef RISCV_FORMAL + `RVFI_OUTPUTS , + `endif + + // Instruction fetch port + output wire bus_aph_req_i, + output wire bus_aph_panic_i, // e.g. branch mispredict + flush + input wire bus_aph_ready_i, + input wire bus_dph_ready_i, + input wire bus_dph_err_i, + + output wire [W_ADDR-1:0] bus_haddr_i, + output wire [2:0] bus_hsize_i, + output wire bus_priv_i, + input wire [W_DATA-1:0] bus_rdata_i, + + // Load/store port + output reg bus_aph_req_d, + output wire bus_aph_excl_d, + input wire bus_aph_ready_d, + input wire bus_dph_ready_d, + input wire bus_dph_err_d, + input wire bus_dph_exokay_d, + + output reg [W_ADDR-1:0] bus_haddr_d, + output reg [2:0] bus_hsize_d, + output reg bus_priv_d, + output reg bus_hwrite_d, + output reg [W_DATA-1:0] bus_wdata_d, + input wire [W_DATA-1:0] bus_rdata_d, + + // Memory ordering signals + output wire fence_i_vld, + output wire fence_d_vld, + input wire fence_rdy, + + // Debugger run/halt control + input wire dbg_req_halt, + input wire dbg_req_halt_on_reset, + input wire dbg_req_resume, + output wire dbg_halted, + output wire dbg_running, + // Debugger access to data0 CSR + input wire [W_DATA-1:0] dbg_data0_rdata, + output wire [W_DATA-1:0] dbg_data0_wdata, + output wire dbg_data0_wen, + // Debugger instruction injection + input wire [W_DATA-1:0] dbg_instr_data, + input wire dbg_instr_data_vld, + output wire dbg_instr_data_rdy, + output wire dbg_instr_caught_exception, + output wire dbg_instr_caught_ebreak, + + // Identification CSR values + input wire [W_DATA-1:0] mhartid_val, + input wire [3:0] eco_version, + + // Level-sensitive interrupt sources + input wire [NUM_IRQS-1:0] irq, // -> mip.meip + input wire soft_irq, // -> mip.msip + input wire timer_irq // -> mip.mtip +); + +`include "hazard3_ops.vh" +`include "rv_opcodes.vh" + +wire x_stall; +wire m_stall; + +localparam HSIZE_WORD = 3'd2; +localparam HSIZE_HWORD = 3'd1; +localparam HSIZE_BYTE = 3'd0; + +localparam [4:0] REGADDR_MASK = {~|EXTENSION_E, 4'hf}; + +wire debug_mode; +assign dbg_halted = DEBUG_SUPPORT && debug_mode; +assign dbg_running = DEBUG_SUPPORT && !debug_mode; + +// ---------------------------------------------------------------------------- +// Pipe Stage F + +wire f_jump_req; +wire [W_ADDR-1:0] f_jump_target; +wire f_jump_priv; +wire f_jump_rdy; +wire f_jump_now = f_jump_req && f_jump_rdy; + +wire f_frontend_pwrdown_ok; + +// Predecoded register numbers, for register file access +wire [W_REGADDR-1:0] f_rs1_coarse; +wire [W_REGADDR-1:0] f_rs2_coarse; +wire [W_REGADDR-1:0] f_rs1_fine; +wire [W_REGADDR-1:0] f_rs2_fine; + +wire [31:0] fd_cir; +wire [31:0] fd_cir_raw; +wire [1:0] fd_cir_err; +wire fd_cir_break_any; +wire fd_cir_break_d_mode; +wire [1:0] fd_cir_predbranch; +wire [1:0] fd_cir_vld; +wire fd_cir_is_32bit; +wire fd_cir_invalid_16bit; +wire fd_cir_is_uop; +wire fd_cir_uop_nonfinal; +wire fd_cir_uop_no_pc_update; +wire fd_cir_uop_atomic; + +wire [1:0] df_cir_use; +wire df_cir_flush_behind; + +wire df_uop_stall; +wire df_uop_clear; +wire df_lspair_phase_next; + +wire x_btb_set; +wire [W_ADDR-1:0] x_btb_set_src_addr; +wire x_btb_set_src_size; +wire [W_ADDR-1:0] x_btb_set_target_addr; +wire x_btb_clear; + +wire [W_ADDR-1:0] d_btb_target_addr; + +wire [W_ADDR-1:0] f_pmp_i_addr; +wire f_pmp_i_m_mode; +wire f_pmp_i_kill; + +wire [W_ADDR-1:0] f_trigger_addr; +wire f_trigger_m_mode; +wire [1:0] f_trigger_break_any; +wire [1:0] f_trigger_break_d_mode; + +assign bus_aph_panic_i = 1'b0; + +wire f_mem_size; +assign bus_hsize_i = f_mem_size ? HSIZE_WORD : HSIZE_HWORD; + +hazard3_frontend #( +`include "hazard3_config_inst.vh" +) frontend ( + .clk (clk), + .rst_n (rst_n), + + .mem_size (f_mem_size), + .mem_addr (bus_haddr_i), + .mem_priv (bus_priv_i), + .mem_addr_vld (bus_aph_req_i), + .mem_addr_rdy (bus_aph_ready_i), + + .mem_data (bus_rdata_i), + .mem_data_err (bus_dph_err_i), + .mem_data_vld (bus_dph_ready_i), + + .jump_target (f_jump_target), + .jump_priv (f_jump_priv), + .jump_target_vld (f_jump_req), + .jump_target_rdy (f_jump_rdy), + + .btb_set (x_btb_set), + .btb_set_src_addr (x_btb_set_src_addr), + .btb_set_src_size (x_btb_set_src_size), + .btb_set_target_addr (x_btb_set_target_addr), + .btb_clear (x_btb_clear), + .btb_target_addr_out (d_btb_target_addr), + + .cir (fd_cir), + .cir_raw (fd_cir_raw), + .cir_err (fd_cir_err), + .cir_predbranch (fd_cir_predbranch), + .cir_break_any (fd_cir_break_any), + .cir_break_d_mode (fd_cir_break_d_mode), + .cir_vld (fd_cir_vld), + .cir_is_32bit (fd_cir_is_32bit), + .cir_invalid_16bit (fd_cir_invalid_16bit), + .cir_is_uop (fd_cir_is_uop), + .cir_uop_nonfinal (fd_cir_uop_nonfinal), + .cir_uop_no_pc_update (fd_cir_uop_no_pc_update), + .cir_uop_atomic (fd_cir_uop_atomic), + + .cir_use (df_cir_use), + .cir_flush_behind (df_cir_flush_behind), + .uop_stall (df_uop_stall), + .uop_clear (df_uop_clear), + .df_lspair_phase_next (df_lspair_phase_next), + + .pwrdown_ok (f_frontend_pwrdown_ok), + .delay_first_fetch (!pwrup_ack), + + .predecode_rs1_coarse (f_rs1_coarse), + .predecode_rs2_coarse (f_rs2_coarse), + .predecode_rs1_fine (f_rs1_fine), + .predecode_rs2_fine (f_rs2_fine), + + .debug_mode (debug_mode), + .dbg_instr_data (dbg_instr_data), + .dbg_instr_data_vld (dbg_instr_data_vld), + .dbg_instr_data_rdy (dbg_instr_data_rdy), + + .pmp_i_addr (f_pmp_i_addr), + .pmp_i_m_mode (f_pmp_i_m_mode), + .pmp_i_kill (f_pmp_i_kill), + + .trigger_addr (f_trigger_addr), + .trigger_m_mode (f_trigger_m_mode), + .trigger_break_any (f_trigger_break_any), + .trigger_break_d_mode (f_trigger_break_d_mode) +); + +// ---------------------------------------------------------------------------- +// Pipe Stage X (Decode Logic) + +// X-check on pieces of instruction which frontend claims are valid +`ifdef HAZARD3_X_CHECKS +always @ (posedge clk) begin + if (rst_n) begin + if (|fd_cir_vld && (^fd_cir[15:0] === 1'bx)) begin + $display("CIR LSBs are X, should be valid!"); + $finish; + end + if (fd_cir_vld[1] && (^fd_cir === 1'bX)) begin + $display("CIR contains X, should be fully valid!"); + $finish; + end + end +end +`endif + +// To X +wire d_starved; +wire [W_DATA-1:0] d_imm; +wire [W_REGADDR-1:0] d_rs1; +wire [W_REGADDR-1:0] d_rs2; +wire [W_REGADDR-1:0] d_rd; +wire [2:0] d_funct3_32b; +wire [6:0] d_funct7_32b; +wire [W_ALUSRC-1:0] d_alusrc_a; +wire [W_ALUSRC-1:0] d_alusrc_b; +wire [W_ALUOP-1:0] d_aluop; +wire [W_MEMOP-1:0] d_memop; +wire [W_MULOP-1:0] d_mulop; +wire [W_BCOND-1:0] d_branchcond; +wire [W_ADDR-1:0] d_addr_offs; +wire d_addr_is_regoffs; +wire [W_ADDR-1:0] d_pc; +wire [W_EXCEPT-1:0] d_except; +wire d_sleep_wfi; +wire d_sleep_block; +wire d_sleep_unblock; +wire d_no_pc_increment; +wire d_uninterruptible; +wire d_fence_i; +wire d_fence_d; +wire d_csr_ren; +wire d_csr_wen; +wire [1:0] d_csr_wtype; +wire d_csr_w_imm; +wire [W_ADDR-1:0] d_lspair_offset; + +wire x_jump_not_except; +wire x_mmode_execution; +wire x_trap_wfi; + +wire [W_ADDR-1:0] debug_dpc_wdata; +wire debug_dpc_wen; +wire [W_ADDR-1:0] debug_dpc_rdata; + +hazard3_decode #( +`include "hazard3_config_inst.vh" +) decode_u ( + .clk (clk), + .rst_n (rst_n), + + .fd_cir (fd_cir), + .fd_cir_err (fd_cir_err), + .fd_cir_predbranch (fd_cir_predbranch), + .fd_cir_vld (fd_cir_vld), + .fd_cir_is_32bit (fd_cir_is_32bit), + .fd_cir_invalid_16bit (fd_cir_invalid_16bit), + .fd_cir_is_uop (fd_cir_is_uop), + .fd_cir_uop_nonfinal (fd_cir_uop_nonfinal), + .fd_cir_uop_no_pc_update (fd_cir_uop_no_pc_update), + .fd_cir_uop_atomic (fd_cir_uop_atomic), + + .df_cir_use (df_cir_use), + .df_cir_flush_behind (df_cir_flush_behind), + .df_uop_stall (df_uop_stall), + .df_uop_clear (df_uop_clear), + .df_lspair_phase_next (df_lspair_phase_next), + + .d_pc (d_pc), + .x_jump_not_except (x_jump_not_except), + + .debug_mode (debug_mode), + .m_mode (x_mmode_execution), + .trap_wfi (x_trap_wfi), + + .debug_dpc_wdata (debug_dpc_wdata), + .debug_dpc_wen (debug_dpc_wen), + .debug_dpc_rdata (debug_dpc_rdata), + + .d_starved (d_starved), + .x_stall (x_stall), + .f_jump_now (f_jump_now), + .f_jump_target (f_jump_target), + .d_btb_target_addr (d_btb_target_addr), + + .d_imm (d_imm), + .d_rs1 (d_rs1), + .d_rs2 (d_rs2), + .d_rd (d_rd), + .d_funct3_32b (d_funct3_32b), + .d_funct7_32b (d_funct7_32b), + .d_alusrc_a (d_alusrc_a), + .d_alusrc_b (d_alusrc_b), + .d_aluop (d_aluop), + .d_memop (d_memop), + .d_mulop (d_mulop), + .d_csr_ren (d_csr_ren), + .d_csr_wen (d_csr_wen), + .d_csr_wtype (d_csr_wtype), + .d_csr_w_imm (d_csr_w_imm), + .d_branchcond (d_branchcond), + .d_addr_offs (d_addr_offs), + .d_addr_is_regoffs (d_addr_is_regoffs), + .d_except (d_except), + .d_sleep_wfi (d_sleep_wfi), + .d_sleep_block (d_sleep_block), + .d_sleep_unblock (d_sleep_unblock), + .d_no_pc_increment (d_no_pc_increment), + .d_uninterruptible (d_uninterruptible), + .d_lspair_offset (d_lspair_offset), + .d_fence_i (d_fence_i), + .d_fence_d (d_fence_d) +); + +// ---------------------------------------------------------------------------- +// Pipe Stage X (Execution Logic) + +// Register the write which took place to the regfile on previous cycle, and bypass. +// This is an alternative to a write -> read bypass in the regfile, +// which we can't implement whilst maintaining BRAM inference compatibility (iCE40). +reg [W_REGADDR-1:0] mw_rd; +reg [W_DATA-1:0] mw_result; + +// From register file: +wire [W_DATA-1:0] x_rdata1; +wire [W_DATA-1:0] x_rdata2; + +// Combinational regs for muxing +reg [W_DATA-1:0] x_rs1_bypass; +reg [W_DATA-1:0] x_rs2_bypass; +reg [W_DATA-1:0] x_op_a; +reg [W_DATA-1:0] x_op_b; +wire [W_DATA-1:0] x_alu_result; +wire x_alu_cmp; + +wire [W_DATA-1:0] m_trap_addr; +wire m_trap_is_irq; +wire m_trap_enter_vld; +wire m_trap_enter_soon; +wire m_trap_enter_rdy = f_jump_rdy; + +wire x_mmode_loadstore; +wire m_mmode_trap_entry; + +reg [W_REGADDR-1:0] xm_rs1; +reg [W_REGADDR-1:0] xm_rs2; +reg [W_REGADDR-1:0] xm_rd; +reg [W_DATA-1:0] xm_result; +reg [1:0] xm_addr_align; +reg [W_MEMOP-1:0] xm_memop; +reg [W_EXCEPT-1:0] xm_except; +reg xm_except_to_d_mode; +reg xm_no_pc_increment; +reg xm_sleep_wfi; +reg xm_sleep_block; +reg xm_delay_irq_entry_on_ls_stagex; + +// ---------------------------------------------------------------------------- +// Stall logic + +// IRQs squeeze in between the instructions in X and M, so in this case X +// stalls but M can continue. -> X always stalls on M trap, M *may* stall. +wire x_stall_on_trap = m_trap_enter_vld && !m_trap_enter_rdy || + m_trap_enter_soon && !m_trap_enter_vld; + +// Stall inserted to avoid illegal pipelining of exclusive accesses on the bus +// (also gives time to update local monitor on direct lr.w -> sc.w instruction +// sequences). Note we don't check for AMOs in stage M, because AMOs fully +// fence off on their own completion before passing down the pipe. + +wire d_memop_is_amo = |EXTENSION_A && d_memop == MEMOP_AMO; + +wire x_stall_on_exclusive_overlap = |EXTENSION_A && ( + (d_memop_is_amo || d_memop == MEMOP_SC_W || d_memop == MEMOP_LR_W) && + (xm_memop == MEMOP_SC_W || xm_memop == MEMOP_LR_W) +); + +// AMOs are issued completely from X. We keep X stalled, and pass bubbles into +// M. Otherwise the exception handling would be even more of a mess. Phases +// 0-3 are read/write address/data phases. Phase 4 is error, due to HRESP or +// due to low HEXOKAY response to read. + +// Also need to clear AMO if it follows an excepting instruction. Note we +// still stall on phase 3 when hready is high if hresp is also high, since we +// then proceed to phase 4 for the error response. + +reg [2:0] x_amo_phase; +wire x_stall_on_amo = |EXTENSION_A && d_memop_is_amo && !m_trap_enter_soon && ( + x_amo_phase < 3'h3 || (x_amo_phase == 3'h3 && (!bus_dph_ready_d || !bus_dph_exokay_d || bus_dph_err_d)) +); + +// Read-after-write hazard detection (e.g. load-use) + +wire m_fast_mul_result_vld; +wire m_generating_result = + (xm_memop < MEMOP_SW) || + (|EXTENSION_A && xm_memop == MEMOP_LR_W) || + (|EXTENSION_A && xm_memop == MEMOP_SC_W) || // sc.w success result is data phase + (|EXTENSION_M && m_fast_mul_result_vld); + +reg x_stall_on_raw; + +always @ (*) begin + x_stall_on_raw = 1'b0; + if (REDUCED_BYPASS) begin + x_stall_on_raw = + |xm_rd && (xm_rd == d_rs1 || xm_rd == d_rs2) || + |mw_rd && (mw_rd == d_rs1 || mw_rd == d_rs2); + end else if (m_generating_result) begin + // With the full bypass network, load-use (or fast multiply-use) is the only RAW stall + if (|xm_rd && xm_rd == d_rs1) begin + // Store addresses cannot be bypassed later, so there is no exception here. + x_stall_on_raw = 1'b1; + end else if (|xm_rd && xm_rd == d_rs2) begin + // Store data can be bypassed in M. Any other instructions must stall. + x_stall_on_raw = !(d_memop == MEMOP_SW || d_memop == MEMOP_SH || d_memop == MEMOP_SB); + end + end +end + +wire x_stall_on_fence; +wire x_stall_muldiv; +wire x_jump_req; + +assign x_stall = + m_stall || + x_stall_on_trap || + x_stall_on_exclusive_overlap || + x_stall_on_amo || + x_stall_on_raw || + x_stall_on_fence || + x_stall_muldiv || + bus_aph_req_d && !bus_aph_ready_d || + x_jump_req && !f_jump_rdy; + +wire m_sleep_stall_release; + +wire x_loadstore_pmp_fail; +wire x_trig_step_break; +wire x_trig_break = fd_cir_break_any || x_trig_step_break; +wire x_trig_break_d_mode = fd_cir_break_d_mode; + +// ---------------------------------------------------------------------------- +// Execution logic + +// ALU, operand muxes and bypass + +// Approximate regnums were predecoded in stage 1, for regfile read. +// (Approximate in the sense that they are invalid when the instruction +// doesn't *have* a register operand on that port.) These aren't usable for +// hazard checking but are fine for bypass, and make the bypass mux +// independent of stage 2 decode. + +reg [W_REGADDR-1:0] d_rs1_predecoded; +reg [W_REGADDR-1:0] d_rs2_predecoded; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + d_rs1_predecoded <= {W_REGADDR{1'b0}}; + d_rs2_predecoded <= {W_REGADDR{1'b0}}; + end else if (d_starved || !x_stall) begin + d_rs1_predecoded <= f_rs1_fine & REGADDR_MASK; + d_rs2_predecoded <= f_rs2_fine & REGADDR_MASK; + end +end + +wire [W_REGADDR-1:0] d_rs1_predecoded_nxt = REGADDR_MASK & ( + d_starved || !x_stall ? f_rs1_fine : d_rs1_predecoded +); +wire [W_REGADDR-1:0] d_rs2_predecoded_nxt = REGADDR_MASK & ( + d_starved || !x_stall ? f_rs2_fine : d_rs2_predecoded +); + +wire [W_REGADDR-1:0] xm_rd_nxt; +wire [W_REGADDR-1:0] mw_rd_nxt; + +reg [2:0] x_rs1_select_regs_xm_mw; +reg [2:0] x_rs2_select_regs_xm_mw; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + x_rs1_select_regs_xm_mw <= 3'b000; + x_rs2_select_regs_xm_mw <= 3'b000; + end else begin + x_rs1_select_regs_xm_mw <= + 5'h00 == d_rs1_predecoded_nxt ? 3'b000 : + xm_rd_nxt == d_rs1_predecoded_nxt ? 3'b010 : + mw_rd_nxt == d_rs1_predecoded_nxt ? 3'b001 : 3'b100; + x_rs2_select_regs_xm_mw <= + 5'h00 == d_rs2_predecoded_nxt ? 3'b000 : + xm_rd_nxt == d_rs2_predecoded_nxt ? 3'b010 : + mw_rd_nxt == d_rs2_predecoded_nxt ? 3'b001 : 3'b100; + end +end + +`ifdef HAZARD3_ASSERTIONS +always @ (posedge clk) if (rst_n && !x_stall) begin + // If stage 2 sees a reg operand, it must have been correctly predecoded too. + if (|d_rs1) + assert(d_rs1_predecoded == d_rs1); + if (|d_rs2) + assert(d_rs2_predecoded == d_rs2); + // If no reg was predecoded, stage 2 decode must agree there is no reg operand. + if (~|d_rs1_predecoded) + assert(~|d_rs1); + if (~|d_rs2_predecoded) + assert(~|d_rs2); + // Make sure bypass mask updates to track older in-flight instructions + if (|d_rs1 && d_rs1 == xm_rd) assert(x_rs1_select_regs_xm_mw[1]); + else if (|d_rs1 && d_rs1 == mw_rd) assert(x_rs1_select_regs_xm_mw[0]); + else if (|d_rs1 ) assert(x_rs1_select_regs_xm_mw[2]); + else if (~|d_rs1_predecoded ) assert(~|x_rs1_select_regs_xm_mw); + if (|d_rs2 && d_rs2 == xm_rd) assert(x_rs2_select_regs_xm_mw[1]); + else if (|d_rs2 && d_rs2 == mw_rd) assert(x_rs2_select_regs_xm_mw[0]); + else if (|d_rs2 ) assert(x_rs2_select_regs_xm_mw[2]); + else if (~|d_rs2_predecoded ) assert(~|x_rs2_select_regs_xm_mw); +end +`endif + + +always @ (*) begin + + x_rs1_bypass = + (x_rdata1 & {W_DATA{x_rs1_select_regs_xm_mw[2]}}) | + (xm_result & {W_DATA{x_rs1_select_regs_xm_mw[1]}}) | + (mw_result & {W_DATA{x_rs1_select_regs_xm_mw[0]}}); + + x_rs2_bypass = + (x_rdata2 & {W_DATA{x_rs2_select_regs_xm_mw[2]}}) | + (xm_result & {W_DATA{x_rs2_select_regs_xm_mw[1]}}) | + (mw_result & {W_DATA{x_rs2_select_regs_xm_mw[0]}}); + + // AMO captures rdata into mw_result at end of read data phase, so we can + // feed back through the ALU. + if (|EXTENSION_A && x_amo_phase == 3'h2) + x_op_a = mw_result; + else if (d_alusrc_a) + x_op_a = d_pc; + else + x_op_a = x_rs1_bypass; + + if (d_alusrc_b) + x_op_b = d_imm; + else + x_op_b = x_rs2_bypass; +end + +hazard3_alu #( +`include "hazard3_config_inst.vh" +) alu ( + .aluop (d_aluop), + .funct3_32b (d_funct3_32b), + .funct7_32b (d_funct7_32b), + .op_a (x_op_a), + .op_b (x_op_b), + .result (x_alu_result), + .cmp (x_alu_cmp) +); + +// Load/store bus request + +wire x_unaligned_addr = d_memop != MEMOP_NONE && ( + bus_hsize_d == HSIZE_WORD && |bus_haddr_d[1:0] || + bus_hsize_d == HSIZE_HWORD && bus_haddr_d[0] +); + +reg mw_local_exclusive_reserved; + +wire x_memop_vld = d_memop != MEMOP_NONE && !( + |EXTENSION_A && d_memop == MEMOP_SC_W && !mw_local_exclusive_reserved || + |EXTENSION_A && d_memop_is_amo && x_amo_phase != 3'h0 && x_amo_phase != 3'h2 +); + +wire x_memop_write = + d_memop == MEMOP_SW || + d_memop == MEMOP_SH || + d_memop == MEMOP_SB || + (|EXTENSION_A && d_memop == MEMOP_SC_W) || + (|EXTENSION_A && d_memop_is_amo && x_amo_phase == 3'h2); + +// Always query the global monitor, except for store-conditional suppressed by local monitor. +assign bus_aph_excl_d = |EXTENSION_A && ( + d_memop == MEMOP_LR_W || + d_memop == MEMOP_SC_W || + d_memop_is_amo +); + +// AMO stalls the pipe, then generates two bus transfers per 4-cycle +// iteration, unless it bails out due to a bus fault or failed load +// reservation. +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + x_amo_phase <= 3'h0; + end else if (|EXTENSION_A && d_memop_is_amo && ( + bus_aph_ready_d || bus_dph_ready_d || + m_trap_enter_vld || x_unaligned_addr || x_loadstore_pmp_fail || + x_amo_phase == 3'h4 + )) begin + if (m_trap_enter_vld) begin + // Bail out, squash the in-progress AMO. + x_amo_phase <= 3'h0; + end else if (x_stall_on_raw || x_stall_on_exclusive_overlap || m_trap_enter_soon) begin + // First address phase stalled due to address dependency on + // previous load/mul/etc. Shouldn't be possible in later phases. + x_amo_phase <= 3'h0; + end else if (x_amo_phase == 3'h4) begin + // Clear fault phase once it goes through to stage 3 and excepts + if (!x_stall) + x_amo_phase <= 3'h0; + end else if (x_unaligned_addr || x_loadstore_pmp_fail) begin + x_amo_phase <= 3'h4; + end else if (x_amo_phase == 3'h1 && !bus_dph_exokay_d) begin + // Load reserve fail indicates the memory region does not support + // exclusives, so we will never succeed at store. Exception. + x_amo_phase <= 3'h4; + end else if ((x_amo_phase == 3'h1 || x_amo_phase == 3'h3) && bus_dph_err_d) begin + // Bus fault. Exception. + x_amo_phase <= 3'h4; + end else if (x_amo_phase == 3'h3) begin + // Either we're done, or the write failed. Either way, back to the start. + x_amo_phase <= 3'h0; + end else begin + // Default progression: read addr -> read data -> write addr -> write data + x_amo_phase <= x_amo_phase + 3'h1; + end + end +end + +`ifdef HAZARD3_ASSERTIONS +// Assertions for above block, extracted to avoid making assertions +// reset-sensitive (limitation of yosys-smtbmc) +always @ (posedge clk) if (rst_n) begin + if (|EXTENSION_A && d_memop_is_amo && ( + bus_aph_ready_d || bus_dph_ready_d || + m_trap_enter_vld || x_unaligned_addr || + x_amo_phase == 3'h4 + )) begin + if (m_trap_enter_vld) begin + // Should only happen during an address phase, *or* the fault phase. + assert(x_amo_phase == 3'h0 || x_amo_phase == 3'h2 || x_amo_phase == 3'h4); + // The fault phase only holds when we have a misaligned AMO directly behind + // a regular memory access that subsequently excepts, and the AMO has gone + // straight to fault phase due to misalignment. + if (x_amo_phase == 3'h4) + assert(x_unaligned_addr); + end else if (x_stall_on_raw || x_stall_on_exclusive_overlap || m_trap_enter_soon) begin + assert(x_amo_phase == 3'h0); + end else if (x_amo_phase == 3'h4) begin + // This should only happen when we are stalled on an older load/store etc + assert(!(x_stall && !m_stall)); + end + end +end +`endif + +`ifdef HAZARD3_ASSERTIONS +// General AMO design assertions +reg prop_dph_is_excl = 1'b0; +always @ (posedge clk) if (rst_n) begin + if (bus_aph_ready_d && bus_aph_req_d) begin + prop_dph_is_excl <= bus_aph_excl_d; + end else if (bus_dph_ready_d) begin + // It's possible for the dphase to end without the aphase ending due + // to e.g. SBA arbitration + prop_dph_is_excl <= 1'b0; + end + // Other states should be unreachable + assert(x_amo_phase <= 3'h4); + // First state should be 0 -- don't want anything carried from one AMO to the next. + if (x_stall_on_amo && !$past(x_stall_on_amo)) + assert(x_amo_phase == 3'h0); + // Should be in resting state between AMOs + if (!d_memop_is_amo) + assert(x_amo_phase == 3'h0); + // Error phase should have no stage 2 blockers, so it can pass to stage 3 to + // raise exception entry. It's ok to block behind a younger instruction, but.. + if (x_amo_phase == 3'h4) + assert(!x_stall || m_stall); + // ..the only way to reach AMO error phase without stage 3 clearing out should + // be an unaligned AMO address, which goes straight to error phase. + if (x_amo_phase == 3'h4 && m_stall) + assert(x_unaligned_addr); + // Should be impossible to get an unaligned address in the write address + // phase, since it would be picked up in the read address phase + if (x_amo_phase == 3'h2) + assert(!x_unaligned_addr); + // Error phase is either due to a bus response, or a misaligned address. + // Neither of these are write-address-phase. + if (x_amo_phase == 3'h4) + assert($past(x_amo_phase) != 3'h2); + // Make sure M is unstalled for passing store data through in phase 2 + if (x_amo_phase == 3'h2) + assert(!m_stall); + // Make sure we don't pipeline AMO aphases with other exclusive dphases + if (d_memop_is_amo && bus_aph_req_d) + assert(!prop_dph_is_excl); + // Only phases 0 (read aphase) and 2 (write aphase) assert new bus transfers + if (d_memop_is_amo && bus_aph_req_d) + assert(x_amo_phase == 3'h0 || x_amo_phase == 3'h2); +end +`endif + +// This adder is used for both branch targets and load/store addresses. +// Supporting all branch types already requires rs1 + I-fmt, and pc + B-fmt. +// B-fmt are almost identical to S-fmt, so we rs1 + S-fmt is almost free. + +wire [W_ADDR-1:0] x_addr_sum = + (d_lspair_offset & {W_ADDR{|EXTENSION_ZILSD}}) + + (d_addr_is_regoffs ? x_rs1_bypass : d_pc) + + d_addr_offs; + +always @ (*) begin + // Need to be careful not to use anything hready-sourced to gate htrans! + bus_haddr_d = x_addr_sum; + bus_hwrite_d = x_memop_write; + bus_priv_d = x_mmode_loadstore; + case (d_memop) + MEMOP_LW: bus_hsize_d = HSIZE_WORD; + MEMOP_SW: bus_hsize_d = HSIZE_WORD; + MEMOP_LH: bus_hsize_d = HSIZE_HWORD; + MEMOP_LHU: bus_hsize_d = HSIZE_HWORD; + MEMOP_SH: bus_hsize_d = HSIZE_HWORD; + MEMOP_LB: bus_hsize_d = HSIZE_BYTE; + MEMOP_LBU: bus_hsize_d = HSIZE_BYTE; + MEMOP_SB: bus_hsize_d = HSIZE_BYTE; + default: bus_hsize_d = HSIZE_WORD; + endcase + bus_aph_req_d = x_memop_vld && !( + x_stall_on_raw || + x_stall_on_exclusive_overlap || + x_loadstore_pmp_fail || + x_trig_break || + x_unaligned_addr || + m_trap_enter_soon || + ((xm_sleep_wfi || xm_sleep_block) && !m_sleep_stall_release) + ); +end + +// Fences are not issued until relevant buses are quiet. +wire m_dphase_in_flight; +assign x_stall_on_fence = + (d_fence_i && !(fence_rdy && !m_dphase_in_flight && f_frontend_pwrdown_ok)) || + (d_fence_d && !(fence_rdy && !m_dphase_in_flight)); + +wire x_fence_mask = + x_trig_break || + m_dphase_in_flight || + m_trap_enter_soon || + ((xm_sleep_wfi || xm_sleep_block) && !m_sleep_stall_release); + +assign fence_i_vld = d_fence_i && !x_fence_mask && f_frontend_pwrdown_ok; +assign fence_d_vld = d_fence_d && !x_fence_mask; + +// Multiply/divide + +wire [W_DATA-1:0] x_muldiv_result; +wire [W_DATA-1:0] m_fast_mul_result; + +wire x_use_fast_mul = d_aluop == ALUOP_MULDIV && ( + (MUL_FAST && d_mulop == M_OP_MUL ) || + (MULH_FAST && d_mulop == M_OP_MULH ) || + (MULH_FAST && d_mulop == M_OP_MULHU ) || + (MULH_FAST && d_mulop == M_OP_MULHSU) +); + +generate +if (EXTENSION_M) begin: has_muldiv + wire x_muldiv_op_vld; + wire x_muldiv_op_rdy; + wire x_muldiv_result_vld; + wire [W_DATA-1:0] x_muldiv_result_h; + wire [W_DATA-1:0] x_muldiv_result_l; + + reg x_muldiv_posted; + always @ (posedge clk or negedge rst_n) + if (!rst_n) + x_muldiv_posted <= 1'b0; + else + x_muldiv_posted <= (x_muldiv_posted || (x_muldiv_op_vld && x_muldiv_op_rdy)) && x_stall; + + wire x_muldiv_kill = m_trap_enter_soon; + + assign x_muldiv_op_vld = (d_aluop == ALUOP_MULDIV && !x_use_fast_mul) + && !(x_muldiv_posted || x_stall_on_raw || x_muldiv_kill); + + hazard3_muldiv_seq #( + `include "hazard3_config_inst.vh" + ) muldiv ( + .clk (clk), + .rst_n (rst_n), + .op (d_mulop), + .op_vld (x_muldiv_op_vld), + .op_rdy (x_muldiv_op_rdy), + .op_kill (x_muldiv_kill), + .op_a (x_rs1_bypass), + .op_b (x_rs2_bypass), + + .result_h (x_muldiv_result_h), + .result_l (x_muldiv_result_l), + .result_vld (x_muldiv_result_vld) + ); + + wire x_muldiv_result_is_high = + d_mulop == M_OP_MULH || + d_mulop == M_OP_MULHSU || + d_mulop == M_OP_MULHU || + d_mulop == M_OP_REM || + d_mulop == M_OP_REMU; + assign x_muldiv_result = x_muldiv_result_is_high ? x_muldiv_result_h : x_muldiv_result_l; + assign x_stall_muldiv = x_muldiv_op_vld || !x_muldiv_result_vld; + + if (MUL_FAST) begin: has_fast_mul + + // If MUL_FASTER is set, the multiplier produces its result + // combinatorially, so we never have to post a mul result to stage 3. + wire x_issue_fast_mul = x_use_fast_mul && |d_rd && !x_stall && !MUL_FASTER; + + hazard3_mul_fast #( + `include "hazard3_config_inst.vh" + ) mul_fast ( + .clk (clk), + .rst_n (rst_n), + + .op_vld (x_issue_fast_mul), + .op (d_mulop), + .op_a (x_rs1_bypass), + .op_b (x_rs2_bypass), + + .result (m_fast_mul_result), + .result_vld (m_fast_mul_result_vld) + ); + + end else begin: no_fast_mul + + assign m_fast_mul_result = {W_DATA{1'b0}}; + assign m_fast_mul_result_vld = 1'b0; + + end + +`ifdef HAZARD3_ASSERTIONS + always @ (posedge clk) if (d_aluop != ALUOP_MULDIV) assert(!x_stall_muldiv); +`endif + +end else begin: no_muldiv + + assign x_muldiv_result = {W_DATA{1'b0}}; + assign m_fast_mul_result = {W_DATA{1'b0}}; + assign m_fast_mul_result_vld = 1'b0; + assign x_stall_muldiv = 1'b0; + +end +endgenerate + +// Branch handling + +// For JALR, the LSB of the result must be cleared by hardware +wire [W_ADDR-1:0] x_jump_target = x_addr_sum & ~32'd1; +wire x_jump_misaligned = ~|EXTENSION_C && x_addr_sum[1]; +wire x_branch_cmp_noinvert; + +wire x_branch_was_predicted = |BRANCH_PREDICTOR && fd_cir_predbranch[0]; +wire x_branch_cmp = x_branch_cmp_noinvert ^ x_branch_was_predicted; + +generate +if (~|FAST_BRANCHCMP) begin: alu_branchcmp + + assign x_branch_cmp_noinvert = x_alu_cmp; + +end else begin: fast_branchcmp + + hazard3_branchcmp #( + `include "hazard3_config_inst.vh" + ) branchcmp_u ( + .cir (fd_cir), + .op_a (x_rs1_bypass), + .op_b (x_rs2_bypass), + .cmp (x_branch_cmp_noinvert) + ); + +end +endgenerate + +// Be careful not to take branches whose comparisons depend on a load result +wire x_jump_req_unchecked = !x_stall_on_raw && ( + d_branchcond == BCOND_ALWAYS || + d_branchcond == BCOND_ZERO && !x_branch_cmp || + d_branchcond == BCOND_NZERO && x_branch_cmp +); + +assign x_jump_req = x_jump_req_unchecked && !x_jump_misaligned && !x_trig_break; + +assign x_btb_set = |BRANCH_PREDICTOR && ( + x_jump_req_unchecked && d_addr_offs[W_ADDR - 1] && !x_branch_was_predicted && + (d_branchcond == BCOND_NZERO || d_branchcond == BCOND_ZERO) +); + +assign x_btb_clear = d_fence_i || (m_trap_enter_vld && m_trap_enter_rdy) || (|BRANCH_PREDICTOR && ( + x_branch_was_predicted && x_jump_req_unchecked +)); + +assign x_btb_set_src_addr = d_pc; +assign x_btb_set_target_addr = x_jump_target; +assign x_btb_set_src_size = fd_cir_is_32bit; + +// Memory protection + +wire [11:0] x_pmp_cfg_addr; +wire x_pmp_cfg_wen; +wire [W_DATA-1:0] x_pmp_cfg_wdata; +wire [W_DATA-1:0] x_pmp_cfg_rdata; + +generate +if (PMP_REGIONS > 0) begin: have_pmp + + wire x_loadstore_pmp_badperm; + assign x_loadstore_pmp_fail = x_loadstore_pmp_badperm && x_memop_vld && !debug_mode; + + // R and W are enforced at the point the load/store address is generated, + // in time to mask the address-phase request. X is enforced by checking + // fetch addresses during fetch data phase (stage F) and promoting PMP + // fails to bus faults. + + hazard3_pmp #( + `include "hazard3_config_inst.vh" + ) pmp ( + .clk (clk), + .rst_n (rst_n), + + .cfg_addr (x_pmp_cfg_addr), + .cfg_wen (x_pmp_cfg_wen), + .cfg_wdata (x_pmp_cfg_wdata), + .cfg_rdata (x_pmp_cfg_rdata), + + .i_addr (f_pmp_i_addr), + .i_m_mode (f_pmp_i_m_mode), + .i_kill (f_pmp_i_kill), + + .d_addr (bus_haddr_d), + .d_addr_addend_rs1 (x_rs1_bypass), + .d_addr_addend_imm (d_addr_offs), + .d_addr_addend_lspair_offs (d_lspair_offset), + .d_m_mode (x_mmode_loadstore), + .d_write (bus_hwrite_d), + .d_kill (x_loadstore_pmp_badperm) + ); + +end else begin: no_pmp + + assign x_pmp_cfg_rdata = 32'd0; + assign x_loadstore_pmp_fail = 1'b0; + assign f_pmp_i_kill = 1'b0; + +end +endgenerate + +// Triggers + +wire [11:0] x_trig_cfg_addr; +wire x_trig_cfg_wen; +wire [W_DATA-1:0] x_trig_cfg_wdata; +wire [W_DATA-1:0] x_trig_cfg_rdata; +wire x_trig_m_en; + +wire m_trig_event_interrupt; +wire m_trig_event_exception; +wire [3:0] m_trig_event_trap_cause; + +wire x_instr_ret; + +generate +if (DEBUG_SUPPORT != 0) begin: have_triggers + + // Connection of .fetch_d_mode is from the wrong stage: safe because we + // enter/exit debug mode via exceptions, which flush the frontend. + + hazard3_triggers #( + `include "hazard3_config_inst.vh" + ) triggers ( + .clk (clk), + .rst_n (rst_n), + + .cfg_addr (x_trig_cfg_addr), + .cfg_wen (x_trig_cfg_wen), + .cfg_wdata (x_trig_cfg_wdata), + .cfg_rdata (x_trig_cfg_rdata), + .trig_m_en (x_trig_m_en), + + .fetch_addr (f_trigger_addr), + .fetch_m_mode (f_trigger_m_mode), + .fetch_d_mode (debug_mode), + + .event_instr_ret (x_instr_ret), + + .event_interrupt (m_trig_event_interrupt), + .event_exception (m_trig_event_exception), + .event_trap_cause (m_trig_event_trap_cause), + .event_trap_enter (m_trap_enter_vld && m_trap_enter_rdy), + + .break_any (f_trigger_break_any), + .break_d_mode (f_trigger_break_d_mode), + + .break_m_step (x_trig_step_break), + + .x_d_mode (debug_mode), + .x_m_mode (x_mmode_execution) + ); + +end else begin: no_triggers + + assign x_trig_cfg_rdata = {W_DATA{1'b0}}; + assign f_trigger_break_any = 2'b00; + assign f_trigger_break_d_mode = 2'b00; + assign x_trig_step_break = 1'b0; + +end +endgenerate + +// CSRs and Trap Handling + +// Use opcode bits directly because d_rs1 is masked when RVE is enabled: +wire [W_DATA-1:0] x_csr_wdata = d_csr_w_imm ? + {{W_DATA-5{1'b0}}, fd_cir[RV_RS1_LSB +: 5]} : x_rs1_bypass; + +wire [W_DATA-1:0] x_csr_rdata; +wire x_csr_illegal_access; +wire x_csr_write_is_fetch_ordered; + +// "Previous" refers to next-most-recent instruction to be in D/X, i.e. the +// most recent instruction to reach stage M (which may or may not still be in M). +reg prev_instr_was_32_bit; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + xm_delay_irq_entry_on_ls_stagex <= 1'b0; + prev_instr_was_32_bit <= 1'b0; + end else begin + // Must hold off IRQ if we are in the second cycle of an address phase or + // later, since at that point the load/store can't be revoked. The IRQ is + // taken once this load/store moves to the next stage: if another load/store + // is chasing down the pipeline then this is immediately suppressed by the + // IRQ entry, before its address phase can begin. + + // Also hold off on AMOs, unless the AMO is transitioning to an address + // phase or completing. ("completing" excludes transitions to error phase.) + + xm_delay_irq_entry_on_ls_stagex <= bus_aph_req_d && !bus_aph_ready_d || + d_memop_is_amo && !( + x_amo_phase == 3'h3 && bus_dph_ready_d && !bus_dph_err_d || + // Read reservation failure failure also generates error + x_amo_phase == 3'h1 && bus_dph_ready_d && !bus_dph_err_d && bus_dph_exokay_d + ) || ( + (fence_i_vld || fence_d_vld) && !fence_rdy + ); + + if (!x_stall) + prev_instr_was_32_bit <= df_cir_use == 2'd2; + end +end + +wire [W_ADDR-1:0] m_exception_return_addr; + +wire [W_EXCEPT-1:0] x_except = + x_trig_break ? EXCEPT_EBREAK : + x_jump_req_unchecked && x_jump_misaligned ? EXCEPT_INSTR_MISALIGN : + x_csr_illegal_access ? EXCEPT_INSTR_ILLEGAL : + |EXTENSION_A && x_unaligned_addr && d_memop_is_amo ? EXCEPT_STORE_ALIGN : + |EXTENSION_A && x_amo_phase == 3'h4 && x_unaligned_addr ? EXCEPT_STORE_ALIGN : + |EXTENSION_A && x_amo_phase == 3'h4 ? EXCEPT_STORE_FAULT : + x_unaligned_addr && x_memop_write ? EXCEPT_STORE_ALIGN : + x_unaligned_addr && !x_memop_write ? EXCEPT_LOAD_ALIGN : + x_loadstore_pmp_fail && (d_memop_is_amo || bus_hwrite_d) ? EXCEPT_STORE_FAULT : + x_loadstore_pmp_fail ? EXCEPT_LOAD_FAULT : + x_csr_write_is_fetch_ordered ? EXCEPT_REFETCH : d_except; + +// If an instruction causes an exceptional condition we do not consider it to have retired. +wire x_except_counts_as_retire = + x_except == EXCEPT_EBREAK || + x_except == EXCEPT_REFETCH || + x_except == EXCEPT_MRET || + x_except == EXCEPT_ECALL_M || + x_except == EXCEPT_ECALL_U; + +assign x_instr_ret = |df_cir_use && (x_except == EXCEPT_NONE || x_except_counts_as_retire); + +assign m_dphase_in_flight = xm_memop != MEMOP_NONE && xm_memop != MEMOP_AMO; + +// Need to delay IRQ entry on sleep exit because, for deep sleep states, we +// can't access the bus until the power handshake has completed. +wire m_delay_irq_entry = xm_delay_irq_entry_on_ls_stagex || + ((xm_sleep_wfi || xm_sleep_block) && !m_sleep_stall_release); + +wire m_pwr_allow_clkgate; +wire m_pwr_allow_power_down; +wire m_pwr_allow_sleep_on_block; +wire m_wfi_wakeup_req; +wire m_clear_excl_on_trap_exit; + +hazard3_csr #( + .XLEN (W_DATA), +`include "hazard3_config_inst.vh" +) csr_u ( + .clk (clk), + .clk_always_on (clk_always_on), + .rst_n (rst_n), + + // Debugger signalling + .debug_mode (debug_mode), + .dbg_req_halt (dbg_req_halt), + .dbg_req_halt_on_reset (dbg_req_halt_on_reset), + .dbg_req_resume (dbg_req_resume), + + .dbg_instr_caught_exception (dbg_instr_caught_exception), + .dbg_instr_caught_ebreak (dbg_instr_caught_ebreak), + + .dbg_data0_rdata (dbg_data0_rdata), + .dbg_data0_wdata (dbg_data0_wdata), + .dbg_data0_wen (dbg_data0_wen), + + .debug_dpc_wdata (debug_dpc_wdata), + .debug_dpc_wen (debug_dpc_wen), + .debug_dpc_rdata (debug_dpc_rdata), + + // CSR access port + // *en_soon are early access strobes which are not a function of bus stall. + // Can generate access faults (hence traps), but do not actually perform access. + .addr (fd_cir[31:20]), // Always I-type immediate + .wdata (x_csr_wdata), + .wen_raw (d_csr_wen), + .wen_soon (d_csr_wen && !m_trap_enter_soon), + .wen (d_csr_wen && !m_trap_enter_soon && !x_stall), + .wtype (d_csr_wtype), + .rdata (x_csr_rdata), + .ren_soon (d_csr_ren && !m_trap_enter_soon), + .ren (d_csr_ren && !m_trap_enter_soon && !x_stall), + .illegal (x_csr_illegal_access), + .write_is_fetch_ordered (x_csr_write_is_fetch_ordered), + + // Trap signalling + .trap_addr (m_trap_addr), + .trap_is_irq (m_trap_is_irq), + .trap_enter_soon (m_trap_enter_soon), + .trap_enter_vld (m_trap_enter_vld), + .trap_enter_rdy (m_trap_enter_rdy), + .loadstore_dphase_pending (m_dphase_in_flight), + .delay_irq_entry (m_delay_irq_entry || d_uninterruptible), + .mepc_in (m_exception_return_addr), + + .pwr_allow_clkgate (m_pwr_allow_clkgate), + .pwr_allow_power_down (m_pwr_allow_power_down), + .pwr_allow_sleep_on_block (m_pwr_allow_sleep_on_block), + .pwr_wfi_wakeup_req (m_wfi_wakeup_req), + + .m_mode_execution (x_mmode_execution), + .m_mode_loadstore (x_mmode_loadstore), + .m_mode_trap_entry (m_mmode_trap_entry), + + // IRQ and exception requests + .irq (irq), + .irq_software (soft_irq), + .irq_timer (timer_irq), + .except (xm_except), + .except_to_d_mode (xm_except_to_d_mode), + + .pmp_cfg_addr (x_pmp_cfg_addr), + .pmp_cfg_wen (x_pmp_cfg_wen), + .pmp_cfg_wdata (x_pmp_cfg_wdata), + .pmp_cfg_rdata (x_pmp_cfg_rdata), + + .trig_cfg_addr (x_trig_cfg_addr), + .trig_cfg_wen (x_trig_cfg_wen), + .trig_cfg_wdata (x_trig_cfg_wdata), + .trig_cfg_rdata (x_trig_cfg_rdata), + .trig_m_en (x_trig_m_en), + + .trig_event_interrupt (m_trig_event_interrupt), + .trig_event_exception (m_trig_event_exception), + .trig_event_trap_cause (m_trig_event_trap_cause), + + // Other CSR-specific signalling + .clear_excl_on_trap_exit (m_clear_excl_on_trap_exit), + .trap_wfi (x_trap_wfi), + .instr_ret (x_instr_ret), + .mhartid_val (mhartid_val), + .eco_version (eco_version) +); + +// Pipe register + +assign xm_rd_nxt = REGADDR_MASK & ( + m_stall ? xm_rd : + x_stall || d_starved || m_trap_enter_soon ? 5'h0 : d_rd +); + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + xm_memop <= MEMOP_NONE; + xm_except <= EXCEPT_NONE; + xm_except_to_d_mode <= 1'b0; + xm_sleep_wfi <= 1'b0; + xm_sleep_block <= 1'b0; + unblock_out <= 1'b0; + xm_rs1 <= {W_REGADDR{1'b0}}; + xm_rs2 <= {W_REGADDR{1'b0}}; + xm_rd <= {W_REGADDR{1'b0}}; + xm_no_pc_increment <= 1'b0; + end else begin + unblock_out <= 1'b0; + if (!m_stall) begin + xm_rs1 <= REGADDR_MASK & d_rs1; + xm_rs2 <= REGADDR_MASK & d_rs2; + xm_rd <= REGADDR_MASK & d_rd; + // PC increment is suppressed non-final micro-ops, only needed for Zcmp: + xm_no_pc_increment <= d_no_pc_increment && |EXTENSION_ZCMP; + // If some X-sourced exception has squashed the address phase, need to squash the data phase too. + xm_memop <= x_except != EXCEPT_NONE ? MEMOP_NONE : d_memop; + xm_except <= x_except; + xm_except_to_d_mode <= x_trig_break_d_mode; + xm_sleep_wfi <= x_except == EXCEPT_NONE && d_sleep_wfi; + xm_sleep_block <= x_except == EXCEPT_NONE && d_sleep_block; + unblock_out <= x_except == EXCEPT_NONE && d_sleep_unblock; + // Note the d_starved term is required because it is possible + // (e.g. PMP X permission fail) to except when the frontend is + // starved, and we get a bad mepc if we let this jump ahead: + if (x_stall || d_starved || m_trap_enter_soon) begin + // Insert bubble + xm_rd <= {W_REGADDR{1'b0}}; + xm_memop <= MEMOP_NONE; + xm_except <= EXCEPT_NONE; + xm_except_to_d_mode <= 1'b0; + xm_sleep_wfi <= 1'b0; + xm_sleep_block <= 1'b0; + unblock_out <= 1'b0; + end + end else if (bus_dph_err_d) begin + // First phase of 2-phase AHB5 error response. Pass the exception along on + // this cycle, and on the next cycle the trap entry will be asserted, + // suppressing any load/store that may currently be in stage X. + xm_except <= + |EXTENSION_A && xm_memop == MEMOP_LR_W ? EXCEPT_LOAD_FAULT : + xm_memop <= MEMOP_LBU ? EXCEPT_LOAD_FAULT : EXCEPT_STORE_FAULT; + end + end +end + +`ifdef HAZARD3_ASSERTIONS +// Properties for previous block +always @ (posedge clk) if (rst_n) begin + if (!m_stall) begin + // Assuming downstream bus is compliant, err && !stalled is the + // last cycle of an error response, in which case this must have + // been previously captured as an exception. + if (bus_dph_err_d && x_amo_phase == 3'h0) begin + assert(xm_except == EXCEPT_LOAD_FAULT || xm_except == EXCEPT_STORE_FAULT); + end + end else if (bus_dph_err_d) begin + // There should be a bus transfer in stage 3 corresponding to this bus error + assert(xm_memop != MEMOP_NONE); + // We should capture this error exactly once, and if there were + // any other source of exception then the load/store should not + // have happened in the first place. Only exception is when the + // entry of the load/store trap is stalled on the frontend. + assert(xm_except == EXCEPT_NONE || (!m_trap_enter_rdy && ( + xm_except == EXCEPT_LOAD_FAULT || xm_except == EXCEPT_STORE_FAULT + ))); + // Make sure we don't have to clear any of the other stage 3 flags + assert(!xm_except_to_d_mode); + assert(!xm_sleep_wfi); + assert(!xm_sleep_block); + assert(!unblock_out); + end + // Some of the exception->ls paths are cut as they are supposed to be + // impossible -- make sure there is no way to have a load/store that + // is not squashed. (This includes debug entry!) + if (m_trap_enter_vld) begin + assert(!bus_aph_req_d); + end +end +`endif + +`ifdef HAZARD3_ASSERTIONS +always @ (posedge clk) if (rst_n) begin + // D bus errors must always squash younger load/stores + if ($past(bus_dph_err_d && !bus_dph_ready_d)) + assert(!bus_aph_req_d); + // Nonsensical to have an active stage 3 memop aligned with a + // stage-2-generated exception -- ought to be squashed. The outlier is an + // M-generated exception, i.e. a dataphase bus fault. Also AMOs are different. + if (xm_except != EXCEPT_NONE && !(bus_dph_err_d || $past(!m_trap_enter_rdy))) + assert(xm_memop == MEMOP_NONE || xm_memop == MEMOP_AMO); + assert(xm_rd == $past(xm_rd_nxt)); +end +`endif + +// Datapath flops +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + xm_result <= {W_DATA{1'b0}}; + xm_addr_align <= 2'b00; + end else if (!m_stall && !(|EXTENSION_A && x_amo_phase == 3'h3 && !bus_dph_ready_d)) begin + // AMOs need special attention (of course): + // - Steer captured read phase data in mw_result back through xm_result at end of AMO + // - Make sure xm_result (store data) doesn't transition during stalled write dphase + xm_result <= + d_csr_ren ? x_csr_rdata : + |EXTENSION_A && x_amo_phase == 3'h3 ? mw_result : + |MUL_FASTER && x_use_fast_mul ? m_fast_mul_result : + |EXTENSION_M && d_aluop == ALUOP_MULDIV ? x_muldiv_result : + x_alu_result; + xm_addr_align <= x_addr_sum[1:0]; + end +end + +// ---------------------------------------------------------------------------- +// Pipe Stage M + +reg [W_DATA-1:0] m_rdata_pick_sext; +reg [W_DATA-1:0] m_wdata; +reg [W_DATA-1:0] m_result; + +assign f_jump_req = x_jump_req || m_trap_enter_vld; +assign f_jump_target = m_trap_enter_vld ? m_trap_addr : x_jump_target; +assign f_jump_priv = m_trap_enter_vld ? m_mmode_trap_entry : x_mmode_execution; +assign x_jump_not_except = !m_trap_enter_vld; + +// Stalls and sleep control + +// EXCEPT_NONE clause is needed in the following sequence: +// - Cycle 0: hresp asserted, hready low. We set the exception to squash behind us. Bus stall high. +// - Cycle 1: hready high. For whatever reason, the frontend can't accept the trap address this cycle. +// - Cycle 2: Our dataphase has ended, so bus_dph_ready_d doesn't pulse again. m_bus_stall stuck high. +wire m_bus_stall = m_dphase_in_flight && !bus_dph_ready_d && xm_except == EXCEPT_NONE && !( + |EXTENSION_A && xm_memop == MEMOP_SC_W && !mw_local_exclusive_reserved +); + +assign m_stall = m_bus_stall || + (m_trap_enter_vld && !m_trap_enter_rdy && !m_trap_is_irq) || + ((xm_sleep_wfi || xm_sleep_block) && !m_sleep_stall_release); + +// Exception is taken against the instruction currently in M, so walk the PC +// back. IRQ is taken "in between" the instruction in M and the instruction +// in X, so set return to X program counter. Note that, if taking an +// exception, we know that the previous instruction to be in X (now in M) +// was *not* a taken branch, which is why we can just walk back the PC. +assign m_exception_return_addr = d_pc - ( + m_trap_is_irq ? 32'd0 : + xm_no_pc_increment ? 32'd0 : + prev_instr_was_32_bit ? 32'd4 : 32'd2 +); + +hazard3_power_ctrl power_ctrl ( + .clk_always_on (clk_always_on), + .rst_n (rst_n), + + .pwrup_req (pwrup_req), + .pwrup_ack (pwrup_ack), + .clk_en (clk_en), + + .allow_clkgate (m_pwr_allow_clkgate), + .allow_power_down (m_pwr_allow_power_down), + .allow_sleep_on_block (m_pwr_allow_sleep_on_block), + + .frontend_pwrdown_ok (f_frontend_pwrdown_ok), + + .sleeping_on_wfi (xm_sleep_wfi), + .wfi_wakeup_req (m_wfi_wakeup_req), + .sleeping_on_block (xm_sleep_block), + .block_wakeup_req_pulse (unblock_in), + .stall_release (m_sleep_stall_release) +); + +// Load/store data handling + +always @ (*) begin + // Local forwarding of store data + if (|mw_rd && xm_rs2 == mw_rd && !REDUCED_BYPASS) begin + m_wdata = mw_result; + end else begin + m_wdata = xm_result; + end + // Replicate store data to ensure appropriate byte lane is driven + case (xm_memop) + MEMOP_SH: bus_wdata_d = {2{m_wdata[15:0]}}; + MEMOP_SB: bus_wdata_d = {4{m_wdata[7:0]}}; + default: bus_wdata_d = m_wdata; + endcase + + casez ({xm_memop, xm_addr_align[1:0]}) + {MEMOP_LH , 2'b0z}: m_rdata_pick_sext = {{16{bus_rdata_d[15]}}, bus_rdata_d[15: 0]}; + {MEMOP_LH , 2'b1z}: m_rdata_pick_sext = {{16{bus_rdata_d[31]}}, bus_rdata_d[31:16]}; + {MEMOP_LHU , 2'b0z}: m_rdata_pick_sext = {{16{1'b0 }}, bus_rdata_d[15: 0]}; + {MEMOP_LHU , 2'b1z}: m_rdata_pick_sext = {{16{1'b0 }}, bus_rdata_d[31:16]}; + {MEMOP_LB , 2'b00}: m_rdata_pick_sext = {{24{bus_rdata_d[ 7]}}, bus_rdata_d[ 7: 0]}; + {MEMOP_LB , 2'b01}: m_rdata_pick_sext = {{24{bus_rdata_d[15]}}, bus_rdata_d[15: 8]}; + {MEMOP_LB , 2'b10}: m_rdata_pick_sext = {{24{bus_rdata_d[23]}}, bus_rdata_d[23:16]}; + {MEMOP_LB , 2'b11}: m_rdata_pick_sext = {{24{bus_rdata_d[31]}}, bus_rdata_d[31:24]}; + {MEMOP_LBU , 2'b00}: m_rdata_pick_sext = {{24{1'b0 }}, bus_rdata_d[ 7: 0]}; + {MEMOP_LBU , 2'b01}: m_rdata_pick_sext = {{24{1'b0 }}, bus_rdata_d[15: 8]}; + {MEMOP_LBU , 2'b10}: m_rdata_pick_sext = {{24{1'b0 }}, bus_rdata_d[23:16]}; + {MEMOP_LBU , 2'b11}: m_rdata_pick_sext = {{24{1'b0 }}, bus_rdata_d[31:24]}; + {MEMOP_LW , 2'bzz}: m_rdata_pick_sext = bus_rdata_d; + {MEMOP_LR_W, 2'bzz}: m_rdata_pick_sext = bus_rdata_d; + default: m_rdata_pick_sext = 32'hxxxx_xxxx; + endcase + + if (|EXTENSION_A && x_amo_phase == 3'h1) begin + // Capture AMO read data into mw_result for feeding back through the ALU. + m_result = bus_rdata_d; + end else if (|EXTENSION_A && xm_memop == MEMOP_SC_W) begin + // sc.w may fail due to negative response from either local or global monitor. + // Note the polarity of the result: 0 for success, 1 for failure. + m_result = {31'h0, !(mw_local_exclusive_reserved && bus_dph_exokay_d)}; + end else if (xm_memop != MEMOP_NONE && xm_memop != MEMOP_AMO) begin + m_result = m_rdata_pick_sext; + end else if (MUL_FAST && m_fast_mul_result_vld) begin + m_result = m_fast_mul_result; + end else begin + m_result = xm_result; + end +end + +// Local monitor update. +// - Set on a load-reserved with good response from global monitor +// - Cleared by any store-conditional +// - Cleared by non-debug-related trap exit + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mw_local_exclusive_reserved <= 1'b0; + end else begin + if (|EXTENSION_A && (!m_stall || bus_dph_err_d)) begin + if (d_memop_is_amo) begin + mw_local_exclusive_reserved <= 1'b0; + end else if (xm_memop == MEMOP_SC_W && (bus_dph_ready_d || bus_dph_err_d)) begin + mw_local_exclusive_reserved <= 1'b0; + end else if (xm_memop == MEMOP_LR_W && bus_dph_ready_d) begin + // In theory, the bus should never report HEXOKAY when HRESP is asserted. + // Still might happen (e.g. if HEXOKAY is tied high), so mask HEXOKAY with + // HRESP to be sure a failed lr.w clears the monitor. + mw_local_exclusive_reserved <= bus_dph_exokay_d && !bus_dph_err_d; + end + end + if (m_clear_excl_on_trap_exit) begin + mw_local_exclusive_reserved <= 1'b0; + end + end +end + +// Note that exception entry prevents writeback, because the exception entry +// replaces the instruction in M. Interrupt entry does not prevent writeback, +// because the interrupt is notionally inserted in between the instruction in +// M and the instruction in X. + +wire m_reg_wen_if_nonzero = !m_bus_stall && xm_except == EXCEPT_NONE; +wire m_reg_wen = |xm_rd && m_reg_wen_if_nonzero; + +`ifdef HAZARD3_X_CHECKS +always @ (posedge clk) begin + if (rst_n) begin + if (m_reg_wen && (^m_result === 1'bX)) begin + $display("Writing X to register file!"); + $finish; + end + end +end +`endif + +`ifdef HAZARD3_ASSERTIONS +// We borrow mw_result during an AMO to capture rdata and feed back through +// the ALU, since it already has the right paths. Make sure this is safe. +// (Whatever instruction is in M ahead of AMO should have passed through by +// the time AMO has reached read dphase) +always @ (posedge clk) if (rst_n) begin + if (x_amo_phase == 3'h1) + assert(m_reg_wen_if_nonzero); + if (x_amo_phase == 3'h1) + assert(~|xm_rd); +end +`endif + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mw_result <= {W_DATA{1'b0}}; + end else if (m_reg_wen_if_nonzero && !(|EXTENSION_A && x_amo_phase[1])) begin + // (don't trash the captured AMO read phase data during stage 2/3 of AMO -- we need it!) + mw_result <= m_result; + end +end + +assign mw_rd_nxt = REGADDR_MASK & ( + m_reg_wen_if_nonzero ? xm_rd : mw_rd +); + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mw_rd <= {W_REGADDR{1'b0}}; + end else begin +`ifdef HAZARD3_X_CHECKS + if (!m_stall && ^bus_wdata_d === 1'bX) begin + $display("Writing Xs to memory!"); + $finish; + end +`endif + if (m_reg_wen_if_nonzero) begin + mw_rd <= REGADDR_MASK & xm_rd; + end + end +end + +hazard3_regfile_1w2r #( +`include "hazard3_config_inst.vh" +) regs ( + .clk (clk), + .rst_n (rst_n), + // On downstream stall, we feed D's addresses back into regfile + // so that output does not change. + .raddr1 (x_stall && !d_starved ? d_rs1 : f_rs1_coarse), + .rdata1 (x_rdata1), + .raddr2 (x_stall && !d_starved ? d_rs2 : f_rs2_coarse), + .rdata2 (x_rdata2), + + .waddr (xm_rd), + .wdata (m_result), + .wen (m_reg_wen) +); + +`ifdef RISCV_FORMAL +`include "hazard3_rvfi_monitor.vh" +`endif + +`ifdef HAZARD3_FORMAL_REGRESSION +// Each formal regression provides its own file with the below name: +`include "hazard3_formal_regression.vh" +`endif + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_cpu_1port.v b/vendor/Hazard3/hdl/hazard3_cpu_1port.v new file mode 100644 index 0000000..bf3300a --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_cpu_1port.v @@ -0,0 +1,343 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// Single-ported top level file for Hazard3 CPU. This file instantiates the +// Hazard3 core, and arbitrates its instruction fetch and load/store signals +// down to a single AHB5 master port. + +`ifdef HAZARD3_RVFI_STANDALONE +`include "hazard3_rvfi_standalone_defs.vh" +`endif + +`default_nettype none + +module hazard3_cpu_1port #( +`include "hazard3_config.vh" +) ( + // Global signals + input wire clk, + input wire clk_always_on, + input wire rst_n, + + `ifdef RISCV_FORMAL + `RVFI_OUTPUTS , + `endif + + // Power control signals + output wire pwrup_req, + input wire pwrup_ack, + output wire clk_en, + output wire unblock_out, + input wire unblock_in, + + // AHB5 Master port + output reg [W_ADDR-1:0] haddr, + output reg hwrite, + output reg [1:0] htrans, + output reg [2:0] hsize, + output wire [2:0] hburst, + output reg [3:0] hprot, + output wire hmastlock, + output reg [7:0] hmaster, + output reg hexcl, + input wire hready, + input wire hresp, + input wire hexokay, + output wire [W_DATA-1:0] hwdata, + input wire [W_DATA-1:0] hrdata, + + // Memory ordering signals + output wire fence_i_vld, + output wire fence_d_vld, + input wire fence_rdy, + + // Debugger run/halt control + input wire dbg_req_halt, + input wire dbg_req_halt_on_reset, + input wire dbg_req_resume, + output wire dbg_halted, + output wire dbg_running, + // Debugger access to data0 CSR + input wire [W_DATA-1:0] dbg_data0_rdata, + output wire [W_DATA-1:0] dbg_data0_wdata, + output wire dbg_data0_wen, + // Debugger instruction injection + input wire [W_DATA-1:0] dbg_instr_data, + input wire dbg_instr_data_vld, + output wire dbg_instr_data_rdy, + output wire dbg_instr_caught_exception, + output wire dbg_instr_caught_ebreak, + + // Optional debug system bus access patch-through + input wire [W_ADDR-1:0] dbg_sbus_addr, + input wire dbg_sbus_write, + input wire [1:0] dbg_sbus_size, + input wire dbg_sbus_vld, + output wire dbg_sbus_rdy, + output wire dbg_sbus_err, + input wire [W_DATA-1:0] dbg_sbus_wdata, + output wire [W_DATA-1:0] dbg_sbus_rdata, + + // Identification CSR values + input wire [W_DATA-1:0] mhartid_val, + input wire [3:0] eco_version, + + // Level-sensitive interrupt sources + input wire [NUM_IRQS-1:0] irq, // -> mip.meip + input wire soft_irq, // -> mip.msip + input wire timer_irq // -> mip.mtip +); + +// ---------------------------------------------------------------------------- +// Processor core + +// Instruction fetch signals +wire core_aph_req_i; +wire core_aph_panic_i; +wire core_aph_ready_i; +wire core_dph_ready_i; +wire core_dph_err_i; + +wire [W_ADDR-1:0] core_haddr_i; +wire [2:0] core_hsize_i; +wire core_priv_i; +wire [W_DATA-1:0] core_rdata_i; + + +// Load/store signals +wire core_aph_req_d; +wire core_aph_excl_d; +wire core_aph_ready_d; +wire core_dph_ready_d; +wire core_dph_err_d; +wire core_dph_exokay_d; + +wire [W_ADDR-1:0] core_haddr_d; +wire [2:0] core_hsize_d; +wire core_priv_d; +wire core_hwrite_d; +wire [W_DATA-1:0] core_wdata_d; +wire [W_DATA-1:0] core_rdata_d; + + +hazard3_core #( +`include "hazard3_config_inst.vh" +) core ( + .clk (clk), + .clk_always_on (clk_always_on), + .rst_n (rst_n), + + .pwrup_req (pwrup_req), + .pwrup_ack (pwrup_ack), + .clk_en (clk_en), + .unblock_out (unblock_out), + .unblock_in (unblock_in), + + `ifdef RISCV_FORMAL + `RVFI_CONN , + `endif + + .bus_aph_req_i (core_aph_req_i), + .bus_aph_panic_i (core_aph_panic_i), + .bus_aph_ready_i (core_aph_ready_i), + .bus_dph_ready_i (core_dph_ready_i), + .bus_dph_err_i (core_dph_err_i), + .bus_haddr_i (core_haddr_i), + .bus_hsize_i (core_hsize_i), + .bus_priv_i (core_priv_i), + .bus_rdata_i (core_rdata_i), + + .bus_aph_req_d (core_aph_req_d), + .bus_aph_excl_d (core_aph_excl_d), + .bus_aph_ready_d (core_aph_ready_d), + .bus_dph_ready_d (core_dph_ready_d), + .bus_dph_err_d (core_dph_err_d), + .bus_dph_exokay_d (core_dph_exokay_d), + .bus_haddr_d (core_haddr_d), + .bus_hsize_d (core_hsize_d), + .bus_priv_d (core_priv_d), + .bus_hwrite_d (core_hwrite_d), + .bus_wdata_d (core_wdata_d), + .bus_rdata_d (core_rdata_d), + + .fence_i_vld (fence_i_vld), + .fence_d_vld (fence_d_vld), + .fence_rdy (fence_rdy), + + .dbg_req_halt (dbg_req_halt), + .dbg_req_halt_on_reset (dbg_req_halt_on_reset), + .dbg_req_resume (dbg_req_resume), + .dbg_halted (dbg_halted), + .dbg_running (dbg_running), + .dbg_data0_rdata (dbg_data0_rdata), + .dbg_data0_wdata (dbg_data0_wdata), + .dbg_data0_wen (dbg_data0_wen), + .dbg_instr_data (dbg_instr_data), + .dbg_instr_data_vld (dbg_instr_data_vld), + .dbg_instr_data_rdy (dbg_instr_data_rdy), + .dbg_instr_caught_exception (dbg_instr_caught_exception), + .dbg_instr_caught_ebreak (dbg_instr_caught_ebreak), + + .mhartid_val (mhartid_val), + .eco_version (eco_version), + + .irq (irq), + .soft_irq (soft_irq), + .timer_irq (timer_irq) +); + +// ---------------------------------------------------------------------------- +// Arbitration state machine + +wire bus_gnt_i; +wire bus_gnt_d; +wire bus_gnt_s; + +reg bus_hold_aph; +reg [2:0] bus_gnt_ids_prev; + +// Note use of clk_always_on: SBA may use this arbiter to access the bus +// whilst the core is asleep. + +always @ (posedge clk_always_on or negedge rst_n) begin + if (!rst_n) begin + bus_hold_aph <= 1'b0; + bus_gnt_ids_prev <= 3'h0; + end else begin + bus_hold_aph <= htrans[1] && !hready && !hresp; + bus_gnt_ids_prev <= {bus_gnt_i, bus_gnt_d, bus_gnt_s}; + end +end + +// Debug SBA access is lower priority than load/store, but higher than +// instruction fetch. This isn't ideal, but in a tight loop the core may be +// performing an instruction fetch or load/store on every single cycle, and +// this is a simple way to guarantee eventual success of debugger accesses. A +// more complex way would be to add a "panic timer" to boost a stalled sbus +// access over an instruction fetch. + +// Note that, often, the sbus will be disconnected: it doesn't provide any +// increase in debugger bus throughput compared with the program buffer and +// autoexec. It's useful for "minimally intrusive" debug bus access(i.e. less +// intrusive than halting the core and resuming it) e.g. for Segger RTT. + +reg bus_active_dph_s; + +assign {bus_gnt_i, bus_gnt_d, bus_gnt_s} = + bus_hold_aph ? bus_gnt_ids_prev : + core_aph_panic_i ? 3'b100 : + core_aph_req_d ? 3'b010 : + dbg_sbus_vld && !bus_active_dph_s ? 3'b001 : + core_aph_req_i ? 3'b100 : + 3'b000 ; +reg bus_active_dph_i; +reg bus_active_dph_d; + +always @ (posedge clk_always_on or negedge rst_n) begin + if (!rst_n) begin + bus_active_dph_i <= 1'b0; + bus_active_dph_d <= 1'b0; + bus_active_dph_s <= 1'b0; + end else if (hready) begin + bus_active_dph_i <= bus_gnt_i; + bus_active_dph_d <= bus_gnt_d; + bus_active_dph_s <= bus_gnt_s; + end +end + +// ---------------------------------------------------------------------------- +// Address phase request muxing + +localparam HTRANS_IDLE = 2'b00; +localparam HTRANS_NSEQ = 2'b10; + +wire [3:0] hprot_data = { + 2'b00, // Noncacheable/nonbufferable + core_priv_d, // Privileged or Normal as per core state + 1'b1 // Data access +}; + +wire [3:0] hprot_instr = { + 2'b00, // Noncacheable/nonbufferable + core_priv_i, // Privileged or Normal as per core state + 1'b0 // Instruction access +}; + +wire [3:0] hprot_sbus = { + 2'b00, // Noncacheable/nonbufferable + 1'b1, // Always privileged + 1'b1 // Data access +}; + +assign hburst = 3'b000; // HBURST_SINGLE +assign hmastlock = 1'b0; + +always @ (*) begin + if (bus_gnt_s) begin + htrans = HTRANS_NSEQ; + hexcl = 1'b0; + haddr = dbg_sbus_addr; + hsize = {1'b0, dbg_sbus_size}; + hwrite = dbg_sbus_write; + hprot = hprot_sbus; + hmaster = 8'h01; + end else if (bus_gnt_d) begin + htrans = HTRANS_NSEQ; + hexcl = core_aph_excl_d; + haddr = core_haddr_d; + hsize = core_hsize_d; + hwrite = core_hwrite_d; + hprot = hprot_data; + hmaster = 8'h00; + end else if (bus_gnt_i) begin + htrans = HTRANS_NSEQ; + hexcl = 1'b0; + haddr = core_haddr_i; + hsize = core_hsize_i; + hwrite = 1'b0; + hprot = hprot_instr; + hmaster = 8'h00; + end else begin + htrans = HTRANS_IDLE; + hexcl = 1'b0; + haddr = {W_ADDR{1'b0}}; + hsize = 3'h0; + hwrite = 1'b0; + hprot = 4'h0; + hmaster = 8'h00; + end +end + +assign hwdata = bus_active_dph_s ? dbg_sbus_wdata : core_wdata_d; + +// ---------------------------------------------------------------------------- +// Response routing + +// Data buses directly connected +assign core_rdata_d = hrdata; +assign core_rdata_i = hrdata; +assign dbg_sbus_rdata = hrdata; + +// Handhshake based on grant and bus stall +assign core_aph_ready_i = hready && bus_gnt_i; +assign core_dph_ready_i = bus_active_dph_i && hready; +assign core_dph_err_i = bus_active_dph_i && hresp; + +// D-side errors are reported even when not ready, so that the core can make +// use of the two-phase error response to cleanly squash a second load/store +// chasing the faulting one down the pipeline. +assign core_aph_ready_d = hready && bus_gnt_d; +assign core_dph_ready_d = bus_active_dph_d && hready; +assign core_dph_err_d = bus_active_dph_d && hresp; +assign core_dph_exokay_d = bus_active_dph_d && hexokay; + +assign dbg_sbus_err = bus_active_dph_s && hresp; +assign dbg_sbus_rdy = bus_active_dph_s && hready; + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_cpu_2port.v b/vendor/Hazard3/hdl/hazard3_cpu_2port.v new file mode 100644 index 0000000..7356671 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_cpu_2port.v @@ -0,0 +1,327 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// Dual-ported top level file for Hazard3 CPU. This file instantiates the +// Hazard3 core, and interfaces its instruction fetch and load/store signals +// to a pair of AHB5 master ports. + +`ifdef HAZARD3_RVFI_STANDALONE +`include "hazard3_rvfi_standalone_defs.vh" +`endif + +`default_nettype none + +module hazard3_cpu_2port #( +`include "hazard3_config.vh" +) ( + // Global signals + input wire clk, + input wire clk_always_on, + input wire rst_n, + + // Power control signals + output wire pwrup_req, + input wire pwrup_ack, + output wire clk_en, + output wire unblock_out, + input wire unblock_in, + + `ifdef RISCV_FORMAL + `RVFI_OUTPUTS , + `endif + + // Instruction fetch port + output wire [W_ADDR-1:0] i_haddr, + output wire i_hwrite, + output wire [1:0] i_htrans, + output wire [2:0] i_hsize, + output wire [2:0] i_hburst, + output wire [3:0] i_hprot, + output wire i_hmastlock, + output wire [7:0] i_hmaster, + input wire i_hready, + input wire i_hresp, + output wire [W_DATA-1:0] i_hwdata, + input wire [W_DATA-1:0] i_hrdata, + + // Load/store port + output wire [W_ADDR-1:0] d_haddr, + output wire d_hwrite, + output wire [1:0] d_htrans, + output wire [2:0] d_hsize, + output wire [2:0] d_hburst, + output wire [3:0] d_hprot, + output wire d_hmastlock, + output wire [7:0] d_hmaster, + output wire d_hexcl, + input wire d_hready, + input wire d_hresp, + input wire d_hexokay, + output wire [W_DATA-1:0] d_hwdata, + input wire [W_DATA-1:0] d_hrdata, + + // Memory ordering signals + output wire fence_i_vld, + output wire fence_d_vld, + input wire fence_rdy, + + // Debugger run/halt control + input wire dbg_req_halt, + input wire dbg_req_halt_on_reset, + input wire dbg_req_resume, + output wire dbg_halted, + output wire dbg_running, + // Debugger access to data0 CSR + input wire [W_DATA-1:0] dbg_data0_rdata, + output wire [W_DATA-1:0] dbg_data0_wdata, + output wire dbg_data0_wen, + // Debugger instruction injection + input wire [W_DATA-1:0] dbg_instr_data, + input wire dbg_instr_data_vld, + output wire dbg_instr_data_rdy, + output wire dbg_instr_caught_exception, + output wire dbg_instr_caught_ebreak, + // Optional debug system bus access patch-through + input wire [W_ADDR-1:0] dbg_sbus_addr, + input wire dbg_sbus_write, + input wire [1:0] dbg_sbus_size, + input wire dbg_sbus_vld, + output wire dbg_sbus_rdy, + output wire dbg_sbus_err, + input wire [W_DATA-1:0] dbg_sbus_wdata, + output wire [W_DATA-1:0] dbg_sbus_rdata, + + // Identification CSR values + input wire [W_DATA-1:0] mhartid_val, + input wire [3:0] eco_version, + + // Level-sensitive interrupt sources + input wire [NUM_IRQS-1:0] irq, // -> mip.meip + input wire soft_irq, // -> mip.msip + input wire timer_irq // -> mip.mtip +); + +// ---------------------------------------------------------------------------- +// Processor core + +// Instruction fetch signals +wire core_aph_req_i; +wire core_aph_ready_i; +wire core_dph_ready_i; +wire core_dph_err_i; + +wire [W_ADDR-1:0] core_haddr_i; +wire [2:0] core_hsize_i; +wire core_priv_i; +wire [W_DATA-1:0] core_rdata_i; + + +// Load/store signals +wire core_aph_req_d; +wire core_aph_excl_d; +wire core_aph_ready_d; +wire core_dph_ready_d; +wire core_dph_err_d; +wire core_dph_exokay_d; + +wire [W_ADDR-1:0] core_haddr_d; +wire [2:0] core_hsize_d; +wire core_priv_d; +wire core_hwrite_d; +wire [W_DATA-1:0] core_wdata_d; +wire [W_DATA-1:0] core_rdata_d; + +hazard3_core #( +`include "hazard3_config_inst.vh" +) core ( + .clk (clk), + .clk_always_on (clk_always_on), + .rst_n (rst_n), + + .pwrup_req (pwrup_req), + .pwrup_ack (pwrup_ack), + .clk_en (clk_en), + .unblock_out (unblock_out), + .unblock_in (unblock_in), + + `ifdef RISCV_FORMAL + `RVFI_CONN , + `endif + + .bus_aph_req_i (core_aph_req_i), + .bus_aph_panic_i (/* unused for 2port */), + .bus_aph_ready_i (core_aph_ready_i), + .bus_dph_ready_i (core_dph_ready_i), + .bus_dph_err_i (core_dph_err_i), + .bus_haddr_i (core_haddr_i), + .bus_hsize_i (core_hsize_i), + .bus_priv_i (core_priv_i), + .bus_rdata_i (core_rdata_i), + + .bus_aph_req_d (core_aph_req_d), + .bus_aph_excl_d (core_aph_excl_d), + .bus_aph_ready_d (core_aph_ready_d), + .bus_dph_ready_d (core_dph_ready_d), + .bus_dph_err_d (core_dph_err_d), + .bus_dph_exokay_d (core_dph_exokay_d), + .bus_haddr_d (core_haddr_d), + .bus_hsize_d (core_hsize_d), + .bus_priv_d (core_priv_d), + .bus_hwrite_d (core_hwrite_d), + .bus_wdata_d (core_wdata_d), + .bus_rdata_d (core_rdata_d), + + .fence_i_vld (fence_i_vld), + .fence_d_vld (fence_d_vld), + .fence_rdy (fence_rdy), + + .dbg_req_halt (dbg_req_halt), + .dbg_req_halt_on_reset (dbg_req_halt_on_reset), + .dbg_req_resume (dbg_req_resume), + .dbg_halted (dbg_halted), + .dbg_running (dbg_running), + .dbg_data0_rdata (dbg_data0_rdata), + .dbg_data0_wdata (dbg_data0_wdata), + .dbg_data0_wen (dbg_data0_wen), + .dbg_instr_data (dbg_instr_data), + .dbg_instr_data_vld (dbg_instr_data_vld), + .dbg_instr_data_rdy (dbg_instr_data_rdy), + .dbg_instr_caught_exception (dbg_instr_caught_exception), + .dbg_instr_caught_ebreak (dbg_instr_caught_ebreak), + + .mhartid_val (mhartid_val), + .eco_version (eco_version), + + .irq (irq), + .soft_irq (soft_irq), + .timer_irq (timer_irq) +); + +// ---------------------------------------------------------------------------- +// Instruction port + +localparam HTRANS_IDLE = 2'b00; +localparam HTRANS_NSEQ = 2'b10; + +assign i_haddr = core_haddr_i; +assign i_htrans = core_aph_req_i ? HTRANS_NSEQ : HTRANS_IDLE; +assign i_hsize = core_hsize_i; + +reg dphase_active_i; +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + dphase_active_i <= 1'b0; + end else if (i_hready) begin + dphase_active_i <= core_aph_req_i; + end +end + +`ifdef HAZARD3_ASSERTIONS +// Wake->sleep transition must wait for outstanding instruction fetches to +// complete, in particular because the arbiter clock will stop +always @ (posedge clk) if (!rst_n) assert(clk_en || !(core_aph_req_i || dphase_active_i)); +`endif + +assign core_aph_ready_i = i_hready && core_aph_req_i; +assign core_dph_ready_i = i_hready && dphase_active_i; +assign core_dph_err_i = i_hready && dphase_active_i && i_hresp; + +assign core_rdata_i = i_hrdata; + +assign i_hwrite = 1'b0; +assign i_hburst = 3'h0; +assign i_hmastlock = 1'b0; +assign i_hmaster = 8'h00; +assign i_hwdata = {W_DATA{1'b0}}; + +assign i_hprot = { + 2'b00, // Noncacheable/nonbufferable + core_priv_i, // Privileged or Normal as per core state + 1'b0 // Instruction access +}; + +// ---------------------------------------------------------------------------- +// Load/store port + +// The debug module has optional System Bus Access support, which can be muxed +// into the processor's D port here (or connected to a standalone AHB shim). +// This confers absolutely no advantage for debugger bus throughput, but +// allows the debugger to access the bus with minimal disturbance to the +// processor. + +wire bus_gnt_d; +wire bus_gnt_s; + +reg bus_hold_aph; +reg [1:0] bus_gnt_ds_prev; +reg bus_active_dph_d; +reg bus_active_dph_s; + +// clk_always_on is used because SBA may access the bus through this arbiter +// whilst the core is asleep (same is not true for I-side interface) + +always @ (posedge clk_always_on or negedge rst_n) begin + if (!rst_n) begin + bus_hold_aph <= 1'b0; + bus_gnt_ds_prev <= 2'h0; + end else begin + bus_hold_aph <= d_htrans[1] && !d_hready && !d_hresp; + bus_gnt_ds_prev <= {bus_gnt_d, bus_gnt_s}; + end +end + +assign {bus_gnt_d, bus_gnt_s} = + bus_hold_aph ? bus_gnt_ds_prev : + core_aph_req_d ? 2'b10 : + dbg_sbus_vld && !bus_active_dph_s ? 2'b01 : + 2'b00 ; + +always @ (posedge clk_always_on or negedge rst_n) begin + if (!rst_n) begin + bus_active_dph_d <= 1'b0; + bus_active_dph_s <= 1'b0; + end else if (d_hready) begin + bus_active_dph_d <= bus_gnt_d; + bus_active_dph_s <= bus_gnt_s; + end +end + +assign d_htrans = bus_gnt_d || bus_gnt_s ? HTRANS_NSEQ : HTRANS_IDLE; + +assign d_haddr = bus_gnt_s ? dbg_sbus_addr : core_haddr_d; +assign d_hwrite = bus_gnt_s ? dbg_sbus_write : core_hwrite_d; +assign d_hsize = bus_gnt_s ? {1'b0, dbg_sbus_size} : core_hsize_d; +assign d_hexcl = bus_gnt_s ? 1'b0 : core_aph_excl_d; + +assign d_hprot = { + 2'b00, // Noncacheable/nonbufferable + bus_gnt_s || core_priv_d, // Privileged or Normal as per core state + 1'b1 // Data access +}; + +assign d_hwdata = bus_active_dph_s ? dbg_sbus_wdata : core_wdata_d; + +// D-side errors are reported even when not ready, so that the core can make +// use of the two-phase error response to cleanly squash a second load/store +// chasing the faulting one down the pipeline. +assign core_aph_ready_d = d_hready && bus_gnt_d; +assign core_dph_ready_d = bus_active_dph_d && d_hready; +assign core_dph_err_d = bus_active_dph_d && d_hresp; +assign core_dph_exokay_d = bus_active_dph_d && d_hexokay; +assign core_rdata_d = d_hrdata; + +assign dbg_sbus_err = bus_active_dph_s && d_hresp; +assign dbg_sbus_rdy = bus_active_dph_s && d_hready; +assign dbg_sbus_rdata = d_hrdata; + +assign d_hburst = 3'h0; +assign d_hmastlock = 1'b0; +assign d_hmaster = bus_gnt_s ? 8'h01 : 8'h00; + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_csr.v b/vendor/Hazard3/hdl/hazard3_csr.v new file mode 100644 index 0000000..3f95557 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_csr.v @@ -0,0 +1,1642 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +`default_nettype none + +// Control and Status Registers (CSRs) +// Also includes CSR-related logic like interrupt enable/masking, +// trap vector calculation. + +module hazard3_csr #( + parameter XLEN = 32, // Must be 32 +`include "hazard3_config.vh" +, +`include "hazard3_width_const.vh" +) ( + input wire clk, + input wire clk_always_on, + input wire rst_n, + + // Debug signalling + output reg debug_mode, + + input wire dbg_req_halt, + input wire dbg_req_halt_on_reset, + input wire dbg_req_resume, + + output wire dbg_instr_caught_exception, + output wire dbg_instr_caught_ebreak, + + input wire [W_DATA-1:0] dbg_data0_rdata, + output wire [W_DATA-1:0] dbg_data0_wdata, + output wire dbg_data0_wen, + + output wire [W_ADDR-1:0] debug_dpc_wdata, + output wire debug_dpc_wen, + input wire [W_ADDR-1:0] debug_dpc_rdata, + + // Read port is combinatorial. + // Write port is synchronous, and write effects will be observed on the next clock cycle. + // The *_soon strobes are versions which the core does not gate with its stall signal. + // These are needed because: + // - Core stall is a function of bus stall + // - Illegal CSR accesses produce trap entry + // - Trap entry (not necessarily caused by CSR access) gates outgoing bus accesses + // - Through-paths from e.g. hready to htrans are problematic for timing/implementation + input wire [11:0] addr, + input wire [XLEN-1:0] wdata, + input wire wen, + input wire wen_soon, // wen will be asserted once some stall condition clears + input wire wen_raw, // raw, ungated instruction decode signal, for D-mode regs + input wire [1:0] wtype, + output reg [XLEN-1:0] rdata, + input wire ren, + input wire ren_soon, // ren will be asserted once some stall condition clears + output wire illegal, + output wire write_is_fetch_ordered, + + // Trap signalling + // *We* tell the core that we are taking a trap, and where to, based on: + // - Synchronous exception inputs from the core + // - External IRQ signals + // - Masking etc based on the state of CSRs like mie + // + // We do this by raising trap_enter_vld, and keeping it raised until trap_enter_rdy + // goes high. trap_addr has the absolute value of trap target address. + // Once trap_enter_vld && _rdy, mepc_in is copied to mepc, and other trap state is set. + // + // Note that an exception input can go away, e.g. if the pipe gets flushed. In this + // case we lower trap_enter_vld. + output wire [XLEN-1:0] trap_addr, + output wire trap_is_irq, + output wire trap_enter_vld, + input wire trap_enter_rdy, + // True when we are about to trap, but are waiting for an excepting or + // potentially-excepting instruction to clear M first. The instruction in X + // is suppressed, X PC does not increment but still tracks exception + // addresses. + output wire trap_enter_soon, + // We need to know about load/stores in data phase because their exception + // status is still unknown, so fence them off before entering debug mode. + input wire loadstore_dphase_pending, + // Asserted when the instruction in stage 2 can't be squashed (e.g. to + // keep an AHB address phase stable), or instruction fetch request can't + // be issued (e.g. waiting for wake from wfi state). + input wire delay_irq_entry, + input wire [XLEN-1:0] mepc_in, + + // Power control signalling + output wire pwr_allow_clkgate, + output wire pwr_allow_power_down, + output wire pwr_allow_sleep_on_block, + output wire pwr_wfi_wakeup_req, + + // Each of these may be performed at a different privilege level from the others: + output wire m_mode_execution, + output wire m_mode_trap_entry, + output wire m_mode_loadstore, + + // Exceptions must *not* be a function of bus stall. + input wire [W_EXCEPT-1:0] except, + input wire except_to_d_mode, + + // Level-sensitive interrupt sources + input wire [NUM_IRQS-1:0] irq, + input wire irq_software, + input wire irq_timer, + + // PMP config interface + output wire [11:0] pmp_cfg_addr, + output reg pmp_cfg_wen, + output wire [W_DATA-1:0] pmp_cfg_wdata, + input wire [W_DATA-1:0] pmp_cfg_rdata, + + // Trigger config interface + output wire [11:0] trig_cfg_addr, + output reg trig_cfg_wen, + output wire [W_DATA-1:0] trig_cfg_wdata, + input wire [W_DATA-1:0] trig_cfg_rdata, + output wire trig_m_en, + + // Interrupt/exception trigger events + output wire trig_event_interrupt, + output wire trig_event_exception, + output wire [3:0] trig_event_trap_cause, + + // Other CSR-specific signalling + output wire clear_excl_on_trap_exit, + output wire trap_wfi, + input wire instr_ret, + input wire [W_DATA-1:0] mhartid_val, + input wire [3:0] eco_version +); + +`include "hazard3_ops.vh" +`include "hazard3_csr_addr.vh" + +localparam X0 = {XLEN{1'b0}}; + +// ---------------------------------------------------------------------------- +// CSR state + update logic +// ---------------------------------------------------------------------------- +// Names are (reg)_(field) + +// Generic update logic for write/set/clear of an entire CSR: +wire [XLEN-1:0] wdata_update = + wtype == CSR_WTYPE_C ? rdata & ~wdata : + wtype == CSR_WTYPE_S ? rdata | wdata : wdata; + +function [XLEN-1:0] update_nonconst; + input [XLEN-1:0] prev; + input [XLEN-1:0] nonconst; +begin + update_nonconst = (wdata_update & nonconst) | (prev & ~nonconst) ; +end +endfunction + +wire enter_debug_mode; +wire exit_debug_mode; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + debug_mode <= 1'b0; + end else if (DEBUG_SUPPORT) begin + debug_mode <= (debug_mode && !exit_debug_mode) || enter_debug_mode; + end +end + +// Core execution state, 1 -> M-mode, 0 -> U-mode (if implemented) +reg m_mode; +wire wen_m_mode = wen && (m_mode || debug_mode); +wire ren_m_mode = ren && (m_mode || debug_mode); + +// ---------------------------------------------------------------------------- +// Standard trap-handling CSRs + +wire debug_suppresses_trap_update = DEBUG_SUPPORT && (debug_mode || enter_debug_mode); + +wire trapreg_update = trap_enter_vld && trap_enter_rdy && !debug_suppresses_trap_update; +wire trapreg_update_enter = trapreg_update && except != EXCEPT_MRET && except != EXCEPT_REFETCH; +wire trapreg_update_exit = trapreg_update && except == EXCEPT_MRET; + +// Local monitor flag cleared on trap exit (but not on debug-mode exit, to +// permit stepping through LR/SC sequences): +assign clear_excl_on_trap_exit = trapreg_update_exit; + +reg mstatus_mpie; +reg mstatus_mie; +reg mstatus_mpp; // only MSB is implemented +reg mstatus_mprv; +reg mstatus_tw; + +// Spec text from priv-1.12: +// "An MRET or SRET instruction is used to return from a trap in M-mode or +// S-mode respectively. When executing an xRET instruction, supposing xPP +// holds the value y, xIE is set to xPIE; the privilege mode is changed to +// y; xPIE is set to 1; and xPP is set to the least-privileged supported +// mode (U if U-mode is implemented, else M). If xPP=M, xRET also sets +// MPRV=0." + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + m_mode <= 1'b1; + mstatus_mpie <= 1'b0; + mstatus_mie <= 1'b0; + mstatus_mpp <= 1'b1; + mstatus_mprv <= 1'b0; + mstatus_tw <= 1'b0; + end else if (!CSR_M_TRAP) begin + // Explicit tie-off when unimplemented; avoid synthesis warning about + // flops insensitive to clk. Should match the rst_n case above so that + // flops are constant-propagated away. + m_mode <= 1'b1; + mstatus_mpie <= 1'b0; + mstatus_mie <= 1'b0; + mstatus_mpp <= 1'b1; + mstatus_mprv <= 1'b0; + mstatus_tw <= 1'b0; + end else begin + if (trapreg_update_exit) begin + mstatus_mpie <= 1'b1; + mstatus_mie <= mstatus_mpie; + if (U_MODE) begin + m_mode <= mstatus_mpp; + mstatus_mpp <= 1'b0; + if (!mstatus_mpp) begin + mstatus_mprv <= 1'b0; + end + end + end else if (trapreg_update_enter) begin + mstatus_mpie <= mstatus_mie; + mstatus_mie <= 1'b0; + if (U_MODE) begin + m_mode <= 1'b1; + mstatus_mpp <= m_mode; + end + end else if (wen_m_mode && addr == MSTATUS) begin + mstatus_mpie <= wdata_update[7]; + mstatus_mie <= wdata_update[3]; + mstatus_mprv <= wdata_update[17]; + // Note only the MSB of MPP is implemented. It reads back as 11 or 00. + mstatus_mpp <= wdata_update[12] || !U_MODE; + mstatus_tw <= wdata_update[21] && U_MODE; + end else if (wen_raw && debug_mode && addr == DCSR && U_MODE && DEBUG_SUPPORT) begin + // Debugger can change/observe core state directly through + // dcsr.prv (this doesn't affect debugger operation, as all + // operations have an effective level of M-mode whilst the core + // is halted for debug.) + m_mode <= wdata_update[1]; + end + if (!U_MODE) begin + // Explicitly tie-off to constant; some synthesis tools don't like + // it when variables are only sensitive to the reset. + m_mode <= 1'b1; + mstatus_mpp <= 1'b1; + end + end +end + +// Trap all U-mode WFIs if timeout bit is set. Note that the debug spec +// says "The `wfi` instruction acts as a `nop`" during program buffer +// execution, and nops do not trap, so in debug mode we allow WFIs but +// immediately wake them. +assign trap_wfi = mstatus_tw && !(debug_mode || m_mode); + +reg [XLEN-1:0] mscratch; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mscratch <= X0; + end else if (!CSR_M_TRAP) begin + // Explicit tie-off when unimplemented + mscratch <= X0; + end else begin + if (wen_m_mode && addr == MSCRATCH) begin + mscratch <= wdata_update; + end + end +end + +// Trap vector base +reg [XLEN-1:0] mtvec_reg; +wire [XLEN-1:0] mtvec = mtvec_reg & ({XLEN{1'b1}} << 2); +wire irq_vector_enable = mtvec_reg[0]; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mtvec_reg <= MTVEC_INIT; + end else if (!CSR_M_TRAP) begin + // Explicit tie-off when unimplemented + mtvec_reg <= MTVEC_INIT; + end else begin + if (wen_m_mode && addr == MTVEC) begin + mtvec_reg <= update_nonconst(mtvec_reg, MTVEC_WMASK); + end + end +end + +// Exception program counter +reg [XLEN-1:0] mepc; +// mepc only holds values aligned to instruction alignment +localparam MEPC_MASK = {{XLEN-2{1'b1}}, |EXTENSION_C, 1'b0}; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mepc <= X0; + end else if (!CSR_M_TRAP) begin + // Explicit tie-off when unimplemented + mepc <= X0; + end else begin + if (trapreg_update_enter) begin + mepc <= mepc_in & MEPC_MASK; + end else if (wen_m_mode && addr == MEPC) begin + mepc <= wdata_update & MEPC_MASK; + end + end +end + +// Interrupt enable (reserved bits are tied to 0) +reg [XLEN-1:0] mie; +localparam MIE_WMASK = 32'h00000888; // meie, mtie, msie + +wire meicontext_clearts; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mie <= X0; + end else if (!CSR_M_TRAP) begin + // Explicit tie-off when unimplemented + mie <= X0; + end else begin + if (wen_m_mode && addr == MIE) begin + mie <= update_nonconst(mie, MIE_WMASK); + end else if (wen_m_mode && addr == MEICONTEXT) begin + // meicontext.clearts/mtiesave/msiesave can be used to clear and + // save standard timer and soft IRQ enables, simultaneously with + // saving external interrupt context. + mie[7] <= (mie[7] || wdata_update[3]) && !meicontext_clearts; + mie[3] <= (mie[3] || wdata_update[2]) && !meicontext_clearts; + end + end +end + +// Interrupt pending register (assigned later). In our implementation this +// register is entirely read-only. +wire [XLEN-1:0] mip; + +// Trap cause registers. The non-constant bits can be written by software, and +// update automatically on trap entry. The `code` field need only be large +// enough to support causes that will be set by hardware, in our case 4 bits. +// (Note this is different to scause which always has a hard minimum of 5 +// bits.) +reg mcause_irq; +reg [3:0] mcause_code; +wire mcause_irq_next; +wire [3:0] mcause_code_next; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mcause_irq <= 1'b0; + mcause_code <= 4'h0; + end else if (!CSR_M_TRAP) begin + // Explicit tie-off when unimplemented + mcause_irq <= 1'b0; + mcause_code <= 4'h0; + end else begin + if (trapreg_update_enter) begin + mcause_irq <= mcause_irq_next; + mcause_code <= mcause_code_next; + end else if (wen_m_mode && addr == MCAUSE) begin + {mcause_irq, mcause_code} <= {wdata_update[31], wdata_update[3:0]}; + end + end +end + +// ---------------------------------------------------------------------------- +// Standard identification CSRs + +// mimpid records the Hazard3 release version. This is updated manually with +// each release. + +// Set to 1 if this RTL has not been released to the stable branch: +localparam [0:0] MIMPID_PRERELEASE = 1'b0; + +// Major version, e.g the 1 in v1.2.3: +localparam [3:0] MIMPID_MAJOR = 4'd1; +// Minor version, e.g. the 2 in v1.2.3: +localparam [7:0] MIMPID_MINOR = 8'd1; +// Patch version, e.g. the 3 in v1.2.3: +localparam [7:0] MIMPID_PATCH = 8'd0; + +// Note the eco_version field is connected externally -- on ASIC this may be +// connected to tie cells so it can be incremented following a metal ECO. +wire [31:0] mimpid_rdata = { + MIMPID_PRERELEASE, + 3'd0, + MIMPID_MAJOR, + MIMPID_MINOR, + MIMPID_PATCH, + eco_version, + 4'h0 +}; + +localparam [31:0] MISA_VAL = { + 2'h1, // MXL: 32-bit + {XLEN-28{1'b0}}, // WLRL + + 2'd0, // Z, Y, no + 1'b1, // X is always set due to (at least) Xh3misa + 2'd0, // V, W, no + |U_MODE, + 7'd0, // T...N, no + |EXTENSION_M, + 3'd0, // L...J, no + ~|EXTENSION_E, // RVI base ISA + 3'd0, // H, G, F, no + |EXTENSION_E, // RVE base ISA + 1'b0, // D, no + |EXTENSION_C, + &{ // B is defined as ZbaZbbZbs + |EXTENSION_ZBA, + |EXTENSION_ZBB, + |EXTENSION_ZBS + }, + |EXTENSION_A +}; + +// ---------------------------------------------------------------------------- +// Custom external IRQ controller + +wire [XLEN-1:0] irq_ctrl_rdata; +wire external_irq_pending; + +generate +if (|EXTENSION_XH3IRQ) begin: have_irq_ctrl + + hazard3_irq_ctrl #( + `include "hazard3_config_inst.vh" + ) irq_ctrl ( + .clk (clk), + .clk_always_on (clk_always_on), + .rst_n (rst_n), + + .addr (addr), + .wtype (wtype), + .wen_m_mode (wen_m_mode), + .ren_m_mode (ren_m_mode), + .wdata_raw (wdata), + .wdata (wdata_update), + .rdata (irq_ctrl_rdata), + + .trapreg_update_enter (trapreg_update_enter), + .trapreg_update_exit (trapreg_update_exit), + .trap_entry_is_eirq (mcause_irq_next && mcause_code_next == 4'hb), + + .meicontext_clearts (meicontext_clearts), + .mie_mtie (mie[7]), + .mie_msie (mie[3]), + + .irq (irq), + .external_irq_pending (external_irq_pending) + ); + +end else begin: no_irq_ctrl + + reg external_irq_pending_r; + + always @ (posedge clk_always_on or negedge rst_n) begin + if (!rst_n) begin + external_irq_pending_r <= 1'b0; + end else begin + external_irq_pending_r <= |irq; + end + end + + assign irq_ctrl_rdata = {W_DATA{1'b0}}; + assign meicontext_clearts = 1'b0; + assign external_irq_pending = external_irq_pending_r; + +end +endgenerate + +// ---------------------------------------------------------------------------- +// Custom sleep/power control CSRs + +reg msleep_sleeponblock; +reg msleep_powerdown; +reg msleep_deepsleep; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + msleep_sleeponblock <= 1'b0; + msleep_powerdown <= 1'b0; + msleep_deepsleep <= 1'b0; + end else if (wen_m_mode && addr == MSLEEP) begin + msleep_sleeponblock <= wdata_update[2] && |EXTENSION_XH3POWER; + msleep_powerdown <= wdata_update[1] && |EXTENSION_XH3POWER; + msleep_deepsleep <= wdata_update[0] && |EXTENSION_XH3POWER; + end +end + +assign pwr_allow_sleep_on_block = msleep_sleeponblock; +assign pwr_allow_power_down = msleep_powerdown; +assign pwr_allow_clkgate = msleep_deepsleep; + +// ---------------------------------------------------------------------------- +// Custom identification CSRs + +// Derive implied extensions: + +// B is a shorthand for ZbaZbbZbs: +localparam [0:0] EXTENSION_B = |EXTENSION_ZBA && |EXTENSION_ZBB && |EXTENSION_ZBS; +// RV32I and RV32E are complementary: +localparam [0:0] EXTENSION_I = ~|EXTENSION_E; +// Zbkc is a subset of Zbc: +localparam [0:0] EXTENSION_ZBKC = |EXTENSION_ZBC; +// Zca is (instructions-wise) a subset of C: +localparam [0:0] EXTENSION_ZCA = |EXTENSION_C; +// We have data-independent timing for all Zkt instructions except for mulh, +// mulhsu executed on the sequential multiply/divide circuit: +localparam [0:0] EXTENSION_ZKT = !(|EXTENSION_M && !(|MUL_FAST && |MULH_FAST)); +// Zmmul is (instructions-wise) a subset of M: +localparam [0:0] EXTENSION_ZMMUL = |EXTENSION_M; + +localparam [31:0] h3misa_standard_len = 32'd77; + +localparam [127:0] h3misa_standard_extensions = + ({127'd0, |EXTENSION_A } << 0 ) | + ({127'd0, |EXTENSION_B } << 1 ) | + ({127'd0, |EXTENSION_C } << 2 ) | + ({127'd0, |EXTENSION_E } << 4 ) | + ({127'd0, |EXTENSION_I } << 8 ) | + ({127'd0, |EXTENSION_M } << 12) | + ({127'd0, |EXTENSION_ZBA } << 27) | + ({127'd0, |EXTENSION_ZBB } << 28) | + ({127'd0, |EXTENSION_ZBC } << 29) | + ({127'd0, |EXTENSION_ZBKB } << 30) | + ({127'd0, |EXTENSION_ZBC } << 31) | + ({127'd0, |EXTENSION_ZBKX } << 32) | + ({127'd0, |EXTENSION_ZBS } << 33) | + ({127'd0, |EXTENSION_ZKT } << 46) | + ({127'd0, |EXTENSION_C } << 66) | + ({127'd0, |EXTENSION_ZCB } << 67) | + ({127'd0, |EXTENSION_ZILSD } << 72) | + ({127'd0, |EXTENSION_ZCLSD } << 73) | + ({127'd0, |EXTENSION_ZCMP } << 74) | + ({127'd0, |EXTENSION_ZIFENCEI} << 75) | + ({127'd0, |EXTENSION_ZMMUL } << 76) | + 128'd0; + +localparam [31:0] h3misa_custom_len = 32'd5; + +localparam [255:0] h3misa_custom_extensions = { + 32'd0, // Reserved + 32'd0, // Reserved + 32'd0, // Reserved + 32'h01_00_00_00 & {32{|EXTENSION_XH3BEXTM}}, // Xh3bextm + 32'h01_00_00_00 & {32{|EXTENSION_XH3POWER}}, // Xh3power + 32'h01_00_00_00 & {32{|EXTENSION_XH3PMPM}}, // Xh3pmpm + 32'h01_00_00_00 & {32{|EXTENSION_XH3IRQ}}, // Xh3irq + 32'h01_00_00_00 & {32{|CSR_M_MANDATORY}} // Xh3misa +}; + +wire [63:0] h3misa_info = { + h3misa_custom_len, + h3misa_standard_len +}; + +wire [31:0] h3misa_rdata = + !wdata[10] ? h3misa_standard_extensions[32 * wdata[1:0] +: 32] : + !wdata[8] ? h3misa_info [32 * wdata[ 0] +: 32] : + h3misa_custom_extensions [32 * wdata[2:0] +: 32]; + +// ---------------------------------------------------------------------------- +// Counters + +reg mcountinhibit_cy; +reg mcountinhibit_ir; + +reg [XLEN-1:0] mcycleh; +reg [XLEN-1:0] mcycle; +reg [XLEN-1:0] minstreth; +reg [XLEN-1:0] minstret; + +// Writing to either half suppresses increment of the entire 64-bit register. +// It doesn't just replace the value of one 32-bit half. See: +// https://github.com/riscv/riscv-isa-manual/issues/1255 +wire mcycle_stopped = mcountinhibit_cy || debug_mode || wen_m_mode && ( + addr == MCYCLEH || addr == MCYCLE +); + +wire minstret_stopped = mcountinhibit_ir || debug_mode || wen_m_mode && ( + addr == MINSTRETH || addr == MINSTRET +); + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mcycleh <= X0; + mcycle <= X0; + minstreth <= X0; + minstret <= X0; + // Counters inhibited by default to save energy + mcountinhibit_cy <= 1'b1; + mcountinhibit_ir <= 1'b1; + end else begin + if (!mcycle_stopped) begin + {mcycleh, mcycle} <= + ({mcycleh, mcycle} + 64'd1) & {2*XLEN{|CSR_COUNTER}}; + end + if (instr_ret && !minstret_stopped) begin + {minstreth, minstret} <= + ({minstreth, minstret} + 64'd1) & {2*XLEN{|CSR_COUNTER}}; + end + if (wen_m_mode) begin + if (addr == MCYCLEH) begin + mcycleh <= wdata_update & {XLEN{|CSR_COUNTER}}; + end else if (addr == MCYCLE) begin + mcycle <= wdata_update & {XLEN{|CSR_COUNTER}}; + end else if (addr == MINSTRETH) begin + minstreth <= wdata_update & {XLEN{|CSR_COUNTER}}; + end else if (addr == MINSTRET) begin + minstret <= wdata_update & {XLEN{|CSR_COUNTER}}; + end else if (addr == MCOUNTINHIBIT) begin + {mcountinhibit_ir, mcountinhibit_cy} <= {wdata_update[2], wdata_update[0]} | {2{~|CSR_COUNTER}}; + end + end + end +end + +// Note we still implement tm, even though the time/timeh CSRs are +// unimplemented. The tm bit provides a standard way to configure whether +// M-mode software permits the trapped access to reach the timer registers. +// (This is in fact required by the RVM-CSI platform spec.) +reg mcounteren_cy; +reg mcounteren_tm; +reg mcounteren_ir; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mcounteren_cy <= 1'b0; + mcounteren_tm <= 1'b0; + mcounteren_ir <= 1'b0; + end else if (wen_m_mode && addr == MCOUNTEREN) begin + mcounteren_cy <= wdata_update[0] && |CSR_COUNTER && |U_MODE; + mcounteren_tm <= wdata_update[1] && |CSR_COUNTER && |U_MODE; + mcounteren_ir <= wdata_update[2] && |CSR_COUNTER && |U_MODE; + end +end + +// ---------------------------------------------------------------------------- +// Debug-mode CSRs + +// The following DCSR bits are read/writable as normal: +// - ebreakm (bit 15) +// - ebreaku (bit 12) if U-mode is supported +// - step (bit 2) +// The following are read-only volatile: +// - cause (bits 8:6) +// All others are hardwired constants. + +reg dcsr_ebreakm; +reg dcsr_ebreaku; +reg dcsr_step; +reg [2:0] dcsr_cause; +wire [2:0] dcause_next; + +localparam DCSR_CAUSE_EBREAK = 3'h1; +localparam DCSR_CAUSE_TRIGGER = 3'h2; +localparam DCSR_CAUSE_HALTREQ = 3'h3; +localparam DCSR_CAUSE_STEP = 3'h4; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + dcsr_ebreakm <= 1'b0; + dcsr_ebreaku <= 1'b0; + dcsr_step <= 1'b0; + dcsr_cause <= 3'h0; + end else begin + // Note we can use the ungated write enable in debug mode since a CSR + // write to a valid D-mode CSR address is guaranteed not to generate + // an exception (of any kind, including PMP) in D-mode. + if (debug_mode && wen_raw && addr == DCSR) begin + {dcsr_ebreakm, dcsr_ebreaku, dcsr_step} <= { + wdata_update[15], + wdata_update[12] && U_MODE, + wdata_update[2] + } & {3{|DEBUG_SUPPORT}}; + end + if (enter_debug_mode) begin + dcsr_cause <= dcause_next & {3{|DEBUG_SUPPORT}}; + end + end +end + +// DPC is mapped to the real PC in the decode module. We set it to the +// exception return address when entering Debug Mode, and whilst in Debug +// Mode we can write to it as though it were a CSR. Note only IALIGN'd values +// can be written to dpc. +assign debug_dpc_wdata = (debug_mode ? wdata_update : mepc_in) & (~X0 << 2 - EXTENSION_C); +assign debug_dpc_wen = debug_mode ? wen_raw && addr == DPC : enter_debug_mode; + +// Debug Module's data0 register is mapped into the core's CSR space: +assign dbg_data0_wdata = wdata; +assign dbg_data0_wen = debug_mode && wen_raw && addr == DMDATA0; + +reg tcontrol_mte; +reg tcontrol_mpte; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + tcontrol_mpte <= 1'b0; + tcontrol_mte <= 1'b0; + end else if (wen_m_mode && addr == TCONTROL) begin + tcontrol_mpte <= wdata_update[7] && DEBUG_SUPPORT; + tcontrol_mte <= wdata_update[3] && DEBUG_SUPPORT; + end else if (DEBUG_SUPPORT && trapreg_update_enter) begin + tcontrol_mte <= 1'b0; + tcontrol_mpte <= tcontrol_mte; + end else if (DEBUG_SUPPORT && trapreg_update_exit) begin + tcontrol_mte <= tcontrol_mpte; + // Unlike mstatus.mie/mpie, tcontrol.mpte is unchanged by trap exit. + end +end + +// ---------------------------------------------------------------------------- +// Read port + detect addressing of unmapped CSRs +// ---------------------------------------------------------------------------- + +reg decode_match; + +// Address match conditions -- certain CSRs are inaccessible if their +// conditions are not met: +wire match_drw = DEBUG_SUPPORT && debug_mode; +wire match_mrw = m_mode || debug_mode; +wire match_mro = (m_mode || debug_mode) && !wen_soon; +wire match_urw = U_MODE && 1'b1; +wire match_uro = U_MODE && !wen_soon; + +always @ (*) begin + decode_match = 1'b0; + rdata = {XLEN{1'b0}}; + pmp_cfg_wen = 1'b0; + trig_cfg_wen = 1'b0; + case (addr) + + // ------------------------------------------------------------------------ + // Mandatory CSRs + + MISA: if (CSR_M_MANDATORY) begin + // WARL, so it is legal to be tied constant + decode_match = match_mrw; + rdata = MISA_VAL; + end + MVENDORID: if (CSR_M_MANDATORY) begin + decode_match = match_mro; + rdata = MVENDORID_VAL; + end + MARCHID: if (CSR_M_MANDATORY) begin + decode_match = match_mro; + // Hazard3's open source architecture ID + rdata = 32'd27; + end + MIMPID: if (CSR_M_MANDATORY) begin + decode_match = match_mro; + rdata = mimpid_rdata; + end + MHARTID: if (CSR_M_MANDATORY) begin + decode_match = match_mro; + rdata = mhartid_val; + end + + MCONFIGPTR: if (CSR_M_MANDATORY) begin + decode_match = match_mro; + rdata = MCONFIGPTR_VAL; + end + + MSTATUS: if (CSR_M_MANDATORY || CSR_M_TRAP) begin + decode_match = match_mrw; + rdata = { + 1'b0, // Never any dirty state besides GPRs + 8'd0, // (WPRI) + 1'b0, // TSR (Trap SRET), tied 0 if no S mode. + mstatus_tw, // TW (Timeout Wait) + 1'b0, // TVM (trap virtual memory), tied 0 if no S mode. + 1'b0, // MXR (Make eXecutable Readable), tied 0 if no S mode. + 1'b0, // SUM, tied 0 if no S mode + mstatus_mprv, // MPRV (modify privilege) + 4'd0, // XS, FS always "off" (no extension state to clear!) + {2{mstatus_mpp}}, // MPP (M-mode previous privilege), only M and U supported + 2'd0, // (WPRI) + 1'b0, // SPP, tied 0 if S mode not supported + mstatus_mpie, // Previous interrupt enable + 3'd0, // No S, U + mstatus_mie, // Interrupt enable + 3'd0 // No S, U + }; + end + + // MSTATUSH is all zeroes (fields are MBE and SBE, which are zero because + // we are pure little-endian.) Prior to priv-1.12 MSTATUSH could be left + // unimplemented in this case, but now it must be decoded even if + // hardwired to 0. + MSTATUSH: if (CSR_M_MANDATORY || CSR_M_TRAP) begin + decode_match = match_mrw; + end + + // MEDELEG, MIDELEG should not exist for no-S-mode implementations. Will + // raise illegal instruction exception if accessed. + + // MENVCFG is seemingly mandatory as of M-mode v1.12, if U-mode is + // implemented. All of its fields are tied to 0 in our implementation. + MENVCFGH: if (U_MODE) begin + decode_match = match_mrw; + end + MENVCFG: if (U_MODE) begin + decode_match = match_mrw; + end + + // ------------------------------------------------------------------------ + // Trap-handling CSRs + + // This is a 32 bit synthesised register with set/clear/write/read, don't + // turn it on unless we really have to + MSCRATCH: if (CSR_M_TRAP && CSR_M_MANDATORY) begin + decode_match = match_mrw; + rdata = mscratch; + end + + MEPC: if (CSR_M_TRAP) begin + decode_match = match_mrw; + rdata = mepc; + end + + MCAUSE: if (CSR_M_TRAP) begin + decode_match = match_mrw; + rdata = { + mcause_irq, // Sign bit is 1 for IRQ, 0 for exception + {27{1'b0}}, // Padding + mcause_code[3:0] // Enough for 16 external IRQs, which is all we have room for in mip/mie + }; + end + + MTVAL: if (CSR_M_TRAP) begin + decode_match = match_mrw; + // Hardwired to 0 + end + + MIE: if (CSR_M_TRAP) begin + decode_match = match_mrw; + rdata = mie; + end + + MIP: if (CSR_M_TRAP) begin + // Writes are permitted, but ignored. + decode_match = match_mrw; + rdata = mip; + end + + MTVEC: if (CSR_M_TRAP) begin + decode_match = match_mrw; + rdata = { + mtvec[XLEN-1:2], // BASE + 1'b0, // Reserved mode bit gets WARL'd to 0 + irq_vector_enable // MODE is vectored (2'h1) or direct (2'h0) + }; + end + + // ------------------------------------------------------------------------ + // Counter CSRs + + // Get the tied WARLs out the way first + MHPMCOUNTER3: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER4: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER5: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER6: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER7: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER8: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER9: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER10: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER11: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER12: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER13: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER14: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER15: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER16: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER17: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER18: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER19: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER20: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER21: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER22: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER23: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER24: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER25: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER26: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER27: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER28: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER29: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER30: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER31: if (CSR_COUNTER) begin decode_match = match_mrw; end + + MHPMCOUNTER3H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER4H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER5H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER6H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER7H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER8H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER9H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER10H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER11H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER12H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER13H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER14H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER15H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER16H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER17H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER18H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER19H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER20H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER21H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER22H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER23H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER24H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER25H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER26H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER27H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER28H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER29H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER30H: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMCOUNTER31H: if (CSR_COUNTER) begin decode_match = match_mrw; end + + MHPMEVENT3: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT4: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT5: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT6: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT7: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT8: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT9: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT10: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT11: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT12: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT13: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT14: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT15: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT16: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT17: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT18: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT19: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT20: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT21: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT22: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT23: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT24: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT25: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT26: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT27: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT28: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT29: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT30: if (CSR_COUNTER) begin decode_match = match_mrw; end + MHPMEVENT31: if (CSR_COUNTER) begin decode_match = match_mrw; end + + MCOUNTINHIBIT: if (CSR_COUNTER) begin + decode_match = match_mrw; + rdata = { + 29'd0, + mcountinhibit_ir, + 1'b0, + mcountinhibit_cy + }; + end + + MCYCLE: if (CSR_COUNTER) begin + decode_match = match_mrw; + rdata = mcycle; + end + MINSTRET: if (CSR_COUNTER) begin + decode_match = match_mrw; + rdata = minstret; + end + + MCYCLEH: if (CSR_COUNTER) begin + decode_match = match_mrw; + rdata = mcycleh; + end + MINSTRETH: if (CSR_COUNTER) begin + decode_match = match_mrw; + rdata = minstreth; + end + + MCOUNTEREN: if (CSR_COUNTER && U_MODE) begin + decode_match = match_mrw; + rdata = { + 29'd0, + mcounteren_ir, + mcounteren_tm, + mcounteren_cy + }; + end + + // ------------------------------------------------------------------------ + // PMP CSRs (bridge to PMP config interface) + + // If PMP is present, all 16 registers are present, but some may be WARL'd + // to 0 depending on how many regions are actually implemented. + PMPCFG0: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPCFG1: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPCFG2: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPCFG3: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR0: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR1: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR2: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR3: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR4: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR5: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR6: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR7: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR8: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR9: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR10: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR11: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR12: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR13: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR14: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR15: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + + // MSECCFG is strictly optional, and we don't implement any of its + // features (ePMP etc) so we don't decode it. + + // ------------------------------------------------------------------------ + // U-mode CSRs + + // The read-only counters are always visible to M mode, and are visible to + // U mode if the corresponding mcounteren bit is set. + CYCLE: if (CSR_COUNTER) begin + decode_match = mcounteren_cy ? match_uro : match_mro; + rdata = mcycle; + end + CYCLEH: if (CSR_COUNTER) begin + decode_match = mcounteren_cy ? match_uro : match_mro; + rdata = mcycleh; + end + + INSTRET: if (CSR_COUNTER) begin + decode_match = mcounteren_ir ? match_uro : match_mro; + rdata = minstret; + end + INSTRETH: if (CSR_COUNTER) begin + decode_match = mcounteren_ir ? match_uro : match_mro; + rdata = minstreth; + end + + // ------------------------------------------------------------------------ + // Trigger Module CSRs + + // When debug support is enabled, we always have basic trigger support + // (instruction count, interrupt and exception). Breakpoints are optional. + TSELECT: if (DEBUG_SUPPORT) begin + decode_match = match_mrw; + trig_cfg_wen = match_mrw && wen; + rdata = trig_cfg_rdata; + end + + TDATA1: if (DEBUG_SUPPORT) begin + decode_match = match_mrw; + trig_cfg_wen = match_mrw && wen; + rdata = trig_cfg_rdata; + end + + TDATA2: if (DEBUG_SUPPORT) begin + decode_match = match_mrw; + trig_cfg_wen = match_mrw && wen; + rdata = trig_cfg_rdata; + end + + TDATA3: if (DEBUG_SUPPORT) begin + // Unimplemented but must be accessible, so hardwired to 0. + decode_match = match_mrw; + rdata = 32'h00000000; + end + + TINFO: if (DEBUG_SUPPORT) begin + // Note tinfo is a read-write CSR (writes don't trap) even though it + // is entirely read-only. + decode_match = match_mrw; + trig_cfg_wen = match_mrw && wen; + rdata = trig_cfg_rdata; + end + + TCONTROL: if (DEBUG_SUPPORT) begin + decode_match = match_mrw; + rdata = { + 24'h0, + tcontrol_mpte, + 3'h0, + tcontrol_mte, + 3'h0 + }; + end + + // ------------------------------------------------------------------------ + // Debug CSRs + + DCSR: if (DEBUG_SUPPORT) begin + decode_match = match_drw; + rdata = { + 4'h4, // xdebugver = 4, for 0.13.2 debug spec + 12'd0, // reserved + dcsr_ebreakm, + 2'h0, // No mode 2/1 + dcsr_ebreaku, + 1'b0, // stepie = 0, no interrupts in single-step mode + 1'b1, // stopcount = 1, no counter increment in debug mode + 1'b1, // stoptime = 0, no core-local timer increment in debug mode + dcsr_cause, + 1'b0, // reserved + 1'b0, // mprven = 0, debugger always acts as M-mode + 1'b0, // nmip = 0, we have no NMI + dcsr_step, + {2{m_mode}} // priv = M or U, report the core state directly + }; + end + + DPC: if (DEBUG_SUPPORT) begin + decode_match = match_drw; + rdata = debug_dpc_rdata; + end + + DMDATA0: if (DEBUG_SUPPORT) begin + decode_match = match_drw; + rdata = dbg_data0_rdata; + end + + // ------------------------------------------------------------------------ + // Custom CSRs + + PMPCFGM0: if (PMP_REGIONS > 0 && EXTENSION_XH3PMPM) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + + MEIEA: if (CSR_M_TRAP && EXTENSION_XH3IRQ) begin + decode_match = match_mrw; + rdata = irq_ctrl_rdata; + end + + MEIPA: if (CSR_M_TRAP && EXTENSION_XH3IRQ) begin + decode_match = match_mrw; + rdata = irq_ctrl_rdata; + end + + MEIFA: if (CSR_M_TRAP && EXTENSION_XH3IRQ) begin + decode_match = match_mrw; + rdata = irq_ctrl_rdata; + end + + MEIPRA: if (CSR_M_TRAP && EXTENSION_XH3IRQ) begin + decode_match = match_mrw; + rdata = irq_ctrl_rdata; + end + + MEINEXT: if (CSR_M_TRAP && EXTENSION_XH3IRQ) begin + decode_match = match_mrw; + rdata = irq_ctrl_rdata; + end + + MEICONTEXT: if (CSR_M_TRAP && EXTENSION_XH3IRQ) begin + decode_match = match_mrw; + rdata = irq_ctrl_rdata; + end + + MSLEEP: if (EXTENSION_XH3POWER) begin + decode_match = match_mrw; + rdata = { + 29'h0, + msleep_sleeponblock, + msleep_powerdown, + msleep_deepsleep + }; + end + + H3MISA: if (CSR_M_MANDATORY) begin + decode_match = match_mrw; + rdata = h3misa_rdata; + end + + default: begin end + endcase +end + +assign illegal = (wen_soon || ren_soon) && !decode_match; + +// Trigger refetch of sequentially-next instructions on certain CSR updates: +assign write_is_fetch_ordered = wen_raw && !debug_mode && ( + (PMP_REGIONS > 0 && addr >= PMPADDR0 && addr <= PMPADDR15) || + (PMP_REGIONS > 0 && addr >= PMPCFG0 && addr <= PMPCFG3 ) || + (DEBUG_SUPPORT > 0 && addr == TDATA1 ) || + (DEBUG_SUPPORT > 0 && addr == TDATA2 ) || + (DEBUG_SUPPORT > 0 && addr == TCONTROL ) +); + +// ---------------------------------------------------------------------------- +// Debug run/halt + +// req_resume_prev is to cut an in->out path from request to trap addr. +reg have_just_reset; +reg step_halt_req; +reg dbg_req_resume_prev; +reg dbg_req_halt_prev; +reg dbg_req_halt_on_reset_sticky; +reg pending_dbg_resume_prev; + +wire pending_dbg_resume = (pending_dbg_resume_prev || dbg_req_resume_prev) && debug_mode; + +// Note exception entry is also considered a step -- in this case we are +// supposed to take the trap and then break to debug mode before executing the +// first trap instruction. *IRQ* entry does not occur when step is 1 (because +// we hardwire dcsr.stepie to 0) +wire step_event = instr_ret || (trap_enter_vld && trap_enter_rdy); + +// Halt request input register needs to always be clocked, because a WFI needs +// to fall through if the debugger requests halt of a sleeping core. +always @ (posedge clk_always_on or negedge rst_n) begin + if (!rst_n) begin + dbg_req_halt_prev <= 1'b0; + end else begin + // Just a delayed version of the request from outside of the core. + // Delay is fine because the DM awaits ack before deasserting. + dbg_req_halt_prev <= dbg_req_halt && DEBUG_SUPPORT != 0; + end +end + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + have_just_reset <= |DEBUG_SUPPORT; + dbg_req_halt_on_reset_sticky <= 1'b0; + step_halt_req <= 1'b0; + dbg_req_resume_prev <= 1'b0; + pending_dbg_resume_prev <= 1'b0; + end else if (DEBUG_SUPPORT) begin + have_just_reset <= 1'b0; + + // One-cycle delay on assertion of reset halt req is harmless because + // of a matching delay on the first instruction fetch (reset_holdoff). + if (have_just_reset && dbg_req_halt_on_reset) begin + dbg_req_halt_on_reset_sticky <= 1'b1; + end else if (enter_debug_mode) begin + dbg_req_halt_on_reset_sticky <= 1'b0; + end + + dbg_req_resume_prev <= dbg_req_resume; + + if (debug_mode) begin + step_halt_req <= 1'b0; + end else if (dcsr_step && step_event) begin + step_halt_req <= 1'b1; + end + + pending_dbg_resume_prev <= pending_dbg_resume; + end +end + +// We can enter the halted state in an IRQ-like manner (squeeze in between the +// instructions in stage 2 and stage 3) or in an exception-like manner +// (replace the instruction in stage 3). +// +// Halt request and step-break are IRQ-like. We need to be careful when a halt +// request lines up with an instruction in M which either has generated an +// exception (e.g. an ecall) or may yet generate an exception (a load). In +// this case the correct thing to do is to: +// +// - Squash whatever instruction may be in X, and inhibit X PC increment +// - Wait until after the exception entry is taken (or the load/store +// completes successfully) +// - Immediately trigger an IRQ-like debug entry. +// +// This ensures the debugger sees mcause/mepc set correctly, with dpc pointing +// to the handler entry point, if the instruction excepts. + +wire exception_req_any; +wire halt_delayed_by_exception = exception_req_any || loadstore_dphase_pending; + +wire want_halt_except = DEBUG_SUPPORT && !debug_mode && ( + dcsr_ebreakm && m_mode && except == EXCEPT_EBREAK || + dcsr_ebreaku && !m_mode && except == EXCEPT_EBREAK || + except_to_d_mode && except == EXCEPT_EBREAK +); + +// Note exception-like causes (trigger, ebreak) are higher priority than +// IRQ-like. +// +// We must mask halt_req with delay_irq_entry (true on cycle 2 and beyond of +// load/store address phase) because at that point we can't suppress the bus +// access. Others are fine because they are never mid-instruction in stage 2. +wire want_halt_irq_if_no_exception = DEBUG_SUPPORT && !debug_mode && !want_halt_except && ( + (dbg_req_halt_prev && !delay_irq_entry) || + dbg_req_halt_on_reset_sticky || + step_halt_req +); + +// Exception (or potential exception due to load/store) in M delays halt +// entry. The halt intention still blocks X, so we can't get blocked forever +// by a string of load/stores. This is just here to get sequencing between an +// exception (or potential exception) in M, and debug mode entry. +wire want_halt_irq = want_halt_irq_if_no_exception && !halt_delayed_by_exception; + +assign dcause_next = + except == EXCEPT_EBREAK && except_to_d_mode ? 3'h2 : // trigger (priority 4) + except == EXCEPT_EBREAK ? 3'h1 : // ebreak (priority 3) + dbg_req_halt_prev || dbg_req_halt_on_reset ? 3'h3 : // halt or reset-halt (priority 1, 2) + 3'h4; // single-step (priority 0) + +wire trap_is_debug_entry = |DEBUG_SUPPORT && !debug_mode && (want_halt_irq || want_halt_except); +assign enter_debug_mode = trap_is_debug_entry && trap_enter_rdy; + +assign exit_debug_mode = debug_mode && pending_dbg_resume && trap_enter_rdy; + +// Report back to DM instruction injector to tell it its instruction sequence +// has finished (ebreak) or crashed out +assign dbg_instr_caught_ebreak = debug_mode && except == EXCEPT_EBREAK && trap_enter_rdy; + +// Note we exclude ebreak from here regardless of dcsr.ebreakm, since we are +// already in debug mode at this point +assign dbg_instr_caught_exception = debug_mode && except != EXCEPT_NONE && + except != EXCEPT_EBREAK && trap_enter_rdy; + +// ---------------------------------------------------------------------------- +// Trap request generation + +reg irq_software_r; +reg irq_timer_r; + +// Always clocked, as it's used to generate a wakeup. +always @ (posedge clk_always_on or negedge rst_n) begin + if (!rst_n) begin + irq_software_r <= 1'b0; + irq_timer_r <= 1'b0; + end else begin + irq_software_r <= irq_software; + irq_timer_r <= irq_timer; + end +end + +assign mip = { + 20'h0, // Reserved + external_irq_pending, // meip, Global pending bit for external IRQs + 3'h0, // Reserved + irq_timer_r, // mtip, interrupt from memory-mapped timer peripheral + 3'h0, // Reserved + irq_software_r, // msip, software interrupt from memory-mapped register + 3'h0 // Reserved +}; + +wire irq_active = |(mip & mie) && (mstatus_mie || !m_mode) && !dcsr_step; + +// WFI clear respects individual interrupt enables but ignores mstatus.mie. +// Additionally, wfi is treated as a nop during single-stepping and D-mode. +// Note that the IRQs and debug halt request input registers are clocked by +// clk_always_on, so that a wakeup can be generated when asleep. Note also +// that want_halt_irq_if_no_exception can be masked while asleep, so also +// feed in the raw halt request as a wakeup source. +assign pwr_wfi_wakeup_req = |(mip & mie) || dcsr_step || debug_mode || + want_halt_irq_if_no_exception || dbg_req_halt_prev; + +// Priority order from priv spec: external > software > timer +wire [3:0] standard_irq_num = + mip[11] && mie[11] ? 4'd11 : + mip[3] && mie[3] ? 4'd3 : + mip[7] && mie[7] ? 4'd7 : 4'd0; + +// ebreak may be treated as a halt-to-debugger or a regular M-mode exception, +// depending on dcsr.ebreakm. +assign exception_req_any = except != EXCEPT_NONE && !( + except == EXCEPT_EBREAK && (m_mode ? dcsr_ebreakm : dcsr_ebreaku) +); + +wire [3:0] mcause_irq_num = irq_active ? standard_irq_num : 4'd0; + +wire [3:0] vector_sel = !exception_req_any && irq_vector_enable ? mcause_irq_num : 4'd0; + +// Note in the refetch case we're relying on the aliasing of dpc and pc +// (The intent is to fetch the sequentially-next instruction again) +assign trap_addr = + except == EXCEPT_MRET ? mepc : + except == EXCEPT_REFETCH ? debug_dpc_rdata : + pending_dbg_resume ? debug_dpc_rdata : + mtvec | {26'h0, vector_sel, 2'h0}; + +// Check for exception-like or IRQ-like trap entry; any debug mode entry takes +// priority over any regular trap. +assign trap_is_irq = DEBUG_SUPPORT && (want_halt_except || want_halt_irq) ? + !want_halt_except : !exception_req_any; + +// When there is a loadstore dphase pending, we block off the IRQ trap +// request, but still assert the trap_enter_soon flag. This blocks issue of +// further load/stores which have not yet been locked into aphase, so the IRQ +// can eventually go through. It's not safe to enter an IRQ when a dphase is +// in progress, because that dphase could subsequently except, and sample the +// IRQ vector PC instead of the load/store instruction PC. + +assign trap_enter_vld = + CSR_M_TRAP && (exception_req_any || + !delay_irq_entry && !debug_mode && irq_active && !loadstore_dphase_pending) || + DEBUG_SUPPORT && ( + (!delay_irq_entry && want_halt_irq) || want_halt_except || pending_dbg_resume); + +assign trap_enter_soon = trap_enter_vld || ( + |DEBUG_SUPPORT && want_halt_irq_if_no_exception || + !delay_irq_entry && !debug_mode && irq_active && loadstore_dphase_pending +); + +assign mcause_irq_next = !exception_req_any; +assign mcause_code_next = exception_req_any ? except : mcause_irq_num; + +// Report trap entry to trigger unit +assign trig_event_exception = |DEBUG_SUPPORT && trapreg_update_enter && !trap_is_irq; +assign trig_event_interrupt = |DEBUG_SUPPORT && trapreg_update_enter && trap_is_irq; +assign trig_event_trap_cause = {4{|DEBUG_SUPPORT}} & mcause_code_next; + +// ---------------------------------------------------------------------------- +// Privilege state outputs + +assign pmp_cfg_addr = addr; +assign pmp_cfg_wdata = wdata_update; + +assign trig_cfg_addr = addr; +assign trig_cfg_wdata = wdata_update; +assign trig_m_en = tcontrol_mte; + +// Effective privilege for execution. Used for: +// - Privilege level of branch target fetches (frontend keeps fetch privilege +// constant during sequential fetch) +// - Checking PC against PMP execute permission + +assign m_mode_execution = !U_MODE || debug_mode || m_mode; + +// Effective privilege for trap entry. Used for: +// - Privilege level of trap target fetches (frontend keeps fetch privilege +// constant during sequential fetch) + +assign m_mode_trap_entry = !U_MODE || ( + except == EXCEPT_MRET ? mstatus_mpp : 1'b1 +); + +// Effective privilege for load/stores. Used for: +// - Privilege level of load/stores on the bus +// - Checking load/stores against PMP read/write permission + +assign m_mode_loadstore = !U_MODE || debug_mode || ( + mstatus_mprv ? mstatus_mpp : m_mode +); + +// ---------------------------------------------------------------------------- +// Properties + +`ifdef HAZARD3_ASSERTIONS + +// Keep track of whether we are in a trap (only for formal property purposes) +reg in_trap; + +always @ (posedge clk or negedge rst_n) + if (!rst_n) + in_trap <= 1'b0; + else + in_trap <= (in_trap || (trap_enter_vld && trap_enter_rdy)) + && !(trap_enter_vld && trap_enter_rdy && except == EXCEPT_MRET); + +always @ (posedge clk) begin +`ifdef RISCV_FORMAL + // Assume there are no nested exceptions, to stop riscv-formal from doing + // annoying things like stopping instructions from retiring by repeatedly + // feeding in invalid instructions + if (in_trap) + assume(except == EXCEPT_NONE || except == EXCEPT_MRET); + + // Note the actual IRQ registers have been moved into hazard3_irq_ctrl + // (and are now optional) so repeat them here for the property + reg [NUM_IRQS-1:0] irq_r; + always @ (posedge clk_always_on or negedge rst_n) begin + if (!rst_n) begin + irq_r <= {NUM_IRQS{1'b0}}; + end else begin + irq_r <= irq; + end + end + + // Assume IRQs are not deasserted on cycles where exception entry does not + // take place + if (!trap_enter_rdy) + assume(~|(irq_r & ~irq)); +`endif + + // Make sure CSR accesses are flushed + if (trap_enter_vld && trap_enter_rdy) + assert(!(wen || ren)); + + // Writing to CSR on cycle after trap entry -- should be impossible, a CSR + // access instruction should have been flushed or moved down to stage 3, and + // no fetch could have arrived by now + if ($past(trap_enter_vld && trap_enter_rdy)) + assert(!wen); + + // Should be impossible to get into the trap and exit immediately: + if (in_trap && !$past(in_trap)) + assert(except != EXCEPT_MRET); + + // Should be impossible to get to another mret so soon after exiting: + if ($past(except == EXCEPT_MRET && trap_enter_vld && trap_enter_rdy)) + assert(except != EXCEPT_MRET); + + // Must be impossible to enter two traps on consecutive cycles. Most importantly: + // + // - IRQ -> Except: no new instruction could have been fetched by this point, + // and an exception by e.g. a left-behind store data phase would sample the + // wrong PC. + // + // - Except -> IRQ: would need to re-set mstatus.mie first, shouldn't happen + // + // Sole exclusion is mret. We can return from an interrupt/exception and take + // a new interrupt on the next cycle. + + if ($past(trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET)) + assert(!(trap_enter_vld && trap_enter_rdy)); + + // Just to stress that this is the the only case: + if (trap_enter_vld && trap_enter_rdy && $past(trap_enter_vld && trap_enter_rdy)) + assert($past(except == EXCEPT_MRET)); + + if (rst_n && $past(trap_enter_vld && !trap_enter_rdy && !trap_is_irq)) begin + // Exception which didn't go through should not disappear + assert(trap_enter_vld); + // Exception should not be replaced by IRQ + assert(!trap_is_irq); + end + + // This must be a breakpoint exception (presumably from the trigger unit). + if (except_to_d_mode) begin + assert(except == EXCEPT_EBREAK); + end + + // Exceptions should squash load/stores before they reach dphase. + if (except != EXCEPT_NONE && !(except == EXCEPT_LOAD_FAULT || except == EXCEPT_STORE_FAULT)) begin + assert(!loadstore_dphase_pending); + end + + // Debug entries are trap entries! (we should also assert in core.v that + // trap entries are never coincident with active load/store address + // phases, which gives some confidence that debug entry does not gobble + // load/stores.) + if (enter_debug_mode) begin + assert(trap_enter_vld); + end +end + +`endif + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_csr_addr.vh b/vendor/Hazard3/hdl/hazard3_csr_addr.vh new file mode 100644 index 0000000..035af00 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_csr_addr.vh @@ -0,0 +1,204 @@ +/*****************************************************************************\ +| Copyright (C) 2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// List of addresses for CSRs implemented by Hazard3, including custom CSRs. + +// ---------------------------------------------------------------------------- +// M-mode CSRs + +// Machine Information Registers (RO) +localparam MVENDORID = 12'hf11; // Vendor ID. +localparam MARCHID = 12'hf12; // Architecture ID. +localparam MIMPID = 12'hf13; // Implementation ID. +localparam MHARTID = 12'hf14; // Hardware thread ID. +localparam MCONFIGPTR = 12'hf15; // Pointer to configuration data structure. + +// Machine Trap Setup (RW) +localparam MSTATUS = 12'h300; // Machine status register. +localparam MSTATUSH = 12'h310; // As of priv-1.12 this must be present even if tied 0. +localparam MISA = 12'h301; // ISA and extensions +localparam MEDELEG = 12'h302; // Machine exception delegation register. +localparam MIDELEG = 12'h303; // Machine interrupt delegation register. +localparam MIE = 12'h304; // Machine interrupt-enable register. +localparam MTVEC = 12'h305; // Machine trap-handler base address. +localparam MCOUNTEREN = 12'h306; // Machine counter enable. + +// Machine Trap Handling (RW) +localparam MSCRATCH = 12'h340; // Scratch register for machine trap handlers. +localparam MEPC = 12'h341; // Machine exception program counter. +localparam MCAUSE = 12'h342; // Machine trap cause. +localparam MTVAL = 12'h343; // Machine bad address or instruction. +localparam MIP = 12'h344; // Machine interrupt pending. + +// Machine Memory Protection (RW) +localparam PMPCFG0 = 12'h3a0; // Physical memory protection configuration. +localparam PMPCFG1 = 12'h3a1; // Physical memory protection configuration, RV32 only. +localparam PMPCFG2 = 12'h3a2; // Physical memory protection configuration. +localparam PMPCFG3 = 12'h3a3; // Physical memory protection configuration, RV32 only. +localparam PMPADDR0 = 12'h3b0; // Physical memory protection address register. +localparam PMPADDR1 = 12'h3b1; // ... +localparam PMPADDR2 = 12'h3b2; +localparam PMPADDR3 = 12'h3b3; +localparam PMPADDR4 = 12'h3b4; +localparam PMPADDR5 = 12'h3b5; +localparam PMPADDR6 = 12'h3b6; +localparam PMPADDR7 = 12'h3b7; +localparam PMPADDR8 = 12'h3b8; +localparam PMPADDR9 = 12'h3b9; +localparam PMPADDR10 = 12'h3ba; +localparam PMPADDR11 = 12'h3bb; +localparam PMPADDR12 = 12'h3bc; +localparam PMPADDR13 = 12'h3bd; +localparam PMPADDR14 = 12'h3be; +localparam PMPADDR15 = 12'h3bf; + +localparam MSECCFG = 12'h747; +localparam MSECCFGH = 12'h757; + +// Performance counters (RW) +localparam MCYCLE = 12'hb00; // Raw cycles since start of day +localparam MINSTRET = 12'hb02; // Instruction retire count since start of day +localparam MHPMCOUNTER3 = 12'hb03; // WARL (we tie to 0) +localparam MHPMCOUNTER4 = 12'hb04; // WARL (we tie to 0) +localparam MHPMCOUNTER5 = 12'hb05; // WARL (we tie to 0) +localparam MHPMCOUNTER6 = 12'hb06; // WARL (we tie to 0) +localparam MHPMCOUNTER7 = 12'hb07; // WARL (we tie to 0) +localparam MHPMCOUNTER8 = 12'hb08; // WARL (we tie to 0) +localparam MHPMCOUNTER9 = 12'hb09; // WARL (we tie to 0) +localparam MHPMCOUNTER10 = 12'hb0a; // WARL (we tie to 0) +localparam MHPMCOUNTER11 = 12'hb0b; // WARL (we tie to 0) +localparam MHPMCOUNTER12 = 12'hb0c; // WARL (we tie to 0) +localparam MHPMCOUNTER13 = 12'hb0d; // WARL (we tie to 0) +localparam MHPMCOUNTER14 = 12'hb0e; // WARL (we tie to 0) +localparam MHPMCOUNTER15 = 12'hb0f; // WARL (we tie to 0) +localparam MHPMCOUNTER16 = 12'hb10; // WARL (we tie to 0) +localparam MHPMCOUNTER17 = 12'hb11; // WARL (we tie to 0) +localparam MHPMCOUNTER18 = 12'hb12; // WARL (we tie to 0) +localparam MHPMCOUNTER19 = 12'hb13; // WARL (we tie to 0) +localparam MHPMCOUNTER20 = 12'hb14; // WARL (we tie to 0) +localparam MHPMCOUNTER21 = 12'hb15; // WARL (we tie to 0) +localparam MHPMCOUNTER22 = 12'hb16; // WARL (we tie to 0) +localparam MHPMCOUNTER23 = 12'hb17; // WARL (we tie to 0) +localparam MHPMCOUNTER24 = 12'hb18; // WARL (we tie to 0) +localparam MHPMCOUNTER25 = 12'hb19; // WARL (we tie to 0) +localparam MHPMCOUNTER26 = 12'hb1a; // WARL (we tie to 0) +localparam MHPMCOUNTER27 = 12'hb1b; // WARL (we tie to 0) +localparam MHPMCOUNTER28 = 12'hb1c; // WARL (we tie to 0) +localparam MHPMCOUNTER29 = 12'hb1d; // WARL (we tie to 0) +localparam MHPMCOUNTER30 = 12'hb1e; // WARL (we tie to 0) +localparam MHPMCOUNTER31 = 12'hb1f; // WARL (we tie to 0) + +localparam MCYCLEH = 12'hb80; // High halves of each counter +localparam MINSTRETH = 12'hb82; +localparam MHPMCOUNTER3H = 12'hb83; +localparam MHPMCOUNTER4H = 12'hb84; +localparam MHPMCOUNTER5H = 12'hb85; +localparam MHPMCOUNTER6H = 12'hb86; +localparam MHPMCOUNTER7H = 12'hb87; +localparam MHPMCOUNTER8H = 12'hb88; +localparam MHPMCOUNTER9H = 12'hb89; +localparam MHPMCOUNTER10H = 12'hb8a; +localparam MHPMCOUNTER11H = 12'hb8b; +localparam MHPMCOUNTER12H = 12'hb8c; +localparam MHPMCOUNTER13H = 12'hb8d; +localparam MHPMCOUNTER14H = 12'hb8e; +localparam MHPMCOUNTER15H = 12'hb8f; +localparam MHPMCOUNTER16H = 12'hb90; +localparam MHPMCOUNTER17H = 12'hb91; +localparam MHPMCOUNTER18H = 12'hb92; +localparam MHPMCOUNTER19H = 12'hb93; +localparam MHPMCOUNTER20H = 12'hb94; +localparam MHPMCOUNTER21H = 12'hb95; +localparam MHPMCOUNTER22H = 12'hb96; +localparam MHPMCOUNTER23H = 12'hb97; +localparam MHPMCOUNTER24H = 12'hb98; +localparam MHPMCOUNTER25H = 12'hb99; +localparam MHPMCOUNTER26H = 12'hb9a; +localparam MHPMCOUNTER27H = 12'hb9b; +localparam MHPMCOUNTER28H = 12'hb9c; +localparam MHPMCOUNTER29H = 12'hb9d; +localparam MHPMCOUNTER30H = 12'hb9e; +localparam MHPMCOUNTER31H = 12'hb9f; + +localparam MCOUNTINHIBIT = 12'h320; // Count inhibit register for mcycle/minstret +localparam MHPMEVENT3 = 12'h323; // WARL (we tie to 0) +localparam MHPMEVENT4 = 12'h324; // WARL (we tie to 0) +localparam MHPMEVENT5 = 12'h325; // WARL (we tie to 0) +localparam MHPMEVENT6 = 12'h326; // WARL (we tie to 0) +localparam MHPMEVENT7 = 12'h327; // WARL (we tie to 0) +localparam MHPMEVENT8 = 12'h328; // WARL (we tie to 0) +localparam MHPMEVENT9 = 12'h329; // WARL (we tie to 0) +localparam MHPMEVENT10 = 12'h32a; // WARL (we tie to 0) +localparam MHPMEVENT11 = 12'h32b; // WARL (we tie to 0) +localparam MHPMEVENT12 = 12'h32c; // WARL (we tie to 0) +localparam MHPMEVENT13 = 12'h32d; // WARL (we tie to 0) +localparam MHPMEVENT14 = 12'h32e; // WARL (we tie to 0) +localparam MHPMEVENT15 = 12'h32f; // WARL (we tie to 0) +localparam MHPMEVENT16 = 12'h330; // WARL (we tie to 0) +localparam MHPMEVENT17 = 12'h331; // WARL (we tie to 0) +localparam MHPMEVENT18 = 12'h332; // WARL (we tie to 0) +localparam MHPMEVENT19 = 12'h333; // WARL (we tie to 0) +localparam MHPMEVENT20 = 12'h334; // WARL (we tie to 0) +localparam MHPMEVENT21 = 12'h335; // WARL (we tie to 0) +localparam MHPMEVENT22 = 12'h336; // WARL (we tie to 0) +localparam MHPMEVENT23 = 12'h337; // WARL (we tie to 0) +localparam MHPMEVENT24 = 12'h338; // WARL (we tie to 0) +localparam MHPMEVENT25 = 12'h339; // WARL (we tie to 0) +localparam MHPMEVENT26 = 12'h33a; // WARL (we tie to 0) +localparam MHPMEVENT27 = 12'h33b; // WARL (we tie to 0) +localparam MHPMEVENT28 = 12'h33c; // WARL (we tie to 0) +localparam MHPMEVENT29 = 12'h33d; // WARL (we tie to 0) +localparam MHPMEVENT30 = 12'h33e; // WARL (we tie to 0) +localparam MHPMEVENT31 = 12'h33f; // WARL (we tie to 0) + +// Other standard M-mode CSRs: +localparam MENVCFG = 12'h30a; +localparam MENVCFGH = 12'h31a; + +// Custom M-mode CSRs: +localparam PMPCFGM0 = 12'hbd0; // Make PMP regions M-mode without locking +// bd1 // (reserved for >32 regions) + +localparam MEIEA = 12'hbe0; // External interrupt pending array +localparam MEIPA = 12'hbe1; // External interrupt enable array +localparam MEIFA = 12'hbe2; // External interrupt force array +localparam MEIPRA = 12'hbe3; // External interrupt priority array +localparam MEINEXT = 12'hbe4; // Next external interrupt +localparam MEICONTEXT = 12'hbe5; // External interrupt context register + +localparam MSLEEP = 12'hbf0; // M-mode sleep control register +localparam H3MISA = 12'hbf1; // Hazard3 M-mode ISA identification register + +// ---------------------------------------------------------------------------- +// U-mode CSRs + +// Read-only aliases of M-mode counter CSRs: +localparam CYCLE = 12'hc00; +localparam TIME = 12'hc01; +localparam INSTRET = 12'hc02; +localparam CYCLEH = 12'hc80; +localparam TIMEH = 12'hc81; +localparam INSTRETH = 12'hc82; + +// Custom U-mode CSRs +localparam SLEEP = 12'h8f0; // U-mode subset of M-mode sleep control + +// ---------------------------------------------------------------------------- +// Trigger Module + +localparam TSELECT = 12'h7a0; +localparam TDATA1 = 12'h7a1; +localparam TDATA2 = 12'h7a2; +localparam TDATA3 = 12'h7a3; +localparam TINFO = 12'h7a4; +localparam TCONTROL = 12'h7a5; +localparam MCONTEXT = 12'h7a8; + +// ---------------------------------------------------------------------------- +// D-mode CSRs + +localparam DCSR = 12'h7b0; +localparam DPC = 12'h7b1; +localparam DMDATA0 = 12'hbff; // Custom read/write diff --git a/vendor/Hazard3/hdl/hazard3_decode.v b/vendor/Hazard3/hdl/hazard3_decode.v new file mode 100644 index 0000000..b8bd750 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_decode.v @@ -0,0 +1,594 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2023 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +`default_nettype none + +module hazard3_decode #( +`include "hazard3_config.vh" +, +`include "hazard3_width_const.vh" +) ( + input wire clk, + input wire rst_n, + + input wire [31:0] fd_cir, + input wire [1:0] fd_cir_err, + input wire [1:0] fd_cir_predbranch, + input wire [1:0] fd_cir_vld, + input wire fd_cir_is_32bit, + input wire fd_cir_invalid_16bit, + input wire fd_cir_is_uop, + input wire fd_cir_uop_nonfinal, + input wire fd_cir_uop_no_pc_update, + input wire fd_cir_uop_atomic, + + output wire [1:0] df_cir_use, + output wire df_cir_flush_behind, + + output wire df_uop_stall, + output wire df_uop_clear, + output wire df_lspair_phase_next, + + output wire [W_ADDR-1:0] d_pc, + + input wire debug_mode, + input wire m_mode, + input wire trap_wfi, + + input wire [W_ADDR-1:0] debug_dpc_wdata, + input wire debug_dpc_wen, + output wire [W_ADDR-1:0] debug_dpc_rdata, + + output wire d_starved, + input wire x_stall, + input wire f_jump_now, + input wire [W_ADDR-1:0] f_jump_target, + input wire x_jump_not_except, + input wire [W_ADDR-1:0] d_btb_target_addr, + + output reg [W_DATA-1:0] d_imm, + output reg [W_REGADDR-1:0] d_rs1, + output reg [W_REGADDR-1:0] d_rs2, + output reg [W_REGADDR-1:0] d_rd, + output reg [2:0] d_funct3_32b, + output reg [6:0] d_funct7_32b, + output reg [W_ALUSRC-1:0] d_alusrc_a, + output reg [W_ALUSRC-1:0] d_alusrc_b, + output reg [W_ALUOP-1:0] d_aluop, + output reg [W_MEMOP-1:0] d_memop, + output reg [W_MULOP-1:0] d_mulop, + output reg d_csr_ren, + output reg d_csr_wen, + output reg [1:0] d_csr_wtype, + output reg d_csr_w_imm, + output reg [W_BCOND-1:0] d_branchcond, + output reg [W_ADDR-1:0] d_addr_offs, + output reg d_addr_is_regoffs, + output reg [W_EXCEPT-1:0] d_except, + output reg d_sleep_wfi, + output reg d_sleep_block, + output reg d_sleep_unblock, + output wire d_no_pc_increment, + output wire d_uninterruptible, + output wire [W_ADDR-1:0] d_lspair_offset, + output reg d_fence_i, + output reg d_fence_d +); + +`include "rv_opcodes.vh" +`include "hazard3_ops.vh" + +localparam HAVE_CSR = CSR_M_MANDATORY || CSR_M_TRAP || CSR_COUNTER; + +// ---------------------------------------------------------------------------- + +wire [31:0] d_instr = fd_cir | { + 30'd0, {2{~|EXTENSION_C}} +}; + +reg d_invalid_32bit; +wire d_invalid = fd_cir_invalid_16bit || d_invalid_32bit; + +assign d_uninterruptible = |EXTENSION_ZCMP && fd_cir_uop_atomic; + +`ifdef HAZARD3_ASSERTIONS +always @ (posedge clk) if (rst_n) begin + assert(!(d_invalid && fd_cir_is_uop)); + assert(!(d_invalid && fd_cir_uop_atomic)); +end +`endif + +wire d_lspair_nonfinal; +// Signal to null the mepc offset when taking an exception on this +// instruction (because uops in a sequence *which can except*, so excluding +// the final sp adjust on popret/popretz, will all have the same PC as the +// next uop, which will be in stage 2 when they take their exception) +assign d_no_pc_increment = fd_cir_uop_nonfinal || d_lspair_nonfinal; + +assign df_uop_stall = x_stall || d_starved; + +// Note !df_cir_flush_behind because the jump in cm.popret/popretz is the +// *penultimate* instruction: we execute the stack adjustment in the fetch +// bubble to save a cycle, still need to finish the uop sequence. +// +// The sp adjust cannot generate an exception (it's an `add` with the same +// PMP.X and breakpoint comparison results as earlier uops) and interrupts are +// suppressed for this part of the sequence. +assign df_uop_clear = f_jump_now && !df_cir_flush_behind; + +// Decode various immediate formats +wire [31:0] d_imm_i = {{21{d_instr[31]}}, d_instr[30:20]}; +wire [31:0] d_imm_s = {{21{d_instr[31]}}, d_instr[30:25], d_instr[11:7]}; +wire [31:0] d_imm_b = {{20{d_instr[31]}}, d_instr[7], d_instr[30:25], d_instr[11:8], 1'b0}; +wire [31:0] d_imm_u = {d_instr[31:12], {12{1'b0}}}; +wire [31:0] d_imm_j = {{12{d_instr[31]}}, d_instr[19:12], d_instr[20], d_instr[30:21], 1'b0}; + +// ---------------------------------------------------------------------------- +// PC/CIR control + +// Must not flag bus error for a valid 16-bit instruction *followed by* an +// error, because instruction fetch errors are speculative, and can be +// flushed by e.g. a branch instruction. Note the 16 LSBs must be valid for +// us to know an instruction's size. +wire d_except_instr_bus_fault = fd_cir_vld > 2'd0 && fd_cir_err[0] || + fd_cir_vld > 2'd1 && fd_cir_is_32bit && fd_cir_err[1]; + +assign d_starved = ~|fd_cir_vld || fd_cir_vld[0] && fd_cir_is_32bit; + +wire d_stall = x_stall || d_starved || fd_cir_uop_nonfinal || d_lspair_nonfinal; + +assign df_cir_use = + d_starved || d_stall ? 2'h0 : + fd_cir_is_32bit ? 2'h2 : 2'h1; + +// CIR Locking is required if we successfully assert a jump request, but +// decode is stalled. It is not possible to gate the jump request if the +// stall depends on bus stall (as this would create a through-path from bus +// stall to bus request) so instead we instruct the frontend to preserve the +// stalled instruction when flushing, and fill in behind it. +// +// Once the stall clears, the stalled instruction can execute its remaining +// side effects e.g. writing a link value to the register file. +wire jump_caused_by_d = f_jump_now && x_jump_not_except; +wire assert_cir_lock = jump_caused_by_d && d_stall; + +// CIR lock ends naturally when an instruction (not just uop) graduates to the +// next stage: +wire finished_cir_lock = !d_stall; + +// CIR lock can meet an untimely end due to trap entry. One way to reach this +// is a dphase load fault on the final load in a cm.popret: here the `ret` +// issues a fetch address while stalled on the first dphase cycle, then is +// flushed by trap on second cycle. +wire deassert_cir_lock = finished_cir_lock || (f_jump_now && !x_jump_not_except); + +reg cir_lock_prev; +wire cir_lock = (cir_lock_prev && !deassert_cir_lock) || assert_cir_lock; +assign df_cir_flush_behind = assert_cir_lock && !cir_lock_prev; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + cir_lock_prev <= 1'b0; + end else begin + cir_lock_prev <= cir_lock; + end +end + +reg [W_ADDR-1:0] pc; +wire [W_ADDR-1:0] pc_seq_next = pc + ( + |EXTENSION_ZCMP && fd_cir_is_uop && fd_cir_uop_no_pc_update ? 32'd0 : + fd_cir_is_32bit ? 32'd4 : 32'd2 +); + +assign d_pc = pc; +assign debug_dpc_rdata = pc; + +// Frontend should mark the whole instruction, and nothing but the +// instruction, as a predicted branch. This goes wrong when we execute the +// address containing the predicted branch twice with different 16-bit +// alignments (!). We need to issue a branch-to-self to get back on a linear +// path, otherwise PC and CIR will diverge and we will misexecute. +wire partial_predicted_branch = !d_starved && + |BRANCH_PREDICTOR && fd_cir_is_32bit && ^fd_cir_predbranch; + +wire predicted_branch = |BRANCH_PREDICTOR && fd_cir_predbranch[0]; + +// Generally locking takes place on a stalled jump/branch, which may need the +// original PC available to produce a link address when it unstalls. An +// exception to this is jumps in micro-op sequences: in this case the jump is +// the penultimate instruction in the sequence (ret before addi sp) and we +// need to capture the pc mid-uop-sequence. +wire hold_pc_on_cir_lock = assert_cir_lock && !(fd_cir_is_uop && !fd_cir_uop_no_pc_update && !x_stall); +wire update_pc_on_cir_unlock = cir_lock_prev && finished_cir_lock && !fd_cir_uop_no_pc_update; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + pc <= RESET_VECTOR; + end else begin + if (debug_dpc_wen) begin + pc <= debug_dpc_wdata; + end else if (debug_mode) begin + pc <= pc; + end else if ((f_jump_now && !hold_pc_on_cir_lock) || update_pc_on_cir_unlock) begin + pc <= f_jump_target; + end else if (!f_jump_now && fd_cir_uop_nonfinal && !fd_cir_uop_no_pc_update && !x_stall) begin + // End of previously stalled jr uop in cm.popret and cm.popretz: + // safe to update PC as next instruction (addi sp) cannot trap. + pc <= f_jump_target; + end else if (!d_stall && !cir_lock) begin + // If this instruction is a predicted-taken branch (and has not + // generated a mispredict recovery jump) then set PC to the + // prediction target instead of the sequentially next PC + pc <= predicted_branch ? d_btb_target_addr : pc_seq_next; + end + end +end + +`ifdef HAZARD3_ASSERTIONS +always @ (posedge clk) if (rst_n) begin + if (~|fd_cir_vld) assert(!fd_cir_is_uop); + if (fd_cir_uop_no_pc_update) assert(fd_cir_is_uop); + if (fd_cir_uop_nonfinal) assert(fd_cir_is_uop); + if ($past(df_uop_clear)) assert(!fd_cir_is_uop); + // Important to avoid spurious PC updates following a trap on the final + // load of a cm.popret: + if ($past(df_uop_clear)) assert(!fd_cir_uop_no_pc_update); +end +`endif + +wire [W_ADDR-1:0] branch_offs = + !fd_cir_is_32bit && predicted_branch ? 32'd2 : + fd_cir_is_32bit && predicted_branch ? 32'd4 : d_imm_b; + +always @ (*) begin + casez ({|EXTENSION_A, d_instr[6:2]}) + {1'bz, 5'b11011}: d_addr_offs = d_imm_j ; // JAL + {1'bz, 5'b11000}: d_addr_offs = branch_offs ; // Branches + {1'bz, 5'b01000}: d_addr_offs = d_imm_s ; // Store + {1'bz, 5'b11001}: d_addr_offs = d_imm_i ; // JALR + {1'bz, 5'b00000}: d_addr_offs = d_imm_i ; // Loads + {1'b1, 5'b01011}: d_addr_offs = 32'h0000_0000; // Atomics + default: d_addr_offs = 32'hxxxx_xxxx; + endcase + if (partial_predicted_branch) begin + d_addr_offs = 32'h0000_0000; + end +end + +// ---------------------------------------------------------------------------- +// Track phase of load/store pair instructions (Zilsd and Zclsd) + +// This could be shared with uop_ctr (for Zcmp) but the two are fundamentally +// different: Zcmp has 16-bit instructions which expand to sequences of +// 32-bit, whereas Zilsd has multi-phase 32-bit instructions and Zclsd has +// direct 16-bit aliases of those instructions. Therefore it's cleaner to +// separate the phasing from the decompression for Zilsd/Zclsd. + +wire d_lspair_phase; + +// Reorder accesses to avoid clobbering rs1 (base) in first half of load: +wire d_lspair_reg_sel = d_lspair_phase == d_instr[15]; + +generate +if (EXTENSION_ZILSD) begin: have_lspair_reg_sel + reg d_lspair_phase_r; + assign d_lspair_phase = d_lspair_phase_r; + reg instr_is_lspair; + always @ (*) begin + casez ({d_invalid || d_starved, d_instr}) + {1'b0, `RVOPC_LD}: instr_is_lspair = 1'b1; + {1'b0, `RVOPC_SD}: instr_is_lspair = 1'b1; + default: instr_is_lspair = 1'b0; + endcase + end + + always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + d_lspair_phase_r <= 1'b0; + end else begin + d_lspair_phase_r <= df_lspair_phase_next; + end + end + + assign df_lspair_phase_next = + !d_stall || f_jump_now ? 1'b0 : + instr_is_lspair && !x_stall ? 1'b1 : d_lspair_phase_r; + + assign d_lspair_nonfinal = instr_is_lspair && !d_lspair_phase_r; + + assign d_lspair_offset = { + 29'h0, + d_lspair_reg_sel && instr_is_lspair, + 2'h0 + }; + +end else begin: no_lspair_reg_sel + + assign d_lspair_phase = 1'b0; + assign df_lspair_phase_next = 1'b0; + assign d_lspair_nonfinal = 1'b0; + assign d_lspair_offset = 32'd0; + +end +endgenerate + +// ---------------------------------------------------------------------------- +// Decode X controls + +localparam X0 = {W_REGADDR{1'b0}}; + +// First decode the instruction bits, based on available extensions and +// privilege state, without gating in any stall/exception signals. +reg [W_REGADDR-1:0] raw_rs1; +reg [W_REGADDR-1:0] raw_rs2; +reg [W_REGADDR-1:0] raw_rd; +reg [W_DATA-1:0] raw_imm; +reg [W_ALUSRC-1:0] raw_alusrc_a; +reg [W_ALUSRC-1:0] raw_alusrc_b; +reg [W_ALUOP-1:0] raw_aluop; +reg [W_MEMOP-1:0] raw_memop; +reg [W_MULOP-1:0] raw_mulop; +reg raw_csr_ren; +reg raw_csr_wen; +reg [1:0] raw_csr_wtype; +reg raw_csr_w_imm; +reg [W_BCOND-1:0] raw_branchcond; +reg raw_addr_is_regoffs; +reg [W_EXCEPT-1:0] raw_except; +reg raw_sleep_wfi; +reg raw_sleep_block; +reg raw_sleep_unblock; +reg raw_fence_i; +reg raw_fence_d; + +always @ (*) begin + // Assign some defaults + raw_rs1 = d_instr[19:15]; + raw_rs2 = d_instr[24:20]; + raw_rd = d_instr[11: 7]; + raw_imm = d_imm_i; + raw_alusrc_a = ALUSRCA_RS1; + raw_alusrc_b = ALUSRCB_RS2; + raw_aluop = ALUOP_ADD; + raw_memop = MEMOP_NONE; + raw_mulop = M_OP_MUL; + raw_csr_ren = 1'b0; + raw_csr_wen = 1'b0; + raw_csr_wtype = CSR_WTYPE_W; + raw_csr_w_imm = 1'b0; + raw_branchcond = BCOND_NEVER; + raw_addr_is_regoffs = 1'b0; + raw_except = EXCEPT_NONE; + raw_sleep_wfi = 1'b0; + raw_sleep_block = 1'b0; + raw_sleep_unblock = 1'b0; + raw_fence_i = 1'b0; + raw_fence_d = 1'b0; + // Note this funct3/funct7 are valid only for 32-bit instructions. They + // are useful for clusters of related ALU ops, such as sh*add, clmul. + d_funct3_32b = fd_cir[14:12]; + d_funct7_32b = fd_cir[31:25]; + + d_invalid_32bit = 1'b0; + + casez (d_instr) + `RVOPC_BEQ: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_SUB; raw_branchcond = BCOND_ZERO; end + `RVOPC_BNE: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_SUB; raw_branchcond = BCOND_NZERO; end + `RVOPC_BLT: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LT; raw_branchcond = BCOND_NZERO; end + `RVOPC_BGE: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LT; raw_branchcond = BCOND_ZERO; end + `RVOPC_BLTU: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LTU; raw_branchcond = BCOND_NZERO; end + `RVOPC_BGEU: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_rd = X0; raw_aluop = ALUOP_LTU; raw_branchcond = BCOND_ZERO; end + `RVOPC_JALR: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_branchcond = BCOND_ALWAYS; raw_addr_is_regoffs = 1'b1; + raw_rs2 = X0; raw_aluop = ALUOP_ADD; raw_alusrc_a = ALUSRCA_PC; raw_alusrc_b = ALUSRCB_IMM; raw_imm = fd_cir_is_32bit ? 32'd4 : 32'd2; end + `RVOPC_JAL: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_branchcond = BCOND_ALWAYS; raw_rs1 = X0; + raw_rs2 = X0; raw_aluop = ALUOP_ADD; raw_alusrc_a = ALUSRCA_PC; raw_alusrc_b = ALUSRCB_IMM; raw_imm = fd_cir_is_32bit ? 32'd4 : 32'd2; end + `RVOPC_LUI: begin raw_aluop = ALUOP_RS2; raw_imm = d_imm_u; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; raw_rs1 = X0; end + `RVOPC_AUIPC: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; raw_aluop = ALUOP_ADD; raw_imm = d_imm_u; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; raw_alusrc_a = ALUSRCA_PC; raw_rs1 = X0; end + `RVOPC_ADDI: begin raw_aluop = ALUOP_ADD; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end + `RVOPC_SLLI: begin raw_aluop = ALUOP_SLL; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end + `RVOPC_SLTI: begin raw_aluop = ALUOP_LT; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end + `RVOPC_SLTIU: begin raw_aluop = ALUOP_LTU; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end + `RVOPC_XORI: begin raw_aluop = ALUOP_XOR; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end + `RVOPC_SRLI: begin raw_aluop = ALUOP_SRL; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end + `RVOPC_SRAI: begin raw_aluop = ALUOP_SRA; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end + `RVOPC_ORI: begin raw_aluop = ALUOP_OR; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end + `RVOPC_ANDI: begin raw_aluop = ALUOP_AND; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; raw_rs2 = X0; end + `RVOPC_ADD: begin raw_aluop = ALUOP_ADD; end + `RVOPC_SUB: begin raw_aluop = ALUOP_SUB; end + `RVOPC_SLL: begin raw_aluop = ALUOP_SLL; end + `RVOPC_SLTU: begin raw_aluop = ALUOP_LTU; end + `RVOPC_XOR: begin raw_aluop = ALUOP_XOR; end + `RVOPC_SRL: begin raw_aluop = ALUOP_SRL; end + `RVOPC_SRA: begin raw_aluop = ALUOP_SRA; end + `RVOPC_OR: begin raw_aluop = ALUOP_OR; end + `RVOPC_AND: begin raw_aluop = ALUOP_AND; end + `RVOPC_LB: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LB; end + `RVOPC_LH: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LH; end + `RVOPC_LW: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LW; end + `RVOPC_LBU: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LBU; end + `RVOPC_LHU: begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LHU; end + `RVOPC_SB: begin raw_addr_is_regoffs = 1'b1; raw_aluop = ALUOP_RS2; raw_memop = MEMOP_SB; raw_rd = X0; end + `RVOPC_SH: begin raw_addr_is_regoffs = 1'b1; raw_aluop = ALUOP_RS2; raw_memop = MEMOP_SH; raw_rd = X0; end + `RVOPC_SW: begin raw_addr_is_regoffs = 1'b1; raw_aluop = ALUOP_RS2; raw_memop = MEMOP_SW; raw_rd = X0; end + + `RVOPC_SLT: begin + raw_aluop = ALUOP_LT; + if (|EXTENSION_XH3POWER && ~|raw_rd && ~|raw_rs1) begin + if (raw_rs2 == 5'h00) begin + // h3.block (power management hint) + d_invalid_32bit = trap_wfi; + raw_sleep_block = !trap_wfi; + end else if (raw_rs2 == 5'h01) begin + // h3.unblock (power management hint) + d_invalid_32bit = trap_wfi; + raw_sleep_unblock = !trap_wfi; + end + end + end + + `RVOPC_MUL: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MUL; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_MULH: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MULH; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_MULHSU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MULHSU; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_MULHU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_MULHU; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_DIV: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_DIV; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_DIVU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_DIVU; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_REM: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_REM; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_REMU: if (EXTENSION_M) begin raw_aluop = ALUOP_MULDIV; raw_mulop = M_OP_REMU; end else begin d_invalid_32bit = 1'b1; end + + `RVOPC_LR_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_LR_W; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_SC_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_SC_W; raw_aluop = ALUOP_RS2; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_AMOSWAP_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_RS2; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_AMOADD_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_ADD; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_AMOXOR_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_XOR; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_AMOAND_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_AND; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_AMOOR_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_OR; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_AMOMIN_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MIN; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_AMOMAX_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MAX; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_AMOMINU_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MINU; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_AMOMAXU_W: if (EXTENSION_A) begin raw_addr_is_regoffs = 1'b1; raw_memop = MEMOP_AMO; raw_aluop = ALUOP_MAXU; end else begin d_invalid_32bit = 1'b1; end + + `RVOPC_LD: if (EXTENSION_ZILSD) begin raw_addr_is_regoffs = 1'b1; raw_rs2 = X0; raw_memop = MEMOP_LW; raw_rd = {d_instr[11: 8], d_lspair_reg_sel}; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_SD: if (EXTENSION_ZILSD) begin raw_addr_is_regoffs = 1'b1; raw_rd = X0; raw_memop = MEMOP_SW; raw_rs2 = {d_instr[24:21], d_lspair_reg_sel}; raw_aluop = ALUOP_RS2; end else begin d_invalid_32bit = 1'b1; end + + `RVOPC_SH1ADD: if (EXTENSION_ZBA) begin raw_aluop = ALUOP_SHXADD; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_SH2ADD: if (EXTENSION_ZBA) begin raw_aluop = ALUOP_SHXADD; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_SH3ADD: if (EXTENSION_ZBA) begin raw_aluop = ALUOP_SHXADD; end else begin d_invalid_32bit = 1'b1; end + + `RVOPC_ANDN: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ANDN; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CLZ: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_CLZ; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CPOP: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_CPOP; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CTZ: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_CTZ; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_MAX: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MAX; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_MAXU: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MAXU; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_MIN: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MIN; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_MINU: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_MINU; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_ORC_B: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ORC_B; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_ORN: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ORN; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_REV8: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_REV8; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_ROL: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ROL; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_ROR: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ROR; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_RORI: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ROR; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_SEXT_B: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_SEXT_B; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_SEXT_H: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_SEXT_H; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_XNOR: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_XNOR; end else begin d_invalid_32bit = 1'b1; end + // Note: ZEXT_H is a subset of PACK from Zbkb. This is fine as long + // as this case appears first, since Zbkb implies Zbb on Hazard3. + `RVOPC_ZEXT_H: if (EXTENSION_ZBB) begin raw_aluop = ALUOP_ZEXT_H; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + + `RVOPC_CLMUL: if (EXTENSION_ZBC) begin raw_aluop = ALUOP_CLMUL; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CLMULH: if (EXTENSION_ZBC) begin raw_aluop = ALUOP_CLMUL; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CLMULR: if (EXTENSION_ZBC) begin raw_aluop = ALUOP_CLMUL; end else begin d_invalid_32bit = 1'b1; end + + `RVOPC_BCLR: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BCLR; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_BCLRI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BCLR; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_BEXT: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BEXT; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_BEXTI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BEXT; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_BINV: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BINV; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_BINVI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BINV; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_BSET: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BSET; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_BSETI: if (EXTENSION_ZBS) begin raw_aluop = ALUOP_BSET; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end + + `RVOPC_PACK: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_PACK; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_PACKH: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_PACKH; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_BREV8: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_BREV8; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_UNZIP: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_UNZIP; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_ZIP: if (EXTENSION_ZBKB) begin raw_aluop = ALUOP_ZIP; raw_rs2 = X0; end else begin d_invalid_32bit = 1'b1; end + + `RVOPC_XPERM8: if (EXTENSION_ZBKX) begin raw_aluop = ALUOP_XPERM; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_XPERM4: if (EXTENSION_ZBKX) begin raw_aluop = ALUOP_XPERM; end else begin d_invalid_32bit = 1'b1; end + + `RVOPC_H3_BEXTM: if (EXTENSION_XH3BEXTM) begin + raw_aluop = ALUOP_BEXTM; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_H3_BEXTMI: if (EXTENSION_XH3BEXTM) begin + raw_aluop = ALUOP_BEXTM; raw_rs2 = X0; raw_imm = d_imm_i; raw_alusrc_b = ALUSRCB_IMM; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CSRRW: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = 1'b1 ; raw_csr_ren = |raw_rd; raw_csr_wtype = CSR_WTYPE_W; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CSRRS: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_S; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CSRRC: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_C; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CSRRWI: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = 1'b1 ; raw_csr_ren = |raw_rd; raw_csr_wtype = CSR_WTYPE_W; raw_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CSRRSI: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_S; raw_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_CSRRCI: if (HAVE_CSR) begin raw_rs2 = X0; raw_imm = d_imm_i; raw_csr_wen = |raw_rs1; raw_csr_ren = 1'b1 ; raw_csr_wtype = CSR_WTYPE_C; raw_csr_w_imm = 1'b1; end else begin d_invalid_32bit = 1'b1; end + + `RVOPC_FENCE: begin raw_rs2 = X0; raw_fence_d = 1'b1; end // Note rs1/rd are zero in instruction + `RVOPC_FENCE_I: if (EXTENSION_ZIFENCEI) begin raw_except = debug_mode ? EXCEPT_NONE : EXCEPT_REFETCH; raw_fence_i = 1'b1; end else begin d_invalid_32bit = 1'b1; end // note rs1/rs2/rd are zero in instruction + `RVOPC_ECALL: if (HAVE_CSR) begin raw_except = m_mode || !U_MODE ? EXCEPT_ECALL_M : EXCEPT_ECALL_U; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_EBREAK: if (HAVE_CSR) begin raw_except = EXCEPT_EBREAK; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_MRET: if (HAVE_CSR && m_mode) begin raw_except = EXCEPT_MRET; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end + `RVOPC_WFI: if (HAVE_CSR && !trap_wfi) begin raw_sleep_wfi = 1'b1; raw_rs2 = X0; raw_rs1 = X0; raw_rd = X0; end else begin d_invalid_32bit = 1'b1; end + + default: begin d_invalid_32bit = 1'b1; end + endcase + + if (|EXTENSION_E && (raw_rd[4] || raw_rs1[4] || raw_rs2[4])) begin + d_invalid_32bit = 1'b1; + end +end + +// Then gate key signals based on CIR fullness, fetch faults etc. The split +// helps to avoid an event scheduling feedback loop that makes simulators +// unhappy and slow, particularly verilator + +localparam [4:0] REGADDR_MASK = {~|EXTENSION_E, 4'hf}; + +always @ (*) begin + // Pass through by default + d_rs1 = raw_rs1 & REGADDR_MASK; + d_rs2 = raw_rs2 & REGADDR_MASK; + d_rd = raw_rd & REGADDR_MASK; + d_imm = raw_imm; + d_alusrc_a = raw_alusrc_a; + d_alusrc_b = raw_alusrc_b; + d_aluop = raw_aluop; + d_memop = raw_memop; + d_mulop = raw_mulop; + d_csr_ren = raw_csr_ren; + d_csr_wen = raw_csr_wen; + d_csr_wtype = raw_csr_wtype; + d_csr_w_imm = raw_csr_w_imm; + d_branchcond = raw_branchcond; + d_addr_is_regoffs = raw_addr_is_regoffs; + d_except = raw_except; + d_sleep_wfi = raw_sleep_wfi; + d_sleep_block = raw_sleep_block; + d_sleep_unblock = raw_sleep_unblock; + d_fence_i = raw_fence_i; + d_fence_d = raw_fence_d; + + if (d_invalid || d_starved || d_except_instr_bus_fault || partial_predicted_branch) begin + d_rs1 = {W_REGADDR{1'b0}}; + d_rs2 = {W_REGADDR{1'b0}}; + d_rd = {W_REGADDR{1'b0}}; + d_memop = MEMOP_NONE; + d_branchcond = BCOND_NEVER; + d_csr_ren = 1'b0; + d_csr_wen = 1'b0; + d_except = EXCEPT_NONE; + d_sleep_wfi = 1'b0; + d_sleep_block = 1'b0; + d_sleep_unblock = 1'b0; + d_fence_i = 1'b0; + d_fence_d = 1'b0; + + if (EXTENSION_M) + d_aluop = ALUOP_ADD; + + if (d_except_instr_bus_fault) + d_except = EXCEPT_INSTR_FAULT; + else if (d_invalid && !d_starved) + d_except = EXCEPT_INSTR_ILLEGAL; + end + if (partial_predicted_branch) begin + d_addr_is_regoffs = 1'b0; + d_branchcond = BCOND_ALWAYS; + end + if (cir_lock_prev) begin + d_branchcond = BCOND_NEVER; + end +end + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_frontend.v b/vendor/Hazard3/hdl/hazard3_frontend.v new file mode 100644 index 0000000..330fe78 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_frontend.v @@ -0,0 +1,906 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +`default_nettype none + +module hazard3_frontend #( +`include "hazard3_config.vh" +) ( + input wire clk, + input wire rst_n, + + // Fetch interface + // addr_vld may be asserted at any time, but after assertion, + // neither addr nor addr_vld may change until the cycle after addr_rdy. + // There is no backpressure on the data interface; the front end + // must ensure it does not request data it cannot receive. + // addr_rdy and dat_vld may be functions of hready, and + // may not be used to compute combinational outputs. + output wire mem_size, // 1'b1 -> 32 bit access + output wire [W_ADDR-1:0] mem_addr, + output wire mem_priv, + output wire mem_addr_vld, + input wire mem_addr_rdy, + input wire [W_DATA-1:0] mem_data, + input wire mem_data_err, + input wire mem_data_vld, + + // Jump/flush interface + // Processor may assert vld at any time. The request will not go through + // unless rdy is high. Processor *may* alter request during this time. + // Inputs must not be a function of hready. + input wire [W_ADDR-1:0] jump_target, + input wire jump_priv, + input wire jump_target_vld, + output wire jump_target_rdy, + + // Interface to the branch target buffer. `src_addr` is the address of the + // last halfword of a taken backward branch. The frontend redirects fetch + // such that `src_addr` appears to be sequentially followed by `target`. + input wire btb_set, + input wire [W_ADDR-1:0] btb_set_src_addr, + input wire btb_set_src_size, + input wire [W_ADDR-1:0] btb_set_target_addr, + input wire btb_clear, + output wire [W_ADDR-1:0] btb_target_addr_out, + + // Interface to Decode + output reg [31:0] cir, // Current instruction register; pre-expanded to 32-bit + output wire [31:0] cir_raw, // Unexpanded instruction data + output reg [1:0] cir_vld, // number of valid halfwords in CIR + input wire [1:0] cir_use, // number of halfwords D intends to consume + // *may* be a function of hready + output wire [1:0] cir_err, // Bus error on upper/lower halfword of CIR. + output wire [1:0] cir_predbranch, // Set for last halfword of a predicted-taken branch + output wire cir_break_any, // Set for exact match of a breakpoint address on CIR LSB + output wire cir_break_d_mode, // As above but specifically break to debug mode + output reg cir_is_32bit, // Can't be decoded from CIR due to pre-expansion + output reg cir_invalid_16bit, // Expanded an invalid 32-bit instruction + output reg cir_is_uop, // Current instruction is part of a micro-op sequence + output reg cir_uop_nonfinal, // ...and there are more to follow in this instruction + output reg cir_uop_no_pc_update, // Suppress PC increment or jump (note the jump in cm.popret is not the final uop!) + output reg cir_uop_atomic, // Prevent IRQ entry, so intermediate states are not observed + input wire uop_stall, + input wire uop_clear, + + // "flush_behind": do not flush the oldest instruction when accepting a + // jump request (but still flush younger instructions). Sometimes a + // stalled instruction may assert a jump request, because e.g. the stall + // is dependent on a bus stall signal so can't gate the request. + input wire cir_flush_behind, + // Required for regnum predecode when Zilsd is enabled: + input wire df_lspair_phase_next, + + // Signal to power controller that power down is safe. (When going to + // sleep, first the pipeline is stalled, and then the power controller + // waits for the frontend to naturally come to a halt before releasing + // its power request. This avoids manually halting the frontend.) + output wire pwrdown_ok, + // Signal to delay the first instruction fetch following reset, because + // powerup has not yet been negotiated. + input wire delay_first_fetch, + + // Provide the rs1/rs2 register numbers which will be in CIR next cycle. + // Coarse: valid if this instruction has a nonzero register operand. + // (Suitable for regfile read) + output reg [4:0] predecode_rs1_coarse, + output reg [4:0] predecode_rs2_coarse, + // Fine: like coarse, but accurate zeroing when the operand is implicit. + // (Suitable for bypass. Still not precise enough for stall logic.) + output reg [4:0] predecode_rs1_fine, + output reg [4:0] predecode_rs2_fine, + + // Debugger instruction injection: instruction fetch is suppressed when in + // debug halt state, and the DM can then inject instructions into the last + // entry of the prefetch queue using the vld/rdy handshake. + input wire debug_mode, + input wire [W_DATA-1:0] dbg_instr_data, + input wire dbg_instr_data_vld, + output wire dbg_instr_data_rdy, + + // PMP query->kill interface for X permission checks + output wire [W_ADDR-1:0] pmp_i_addr, + output wire pmp_i_m_mode, + input wire pmp_i_kill, + + // Trigger unit query->break interface for breakpoints + output wire [W_ADDR-1:0] trigger_addr, + output wire trigger_m_mode, + input wire [1:0] trigger_break_any, + input wire [1:0] trigger_break_d_mode + +); + +`include "rv_opcodes.vh" + +localparam W_BUNDLE = 16; +// This is the minimum for full throughput (enough to avoid dropping data when +// decode stalls) and there is no significant advantage to going larger. +localparam FIFO_DEPTH = 2; + +// ---------------------------------------------------------------------------- +// Fetch queue + +wire jump_now = jump_target_vld && jump_target_rdy; +reg [1:0] mem_data_hwvld; + +// PMP X faults are checked in parallel with the fetch (fine if executable +// memory is read-idempotent) and failures are promoted to bus errors: +wire pmp_kill_fetch_dph; +wire mem_or_pmp_err = mem_data_err || pmp_kill_fetch_dph; + +// Similarly, breakpoint matches are checked during fetch data phase. These +// are called mem_xxx because they are the breakpoint metadata for the data +// coming back from memory in this dphase. +wire [1:0] mem_break_any; +wire [1:0] mem_break_d_mode; + +// Mark data as containing a predicted-taken branch instruction so that +// mispredicts can be recovered -- need to track both halfwords so that we +// can mark the entire instruction, and nothing but the instruction: +reg [1:0] mem_data_predbranch; + +// Bus errors (and other metadata) travel alongside data. They cause an +// exception if the core decodes the instruction, but until then can be +// flushed harmlessly. + +reg [W_DATA-1:0] fifo_mem [0:FIFO_DEPTH]; +reg fifo_err [0:FIFO_DEPTH]; +reg [1:0] fifo_break_any [0:FIFO_DEPTH]; +reg [1:0] fifo_break_d_mode [0:FIFO_DEPTH]; +reg [1:0] fifo_predbranch [0:FIFO_DEPTH]; +reg [1:0] fifo_valid_hw [0:FIFO_DEPTH]; +reg fifo_valid [0:FIFO_DEPTH]; +reg fifo_valid_m1 [0:FIFO_DEPTH]; + +wire [W_DATA-1:0] fifo_rdata = fifo_mem[0]; +wire fifo_full = fifo_valid[FIFO_DEPTH - 1]; +wire fifo_empty = !fifo_valid[0]; +wire fifo_almost_full = fifo_valid[FIFO_DEPTH - 2]; + +wire fifo_push; +wire fifo_pop; +wire fifo_dbg_inject = DEBUG_SUPPORT && dbg_instr_data_vld && dbg_instr_data_rdy; + +always @ (*) begin: boundary_conditions + integer i; + fifo_mem[FIFO_DEPTH] = mem_data; + fifo_predbranch[FIFO_DEPTH] = 2'b00; + fifo_err[FIFO_DEPTH] = 1'b0; + fifo_break_any[FIFO_DEPTH] = 2'b00; + fifo_break_d_mode[FIFO_DEPTH] = 2'b00; + for (i = 0; i <= FIFO_DEPTH; i = i + 1) begin + fifo_valid[i] = |EXTENSION_C ? |fifo_valid_hw[i] : fifo_valid_hw[i][0]; + // valid-to-right condition: i == 0 || fifo_valid[i - 1], but without + // using negative array bound (seems broken in Yosys?) or OOB in the + // short circuit case (gives lint although result is well-defined) + if (i == 0) begin + fifo_valid_m1[i] = 1'b1; + end else begin + fifo_valid_m1[i] = fifo_valid[i - 1]; + end + end +end + +always @ (posedge clk or negedge rst_n) begin: fifo_update + integer i; + if (!rst_n) begin + for (i = 0; i < FIFO_DEPTH; i = i + 1) begin + fifo_valid_hw[i] <= 2'b00; + fifo_mem[i] <= 32'd0; + fifo_err[i] <= 1'b0; + fifo_break_any[i] <= 2'b00; + fifo_break_d_mode[i] <= 2'b00; + fifo_predbranch[i] <= 2'b00; + end + // This exists only for loop boundary conditions, but is tied off in + // this synchronous process to work around a Verilator scheduling + // issue (see issue #21) + fifo_valid_hw[FIFO_DEPTH] <= 2'b00; + end else begin + for (i = 0; i < FIFO_DEPTH; i = i + 1) begin + if (fifo_pop || (fifo_push && !fifo_valid[i])) begin + fifo_mem[i] <= fifo_valid[i + 1] ? fifo_mem[i + 1] : mem_data; + fifo_err[i] <= fifo_valid[i + 1] ? fifo_err[i + 1] : mem_or_pmp_err; + fifo_break_any[i] <= fifo_valid[i + 1] ? fifo_break_any[i + 1] : mem_break_any; + fifo_break_d_mode[i] <= fifo_valid[i + 1] ? fifo_break_d_mode[i + 1] : mem_break_d_mode; + fifo_predbranch[i] <= fifo_valid[i + 1] ? fifo_predbranch[i + 1] : mem_data_predbranch; + end + fifo_valid_hw[i] <= + jump_now ? 2'h0 : + fifo_valid[i + 1] && fifo_pop ? fifo_valid_hw[i + 1] : + fifo_valid[i] && fifo_pop ? mem_data_hwvld & {2{fifo_push}} : + fifo_valid[i] ? fifo_valid_hw[i] : + fifo_push && !fifo_pop && fifo_valid_m1[i] ? mem_data_hwvld : 2'h0; + end + // Allow DM to inject instructions directly into the lowest-numbered + // queue entry. This mux should not extend critical path since it is + // balanced with the instruction-assembly muxes on the queue bypass + // path. Note that flush takes precedence over debug injection + // (and the debug module design must account for this) + if (fifo_dbg_inject) begin + fifo_mem[0] <= dbg_instr_data; + fifo_err[0] <= 1'b0; + fifo_predbranch[0] <= 2'b00; + fifo_break_any[0] <= 2'b00; + fifo_break_d_mode[0] <= 2'b00; + fifo_valid_hw[0] <= jump_now ? 2'b00 : 2'b11; + end + fifo_valid_hw[FIFO_DEPTH] <= 2'b00; + end +end + +`ifdef HAZARD3_ASSERTIONS +always @ (posedge clk) if (rst_n) begin + // FIFO validity must be compact, so we can always consume from the end + if (!fifo_valid[0]) begin + assert(!fifo_valid[1]); + end +end +`endif + +assign pwrdown_ok = (fifo_full && !jump_target_vld) || debug_mode; + +// ---------------------------------------------------------------------------- +// Branch target buffer + +wire [W_ADDR-1:0] btb_src_addr; +wire btb_src_size; +wire [W_ADDR-1:0] btb_target_addr; +wire btb_valid; + +generate +if (BRANCH_PREDICTOR) begin: have_btb + reg [W_ADDR-1:0] btb_src_addr_r; + reg btb_src_size_r; + reg [W_ADDR-1:0] btb_target_addr_r; + reg btb_valid_r; + assign btb_src_addr = btb_src_addr_r; + assign btb_src_size = btb_src_size_r; + assign btb_target_addr = btb_target_addr_r; + assign btb_valid = btb_valid_r; + always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + btb_src_addr_r <= {W_ADDR{1'b0}}; + btb_src_size_r <= 1'b0; + btb_target_addr_r <= {W_ADDR{1'b0}}; + btb_valid_r <= 1'b0; + end else if (btb_clear) begin + // Clear takes precedences over set. E.g. if a taken branch is in + // stage 2 and an exception is in stage 3, we must clear the BTB. + btb_valid_r <= 1'b0; + end else if (btb_set) begin + btb_src_addr_r <= btb_set_src_addr; + btb_src_size_r <= btb_set_src_size; + btb_target_addr_r <= btb_set_target_addr; + btb_valid_r <= 1'b1; + end + end +end else begin: no_btb + assign btb_src_addr = {W_ADDR{1'b0}}; + assign btb_src_size = 1'b0; + assign btb_target_addr = {W_ADDR{1'b0}}; + assign btb_valid = 1'b0; +end +endgenerate + +// Decode uses the target address to set the PC to the correct branch target +// value following a predicted-taken branch (as normally it would update PC +// by following an X jump request, and in this case there is none). +// +// Note this assumes the BTB target has not changed by the time the predicted +// branch arrives at decode! This is always true because the only way for the +// target address to change is when an older branch is taken, which would +// flush the younger predicted-taken branch before it reaches decode. + +assign btb_target_addr_out = btb_target_addr; + +// ---------------------------------------------------------------------------- +// Fetch request generation + +// Fetch addr runs ahead of the PC, in word increments. +reg [W_ADDR-1:0] fetch_addr; +reg fetch_priv; +reg btb_prev_start_of_overhanging; +reg [1:0] mem_aph_hwvld; +reg mem_addr_hold; + +wire btb_match_word = |BRANCH_PREDICTOR && btb_valid && ( + fetch_addr[W_ADDR-1:2] == btb_src_addr[W_ADDR-1:2] +); + +// Catch case where predicted-taken branch instruction extends into next word: +wire btb_src_overhanging = btb_src_size && btb_src_addr[1]; + +// Suppress case where we have jumped immediately after a word-aligned halfword-sized +// branch, and the jump target went into fetch_addr due to an address-phase hold: +wire btb_jumped_beyond = !btb_src_size && !btb_src_addr[1] && !mem_aph_hwvld[0]; + +wire btb_match_current_addr = btb_match_word && !btb_src_overhanging && !btb_jumped_beyond; +wire btb_match_next_addr = btb_match_word && btb_src_overhanging; + +wire btb_match_now = btb_match_current_addr || btb_prev_start_of_overhanging; + +// Post-increment if jump request is going straight through +wire [W_ADDR-1:0] jump_target_post_increment = + {jump_target[W_ADDR-1:2], 2'b00} + + {{W_ADDR-3{1'b0}}, mem_addr_rdy && !mem_addr_hold, 2'b00}; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + fetch_addr <= RESET_VECTOR; + // M-mode at reset: + fetch_priv <= 1'b1; + btb_prev_start_of_overhanging <= 1'b0; + end else begin + if (jump_now) begin + fetch_addr <= jump_target_post_increment; + fetch_priv <= jump_priv || !U_MODE; + btb_prev_start_of_overhanging <= 1'b0; + end else if (mem_addr_vld && mem_addr_rdy) begin + if (btb_match_now && |BRANCH_PREDICTOR) begin + fetch_addr <= {btb_target_addr[W_ADDR-1:2], 2'b00}; + end else begin + fetch_addr <= fetch_addr + 32'd4; + end + btb_prev_start_of_overhanging <= btb_match_next_addr; + end + end +end + +// Combinatorially generate the address-phase request + +reg reset_holdoff; +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + reset_holdoff <= 1'b1; + end else begin + reset_holdoff <= (|EXTENSION_XH3POWER && delay_first_fetch) ? reset_holdoff : 1'b0; + // This should be impossible, but assert to be sure, because it *will* + // change the fetch address (and we shouldn't check it in hardware if + // we can prove it doesn't happen) + end +end + +`ifdef HAZARD3_ASSERTIONS +always @ (posedge clk) if (rst_n) begin + assert(!(jump_target_vld && reset_holdoff)); +end +`endif + +reg [W_ADDR-1:0] mem_addr_r; +reg mem_priv_r; +reg mem_addr_vld_r; + +// Downstream accesses are always word-sized word-aligned. +assign mem_addr = mem_addr_r; +assign mem_priv = mem_priv_r; +assign mem_addr_vld = mem_addr_vld_r && !reset_holdoff; +assign mem_size = 1'b1; + +wire fetch_stall; + +always @ (*) begin + mem_addr_r = fetch_addr; + mem_priv_r = fetch_priv; + mem_addr_vld_r = 1'b1; + case (1'b1) + mem_addr_hold : begin mem_addr_r = fetch_addr; end + jump_target_vld || reset_holdoff : begin + mem_addr_r = {jump_target[W_ADDR-1:2], 2'b00}; + mem_priv_r = jump_priv || !U_MODE; + end + DEBUG_SUPPORT && debug_mode : begin mem_addr_vld_r = 1'b0; end + !fetch_stall : begin mem_addr_r = fetch_addr; end + default : begin mem_addr_vld_r = 1'b0; end + endcase +end + +assign jump_target_rdy = !mem_addr_hold; + +// ---------------------------------------------------------------------------- +// Bus Pipeline Tracking + +// Keep track of some useful state of the memory interface + +reg [1:0] pending_fetches; +reg [1:0] ctr_flush_pending; + +wire [1:0] pending_fetches_next = pending_fetches + (mem_addr_vld && !mem_addr_hold) - mem_data_vld; + +// Using the non-registered version of pending_fetches would improve FIFO +// utilisation, but create a combinatorial path from hready to address phase! +// This means at least a 2-word FIFO is required for full fetch throughput. +assign fetch_stall = fifo_full + || fifo_almost_full && |pending_fetches + || pending_fetches > 2'h1; + +// Debugger only injects instructions when the frontend is at rest and empty. +assign dbg_instr_data_rdy = DEBUG_SUPPORT && !fifo_valid[0] && ~|ctr_flush_pending; + +wire cir_room_for_fetch; +// If fetch data is forwarded past the FIFO, ensure it is not also written to it. +assign fifo_push = mem_data_vld && ~|ctr_flush_pending && !(cir_room_for_fetch && fifo_empty) + && !(DEBUG_SUPPORT && debug_mode); + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mem_addr_hold <= 1'b0; + pending_fetches <= 2'h0; + ctr_flush_pending <= 2'h0; + end else begin + mem_addr_hold <= mem_addr_vld && !mem_addr_rdy; + pending_fetches <= pending_fetches_next; + if (jump_now) begin + ctr_flush_pending <= pending_fetches - mem_data_vld; + end else if (|ctr_flush_pending && mem_data_vld) begin + ctr_flush_pending <= ctr_flush_pending - 1'b1; + end + end +end + +`ifdef HAZARD3_ASSERTIONS +always @ (posedge clk) if (rst_n) begin + assert(ctr_flush_pending <= pending_fetches); + assert(pending_fetches < 2'd3); + assert(!(mem_data_vld && !pending_fetches)); +end +`endif + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + mem_data_hwvld <= 2'b11; + mem_aph_hwvld <= 2'b11; + mem_data_predbranch <= 2'b00; + end else begin + if (jump_now) begin + if (|EXTENSION_C) begin + if (mem_addr_rdy) begin + mem_aph_hwvld <= 2'b11; + mem_data_hwvld <= {1'b1, !jump_target[1]}; + end else begin + mem_aph_hwvld <= {1'b1, !jump_target[1]}; + end + end + mem_data_predbranch <= 2'b00; + end else if (mem_addr_vld && mem_addr_rdy) begin + if (|EXTENSION_C) begin + // If a predicted-taken branch instruction only spans the first + // half of a word, need to flag the second half as invalid. + mem_data_hwvld <= mem_aph_hwvld & { + !(|BRANCH_PREDICTOR && btb_match_now && (btb_src_addr[1] == btb_src_size)), + 1'b1 + }; + // Also need to take the alignment of the destination into account. + mem_aph_hwvld <= { + 1'b1, + !(|BRANCH_PREDICTOR && btb_match_now && btb_target_addr[1]) + }; + end + mem_data_predbranch <= + |BRANCH_PREDICTOR && btb_match_word ? ( + btb_src_addr[1] ? 2'b10 : + btb_src_size ? 2'b11 : 2'b01 + ) : + |BRANCH_PREDICTOR && btb_prev_start_of_overhanging ? ( + 2'b01 + ) : 2'b00; + end + end +end + +// ---------------------------------------------------------------------------- +// PMP and trigger unit interfacing: query -> kill/break + +wire [W_ADDR-1:0] pmp_trigger_check_dph_addr; +wire pmp_trigger_check_dph_m_mode; + +// Register the fetch address into stage F so that the PMP can check it in +// parallel with the bus data phase. Feels wasteful to have a separate +// register, but using the fetch_addr counter is fraught due to the way that +// new addresses go into it or past it (depending on aphase hold). + +generate +if (PMP_REGIONS > 0 || DEBUG_SUPPORT != 0) begin: have_check_reg + + reg [W_ADDR-1:0] check_addr_dph; + reg check_m_mode_dph; + always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + check_addr_dph <= {W_ADDR{1'b0}}; + check_m_mode_dph <= 1'b0; + end else if (mem_addr_vld && mem_addr_rdy) begin + check_addr_dph <= mem_addr; + check_m_mode_dph <= mem_priv; + end + end + + assign pmp_trigger_check_dph_addr = check_addr_dph; + assign pmp_trigger_check_dph_m_mode = check_m_mode_dph; + +end else begin: no_check_reg + + assign pmp_trigger_check_dph_addr = {W_ADDR{1'b0}}; + assign pmp_trigger_check_dph_m_mode = 1'b0; + +end +endgenerate + +generate +if (PMP_REGIONS == 0) begin: no_pmp + + assign pmp_i_addr = {W_ADDR{1'b0}}; + assign pmp_i_m_mode = 1'b0; + assign pmp_kill_fetch_dph = 1'b0; + +end else begin: have_pmp + + assign pmp_i_addr = pmp_trigger_check_dph_addr; + assign pmp_i_m_mode = pmp_trigger_check_dph_m_mode; + assign pmp_kill_fetch_dph = pmp_i_kill && !debug_mode; + +end +endgenerate + +generate +if (DEBUG_SUPPORT == 0) begin: no_triggers + + assign trigger_addr = {W_ADDR{1'b0}}; + assign trigger_m_mode = 1'b0; + assign mem_break_any = 2'b00; + assign mem_break_d_mode = 2'b00; + +end else begin: have_triggers + + assign trigger_addr = pmp_trigger_check_dph_addr; + assign trigger_m_mode = pmp_trigger_check_dph_m_mode; + assign mem_break_any = trigger_break_any & {|EXTENSION_C, 1'b1}; + assign mem_break_d_mode = trigger_break_d_mode & {|EXTENSION_C, 1'b1}; + +end +endgenerate + +// ---------------------------------------------------------------------------- +// Instruction buffer + +// The instruction buffer is a 3 x ~16-bit shift register: +// +// * 2 x 16-bit entries form the 32-bit current instruction register (CIR) +// which is the processor's decode window +// +// * 1 x 16-bit entry allows the decode window to be non-32-bit-aligned with +// respect to the 2 x 32-bit prefetch queue entries, which are always +// naturally aligned in memory (if fully populated). +// +// The third entry should be trimmed for non-RVC configurations due to +// constant-folding on EXTENSION_C; it is unnecessary here because the +// instructions are always 32-bit-aligned. + +// The entries ("slots") are slightly larger than 16 bits because they also +// contain metadata like bus errors: +localparam W_SLOT = 4 + W_BUNDLE; +localparam SLOT_BREAK_ANY_BIT = 3 + W_BUNDLE; +localparam SLOT_BREAK_D_MODE_BIT = 2 + W_BUNDLE; +localparam SLOT_ERR_BIT = 1 + W_BUNDLE; +localparam SLOT_PREDBRANCH_BIT = 0 + W_BUNDLE; + +reg [3*W_SLOT-1:0] buf_contents; +reg [1:0] buf_level; + +wire fetch_data_vld = !fifo_empty || (mem_data_vld && ~|ctr_flush_pending && !debug_mode); + +wire [W_DATA-1:0] fetch_data = fifo_empty ? mem_data : fifo_rdata; +wire [1:0] fetch_data_hwvld = fifo_empty ? mem_data_hwvld : fifo_valid_hw[0]; +wire fetch_bus_err = fifo_empty ? mem_or_pmp_err : fifo_err[0]; +wire [1:0] fetch_break_any = fifo_empty ? mem_break_any : fifo_break_any[0]; +wire [1:0] fetch_break_d_mode = fifo_empty ? mem_break_d_mode : fifo_break_d_mode[0]; +wire [1:0] fetch_predbranch = fifo_empty ? mem_data_predbranch : fifo_predbranch[0]; + +wire [W_SLOT-1:0] fetch_contents_hw1 = { + fetch_break_any[1], + fetch_break_d_mode[1], + fetch_bus_err, + fetch_predbranch[1], + fetch_data[W_BUNDLE +: W_BUNDLE] +}; + +wire [W_SLOT-1:0] fetch_contents_hw0 = { + fetch_break_any[0], + fetch_break_d_mode[0], + fetch_bus_err, + fetch_predbranch[0], + fetch_data[0 +: W_BUNDLE] +}; + +wire [2*W_SLOT-1:0] fetch_contents_aligned = { + fetch_contents_hw1, + fetch_data_hwvld[0] || ~|EXTENSION_C ? fetch_contents_hw0 : fetch_contents_hw1 +}; + +// Shift not-yet-used contents down to backfill D's consumption. We don't care +// about anything which is invalid or will be overlaid with fresh data, so +// choose these values in a way that minimises muxes. +wire [3*W_SLOT-1:0] buf_shifted = + cir_use[1] ? {buf_contents[W_SLOT +: 2 * W_SLOT], buf_contents[2 * W_SLOT +: W_SLOT]} : + cir_use[0] && EXTENSION_C ? {buf_contents[2 * W_SLOT +: W_SLOT], buf_contents[W_SLOT +: 2 * W_SLOT]} : + buf_contents; + +wire [1:0] level_next_no_fetch = buf_level - cir_use; + +// Overlay fresh fetch data onto the shifted/recycled buffer contents. Again, +// if something won't be looked at, generate the cheapest possible garbage. +assign cir_room_for_fetch = level_next_no_fetch <= (|EXTENSION_C && ~&fetch_data_hwvld ? 2'h2 : 2'h1); +assign fifo_pop = cir_room_for_fetch && !fifo_empty; + +wire [3*W_SLOT-1:0] buf_shifted_plus_fetch = + !cir_room_for_fetch ? buf_shifted : + level_next_no_fetch[1] && |EXTENSION_C ? {fetch_contents_aligned[0 +: W_SLOT], buf_shifted[0 +: 2 * W_SLOT]} : + level_next_no_fetch[0] && |EXTENSION_C ? {fetch_contents_aligned, buf_shifted[0 +: W_SLOT]} : + {buf_shifted[2 * W_SLOT +: W_SLOT], fetch_contents_aligned}; + +wire [1:0] fetch_fill_amount = cir_room_for_fetch && fetch_data_vld ? ( + &fetch_data_hwvld || ~|EXTENSION_C ? 2'h2 : 2'h1 +) : 2'h0; + +wire [1:0] buf_level_next = {1'b1, |EXTENSION_C} & ( + jump_now && cir_flush_behind ? (cir_is_32bit ? 2'h2 : 2'h1) : + jump_now ? 2'h0 : level_next_no_fetch + fetch_fill_amount +); + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + buf_level <= 2'h0; + cir_vld <= 2'h0; + // Mysterious reset value ensures address buses are zero in reset + // (see definition of d_addr_offs in hazard3_decode) + buf_contents <= {{3 * W_SLOT - 2{1'b0}}, 2'b11}; + end else begin + buf_level <= buf_level_next; + cir_vld <= buf_level_next & ~(buf_level_next >> 1'b1); + buf_contents <= buf_shifted_plus_fetch; + end +end + +`ifdef HAZARD3_ASSERTIONS +reg [1:0] prop_past_buf_level; // Workaround for weird non-constant $past reset issue +always @ (posedge clk) begin + if (!rst_n) begin + prop_past_buf_level <= 2'h0; + end else begin + prop_past_buf_level <= buf_level; + + assert(cir_vld <= 2); + assert(cir_use <= cir_vld); + if (!jump_now) assert(buf_level_next >= level_next_no_fetch); + // We fetch 32 bits per cycle, max. If this happens it's due to negative overflow. + if (prop_past_buf_level == 2'h0) + assert(buf_level != 2'h3); + end +end +`endif + +assign cir_err = { + buf_contents[1 * W_SLOT + SLOT_ERR_BIT], + buf_contents[0 * W_SLOT + SLOT_ERR_BIT] +}; + +assign cir_predbranch = { + buf_contents[1 * W_SLOT + SLOT_PREDBRANCH_BIT], + buf_contents[0 * W_SLOT + SLOT_PREDBRANCH_BIT] +}; + +assign cir_break_any = buf_contents[0 * W_SLOT + SLOT_BREAK_ANY_BIT] && |cir_vld; + +assign cir_break_d_mode = buf_contents[0 * W_SLOT + SLOT_BREAK_D_MODE_BIT] && |cir_vld; + +// ---------------------------------------------------------------------------- +// Register number predecode + +wire [31:0] next_instr = { + buf_shifted_plus_fetch[1 * W_SLOT +: W_BUNDLE], + buf_shifted_plus_fetch[0 * W_SLOT +: W_BUNDLE] +}; + +wire next_instr_is_32bit = next_instr[1:0] == 2'b11 || ~|EXTENSION_C; + +wire [3:0] decomp_uop_step; +wire [3:0] uop_ctr = decomp_uop_step & {4{|EXTENSION_ZCMP}}; + +wire [4:0] zcmp_pushpop_rs2 = + uop_ctr == 4'h0 ? 5'd01 : // ra + uop_ctr == 4'h1 ? 5'd08 : // s0 + uop_ctr == 4'h2 ? 5'd09 : // s1 + 5'd15 + {1'b0, uop_ctr} ; // s2-s11 + +wire [4:0] zcmp_pushpop_rs1 = + uop_ctr < 4'hd ? 5'd02 : // sp (addr base reg) + uop_ctr == 4'hd ? 5'd00 : // zero (clear a0) + uop_ctr == 4'he ? 5'd01 : // ra (ret) + 5'd02 ; // sp (stack adj) + +wire [4:0] zcmp_sa01_r1s = {|next_instr[9:8], ~|next_instr[9:8], next_instr[9:7]}; +wire [4:0] zcmp_sa01_r2s = {|next_instr[4:3], ~|next_instr[4:3], next_instr[4:2]}; + +wire [4:0] zcmp_mvsa01_rs1 = {4'h5, uop_ctr[0]}; +wire [4:0] zcmp_mva01s_rs1 = uop_ctr[0] ? zcmp_sa01_r2s : zcmp_sa01_r1s; + +// "coarse" because the mapping of pair (x0, x1) -> (x0, x0) is not yet applied +wire [4:0] zilsd_rs2_coarse = { next_instr[24:21], df_lspair_phase_next ^ ~next_instr[15]}; +wire [4:0] zclsd_sd_rs2_coarse = {2'b01, next_instr[4:3], df_lspair_phase_next ^ ~next_instr[7] }; +wire [4:0] zclsd_sdsp_rs2_coarse = { next_instr[6:3], df_lspair_phase_next ^ 1'b1 }; + +always @ (*) begin + + casez ({next_instr_is_32bit, |EXTENSION_ZCMP, next_instr[15:0]}) + {1'b1, 1'bz, 16'bzzzzzzzzzzzzzzzz}: predecode_rs1_coarse = next_instr[19:15]; // 32-bit R, S, B formats + {1'b0, 1'bz, 16'b00zzzzzzzzzzzz00}: predecode_rs1_coarse = 5'd2; // c.addi4spn + don't care + {1'b0, 1'bz, 16'b0zzzzzzzzzzzzz01}: predecode_rs1_coarse = next_instr[11:7]; // c.addi, c.addi16sp + don't care (jal, li) + {1'b0, 1'bz, 16'bz1zzzzzzzzzzzz10}: predecode_rs1_coarse = 5'd2; // c.lwsp, c.swsp, c.ldsp, c.sdsp + {1'b0, 1'bz, 16'bz00zzzzzzzzzzz10}: predecode_rs1_coarse = next_instr[11:7]; // c.slli, c.mv, c.add + {1'b0, 1'b1, 16'b1011zzzzzzzzzz10}: predecode_rs1_coarse = zcmp_pushpop_rs1; // cm.push, cm.pop* + {1'b0, 1'b1, 16'b1010zzzzz0zzzz10}: predecode_rs1_coarse = zcmp_mvsa01_rs1; // cm.mvsa01 + {1'b0, 1'b1, 16'b1010zzzzz1zzzz10}: predecode_rs1_coarse = zcmp_mva01s_rs1; // cm.mva01s + default: predecode_rs1_coarse = {2'b01, next_instr[9:7]}; + endcase + + casez ({next_instr_is_32bit, |EXTENSION_ZCMP, |EXTENSION_ZILSD, |EXTENSION_ZCLSD, next_instr[15:0]}) + {1'b1, 1'bz, 1'b1, 1'bz, 16'bzz11zzzzz0z0zzzz}: predecode_rs2_coarse = zilsd_rs2_coarse; // ld, sd (Zilsd) + {1'b1, 1'bz, 1'b0, 1'bz, 16'bzz11zzzzz0z0zzzz}: predecode_rs2_coarse = next_instr[24:20]; // ld, sd (no Zilsd) + + {1'b1, 1'bz, 1'bz, 1'bz, 16'bzz0zzzzzzzzzzzzz}: predecode_rs2_coarse = next_instr[24:20]; // (cover remaining 32-bit + {1'b1, 1'bz, 1'bz, 1'bz, 16'bzz10zzzzzzzzzzzz}: predecode_rs2_coarse = next_instr[24:20]; // patterns, without overlap) + {1'b1, 1'bz, 1'bz, 1'bz, 16'bzz11zzzzz1zzzzzz}: predecode_rs2_coarse = next_instr[24:20]; + {1'b1, 1'bz, 1'bz, 1'bz, 16'bzz11zzzzz0z1zzzz}: predecode_rs2_coarse = next_instr[24:20]; + + {1'b0, 1'bz, 1'b1, 1'b1, 16'bzz1zzzzzzzzzzz00}: predecode_rs2_coarse = zclsd_sd_rs2_coarse; + {1'b0, 1'bz, 1'bz, 1'bz, 16'bzz0zzzzzzzzzzz10}: predecode_rs2_coarse = next_instr[6:2]; // c.add, c.swsp + {1'b0, 1'b1, 1'bz, 1'bz, 16'bz01zzzzzzzzzzz10}: predecode_rs2_coarse = zcmp_pushpop_rs2; // cm.push + {1'b0, 1'bz, 1'b1, 1'b1, 16'bz11zzzzzzzzzzz10}: predecode_rs2_coarse = zclsd_sdsp_rs2_coarse; + default: predecode_rs2_coarse = {2'b01, next_instr[4:2]}; + endcase + + // The "fine" predecode targets those instructions which either: + // - Have an implicit zero-register operand in their expanded form (e.g. c.beqz) + // - Do not have a register operand on that port, but rely on the port being 0 + // We don't care about instructions which ignore the reg ports, e.g. ebreak + + casez ({|EXTENSION_C, next_instr}) + // -> addi rd, x0, imm: + {1'b1, 16'hzzzz, `RVOPC_C_LI}: predecode_rs1_fine = 5'd0; + {1'b1, 16'hzzzz, `RVOPC_C_MV}: begin + if (next_instr[6:2] == 5'd0) begin + // c.jr has rs1 as normal + predecode_rs1_fine = predecode_rs1_coarse; + end else begin + // -> add rd, x0, rs2: + predecode_rs1_fine = 5'd0; + end + end + default: predecode_rs1_fine = predecode_rs1_coarse; + endcase + + casez ({|EXTENSION_C, |EXTENSION_ZILSD, |EXTENSION_ZCLSD, next_instr}) + {1'b1, 1'bz, 1'bz, 16'hzzzz, `RVOPC_C_BEQZ}: predecode_rs2_fine = 5'd0; // -> beq rs1, x0, label + {1'b1, 1'bz, 1'bz, 16'hzzzz, `RVOPC_C_BNEZ}: predecode_rs2_fine = 5'd0; // -> bne rs1, x0, label + {1'b1, 1'b1, 1'bz, `RVOPC_SD }: predecode_rs2_fine = predecode_rs2_coarse & {5{|next_instr[24:21]}}; + {1'b1, 1'b1, 1'b1, 16'hzzzz, `RVOPC_C_SDSP}: predecode_rs2_fine = predecode_rs2_coarse & {5{|next_instr[ 6: 3]}}; + default: predecode_rs2_fine = predecode_rs2_coarse; + endcase + + if (|EXTENSION_E) begin + predecode_rs1_coarse[4] = 1'b0; + predecode_rs2_coarse[4] = 1'b0; + predecode_rs1_fine[4] = 1'b0; + predecode_rs2_fine[4] = 1'b0; + end + +end + +// ---------------------------------------------------------------------------- +// Instruction decompression + +// Instructions are decompressed at the end of stage 1 (fetch data phase). On +// ASIC, where the register file is synthesised with muxes, this puts +// decompression somewhat in parallel with register file read, which uses +// approximately decoded regnums. + +generate +if (~|EXTENSION_C) begin: no_decompress + + // No decompression; instructions decoded directly from prefetch buffer + always @ (*) begin + cir = { + buf_contents[1 * W_SLOT +: W_BUNDLE], + buf_contents[0 * W_SLOT +: W_BUNDLE] + } | 32'd3; + cir_is_32bit = 1'b1; + cir_invalid_16bit = ~&buf_contents[1:0]; + cir_is_uop = 1'b0; + cir_uop_nonfinal = 1'b0; + cir_uop_no_pc_update = 1'b0; + cir_uop_atomic = 1'b0; + end + + assign decomp_uop_step = 4'h0; + +end else begin: have_decompress + + wire decomp_instr_is_32bit; + wire [31:0] decomp_instr_out; + wire decomp_is_uop; + wire decomp_is_final_uop; + wire decomp_uop_no_pc_update; + wire decomp_uop_atomic; + wire decomp_invalid; + + wire first_uop = ~|decomp_uop_step; + // Ensure the first uop goes straight through, as it is registered into CIR: + wire uop_stall_non_first = first_uop ? ~|buf_level_next : uop_stall; + // Ensure the uop counter stops at 0 after rolling over once: + wire uop_stall_on_repeat = cir_is_uop && !cir_uop_nonfinal && ~|cir_use; + + hazard3_instr_decompress #( + `include "hazard3_config_inst.vh" + ) decomp ( + .clk (clk), + .rst_n (rst_n), + + .instr_in (next_instr), + + .instr_is_32bit (decomp_instr_is_32bit), + .instr_out (decomp_instr_out), + + .instr_out_is_uop (decomp_is_uop), + .instr_out_is_final_uop (decomp_is_final_uop), + .instr_out_uop_no_pc_update (decomp_uop_no_pc_update), + .instr_out_uop_atomic (decomp_uop_atomic), + .instr_out_uop_stall (uop_stall_non_first || uop_stall_on_repeat), + .instr_out_uop_clear (uop_clear), + .df_uop_step (decomp_uop_step), + + .invalid (decomp_invalid) + ); + + wire cir_clken = + ~|cir_vld || (!cir_vld[1] && &buf_contents[1:0]) || + |cir_use || (|EXTENSION_ZCMP && cir_is_uop && !uop_stall) || + (|EXTENSION_ZCMP && uop_clear); + + wire cir_is_uop_next = decomp_is_uop && |buf_level_next; + wire cir_uop_nonfinal_next = cir_is_uop_next && !decomp_is_final_uop; + + always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + cir <= 32'd3; + cir_is_32bit <= 1'b0; + cir_invalid_16bit <= 1'b0; + cir_is_uop <= 1'b0; + cir_uop_nonfinal <= 1'b0; + cir_uop_no_pc_update <= 1'b0; + cir_uop_atomic <= 1'b0; + end else if (cir_clken) begin + cir <= decomp_instr_out | 32'd3; + cir_is_32bit <= decomp_instr_is_32bit; + cir_invalid_16bit <= decomp_invalid; + cir_is_uop <= |EXTENSION_ZCMP && !uop_clear && cir_is_uop_next; + cir_uop_nonfinal <= |EXTENSION_ZCMP && !uop_clear && cir_uop_nonfinal_next; + cir_uop_no_pc_update <= |EXTENSION_ZCMP && !uop_clear && decomp_uop_no_pc_update; + cir_uop_atomic <= |EXTENSION_ZCMP && !uop_clear && decomp_uop_atomic; + end + end + +end +endgenerate + +assign cir_raw = { + buf_contents[1 * W_SLOT +: W_BUNDLE], + buf_contents[0 * W_SLOT +: W_BUNDLE] +}; + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_instr_decompress.v b/vendor/Hazard3/hdl/hazard3_instr_decompress.v new file mode 100644 index 0000000..ddfab1a --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_instr_decompress.v @@ -0,0 +1,478 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2023 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// Little instructions go in, big instructions come out + +`default_nettype none + +module hazard3_instr_decompress #( +`include "hazard3_config.vh" +) ( + input wire clk, + input wire rst_n, + + input wire [31:0] instr_in, + + output reg instr_is_32bit, + output reg [31:0] instr_out, + + // If instruction is a non-final uop, need to suppress PC update, and null + // the PC offset in the mepc address in stage 3. + output wire instr_out_is_uop, + output wire instr_out_is_final_uop, + output wire instr_out_uop_no_pc_update, + // Indicate instr_out is a uop from the noninterruptible part of a uop + // sequence. If one uop is noninterruptible, all following uops until the + // end of the sequence are also noninterruptible. + output wire instr_out_uop_atomic, + // Current ucode sequence is stalled on downstream execution + input wire instr_out_uop_stall, + input wire instr_out_uop_clear, + + // To regnum decoder in frontend + output wire [3:0] df_uop_step, + + output reg invalid +); + +`include "rv_opcodes.vh" + +localparam W_REGADDR = 5; +localparam PASSTHROUGH = ~|EXTENSION_C; + +// Long-register formats: cr, ci, css +// Short-register formats: ciw, cl, cs, cb, cj +wire [W_REGADDR-1:0] rd_l = instr_in[11:7]; +wire [W_REGADDR-1:0] rs1_l = instr_in[11:7]; +wire [W_REGADDR-1:0] rs2_l = instr_in[6:2]; +wire [W_REGADDR-1:0] rd_s = {2'b01, instr_in[4:2]}; +wire [W_REGADDR-1:0] rs1_s = {2'b01, instr_in[9:7]}; +wire [W_REGADDR-1:0] rs2_s = {2'b01, instr_in[4:2]}; + +// Mapping of cx -> x immediate formats (we are *expanding* instructions, not +// decoding them): + +wire [31:0] imm_ci = { + {7{instr_in[12]}}, + instr_in[6:2], + 20'h00000 +}; + +wire [31:0] imm_cj = { + instr_in[12], + instr_in[8], + instr_in[10:9], + instr_in[6], + instr_in[7], + instr_in[2], + instr_in[11], + instr_in[5:3], + {9{instr_in[12]}}, + 12'h000 +}; + +wire [31:0] imm_cb ={ + {4{instr_in[12]}}, + instr_in[6:5], + instr_in[2], + 13'h0000, + instr_in[11:10], + instr_in[4:3], + instr_in[12], + 7'h00 +}; + +wire [31:0] imm_c_lb = { + 10'h0, + instr_in[5], + instr_in[6], + 20'h00000 +}; + +wire [31:0] imm_c_lh = { + 10'h000, + instr_in[5], + 1'b0, + 20'h00000 +}; + +function [31:0] rfmt_rd; input [4:0] rd; begin rfmt_rd = {20'h00000, rd, 7'h00}; end endfunction +function [31:0] rfmt_rs1; input [4:0] rs1; begin rfmt_rs1 = {12'h000, rs1, 15'h0000}; end endfunction +function [31:0] rfmt_rs2; input [4:0] rs2; begin rfmt_rs2 = {7'h00, rs2, 20'h00000}; end endfunction + +// ---------------------------------------------------------------------------- +// Push/pop and friends + +// The longest uop sequence is a maximal cm.popretz: +// +// - 13x lw (counter = 0..12) +// - 1x addi to set a0 to zero (counter = 13 ) < atomic section +// - 1x jalr to jump through ra (counter = 14 ) < atomic section +// - 1x addi to adjust sp (counter = 15 ) < atomic section + +wire [3:0] uop_ctr; +reg [3:0] uop_ctr_nxt_in_seq; +reg in_uop_seq; +reg uop_no_pc_update; + +wire zcmp_is_pushpop = instr_in[12]; +wire uop_seq_end = |EXTENSION_ZCMP && (zcmp_is_pushpop ? uop_ctr == 4'hf : uop_ctr[0]); +wire uop_atomic = |EXTENSION_ZCMP && (zcmp_is_pushpop ? uop_ctr >= 4'he : uop_ctr[0]); + +wire [3:0] uop_ctr_nxt = + instr_out_uop_clear ? 4'h0 : + instr_out_uop_stall ? uop_ctr : uop_ctr_nxt_in_seq; + +assign instr_out_is_uop = in_uop_seq; +assign instr_out_is_final_uop = uop_seq_end; +assign instr_out_uop_atomic = uop_atomic; +assign instr_out_uop_no_pc_update = uop_no_pc_update; +assign df_uop_step = uop_ctr; + +// The offset from current sp value to the lowest-addressed saved register, +64. +wire [3:0] zcmp_rlist = instr_in[7:4]; +wire [3:0] zcmp_n_regs = zcmp_rlist == 4'hf ? 4'hd : zcmp_rlist - 4'h3; +wire zcmp_rlist_invalid = zcmp_rlist < 4'h4 || (|EXTENSION_E && zcmp_rlist > 4'h6); + +wire [11:0] zcmp_stack_adj_base = + zcmp_rlist == 4'hf ? 12'h040 : + zcmp_rlist >= 4'hc ? 12'h030 : + zcmp_rlist >= 4'h8 ? 12'h020 : 12'h010; + +wire [11:0] zcmp_stack_adj = zcmp_stack_adj_base + {6'h00, instr_in[3:2], 4'h0}; + +// Note we perform all load/stores before moving the stack pointer. +wire [11:0] zcmp_stack_lw_offset = -{6'h00, {zcmp_n_regs - uop_ctr}, 2'h0} + zcmp_stack_adj; +wire [11:0] zcmp_stack_sw_offset = -{6'h00, {zcmp_n_regs - uop_ctr}, 2'h0}; + +wire [4:0] zcmp_ls_reg = + uop_ctr == 4'h0 ? 5'd01 : // ra + uop_ctr == 4'h1 ? 5'd08 : // s0 + uop_ctr == 4'h2 ? 5'd09 : // s1 + 5'd15 + {1'b0, uop_ctr}; // s2-s11 (s2 == x18) + +wire [31:0] zcmp_push_sw_instr = `RVOPC_NOZ_SW | rfmt_rs1(5'd2) | rfmt_rs2(zcmp_ls_reg) | { + zcmp_stack_sw_offset[11:5], 13'h0000, zcmp_stack_sw_offset[4:0], 7'h00 +}; + +wire [31:0] zcmp_pop_lw_instr = `RVOPC_NOZ_LW | rfmt_rd(zcmp_ls_reg) | rfmt_rs1(5'd2)| { + zcmp_stack_lw_offset[11:0], 20'h00000 +}; + +wire [31:0] zcmp_push_stack_adj_instr = `RVOPC_NOZ_ADDI | rfmt_rd(5'd2) | rfmt_rs1(5'd2) | { + -zcmp_stack_adj, + 20'h00000 +}; + +wire [31:0] zcmp_pop_stack_adj_instr = `RVOPC_NOZ_ADDI | rfmt_rd(5'd2) | rfmt_rs1(5'd2) | { + zcmp_stack_adj, + 20'h00000 +}; + +wire [4:0] zcmp_sa01_r1s = {|instr_in[9:8], ~|instr_in[9:8], instr_in[9:7]}; +wire [4:0] zcmp_sa01_r2s = {|instr_in[4:3], ~|instr_in[4:3], instr_in[4:2]}; + +wire zcmp_sa01_invalid = |EXTENSION_E && |{instr_in[9:8], instr_in[4:3]}; + +// ---------------------------------------------------------------------------- + +generate +if (PASSTHROUGH) begin: instr_passthrough + always @ (*) begin + instr_is_32bit = 1'b1; + instr_out = instr_in; + invalid = 1'b0; + end +end else begin: instr_decompress + always @ (*) begin + if (instr_in[1:0] == 2'b11) begin + instr_is_32bit = 1'b1; + instr_out = instr_in; + invalid = 1'b0; + in_uop_seq = 1'b0; + uop_no_pc_update = 1'b0; + uop_ctr_nxt_in_seq = uop_ctr; + end else begin + instr_is_32bit = 1'b0; + instr_out = 32'd0; + invalid = 1'b0; + in_uop_seq = 1'b0; + uop_no_pc_update = 1'b0; + uop_ctr_nxt_in_seq = uop_ctr; + casez (instr_in[15:0]) + `RVOPC_C_ADDI4SPN: begin + instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(rd_s) | rfmt_rs1(5'd2) + | {2'h0, instr_in[10:7], instr_in[12:11], instr_in[5], instr_in[6], 2'b00, 20'h00000}; + invalid = ~|instr_in[12:2]; // Always-invalid all-zeroes instruction + end + `RVOPC_C_LW: instr_out = `RVOPC_NOZ_LW | rfmt_rd(rd_s) | rfmt_rs1(rs1_s) + | {5'h00, instr_in[5], instr_in[12:10], instr_in[6], 2'b00, 20'h00000}; + `RVOPC_C_SW: instr_out = `RVOPC_NOZ_SW | rfmt_rs2(rs2_s) | rfmt_rs1(rs1_s) + | {5'h00, instr_in[5], instr_in[12], 13'h0000, instr_in[11:10], instr_in[6], 2'b00, 7'h00}; + `RVOPC_C_ADDI: instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(rd_l) | rfmt_rs1(rs1_l) | imm_ci; + `RVOPC_C_JAL: instr_out = `RVOPC_NOZ_JAL | rfmt_rd(5'd1) | imm_cj; + `RVOPC_C_J: instr_out = `RVOPC_NOZ_JAL | rfmt_rd(5'd0) | imm_cj; + `RVOPC_C_LI: instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(rd_l) | imm_ci; + `RVOPC_C_LUI: begin + if (rd_l == 5'd2) begin + // addi16sp + instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd2) | rfmt_rs1(5'd2) | + {{3{instr_in[12]}}, instr_in[4:3], instr_in[5], instr_in[2], instr_in[6], 24'h000000}; + end else begin + instr_out = `RVOPC_NOZ_LUI | rfmt_rd(rd_l) | {{15{instr_in[12]}}, instr_in[6:2], 12'h000}; + end + invalid = ~|{instr_in[12], instr_in[6:2]}; // RESERVED if imm == 0 + end + `RVOPC_C_SLLI: instr_out = `RVOPC_NOZ_SLLI | rfmt_rd(rs1_l) | rfmt_rs1(rs1_l) | imm_ci; + `RVOPC_C_SRAI: instr_out = `RVOPC_NOZ_SRAI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | imm_ci; + `RVOPC_C_SRLI: instr_out = `RVOPC_NOZ_SRLI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | imm_ci; + `RVOPC_C_ANDI: instr_out = `RVOPC_NOZ_ANDI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | imm_ci; + `RVOPC_C_AND: instr_out = `RVOPC_NOZ_AND | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s); + `RVOPC_C_OR: instr_out = `RVOPC_NOZ_OR | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s); + `RVOPC_C_XOR: instr_out = `RVOPC_NOZ_XOR | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s); + `RVOPC_C_SUB: instr_out = `RVOPC_NOZ_SUB | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s); + `RVOPC_C_ADD: begin + if (|rs2_l) begin + instr_out = `RVOPC_NOZ_ADD | rfmt_rd(rd_l) | rfmt_rs1(rs1_l) | rfmt_rs2(rs2_l); + end else if (|rs1_l) begin // jalr + instr_out = `RVOPC_NOZ_JALR | rfmt_rd(5'd1) | rfmt_rs1(rs1_l); + end else begin // ebreak + instr_out = `RVOPC_NOZ_EBREAK; + end + end + `RVOPC_C_MV: begin + if (|rs2_l) begin // mv + instr_out = `RVOPC_NOZ_ADD | rfmt_rd(rd_l) | rfmt_rs2(rs2_l); + end else begin // jr + instr_out = `RVOPC_NOZ_JALR | rfmt_rs1(rs1_l); + invalid = ~|rs1_l; // RESERVED + end + end + `RVOPC_C_LWSP: begin + instr_out = `RVOPC_NOZ_LW | rfmt_rd(rd_l) | rfmt_rs1(5'd2) | + {4'h0, instr_in[3:2], instr_in[12], instr_in[6:4], 2'b00, 20'h00000}; + invalid = ~|rd_l; // RESERVED + end + `RVOPC_C_SWSP: instr_out = `RVOPC_NOZ_SW | rfmt_rs2(rs2_l) | rfmt_rs1(5'd2) + | {4'h0, instr_in[8:7], instr_in[12], 13'h0000, instr_in[11:9], 2'b00, 7'h00}; + `RVOPC_C_BEQZ: instr_out = `RVOPC_NOZ_BEQ | rfmt_rs1(rs1_s) | imm_cb; + `RVOPC_C_BNEZ: instr_out = `RVOPC_NOZ_BNE | rfmt_rs1(rs1_s) | imm_cb; + + // Optional Zcb instructions: + `RVOPC_C_LBU: begin + instr_out = `RVOPC_NOZ_LBU | rfmt_rd(rd_s) | rfmt_rs1(rs1_s) | imm_c_lb; + invalid = ~|EXTENSION_ZCB; + end + `RVOPC_C_LHU: begin + instr_out = `RVOPC_NOZ_LHU | rfmt_rd(rd_s) | rfmt_rs1(rs1_s) | imm_c_lh; + invalid = ~|EXTENSION_ZCB; + end + `RVOPC_C_LH: begin + instr_out = `RVOPC_NOZ_LH | rfmt_rd(rd_s) | rfmt_rs1(rs1_s) | imm_c_lh; + invalid = ~|EXTENSION_ZCB; + end + `RVOPC_C_SB: begin + instr_out = `RVOPC_NOZ_SB | rfmt_rs2(rd_s) | rfmt_rs1(rs1_s) | imm_c_lb >> 13; + invalid = ~|EXTENSION_ZCB; + end + `RVOPC_C_SH: begin + instr_out = `RVOPC_NOZ_SH | rfmt_rs2(rd_s) | rfmt_rs1(rs1_s) | imm_c_lh >> 13; + invalid = ~|EXTENSION_ZCB; + end + `RVOPC_C_ZEXT_B: begin + instr_out = `RVOPC_NOZ_ANDI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | 32'h0ff00000; + invalid = ~|EXTENSION_ZCB; + end + `RVOPC_C_SEXT_B: begin + instr_out = `RVOPC_NOZ_SEXT_B | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s); + invalid = ~|EXTENSION_ZCB || ~|EXTENSION_ZBB; + end + `RVOPC_C_ZEXT_H: begin + instr_out = `RVOPC_NOZ_ZEXT_H | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s); + invalid = ~|EXTENSION_ZCB || ~|EXTENSION_ZBB; + end + `RVOPC_C_SEXT_H: begin + instr_out = `RVOPC_NOZ_SEXT_H | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s); + invalid = ~|EXTENSION_ZCB || ~|EXTENSION_ZBB; + end + `RVOPC_C_NOT: begin + instr_out = `RVOPC_NOZ_XORI | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | 32'hfff00000; + invalid = ~|EXTENSION_ZCB; + end + `RVOPC_C_MUL: begin + instr_out = `RVOPC_NOZ_MUL | rfmt_rd(rs1_s) | rfmt_rs1(rs1_s) | rfmt_rs2(rs2_s); + invalid = ~|EXTENSION_ZCB || ~|EXTENSION_M; + end + + // Optional Zclsd instructions: + `RVOPC_C_LD: begin + instr_out = `RVOPC_NOZ_LD | rfmt_rd(rd_s) | rfmt_rs1(rs1_s) + | {4'h0, instr_in[6:5], instr_in[12:10], 3'b000, 20'h00000}; + invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD; + end + `RVOPC_C_SD: begin + instr_out = `RVOPC_NOZ_SD | rfmt_rs2(rs2_s) | rfmt_rs1(rs1_s) + | {4'h0, instr_in[6:5], instr_in[12], 13'h0000, instr_in[11:10], 3'b000, 7'h00}; + invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD; + end + `RVOPC_C_LDSP: begin + instr_out = `RVOPC_NOZ_LD | rfmt_rd(rd_l) | rfmt_rs1(5'd2) | + {3'h0, instr_in[4:2], instr_in[12], instr_in[6:5], 3'b000, 20'h00000}; + invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD || ~|rd_l; // RESERVED + end + `RVOPC_C_SDSP: begin + instr_out = `RVOPC_NOZ_SD | rfmt_rs2(rs2_l) | rfmt_rs1(5'd2) + | {3'h0, instr_in[9:7], instr_in[12], 13'h0000, instr_in[11:10], 3'b000, 7'h00}; + invalid = ~|EXTENSION_ZILSD || ~|EXTENSION_ZCLSD; + end + + // Optional Zcmp instructions: + `RVOPC_CM_PUSH: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin + invalid = 1'b1; + end else if (uop_ctr == 4'hf) begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = 4'h0; + instr_out = zcmp_push_stack_adj_instr; + end else begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = uop_ctr + 4'h1; + instr_out = zcmp_push_sw_instr; + uop_no_pc_update = 1'b1; + if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin + uop_ctr_nxt_in_seq = 4'hf; + end + end + + `RVOPC_CM_POP: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin + invalid = 1'b1; + end else if (uop_ctr == 4'hf) begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = 4'h0; + instr_out = zcmp_pop_stack_adj_instr; + end else begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = uop_ctr + 4'h1; + uop_no_pc_update = 1'b1; + instr_out = zcmp_pop_lw_instr; + if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin + uop_ctr_nxt_in_seq = 4'hf; + end + end + + `RVOPC_CM_POPRET: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin + invalid = 1'b1; + end else if (uop_ctr == 4'he) begin + // Note although this is only the first instruction in the uninterruptible sequence, + // we mark this instruction as uninterruptible: there is some special case logic to + // allow this jump to execute without flushing the final stack adjust uop, which can + // cause the wrong exception PC to be sampled if this uop is interrupted. + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = uop_ctr + 4'h1; + instr_out = `RVOPC_NOZ_JALR | rfmt_rs1(5'd1); + end else if (uop_ctr == 4'hf) begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = 4'h0; + uop_no_pc_update = 1'b1; + instr_out = zcmp_pop_stack_adj_instr; + end else begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = uop_ctr + 4'h1; + instr_out = zcmp_pop_lw_instr; + uop_no_pc_update = 1'b1; + if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin + uop_ctr_nxt_in_seq = 4'he; + end + end + + `RVOPC_CM_POPRETZ: if (~|EXTENSION_ZCMP || zcmp_rlist_invalid) begin + invalid = 1'b1; + end else if (uop_ctr == 4'hd) begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = uop_ctr + 4'h1; + uop_no_pc_update = 1'b1; + instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd10); // li a0, 0 + end else if (uop_ctr == 4'he) begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = uop_ctr + 4'h1; + instr_out = `RVOPC_NOZ_JALR | rfmt_rs1(5'd1); + end else if (uop_ctr == 4'hf) begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = 4'h0; + uop_no_pc_update = 1'b1; + instr_out = zcmp_pop_stack_adj_instr; + end else begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = uop_ctr + 4'h1; + uop_no_pc_update = 1'b1; + instr_out = zcmp_pop_lw_instr; + if (uop_ctr_nxt_in_seq == zcmp_n_regs) begin + uop_ctr_nxt_in_seq = 4'hd; + end + end + + `RVOPC_CM_MVSA01: if (~|EXTENSION_ZCMP || zcmp_sa01_invalid) begin + invalid = 1'b1; + end else if (uop_ctr == 4'h0) begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = uop_ctr + 4'h1; + uop_no_pc_update = 1'b1; + instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(zcmp_sa01_r1s) | rfmt_rs1(5'd10); + end else begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = 4'h0; + instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(zcmp_sa01_r2s) | rfmt_rs1(5'd11); + end + + `RVOPC_CM_MVA01S: if (~|EXTENSION_ZCMP || zcmp_sa01_invalid) begin + invalid = 1'b1; + end else if (uop_ctr == 4'h0) begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = uop_ctr + 4'h1; + uop_no_pc_update = 1'b1; + instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd10) | rfmt_rs1(zcmp_sa01_r1s); + end else begin + in_uop_seq = 1'b1; + uop_ctr_nxt_in_seq = 4'h0; + instr_out = `RVOPC_NOZ_ADDI | rfmt_rd(5'd11) | rfmt_rs1(zcmp_sa01_r2s); + end + + default: invalid = 1'b1; + endcase + end + end +end +endgenerate + +generate +if (EXTENSION_ZCMP) begin: have_uop_ctr + reg [3:0] uop_ctr_r; + assign uop_ctr = uop_ctr_r; + always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + uop_ctr_r <= 4'h0; + end else begin + uop_ctr_r <= uop_ctr_nxt; +`ifdef HAZARD3_ASSERTIONS + assert(in_uop_seq || uop_ctr_r == 4'h0); + assert(in_uop_seq || zcmp_ls_reg == 5'h01); + assert(in_uop_seq || !uop_atomic); + assert(in_uop_seq || !uop_no_pc_update); + if (uop_seq_end) begin + assert(in_uop_seq); + assert(instr_out_uop_stall || uop_ctr_nxt == 4'h0); + end +`endif + end + end +end else begin: no_uop_ctr + assign uop_ctr = 4'h0; +end +endgenerate + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_irq_ctrl.v b/vendor/Hazard3/hdl/hazard3_irq_ctrl.v new file mode 100644 index 0000000..068e034 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_irq_ctrl.v @@ -0,0 +1,327 @@ +/*****************************************************************************\ +| Copyright (C) 2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +`default_nettype none + +// Hazard3 interrupt controller. Support for up to 512 external interrupt +// lines, with up to 16 levels of preemption. + +module hazard3_irq_ctrl #( +`include "hazard3_config.vh" +) ( + input wire clk, + input wire clk_always_on, + input wire rst_n, + + // CSR interface + input wire [11:0] addr, + input wire [1:0] wtype, + input wire wen_m_mode, + input wire ren_m_mode, + input wire [W_DATA-1:0] wdata_raw, + input wire [W_DATA-1:0] wdata, + output reg [W_DATA-1:0] rdata, + + // Trap entry/exit signals for context update + input wire trapreg_update_enter, + input wire trapreg_update_exit, + input wire trap_entry_is_eirq, + + // Interface for clearing and saving mie.mtie/msie via meicontext + output wire meicontext_clearts, + input wire mie_mtie, + input wire mie_msie, + + // External IRQ inputs: + input wire [NUM_IRQS-1:0] irq, + + // mip.meip: + output wire external_irq_pending +); + +`include "hazard3_ops.vh" +`include "hazard3_csr_addr.vh" + +localparam MAX_IRQS = 512; +localparam [3:0] IRQ_PRIORITY_MASK = ~(4'hf >> IRQ_PRIORITY_BITS); +localparam W_IRQ_INDEX = $clog2(MAX_IRQS); + +// ---------------------------------------------------------------------------- +// IRQ input flops + +// Register external IRQ signals (mainly to avoid a through-path from IRQs to +// bus request signals). Always clocked, as it's used to generate a wakeup. +// Input registers can be removed on a per-IRQ basis, but this should be done +// with care as it does create a through-path from the IRQ to the bus. + +wire [NUM_IRQS-1:0] irq_r; + +genvar g; +generate +for (g = 0; g < NUM_IRQS; g = g + 1) begin: irq_reg_loop + if (IRQ_INPUT_BYPASS[g]) begin: no_reg + assign irq_r[g] = irq[g]; + end else begin: have_reg + reg q; + always @ (posedge clk_always_on or negedge rst_n) begin + if (!rst_n) begin + q <= 1'b0; + end else begin + q <= irq[g]; + end + end + assign irq_r[g] = q; + end +end +endgenerate + +// ---------------------------------------------------------------------------- +// CSR write + +// Assigned later: +wire [W_IRQ_INDEX-1:0] meinext_irq; +wire meinext_noirq; +reg [3:0] eirq_highest_priority; + +// Interrupt array registers: +reg [NUM_IRQS-1:0] meiea; +reg [NUM_IRQS-1:0] meifa; +reg [4*NUM_IRQS-1:0] meipra; + +// Padded vectors for CSR readout +wire [MAX_IRQS-1:0] meiea_rdata = {{MAX_IRQS-NUM_IRQS{1'b0}}, meiea}; +wire [MAX_IRQS-1:0] meifa_rdata = {{MAX_IRQS-NUM_IRQS{1'b0}}, meifa}; +wire [4*MAX_IRQS-1:0] meipra_rdata = {{4*(MAX_IRQS-NUM_IRQS){1'b0}}, meipra}; + +always @ (posedge clk or negedge rst_n) begin: update_irq_reg_arrays + reg signed [31:0] i; + if (!rst_n) begin + meiea <= {NUM_IRQS{1'b0}}; + meifa <= {NUM_IRQS{1'b0}}; + meipra <= {4*NUM_IRQS{1'b0}}; + end else begin + for (i = 0; i < NUM_IRQS; i = i + 1) begin + // CSR write update. Note raw wdata is used for array indexing -- + // necessary for correctness, and also avoid a loop with rdata. + if (wen_m_mode && addr == MEIEA && $signed(wdata_raw[4:0]) == i[W_IRQ_INDEX-1:4]) begin + meiea[i] <= wdata[16 + (i % 16)]; + end + if (wen_m_mode && addr == MEIFA && $signed(wdata_raw[4:0]) == i[W_IRQ_INDEX-1:4]) begin + meifa[i] <= wdata[16 + (i % 16)]; + end + if (wen_m_mode && addr == MEIPRA && $signed(wdata_raw[6:0]) == i[W_IRQ_INDEX-1:2]) begin + meipra[4 * i +: 4] <= wdata[16 + 4 * (i % 4) +: 4] & IRQ_PRIORITY_MASK; + end + // Clear IRQ force when the corresponding IRQ is sampled from meinext + // (so that an IRQ can be posted *once* without modifying the ISR source) + if (meinext_irq == i[W_IRQ_INDEX-1:0] && ren_m_mode && addr == MEINEXT && !meinext_noirq) begin + meifa[i[$clog2(NUM_IRQS)-1:0]] <= 1'b0; + end + end + end +end + +reg [3:0] meicontext_pppreempt; +reg [3:0] meicontext_ppreempt; +reg [4:0] meicontext_preempt; +reg meicontext_noirq; +reg [W_IRQ_INDEX-1:0] meicontext_irq; +reg meicontext_mreteirq; + +wire [4:0] preempt_level_next = meinext_noirq ? 5'h10 : ( + (5'd1 << (4 - IRQ_PRIORITY_BITS)) + {1'b0, eirq_highest_priority} +); + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + meicontext_pppreempt <= 4'h0; + meicontext_ppreempt <= 4'h0; + meicontext_preempt <= 5'h0; + meicontext_noirq <= 1'b1; + meicontext_irq <= {W_IRQ_INDEX{1'b0}}; + meicontext_mreteirq <= 1'b0; + end else if (trapreg_update_enter) begin + if (trap_entry_is_eirq) begin + // Priority save. Note the MSB of preempt needn't be saved since, + // when it is set, preemption is impossible, so we won't be here. + meicontext_pppreempt <= meicontext_ppreempt & IRQ_PRIORITY_MASK; + meicontext_ppreempt <= meicontext_preempt[3:0] & IRQ_PRIORITY_MASK; + // Setting preempt isn't strictly necessary, since an updating read + // of meinext ought to be performed before re-enabling IRQs via + // mstatus.mie, but it seems the least surprising thing to do: + meicontext_preempt <= preempt_level_next & {1'b1, IRQ_PRIORITY_MASK}; + meicontext_mreteirq <= 1'b1; + end else begin + meicontext_mreteirq <= 1'b0; + end + end else if (trapreg_update_exit) begin + meicontext_mreteirq <= 1'b0; + if (meicontext_mreteirq) begin + // Priority restore + meicontext_pppreempt <= 4'h0; + meicontext_ppreempt <= meicontext_pppreempt & IRQ_PRIORITY_MASK; + meicontext_preempt <= {1'b0, meicontext_ppreempt & IRQ_PRIORITY_MASK}; + end + end else if (wen_m_mode && addr == MEICONTEXT) begin + meicontext_pppreempt <= wdata[31:28] & IRQ_PRIORITY_MASK; + meicontext_ppreempt <= wdata[27:24] & IRQ_PRIORITY_MASK; + meicontext_preempt <= wdata[20:16] & {1'b1, IRQ_PRIORITY_MASK}; + meicontext_noirq <= wdata[15]; + meicontext_irq <= wdata[12:4]; + meicontext_mreteirq <= wdata[0]; + end else if (wen_m_mode && addr == MEINEXT && wdata[0]) begin + // Interrupt has been sampled, with the update request set, so update + // the context (including preemption level) appropriately. + meicontext_preempt <= preempt_level_next & {1'b1, IRQ_PRIORITY_MASK}; + meicontext_noirq <= meinext_noirq; + meicontext_irq <= meinext_irq; + end +end + +assign meicontext_clearts = wen_m_mode && wtype != CSR_WTYPE_C && addr == MEICONTEXT && wdata_raw[1]; + +// ---------------------------------------------------------------------------- +// External interrupt logic + +// Trap request is asserted when there is an interrupt at or above our current +// preemption level. meinext displays interrupts at or above our *previous* +// preemption level: this masking helps avoid re-taking IRQs in frames that you +// have preempted. + +wire [NUM_IRQS-1:0] meipa = irq_r | meifa; +wire [MAX_IRQS-1:0] meipa_rdata = {{MAX_IRQS-NUM_IRQS{1'b0}}, meipa}; + +reg [NUM_IRQS-1:0] eirq_active_above_preempt; +reg [NUM_IRQS-1:0] eirq_active_above_ppreempt; + +always @ (*) begin: eirq_compare + integer i; + for (i = 0; i < NUM_IRQS; i = i + 1) begin + eirq_active_above_preempt[i] = meipa[i] && meiea[i] && {1'b0, meipra[i * 4 +: 4]} >= meicontext_preempt; + eirq_active_above_ppreempt[i] = meipa[i] && meiea[i] && meipra[i * 4 +: 4] >= meicontext_ppreempt; + end +end + +assign external_irq_pending = |eirq_active_above_preempt; +assign meinext_noirq = ~|eirq_active_above_ppreempt; + +// Two things remaining to calculate: +// +// - What is the IRQ number of the highest-priority pending IRQ that is above +// meicontext.ppreempt +// - What is the priority of that IRQ +// +// In the second case we can relax the calculation to ignore ppreempt, since it +// only needs to be valid if such an IRQ exists. Currently we choose to reuse +// the same priority selector (possibly longer critpath while saving area), but +// we could use a second priority selector that ignores ppreempt masking. + +wire [NUM_IRQS-1:0] highest_eirq_onehot; +wire [W_IRQ_INDEX-1:0] meinext_irq_unmasked; + +hazard3_onehot_priority_dynamic #( + .W_REQ (NUM_IRQS), + .N_PRIORITIES (16), + .PRIORITY_HIGHEST_WINS (1), + .TIEBREAK_HIGHEST_WINS (0) +) eirq_priority_u ( + .pri (meipra[4*NUM_IRQS-1:0] & {NUM_IRQS{IRQ_PRIORITY_MASK}}), + .req (eirq_active_above_ppreempt), + .gnt (highest_eirq_onehot) +); + +always @ (*) begin: get_highest_eirq_priority + integer i; + eirq_highest_priority = 4'h0; + for (i = 0; i < NUM_IRQS; i = i + 1) begin + eirq_highest_priority = eirq_highest_priority | ( + meipra[4 * i +: 4] & {4{highest_eirq_onehot[i]}} + ); + end +end + +wire [$clog2(NUM_IRQS)-1:0] meinext_irq_unmasked_nopad; + +hazard3_onehot_encode #( + .W_REQ (NUM_IRQS) +) eirq_encode_u ( + .req (highest_eirq_onehot), + .gnt (meinext_irq_unmasked_nopad) +); + +generate +if ($clog2(NUM_IRQS) == $clog2(MAX_IRQS)) begin: encode_eirq_no_padding + assign meinext_irq_unmasked = meinext_irq_unmasked_nopad; +end else begin: encode_eirq_padded + assign meinext_irq_unmasked = { + {$clog2(MAX_IRQS) - $clog2(NUM_IRQS){1'b0}}, + meinext_irq_unmasked_nopad + }; +end +endgenerate + +// It is unnecessary to mask meinext_irq based on meinext_noirq because: +// - The value of the CSR field is unimportant when noirq is set +// - There are no IRQ inputs to the priority selector when there +// are no IRQs, so result is already 0. +assign meinext_irq = meinext_irq_unmasked; + +// ---------------------------------------------------------------------------- +// CSR read + +always @ (*) begin + rdata = {W_DATA{1'b0}}; + case (addr) + + MEIEA: rdata = { + meiea_rdata[wdata_raw[4:0] * 16 +: 16], + 16'h0 + }; + + MEIPA: rdata = { + meipa_rdata[wdata_raw[4:0] * 16 +: 16], + 16'h0 + }; + + MEIFA: rdata = { + meifa_rdata[wdata_raw[4:0] * 16 +: 16], + 16'h0 + }; + + MEIPRA: rdata = { + meipra_rdata[wdata_raw[6:0] * 16 +: 16], + 16'h0 + }; + + MEINEXT: rdata = { + meinext_noirq, + 20'h0, + meinext_irq, + 2'h0 + }; + + MEICONTEXT: rdata = { + meicontext_pppreempt, + meicontext_ppreempt, + 3'h0, + meicontext_preempt, + meicontext_noirq, + 2'h0, + meicontext_irq, + mie_mtie && meicontext_clearts, + mie_msie && meicontext_clearts, + 1'b0, + meicontext_mreteirq + }; + + default: rdata = {W_DATA{1'b0}}; + endcase +end + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_ops.vh b/vendor/Hazard3/hdl/hazard3_ops.vh new file mode 100644 index 0000000..b34401d --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_ops.vh @@ -0,0 +1,124 @@ +/*****************************************************************************\ +| Copyright (C) 2021 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// ALU operation selectors + +localparam ALUOP_ADD = 6'h00; +localparam ALUOP_SUB = 6'h01; +localparam ALUOP_LT = 6'h02; +localparam ALUOP_LTU = 6'h04; +localparam ALUOP_AND = 6'h06; +localparam ALUOP_OR = 6'h07; +localparam ALUOP_XOR = 6'h08; +localparam ALUOP_SRL = 6'h09; +localparam ALUOP_SRA = 6'h0a; +localparam ALUOP_SLL = 6'h0b; +localparam ALUOP_MULDIV = 6'h0c; +localparam ALUOP_RS2 = 6'h0d; // differs from AND/OR/XOR in [1:0] +// Bitmanip ALU operations (some also used by AMOs): +localparam ALUOP_SHXADD = 6'h20; +localparam ALUOP_CLZ = 6'h23; +localparam ALUOP_CPOP = 6'h24; +localparam ALUOP_CTZ = 6'h25; +localparam ALUOP_ANDN = 6'h26; // Same LSBs as non-inverted +localparam ALUOP_ORN = 6'h27; // Same LSBs as non-inverted +localparam ALUOP_XNOR = 6'h28; // Same LSBs as non-inverted +localparam ALUOP_MAX = 6'h29; +localparam ALUOP_MAXU = 6'h2a; +localparam ALUOP_MIN = 6'h2b; +localparam ALUOP_MINU = 6'h2c; +localparam ALUOP_ORC_B = 6'h2d; +localparam ALUOP_REV8 = 6'h2e; +localparam ALUOP_ROL = 6'h2f; +localparam ALUOP_ROR = 6'h30; +localparam ALUOP_SEXT_B = 6'h31; +localparam ALUOP_SEXT_H = 6'h32; +localparam ALUOP_ZEXT_H = 6'h33; + +localparam ALUOP_CLMUL = 6'h34; + +localparam ALUOP_BCLR = 6'h35; +localparam ALUOP_BEXT = 6'h36; +localparam ALUOP_BINV = 6'h37; +localparam ALUOP_BSET = 6'h38; + +localparam ALUOP_PACK = 6'h39; +localparam ALUOP_PACKH = 6'h3a; +localparam ALUOP_BREV8 = 6'h3b; +localparam ALUOP_ZIP = 6'h3c; +localparam ALUOP_UNZIP = 6'h3d; + +localparam ALUOP_BEXTM = 6'h3e; + +localparam ALUOP_XPERM = 6'h3f; + +// Parameters to control ALU input muxes. Bypass mux paths are +// controlled by X, so D has no parameters to choose these. + +localparam ALUSRCA_RS1 = 1'h0; +localparam ALUSRCA_PC = 1'h1; + +localparam ALUSRCB_RS2 = 1'h0; +localparam ALUSRCB_IMM = 1'h1; + +localparam MEMOP_LW = 5'h00; +localparam MEMOP_LH = 5'h01; +localparam MEMOP_LB = 5'h02; +localparam MEMOP_LHU = 5'h03; +localparam MEMOP_LBU = 5'h04; +localparam MEMOP_SW = 5'h05; +localparam MEMOP_SH = 5'h06; +localparam MEMOP_SB = 5'h07; + +localparam MEMOP_LR_W = 5'h08; +localparam MEMOP_SC_W = 5'h09; +localparam MEMOP_AMO = 5'h0a; +localparam MEMOP_NONE = 5'h10; + +localparam BCOND_NEVER = 2'h0; +localparam BCOND_ALWAYS = 2'h1; +localparam BCOND_ZERO = 2'h2; +localparam BCOND_NZERO = 2'h3; + +// CSR access types + +localparam CSR_WTYPE_W = 2'h0; +localparam CSR_WTYPE_S = 2'h1; +localparam CSR_WTYPE_C = 2'h2; + +// Exceptional condition signals which travel alongside (or instead of) +// instructions in the pipeline. These are speculative and can be flushed on +// e.g. branch mispredict. These mostly align with mcause values. + +localparam EXCEPT_NONE = 4'hf; + +localparam EXCEPT_INSTR_MISALIGN = 4'h0; +localparam EXCEPT_INSTR_FAULT = 4'h1; +localparam EXCEPT_INSTR_ILLEGAL = 4'h2; +localparam EXCEPT_EBREAK = 4'h3; +localparam EXCEPT_LOAD_ALIGN = 4'h4; +localparam EXCEPT_LOAD_FAULT = 4'h5; +localparam EXCEPT_STORE_ALIGN = 4'h6; +localparam EXCEPT_STORE_FAULT = 4'h7; +localparam EXCEPT_ECALL_U = 4'h8; +// MRET, Return from M-mode: not really an exception, but handled like one +localparam EXCEPT_MRET = 4'ha; +localparam EXCEPT_ECALL_M = 4'hb; +// spare: c +// spare: d +// REFETCH: flush and refetch sequentially-following instructions, e.g. on +// executing fence.i. Jumps from stage 3 to get ordering against L/S dphase. +localparam EXCEPT_REFETCH = 4'he; + +// Operations for M extension (these are just instr[14:12]) + +localparam M_OP_MUL = 3'h0; +localparam M_OP_MULH = 3'h1; +localparam M_OP_MULHSU = 3'h2; +localparam M_OP_MULHU = 3'h3; +localparam M_OP_DIV = 3'h4; +localparam M_OP_DIVU = 3'h5; +localparam M_OP_REM = 3'h6; +localparam M_OP_REMU = 3'h7; diff --git a/vendor/Hazard3/hdl/hazard3_pmp.v b/vendor/Hazard3/hdl/hazard3_pmp.v new file mode 100644 index 0000000..9c1f1fb --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_pmp.v @@ -0,0 +1,380 @@ +/*****************************************************************************\ +| Copyright (C) 2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +`default_nettype none + +// Physical memory protection unit + +module hazard3_pmp #( +`include "hazard3_config.vh" +) ( + input wire clk, + input wire rst_n, + + // Config interface passed through CSR block + input wire [11:0] cfg_addr, + input wire cfg_wen, + input wire [W_DATA-1:0] cfg_wdata, + output reg [W_DATA-1:0] cfg_rdata, + + // Fetch address query + input wire [W_ADDR-1:0] i_addr, + input wire i_m_mode, + output wire i_kill, + + // Load/store address query + input wire [W_ADDR-1:0] d_addr, + // Broken out separately for carry-save: + input wire [W_ADDR-1:0] d_addr_addend_rs1, + input wire [W_ADDR-1:0] d_addr_addend_imm, + input wire [W_ADDR-1:0] d_addr_addend_lspair_offs, + input wire d_m_mode, + input wire d_write, + output wire d_kill +); + +localparam PMP_A_NAPOT = 2'b11; +localparam PMP_A_NA4 = 2'b10; +localparam PMP_A_TOR = 2'b01; +localparam PMP_A_OFF = 2'b00; + +// Which values are supported in A field (unsupported are mapped to OFF): +localparam [3:0] PMP_A_SUPPORTED = { + |PMP_MATCH_NAPOT, + |PMP_MATCH_NAPOT && PMP_GRAIN == 0, + |PMP_MATCH_TOR, + 1'b1 +}; + +`include "hazard3_csr_addr.vh" + +generate +if (PMP_REGIONS == 0) begin: no_pmp + +// This should already be stubbed out in core.v, but use a generate here too +// so that we don't get a warning for elaborating this module with a region +// count of 0. + +always @ (*) cfg_rdata = {W_DATA{1'b0}}; +assign i_kill = 1'b0; +assign d_kill = 1'b0; + +end else begin: have_pmp + +// ---------------------------------------------------------------------------- +// Config registers and read/write interface + +// Whether a region's configuration is writable; this is non-trivial when TOR +// is supported because locking region i + 1 can also lock region i. +wire [PMP_REGIONS-1:0] region_locked; + +reg [PMP_REGIONS-1:0] pmpcfg_l; +reg [1:0] pmpcfg_a [0:PMP_REGIONS-1]; +reg [PMP_REGIONS-1:0] pmpcfg_x; +reg [PMP_REGIONS-1:0] pmpcfg_w; +reg [PMP_REGIONS-1:0] pmpcfg_r; + +// Address register contains bits 33:2 of the address (to support 16 GiB +// physical address space). We don't implement bits 33 or 32. +reg [W_ADDR-3:0] pmpaddr [0:PMP_REGIONS-1]; + +// Hazard3 extension for applying PMP regions to M-mode without locking. +// Different from ePMP mseccfg.rlb: low-numbered regions may be locked for +// security reasons, but higher-numbered regions should stll be available for +// other purposes e.g. stack guarding, peripheral emulation +reg [PMP_REGIONS-1:0] pmpcfg_m; + +always @ (posedge clk or negedge rst_n) begin: cfg_update + reg signed [31:0] i; + if (!rst_n) begin + for (i = 0; i < PMP_REGIONS; i = i + 1) begin + pmpcfg_l[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_CFG[8 * i + 7] : 1'b0; + pmpcfg_a[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_CFG[8 * i + 3 +: 2] : 2'h0; + pmpcfg_x[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_CFG[8 * i + 2] : 1'b0; + pmpcfg_w[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_CFG[8 * i + 1] : 1'b0; + pmpcfg_r[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_CFG[8 * i + 0] : 1'b0; + + pmpaddr[i] <= PMP_HARDWIRED[i] ? PMP_HARDWIRED_ADDR[32 * i +: 30] : + PMP_GRAIN > 1 ? ~(~30'h0 << (PMP_GRAIN - 1)) : 30'h0; + end + pmpcfg_m <= {PMP_REGIONS{1'b0}}; + end else if (cfg_wen) begin + for (i = 0; i < PMP_REGIONS; i = i + 1) begin + if (cfg_addr == PMPCFG0 + i[13:2] && !region_locked[i]) begin + if (PMP_HARDWIRED[i]) begin + // Keep tied to hardwired value (but still make the "register" sensitive to clk) + pmpcfg_l[i] <= PMP_HARDWIRED_CFG[8 * i + 7]; + pmpcfg_a[i] <= PMP_HARDWIRED_CFG[8 * i + 3 +: 2]; + pmpcfg_x[i] <= PMP_HARDWIRED_CFG[8 * i + 2]; + pmpcfg_w[i] <= PMP_HARDWIRED_CFG[8 * i + 1]; + pmpcfg_r[i] <= PMP_HARDWIRED_CFG[8 * i + 0]; + pmpaddr[i] <= PMP_HARDWIRED_ADDR[32 * i +: 30]; + end else begin + pmpcfg_l[i] <= cfg_wdata[i % 4 * 8 + 7]; + pmpcfg_x[i] <= cfg_wdata[i % 4 * 8 + 2]; + pmpcfg_w[i] <= cfg_wdata[i % 4 * 8 + 1]; + pmpcfg_r[i] <= cfg_wdata[i % 4 * 8 + 0]; + // Unsupported A values are mapped to OFF (it's a WARL field). + pmpcfg_a[i] <= PMP_A_SUPPORTED[cfg_wdata[i % 4 * 8 + 3 +: 2]] ? + cfg_wdata[i % 4 * 8 + 3 +: 2] : PMP_A_OFF; + end + end + if (cfg_addr == PMPADDR0 + i[11:0] && !region_locked[i]) begin + // This implements one bit too many when G > 0 and only + // PMP_MATCH_TOR is enabled, however that bit is ignored for + // both rdata and address matching, so should be trimmed. + if (PMP_GRAIN > 1) begin + pmpaddr[i] <= cfg_wdata[W_ADDR-3:0] | ~(~30'h0 << (PMP_GRAIN - 1)); + end else begin + pmpaddr[i] <= cfg_wdata[W_ADDR-3:0]; + end + end + end + if (cfg_addr == PMPCFGM0) begin + pmpcfg_m <= cfg_wdata[PMP_REGIONS-1:0] & ~PMP_HARDWIRED & {PMP_REGIONS{|EXTENSION_XH3PMPM}}; + end + end +end + + +always @ (*) begin: cfg_read + reg signed [31:0] i; + cfg_rdata = {W_DATA{1'b0}}; + for (i = 0; i < PMP_REGIONS; i = i + 1) begin + if (cfg_addr == PMPCFG0 + i[13:2]) begin + cfg_rdata[i % 4 * 8 +: 8] = { + pmpcfg_l[i], + 2'b00, + pmpcfg_a[i], + pmpcfg_x[i], + pmpcfg_w[i], + pmpcfg_r[i] + }; + end else if (cfg_addr == PMPADDR0 + i[11:0]) begin + if (PMP_GRAIN >= 2 && pmpcfg_a[i][1]) begin + // Bits G-2:0 read back as all-ones when A is NA4 or NAPOT. + cfg_rdata[W_ADDR-3:0] = pmpaddr[i] | ~({W_ADDR-2{1'b1}} << (PMP_GRAIN - 1)); + end else if (PMP_GRAIN >= 1 && !pmpcfg_a[i][1]) begin + // Bits G-1:0 read back as all-zeroes when A is OFF or TOR. + cfg_rdata[W_ADDR-3:0] = pmpaddr[i] & ({W_ADDR-2{1'b1}} << PMP_GRAIN); + end else begin + cfg_rdata[W_ADDR-3:0] = pmpaddr[i]; + end + end + end + if (cfg_addr == PMPCFGM0) begin + cfg_rdata = {{32-PMP_REGIONS{1'b0}}, pmpcfg_m} & {32{|EXTENSION_XH3PMPM}}; + end +end + +// ---------------------------------------------------------------------------- +// Region locking rules + +reg [PMP_REGIONS-1:0] pmp_region_is_tor; +always @ (*) begin: check_region_is_tor + integer i; + for (i = 0; i < PMP_REGIONS; i = i + 1) begin + pmp_region_is_tor[i] = PMP_MATCH_TOR && pmpcfg_a[i] == PMP_A_TOR; + end +end + +assign region_locked = pmpcfg_l | ((pmpcfg_l & pmp_region_is_tor) >> 1); + +// ---------------------------------------------------------------------------- +// Match addresses against regions + +wire [PMP_REGIONS-1:0] d_match_napot; +wire [PMP_REGIONS-1:0] i_match_napot; +wire [PMP_REGIONS-1:0] d_match_tor; +wire [PMP_REGIONS-1:0] i_match_tor; + +if (PMP_MATCH_NAPOT != 0) begin: have_napot + + reg [PMP_REGIONS-1:0] d_match_napot_r; + reg [PMP_REGIONS-1:0] i_match_napot_r; + + assign d_match_napot = d_match_napot_r; + assign i_match_napot = i_match_napot_r; + + // Decode PMPCFGx.A and PMPADDRx into a 32-bit address mask and address + reg [W_ADDR-1:0] match_mask [0:PMP_REGIONS-1]; + reg [W_ADDR-1:0] match_addr [0:PMP_REGIONS-1]; + + // Encoding: (noting ADDR is a 4-byte address, not a word address): + // CFG.A | ADDR | Region size + // ------+----------+------------ + // NA4 | y..yyyyy | 4 bytes + // NAPOT | y..yyyy0 | 8 bytes + // NAPOT | y..yyy01 | 16 bytes + // NAPOT | y..yy011 | 32 bytes + // NAPOT | y..y0111 | 64 bytes + // etc. + // + // So, with the exception of NA4, the rule is to check all bits more + // significant than the least-significant 0 bit. + + always @ (*) begin: decode_match_mask_addr + integer i, j; + for (i = 0; i < PMP_REGIONS; i = i + 1) begin + if (!pmpcfg_a[i][0]) begin + match_mask[i] = {{W_ADDR-2{1'b1}}, 2'b00}; + end else begin + // Bits 1:0 are always 0. Bit 2 is 0 because NAPOT is at least 8 bytes. + match_mask[i] = {W_ADDR{1'b0}}; + for (j = 3; j < W_ADDR; j = j + 1) begin + match_mask[i][j] = match_mask[i][j - 1] || !pmpaddr[i][j - 3]; + end + end + match_addr[i] = {pmpaddr[i], 2'b00} & match_mask[i]; + end + end + + // We check only the least-addressed byte of each access. See later + // comments for an argument as to why this is sufficient. + + always @ (*) begin: check_d_match + integer i; + for (i = PMP_REGIONS - 1; i >= 0; i = i - 1) begin + d_match_napot_r[i] = pmpcfg_a[i][1] && + (d_addr & match_mask[i]) == match_addr[i]; + i_match_napot_r[i] = pmpcfg_a[i][1] && + (i_addr & match_mask[i]) == match_addr[i]; + end + end + +end else begin: no_napot + + assign d_match_napot = {PMP_REGIONS{1'b0}}; + assign i_match_napot = {PMP_REGIONS{1'b0}}; + +end + +if (PMP_MATCH_TOR != 0) begin: have_tor + + reg [PMP_REGIONS-1:0] d_match_tor_r; + reg [PMP_REGIONS-1:0] i_match_tor_r; + reg [W_ADDR-1:0] watermark [0:PMP_REGIONS-1]; + reg [PMP_REGIONS-1:0] d_lt; + reg [PMP_REGIONS-1:0] i_lt; + + assign d_match_tor = d_match_tor_r; + assign i_match_tor = i_match_tor_r; + + always @ (*) begin: compare + integer i; + for (i = 0; i < PMP_REGIONS; i = i + 1) begin + watermark[i] = { + pmpaddr[i][W_ADDR-3:0] & (~30'h0 << PMP_GRAIN), + 2'b00 + }; + // Bring terms in separately to try to encourage adder merging + d_lt[i] = ( + d_addr_addend_rs1 + d_addr_addend_imm + d_addr_addend_lspair_offs + ) < watermark[i]; + i_lt[i] = i_addr < watermark[i]; + end + end + + wire [PMP_REGIONS-1:0] d_prev_ge = ~(d_lt << 1); + wire [PMP_REGIONS-1:0] i_prev_ge = ~(i_lt << 1); + + always @ (*) begin: match + integer i; + for (i = 0; i < PMP_REGIONS; i = i + 1) begin + d_match_tor_r[i] = d_lt[i] && d_prev_ge[i] && pmpcfg_a[i] == PMP_A_TOR; + i_match_tor_r[i] = i_lt[i] && i_prev_ge[i] && pmpcfg_a[i] == PMP_A_TOR; + end + end + +end else begin: no_tor + + assign d_match_tor = {PMP_REGIONS{1'b0}}; + assign i_match_tor = {PMP_REGIONS{1'b0}}; + +end + +// ---------------------------------------------------------------------------- +// Decode permissions from matches + +// For load/stores we assume any non-naturally-aligned transfers trigger a +// misaligned load/store/AMO exception, so we only need to decode the PMP +// attribute for the first byte of the access. Note the spec gives us freedom +// to report *either* a load/store/AMO access fault (mcause = 5, 7) or a +// load/store/AMO alignment fault (mcause = 4, 6), in the case that both +// happen, and we choose alignment fault in this case. + +reg d_m; // Hazard3 extension (M-mode without locking) +reg d_l; +reg d_r; +reg d_w; + +always @ (*) begin: check_d_match + integer i; + d_m = 1'b0; + d_l = 1'b0; + d_r = 1'b0; + d_w = 1'b0; + // Lowest-numbered match wins, so work down from the top. This should be + // inferred as a priority mux structure (cascade mux). + for (i = PMP_REGIONS - 1; i >= 0; i = i - 1) begin + if (d_match_napot[i] || d_match_tor[i]) begin + d_m = pmpcfg_m[i]; + d_l = pmpcfg_l[i]; + d_r = pmpcfg_r[i]; + d_w = pmpcfg_w[i]; + end + end +end + +// Instructions work similarly because we check *fetches*, not instructions. +// Fetch is always word-sized word-aligned. The spec permits this: +// +// "On some implementations, misaligned loads, stores, and instruction fetches +// may also be decomposed into multiple accesses, some of which may succeed +// before an access-fault exception occurs." +// +// Hazard3 separately checks the naturally-aligned fetches that occur in the +// course of fetching a non-naturally-aligned instruction. This means +// instruction fetch spanning two different regions which both grant X +// permission *is* permitted, unlike the RP2350 version of Hazard3. + +reg i_m; // Hazard3 extension (M-mode without locking) +reg i_l; +reg i_x; + +always @ (*) begin: check_i_match + integer i; + i_m = 1'b0; + i_l = 1'b0; + i_x = 1'b0; + for (i = PMP_REGIONS - 1; i >= 0; i = i - 1) begin + if (i_match_napot[i] || i_match_tor[i]) begin + i_m = pmpcfg_m[i]; + i_l = pmpcfg_l[i]; + i_x = pmpcfg_x[i]; + end + end +end + +// ---------------------------------------------------------------------------- +// Access rules + +// M-mode gets to ignore protections, unless the lock or M-mode bit is set. + +assign d_kill = (!d_m_mode || d_l || d_m) && ( + (!d_write && !d_r) || + ( d_write && !d_w) +); + +assign i_kill = (!i_m_mode || i_l || i_m) && !i_x; + +end +endgenerate + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_power_ctrl.v b/vendor/Hazard3/hdl/hazard3_power_ctrl.v new file mode 100644 index 0000000..eab5582 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_power_ctrl.v @@ -0,0 +1,173 @@ +/*****************************************************************************\ +| Copyright (C) 2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +`default_nettype none + +// Wake/sleep (power) state machine for Hazard3 + +module hazard3_power_ctrl #( +`include "hazard3_config.vh" +) ( + input wire clk_always_on, + input wire rst_n, + + // 4-phase (Gray code) req/ack handshake for requesting and releasing + // power+clock enable on non-processor hardware, e.g. the bus fabric. This + // can also be used for an external controller to gate the processor's clk + // input, rather than the clk_en signal below. + output reg pwrup_req, + input wire pwrup_ack, + + // Top-level clock enable for an optional clock gate on the processor's clk + // input (but not clk_always_on, which clocks this module and the IRQ input + // flops). This allows the processor to clock-gate when sleeping. It's + // acceptable for the clock gate cell to have one cycle of delay when + // clk_en changes. + output reg clk_en, + + // Power state controls from CSRs + input wire allow_clkgate, + input wire allow_power_down, + input wire allow_sleep_on_block, + + // Signal from frontend that it has stalled against the WFI pipeline + // stall, and we are now clear to enter a deep sleep state + input wire frontend_pwrdown_ok, + + input wire sleeping_on_wfi, + input wire wfi_wakeup_req, + input wire sleeping_on_block, + input wire block_wakeup_req_pulse, + output reg stall_release +); + +// ---------------------------------------------------------------------------- +// Wake/sleep state machine + +localparam W_STATE = 2; +localparam S_AWAKE = 2'h0; +localparam S_ENTER_ASLEEP = 2'h1; +localparam S_ASLEEP = 2'h2; +localparam S_ENTER_AWAKE = 2'h3; + +reg [W_STATE-1:0] state; +reg block_wakeup_req; + +wire active_wake_req = + (sleeping_on_block && (block_wakeup_req || wfi_wakeup_req)) || + (sleeping_on_wfi && wfi_wakeup_req); + +// Note: we assert our power up request during reset, and *assume* that the +// power up acknowledge is also high at reset. If this is a problem, extend +// the core reset. + +always @ (posedge clk_always_on or negedge rst_n) begin + if (!rst_n) begin + state <= S_AWAKE; + pwrup_req <= 1'b1; + clk_en <= 1'b1; + stall_release <= 1'b0; + end else begin + stall_release <= 1'b0; + case (state) + S_AWAKE: if (sleeping_on_wfi || sleeping_on_block) begin + if (stall_release) begin + // The last cycle of an ongoing which we have just released. Sit + // tight, this instruction will move down the pipeline at the + // end of this cycle. (There is an assertion that this doesn't + // happen twice.) + state <= S_AWAKE; + end else if (active_wake_req) begin + // Skip deep sleep if it would immediately fall through. + stall_release <= 1'b1; + end else if ((allow_power_down || allow_clkgate) && (sleeping_on_wfi || allow_sleep_on_block)) begin + if (frontend_pwrdown_ok) begin + pwrup_req <= !allow_power_down; + clk_en <= !allow_clkgate; + state <= allow_power_down ? S_ENTER_ASLEEP : S_ASLEEP; + end else begin + // Stay awake until it is safe to power down (i.e. until our + // instruction fetch goes quiet). + state <= S_AWAKE; + end + end else begin + // No power state change. Just sit with the pipeline stalled. + state <= S_AWAKE; + end + end + S_ENTER_ASLEEP: if (!pwrup_ack) begin + state <= S_ASLEEP; + end + S_ASLEEP: if (active_wake_req) begin + pwrup_req <= 1'b1; + clk_en <= 1'b1; + // Still go through the enter state for non-power-down wakeup, in + // case the clock gate cell has a 1 cycle delay. + state <= S_ENTER_AWAKE; + end + S_ENTER_AWAKE: if (pwrup_ack || !allow_power_down) begin + state <= S_AWAKE; + stall_release <= 1'b1; + end + default: begin + state <= S_AWAKE; + end + endcase + end +end + +`ifdef HAZARD3_ASSERTIONS +// Regs are a workaround for the non-constant reset value issue with +// $past() in yosys-smtbmc. +reg past_sleeping; +reg past_stall_release; +always @ (posedge clk_always_on) begin + if (!rst_n) begin + past_sleeping <= 1'b0; + past_stall_release <= 1'b0; + end else begin + past_sleeping <= sleeping_on_wfi || sleeping_on_block; + past_stall_release <= stall_release; + // These must always be mutually exclusive. + assert(!(sleeping_on_wfi && sleeping_on_block)); + if (stall_release) begin + // Presumably there was a stall which we just released + assert(past_sleeping); + // Presumably we are still in that stall + assert(sleeping_on_wfi|| sleeping_on_block); + // It takes one cycle to do a release and enter a new sleep state, so a + // double release should be impossible. + assert(!past_stall_release); + end + if (state == S_ASLEEP) begin + assert(allow_power_down || allow_clkgate); + end + end +end +`endif + +// ---------------------------------------------------------------------------- +// Pulse->level for block wakeup + +// Unblock signal is sticky: a prior unblock with no block since will cause +// the next block to immediately fall through. + +always @ (posedge clk_always_on or negedge rst_n) begin + if (!rst_n) begin + block_wakeup_req <= 1'b0; + end else begin + // Note the OR takes precedence over the AND, so we don't miss a second + // unblock that arrives at the instant we wake up. + block_wakeup_req <= (block_wakeup_req && !( + sleeping_on_block && stall_release + )) || block_wakeup_req_pulse; + end +end + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_regfile_1w2r.v b/vendor/Hazard3/hdl/hazard3_regfile_1w2r.v new file mode 100644 index 0000000..1bd2379 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_regfile_1w2r.v @@ -0,0 +1,84 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// Register file +// Single write port, dual read port + +`default_nettype none + +module hazard3_regfile_1w2r #( +`include "hazard3_config.vh" +) ( + input wire clk, + input wire rst_n, + + input wire [4:0] raddr1, + output reg [W_DATA-1:0] rdata1, + + input wire [4:0] raddr2, + output reg [W_DATA-1:0] rdata2, + + input wire [4:0] waddr, + input wire [W_DATA-1:0] wdata, + input wire wen +); + +localparam N_REGS = EXTENSION_E == 0 ? 32 : 16; +localparam [4:0] REGNUM_MASK = {~|EXTENSION_E, 4'hf}; + +wire [4:0] raddr1_masked = raddr1 & REGNUM_MASK; +wire [4:0] raddr2_masked = raddr2 & REGNUM_MASK; +wire [4:0] waddr_masked = waddr & REGNUM_MASK; + +generate +if (RESET_REGFILE) begin: real_dualport_reset + // This will presumably always be implemented with flops + reg [W_DATA-1:0] mem [0:N_REGS-1]; + + integer i; + always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + for (i = 0; i < N_REGS; i = i + 1) begin + mem[i] <= {W_DATA{1'b0}}; + end + rdata1 <= {W_DATA{1'b0}}; + rdata2 <= {W_DATA{1'b0}}; + end else begin + if (wen) begin + mem[waddr_masked] <= wdata; + end + rdata1 <= mem[raddr1_masked]; + rdata2 <= mem[raddr2_masked]; + end + end +end else begin: real_dualport_noreset + // This should be inference-compatible on FPGAs with dual-port (or 1R1W) BRAMs + `ifdef YOSYS + `ifdef FPGA_ICE40 + // We do not require write-to-read bypass logic on the BRAM + (* no_rw_check *) + `endif + `endif + // Optionally force use of distributed RAM on Xilinx for better timing + `ifdef HAZARD3_REGFILE_RAM_STYLE_DISTRIBUTED + (* ram_style = "distributed" *) + `endif + reg [W_DATA-1:0] mem [0:N_REGS-1]; + + always @ (posedge clk) begin + if (wen) begin + mem[waddr_masked] <= wdata; + end + rdata1 <= mem[raddr1_masked]; + rdata2 <= mem[raddr2_masked]; + end +end +endgenerate + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_rvfi_monitor.vh b/vendor/Hazard3/hdl/hazard3_rvfi_monitor.vh new file mode 100644 index 0000000..15cbe02 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_rvfi_monitor.vh @@ -0,0 +1,377 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2025 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// ---------------------------------------------------------------------------- +// RVFI Instrumentation +// ---------------------------------------------------------------------------- +// To be included into hazard3_core.v for use with riscv-formal. +// Contains some state modelling to diagnose exactly what the core is doing, +// and report this in a way RVFI understands. +// We consider instructions to "retire" as they cross the M/W pipe register. +// +// All modelling signals prefixed with rvfm (riscv-formal monitor) + +// ---------------------------------------------------------------------------- +// Instruction monitor + +// Diagnose whether X, M contain valid in-flight instructions, to produce +// rvfi_valid signal. + +wire rvfm_x_valid = fd_cir_vld >= 2 || (fd_cir_vld >= 1 && fd_cir_raw[1:0] != 2'b11); + +reg rvfm_m_valid; +reg [31:0] rvfm_m_instr; + +wire rvfm_m_trap = xm_except != EXCEPT_NONE && xm_except != EXCEPT_MRET && m_trap_enter_rdy; + +reg rvfi_valid_r; +reg [31:0] rvfi_insn_r; +reg rvfi_trap_r; + +assign rvfi_valid = rvfi_valid_r; +assign rvfi_insn = rvfi_insn_r; +assign rvfi_trap = rvfi_trap_r; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + rvfm_m_valid <= 1'b0; + rvfi_valid_r <= 1'b0; + rvfi_trap_r <= 1'b0; + rvfi_insn_r <= 32'h0; + end else begin + if (!x_stall) begin + // X instruction squashed by any trap, as it's in the branch + // shadow. + rvfm_m_valid <= |df_cir_use && !m_trap_enter_vld; + rvfm_m_instr <= {fd_cir_raw[31:16] & {16{df_cir_use[1]}}, fd_cir_raw[15:0]}; + end else if (!m_stall) begin + rvfm_m_valid <= 1'b0; + end + rvfi_valid_r <= rvfm_m_valid && !m_stall; + // Instructions which experienced fetch faults are reported as + // all-zeroes, per riscv-formal docs. + rvfi_insn_r <= rvfm_m_instr & {32{ + xm_except != EXCEPT_INSTR_FAULT && + xm_except != EXCEPT_INSTR_MISALIGN + }}; + rvfi_trap_r <= rvfm_m_trap; + end +end + +`ifdef HAZARD3_ASSERTIONS +always @ (posedge clk) if (rst_n) begin + // Sanity checks for above + if (d_rd != 5'h0) + assert(rvfm_x_valid); + if (xm_rd != 5'h0) + assert(rvfm_m_valid); +end +`endif + +// Track whether an instruction is the first of an interrupt or exception; +// when a trap happens, a flag is installed in stage X, and once a new +// instruction arrives the flag travels alongside it down to the RVFI port. +reg rvfm_x_intr; +reg rvfm_m_intr; +reg rvfi_intr_r; +always @ (posedge clk) begin + if (!rst_n) begin + rvfm_x_intr <= 1'b0; + rvfm_m_intr <= 1'b0; + rvfi_intr_r <= 1'b0; + end else begin + rvfm_x_intr <= (rvfm_x_intr && (x_stall || d_starved || fd_cir_uop_nonfinal || df_lspair_phase_next)) || + (m_trap_enter_vld && m_trap_enter_rdy); + if (!x_stall) begin + rvfm_m_intr <= rvfm_x_intr; + end + if (!m_stall) begin + rvfi_intr_r <= rvfm_m_intr; + end + end +end + + +// Hazard3 is an in-order core: +reg [63:0] rvfm_retire_ctr; +assign rvfi_order = rvfm_retire_ctr; +always @ (posedge clk or negedge rst_n) + if (!rst_n) + rvfm_retire_ctr <= 0; + else if (rvfi_valid) + rvfm_retire_ctr <= rvfm_retire_ctr + 1; + +assign rvfi_intr = rvfi_intr_r && rvfi_valid; + +// ---------------------------------------------------------------------------- +// PC and jump monitor + +reg [31:0] rvfm_xm_pc; +reg [31:0] rvfm_xm_pc_next; + +// Record a jump target that was issued while stalled +reg rvfm_x_saw_f_jump; +reg [31:0] rvfm_x_saw_f_jump_target; +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + rvfm_x_saw_f_jump <= 1'b0; + rvfm_x_saw_f_jump_target <= 32'd0; + end else if (f_jump_now && !(m_trap_enter_vld && m_trap_enter_rdy) && (x_stall || (fd_cir_is_uop && fd_cir_uop_nonfinal))) begin + // Record fetch address issued by instruction. Note this case is gated + // on m_trap_enter_vld && m_trap_enter_rdy (not just vld). If + // !m_trap_enter_rdy it is still possible to get a new fetch address + // in the following case: + // + // * M instr is a load/store (in dphase) + // + // * IRQ is asserted, but blocked by the load/store dphase to avoid + // trashing exception PC of a potential data-phase bus fault + // + // * X instr is a jump, which stalls due to the IRQ assertion + // + // * X instr's PC goes through to frontend during stall (and would be + // flushed if the IRQ went through) because stall cannot gate fetch + // address request to avoid AHB through-path. + // + // * IRQ's trap address is not immediately accepted by frontend due to + // address-phase stall on issuing jump instr's address + // + // * IRQ deasserts on the next cycle, so its trap address is not accepted. + rvfm_x_saw_f_jump <= 1'b1; + rvfm_x_saw_f_jump_target <= f_jump_target; + end else if (!x_stall && !(fd_cir_is_uop && fd_cir_uop_nonfinal)) begin + rvfm_x_saw_f_jump <= 1'b0; + end else if (m_trap_enter_vld && m_trap_enter_rdy) begin + // E.g. trap during uop sequence + rvfm_x_saw_f_jump <= 1'b0; + end +end + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + rvfm_xm_pc <= 0; + rvfm_xm_pc_next <= 0; + end else begin + if (!x_stall) begin + // For cm.popret and cm.popretz the PC actually changes on the + // penultimate uop; ignore this and retain the initial PC. Abuse + // knowledge that the PC update is always in the atomic section, + // and the first instruction is always interruptible. + if (!(fd_cir_is_uop && fd_cir_uop_atomic)) begin + rvfm_xm_pc <= d_pc; + end + rvfm_xm_pc_next <= + f_jump_now ? f_jump_target : + rvfm_x_saw_f_jump ? rvfm_x_saw_f_jump_target : + d_pc + (fd_cir_raw[1:0] == 2'b11 ? 32'h4 : 32'h2); + end + end +end + +reg [31:0] rvfi_pc_rdata_r; +reg [31:0] rvfi_pc_wdata_r; + +assign rvfi_pc_rdata = rvfi_pc_rdata_r; +assign rvfi_pc_wdata = rvfi_pc_wdata_r; + +always @ (posedge clk) begin + if (!m_stall) begin + rvfi_pc_rdata_r <= rvfm_xm_pc; + rvfi_pc_wdata_r <= + m_trap_enter_vld && m_trap_enter_rdy && xm_except != EXCEPT_NONE ? + m_trap_addr : rvfm_xm_pc_next; + end +end + +// ---------------------------------------------------------------------------- +// Register file monitor: + +// When writeback is suppressed due to trap, the previous instruction is left +// in the writeback buffer (and can be re-bypassed from there). Make sure not +// to report this as a writeback on RVFI. +reg rvfm_writeback_mask; +always @ (posedge clk) begin + if (!m_stall) begin + rvfm_writeback_mask <= m_reg_wen_if_nonzero; + end +end + +assign rvfi_rd_addr = mw_rd & {5{rvfm_writeback_mask}}; +assign rvfi_rd_wdata = |mw_rd && rvfm_writeback_mask ? mw_result : 32'h0; + +// Do not reimplement internal bypassing logic. Danger of implementing +// it correctly here but incorrectly in core. + +reg [31:0] rvfm_xm_rdata1; +reg [31:0] rvfm_xm_rdata2; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + rvfm_xm_rdata1 <= 32'h0; + rvfm_xm_rdata2 <= 32'h0; + end else if (!x_stall) begin + // rs*_bypass may have garbage on them for instructions with *no* + // register operands (due to some interesting optimisations), though + // d_rs* is still driven to 0 to disable stalling on that register + // lane. riscv-formal still likes to see zeroes from x0, so fix that + // up here. This shouldn't cover up any bugs, since a + // register-operand instruction would still *use* the garbage value. + rvfm_xm_rdata1 <= |d_rs1 ? x_rs1_bypass : 32'h0; + rvfm_xm_rdata2 <= |d_rs2 ? x_rs2_bypass : 32'h0; + end +end + +reg [4:0] rvfi_rs1_addr_r; +reg [4:0] rvfi_rs2_addr_r; +reg [31:0] rvfi_rs1_rdata_r; +reg [31:0] rvfi_rs2_rdata_r; + +assign rvfi_rs1_addr = rvfi_rs1_addr_r; +assign rvfi_rs2_addr = rvfi_rs2_addr_r; +assign rvfi_rs1_rdata = rvfi_rs1_rdata_r; +assign rvfi_rs2_rdata = rvfi_rs2_rdata_r; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + rvfi_rs1_addr_r <= 5'h0; + rvfi_rs2_addr_r <= 5'h0; + rvfi_rs1_rdata_r <= 32'h0; + rvfi_rs2_rdata_r <= 32'h0; + end else begin + rvfi_rs1_addr_r <= m_stall ? 5'h0 : xm_rs1; + rvfi_rs2_addr_r <= m_stall ? 5'h0 : xm_rs2; + rvfi_rs1_rdata_r <= rvfm_xm_rdata1; + rvfi_rs2_rdata_r <= xm_rs2 == mw_rd && |xm_rs2 ? m_wdata : rvfm_xm_rdata2; + end +end + +// ---------------------------------------------------------------------------- +// Load/store monitor: based on bus signals, NOT processor internals. +// Marshal up a description of the current data phase, and then register this +// into the RVFI signals. + +`ifdef HAZARD3_ASSERTIONS +`ifndef RISCV_FORMAL_ALIGNED_MEM +initial $fatal; +`endif +`endif + +reg [31:0] rvfm_haddr_dph; +reg rvfm_hwrite_dph; +reg [1:0] rvfm_htrans_dph; +reg [2:0] rvfm_hsize_dph; + +always @ (posedge clk) begin + if (bus_aph_ready_d) begin + rvfm_htrans_dph <= {bus_aph_req_d, 1'b0}; + rvfm_haddr_dph <= bus_haddr_d; + rvfm_hwrite_dph <= bus_hwrite_d; + rvfm_hsize_dph <= bus_hsize_d; + end +end + +wire [3:0] rvfm_mem_bytemask_dph = ( + rvfm_hsize_dph == 3'h0 ? 4'h1 : + rvfm_hsize_dph == 3'h1 ? 4'h3 : + 4'hf + ) << rvfm_haddr_dph[1:0]; + +reg [31:0] rvfi_mem_addr_r; +reg [3:0] rvfi_mem_rmask_r; +reg [31:0] rvfi_mem_rdata_r; +reg [3:0] rvfi_mem_wmask_r; +reg [31:0] rvfi_mem_wdata_r; +reg rvfi_mem_fault_r; +// May have to hold the strobes for multiple cycles following a bus +// fault, as the trap entry may not go through immediately (depending +// on instruction-side bus stall): +reg rvfm_mem_hold; + +assign rvfi_mem_addr = rvfi_mem_addr_r; +assign rvfi_mem_rdata = rvfi_mem_rdata_r; +assign rvfi_mem_wdata = rvfi_mem_wdata_r; + +assign rvfi_mem_fault = rvfi_mem_fault_r; +assign rvfi_mem_wmask = rvfi_mem_wmask_r & {4{!rvfi_mem_fault_r}}; +assign rvfi_mem_rmask = rvfi_mem_rmask_r & {4{!rvfi_mem_fault_r}}; +assign rvfi_mem_fault_rmask = rvfi_mem_rmask_r & {4{ rvfi_mem_fault_r}}; +assign rvfi_mem_fault_wmask = rvfi_mem_wmask_r & {4{ rvfi_mem_fault_r}}; + +always @ (posedge clk) begin + rvfm_mem_hold <= (rvfm_mem_hold || (rvfm_htrans_dph && bus_dph_ready_d)) && m_stall; + if (xm_memop == MEMOP_AMO) begin + // AMO has completed in stage X. Progressing to stage M without MEMOP + // going to NONE then there has been no trap, therefore no stall, + // therefore no time for another address to have issued: + assert(!m_stall); + rvfi_mem_addr_r <= rvfm_haddr_dph; + // Always 32-bit, always both read and write: + rvfi_mem_rmask_r <= 4'hf; + rvfi_mem_wmask_r <= 4'hf; + // Has been juggled since the read that matched the winning write: + rvfi_mem_rdata_r <= xm_result; + // Incidentally captured on previous cycle: + rvfi_mem_wdata_r <= rvfi_mem_wdata_r; + end else if (bus_dph_ready_d) begin + // RVFI has an AXI-like concept of byte strobes, rather than AHB-like + rvfi_mem_addr_r <= rvfm_haddr_dph & 32'hffff_fffc; + {rvfi_mem_rmask_r, rvfi_mem_wmask_r} <= 0; + if (rvfm_htrans_dph[1] && rvfm_hwrite_dph) begin + rvfi_mem_wmask_r <= rvfm_mem_bytemask_dph; + rvfi_mem_wdata_r <= bus_wdata_d; + end else if (rvfm_htrans_dph[1] && !rvfm_hwrite_dph) begin + rvfi_mem_rmask_r <= rvfm_mem_bytemask_dph; + rvfi_mem_rdata_r <= bus_rdata_d; + end + rvfi_mem_fault_r <= bus_dph_err_d; + end else if (!rvfm_mem_hold) begin + rvfi_mem_rmask_r <= 4'h0; + rvfi_mem_wmask_r <= 4'h0; + rvfi_mem_fault_r <= 1'b0; + end + // Also need to report rvfi_mem_fault on a fetch fault + if (xm_except == EXCEPT_INSTR_FAULT && m_trap_enter_vld && m_trap_enter_rdy) begin + rvfi_mem_fault_r <= 1'b1; + end +end + +// ---------------------------------------------------------------------------- +// Constraints + +// Trying to keep internal constraints to a minimum. + +// Limit sleep duration for liveness checks +// TODO is it possible to do this in a way that doesn't assume the wakeup logic is functional? +`ifdef RISCV_FORMAL_FAIRNESS +reg [7:0] rvfm_sleep_counter; +always @ (posedge clk) begin + if (!rst_n) begin + rvfm_sleep_counter <= 8'd00; + end else if (xm_sleep_wfi || xm_sleep_block) begin + rvfm_sleep_counter <= rvfm_sleep_counter + 8'h01; + assume(rvfm_sleep_counter < 8'd5); + end else begin + rvfm_sleep_counter <= 8'd00; + end +end +`endif + +// ---------------------------------------------------------------------------- +// Tie-offs + +// Note: Hazard3 does not have any instructions which irrversibly halt +// execution. For the liveness check (RISCV_FORMAL_FAIRNESS is defined), +// length of stalls is constrained and WFI is assumed to wake immediately +// after going to sleep. +assign rvfi_halt = 1'b0; + +// Note: this always reports M-mode, which is not correct if the U_MODE config +// is set. However no riscv-formal checks currently use this signal. +assign rvfi_mode = 2'h3; + +// Maximum XLEN is always 32 bits +assign rvfi_ixl = 2'h1; + + diff --git a/vendor/Hazard3/hdl/hazard3_rvfi_standalone_defs.vh b/vendor/Hazard3/hdl/hazard3_rvfi_standalone_defs.vh new file mode 100644 index 0000000..58f1bb9 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_rvfi_standalone_defs.vh @@ -0,0 +1,96 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2025 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// Macros for building Hazard3 with RVFI trace port outside of the +// riscv-formal test harness. For example, when including the trace port in a +// synthesised processor. +`ifdef HAZARD3_RVFI_STANDALONE + +`define RISCV_FORMAL +`define RISCV_FORMAL_NRET 1 +`define RISCV_FORMAL_XLEN 32 +`define RISCV_FORMAL_ILEN 32 +`define RISCV_FORMAL_MEM_FAULT +`define RISCV_FORMAL_ALIGNED_MEM + +`define RVFI_OUTPUTS \ + output wire rvfi_valid, \ + output wire [63:0] rvfi_order, \ + output wire [31:0] rvfi_insn, \ + output wire rvfi_trap, \ + output wire rvfi_halt, \ + output wire rvfi_intr, \ + output wire [1:0] rvfi_mode, \ + output wire [1:0] rvfi_ixl, \ + output wire [4:0] rvfi_rs1_addr, \ + output wire [4:0] rvfi_rs2_addr, \ + output wire [31:0] rvfi_rs1_rdata, \ + output wire [31:0] rvfi_rs2_rdata, \ + output wire [4:0] rvfi_rd_addr, \ + output wire [31:0] rvfi_rd_wdata, \ + output wire [31:0] rvfi_pc_rdata, \ + output wire [31:0] rvfi_pc_wdata, \ + output wire [31:0] rvfi_mem_addr, \ + output wire [3:0] rvfi_mem_rmask, \ + output wire [3:0] rvfi_mem_wmask, \ + output wire [31:0] rvfi_mem_rdata, \ + output wire [31:0] rvfi_mem_wdata, \ + output wire rvfi_mem_fault, \ + output wire [3:0] rvfi_mem_fault_rmask, \ + output wire [3:0] rvfi_mem_fault_wmask + +`define RVFI_WIRES \ + wire rvfi_valid; \ + wire [63:0] rvfi_order; \ + wire [31:0] rvfi_insn; \ + wire rvfi_trap; \ + wire rvfi_halt; \ + wire rvfi_intr; \ + wire [1:0] rvfi_mode; \ + wire [1:0] rvfi_ixl; \ + wire [4:0] rvfi_rs1_addr; \ + wire [4:0] rvfi_rs2_addr; \ + wire [31:0] rvfi_rs1_rdata; \ + wire [31:0] rvfi_rs2_rdata; \ + wire [4:0] rvfi_rd_addr; \ + wire [31:0] rvfi_rd_wdata; \ + wire [31:0] rvfi_pc_rdata; \ + wire [31:0] rvfi_pc_wdata; \ + wire [31:0] rvfi_mem_addr; \ + wire [3:0] rvfi_mem_rmask; \ + wire [3:0] rvfi_mem_wmask; \ + wire [31:0] rvfi_mem_rdata; \ + wire [31:0] rvfi_mem_wdata; \ + wire rvfi_mem_fault; \ + wire [3:0] rvfi_mem_fault_rmask; \ + wire [3:0] rvfi_mem_fault_wmask; + +`define RVFI_CONN \ + .rvfi_valid (rvfi_valid), \ + .rvfi_order (rvfi_order), \ + .rvfi_insn (rvfi_insn), \ + .rvfi_trap (rvfi_trap), \ + .rvfi_halt (rvfi_halt), \ + .rvfi_intr (rvfi_intr), \ + .rvfi_mode (rvfi_mode), \ + .rvfi_ixl (rvfi_ixl), \ + .rvfi_rs1_addr (rvfi_rs1_addr), \ + .rvfi_rs2_addr (rvfi_rs2_addr), \ + .rvfi_rs1_rdata (rvfi_rs1_rdata), \ + .rvfi_rs2_rdata (rvfi_rs2_rdata), \ + .rvfi_rd_addr (rvfi_rd_addr), \ + .rvfi_rd_wdata (rvfi_rd_wdata), \ + .rvfi_pc_rdata (rvfi_pc_rdata), \ + .rvfi_pc_wdata (rvfi_pc_wdata), \ + .rvfi_mem_addr (rvfi_mem_addr), \ + .rvfi_mem_rmask (rvfi_mem_rmask), \ + .rvfi_mem_wmask (rvfi_mem_wmask), \ + .rvfi_mem_rdata (rvfi_mem_rdata), \ + .rvfi_mem_wdata (rvfi_mem_wdata), \ + .rvfi_mem_fault (rvfi_mem_fault), \ + .rvfi_mem_fault_rmask (rvfi_mem_fault_rmask), \ + .rvfi_mem_fault_wmask (rvfi_mem_fault_wmask) + + `endif diff --git a/vendor/Hazard3/hdl/hazard3_triggers.v b/vendor/Hazard3/hdl/hazard3_triggers.v new file mode 100644 index 0000000..0dff196 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_triggers.v @@ -0,0 +1,499 @@ +/*****************************************************************************\ +| Copyright (C) 2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +`default_nettype none + +// The Hazard3 trigger unit always implements one trigger of each of the +// following types: +// +// * Instruction count trigger (type=3) with count=1 (can single-step U-mode +// from M-mode, or step M-mode foreground from an M-mode exception handler) +// +// * Interrupt trigger (type=4): trigger on mask of mtip/msip/meip interrupts +// +// * Exception trigger (type=5): trigger on mask of exception causes +// +// The following are optionally supported: +// +// * Instruction address triggers (type=2 execute=1 select=0) aka breakpoints +// +// Breakpoints always use exact address matches, and the timing is always +// "early". The number of breakpoints is configured by BREAKPOINT_TRIGGERS, +// which can be 0. +// +// Interrupt/exception triggers break after the core transfers to its trap +// handler, but before the first trap handler instruction executes. Only +// action=1 is supported for these triggers, since trap-on-trap is useless +// when M is the only privileged mode. + +module hazard3_triggers #( +`include "hazard3_config.vh" +) ( + input wire clk, + input wire rst_n, + + // Config interface passed through CSR block + input wire [11:0] cfg_addr, + input wire cfg_wen, + input wire [W_DATA-1:0] cfg_wdata, + output reg [W_DATA-1:0] cfg_rdata, + + // Global trigger-to-M-mode enable from tcontrol + input wire trig_m_en, + + // Fetch address query from stage F + input wire [W_ADDR-1:0] fetch_addr, + input wire fetch_m_mode, + input wire fetch_d_mode, + + // Trap trigger events from stage X + input wire event_instr_ret, + + // Trap trigger events from stage M + input wire event_interrupt, + input wire event_exception, + input wire [3:0] event_trap_cause, + input wire event_trap_enter, + + // F-aligned break request (for each halfword of word-sized word-aligned fetch) + output wire [1:0] break_any, + output wire [1:0] break_d_mode, + + // X-aligned step break request (to M-mode only) + output wire break_m_step, + + // Stage-X debug mode flag, for CSR protection (may or may not be the same + // as the query debug mode flag) + input wire x_d_mode, + // Stage-X M-mode flag, for enables on interrupt/exception triggers + input wire x_m_mode +); + +`include "hazard3_csr_addr.vh" + +generate +if (DEBUG_SUPPORT == 0) begin: no_triggers + +// The instantiation of this block should already be stubbed out in core.v if +// there are no triggers, but we still get warnings for elaborating this +// module with zero triggers, so add a generate block here too. + +always @ (*) cfg_rdata = {W_DATA{1'b0}}; +assign break_any = 1'b0; +assign break_d_mode = 1'b0; + +end else begin: have_triggers + +localparam TINDEX_ICOUNT = BREAKPOINT_TRIGGERS + 0; +localparam TINDEX_INTERRUPT = BREAKPOINT_TRIGGERS + 1; +localparam TINDEX_EXCEPTION = BREAKPOINT_TRIGGERS + 2; +localparam N_TRIGGERS = BREAKPOINT_TRIGGERS + 3; + +// If there are no breakpoints, we still have one dummy register (hardwired to +// zero) for Verilog wrangling purposes. It has no synthesis effect. +localparam N_BREAKPOINT_REGS = BREAKPOINT_TRIGGERS > 0 ? BREAKPOINT_TRIGGERS : 1; + +// ---------------------------------------------------------------------------- +// Configuration state + +localparam W_TSELECT = $clog2(N_TRIGGERS); + +reg [W_TSELECT-1:0] tselect; + +// Note tdata1 and mcontrol are the same CSR. tdata1 refers to the universal +// fields (type/dmode) and mcontrol refers to those fields specific to +// type=2 (address/data match), the only trigger type we implement. + +// State for instruction address match triggers (breakpoints). +reg bp_tdata1_dmode [0:N_BREAKPOINT_REGS-1]; +reg mcontrol_action [0:N_BREAKPOINT_REGS-1]; +reg mcontrol_m [0:N_BREAKPOINT_REGS-1]; +reg mcontrol_u [0:N_BREAKPOINT_REGS-1]; +reg mcontrol_execute [0:N_BREAKPOINT_REGS-1]; +reg [W_DATA-1:0] bp_tdata2 [0:N_BREAKPOINT_REGS-1]; + +// State for instruction count trigger +// (hardwired: count=1 dmode=0 action=0; Debug mode single step is already +// available via dcsr) +reg icount_m; +reg icount_u; + +// State for interrupt trigger +// (hardwired: action=1; M-mode trap-on-trap is useless as you lose the +// original trap state) +reg trigger_irq_m; +reg trigger_irq_u; +reg trigger_irq_dmode; +reg [15:0] trigger_irq_cause; + +localparam [15:0] IMPLEMENTED_IRQ_CAUSES = { + 4'h0, // reserved + 1'b1, // meip + 3'h0, // reserved or unimplemented + 1'b1, // mtip + 3'h0, // reserved or unimplemented + 1'b1, // msip + 3'h0 // reserved or unimplemented +}; + +// State for exception trigger +// (hardwired: action=1; M-mode trap-on-trap is useless as you lose the +// original trap state) +reg trigger_exception_m; +reg trigger_exception_u; +reg trigger_exception_dmode; +reg [15:0] trigger_exception_cause; + +localparam [15:0] IMPLEMENTED_EXCEPTION_CAUSES = { + 4'h0, // reserved + 1'b1, // 11 -> ecall from M-mode + 2'h0, // reserved or unimplemented + |U_MODE, // 8 -> ecall from U-mode + 1'b1, // 7 -> store/AMO fault + 1'b1, // 6 -> store/AMO align + 1'b1, // 5 -> load fault + 1'b1, // 4 -> load align + 1'b0, // 3 -> breakpoint; seems useless and risky so disallow + 1'b1, // 2 -> illegal opcode + 1'b1, // 1 -> fetch fault + ~|EXTENSION_C // 0 -> fetch align (only when IALIGN is 32-bit) +}; + +// ---------------------------------------------------------------------------- +// Configuration write port + +localparam N_TRIGGERS_PADDED = 1 << $clog2(N_TRIGGERS); +wire [N_TRIGGERS_PADDED-1:0] tselect_match = {{N_TRIGGERS_PADDED-1{1'b0}}, 1'b1} << tselect; + +always @ (posedge clk or negedge rst_n) begin: cfg_update + integer i; + if (!rst_n) begin + + tselect <= {W_TSELECT{1'b0}}; + + icount_m <= 1'b0; + icount_u <= 1'b0; + + trigger_irq_m <= 1'b0; + trigger_irq_u <= 1'b0; + trigger_irq_dmode <= 1'b0; + trigger_irq_cause <= 16'h0; + + trigger_exception_m <= 1'b0; + trigger_exception_u <= 1'b0; + trigger_exception_dmode <= 1'b0; + trigger_exception_cause <= 16'h0; + + for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin + bp_tdata1_dmode[i] <= 1'b0; + mcontrol_action[i] <= 1'b0; + mcontrol_m[i] <= 1'b0; + mcontrol_u[i] <= 1'b0; + mcontrol_execute[i] <= 1'b0; + bp_tdata2[i] <= {W_DATA{1'b0}}; + end + + end else begin + + if (cfg_wen && cfg_addr == TSELECT) begin + + tselect <= cfg_wdata[W_TSELECT-1:0]; + + end else if (cfg_wen && cfg_addr == TDATA1) begin + + if (tselect_match[TINDEX_ICOUNT]) begin + // This trigger does not implement a dmode bit, as Debug-mode + // break on single-step is already provided by dcsr.step + icount_m <= cfg_wdata[9]; + icount_u <= cfg_wdata[6] && |U_MODE; + end + if (tselect_match[TINDEX_INTERRUPT] && !(trigger_irq_dmode && !x_d_mode)) begin + trigger_irq_dmode <= cfg_wdata[27]; + trigger_irq_m <= cfg_wdata[9]; + trigger_irq_u <= cfg_wdata[6] && |U_MODE; + end + if (tselect_match[TINDEX_EXCEPTION] && !(trigger_exception_dmode && !x_d_mode)) begin + trigger_exception_dmode <= cfg_wdata[27]; + trigger_exception_m <= cfg_wdata[9]; + trigger_exception_u <= cfg_wdata[6] && |U_MODE; + end + for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin + if (tselect_match[i] && !(bp_tdata1_dmode[i] && !x_d_mode)) begin + if (x_d_mode) begin + bp_tdata1_dmode[i] <= cfg_wdata[27]; + end + mcontrol_action[i] <= cfg_wdata[12]; + mcontrol_m[i] <= cfg_wdata[6]; + mcontrol_u[i] <= cfg_wdata[3] && |U_MODE; + mcontrol_execute[i] <= cfg_wdata[2]; + end + end + + end else if (cfg_wen && cfg_addr == TDATA2) begin + + if (tselect_match[TINDEX_INTERRUPT] && !(trigger_irq_dmode && !x_d_mode)) begin + trigger_irq_cause <= cfg_wdata[15:0] & IMPLEMENTED_IRQ_CAUSES; + end + if (tselect_match[TINDEX_EXCEPTION] && !(trigger_exception_dmode && !x_d_mode)) begin + trigger_exception_cause <= cfg_wdata[15:0] & IMPLEMENTED_EXCEPTION_CAUSES; + end + for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin + if (tselect_match[i] && !(bp_tdata1_dmode[i] && !x_d_mode)) begin + bp_tdata2[i] <= cfg_wdata & {{W_ADDR-2{1'b1}}, |EXTENSION_C, 1'b0}; + end + end + + end + + if ((x_d_mode || event_trap_enter) && break_m_step) begin + // count field is hardwired, so the trigger is required to disable + // itself by clearing its own enables + icount_m <= 1'b0; + icount_u <= 1'b0; + end + + // With no breakpoints, there is still a dummy entry to avoid + // `generate` spaghetti; tools complain about comb processes without + // sensitivities etc, so just synchronously tie to 0: + if (BREAKPOINT_TRIGGERS == 0) begin + bp_tdata1_dmode[0] <= 1'b0; + mcontrol_action[0] <= 1'b0; + mcontrol_m[0] <= 1'b0; + mcontrol_u[0] <= 1'b0; + mcontrol_execute[0] <= 1'b0; + bp_tdata2[0] <= {W_DATA{1'b0}}; + end + + end +end + +// ---------------------------------------------------------------------------- +// Configuration read port + +reg [W_DATA-1:0] tdata1_rdata [0:N_TRIGGERS_PADDED-1]; +reg [W_DATA-1:0] tdata2_rdata [0:N_TRIGGERS_PADDED-1]; +reg [W_DATA-1:0] tinfo_rdata [0:N_TRIGGERS_PADDED-1]; + +always @ (*) begin: generate_padded_rdata + + // Default for unimplemented triggers + integer i; + for (i = 0; i < N_TRIGGERS_PADDED; i = i + 1) begin + tdata1_rdata[i] = {W_DATA{1'b0}}; + tdata2_rdata[i] = {W_DATA{1'b0}}; + tinfo_rdata[i] = 32'd1 << 0; // type = 0, no trigger + end + + // Breakpoints are the first n triggers + for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin + tdata1_rdata[i] = { + 4'h2, // type = address/data match + bp_tdata1_dmode[i], + 6'h00, // maskmax = 0, exact match only + 1'b0, // hit = 0, not implemented + 1'b0, // select = 0, address match only + 1'b0, // timing = 0, trigger before execution + 2'h0, // sizelo = 0, unsized + {3'h0, mcontrol_action[i]}, // action = 0/1, break to M-mode/D-mode + 1'b0, // chain = 0, chaining is useless for exact matches + 4'h0, // match = 0, exact match only + mcontrol_m[i], + 1'b0, + 1'b0, // s = 0, no S-mode + mcontrol_u[i], + mcontrol_execute[i], + 1'b0, // store = 0, this is not a watchpoint + 1'b0 // load = 0, this is not a watchpoint + }; + tdata2_rdata[i] = bp_tdata2[i]; + tinfo_rdata[i] = 32'd1 << 2; // type = 2, address/data match + end + + // Instruction count trigger + tdata1_rdata[TINDEX_ICOUNT] = { + 4'h3, // type = instruction count + 1'b0, // dmode = 0 (Debug mode already has dcsr.step) + 2'h0, // reserved + 1'b0, // hit = 0 + 14'd1, // count = 1, single-step only + icount_m, + 1'b0, // reserved + 1'b0, // s = 0, no S-mode + icount_u, + 6'h0 // action = 0, break to M-mode + }; + tinfo_rdata[TINDEX_ICOUNT] = 32'd1 << 3; // type = 3, instruction count + + // Interrupt trigger + tdata1_rdata[TINDEX_INTERRUPT] = { + 4'h4, // type = interrupt + trigger_irq_dmode, + 1'b0, // hit = 0 + 16'h0, // reserved + trigger_irq_m, + 1'b0, // reserved + 1'b0, // s = 0, no S-mode + trigger_irq_u, + 6'd1 // action = 1, break to Debug mode (if dmode=1) + }; + tdata2_rdata[TINDEX_INTERRUPT] = { + 16'h0, + trigger_irq_cause & IMPLEMENTED_IRQ_CAUSES + }; + tinfo_rdata[TINDEX_INTERRUPT] = 32'd1 << 4; + + // Exception trigger + tdata1_rdata[TINDEX_EXCEPTION] = { + 4'h5, // type = exception + trigger_exception_dmode, + 1'b0, // hit = 0 + 16'h0, // reserved + trigger_exception_m, + 1'b0, // reserved + 1'b0, // s = 0, no S-mode + trigger_exception_u, + 6'd1 // action = 1, break to Debug mode (if dmode=1) + }; + tdata2_rdata[TINDEX_EXCEPTION] = { + 16'h0, + trigger_exception_cause & IMPLEMENTED_EXCEPTION_CAUSES + }; + tinfo_rdata[TINDEX_EXCEPTION] = 32'd1 << 5; + +end + +always @ (*) begin + cfg_rdata = {W_DATA{1'b0}}; + if (cfg_addr == TSELECT) begin + cfg_rdata = {{W_DATA-W_TSELECT{1'b0}}, tselect}; + end else if (cfg_addr == TDATA1) begin + cfg_rdata = tdata1_rdata[tselect]; + end else if (cfg_addr == TDATA2) begin + cfg_rdata = tdata2_rdata[tselect]; + end else if (cfg_addr == TINFO) begin + cfg_rdata = tinfo_rdata[tselect]; + end +end + +// ---------------------------------------------------------------------------- +// Interrupt/exception trigger logic + +// Ignore tcontrol.mte as these triggers never target M-mode. +wire exception_trigger_match = + !x_d_mode && trigger_exception_dmode && + (x_m_mode ? trigger_exception_m : trigger_exception_u) && + event_exception && + trigger_exception_cause[event_trap_cause] && + IMPLEMENTED_EXCEPTION_CAUSES[event_trap_cause]; + +wire interrupt_trigger_match = + !x_d_mode && trigger_irq_dmode && + (x_m_mode ? trigger_irq_m : trigger_irq_u) && + event_interrupt && + trigger_irq_cause[event_trap_cause] && + IMPLEMENTED_IRQ_CAUSES[event_trap_cause]; + +// Asserted no later than the end of the aphase for the instruction fetch at +// mtvec. Tags the dphase of trap handler instruction fetches as containing +// breakpoints. +reg break_ie; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + break_ie <= 1'b0; + end else begin + break_ie <= !x_d_mode && (break_ie || ( + exception_trigger_match || interrupt_trigger_match + )); + end +end + +// ---------------------------------------------------------------------------- +// Instruction count trigger logic (single-step under M-mode control) + +wire step_break_enabled = trig_m_en && !x_d_mode && ( + x_m_mode ? icount_m : icount_u +); + +reg break_on_step; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + break_on_step <= 1'b0; + end else begin + // Note icount triggers differ from dcsr.step in that they ignore + // exceptions, only triggering on retired instructions. + break_on_step <= !(x_d_mode || event_trap_enter) && (break_on_step || ( + event_instr_ret && step_break_enabled + )); + end +end + +assign break_m_step = break_on_step; + +// ---------------------------------------------------------------------------- +// Breakpoint trigger logic + +// To reduce the fanin of jump and load/store gating in stage X, the address +// lookup is in stage F (fetch data phase). We check *fetch addresses*, not +// program counter values. Fetches are always word-sized and word-aligned. +// +// To ensure it is safe to do this, non-debug-mode writes to the TDATA1 and +// TDATA2 CSRs cause a prefetch flush, to maintain write-to-fetch ordering. +// +// It's possible for different breakpoints to match different halfwords of the +// fetch word. The trigger unit must report both matches separately, because +// it is not known at this point where the instruction boundaries are (we +// don't have the instruction data yet). + +wire [N_BREAKPOINT_REGS-1:0] breakpoint_enabled; +wire [N_BREAKPOINT_REGS-1:0] breakpoint_match; +wire [N_BREAKPOINT_REGS-1:0] want_d_mode_break; +wire [N_BREAKPOINT_REGS-1:0] want_m_mode_break; +wire [N_BREAKPOINT_REGS-1:0] want_d_mode_break_hw0; +wire [N_BREAKPOINT_REGS-1:0] want_d_mode_break_hw1; +wire [N_BREAKPOINT_REGS-1:0] want_m_mode_break_hw0; +wire [N_BREAKPOINT_REGS-1:0] want_m_mode_break_hw1; + +genvar g; +for (g = 0; g < N_BREAKPOINT_REGS; g = g + 1) begin: match_pc + // Detect tripped breakpoints + assign breakpoint_enabled[g] = mcontrol_execute[g] && !fetch_d_mode && ( + fetch_m_mode ? mcontrol_m[g] : mcontrol_u[g] + ); + assign breakpoint_match[g] = breakpoint_enabled[g] && fetch_addr == {bp_tdata2[g][W_DATA-1:2], 2'b00}; + // Decide the type of break implied by the trip + assign want_d_mode_break[g] = breakpoint_match[g] && mcontrol_action[g] && bp_tdata1_dmode[g]; + assign want_m_mode_break[g] = breakpoint_match[g] && !mcontrol_action[g] && trig_m_en; + // Report separately for each halfword, so the frontend can pass this + // through the prefetch buffer. A breakpoint exception is taken when + // the first halfword of an instruction (of any size) is flagged with + // a breakpoint, implying an exact match. + assign want_d_mode_break_hw0[g] = want_d_mode_break[g] && !bp_tdata2[g][1]; + assign want_d_mode_break_hw1[g] = want_d_mode_break[g] && bp_tdata2[g][1]; + assign want_m_mode_break_hw0[g] = want_m_mode_break[g] && !bp_tdata2[g][1]; + assign want_m_mode_break_hw1[g] = want_m_mode_break[g] && bp_tdata2[g][1]; +end + +// Break flags to frontend (tag the current fetch dphase as containing a breakpoint): + +assign break_any = { + |want_m_mode_break_hw1 || |want_d_mode_break_hw1 || break_ie, + |want_m_mode_break_hw0 || |want_d_mode_break_hw0 || break_ie +} & {2{BREAKPOINT_TRIGGERS > 0}}; + +assign break_d_mode = { + |want_d_mode_break_hw1 || break_ie, + |want_d_mode_break_hw0 || break_ie +} & {2{BREAKPOINT_TRIGGERS > 0}}; + +end +endgenerate + +endmodule + +`ifndef YOSYS +`default_nettype wire +`endif diff --git a/vendor/Hazard3/hdl/hazard3_width_const.vh b/vendor/Hazard3/hdl/hazard3_width_const.vh new file mode 100644 index 0000000..9b838a0 --- /dev/null +++ b/vendor/Hazard3/hdl/hazard3_width_const.vh @@ -0,0 +1,20 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +// These really ought to be localparams, but are occasionally needed for +// passing flags around between modules, so are made available as parameters +// instead. It's ugly, but better scope hygiene than the preprocessor. These +// parameters should not be changed from their default values. + +parameter W_REGADDR = 5, + +parameter W_ALUOP = 6, +parameter W_ALUSRC = 1, +parameter W_MEMOP = 5, +parameter W_BCOND = 2, +parameter W_SHAMT = 5, + +parameter W_EXCEPT = 4, +parameter W_MULOP = 3 diff --git a/vendor/Hazard3/hdl/rv_opcodes.vh b/vendor/Hazard3/hdl/rv_opcodes.vh new file mode 100644 index 0000000..9582338 --- /dev/null +++ b/vendor/Hazard3/hdl/rv_opcodes.vh @@ -0,0 +1,276 @@ +/*****************************************************************************\ +| Copyright (C) 2021-2022 Luke Wren | +| SPDX-License-Identifier: Apache-2.0 | +\*****************************************************************************/ + +localparam RV_RS1_LSB = 15; +localparam RV_RS1_BITS = 5; +localparam RV_RS2_LSB = 20; +localparam RV_RS2_BITS = 5; +localparam RV_RD_LSB = 7; +localparam RV_RD_BITS = 5; + +// Note: these are preprocessor macros, rather than the usual localparams, +// because it's quite difficult to get a definitive citation from 1364-2005 +// for whether Z values are propagated through a localparam to a casez. +// Multiple tools complain about it, so just this once I'll use macros. + +`ifndef HAZARD3_RVOPC_MACROS +`define HAZARD3_RVOPC_MACROS + +// Base ISA (some of these are Z now) +`define RVOPC_BEQ 32'b?????????????????000?????1100011 +`define RVOPC_BNE 32'b?????????????????001?????1100011 +`define RVOPC_BLT 32'b?????????????????100?????1100011 +`define RVOPC_BGE 32'b?????????????????101?????1100011 +`define RVOPC_BLTU 32'b?????????????????110?????1100011 +`define RVOPC_BGEU 32'b?????????????????111?????1100011 +`define RVOPC_JALR 32'b?????????????????000?????1100111 +`define RVOPC_JAL 32'b?????????????????????????1101111 +`define RVOPC_LUI 32'b?????????????????????????0110111 +`define RVOPC_AUIPC 32'b?????????????????????????0010111 +`define RVOPC_ADDI 32'b?????????????????000?????0010011 +`define RVOPC_SLLI 32'b0000000??????????001?????0010011 +`define RVOPC_SLTI 32'b?????????????????010?????0010011 +`define RVOPC_SLTIU 32'b?????????????????011?????0010011 +`define RVOPC_XORI 32'b?????????????????100?????0010011 +`define RVOPC_SRLI 32'b0000000??????????101?????0010011 +`define RVOPC_SRAI 32'b0100000??????????101?????0010011 +`define RVOPC_ORI 32'b?????????????????110?????0010011 +`define RVOPC_ANDI 32'b?????????????????111?????0010011 +`define RVOPC_ADD 32'b0000000??????????000?????0110011 +`define RVOPC_SUB 32'b0100000??????????000?????0110011 +`define RVOPC_SLL 32'b0000000??????????001?????0110011 +`define RVOPC_SLT 32'b0000000??????????010?????0110011 +`define RVOPC_SLTU 32'b0000000??????????011?????0110011 +`define RVOPC_XOR 32'b0000000??????????100?????0110011 +`define RVOPC_SRL 32'b0000000??????????101?????0110011 +`define RVOPC_SRA 32'b0100000??????????101?????0110011 +`define RVOPC_OR 32'b0000000??????????110?????0110011 +`define RVOPC_AND 32'b0000000??????????111?????0110011 +`define RVOPC_LB 32'b?????????????????000?????0000011 +`define RVOPC_LH 32'b?????????????????001?????0000011 +`define RVOPC_LW 32'b?????????????????010?????0000011 +`define RVOPC_LBU 32'b?????????????????100?????0000011 +`define RVOPC_LHU 32'b?????????????????101?????0000011 +`define RVOPC_SB 32'b?????????????????000?????0100011 +`define RVOPC_SH 32'b?????????????????001?????0100011 +`define RVOPC_SW 32'b?????????????????010?????0100011 +`define RVOPC_FENCE 32'b????????????00000000000000001111 +`define RVOPC_FENCE_I 32'b00000000000000000001000000001111 +`define RVOPC_ECALL 32'b00000000000000000000000001110011 +`define RVOPC_EBREAK 32'b00000000000100000000000001110011 +`define RVOPC_CSRRW 32'b?????????????????001?????1110011 +`define RVOPC_CSRRS 32'b?????????????????010?????1110011 +`define RVOPC_CSRRC 32'b?????????????????011?????1110011 +`define RVOPC_CSRRWI 32'b?????????????????101?????1110011 +`define RVOPC_CSRRSI 32'b?????????????????110?????1110011 +`define RVOPC_CSRRCI 32'b?????????????????111?????1110011 +`define RVOPC_MRET 32'b00110000001000000000000001110011 +`define RVOPC_SYSTEM 32'b?????????????????????????1110011 +`define RVOPC_WFI 32'b00010000010100000000000001110011 + +// M extension +`define RVOPC_MUL 32'b0000001??????????000?????0110011 +`define RVOPC_MULH 32'b0000001??????????001?????0110011 +`define RVOPC_MULHSU 32'b0000001??????????010?????0110011 +`define RVOPC_MULHU 32'b0000001??????????011?????0110011 +`define RVOPC_DIV 32'b0000001??????????100?????0110011 +`define RVOPC_DIVU 32'b0000001??????????101?????0110011 +`define RVOPC_REM 32'b0000001??????????110?????0110011 +`define RVOPC_REMU 32'b0000001??????????111?????0110011 + +// A extension +`define RVOPC_LR_W 32'b00010??00000?????010?????0101111 +`define RVOPC_SC_W 32'b00011????????????010?????0101111 +`define RVOPC_AMOSWAP_W 32'b00001????????????010?????0101111 +`define RVOPC_AMOADD_W 32'b00000????????????010?????0101111 +`define RVOPC_AMOXOR_W 32'b00100????????????010?????0101111 +`define RVOPC_AMOAND_W 32'b01100????????????010?????0101111 +`define RVOPC_AMOOR_W 32'b01000????????????010?????0101111 +`define RVOPC_AMOMIN_W 32'b10000????????????010?????0101111 +`define RVOPC_AMOMAX_W 32'b10100????????????010?????0101111 +`define RVOPC_AMOMINU_W 32'b11000????????????010?????0101111 +`define RVOPC_AMOMAXU_W 32'b11100????????????010?????0101111 + +// Zba (address generation) +`define RVOPC_SH1ADD 32'b0010000??????????010?????0110011 +`define RVOPC_SH2ADD 32'b0010000??????????100?????0110011 +`define RVOPC_SH3ADD 32'b0010000??????????110?????0110011 + +// Zbb (basic bit manipulation) +`define RVOPC_ANDN 32'b0100000??????????111?????0110011 +`define RVOPC_CLZ 32'b011000000000?????001?????0010011 +`define RVOPC_CPOP 32'b011000000010?????001?????0010011 +`define RVOPC_CTZ 32'b011000000001?????001?????0010011 +`define RVOPC_MAX 32'b0000101??????????110?????0110011 +`define RVOPC_MAXU 32'b0000101??????????111?????0110011 +`define RVOPC_MIN 32'b0000101??????????100?????0110011 +`define RVOPC_MINU 32'b0000101??????????101?????0110011 +`define RVOPC_ORC_B 32'b001010000111?????101?????0010011 +`define RVOPC_ORN 32'b0100000??????????110?????0110011 +`define RVOPC_REV8 32'b011010011000?????101?????0010011 +`define RVOPC_ROL 32'b0110000??????????001?????0110011 +`define RVOPC_ROR 32'b0110000??????????101?????0110011 +`define RVOPC_RORI 32'b0110000??????????101?????0010011 +`define RVOPC_SEXT_B 32'b011000000100?????001?????0010011 +`define RVOPC_SEXT_H 32'b011000000101?????001?????0010011 +`define RVOPC_XNOR 32'b0100000??????????100?????0110011 +`define RVOPC_ZEXT_H 32'b000010000000?????100?????0110011 + +// Zbc (carry-less multiply) +`define RVOPC_CLMUL 32'b0000101??????????001?????0110011 +`define RVOPC_CLMULH 32'b0000101??????????011?????0110011 +`define RVOPC_CLMULR 32'b0000101??????????010?????0110011 + +// Zbs (single-bit manipulation) +`define RVOPC_BCLR 32'b0100100??????????001?????0110011 +`define RVOPC_BCLRI 32'b0100100??????????001?????0010011 +`define RVOPC_BEXT 32'b0100100??????????101?????0110011 +`define RVOPC_BEXTI 32'b0100100??????????101?????0010011 +`define RVOPC_BINV 32'b0110100??????????001?????0110011 +`define RVOPC_BINVI 32'b0110100??????????001?????0010011 +`define RVOPC_BSET 32'b0010100??????????001?????0110011 +`define RVOPC_BSETI 32'b0010100??????????001?????0010011 + +// Zbkb (basic bit manipulation for crypto) (minus those in Zbb) +`define RVOPC_PACK 32'b0000100??????????100?????0110011 +`define RVOPC_PACKH 32'b0000100??????????111?????0110011 +`define RVOPC_BREV8 32'b011010000111?????101?????0010011 +`define RVOPC_UNZIP 32'b000010001111?????101?????0010011 +`define RVOPC_ZIP 32'b000010001111?????001?????0010011 + +// Zbkc is a subset of Zbc. + +// Zbkx (crossbar permutation) +`define RVOPC_XPERM8 32'b0010100??????????100?????0110011 +`define RVOPC_XPERM4 32'b0010100??????????010?????0110011 + +// Zilsd (load/store pair) +`define RVOPC_LD 32'b?????????????????011????00000011 // rd[0] == 0 +`define RVOPC_SD 32'b???????????0?????011?????0100011 // rs2[0] == 0 + +// Hazard3 custom instructions + +// Xh3bextm (Hazard3 multi-bit extract): multi-bit versions of bext/bexti from Zbs +`define RVOPC_H3_BEXTM 32'b000???0??????????000?????0001011 // custom-0 funct3=0 +`define RVOPC_H3_BEXTMI 32'b000???0??????????100?????0001011 // custom-0 funct3=4 + +// C Extension +`define RVOPC_C_ADDI4SPN 16'b000???????????00 // *** illegal if imm 0 +`define RVOPC_C_LW 16'b010???????????00 +`define RVOPC_C_SW 16'b110???????????00 + +`define RVOPC_C_ADDI 16'b000???????????01 +`define RVOPC_C_JAL 16'b001???????????01 +`define RVOPC_C_J 16'b101???????????01 +`define RVOPC_C_LI 16'b010???????????01 +// addi16sp when rd=2: +`define RVOPC_C_LUI 16'b011???????????01 // *** reserved if imm 0 (for both LUI and ADDI16SP) +`define RVOPC_C_SRLI 16'b100000????????01 // On RV32 imm[5] (instr[12]) must be 0, else reserved NSE. +`define RVOPC_C_SRAI 16'b100001????????01 // On RV32 imm[5] (instr[12]) must be 0, else reserved NSE. +`define RVOPC_C_ANDI 16'b100?10????????01 +`define RVOPC_C_SUB 16'b100011???00???01 +`define RVOPC_C_XOR 16'b100011???01???01 +`define RVOPC_C_OR 16'b100011???10???01 +`define RVOPC_C_AND 16'b100011???11???01 +`define RVOPC_C_BEQZ 16'b110???????????01 +`define RVOPC_C_BNEZ 16'b111???????????01 + +`define RVOPC_C_SLLI 16'b0000??????????10 // On RV32 imm[5] (instr[12]) must be 0, else reserved NSE. +// jr if !rs2: +`define RVOPC_C_MV 16'b1000??????????10 // *** reserved if JR and !rs1 (instr[11:7]) +// jalr if !rs2: +`define RVOPC_C_ADD 16'b1001??????????10 // *** EBREAK if !instr[11:2] +`define RVOPC_C_LWSP 16'b010???????????10 // *** reserved if rd=x0 +`define RVOPC_C_SWSP 16'b110???????????10 + +// Zcb simple additional compressed instructions +`define RVOPC_C_LBU 16'b100000????????00 +`define RVOPC_C_LHU 16'b100001???0????00 +`define RVOPC_C_LH 16'b100001???1????00 +`define RVOPC_C_SB 16'b100010????????00 +`define RVOPC_C_SH 16'b100011???0????00 +`define RVOPC_C_ZEXT_B 16'b100111???1100001 +`define RVOPC_C_SEXT_B 16'b100111???1100101 +`define RVOPC_C_ZEXT_H 16'b100111???1101001 +`define RVOPC_C_SEXT_H 16'b100111???1101101 +`define RVOPC_C_NOT 16'b100111???1110101 +`define RVOPC_C_MUL 16'b100111???10???01 + +// Zclsd load/store pair instructions +`define RVOPC_C_LD 16'b011??????????000 // rd[0] == 0 +`define RVOPC_C_LDSP 16'b011?????0?????10 // rd[0] == 0 +`define RVOPC_C_SD 16'b111??????????000 // rs2[0] == 0 +`define RVOPC_C_SDSP 16'b111??????????010 // rs2[0] == 0 + +// Zcmp push/pop instructions +`define RVOPC_CM_PUSH 16'b10111000??????10 +`define RVOPC_CM_POP 16'b10111010??????10 +`define RVOPC_CM_POPRETZ 16'b10111100??????10 +`define RVOPC_CM_POPRET 16'b10111110??????10 +`define RVOPC_CM_MVSA01 16'b101011???01???10 +`define RVOPC_CM_MVA01S 16'b101011???11???10 + +// Copies provided here with 0 instead of ? so that these can be used to build 32-bit instructions in the decompressor + +`define RVOPC_NOZ_BEQ 32'b00000000000000000000000001100011 +`define RVOPC_NOZ_BNE 32'b00000000000000000001000001100011 +`define RVOPC_NOZ_BLT 32'b00000000000000000100000001100011 +`define RVOPC_NOZ_BGE 32'b00000000000000000101000001100011 +`define RVOPC_NOZ_BLTU 32'b00000000000000000110000001100011 +`define RVOPC_NOZ_BGEU 32'b00000000000000000111000001100011 +`define RVOPC_NOZ_JALR 32'b00000000000000000000000001100111 +`define RVOPC_NOZ_JAL 32'b00000000000000000000000001101111 +`define RVOPC_NOZ_LUI 32'b00000000000000000000000000110111 +`define RVOPC_NOZ_AUIPC 32'b00000000000000000000000000010111 +`define RVOPC_NOZ_ADDI 32'b00000000000000000000000000010011 +`define RVOPC_NOZ_SLLI 32'b00000000000000000001000000010011 +`define RVOPC_NOZ_SLTI 32'b00000000000000000010000000010011 +`define RVOPC_NOZ_SLTIU 32'b00000000000000000011000000010011 +`define RVOPC_NOZ_XORI 32'b00000000000000000100000000010011 +`define RVOPC_NOZ_SRLI 32'b00000000000000000101000000010011 +`define RVOPC_NOZ_SRAI 32'b01000000000000000101000000010011 +`define RVOPC_NOZ_ORI 32'b00000000000000000110000000010011 +`define RVOPC_NOZ_ANDI 32'b00000000000000000111000000010011 +`define RVOPC_NOZ_ADD 32'b00000000000000000000000000110011 +`define RVOPC_NOZ_SUB 32'b01000000000000000000000000110011 +`define RVOPC_NOZ_SLL 32'b00000000000000000001000000110011 +`define RVOPC_NOZ_SLT 32'b00000000000000000010000000110011 +`define RVOPC_NOZ_SLTU 32'b00000000000000000011000000110011 +`define RVOPC_NOZ_XOR 32'b00000000000000000100000000110011 +`define RVOPC_NOZ_SRL 32'b00000000000000000101000000110011 +`define RVOPC_NOZ_SRA 32'b01000000000000000101000000110011 +`define RVOPC_NOZ_OR 32'b00000000000000000110000000110011 +`define RVOPC_NOZ_AND 32'b00000000000000000111000000110011 +`define RVOPC_NOZ_LB 32'b00000000000000000000000000000011 +`define RVOPC_NOZ_LH 32'b00000000000000000001000000000011 +`define RVOPC_NOZ_LW 32'b00000000000000000010000000000011 +`define RVOPC_NOZ_LBU 32'b00000000000000000100000000000011 +`define RVOPC_NOZ_LHU 32'b00000000000000000101000000000011 +`define RVOPC_NOZ_SB 32'b00000000000000000000000000100011 +`define RVOPC_NOZ_SH 32'b00000000000000000001000000100011 +`define RVOPC_NOZ_SW 32'b00000000000000000010000000100011 +`define RVOPC_NOZ_FENCE 32'b00000000000000000000000000001111 +`define RVOPC_NOZ_FENCE_I 32'b00000000000000000001000000001111 +`define RVOPC_NOZ_ECALL 32'b00000000000000000000000001110011 +`define RVOPC_NOZ_EBREAK 32'b00000000000100000000000001110011 +`define RVOPC_NOZ_CSRRW 32'b00000000000000000001000001110011 +`define RVOPC_NOZ_CSRRS 32'b00000000000000000010000001110011 +`define RVOPC_NOZ_CSRRC 32'b00000000000000000011000001110011 +`define RVOPC_NOZ_CSRRWI 32'b00000000000000000101000001110011 +`define RVOPC_NOZ_CSRRSI 32'b00000000000000000110000001110011 +`define RVOPC_NOZ_CSRRCI 32'b00000000000000000111000001110011 +`define RVOPC_NOZ_SYSTEM 32'b00000000000000000000000001110011 + +// Non-RV32I instructions for Zcb: +`define RVOPC_NOZ_MUL 32'b00000010000000000000000000110011 +`define RVOPC_NOZ_SEXT_B 32'b01100000010000000001000000010011 +`define RVOPC_NOZ_SEXT_H 32'b01100000010100000001000000010011 +`define RVOPC_NOZ_ZEXT_H 32'b00001000000000000100000000110011 + +// Non-RV32I instructions for Zclsd: +`define RVOPC_NOZ_LD 32'b00000000000000000011000000000011 +`define RVOPC_NOZ_SD 32'b00000000000000000011000000100011 + +`endif diff --git a/vendor/Hazard3/project_paths.mk b/vendor/Hazard3/project_paths.mk new file mode 100644 index 0000000..6d50d69 --- /dev/null +++ b/vendor/Hazard3/project_paths.mk @@ -0,0 +1,7 @@ +# Set up root paths used by Makefiles (there is a lot of cross-referencing, +# e.g. tests referencing the HDL directory). This .mk file is +# (eventually) included by every Makefile in the project. + +PROJ_ROOT := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +HDL := $(PROJ_ROOT)/hdl +SCRIPTS := $(PROJ_ROOT)/scripts diff --git a/vendor/Hazard3/scripts/Readme.md b/vendor/Hazard3/scripts/Readme.md new file mode 100644 index 0000000..95ad64a --- /dev/null +++ b/vendor/Hazard3/scripts/Readme.md @@ -0,0 +1,6 @@ +FPGA Scripts +============ + +A loose collection of scripts I've collected while playing with FPGAs at home. Simulation, synthesis, so forth. All of them are terrible, some of them work. + +The most useful one is `listfiles` which provides a simple way of describing source-level dependencies and producing file lists for e.g. a synthesis tool. diff --git a/vendor/Hazard3/scripts/ckcompress b/vendor/Hazard3/scripts/ckcompress new file mode 100755 index 0000000..058b632 --- /dev/null +++ b/vendor/Hazard3/scripts/ckcompress @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 + +# Based on Commander Keen compresssion algorithm (a variant of LZSS) + +import sys +import argparse + +def get_symbol(data, window): + it = iter(data) + needle = bytearray([next(it)]) + try: + while window.find(needle) >= 0: + needle.append(next(it)) + except StopIteration: + pass + + matchlen = len(needle) - 1 + if matchlen <= 1: + return (1, (False, needle[0])) + else: + matchpos = len(window) - 1 - window.find(needle[:-1]) + return (matchlen, (True, matchpos, matchlen - 1)) + +def iter_symbols(data, windowsize): + data = data[:] + window = bytearray() + + while True: + consumed, symbol = get_symbol(data, window) + yield symbol + del window[:max(0, len(window) + consumed - windowsize)] + window.extend(data[:consumed]) + del data[:consumed] + +def bitmap(bits): + bm = 0 + for i, bit in enumerate(bits): + bm = bm | (bool(bit) << i) + return bm + +def iter_bytes(data, windowsize): + syms = iter(iter_symbols(data, windowsize)) + holding = [] + eof = False + while not eof: + try: + holding.append(next(syms)) + except StopIteration: + eof = True + if eof and len(holding) or len(holding) >= 8: + yield bitmap(sym[0] for sym in holding) + for sym in holding: + yield from sym[1:] + holding.clear() + +def compress(data, windowsize): + return bytearray(iter_bytes(data, windowsize)) + +def decompress(cdata, windowsize): + out = bytearray() + window = bytearray() + symbolcount = 0 + it = iter(cdata) + while True: + if symbolcount == 0: + try: + bitmap = next(it) + except StopIteration: + break + symbolcount = 7 + else: + symbolcount -= 1 + if bitmap & 1: + start = len(window) - 1 - next(it) + count = next(it) + 1 + new = window[start:start + count] + out.extend(new) + window.extend(new) + else: + try: + lit = next(it) + except StopIteration: + break + window.append(lit) + out.append(lit) + del window[:max(0, len(window) - windowsize)] + bitmap = bitmap >> 1 + + return out + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("input", help="Input file name") + parser.add_argument("output", help="Output file name") + parser.add_argument("-d", "--decompress", action="store_true", help="Decompress input (default is to compress)") + parser.add_argument("-w", "--window", help="Compression window size") + args = parser.parse_args() + if args.input == "-": + ifile = sys.stdin + else: + ifile = open(args.input, "rb") + if args.output == "-": + ofile = sys.stdout + else: + ofile = open(args.output, "wb") + if args.window is None: + args.window = 256 + + ibuf = bytearray(ifile.read()) + ifile.close() + + if args.decompress: + obuf = decompress(ibuf, args.window) + else: + obuf = compress(ibuf, args.window) + + ofile.write(obuf) + ofile.close() diff --git a/vendor/Hazard3/scripts/extract_ref_nets b/vendor/Hazard3/scripts/extract_ref_nets new file mode 100755 index 0000000..00a6cd7 --- /dev/null +++ b/vendor/Hazard3/scripts/extract_ref_nets @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# Turn KiCad netlist into a PCF file for icestorm + +import argparse +import re +import shlex +from collections import OrderedDict + +def sanitize_net_name(s): + s = s.replace("~", "") + s = re.sub(r"(\d+)$", r"[\1]", s) + return s + +def natural_key(s): + return tuple(int(x) if x.isdigit() else x for x in re.split(r"(\d+)", s)) + +def extract_nets(fh, ref, filters): + nets = [] + + name = None + namevalid = False + + for l in fh: + m = re.match(r"^\s*\(net \([^\)]*\) \(name ([^\)]+)\)", l) + if m: + name = m.group(1).strip().strip("/").lower() + namevalid = not any(name.startswith(s) for s in [ + "\"net", "+", "-", "gnd", "vcc", "vdd", "vss", *filters + ]) + elif namevalid: + m = re.match(r"^\s*\(node \(ref ([^\)]+)\) \(pin ([^\)]+)\)", l) + if m and m.group(1) == ref: + nets.append((name, m.group(2))) + return nets + +def align_buses(nets): + buses = OrderedDict() + onets = [] + # First figure out what is/isn't a bus + for net in nets: + if "[" in net[0]: + busname = net[0].split("[")[0] + if busname not in buses: + buses[busname] = [] + buses[busname].append(net) + else: + onets.append(net) + # Then right-justify the buses + for busname, bus in buses.items(): + indices = list(int(net[0].split('[')[1].strip(']')) for net in bus) + lsb = min(indices) + for index, net in zip(indices, bus): + onets.append(("{}[{}]".format(busname, index - lsb), net[1])) + return onets + + +def write_pcf(fh, nets): + fh.write("# Generated with extract_ref_nets\n\n") + for name, loc in sorted(nets, key = lambda x: natural_key(x[0])): + fh.write("set_io {:<20} {}\n".format(name, loc)) + +def main(): + parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("netfile", help="Path to KiCad .net file") + parser.add_argument("ref", help="Component reference whose named nets will be extracted") + parser.add_argument("--output", "-o", help="Output file name") + parser.add_argument("--filters", "-f", help="-f \"abc xyz\": filter out nets beginning with abc or xyz") + args = parser.parse_args() + opath = "chip.pcf" if args.output is None else args.output + filters = [] if args.filters is None else shlex.split(args.filters) + with open(args.netfile) as ifile: + nets = extract_nets(ifile, args.ref, filters) + nets = map(lambda n: (sanitize_net_name(n[0]), n[1]), nets) + nets = align_buses(nets) + with open(opath, "w") as ofile: + write_pcf(ofile, nets) + +if __name__ == "__main__": + main() diff --git a/vendor/Hazard3/scripts/formal.mk b/vendor/Hazard3/scripts/formal.mk new file mode 100644 index 0000000..6de0c60 --- /dev/null +++ b/vendor/Hazard3/scripts/formal.mk @@ -0,0 +1,43 @@ +# Must define: +# DOTF: .f file containing root of file list +# TOP: name of top-level module + +SRCS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flat $(DOTF)) +INCDIRS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flati $(DOTF)) + +YOSYS=yosys +YOSYS_SMTBMC=$(YOSYS)-smtbmc + +DEPTH?=20 +COVER_APPEND?=3 +YOSYS_SMT_SOLVER?=z3 + +DEFINES?= + +PREP_CMD =read_verilog -formal +PREP_CMD+=$(addprefix -I,$(INCDIRS)) +PREP_CMD+=$(addprefix -D,$(DEFINES) ) +PREP_CMD+= $(SRCS); +PREP_CMD+=prep -top $(TOP); async2sync; dffunmap; write_smt2 -wires $(TOP).smt2 + +BMC_ARGS=-s $(YOSYS_SMT_SOLVER) --dump-vcd $(TOP).vcd -t $(DEPTH) +IND_ARGS=-i $(BMC_ARGS) +COV_ARGS = -c $(BMC_ARGS) --append $(COVER_APPEND) +.PHONY: prove prep bmc induct clean + +prove: bmc induct + +prep: + $(YOSYS) -p "$(PREP_CMD)" > prep.log + +bmc: prep + $(YOSYS_SMTBMC) $(BMC_ARGS) $(TOP).smt2 | tee bmc.log + +induct: prep + $(YOSYS_SMTBMC) $(IND_ARGS) $(TOP).smt2 | tee induct.log + +cover: prep + $(YOSYS_SMTBMC) $(COV_ARGS) $(TOP).smt2 | tee cover.log + +clean:: + rm -f $(TOP).vcd $(TOP).smt2 srcs.mk prep.log bmc.log induct.log cover.log diff --git a/vendor/Hazard3/scripts/gui_run.tcl b/vendor/Hazard3/scripts/gui_run.tcl new file mode 100644 index 0000000..069b1c5 --- /dev/null +++ b/vendor/Hazard3/scripts/gui_run.tcl @@ -0,0 +1,13 @@ +# Navigate up directories until we find a file called "Default.wcfg" +# Stop if we get to root + +set cfgdir [file normalize .]; +while {"$cfgdir" != "/"} { + if [file exists $cfgdir/Default.wcfg] { + wcfg open $cfgdir/Default.wcfg + break + } + set cfgdir [file normalize $cfgdir/..] +} + +run 100us; \ No newline at end of file diff --git a/vendor/Hazard3/scripts/listfiles b/vendor/Hazard3/scripts/listfiles new file mode 100755 index 0000000..8a27ea1 --- /dev/null +++ b/vendor/Hazard3/scripts/listfiles @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 + +import shlex +import os +import sys +import argparse +import re + +help_str = """ + +Tool for building file lists, into formats required by various tools. +".f" files contain four types of command: + +file (filename) + adds a file to the list +include (dir) + add a directory to include path, if output format supports this +wildcard (.extension) (dir) + add all files with a given extension in a given directory +list (filename) + recurse on another filelist file + +The idea is that each component in a project has a +.f file which lists all of the Verilog (e.g.) files +for that component. Higher-level .f files will hierarchically +include the lower-level ones. + +In this way, you can build large flat file lists to pass into the +various tools, but never have to *write* large flat file lists. + +It also makes it easier to specify parts of your design hierarchy +for a given tool. For example, if you just want to synthesise your +CPU, you can run "listfiles cpu.f -f flat" +""" + +def wildcard(dir, extension): + prev_dir = os.getcwd() + os.chdir(dir) + files = [os.path.abspath(f) for f in os.listdir() if os.path.splitext(f)[-1] == extension] + os.chdir(prev_dir) + return files + +def read_filelist(fname): + files = [] + includes = [] + f = open(fname) + prev_dir = os.getcwd() + os.chdir(os.path.dirname(os.path.abspath(fname))) + for l in f.readlines(): + l = l.split("#")[0].strip() + if l == "": + continue + words = shlex.split(l) + if words[0] == "file": + assert(len(words) == 2) + files.append(os.path.abspath(os.path.expandvars(words[1]))) + elif words[0] == "include": + assert(len(words) == 2) + includes.append(os.path.abspath(os.path.expandvars(words[1]))) + elif words[0] == "wildcard": + assert(len(words) == 3) + files.extend(wildcard(os.path.expandvars(words[2]), words[1])) + elif words[0] == "list": + assert(len(words) == 2) + newfiles, newincludes = read_filelist(os.path.expandvars(words[1])) + files.extend(newfiles) + includes.extend(newincludes) + else: + raise Exception("In filelist {}: Invalid command \"{}\"".format(fname, words[0])) + os.chdir(prev_dir) + return (files, includes) + +formats = { + "isim": lambda f, i: "verilog work {} {}\n".format(" ".join(f), " ".join("-i " + inc for inc in i)), + "flat": lambda f, i: " ".join(f) + "\n", + "flati": lambda f, i: " ".join(i) + "\n", + "make": lambda f, i: "SRCS={}\nINCDIRS={}\n".format(" ".join(f), " ".join(i)) +} + +if __name__ == "__main__": + parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter) + parser.epilog = help_str + parser.add_argument("src", help="File list source file") + parser.add_argument("--format", "-f", help="Format to generate output in. Allowed: isim, make, flat (default)") + parser.add_argument("--relative", "-r", action="store_true", help="Use relative paths in output file") + parser.add_argument("--relativeto", help="Use relative paths, relative to some specified path") + parser.add_argument("--output", "-o", help="Output file name") + args = parser.parse_args() + if args.format is None: + args.format = "flat" + files, includes = read_filelist(args.src) + # Uniquify whilst preserving order + func = lambda l: list(dict.fromkeys(l)) + if args.relative: + func = lambda l, func=func: [os.path.relpath(f) for f in func(l)] + elif args.relativeto: + func = lambda l, func=func: [os.path.relpath(f, args.relativeto) for f in func(l)] + + files, includes = map(func, (files, includes)) + if args.format not in formats: + sys.exit("Unknown format: " + args.format) + ofile = sys.stdout if args.output is None else open(args.output, "w") + ofile.write(formats[args.format](files, includes)) diff --git a/vendor/Hazard3/scripts/mkflashexec b/vendor/Hazard3/scripts/mkflashexec new file mode 100755 index 0000000..62b5881 --- /dev/null +++ b/vendor/Hazard3/scripts/mkflashexec @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import argparse +import struct + +parser = argparse.ArgumentParser() +parser.add_argument("ifile") +parser.add_argument("ofile") +args = parser.parse_args() +data = open(args.ifile, "rb").read() +with open(args.ofile, "wb") as ofile: + ofile.write("RISCBoy".encode() + bytes(1)) + ofile.write(struct.pack("= 8: + yield accum & 0xff + accum = accum >> 8 + accum_size -= 8 + +class BinHeader: + def __init__(self, filename, arrayname=None): + if arrayname is None: + arrayname = filename.split(".")[0] + self.f = open(filename, "w") + self.out_count = 0 + self.f.write( + "#ifndef _IMG_ASSET_SECTION\n" \ + "#define _IMG_ASSET_SECTION \".data\"\n" \ + "#endif\n\n" \ + f"static const char __attribute__((aligned(16), section(_IMG_ASSET_SECTION \".{arrayname}\"))) {arrayname}[] = {{\n\t" + ) + + def write(self, bs): + for b in bs: + self.f.write("0x{:02x}".format(b) + (",\n\t" if self.out_count % 16 == 15 else ", ")) + self.out_count += 1 + + def close(self): + self.f.write("\n};\n") + self.f.close() + +# Fixed dither -- note every number 0...15 appears once (thanks Graham) +dither_pattern_4x4 = [ + [0 , 8 , 2 , 10], + [12 , 4 , 14 , 6 ], + [3 , 11 , 1 , 9 ], + [15 , 7 , 13 , 5 ], +] + +def format_channel(data, msb, lsb, dither=False, dithercoord=None): + # Assume data to be 8 bits + out_width = msb - lsb + 1 + assert(out_width <= 8) + if dither: + ditherval = dither_pattern_4x4[dithercoord[1] % 4][dithercoord[0] % 4] + shamt = (8 - out_width) - 4 + if shamt >= 0: + data += ditherval << shamt + else: + data += ditherval >> -shamt + data = min(data, 0xff) + return (data >> (8 - out_width)) << lsb + +def format_rgb_pixel(pix, fmt, dither=False, dithercoord=None): + accum = 0 + for p, f in zip(pix, fmt): + accum |= format_channel(p, f[0], f[1], dither, dithercoord) + if len(pix) == len(fmt) - 1: + accum |= format_channel(0xff, fmt[-1][0], fmt[-1][1]) + return accum + +# TODO would be kind of nice to generate these based on format string but I don't need that yet +rgb_formats = { + "argb1555": (16, ((14, 10), (9, 5), (4, 0), (15, 15))), + "rgab5515": (16, ((15, 11), (10, 6), (4, 0), (5, 5))), + "bgar5515": (16, ((4, 0), (10, 6), (15, 11), (5, 5))), + "rgb565" : (16, ((15, 11), (10, 5), (4, 0))), + "argb1232": (8, ((6, 5), (4, 2), (1, 0), (7, 7))), + "ragb2132": (8, ((7, 6), (4, 2), (1, 0), (5, 5))), + "rgb332" : (8, ((7, 5), (4, 2), (1, 0))), + "r2" : (2, ((1, 0), (-1, 0), (-1, 0))), + "r1" : (1, ((0, 0), (-1, 0), (-1, 0))), +} + +def format_pixel(format, src_has_transparency, pixel, dither=False, dithercoord=None): + assert(format in FORMATS) + if format in rgb_formats: + return (rgb_formats[format][0], format_rgb_pixel(pixel, rgb_formats[format][1], dither, dithercoord)) + elif format in ["p8", "p4", "p2", "p1"]: + size = int(format[1:]) + return (size, (pixel + src_has_transparency) & ((1 << size) - 1)) + else: + raise Exception() + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("input", help="Input file name") + parser.add_argument("output", help="Output file name") + parser.add_argument("--tilesize", "-t", help="Tile size (pixels), default 8", + default="8", choices=[str(2 ** i) for i in range(3, 11)]) + parser.add_argument("--single", "-s", action="store_true", + help="The input consists of a single image of arbitrary width/height, rather than a tileset") + parser.add_argument("--format", "-f", help="Output pixel format, default argb1555", + default="argb1555", choices=FORMATS) + parser.add_argument("--dither", "-d", action="store_true", + help="Apply a simple fixed dither pattern when packing RGB files") + parser.add_argument("--metadata", "-m", action="store_true", + help="Write out opacity metadata at end of file for faster alpha blit (must be used with --single)") + args = parser.parse_args() + img = Image.open(args.input) + if args.single: + tsize_x = img.width + tsize_y = img.height + else: + tsize_x = int(args.tilesize) + tsize_y = tsize_x + if args.metadata and not args.single: + sys.exit("--metadata must be used with --single") + + format_is_paletted = args.format.startswith("p") + image_is_transparent = img.mode == "RGBA" and img.getextrema()[3][0] < 255 + if args.metadata and not image_is_transparent: + sys.exit("Can't write opacity metadata for a non-transparent image") + + friendly_out_name = os.path.basename(args.input).split(".")[0] + + if format_is_paletted: + ncolours_max = 1 << int(args.format[1:]) + ncolours_actual = min(ncolours_max, len(img.getcolors())) + pimg = img.quantize(ncolours_max) + palette = pimg.getpalette() + # TODO haven't found a sane way to make PIL map transparency to palette + if image_is_transparent: + for x in range(img.width): + for y in range(img.height): + if not (img.getpixel((x, y))[3] & 0x80): + pimg.putpixel((x, y), 255) + + if args.output.endswith(".h"): + pfile = BinHeader(args.output + ".pal", arrayname=friendly_out_name + "_pal") + else: + pfile = open(args.output + ".pal", "wb") + if image_is_transparent: + pfile.write(bytes(2)) + pfile.write(bytes(bytes_from_bitstream_le( + format_pixel("argb1555", False, palette[i:i+3]) for i in range(0, ncolours_actual * 3, 3) + ))) + if ncolours_actual < ncolours_max: + pfile.write(bytes(2 * (ncolours_max - ncolours_actual))) + pfile.close() + + img = pimg + + if args.output.endswith(".h"): + ofile = BinHeader(args.output, arrayname=friendly_out_name) + else: + ofile = open(args.output, "wb") + + for y in range(0, img.height - (tsize_y - 1), tsize_y): + for x in range(0, img.width - (tsize_x - 1), tsize_x): + tile = img.crop((x, y, x + tsize_x, y + tsize_y)) + ofile.write(bytes(bytes_from_bitstream_le( + format_pixel(args.format, image_is_transparent, tile.getpixel((i, j)), args.dither, dithercoord=(i, j)) for j in range(tsize_y) for i in range(tsize_x) + ))) + if args.metadata: + assert(tsize_x * tsize_y % 4 == 0) + for y in range(0, tsize_y): + opacity = list(img.getpixel((x, y))[3] >= 128 for x in range(tsize_x)) + try: + first_transparent = opacity.index(True) + last_transparent = tsize_x - 1 - list(reversed(opacity)).index(True) + continuous_span = all(opacity[first_transparent:last_transparent + 1]) + ofile.write(struct.pack("> 7) + byte = (byte << 1) & 0xff + bits.extend(49 * [0]) # at least 49 dummy cycles required at end. + print("Starting") + gpio.output(fpga_rst, 0) + gpio.output(fpga_ss, 0) + gpio.output(fpga_sclk, 1) # CPOL = 1 (clock idle high) + gpio.output(fpga_sdi, 0) + sleep(0.001) + gpio.output(fpga_rst, 1) + sleep(0.001) + gpio.output(fpga_ss, 1) + for i in range(8): + gpio.output(fpga_sclk, 0) + gpio.output(fpga_sclk, 1) + # CPHA = 1 (data captured on trailing edge of clock pulse) + gpio.output(fpga_ss, 0) + for bit in bits: + gpio.output(fpga_sdi, bit) + gpio.output(fpga_sclk, 0) + gpio.output(fpga_sclk, 1) + if gpio.input(fpga_done): + print("CDONE high, yay!") + else: + print("CDONE not high, something may have gone wrong") + +if __name__ == "__main__": + if len(sys.argv) != 2: + exit("Usage: piceprog (file.bin)") + prog(sys.argv[1]) diff --git a/vendor/Hazard3/scripts/regblock b/vendor/Hazard3/scripts/regblock new file mode 100755 index 0000000..cebdb83 --- /dev/null +++ b/vendor/Hazard3/scripts/regblock @@ -0,0 +1,526 @@ +#!/usr/bin/env python3 + +# Regblock generation script + +import argparse +import re +import sys +import textwrap +import yaml +from collections import OrderedDict +from math import log2, ceil + +# Regblock data structures and parser/loader + +class RegBlock: + bus_types = ["apb"] + field_properties = ["name", "b", "access", "info", "rst", "concat"] + def __init__(self, name, w_data, w_addr, bus, info): + self.name = name + self.w_data = w_data + self.w_addr = w_addr + if bus not in self.bus_types: + raise Exception("Unknown bus type: {}".format(bus)) + self.bus = bus + self.info = info + self.regs = [] + + def add(self, reg): + self.regs.append(reg) + + @staticmethod + def load(file): + y = yaml.load(file.read(), Loader=yaml.FullLoader) + if "bus" not in y: + sys.exit("Must specify bus type with \"bus: x\"") + if "data" not in y: + sys.exit("Must specify data width with \"data: x\"") + if "addr" not in y: + sys.exit("Must specify address width with \"addr: x\"") + if "name" not in y: + sys.exit("Must specify regblock name with \"name: x\"") + rb = RegBlock(y["name"], y["data"], y["addr"], y["bus"], y["info"] if "info" in y else None) + param_dict = {"W_DATA": y["data"]} + if ("params" in y): + param_dict = {**param_dict, **y["params"]} + param_resolve = lambda x: x if type(x) is int else int(eval(x, {**param_dict})) + # Expand any generate blocks (one level only) + reglist = [] + for i, rspec in enumerate(y["regs"]): + if "generate" not in rspec: + reglist.append(rspec) + continue + yaml_lines = [] + emit = lambda x: yaml_lines.append(str(x)) + exec(rspec["generate"], {"_": emit, **param_dict}, dict()) + newregs = yaml.load("\n".join(yaml_lines), Loader=yaml.FullLoader) + if newregs is not None: + reglist.extend(newregs) + # Then process the expanded reglist + for rspec in reglist: + reg = Register(rspec["name"], rb.w_data, rspec["info"] if "info" in rspec else None, rspec["wstb"] if "wstb" in rspec else None) + rb.add(reg) + for fspec in rspec["bits"]: + for key in fspec: + if key not in RegBlock.field_properties: + raise Exception("'{}' is not a valid property for a field.".format(key)) + bitrange = fspec["b"] + if type(bitrange) is int: + bitrange = [bitrange, bitrange] + reg.add(Field( + fspec["name"] if "name" in fspec else "", + param_resolve(bitrange[0]), + param_resolve(bitrange[1]), + fspec["access"], + fspec["rst"] if "rst" in fspec else 0, + fspec["info"] if "info" in fspec else None, + fspec["concat"] if "concat" in fspec else None + )) + return rb + + def accept(self, visitor): + visitor.pre(self) + for reg in self.regs: + reg.accept(visitor) + visitor.post(self) + +class Register: + def __init__(self, name, width, info, wstrobe=None): + assert(width > 0) + self.name = name + self.width = width + self.info = info + self.occupancy = [None] * width + self.fields = OrderedDict() + self.wstrobe = wstrobe + + def add(self, field): + if field.lsb >= self.width or field.msb >= self.width or field.lsb < 0 or field.msb < 0: + raise Exception("Field {} extends outside of register {}".format(field.name, self.name)) + if field.name in self.fields: + raise Exception("{} already has a field called \"{}\"".format(self.name, field.name)) + for i in range(field.lsb, field.msb + 1): + if self.occupancy[i] is not None: + raise Exception("Field {} overlaps {} in register {}".format(field.name, self.occupancy[i], self.name)) + self.occupancy[i] = field.name + self.fields[field.name] = field + field.parent = self + + def accept(self, visitor): + visitor.pre(self) + for field in self.fields.values(): + field.accept(visitor) + visitor.post(self) + + +class Field: + access_types = ["ro", "rov", "wo", "rw", "rf", "wf", "rwf", "sc", "w1c"] + def __init__(self, name, msb, lsb, access, resetval=0, info="", concat=None, parent=None): + if lsb > msb: + raise Exception("Field width must be >= 0 in field {}".format(name)) + if access not in self.access_types: + raise Exception("Unknown access type: {}. Recognised types: {}".format(access, ", ".join(self.access_types))) + if access in ["sc", "w1c"] and msb != lsb: + raise Exception("Field width must be 1 for access type '{}'".format(access)) + self.name = name + self.msb = msb + self.lsb = lsb + self.access = access + self.resetval = resetval + self.info = info + self.concat = concat + self.parent = parent + + @property + def width_decl(self): + if self.msb == self.lsb: + return "" + else: + return "[{}:0]".format(self.msb - self.lsb) + + @property + def width(self): + return self.msb - self.lsb + 1 + + @property + def fullname(self): + if self.name == "": + return self.parent.name + else: + return "{}_{}".format(self.parent.name, self.name) + + def accept(self, visitor): + visitor.pre(self) + +# Base class for various useful lumps of functionality +# which are applied as traversals on a regblock description tree +class RegBlockVisitor: + def pre(self, x): + if type(x) is RegBlock: + self.pre_regblock(x) + elif type(x) is Register: + self.pre_register(x) + elif type(x) is Field: + self.pre_field(x) + else: + raise TypeError() + + def post(self, x): + if type(x) is RegBlock: + self.post_regblock(x) + elif type(x) is Register: + self.post_register(x) + elif type(x) is Field: + self.post_field(x) + else: + raise TypeError() + + def pre_regblock(self, rb): + raise NotImplementedError() + + def pre_register(self, r): + raise NotImplementedError() + + def pre_field(self, f): + raise NotImplementedError() + + def post_regblock(self, rb): + raise NotImplementedError() + + def post_register(self, r): + raise NotImplementedError() + + def post_field(self, f): + raise NotImplementedError() + + +# Verilog generation + +def width2str(w): + return "[{}:0]".format(w - 1) + +def c_block_comment(lines, width=80, align="<"): + blines = [] + blines.append("/" + (width - 1) * "*") + line_fmt = "* {:" + align + str(width - 4) + "} *" + for l in lines: + blines.append(line_fmt.format(l.strip())) + blines.append((width - 1) * "*" + "/") + return blines + +def c_line_comment(line): + if not hasattr(c_line_comment, "wrap"): + c_line_comment.wrap = textwrap.TextWrapper(width=80, initial_indent="// ", subsequent_indent="// ") + return "\n".join(c_line_comment.wrap.wrap(line.strip())) + +class Verilog: + def __init__(self): + self.header = [] + self.ports = [] + self.decls = [] + self.logic_comb = [] + self.logic_rst = [] + self.logic_clk = [] + + def __add__(self, other): + new = Verilog() + new.header.extend(self.header) + for n, s, o in zip( + [new.ports, new.decls, new.logic_comb, new.logic_rst, new.logic_clk], + [self.ports, self.decls, self.logic_comb, self.logic_rst, self.logic_clk], + [other.ports, other.decls, other.logic_comb, other.logic_rst, other.logic_clk]): + n.extend(s) + n.extend(o) + return new + + def __str__(self): + strs = [] + strs.extend(self.header) + strs.extend("\t" + s + ("" if s == "" or s.startswith("//") else ",") for s in self.ports[:-1]) + if len(self.ports) > 0: + strs.append("\t" + self.ports[-1]) + strs.append(");") + strs.append("") + strs.extend(self.decls); + strs.append("") + strs.append("always @ (*) begin") + strs.extend("\t" + s for s in self.logic_comb) + strs.append("end") + strs.append("") + strs.append("always @ (posedge clk or negedge rst_n) begin") + strs.append("\tif (!rst_n) begin") + strs.extend("\t\t" + s for s in self.logic_rst) + strs.append("\tend else begin") + strs.extend("\t\t" + s for s in self.logic_clk) + strs.append("\tend") + strs.append("end") + strs.append("") + strs.append("endmodule\n") + return "\n".join(strs) + + +class VerilogWriter(RegBlockVisitor): + def __init__(self): + self.v = Verilog() + self.regname = None + self.concat = OrderedDict() + self.wstrobe = OrderedDict() + + def pre_regblock(self, rb): + v = self.v + v.header.extend(c_block_comment(["AUTOGENERATED BY REGBLOCK", "Do not edit manually.", + "Edit the source file (or regblock utility) and regenerate."], align = "^")) + v.header.append("") + v.header.append(c_line_comment("{:<20} : {}".format("Block name", rb.name))) + v.header.append(c_line_comment("{:<20} : {}".format("Bus type", rb.bus))) + v.header.append(c_line_comment("{:<20} : {}".format("Bus data width", rb.w_data))) + v.header.append(c_line_comment("{:<20} : {}".format("Bus address width", rb.w_addr))) + v.header.append("") + if rb.info is not None: + v.header.append(c_line_comment(rb.info)) + v.header.append("") + v.header.append("module {}_regs (".format(rb.name)) + v.ports.extend(["input wire clk", "input wire rst_n",]) + addr_mask = (1 << 2 + ceil(log2(len(rb.regs)))) - 1 + addr_mask = addr_mask & (-1 << ceil(log2(rb.w_data / 8))) + if rb.bus == "apb": + v.ports.append("") + v.ports.append("// APB Port") + v.ports.append("input wire apbs_psel") + v.ports.append("input wire apbs_penable") + v.ports.append("input wire apbs_pwrite") + v.ports.append("input wire {} apbs_paddr".format(width2str(rb.w_addr))) + v.ports.append("input wire {} apbs_pwdata".format(width2str(rb.w_data))) + v.ports.append("output wire {} apbs_prdata".format(width2str(rb.w_data))) + v.ports.append("output wire apbs_pready") + v.ports.append("output wire apbs_pslverr") + v.decls.append("// APB adapter") + v.decls.append("wire {} wdata = apbs_pwdata;".format(width2str(rb.w_data))) + v.decls.append("reg {} rdata;".format(width2str(rb.w_data))) + v.decls.append("wire wen = apbs_psel && apbs_penable && apbs_pwrite;") + v.decls.append("wire ren = apbs_psel && apbs_penable && !apbs_pwrite;") + v.decls.append("wire {} addr = apbs_paddr & {}'h{:x};".format(width2str(rb.w_addr), rb.w_addr, addr_mask)) + v.decls.append("assign apbs_prdata = rdata;") + v.decls.append("assign apbs_pready = 1'b1;") + v.decls.append("assign apbs_pslverr = 1'b0;") + v.decls.append("") + v.ports.append("") + v.ports.append("// Register interfaces") + for i, reg in enumerate(rb.regs): + v.decls.append("localparam ADDR_{} = {};".format(reg.name.upper(), i * 4)) + v.decls.append("") + for reg in rb.regs: + v.decls.append("wire __{}_wen = wen && addr == ADDR_{};".format(reg.name, reg.name.upper())) + v.decls.append("wire __{}_ren = ren && addr == ADDR_{};".format(reg.name, reg.name.upper())) + v.logic_comb.append("case (addr)") + for reg in rb.regs: + v.logic_comb.append("\tADDR_{}: rdata = __{}_rdata;".format(reg.name.upper(), reg.name)) + v.logic_comb.append("\tdefault: rdata = {}'h0;".format(rb.w_data)) + v.logic_comb.append("endcase") + + def post_regblock(self, rb): + for name, conns in self.concat.items(): + concat_name = "concat_{}_o".format(name) + width = sum(f[0] for f in conns) + self.v.ports.append("output wire [{}:0] {}".format(width - 1, concat_name)) + self.v.decls.append("assign {} = {{{}}};".format(concat_name, ", ".join(f[1] for f in reversed(conns)))) + max_strobe_index = OrderedDict() + for name, conns in self.wstrobe.items(): + self.v.logic_rst.append("wstrobe_{} <= 1'b0;".format(name)) + self.v.logic_clk.append("wstrobe_{} <= {};".format(name, " || ".join( + "__{}_wen".format(conn) for conn in conns))) + if name.endswith("]"): + shortname = name.split("[")[0] + idx = int(name.split("[")[-1][:-1]) + if shortname in max_strobe_index: + max_strobe_index[shortname] = max(max_strobe_index[shortname], idx) + else: + max_strobe_index[shortname] = idx + else: + self.v.ports.append("output reg wstrobe_{}".format(name)) + for name, max_idx in max_strobe_index.items(): + self.v.ports.append("output reg [{}:0] wstrobe_{}".format(max_idx, name)) + + def pre_register(self, reg): + v = self.v + self.regname = reg.name + v.decls.append("") + rdata_conns = [] + empty_count = 0 + last_occupant = None + for occupant in reversed(reg.occupancy): + if occupant is None: + empty_count += 1 + elif occupant != last_occupant: + if empty_count > 0: + rdata_conns.append("{}'h0".format(empty_count)) + empty_count = 0 + rdata_conns.append("{}_rdata".format(reg.fields[occupant].fullname)) + last_occupant = occupant + if empty_count > 0: + rdata_conns.append("{}'h0".format(empty_count)) + for field in reg.fields.values(): + lsb = reg.occupancy.index(field.name) + msb = lsb - 1 + reg.occupancy.count(field.name) + index = "[{}]".format(lsb) if msb == lsb else "[{}:{}]".format(msb, lsb) + v.decls.append("wire {} {}_wdata = wdata{};".format(field.width_decl, field.fullname, index)) + v.decls.append("wire {} {}_rdata;".format(field.width_decl, field.fullname)) + v.decls.append("wire {} __{}_rdata = {{{}}};".format(width2str(reg.width), reg.name, ", ".join(rdata_conns))) + + if reg.wstrobe is not None: + if not reg.wstrobe in self.wstrobe: + self.wstrobe[reg.wstrobe] = [] + self.wstrobe[reg.wstrobe].append(reg.name) + + def post_register(self, reg): + pass + + def pre_field(self, f): + v = self.v + rname = self.regname + fname = f.fullname + if f.access in ["rov", "rf", "rwf", "w1c"]: + v.ports.append("input wire {} {}_i".format(f.width_decl, fname)) + if f.access in ["rov", "rf", "rwf"]: + v.decls.append("assign {}_rdata = {}_i;".format(fname, fname)) + if f.access in ["wo", "rw", "wf", "rwf", "sc", "w1c"]: + v.ports.append("output reg {} {}_o".format(f.width_decl, fname)) + if f.access in ["wf", "rwf"]: + v.ports.append("output reg {}_wen".format(fname)) + v.logic_comb.append("{}_wen = __{}_wen;".format(fname, rname)) + v.logic_comb.append("{}_o = {}_wdata;".format(fname, fname)) + if f.access in ["rf", "rwf"]: + v.ports.append("output reg {}_ren".format(fname)) + v.logic_comb.append("{}_ren = __{}_ren;".format(fname, rname)) + if f.access in ["rw"]: + v.decls.append("assign {}_rdata = {}_o;".format(fname, fname)) + if f.access in ["rw", "wo"]: + v.logic_rst.append("{}_o <= {}'h{:x};".format(fname, f.width, f.resetval)) + v.logic_clk.append("if (__{}_wen)".format(rname)) + v.logic_clk.append("\t{}_o <= {}_wdata;".format(fname, fname)) + if f.access in ["w1c"]: + v.logic_rst.append("{} <= {}'h{:x};".format(fname, f.width, f.resetval)) + v.decls.append("reg {} {};".format(f.width_decl, fname)) + v.decls.append("assign {}_rdata = {};".format(fname, fname)) + v.logic_clk.append("{0} <= ({0} && !(__{1}_wen && {0}_wdata)) || {0}_i;".format(fname, rname)) + v.logic_comb.append("{0}_o = {0};".format(fname)) + if f.access in ["sc"]: + v.logic_comb.append("{0}_o = {0}_wdata & {{{1}{{__{2}_wen}}}};".format(fname, f.width, rname)) + if f.access in ["ro", "wo", "wf", "sc"]: + v.decls.append("assign {}_rdata = {}'h{:x};".format(fname, f.width, f.resetval)) + + if f.concat is not None: + if not f.access in ["wo", "rw", "wf", "rwf", "sc"]: + raise Exception("concat specified for port with no regblock output") + if not f.concat in self.concat: + self.concat[f.concat] = [] + self.concat[f.concat].append((f.width, "{}_o".format(fname))) + + +# C header generation + +class HeaderWriter(RegBlockVisitor): + def __init__(self): + self.lines = [] + self.blockname = None + self.regname = None + + def __str__(self): + return "".join(l + "\n" for l in self.lines) + + def pre_regblock(self, rb): + lines = self.lines + self.blockname = rb.name + lines.extend(c_block_comment(["AUTOGENERATED BY REGBLOCK", "Do not edit manually.", + "Edit the source file (or regblock utility) and regenerate."], align = "^")) + lines.append("") + lines.append("#ifndef _{}_REGS_H_".format(rb.name.upper())) + lines.append("#define _{}_REGS_H_".format(rb.name.upper())) + lines.append("") + lines.append(c_line_comment("{:<20} : {}".format("Block name", rb.name))) + lines.append(c_line_comment("{:<20} : {}".format("Bus type", rb.bus))) + lines.append(c_line_comment("{:<20} : {}".format("Bus data width", rb.w_data))) + lines.append(c_line_comment("{:<20} : {}".format("Bus address width", rb.w_addr))) + lines.append("") + if rb.info is not None: + lines.append(c_line_comment(rb.info)) + lines.append("") + for i, reg in enumerate(rb.regs): + lines.append("#define {}_{}_OFFS {}".format(rb.name.upper(), reg.name.upper(), i * 4)) + + def post_regblock(self, rb): + self.lines.append("") + self.lines.append("#endif // _{}_REGS_H_".format(rb.name.upper())) + + def pre_register(self, reg): + lines = self.lines + lines.append("") + lines.extend(c_block_comment([reg.name.upper()], align = "^")) + lines.append("") + if reg.info is not None: + lines.append(c_line_comment(reg.info)) + lines.append("") + self.regname = reg.name + + def post_register(self, reg): + pass + + def pre_field(self, f): + fname = f.fullname.upper() + lines = self.lines + lines.append(c_line_comment("Field: {} Access: {}".format(fname, f.access.upper()))) + fname = self.blockname.upper() + "_" + fname + if f.info is not None: + lines.append(c_line_comment(f.info)) + lines.append("#define {}_LSB {}".format(fname, f.lsb)) + lines.append("#define {}_BITS {}".format(fname, f.width)) + mask = 0 + for i in range(32): + if i in range(f.lsb, f.msb + 1): + mask = mask | (1 << i) + lines.append("#define {}_MASK {:#x}".format(fname, mask)) + +def change_ext(fname, ext): + return ".".join(fname.split(".")[0:-1] + [ext.strip(".")]) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("src", help="Source file to read from (pass - for stdin)") + parser.add_argument("--verilog", "-v", help="Verilog file to write to (pass - for stdout)") + parser.add_argument("--cheader", "-c", help="C header file to write to (pass - for stdout)") + parser.add_argument("--all", "-a", action="store_true", help="Generate all output types, with default filenames") + args = parser.parse_args() + sfile = None + vfile = None + hfile = None + if args.src == "-" and args.all: + exit("Cannot use --all with stdin input") + if args.src == "-": + sfile = sys.stdin + else: + sfile = open(args.src) + rb = RegBlock.load(sfile) + + if args.verilog is not None or args.all: + if args.verilog == "-": + vfile = sys.stdout + elif args.verilog is None: + vfile = open(change_ext(args.src, ".v"), "w") + else: + vfile = open(args.verilog, "w") + if args.cheader is not None or args.all: + if (args.cheader == "-"): + hfile = sys.stdout + elif args.verilog is None: + hfile = open(change_ext(args.src, ".h"), "w") + else: + hfile = open(args.cheader, "w") + + if vfile is not None: + vw = VerilogWriter() + rb.accept(vw) + vfile.write(str(vw.v)) + if hfile is not None: + hw = HeaderWriter() + rb.accept(hw) + hfile.write(str(hw)) diff --git a/vendor/Hazard3/scripts/sim.mk b/vendor/Hazard3/scripts/sim.mk new file mode 100644 index 0000000..a995831 --- /dev/null +++ b/vendor/Hazard3/scripts/sim.mk @@ -0,0 +1,26 @@ +TOP ?= tb +DOTF ?= $(TOP).f +SIMNAME?=simulation + +SIM_VARS = PLATFORM=lin64 LD_LIBRARY_PATH=$XILINX/lib/$PLATFORM +FUSE ?= $(SIM_VARS) fuse +SIMSCRIPT ?= $(SCRIPTS)/sim_run.tcl +GUISCRIPT ?= $(SCRIPTS)/gui_run.tcl + +# Kill implicit rules +.SUFFIXES: +.IMPLICIT: + +sim: build + (cd sim; $(SIM_VARS) ./$(SIMNAME) -tclbatch $(SIMSCRIPT)) + +gui: build + (cd sim; $(SIM_VARS) ./$(SIMNAME) -gui -tclbatch $(GUISCRIPT)) + +build: + mkdir -p sim + $(SCRIPTS)/listfiles --relativeto sim -f isim $(DOTF) -o sim/sim.prj + (cd sim; $(FUSE) -d SIM -prj sim.prj $(TOP) -o $(SIMNAME)) + +clean:: + rm -rf sim \ No newline at end of file diff --git a/vendor/Hazard3/scripts/sim_run.tcl b/vendor/Hazard3/scripts/sim_run.tcl new file mode 100644 index 0000000..a7d3d8d --- /dev/null +++ b/vendor/Hazard3/scripts/sim_run.tcl @@ -0,0 +1 @@ +run 1s; diff --git a/vendor/Hazard3/scripts/software.mk b/vendor/Hazard3/scripts/software.mk new file mode 100644 index 0000000..0bc1868 --- /dev/null +++ b/vendor/Hazard3/scripts/software.mk @@ -0,0 +1,55 @@ +SRCS ?= $(wildcard *.c) $(wildcard *.S) +APPNAME ?= test + +OBJS = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SRCS))) + +CROSS_PREFIX=riscv32-unknown-elf- +CC=$(CROSS_PREFIX)gcc +LD=$(CROSS_PREFIX)gcc +OBJCOPY=$(CROSS_PREFIX)objcopy +OBJDUMP=$(CROSS_PREFIX)objdump + +MARCH?=rv32ic +LDSCRIPT?=memmap.ld +override CCFLAGS+=-c -march=$(MARCH) $(addprefix -I ,$(INCDIRS)) +override CCFLAGS+=-Wall -Wextra -Wno-parentheses +override LDFLAGS+=-march=$(MARCH) -T $(LDSCRIPT) + +# Override to -D to get all sections +DISASSEMBLE?=-d + +.SUFFIXES: +.SECONDARY: +.PHONY: all clean +all: compile + +%.o: %.c + $(CC) $(CCFLAGS) $< -o $@ + +%.o: %.S + $(CC) $(CCFLAGS) $< -o $@ + +$(APPNAME).elf: $(OBJS) + $(LD) $(LDFLAGS) $(OBJS) $(addprefix -l,$(LIBS)) -o $(APPNAME).elf + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +%8.hex: %.elf + $(OBJCOPY) -O verilog $< $@ + +%32.hex: %8.hex + $(SCRIPTS)/vhexwidth -w 32 $< -o $@ + +$(APPNAME).dis: $(APPNAME).elf + @echo ">>>>>>>>> Memory map:" > $(APPNAME).dis + $(OBJDUMP) -h $(APPNAME).elf >> $(APPNAME).dis + @echo >> $(APPNAME).dis + @echo ">>>>>>>>> Disassembly:" >> $(APPNAME).dis + $(OBJDUMP) $(DISASSEMBLE) $(APPNAME).elf >> $(APPNAME).dis + + +compile:: $(APPNAME)32.hex $(APPNAME).dis $(APPNAME).bin + +clean:: + rm -f $(APPNAME).elf $(APPNAME)32.hex $(APPNAME)8.hex $(APPNAME).dis $(APPNAME).bin $(OBJS) diff --git a/vendor/Hazard3/scripts/synth_ecp5.mk b/vendor/Hazard3/scripts/synth_ecp5.mk new file mode 100644 index 0000000..1d4823d --- /dev/null +++ b/vendor/Hazard3/scripts/synth_ecp5.mk @@ -0,0 +1,82 @@ +YOSYS=yosys +NEXTPNR=nextpnr-ecp5 +TRELLIS?=/usr/share/trellis + +CHIPNAME?=chip +DEVICE?=um5g-85k +PACKAGE?=CABGA381 +DEVICE_IDCODE?=0x41113043 + +DEFINES+=FPGA FPGA_ECP5 + +SYNTH_OPT?= +PNR_OPT?= + +SYNTH_CMD=read_verilog $(addprefix -I,$(INCDIRS)) $(addprefix -D,$(DEFINES)) $(SRCS); +ifneq (,$(TOP)) + SYNTH_CMD+=hierarchy -top $(TOP); +endif +SYNTH_CMD+=synth_ecp5 $(SYNTH_OPT) -json $(CHIPNAME).json + +# Kill implicit rules +.SUFFIXES: +.IMPLICIT: + +.PHONY: all romfiles synth clean program dump + +all: bit + +romfiles:: +synth: romfiles $(CHIPNAME).json +dump: romfiles +pnr: synth $(CHIPNAME).config +bit: pnr $(CHIPNAME).bit $(CHIPNAME).svf + +SRCS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flat $(DOTF)) +INCDIRS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flati $(DOTF)) + +dump: + $(YOSYS) -p "$(SYNTH_CMD); write_verilog $(CHIPNAME)_synth.v" + +$(CHIPNAME).json: $(SRCS) + @echo ">>> Synth" + @echo + $(YOSYS) -p "$(SYNTH_CMD)" > synth.log + tail -n 35 synth.log + + +$(CHIPNAME).config: $(CHIPNAME).json $(CHIPNAME).lpf + @echo ">>> Place and Route" + @echo + $(NEXTPNR) -r --placer sa --$(DEVICE) --package $(PACKAGE) --lpf $(CHIPNAME).lpf --json $(CHIPNAME).json --textcfg $@ $(PNR_OPT) --quiet --log pnr.log + @grep "Info: Max frequency for clock " pnr.log | tail -n 1 + +$(CHIPNAME).bit: $(CHIPNAME).config + @echo ">>> Generate Bitstream" + @echo + ecppack --compress --svf $(CHIPNAME).svf --idcode $(DEVICE_IDCODE) $< $@ + +$(CHIPNAME).svf: $(CHIPNAME).bit + +clean:: + rm -f $(CHIPNAME).json $(CHIPNAME).asc $(CHIPNAME).bit $(CHIPNAME)_synth.v + rm -f synth.log pnr.log + +# Code for trying n different pnr seeds and reporting results + +PNR_N_TRIES := 100 +PNR_TRY_LIST := $(shell seq $(PNR_N_TRIES)) + +pnr_sweep: $(addprefix pnr_try,$(PNR_TRY_LIST)) + +define make-sweep-target +pnr_try$1: synth + @echo ">>> Starting sweep $1" + $(NEXTPNR) --seed $1 --placer sa --$(DEVICE) --package $(PACKAGE) --lpf $(CHIPNAME).lpf --json $(CHIPNAME).json --textcfg pnr_try$1.config $(PNR_OPT) --quiet --log pnr$1.log + @grep "Info: Max frequency for clock " pnr$1.log | tail -n 1 +endef + +$(foreach try,$(PNR_TRY_LIST),$(eval $(call make-sweep-target,$(try)))) + +clean:: + rm -f pnr_try*.asc pnr*.log diff --git a/vendor/Hazard3/scripts/synth_ice40.mk b/vendor/Hazard3/scripts/synth_ice40.mk new file mode 100644 index 0000000..59c197c --- /dev/null +++ b/vendor/Hazard3/scripts/synth_ice40.mk @@ -0,0 +1,80 @@ +YOSYS=yosys +NEXTPNR=nextpnr-ice40 +CHIPNAME?=chip +DEVICE?=hx8k +PACKAGE?=bg121 + +DEFINES+=FPGA FPGA_ICE40 + +PRE_SYNTH_CMD?= +SYNTH_OPT?= +PNR_OPT?= + +SYNTH_CMD=read_verilog $(addprefix -I,$(INCDIRS)) $(addprefix -D,$(DEFINES)) $(SRCS); +ifneq (,$(TOP)) + SYNTH_CMD+=hierarchy -top $(TOP); +endif +SYNTH_CMD+=$(PRE_SYNTH_CMD) +SYNTH_CMD+=synth_ice40 $(SYNTH_OPT); write_json $(CHIPNAME).json + +# Kill implicit rules +.SUFFIXES: +.IMPLICIT: + +.PHONY: all romfiles synth clean program dump + +all: bit + +romfiles:: +synth: romfiles $(CHIPNAME).json +dump: romfiles +pnr: synth $(CHIPNAME).asc +bit: pnr $(CHIPNAME).bin + +SRCS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flat $(DOTF)) +INCDIRS=$(shell PROJ_ROOT=$(PROJ_ROOT) HDL=$(HDL) $(SCRIPTS)/listfiles --relative -f flati $(DOTF)) + +dump: + $(YOSYS) -p "$(SYNTH_CMD); write_verilog $(CHIPNAME)_synth.v" + +$(CHIPNAME).json: $(SRCS) + @echo ">>> Synth" + @echo + $(YOSYS) -p "$(SYNTH_CMD)" > synth.log + tail -n 35 synth.log + + +$(CHIPNAME).asc: $(CHIPNAME).json $(CHIPNAME).pcf + @echo ">>> Place and Route" + @echo + $(NEXTPNR) -r --$(DEVICE) --package $(PACKAGE) --pcf $(CHIPNAME).pcf --json $(CHIPNAME).json --asc $(CHIPNAME).asc $(PNR_OPT) --quiet --log pnr.log + @grep "Info: Max frequency for clock " pnr.log | tail -n 1 + +$(CHIPNAME).bin: $(CHIPNAME).asc + @echo ">>> Generate Bitstream" + @echo + icepack -s $(CHIPNAME).asc $(CHIPNAME).bin + +clean:: + rm -f $(CHIPNAME).json $(CHIPNAME).asc $(CHIPNAME).bin $(CHIPNAME)_synth.v + rm -f synth.log pnr.log + +# Code for trying n different pnr seeds and reporting results + +PNR_N_TRIES := 100 +PNR_TRY_LIST := $(shell seq $(PNR_N_TRIES)) + +pnr_sweep: $(addprefix pnr_try,$(PNR_TRY_LIST)) + +define make-sweep-target +pnr_try$1: synth + @echo ">>> Starting sweep $1" + -$(NEXTPNR) --seed $1 --$(DEVICE) --package $(PACKAGE) --pcf $(CHIPNAME).pcf --json $(CHIPNAME).json --asc pnr_try$1.asc $(PNR_OPT) --quiet --log pnr$1.log + @grep "Info: Max frequency for clock " pnr$1.log | tail -n 1 +endef + +$(foreach try,$(PNR_TRY_LIST),$(eval $(call make-sweep-target,$(try)))) + +clean:: + rm -f pnr_try*.asc pnr*.log + diff --git a/vendor/Hazard3/scripts/uartprog b/vendor/Hazard3/scripts/uartprog new file mode 100755 index 0000000..7c80ebb --- /dev/null +++ b/vendor/Hazard3/scripts/uartprog @@ -0,0 +1,275 @@ +#!/usr/bin/env python3 + +# Simple utility for programming SPI flash via a UART shell in the bootloader. +# The bootloader possesses a 1-page buffer which we can write to, read from and +# checksum. +# It also possesses commands for transfers between this buffer and the SPI flash, +# flash erasure, and launching the 2nd stage boot code. + +import argparse +import os +import serial +import serial.tools.list_ports +import sys +import time + +PAGESIZE = 2 ** 8 +SECTORSIZE = 2 ** 12 +BLOCKSIZE = 2 ** 16 + +SRAM_LOAD_ADDR = 0x2 << 28 +SRAM_EXEC_ADDR = SRAM_LOAD_ADDR + 0xc0 + +class ProtocolError(Exception): + pass + +class CMD: + NOP = '\n'.encode() + WRITE_BUF = 'w'.encode() + READ_BUF = 'r'.encode() + GET_CHECKSUM = 'c'.encode() + SET_ADDR = 'a'.encode() + WRITE_FLASH = 'W'.encode() + READ_FLASH = 'R'.encode() + ERASE_SECTOR = 'E'.encode() + ERASE_BLOCK = 'B'.encode() + BOOT_2ND = '2'.encode() + LOAD_MEM = 'l'.encode() + EXEC_MEM = 'x'.encode() + ACK = ':'.encode() + +def reset_shell(port): + while True: + port.write(CMD.NOP * (PAGESIZE + 1)) + port.flushOutput() + time.sleep(0.02) + port.flushInput() + port.write(CMD.NOP) + time.sleep(0.02) + if port.readable() and port.read_all().endswith(CMD.ACK): + break + +def check_ack(port): + resp = port.read() + if len(resp) == 0 or resp != CMD.ACK: + print("Got '{!r}'".format(resp)) + raise ProtocolError() + +def write_buf(port, data, check=True): + assert(len(data) == 256) + port.write(CMD.WRITE_BUF) + port.write(data) + if check: + check_ack(port) + # TODO checksum + +def read_buf(port, pipelined=False): + if not pipelined: + port.write(CMD.READ_BUF) + resp = port.read(PAGESIZE) + if len(resp) != PAGESIZE: + raise ProtocolError() + return resp + # TODO checksum + +def set_addr(port, addr): + addr_b = bytes([ + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr >> 0) & 0xff + ]) + port.write(CMD.SET_ADDR + addr_b) + echo = port.read(3) + if echo != addr_b: + raise ProtocolError() + check_ack(port) + +def progress(header, frac, width=40): + n = int(width * frac) + sys.stdout.write("\r" + header + "▕" + "▒" * n + " " * (width - n) + "▏") + +def read_flash(port, addr, size): + set_addr(port, addr) + data = bytes() + addr_range = range(addr, addr + size, PAGESIZE) + for a in addr_range: + port.write(CMD.READ_FLASH) + port.write(CMD.READ_BUF) # Should have space for 2 cmds in FIFO. Hoist this one up from read_buf() + progress("Read: ", (a - addr) / size) + check_ack(port) + data += read_buf(port, pipelined=True) + progress("Read: ", 1) + if len(data) > size: + data = data[:size] # ouch + return data + +def write_flash(port, addr, data): + assert(addr % PAGESIZE == 0) + if len(data) % PAGESIZE != 0: + data += bytes(PAGESIZE - len(data) % PAGESIZE) + set_addr(port, addr) + for i in range(len(data) // PAGESIZE): + write_buf(port, data[i * PAGESIZE : (i + 1) * PAGESIZE], check=False) + port.write(CMD.WRITE_FLASH) + progress("Write: ", i / (len(data) // PAGESIZE)) + check_ack(port) + check_ack(port) + progress("Write: ", 1) + +def erase_flash(port, addr, size): + end = addr + size + if end % SECTORSIZE != 0: + end += SECTORSIZE - end % SECTORSIZE + start = addr - addr % SECTORSIZE + set_addr(port, start) + a = start + while a < end: + progress("Erase: ", (a - start) / (end - start)) + if end - a >= BLOCKSIZE and a % BLOCKSIZE == 0: + port.write(CMD.ERASE_BLOCK) + a += BLOCKSIZE + else: + port.write(CMD.ERASE_SECTOR) + a += SECTORSIZE + check_ack(port) + progress("Erase: ", 1) + +def run_2nd_stage(port): + set_addr(port, 0x123456) + port.write(CMD.BOOT_2ND) + check_ack(port) + +def load_mem(port, data): + set_addr(port, 0xb00000 | SRAM_LOAD_ADDR) + port.write(CMD.LOAD_MEM + bytes([ + (len(data) >> 16) & 0xff, + (len(data) >> 8) & 0xff, + (len(data) >> 0) & 0xff + ])) + check_ack(port) + port.write(data) # yup + check_ack(port) + +def exec_mem(port): + set_addr(port, 0xb00000 | SRAM_EXEC_ADDR) + port.write(CMD.EXEC_MEM) + check_ack(port) + +def any_int(x): + return int(x, 0) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("file", nargs="?", help="Filename to read from or dump to.") + parser.add_argument("--uart", "-u", help="Path to UART device (e.g. /dev/ttyUSB0)") + parser.add_argument("--baud", "-b", help="Baud rate for UART. Default 1 Mbaud") + parser.add_argument("--write", "-w", action="store_true", + help="Write to flash (default is read)") + parser.add_argument("--verify", "-v", action="store_true", + help="Verify contents after programming (use with --write)") + parser.add_argument("--run", "-r", action="store_true", + help="Run code from flash after programming") + parser.add_argument("--execute", "-x", action="store_true", + help="Load code directly into SRAM and execute it. Not to be combined with -w,-v,-r") + parser.add_argument("--start", "-s", type=any_int, help="Base address to start read/write") + parser.add_argument("--len", "-l", type=any_int, help="Number of bytes to read, or override file length for write.") + args = parser.parse_args() + + # Do some simple parameter checking to make it harder to accidentally trash your device + if args.write and args.file is None: + sys.exit("Must specify filename for write") + if args.start is None: + args.start = 0 + if args.uart is None: + args.uart = sorted(p.device for p in serial.tools.list_ports.comports())[-1] + if args.baud is None: + args.baud = 1000000 + if args.verify and not args.write: + sys.exit("Verify is only valid for a write operation") + if args.run and not args.write: + sys.exit("Run is only valid for a write operation") + if args.write and (args.start % PAGESIZE != 0): + sys.exit("Writes must be aligned on a {}-byte boundary.".format(PAGESIZE)) + if args.execute and (args.write or args.verify or args.run or args.start or args.len): + sys.exit("--execute is not compatible with flash-related arguments") + if args.write or args.execute: + try: + filesize = os.stat(args.file).st_size + if args.len is None: + args.len = filesize + except: + sys.exit("Could not open file '{}'".format(args.file)) + else: + if args.len is None: + args.len = PAGESIZE + + # Don't want to overwrite their image if they forget -w + if not (args.write or args.execute) and args.file and os.path.exists(args.file): + resp = "" + while not resp in ["y", "n"]: + resp = input("File '{}' exists. Overwrite? (y/n) ".format(args.file)) + resp = resp.lower().strip() + if resp == "n": + sys.exit(0) + + # Summarise what we're about to do + if args.execute: + print("Loading {} bytes to SRAM and running".format(args.len)) + else: + print("{} {} bytes {} {}, starting at address 0x{:06x}".format( + "Writing" + (" and verifying" if args.verify else "") if args.write else "Reading", + args.len, + "from" if args.write else "to", + "stdout" if args.file is None else "file " + args.file, + args.start, + ", and verifying" if args.verify else "" + )) + + # And then do it + # need to allow for page erase, upward of 60 ms. (Uh, actually it seems to be much longer) + port = serial.Serial(args.uart, args.baud, timeout=1) + print("Waiting for bootloader on {}...".format(port.name)) + reset_shell(port) + print("") + + if args.execute: + print("Loading...") + load_mem(port, open(args.file, "rb").read()) + print("Load ok, running...") + exec_mem(port) + elif args.write: + data = open(args.file, "rb").read() + if len(data) > args.len: + data = data[:args.len] + else: + data = data + bytes(args.len - len(data)) # zero padding + + start = time.time() + erase_flash(port, args.start, args.len) + print(" Took {:.1f} s\n".format(time.time() - start)) + start = time.time() + write_flash(port, args.start, data) + print(" Took {:.1f} s\n".format(time.time() - start)) + if args.verify: + start = time.time() + readback = read_flash(port, args.start, args.len) + print(" Took {:.1f} s".format(time.time() - start)) + if readback != data: + sys.exit("Verification failed.") # TODO: better info + if args.run: + print("Launching flash second stage") + run_2nd_stage(port) + print("Done") + else: + start = time.time() + data = read_flash(port, args.start, args.len) + print(" Took {:.1f} s\nDone".format(time.time() - start)) + if args.file: + open(args.file, "wb").write(data) + else: + for i, byte in enumerate(data): + if i % 8 == 0: + sys.stdout.write("{:06x}: ".format(args.start + i)) + sys.stdout.write("{:02x}".format(byte)) + sys.stdout.write("\n" if i % 8 == 7 else " ") + sys.stdout.write("\n") diff --git a/vendor/Hazard3/scripts/uniquetiles b/vendor/Hazard3/scripts/uniquetiles new file mode 100755 index 0000000..0889345 --- /dev/null +++ b/vendor/Hazard3/scripts/uniquetiles @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import argparse +from PIL import Image +from collections import OrderedDict + +__doc__ = """Utility for stripping non-unique tiles from a tileset image. +Outputs the uniquified image, and an index map file from original tile indices +to new tile indices.""" + +parser = argparse.ArgumentParser() +parser.add_argument("input", help="Input file name") +parser.add_argument("output", help="Output file name") +parser.add_argument("--tilesize", "-t", help="Tile size (pixels), default 8", + default="8", choices=["8", "16"]) +parser.epilog = __doc__ +args = parser.parse_args() + +img = Image.open(args.input) +tilesize = int(args.tilesize) + +tile_first_seen = {} +index_mapping = [] +output_images = [] + +src_index = 0 +for y in range(0, img.height - (tilesize - 1), tilesize): + for x in range(0, img.width - (tilesize - 1), tilesize): + tile = img.crop((x, y, x + tilesize, y + tilesize)) + tiledata = tuple(tile.getdata()) + if tiledata in tile_first_seen: + index_mapping.append((src_index, tile_first_seen[tiledata])) + else: + tile_first_seen[tiledata] = len(output_images) + index_mapping.append((src_index, len(output_images))) + output_images.append(tile) + src_index += 1 + +print("Found {} unique tile images".format(len(output_images))) + +oimg = Image.new("RGBA", (tilesize * len(output_images), tilesize)) +for i, tile in enumerate(output_images): + oimg.paste(tile, (i * tilesize, 0)) + +with open(args.output, "wb") as ofile: + oimg.save(ofile) +with open(args.output + ".index_map", "w") as mapfile: + for old, new in index_mapping: + mapfile.write("{}, {}\n".format(old, new)) diff --git a/vendor/Hazard3/scripts/vhexwidth b/vendor/Hazard3/scripts/vhexwidth new file mode 100755 index 0000000..dde2107 --- /dev/null +++ b/vendor/Hazard3/scripts/vhexwidth @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +# Tool for converting width of verilog hex files + +import sys +import argparse +import re + +# Assume little-endian, and that widths are multiples of 4 + +def width_convert(ilines, width, base): + olines = [] + pad_fmt = "{:>0" + str(width // 4) + "}" + accum = "" + for l in ilines: + if l.startswith("@"): + if len(accum): + olines.append(pad_fmt.format(accum)) + accum = "" + # TODO: address scaling wrong if input not byte-sized: + olines.append("@{:x}".format((int(l[1:], 16) - base) // (width // 8))) + continue + for num in re.findall(r"[0-9a-fA-F]+", l): + accum = num + accum + while len(accum) >= width // 4: + olines.append(accum[len(accum) - width // 4 :]) + accum = accum[:len(accum) - width // 4] + if len(accum): + olines.append(pad_fmt.format(accum)) + return olines + +def parseint(arg, name, default): + if arg is None: + arg = default + try: + if type(arg) is str and arg.startswith("0x"): + arg = int(arg, 16) + else: + arg = int(arg) + except ValueError: + sys.exit("Invalid value for {}: {}".format(name, arg)) + return arg + +if __name__ == "__main__": + parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("input", help="Input file name") + parser.add_argument("--width", "-w", help="Output hex width (default 32)") + parser.add_argument("--base", "-b", help="Base address for @ commands (subtracted)") + parser.add_argument("--output", "-o", help="Output file name") + args = parser.parse_args() + if args.output is None: + ofile = sys.stdout + else: + ofile = open(args.output, "w") + if args.input is None: + ifile = sys.stdin + else: + ifile = open(args.input) + width = parseint(args.width, "width", 32) + base = parseint(args.base, "base", 0) + olines = width_convert(ifile, width, base) + for l in olines: + ofile.write(l + "\n") \ No newline at end of file diff --git a/vendor/Hazard3/test/project_paths.mk b/vendor/Hazard3/test/project_paths.mk new file mode 100644 index 0000000..934f996 --- /dev/null +++ b/vendor/Hazard3/test/project_paths.mk @@ -0,0 +1,2 @@ +# Link up to project root +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../project_paths.mk diff --git a/vendor/Hazard3/test/sim/common/hazard3_csr.h b/vendor/Hazard3/test/sim/common/hazard3_csr.h new file mode 100644 index 0000000..413ac4a --- /dev/null +++ b/vendor/Hazard3/test/sim/common/hazard3_csr.h @@ -0,0 +1,66 @@ +#ifndef _HAZARD3_CSR_H +#define _HAZARD3_CSR_H + +#ifndef __ASSEMBLER__ +#include "stdint.h" +#endif + +#define hazard3_csr_dmdata0 0xbff // Debug-mode shadow CSR for DM data transfer + +#define hazard3_csr_meiea 0xbe0 // External interrupt pending array +#define hazard3_csr_meipa 0xbe1 // External interrupt enable array +#define hazard3_csr_meifa 0xbe2 // External interrupt force array +#define hazard3_csr_meipra 0xbe3 // External interrupt priority array +#define hazard3_csr_meinext 0xbe4 // Next external interrupt +#define hazard3_csr_meicontext 0xbe5 // External interrupt context register + +#define hazard3_csr_msleep 0xbf0 // M-mode sleep control register + +#define hazard3_csr_pmpcfgm0 0xbd0 // Non-locking M-mode enables for PMP regions + +#define _read_csr(csrname) ({ \ + uint32_t __csr_tmp_u32; \ + asm volatile ("csrr %0, " #csrname : "=r" (__csr_tmp_u32)); \ + __csr_tmp_u32; \ +}) + +#define _write_csr(csrname, data) ({ \ + asm volatile ("csrw " #csrname ", %0" : : "r" (data)); \ +}) + +#define _set_csr(csrname, data) ({ \ + asm volatile ("csrs " #csrname ", %0" : : "r" (data)); \ +}) + +#define _clear_csr(csrname, data) ({ \ + asm volatile ("csrc " #csrname ", %0" : : "r" (data)); \ +}) + +#define _read_write_csr(csrname, data) ({ \ + uint32_t __csr_tmp_u32; \ + asm volatile ("csrrw %0, " #csrname ", %1" : "=r" (__csr_tmp_u32) : "r" (data)); \ + __csr_tmp_u32; \ +}) + +#define _read_set_csr(csrname, data) ({ \ + uint32_t __csr_tmp_u32; \ + asm volatile ("csrrs %0, " #csrname ", %1" : "=r" (__csr_tmp_u32) : "r" (data)); \ + __csr_tmp_u32; \ +}) + +#define _read_clear_csr(csrname, data) ({ \ + uint32_t __csr_tmp_u32; \ + asm volatile ("csrrc %0, " #csrname ", %1" : "=r" (__csr_tmp_u32) : "r" (data)); \ + __csr_tmp_u32; \ +}) + +// Argument macro expansion layer +#define read_csr(csrname) _read_csr(csrname) +#define write_csr(csrname, data) _write_csr(csrname, data) +#define set_csr(csrname, data) _set_csr(csrname, data) +#define clear_csr(csrname, data) _clear_csr(csrname, data) +#define read_write_csr(csrname, data) _read_write_csr(csrname, data) +#define read_set_csr(csrname, data) _read_set_csr(csrname, data) +#define read_clear_csr(csrname, data) _read_clear_csr(csrname, data) + +#endif diff --git a/vendor/Hazard3/test/sim/common/hazard3_instr.h b/vendor/Hazard3/test/sim/common/hazard3_instr.h new file mode 100644 index 0000000..f360d7e --- /dev/null +++ b/vendor/Hazard3/test/sim/common/hazard3_instr.h @@ -0,0 +1,32 @@ +#ifndef _HAZARD3_INSTR_H +#define _HAZARD3_INSTR_H + +#include + +// C macros for Hazard3 custom instructions + +// nbits must be a constant expression +#define __hazard3_bextm(nbits, rs1, rs2) ({\ + uint32_t __h3_bextm_rd; \ + asm (".insn r 0x0b, 0, %3, %0, %1, %2"\ + : "=r" (__h3_bextm_rd) \ + : "r" (rs1), "r" (rs2), "i" ((((nbits) - 1) & 0x7) << 1)\ + ); \ + __h3_bextm_rd; \ +}) + +// nbits and shamt must be constant expressions +#define __hazard3_bextmi(nbits, rs1, shamt) ({\ + uint32_t __h3_bextmi_rd; \ + asm (".insn i 0x0b, 0x4, %0, %1, %2"\ + : "=r" (__h3_bextmi_rd) \ + : "r" (rs1), "i" ((((nbits) - 1) & 0x7) << 6 | ((shamt) & 0x1f)) \ + ); \ + __h3_bextmi_rd; \ +}) + +#define __hazard3_block() asm ("slt x0, x0, x0" : : : "memory") + +#define __hazard3_unblock() asm ("slt x0, x0, x1" : : : "memory") + +#endif diff --git a/vendor/Hazard3/test/sim/common/hazard3_irq.h b/vendor/Hazard3/test/sim/common/hazard3_irq.h new file mode 100644 index 0000000..51ff599 --- /dev/null +++ b/vendor/Hazard3/test/sim/common/hazard3_irq.h @@ -0,0 +1,98 @@ +#ifndef _HAZARD3_IRQ_H +#define _HAZARD3_IRQ_H + +#include "hazard3_csr.h" +#include "stdint.h" +#include "stdbool.h" + +// Should match processor configuration in testbench: +#define NUM_IRQS 32 +#define MAX_PRIORITY 15 + +// Declarations for irq_dispatch.S +extern uintptr_t _external_irq_table[NUM_IRQS]; +extern uint32_t _external_irq_entry_count; + +#define h3irq_array_read(csr, index) (read_set_csr(csr, (index)) >> 16) + +#define h3irq_array_write(csr, index, data) (write_csr(csr, (index) | ((uint32_t)(data) << 16))) +#define h3irq_array_set(csr, index, data) (set_csr(csr, (index) | ((uint32_t)(data) << 16))) +#define h3irq_array_clear(csr, index, data) (clear_csr(csr, (index) | ((uint32_t)(data) << 16))) + +static inline void h3irq_enable(unsigned int irq, bool enable) { + if (enable) { + h3irq_array_set(hazard3_csr_meiea, irq >> 4, 1u << (irq & 0xfu)); + } + else { + h3irq_array_clear(hazard3_csr_meiea, irq >> 4, 1u << (irq & 0xfu)); + } +} + +static inline bool h3irq_pending(unsigned int irq) { + return h3irq_array_read(hazard3_csr_meipa, irq >> 4) & (1u << (irq & 0xfu)); +} + +static inline void h3irq_force_pending(unsigned int irq, bool force) { + if (force) { + h3irq_array_set(hazard3_csr_meifa, irq >> 4, 1u << (irq & 0xfu)); + } + else { + h3irq_array_clear(hazard3_csr_meifa, irq >> 4, 1u << (irq & 0xfu)); + } +} + +static inline bool h3irq_is_forced(unsigned int irq) { + return h3irq_array_read(hazard3_csr_meifa, irq >> 4) & (1u << (irq & 0xfu)); +} + +// -1 for no IRQ +static inline int h3irq_get_current_irq() { + uint32_t meicontext = read_csr(hazard3_csr_meicontext); + return meicontext & 0x8000u ? -1 : (meicontext >> 4) & 0x1ffu; +} + +static inline void h3irq_set_priority(unsigned int irq, uint32_t priority) { + // Don't want read-modify-write, but no instruction for atomically writing + // a bitfield. So, first drop priority to minimum, then set to the target + // value. It should be safe to drop an IRQ's priority below its current + // even from within that IRQ (but it is never safe to boost an IRQ when + // it may already be in an older stack frame) + h3irq_array_clear(hazard3_csr_meipra, irq >> 2, 0xfu << (4 * (irq & 0x3))); + h3irq_array_set(hazard3_csr_meipra, irq >> 2, (priority & 0xfu) << (4 * (irq & 0x3))); +} + +static inline void h3irq_set_handler(unsigned int irq, void (*handler)(void)) { + _external_irq_table[irq] = (uintptr_t)handler; +} + +static inline void global_irq_enable(bool en) { + // mstatus.mie + if (en) { + set_csr(mstatus, 0x8); + } + else { + clear_csr(mstatus, 0x8); + } +} + +static inline void external_irq_enable(bool en) { + // mie.meie + if (en) { + set_csr(mie, 0x800); + } + else { + clear_csr(mie, 0x800); + } +} + +static inline void timer_irq_enable(bool en) { + // mie.mtie + if (en) { + set_csr(mie, 0x080); + } + else { + clear_csr(mie, 0x080); + } +} + +#endif diff --git a/vendor/Hazard3/test/sim/common/init.S b/vendor/Hazard3/test/sim/common/init.S new file mode 100644 index 0000000..e9aae9b --- /dev/null +++ b/vendor/Hazard3/test/sim/common/init.S @@ -0,0 +1,234 @@ +#include "hazard3_csr.h" + +#define IO_BASE 0xc0000000 +#define IO_PRINT_CHAR (IO_BASE + 0x0) +#define IO_PRINT_U32 (IO_BASE + 0x4) +#define IO_EXIT (IO_BASE + 0x8) + +// Provide trap vector table, reset handler and weak default trap handlers for +// Hazard3. This is not a crt0: the reset handler calls an external _start + +.option push +.option norelax +.file 1 "vendor/Hazard3/test/sim/common/init.S" +.section .vectors,"ax",@progbits + +.macro VEC name:req +.p2align 2 +j \name +.p2align 2 +.endm + +// ---------------------------------------------------------------------------- +// Vector table (must be at least aligned to its size rounded up to power of 2) + +.p2align 12 +.vector_table: + +// Single exception vector, also takes IRQs if vectoring is disabled + + VEC handle_exception + +// Standard interrupts, if vectoring is enabled +// Note: global EIRQ does not fire. Instead we have 16 separate vectors + + // handle_exception ^^^ takes the slot where U-mode softirq would be + VEC .halt + VEC .halt + VEC isr_machine_softirq + VEC .halt + VEC .halt + VEC .halt + VEC isr_machine_timer + VEC .halt + VEC .halt + VEC .halt + VEC isr_external_irq + VEC .halt + VEC .halt + VEC .halt + VEC .halt + +// ---------------------------------------------------------------------------- +// Reset handler + + +.reset_handler: + // Set counters running, as they are off by default. This may trap if counters + // are unimplemented, so catch the trap and continue. + .loc 1 59 0 ; la a0, 1f + .loc 1 60 0 ; csrw mtvec, a0 + .loc 1 61 0 ; csrci mcountinhibit, 0x5 + .loc 1 62 0 ; j 2f +.p2align 2 +1: + .loc 1 65 0 ; csrw mcause, zero +2: + + // Set up trap vector table. mtvec LSB enables vectoring + .loc 1 69 0 ; la a0, .vector_table + 1 + .loc 1 70 0 ; csrw mtvec, a0 + + // Ensure gp is initialised on all cores -- don't wait for newlib _start + // as that is core-0-only +.option push +.option norelax + .loc 1 76 0 ; la gp, __global_pointer$ +.option pop + + // Put spare cores to sleep before setting up core 0 stack + // Note csrr is a NOP when there are no CSRs at all (no traps!): + .loc 1 81 0 ; li a0, 0 + .loc 1 82 0 ; csrr a0, mhartid + .loc 1 83 0 ; bnez a0, .core1_wait + + // Set up stack pointer before doing anything else + .loc 1 86 0 ; la sp, __stack_top + + // newlib _start expects argc, argv on the stack. Leave stack 16-byte aligned. + .loc 1 89 0 ; addi sp, sp, -16 + .loc 1 90 0 ; li a0, 1 + .loc 1 91 0 ; sw a0, (sp) + .loc 1 92 0 ; la a0, progname + .loc 1 93 0 ; sw a0, 4(sp) + + .loc 1 95 0 ; jal _start + .loc 1 96 0 ; j .halt + +.core1_wait: + // IRQs disabled, but soft IRQ unmasked -> soft IRQ will exit WFI. + csrci mstatus, 0x8 + csrw mie, 0x8 +.core1_wait_loop: + wfi + la a0, core1_entry_vector + lw a0, (a0) + beqz a0, .core1_wait_loop + la sp, __stack_top - 0x10000 + jalr a0 +.core1_finish: + wfi + j .core1_finish + +.p2align 2 +.global core1_entry_vector +core1_entry_vector: + .word 0 + +.global _exit +_exit: + li a1, IO_EXIT + sw a0, (a1) + +.global _sbrk +_sbrk: + la a1, heap_ptr + lw a2, (a1) + add a0, a0, a2 + sw a0, (a1) + mv a0, a2 + ret + +.p2align 2 +heap_ptr: + .word _end + +.global .halt +.halt: + j .halt + +progname: + .asciz "hazard3-testbench" + +.p2align 2 + +// ---------------------------------------------------------------------------- +// Weak handler/ISR symbols + +// Routine to print out trap name, trap address, and some core registers +// (x8..x15, ra, sp). The default handlers are all patched into this routine, +// so the CPU will print some basic diagnostics on any unhandled trap +// (assuming the processor is not internally completely broken) + +// argument in t0, IO pointer in t1, return in tp, trashes t0 and t2; +_tb_puts: +1: + lbu t2, (t0) + addi t0, t0, 1 + beqz t2, 2f + sw t2, IO_PRINT_CHAR - IO_BASE(t1) + j 1b +2: + jr tp + +.macro print_reg str reg + la t0, \str + jal tp, _tb_puts + sw \reg, IO_PRINT_U32 - IO_BASE(t1) +.endm + +_weak_handler_name_in_gp: + la t0, _str_unhandled_trap + li t1, IO_BASE + jal tp, _tb_puts + mv t0, gp + jal tp, _tb_puts + la t0, _str_at_mepc + jal tp, _tb_puts + csrr t0, mepc + sw t0, IO_PRINT_U32 - IO_BASE(t1) + csrr gp, mcause + bltz gp, 1f + print_reg _str_mcause gp +1: + print_reg _str_s0 s0 + print_reg _str_s1 s1 + print_reg _str_a0 a0 + print_reg _str_a1 a1 + print_reg _str_a2 a2 + print_reg _str_a3 a3 + print_reg _str_a4 a4 + print_reg _str_a5 a5 + print_reg _str_ra ra + print_reg _str_sp sp + li t2, -1 + sw t2, IO_EXIT - IO_BASE(t1) + // Should be unreachable: + j .halt + +_str_unhandled_trap: .asciz "*** Unhandled trap ***\n" +_str_at_mepc: .asciz " @ mepc = " +_str_mcause: .asciz " mcause = " +_str_s0: .asciz "s0: " +_str_s1: .asciz "s1: " +_str_a0: .asciz "a0: " +_str_a1: .asciz "a1: " +_str_a2: .asciz "a2: " +_str_a3: .asciz "a3: " +_str_a4: .asciz "a4: " +_str_a5: .asciz "a5: " +_str_ra: .asciz "ra: " +_str_sp: .asciz "sp: " +.p2align 2 + +// Provide a default weak handler for each trap, which calls into the above +// diagnostic routine with the trap name (a null-terminated string) in gp + +.macro weak_handler name:req +.p2align 2 +.global \name +.weak \name +\name: + la gp, _str_\name + j _weak_handler_name_in_gp +_str_\name: + .asciz "\name" +.endm + +weak_handler handle_exception +weak_handler isr_machine_softirq +weak_handler isr_machine_timer +weak_handler isr_external_irq + +// You can relax now +.option pop diff --git a/vendor/Hazard3/test/sim/common/irq_dispatch.S b/vendor/Hazard3/test/sim/common/irq_dispatch.S new file mode 100644 index 0000000..518fe05 --- /dev/null +++ b/vendor/Hazard3/test/sim/common/irq_dispatch.S @@ -0,0 +1,135 @@ +#include "hazard3_csr.h" + +.global isr_external_irq +isr_external_irq: + // Save caller saves and exception return state whilst IRQs are disabled. + // We can't be pre-empted during this time, but if a higher-priority IRQ + // arrives ("late arrival"), that will be the one displayed in meinext. + addi sp, sp, -80 + sw ra, 0(sp) + sw t0, 4(sp) + sw t1, 8(sp) + sw t2, 12(sp) + sw a0, 16(sp) + sw a1, 20(sp) + sw a2, 24(sp) + sw a3, 28(sp) + sw a4, 32(sp) + sw a5, 36(sp) +#if __riscv_i + sw a6, 40(sp) + sw a7, 44(sp) + sw t3, 48(sp) + sw t4, 52(sp) + sw t5, 56(sp) + sw t6, 60(sp) +#endif + + // Update a count of the number of external IRQ vector entries (just for + // use in tests) + la a0, _external_irq_entry_count + lw a1, (a0) + addi a1, a1, 1 + sw a1, (a0) + // Make sure to delete the above ^^^ if you use this code for real! + + csrr a0, mepc + sw a0, 64(sp) + // Make sure to set meicontext.clearts to clear and save mie.msie/mtie + // when saving context. + csrrsi a0, hazard3_csr_meicontext, 0x2 + sw a0, 68(sp) + csrr a0, mstatus + sw a0, 72(sp) + + j get_next_irq + +dispatch_irq: + // Preemption priority was configured by meinext update, so enable preemption: + csrsi mstatus, 0x8 + // meinext is pre-shifted by 2, so only an add is required to index table + la a1, _external_irq_table + add a1, a1, a0 + lw a1, (a1) + jalr ra, a1 + + // Disable IRQs on returning so we can sample the next IRQ + csrci mstatus, 0x8 + +get_next_irq: + // Sample the current highest-priority active IRQ (left-shifted by 2) from + // meinext, and write 1 to the LSB to tell hardware to tell hw to update + // meicontext with the preemption priority (and IRQ number) of this IRQ + csrrsi a0, hazard3_csr_meinext, 0x1 + // MSB will be set if there is no active IRQ at the current priority level + bgez a0, dispatch_irq + +no_more_irqs: + // Restore saved context and return from IRQ + lw a0, 64(sp) + csrw mepc, a0 + lw a0, 68(sp) + csrw hazard3_csr_meicontext, a0 + lw a0, 72(sp) + csrw mstatus, a0 + + lw ra, 0(sp) + lw t0, 4(sp) + lw t1, 8(sp) + lw t2, 12(sp) + lw a0, 16(sp) + lw a1, 20(sp) + lw a2, 24(sp) + lw a3, 28(sp) + lw a4, 32(sp) + lw a5, 36(sp) +#if __riscv_i + lw a6, 40(sp) + lw a7, 44(sp) + lw t3, 48(sp) + lw t4, 52(sp) + lw t5, 56(sp) + lw t6, 60(sp) +#endif + addi sp, sp, 80 + mret + +// ------------------------------------------------------------ +// Handler table and default handler symbols + +// Provide weak symbol for all IRQs, pointing to a breakpoint instruction: + +.macro decl_eirq num +.weak isr_irq\num +isr_irq\num: +.endm + +.macro ref_eirq num +.word isr_irq\num +.endm + +#define NUM_IRQS 32 + +.equ i, 0 +.rept NUM_IRQS +decl_eirq i +.equ i, i + 1 +.endr + ebreak + +// Soft vector table is preloaded to RAM, and by default contains the weak ISR +// symbols, but can also be patched at runtime: + +.section .data +.global _external_irq_table +_external_irq_table: + +.equ i, 0 +.rept NUM_IRQS +ref_eirq i +.equ i, i + 1 +.endr + +.global _external_irq_entry_count +_external_irq_entry_count: +.word 0 diff --git a/vendor/Hazard3/test/sim/common/link_hazard3.ld b/vendor/Hazard3/test/sim/common/link_hazard3.ld new file mode 100644 index 0000000..e784517 --- /dev/null +++ b/vendor/Hazard3/test/sim/common/link_hazard3.ld @@ -0,0 +1,48 @@ +OUTPUT_FORMAT("elf32-littleriscv") +OUTPUT_ARCH(riscv) +ENTRY(_start) + +SECTIONS +{ + /* Hazard3 testbench RAM window */ + . = 0x80000000; + PROVIDE(__stack_top = 0x80100000); + + /* Reset/trap vectors are in Hazard3 init.S (.vectors). Keep them first so + .reset_handler lands at 0x80000040 (Hazard3 RESET_VECTOR). */ + .text : ALIGN(4) + { + KEEP(*(.vectors)) + *(.text .text.*) + } + + /* Keep constants non-executable. Merging arbitrary-length strings into + .text makes some RISC-V objdump versions try to decode the final partial + instruction and abort instead of producing the teaching listing. */ + .rodata : ALIGN(4) + { + *(.rodata .rodata.*) + } + + .data : ALIGN(4) + { + __data_start = .; + *(.data .data.*) + *(.sdata .sdata.*) + __data_end = .; + } + + .bss : ALIGN(4) + { + __bss_start = .; + *(.bss .bss.* COMMON) + *(.sbss .sbss.*) + __bss_end = .; + } + + /* Conservative default (good enough for this ASM-only demo). */ + __global_pointer$ = __data_start + 0x800; + + _end = .; + PROVIDE(end = .); +} diff --git a/vendor/Hazard3/test/sim/common/memmap.ld b/vendor/Hazard3/test/sim/common/memmap.ld new file mode 100644 index 0000000..716c952 --- /dev/null +++ b/vendor/Hazard3/test/sim/common/memmap.ld @@ -0,0 +1,275 @@ +/* Script for -z combreloc */ +/* Copyright (C) 2014-2025 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv") +OUTPUT_ARCH(riscv) +ENTRY(_start) +SEARCH_DIR("/opt/riscv/gcc15/riscv32-unknown-elf/lib"); +SECTIONS +{ + . = 0x80000000; + PROVIDE(__stack_top = 0x80100000); + /* Place the build-id as close to the ELF headers as possible. This + maximises the chance the build-id will be present in core files, + which GDB can then use to locate the associated debuginfo file. */ + .interp : { *(.interp) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) + *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) + *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) + *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + *(.rela.ifunc) + } + .rela.plt : + { + *(.rela.plt) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + /* Start of the executable code region. */ + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) *(.iplt) } + .text : + { + KEEP(*(.vectors)) + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(SORT(.text.sorted.*)) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + /* Start of the Read Only Data region. */ + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .sdata2 : + { + *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) + } + .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .sframe : ONLY_IF_RO { *(.sframe) *(.sframe.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Various note sections. Placed here so that they are always included + in the read-only segment and not treated as orphan sections. The + current orphan handling algorithm does place note sections after R/O + data, but this is not guaranteed to always be the case. */ + .note.build-id : { *(.note.build-id) } + .note.GNU-stack : { *(.note.GNU-stack) } + .note.gnu-property : { *(.note.gnu-property) } + .note.ABI-tag : { *(.note.ABI-tag) } + .note.package : { *(.note.package) } + .note.dlopen : { *(.note.dlopen) } + .note.netbsd.ident : { *(.note.netbsd.ident) } + .note.openbsd.ident : { *(.note.openbsd.ident) } + /* Start of the Read Write Data region. */ + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling. */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .sframe : ONLY_IF_RW { *(.sframe) *(.sframe.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } + /* Thread Local Storage sections. */ + .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got) *(.igot) } + . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 8 ? 8 : 0, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .data : + { + __DATA_BEGIN__ = .; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : + { + __SDATA_BEGIN__ = .; + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + _edata = .; + PROVIDE (edata = .); + . = ALIGN(ALIGNOF(NEXT_SECTION)); + __bss_start = .; + .sbss : + { + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + } + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that in the common case of there only being one + type of .bss section, the section occupies space up to _end. + Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 32 / 8 : 1); + } + . = ALIGN(32 / 8); + /* Start of the Large Data region. */ + . = SEGMENT_START("ldata-segment", .); + . = ALIGN(32 / 8); + __BSS_END__ = .; + __global_pointer$ = MIN(__SDATA_BEGIN__ + 0x800, + MAX(__DATA_BEGIN__ + 0x800, __BSS_END__ - 0x800)); + _end = .; + PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Start of the Tiny Data region. */ + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 (INFO) : { *(.comment); LINKER_VERSION; } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1. */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions. */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2. */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2. */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions. */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3. */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF 5. */ + .debug_addr 0 : { *(.debug_addr) } + .debug_line_str 0 : { *(.debug_line_str) } + .debug_loclists 0 : { *(.debug_loclists) } + .debug_macro 0 : { *(.debug_macro) } + .debug_names 0 : { *(.debug_names) } + .debug_rnglists 0 : { *(.debug_rnglists) } + .debug_str_offsets 0 : { *(.debug_str_offsets) } + .debug_sup 0 : { *(.debug_sup) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.gnu_object_only) } +} + + diff --git a/vendor/Hazard3/test/sim/common/multilib-gen-gen.py b/vendor/Hazard3/test/sim/common/multilib-gen-gen.py new file mode 100755 index 0000000..28dc2ef --- /dev/null +++ b/vendor/Hazard3/test/sim/common/multilib-gen-gen.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 + +# Generate a multilib configure line for riscv-gnu-toolchain with useful +# combinations of extensions supported by both Hazard3 and mainline GCC +# (currently GCC 14). Use as: +# ./configure ... --with-multilib-generator="$(path/to/multilib-gen-gen.py)" + +base = "rv32i" +abi = "-ilp32--" +options = [ + "m", + "a", + "c", + "zba", + "zbb", + "zbc", + "zbs", + "zbkb", + "zbkx", + "zca", + "zcb", + "zcmp", + "zmmul" +] + +# Do not build for LHS except when *all of* RHS is also present. This cuts +# down on the number of configurations. A leading "!" means antidependency, +# i.e. an incompatibility. +depends_on = { + "m": ["!zmmul" ], + "zmmul": ["!m" ], + "zbb": ["m", "zba", "zbs" ], + "zba": ["m", "zbb", "zbs" ], + "zbs": ["m", "zba", "zbb" ], + "zbkb": ["zbb" ], + "zbc": ["zba", "zbb", "zbs", "zbkb"], + "zbkx": ["zba", "zbb", "zbs", "zbkb"], + "zifencei": ["zicsr" ], + "c": ["!zca" ], + "zca": ["!c" ], + "zcb": ["zca" ], + "zcmp": ["zca", "zcb", ], +} + +l = [] +for i in range(2 ** len(options)): + isa = base + violates_dependencies = False + for j in (j for j in range(len(options)) if i & (1 << j)): + opt = options[j] + if opt in depends_on: + for dep in depends_on[opt]: + inverted_dep = dep.startswith("!") + if inverted_dep: dep = dep[1:] + if inverted_dep == bool(i & (1 << options.index(dep))): + violates_dependencies = True + break + if violates_dependencies: + break + if len(opt) > 1: + isa += "_" + isa += opt + isa += "_zicsr_zifencei" + if not violates_dependencies: + l.append(isa + abi) + +# Bonus RV32E configs: +l.append("rv32e_zicsr_zifencei-ilp32e--") +l.append("rv32ema_zicsr_zifencei-ilp32e--") +l.append("rv32emac_zicsr_zifencei-ilp32e--") +l.append("rv32ema_zicsr_zifencei_zba_zbb_zbc_zbkb_zbkx_zbs_zca_zcb_zcmp-ilp32e--") + +print(";".join(l)) + +print(len(l)) diff --git a/vendor/Hazard3/test/sim/common/src_only_app.mk b/vendor/Hazard3/test/sim/common/src_only_app.mk new file mode 100644 index 0000000..6507a68 --- /dev/null +++ b/vendor/Hazard3/test/sim/common/src_only_app.mk @@ -0,0 +1,55 @@ +ifndef SRCS +$(error Must define list of test sources as SRCS) +endif + +ifndef APP +$(error Must define application name as APP) +endif + +DOTF ?= tb.f +CCFLAGS ?= +LDSCRIPT ?= ../common/memmap.ld +CROSS_PREFIX ?= riscv32-unknown-elf- +TBEXEC ?= ../tb_cxxrtl/tb +TBDIR := $(dir $(abspath $(TBEXEC))) +INCDIR ?= ../common +MAX_CYCLES ?= 100000 +TMP_PREFIX ?= tmp/ + +# Useless: +override CCFLAGS += -Wl,--no-warn-rwx-segments + +############################################################################### + +.SUFFIXES: +.PHONY: all run view tb clean clean_tb + +all: run + +run: $(TMP_PREFIX)$(APP).bin + $(TBEXEC) --bin $(TMP_PREFIX)$(APP).bin --vcd $(TMP_PREFIX)$(APP)_run.vcd --cycles $(MAX_CYCLES) + +view: run + gtkwave $(TMP_PREFIX)$(APP)_run.vcd + +bin: $(TMP_PREFIX)$(APP).bin + +tb: + $(MAKE) -C $(TBDIR) DOTF=$(DOTF) + +clean: + rm -rf $(TMP_PREFIX) + +clean_tb: clean + $(MAKE) -C $(TBDIR) clean + +############################################################################### + +$(TMP_PREFIX)$(APP).bin: $(TMP_PREFIX)$(APP).elf + $(CROSS_PREFIX)objcopy -O binary $^ $@ + $(CROSS_PREFIX)objdump -h $^ > $(TMP_PREFIX)$(APP).dis + $(CROSS_PREFIX)objdump -d $^ >> $(TMP_PREFIX)$(APP).dis + +$(TMP_PREFIX)$(APP).elf: $(SRCS) $(wildcard %.h) + mkdir -p $(TMP_PREFIX) + $(CROSS_PREFIX)gcc $(CCFLAGS) $(SRCS) -T $(LDSCRIPT) $(addprefix -I,$(INCDIR)) -o $@ diff --git a/vendor/Hazard3/test/sim/common/tb_cxxrtl_io.h b/vendor/Hazard3/test/sim/common/tb_cxxrtl_io.h new file mode 100644 index 0000000..d464aca --- /dev/null +++ b/vendor/Hazard3/test/sim/common/tb_cxxrtl_io.h @@ -0,0 +1,117 @@ +#ifndef _TB_CXXRTL_IO_H +#define _TB_CXXRTL_IO_H + +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +// Testbench IO hardware layout + +#define IO_BASE 0xc0000000 + +typedef struct { + volatile uint32_t print_char; + volatile uint32_t print_u32; + volatile uint32_t exit; + uint32_t _pad0; + volatile uint32_t set_softirq; + volatile uint32_t clr_softirq; + volatile uint32_t globmon_en; + volatile uint32_t poison_addr; + volatile uint32_t set_irq; + uint32_t _pad2[3]; + volatile uint32_t clr_irq; + uint32_t _pad3[3]; +} io_hw_t; + +#define mm_io ((io_hw_t *const)IO_BASE) + +typedef struct { + volatile uint32_t mtime; + volatile uint32_t mtimeh; + volatile uint32_t mtimecmp; + volatile uint32_t mtimecmph; +} timer_hw_t; + +#define mm_timer ((timer_hw_t *const)(IO_BASE + 0x100)) + +// ---------------------------------------------------------------------------- +// Testbench IO convenience functions + +static inline void tb_putc(char c) { + mm_io->print_char = (uint32_t)c; +} + +static inline void tb_puts(const char *s) { + while (*s) + tb_putc(*s++); +} + +static inline void tb_put_u32(uint32_t x) { + mm_io->print_u32 = x; +} + +static inline void tb_exit(uint32_t ret) { + mm_io->exit = ret; +} + +#ifndef PRINTF_BUF_SIZE +#define PRINTF_BUF_SIZE 256 +#endif + +static inline void tb_printf(const char *fmt, ...) { + char buf[PRINTF_BUF_SIZE]; + va_list args; + va_start(args, fmt); + vsnprintf(buf, PRINTF_BUF_SIZE, fmt, args); + tb_puts(buf); + va_end(args); +} + +#define tb_assert(cond, ...) if (!(cond)) {tb_printf(__VA_ARGS__); tb_exit(-1);} + +static inline void tb_set_softirq(int idx) { + mm_io->set_softirq = 1u << idx; +} + +static inline void tb_clr_softirq(int idx) { + mm_io->clr_softirq = 1u << idx; +} + +static inline bool tb_get_softirq(int idx) { + return (bool)(mm_io->set_softirq & (1u << idx)); +} + +static inline void tb_enable_global_monitor(bool en) { + mm_io->globmon_en = en; +} + +// Set an address to generate faults on any access +static inline void tb_set_poison_addr(uint32_t addr) { + asm volatile ("fence" : : : "memory"); + mm_io->poison_addr = addr; + asm volatile ("fence" : : : "memory"); +} + +static inline void tb_set_irq_masked(uint32_t mask) { + mm_io->set_irq = mask; +} + +static inline void tb_clr_irq_masked(uint32_t mask) { + mm_io->clr_irq = mask; +} + +static inline uint32_t tb_get_irq_mask() { + return mm_io->set_irq; +} + +extern volatile uintptr_t core1_entry_vector; + +static inline void tb_launch_core1(void (*entry)(void)) { + core1_entry_vector = (uintptr_t)entry; + tb_set_softirq(1); +} + +#endif diff --git a/vendor/Hazard3/test/sim/common/tb_uart.hpp b/vendor/Hazard3/test/sim/common/tb_uart.hpp new file mode 100644 index 0000000..b028d6a --- /dev/null +++ b/vendor/Hazard3/test/sim/common/tb_uart.hpp @@ -0,0 +1,34 @@ +#ifndef _TB_UART_HPP +#define _TB_UART_HPP + +#include + +extern "C" { +#include "tb_uart_io.h" +} + +class TbUart { +public: + explicit constexpr TbUart(unsigned index) : index_(index) {} + + void write(char c) const { tb_uart_write(index_, static_cast(c)); } + + void write(const char *str) const { + while (*str) + write(*str++); + } + + bool connected() const { return tb_uart_connected(index_); } + bool canRead() const { return tb_uart_can_read(index_); } + + // Returns [0..255] on success, or -1 if RX FIFO is empty. + int tryRead() const { return tb_uart_try_read(index_); } + + void clearOverrun() const { tb_uart_clear_overrun(index_); } + +private: + unsigned index_; +}; + +#endif + diff --git a/vendor/Hazard3/test/sim/common/tb_uart_io.h b/vendor/Hazard3/test/sim/common/tb_uart_io.h new file mode 100644 index 0000000..bc4829b --- /dev/null +++ b/vendor/Hazard3/test/sim/common/tb_uart_io.h @@ -0,0 +1,61 @@ +#ifndef _TB_UART_IO_H +#define _TB_UART_IO_H + +#include +#include + +// Testbench UART-over-TCP MMIO (see tb_common/include/tb_constants.h). +// +// Each UART is exposed as a raw TCP byte stream (one client at a time). +// Software should poll STATUS.RX_AVAIL before reading DATA. + +#ifndef TB_IO_BASE +#define TB_IO_BASE 0xC0000000u +#endif + +#define TB_UART_BASE 0x200u +#define TB_UART_STRIDE 0x20u + +#define TB_UART_DATA 0x00u +#define TB_UART_STATUS 0x04u +#define TB_UART_CTRL 0x08u + +#define TB_UART_STATUS_RX_AVAIL (1u << 0) +#define TB_UART_STATUS_TX_READY (1u << 1) +#define TB_UART_STATUS_CONNECTED (1u << 2) +#define TB_UART_STATUS_OVERRUN (1u << 3) + +#define TB_UART_CTRL_CLR_OVERRUN (1u << 0) + +static inline volatile uint32_t *tb_uart_reg(unsigned uart_idx, unsigned reg_off) { + return (volatile uint32_t *)(TB_IO_BASE + TB_UART_BASE + (uart_idx * TB_UART_STRIDE) + reg_off); +} + +static inline uint32_t tb_uart_status(unsigned uart_idx) { + return *tb_uart_reg(uart_idx, TB_UART_STATUS); +} + +static inline bool tb_uart_connected(unsigned uart_idx) { + return (tb_uart_status(uart_idx) & TB_UART_STATUS_CONNECTED) != 0; +} + +static inline bool tb_uart_can_read(unsigned uart_idx) { + return (tb_uart_status(uart_idx) & TB_UART_STATUS_RX_AVAIL) != 0; +} + +static inline int tb_uart_try_read(unsigned uart_idx) { + if (!tb_uart_can_read(uart_idx)) + return -1; + return (int)(*tb_uart_reg(uart_idx, TB_UART_DATA) & 0xffu); +} + +static inline void tb_uart_write(unsigned uart_idx, uint8_t byte) { + *tb_uart_reg(uart_idx, TB_UART_DATA) = (uint32_t)byte; +} + +static inline void tb_uart_clear_overrun(unsigned uart_idx) { + *tb_uart_reg(uart_idx, TB_UART_CTRL) = TB_UART_CTRL_CLR_OVERRUN; +} + +#endif + diff --git a/vendor/Hazard3/test/sim/project_paths.mk b/vendor/Hazard3/test/sim/project_paths.mk new file mode 100644 index 0000000..934f996 --- /dev/null +++ b/vendor/Hazard3/test/sim/project_paths.mk @@ -0,0 +1,2 @@ +# Link up to project root +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../project_paths.mk diff --git a/vendor/Hazard3/test/sim/tb_common/hdl/config_default.vh b/vendor/Hazard3/test/sim/tb_common/hdl/config_default.vh new file mode 100644 index 0000000..cbffe48 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/hdl/config_default.vh @@ -0,0 +1,50 @@ +// Default Hazard3 config for testbench: all ISA features + +localparam RESET_VECTOR = 32'h80000040; +localparam MTVEC_INIT = 32'h80000000; +localparam EXTENSION_A = 1; +localparam EXTENSION_C = 1; +localparam EXTENSION_E = 0; +localparam EXTENSION_M = 1; +localparam EXTENSION_ZBA = 1; +localparam EXTENSION_ZBB = 1; +localparam EXTENSION_ZBC = 1; +localparam EXTENSION_ZBKB = 1; +localparam EXTENSION_ZBKX = 1; +localparam EXTENSION_ZBS = 1; +localparam EXTENSION_ZCB = 1; +localparam EXTENSION_ZCLSD = 1; +localparam EXTENSION_ZCMP = 1; +localparam EXTENSION_ZIFENCEI = 1; +localparam EXTENSION_ZILSD = 1; +localparam EXTENSION_XH3BEXTM = 1; +localparam EXTENSION_XH3IRQ = 1; +localparam EXTENSION_XH3PMPM = 1; +localparam EXTENSION_XH3POWER = 1; +localparam CSR_M_MANDATORY = 1; +localparam CSR_M_TRAP = 1; +localparam CSR_COUNTER = 1; +localparam U_MODE = 1; +localparam PMP_REGIONS = 4; +localparam PMP_GRAIN = 0; +localparam PMP_MATCH_NAPOT = 1; +localparam PMP_MATCH_TOR = 1; +localparam PMP_HARDWIRED = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){1'b0}}; +localparam PMP_HARDWIRED_ADDR = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){32'h0}}; +localparam PMP_HARDWIRED_CFG = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){8'h00}}; +localparam DEBUG_SUPPORT = 1; +localparam BREAKPOINT_TRIGGERS = 4; +localparam NUM_IRQS = 32; +localparam IRQ_PRIORITY_BITS = 4; +localparam IRQ_INPUT_BYPASS = {NUM_IRQS{1'b0}}; +localparam MVENDORID_VAL = 32'hdeadbeef; +localparam MCONFIGPTR_VAL = 32'h9abcdef0; +localparam REDUCED_BYPASS = 0; +localparam MULDIV_UNROLL = 2; +localparam MUL_FAST = 1; +localparam MUL_FASTER = 1; +localparam MULH_FAST = 1; +localparam FAST_BRANCHCMP = 1; +localparam RESET_REGFILE = 1; +localparam BRANCH_PREDICTOR = 1; +localparam MTVEC_WMASK = 32'hfffffffd; diff --git a/vendor/Hazard3/test/sim/tb_common/hdl/config_default_rve.vh b/vendor/Hazard3/test/sim/tb_common/hdl/config_default_rve.vh new file mode 100644 index 0000000..1e92250 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/hdl/config_default_rve.vh @@ -0,0 +1,50 @@ +// All ISA features (but RVE) + +localparam RESET_VECTOR = 32'h80000040; +localparam MTVEC_INIT = 32'h80000000; +localparam EXTENSION_A = 1; +localparam EXTENSION_C = 1; +localparam EXTENSION_E = 1; +localparam EXTENSION_M = 1; +localparam EXTENSION_ZBA = 1; +localparam EXTENSION_ZBB = 1; +localparam EXTENSION_ZBC = 1; +localparam EXTENSION_ZBKB = 1; +localparam EXTENSION_ZBKX = 1; +localparam EXTENSION_ZBS = 1; +localparam EXTENSION_ZCB = 1; +localparam EXTENSION_ZCLSD = 1; +localparam EXTENSION_ZCMP = 1; +localparam EXTENSION_ZIFENCEI = 1; +localparam EXTENSION_ZILSD = 1; +localparam EXTENSION_XH3BEXTM = 1; +localparam EXTENSION_XH3IRQ = 1; +localparam EXTENSION_XH3PMPM = 1; +localparam EXTENSION_XH3POWER = 1; +localparam CSR_M_MANDATORY = 1; +localparam CSR_M_TRAP = 1; +localparam CSR_COUNTER = 1; +localparam U_MODE = 1; +localparam PMP_REGIONS = 4; +localparam PMP_GRAIN = 0; +localparam PMP_MATCH_NAPOT = 1; +localparam PMP_MATCH_TOR = 1; +localparam PMP_HARDWIRED = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){1'b0}}; +localparam PMP_HARDWIRED_ADDR = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){32'h0}}; +localparam PMP_HARDWIRED_CFG = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){8'h00}}; +localparam DEBUG_SUPPORT = 1; +localparam BREAKPOINT_TRIGGERS = 4; +localparam NUM_IRQS = 32; +localparam IRQ_PRIORITY_BITS = 4; +localparam IRQ_INPUT_BYPASS = {NUM_IRQS{1'b0}}; +localparam MVENDORID_VAL = 32'hdeadbeef; +localparam MCONFIGPTR_VAL = 32'h9abcdef0; +localparam REDUCED_BYPASS = 0; +localparam MULDIV_UNROLL = 2; +localparam MUL_FAST = 1; +localparam MUL_FASTER = 1; +localparam MULH_FAST = 1; +localparam FAST_BRANCHCMP = 1; +localparam RESET_REGFILE = 1; +localparam BRANCH_PREDICTOR = 1; +localparam MTVEC_WMASK = 32'hfffffffd; diff --git a/vendor/Hazard3/test/sim/tb_common/hdl/config_late_mul.vh b/vendor/Hazard3/test/sim/tb_common/hdl/config_late_mul.vh new file mode 100644 index 0000000..1f37ecc --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/hdl/config_late_mul.vh @@ -0,0 +1,50 @@ +// Default Hazard3 config for testbench: all ISA features + +localparam RESET_VECTOR = 32'h80000040; +localparam MTVEC_INIT = 32'h80000000; +localparam EXTENSION_A = 1; +localparam EXTENSION_C = 1; +localparam EXTENSION_E = 0; +localparam EXTENSION_M = 1; +localparam EXTENSION_ZBA = 1; +localparam EXTENSION_ZBB = 1; +localparam EXTENSION_ZBC = 1; +localparam EXTENSION_ZBKB = 1; +localparam EXTENSION_ZBKX = 1; +localparam EXTENSION_ZBS = 1; +localparam EXTENSION_ZCB = 1; +localparam EXTENSION_ZCLSD = 1; +localparam EXTENSION_ZCMP = 1; +localparam EXTENSION_ZIFENCEI = 1; +localparam EXTENSION_ZILSD = 1; +localparam EXTENSION_XH3BEXTM = 1; +localparam EXTENSION_XH3IRQ = 1; +localparam EXTENSION_XH3PMPM = 1; +localparam EXTENSION_XH3POWER = 1; +localparam CSR_M_MANDATORY = 1; +localparam CSR_M_TRAP = 1; +localparam CSR_COUNTER = 1; +localparam U_MODE = 1; +localparam PMP_REGIONS = 4; +localparam PMP_GRAIN = 0; +localparam PMP_MATCH_NAPOT = 1; +localparam PMP_MATCH_TOR = 1; +localparam PMP_HARDWIRED = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){1'b0}}; +localparam PMP_HARDWIRED_ADDR = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){32'h0}}; +localparam PMP_HARDWIRED_CFG = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){8'h00}}; +localparam DEBUG_SUPPORT = 1; +localparam BREAKPOINT_TRIGGERS = 4; +localparam NUM_IRQS = 32; +localparam IRQ_PRIORITY_BITS = 4; +localparam IRQ_INPUT_BYPASS = {NUM_IRQS{1'b0}}; +localparam MVENDORID_VAL = 32'hdeadbeef; +localparam MCONFIGPTR_VAL = 32'h9abcdef0; +localparam REDUCED_BYPASS = 0; +localparam MULDIV_UNROLL = 1; +localparam MUL_FAST = 1; +localparam MUL_FASTER = 0; +localparam MULH_FAST = 0; +localparam FAST_BRANCHCMP = 1; +localparam RESET_REGFILE = 1; +localparam BRANCH_PREDICTOR = 1; +localparam MTVEC_WMASK = 32'hfffffffd; diff --git a/vendor/Hazard3/test/sim/tb_common/hdl/config_min.vh b/vendor/Hazard3/test/sim/tb_common/hdl/config_min.vh new file mode 100644 index 0000000..c3cfa7b --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/hdl/config_min.vh @@ -0,0 +1,51 @@ +// True minimum configuration -- enough to run hello world but no support for +// traps, debug, or any non-mandatory ISA extensions. + +localparam RESET_VECTOR = 32'h80000040; +localparam MTVEC_INIT = 32'h80000000; +localparam EXTENSION_A = 0; +localparam EXTENSION_C = 0; +localparam EXTENSION_E = 0; +localparam EXTENSION_M = 0; +localparam EXTENSION_ZBA = 0; +localparam EXTENSION_ZBB = 0; +localparam EXTENSION_ZBC = 0; +localparam EXTENSION_ZBKB = 0; +localparam EXTENSION_ZBKX = 0; +localparam EXTENSION_ZBS = 0; +localparam EXTENSION_ZCB = 0; +localparam EXTENSION_ZCLSD = 0; +localparam EXTENSION_ZCMP = 0; +localparam EXTENSION_ZIFENCEI = 0; +localparam EXTENSION_ZILSD = 0; +localparam EXTENSION_XH3BEXTM = 0; +localparam EXTENSION_XH3IRQ = 0; +localparam EXTENSION_XH3PMPM = 0; +localparam EXTENSION_XH3POWER = 0; +localparam CSR_M_MANDATORY = 0; +localparam CSR_M_TRAP = 0; +localparam CSR_COUNTER = 0; +localparam U_MODE = 0; +localparam PMP_REGIONS = 0; +localparam PMP_GRAIN = 0; +localparam PMP_MATCH_NAPOT = 1; +localparam PMP_MATCH_TOR = 0; +localparam PMP_HARDWIRED = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){1'b0}}; +localparam PMP_HARDWIRED_ADDR = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){32'h0}}; +localparam PMP_HARDWIRED_CFG = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){8'h00}}; +localparam DEBUG_SUPPORT = 0; +localparam BREAKPOINT_TRIGGERS = 4; +localparam NUM_IRQS = 32; +localparam IRQ_PRIORITY_BITS = 0; +localparam IRQ_INPUT_BYPASS = {NUM_IRQS{1'b0}}; +localparam MVENDORID_VAL = 32'hdeadbeef; +localparam MCONFIGPTR_VAL = 32'h9abcdef0; +localparam REDUCED_BYPASS = 1; +localparam MULDIV_UNROLL = 1; +localparam MUL_FAST = 0; +localparam MUL_FASTER = 0; +localparam MULH_FAST = 0; +localparam FAST_BRANCHCMP = 0; +localparam RESET_REGFILE = 1; +localparam BRANCH_PREDICTOR = 0; +localparam MTVEC_WMASK = 32'hfffffffd; diff --git a/vendor/Hazard3/test/sim/tb_common/hdl/config_min_debug.vh b/vendor/Hazard3/test/sim/tb_common/hdl/config_min_debug.vh new file mode 100644 index 0000000..e28126b --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/hdl/config_min_debug.vh @@ -0,0 +1,51 @@ +// Minimum performance and unprivileged ISA feature set, but enough privileged +// ISA and debug support to run a more interesting test suite. + +localparam RESET_VECTOR = 32'h80000040; +localparam MTVEC_INIT = 32'h80000000; +localparam EXTENSION_A = 0; +localparam EXTENSION_C = 0; +localparam EXTENSION_E = 0; +localparam EXTENSION_M = 0; +localparam EXTENSION_ZBA = 0; +localparam EXTENSION_ZBB = 0; +localparam EXTENSION_ZBC = 0; +localparam EXTENSION_ZBKB = 0; +localparam EXTENSION_ZBKX = 0; +localparam EXTENSION_ZBS = 0; +localparam EXTENSION_ZCB = 0; +localparam EXTENSION_ZCLSD = 0; +localparam EXTENSION_ZCMP = 0; +localparam EXTENSION_ZIFENCEI = 0; +localparam EXTENSION_ZILSD = 0; +localparam EXTENSION_XH3BEXTM = 0; +localparam EXTENSION_XH3IRQ = 0; +localparam EXTENSION_XH3PMPM = 0; +localparam EXTENSION_XH3POWER = 0; +localparam CSR_M_MANDATORY = 1; +localparam CSR_M_TRAP = 1; +localparam CSR_COUNTER = 0; +localparam U_MODE = 1; +localparam PMP_REGIONS = 4; +localparam PMP_GRAIN = 0; +localparam PMP_MATCH_NAPOT = 1; +localparam PMP_MATCH_TOR = 0; +localparam PMP_HARDWIRED = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){1'b0}}; +localparam PMP_HARDWIRED_ADDR = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){32'h0}}; +localparam PMP_HARDWIRED_CFG = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){8'h00}}; +localparam DEBUG_SUPPORT = 1; +localparam BREAKPOINT_TRIGGERS = 0; +localparam NUM_IRQS = 32; +localparam IRQ_PRIORITY_BITS = 0; +localparam IRQ_INPUT_BYPASS = {NUM_IRQS{1'b0}}; +localparam MVENDORID_VAL = 32'hdeadbeef; +localparam MCONFIGPTR_VAL = 32'h9abcdef0; +localparam REDUCED_BYPASS = 1; +localparam MULDIV_UNROLL = 1; +localparam MUL_FAST = 0; +localparam MUL_FASTER = 0; +localparam MULH_FAST = 0; +localparam FAST_BRANCHCMP = 0; +localparam RESET_REGFILE = 1; +localparam BRANCH_PREDICTOR = 0; +localparam MTVEC_WMASK = 32'hfffffffd; diff --git a/vendor/Hazard3/test/sim/tb_common/hdl/config_pmpfull.vh b/vendor/Hazard3/test/sim/tb_common/hdl/config_pmpfull.vh new file mode 100644 index 0000000..adf7a87 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/hdl/config_pmpfull.vh @@ -0,0 +1,50 @@ +// Default Hazard3 config for testbench: all ISA features + +localparam RESET_VECTOR = 32'h80000040; +localparam MTVEC_INIT = 32'h80000000; +localparam EXTENSION_A = 1; +localparam EXTENSION_C = 1; +localparam EXTENSION_E = 0; +localparam EXTENSION_M = 1; +localparam EXTENSION_ZBA = 1; +localparam EXTENSION_ZBB = 1; +localparam EXTENSION_ZBC = 1; +localparam EXTENSION_ZBKB = 1; +localparam EXTENSION_ZBKX = 1; +localparam EXTENSION_ZBS = 1; +localparam EXTENSION_ZCB = 1; +localparam EXTENSION_ZCLSD = 1; +localparam EXTENSION_ZCMP = 1; +localparam EXTENSION_ZIFENCEI = 1; +localparam EXTENSION_ZILSD = 1; +localparam EXTENSION_XH3BEXTM = 1; +localparam EXTENSION_XH3IRQ = 1; +localparam EXTENSION_XH3PMPM = 1; +localparam EXTENSION_XH3POWER = 1; +localparam CSR_M_MANDATORY = 1; +localparam CSR_M_TRAP = 1; +localparam CSR_COUNTER = 1; +localparam U_MODE = 1; +localparam PMP_REGIONS = 16; +localparam PMP_GRAIN = 0; +localparam PMP_MATCH_NAPOT = 1; +localparam PMP_MATCH_TOR = 1; +localparam PMP_HARDWIRED = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){1'b0}}; +localparam PMP_HARDWIRED_ADDR = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){32'h0}}; +localparam PMP_HARDWIRED_CFG = {(PMP_REGIONS > 0 ? PMP_REGIONS : 1){8'h00}}; +localparam DEBUG_SUPPORT = 1; +localparam BREAKPOINT_TRIGGERS = 4; +localparam NUM_IRQS = 32; +localparam IRQ_PRIORITY_BITS = 4; +localparam IRQ_INPUT_BYPASS = {NUM_IRQS{1'b0}}; +localparam MVENDORID_VAL = 32'hdeadbeef; +localparam MCONFIGPTR_VAL = 32'h9abcdef0; +localparam REDUCED_BYPASS = 0; +localparam MULDIV_UNROLL = 2; +localparam MUL_FAST = 1; +localparam MUL_FASTER = 1; +localparam MULH_FAST = 1; +localparam FAST_BRANCHCMP = 1; +localparam RESET_REGFILE = 1; +localparam BRANCH_PREDICTOR = 1; +localparam MTVEC_WMASK = 32'hfffffffd; diff --git a/vendor/Hazard3/test/sim/tb_common/hdl/tb.f b/vendor/Hazard3/test/sim/tb_common/hdl/tb.f new file mode 100644 index 0000000..db7c58d --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/hdl/tb.f @@ -0,0 +1,2 @@ +file tb.v +list tb_common.f diff --git a/vendor/Hazard3/test/sim/tb_common/hdl/tb.v b/vendor/Hazard3/test/sim/tb_common/hdl/tb.v new file mode 100644 index 0000000..a93cccd --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/hdl/tb.v @@ -0,0 +1,341 @@ +// An integration of JTAG-DTM + DM + CPU for openocd to poke at over a remote +// bitbang socket + +`default_nettype none + +module tb #( + parameter W_DATA = 32, // do not modify + parameter W_ADDR = 32 // do not modify +) ( + // Global signals + input wire clk, + input wire rst_n, + + // JTAG port + input wire tck, + input wire trst_n, + input wire tms, + input wire tdi, + output wire tdo, + + // Instruction fetch port + output wire [W_ADDR-1:0] i_haddr, + output wire i_hwrite, + output wire [1:0] i_htrans, + output wire i_hexcl, + output wire [2:0] i_hsize, + output wire [2:0] i_hburst, + output wire [3:0] i_hprot, + output wire i_hmastlock, + output wire [7:0] i_hmaster, + input wire i_hready, + input wire i_hresp, + input wire i_hexokay, + output wire [W_DATA-1:0] i_hwdata, + input wire [W_DATA-1:0] i_hrdata, + + // Load/store port + output wire [W_ADDR-1:0] d_haddr, + output wire d_hwrite, + output wire [1:0] d_htrans, + output wire d_hexcl, + output wire [2:0] d_hsize, + output wire [2:0] d_hburst, + output wire [3:0] d_hprot, + output wire d_hmastlock, + output wire [7:0] d_hmaster, + input wire d_hready, + input wire d_hresp, + input wire d_hexokay, + output wire [W_DATA-1:0] d_hwdata, + input wire [W_DATA-1:0] d_hrdata, + + // Level-sensitive interrupt sources + input wire [NUM_IRQS-1:0] irq, // -> mip.meip + input wire [1:0] soft_irq, // -> mip.msip + input wire [1:0] timer_irq // -> mip.mtip +); + +// JTAG-DTM IDCODE, selected after TAP reset, would normally be a +// JEP106-compliant ID +localparam IDCODE = 32'hdeadbeef; + +wire dmi_psel; +wire dmi_penable; +wire dmi_pwrite; +wire [8:0] dmi_paddr; +wire [31:0] dmi_pwdata; +reg [31:0] dmi_prdata; +wire dmi_pready; +wire dmi_pslverr; + +wire dmihardreset_req; +wire assert_dmi_reset = !rst_n || dmihardreset_req; +wire rst_n_dmi; + +hazard3_reset_sync dmi_reset_sync_u ( + .clk (clk), + .rst_n_in (!assert_dmi_reset), + .rst_n_out (rst_n_dmi) +); + +// Note the idle hint of 8 cycles was empirically found to be the correct +// value for a 1:2 TCK:clk_dmi ratio. OpenOCD doesn't particularly care +// because it will just increase idle cycles until it stops seeing BUSY. + +hazard3_jtag_dtm #( + .IDCODE (IDCODE), + .DTMCS_IDLE_HINT (8) +) inst_hazard3_jtag_dtm ( + .tck (tck), + .trst_n (trst_n), + .tms (tms), + .tdi (tdi), + .tdo (tdo), + + .dmihardreset_req (dmihardreset_req), + + .clk_dmi (clk), + .rst_n_dmi (rst_n_dmi), + + .dmi_psel (dmi_psel), + .dmi_penable (dmi_penable), + .dmi_pwrite (dmi_pwrite), + .dmi_paddr (dmi_paddr), + .dmi_pwdata (dmi_pwdata), + .dmi_prdata (dmi_prdata), + .dmi_pready (dmi_pready), + .dmi_pslverr (dmi_pslverr) +); + +localparam N_HARTS = 1; +localparam XLEN = 32; + +wire sys_reset_req; +wire sys_reset_done; +wire [N_HARTS-1:0] hart_reset_req; +wire [N_HARTS-1:0] hart_reset_done; + +wire [N_HARTS-1:0] hart_req_halt; +wire [N_HARTS-1:0] hart_req_halt_on_reset; +wire [N_HARTS-1:0] hart_req_resume; +wire [N_HARTS-1:0] hart_halted; +wire [N_HARTS-1:0] hart_running; + +wire [N_HARTS*XLEN-1:0] hart_data0_rdata; +wire [N_HARTS*XLEN-1:0] hart_data0_wdata; +wire [N_HARTS-1:0] hart_data0_wen; + +wire [N_HARTS*XLEN-1:0] hart_instr_data; +wire [N_HARTS-1:0] hart_instr_data_vld; +wire [N_HARTS-1:0] hart_instr_data_rdy; +wire [N_HARTS-1:0] hart_instr_caught_exception; +wire [N_HARTS-1:0] hart_instr_caught_ebreak; + +wire [31:0] sbus_addr; +wire sbus_write; +wire [1:0] sbus_size; +wire sbus_vld; +wire sbus_rdy; +wire sbus_err; +wire [31:0] sbus_wdata; +wire [31:0] sbus_rdata; + +hazard3_dm #( + .N_HARTS (N_HARTS), + .HAVE_SBA (1), + .NEXT_DM_ADDR (0) +) dm ( + .clk (clk), + .rst_n (rst_n), + + .dmi_psel (dmi_psel), + .dmi_penable (dmi_penable), + .dmi_pwrite (dmi_pwrite), + .dmi_paddr (dmi_paddr), + .dmi_pwdata (dmi_pwdata), + .dmi_prdata (dmi_prdata), + .dmi_pready (dmi_pready), + .dmi_pslverr (dmi_pslverr), + + .sys_reset_req (sys_reset_req), + .sys_reset_done (sys_reset_done), + .hart_reset_req (hart_reset_req), + .hart_reset_done (hart_reset_done), + + .hart_req_halt (hart_req_halt), + .hart_req_halt_on_reset (hart_req_halt_on_reset), + .hart_req_resume (hart_req_resume), + .hart_halted (hart_halted), + .hart_running (hart_running), + + .hart_data0_rdata (hart_data0_rdata), + .hart_data0_wdata (hart_data0_wdata), + .hart_data0_wen (hart_data0_wen), + + .hart_instr_data (hart_instr_data), + .hart_instr_data_vld (hart_instr_data_vld), + .hart_instr_data_rdy (hart_instr_data_rdy), + .hart_instr_caught_exception (hart_instr_caught_exception), + .hart_instr_caught_ebreak (hart_instr_caught_ebreak), + + .sbus_addr (sbus_addr), + .sbus_write (sbus_write), + .sbus_size (sbus_size), + .sbus_vld (sbus_vld), + .sbus_rdy (sbus_rdy), + .sbus_err (sbus_err), + .sbus_wdata (sbus_wdata), + .sbus_rdata (sbus_rdata) +); + + +// Generate resynchronised reset for CPU based on upstream reset and +// on reset requests from DM. + +wire assert_cpu_reset = !rst_n || sys_reset_req || hart_reset_req[0]; +wire rst_n_cpu; + +hazard3_reset_sync cpu_reset_sync ( + .clk (clk), + .rst_n_in (!assert_cpu_reset), + .rst_n_out (rst_n_cpu) +); + +// Still some work to be done on the reset handshake -- this ought to be +// resynchronised to DM's reset domain here, and the DM should wait for a +// rising edge after it has asserted the reset pulse, to make sure the tail +// of the previous "done" is not passed on. +assign sys_reset_done = rst_n_cpu; +assign hart_reset_done = rst_n_cpu; + + +wire pwrup_req; +reg pwrup_ack; +wire clk_en; +wire unblock_out; +wire unblock_in = unblock_out; + +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + pwrup_ack <= 1'b1; + end else begin + pwrup_ack <= pwrup_req; + end +end + +wire fence_i_vld; +wire fence_d_vld; +reg [3:0] fence_rdy_delay_ctr; +wire fence_rdy = &fence_rdy_delay_ctr; +always @ (posedge clk or negedge rst_n) begin + if (!rst_n) begin + fence_rdy_delay_ctr <= 4'h0; + end else if (fence_i_vld || fence_d_vld) begin + fence_rdy_delay_ctr <= fence_rdy_delay_ctr + 4'h1; + end else begin + fence_rdy_delay_ctr <= 4'h0; + end +end + +// Clock gate is disabled, as CXXRTL currently can't simulated gated clocks +// due to a limitation of the scheduler design + +// // Latching clock gate. Does not insert an NBA delay on the gated clock, so +// // safe to exchange data between NBAs on the gated and non-gated clock. Does +// // not glitch as long as clk_en is driven from an NBA on the posedge of clk +// // (e.g. a normal RTL register). The clock stops *high*. + +// reg clk_gated; + +// always @ (*) begin +// if (clk_en) +// clk_gated = clk; +// end + +`ifndef CONFIG_HEADER +`define CONFIG_HEADER "config_default.vh" +`endif +`include `CONFIG_HEADER + +hazard3_cpu_2port #( +`include "hazard3_config_inst.vh" +) cpu ( + .clk (clk), + .clk_always_on (clk), + .rst_n (rst_n_cpu), + + .pwrup_req (pwrup_req), + .pwrup_ack (pwrup_ack), + .clk_en (clk_en), + .unblock_out (unblock_out), + .unblock_in (unblock_in), + + .i_haddr (i_haddr), + .i_hwrite (i_hwrite), + .i_htrans (i_htrans), + .i_hsize (i_hsize), + .i_hburst (i_hburst), + .i_hprot (i_hprot), + .i_hmastlock (i_hmastlock), + .i_hmaster (i_hmaster), + .i_hready (i_hready), + .i_hresp (i_hresp), + .i_hwdata (i_hwdata), + .i_hrdata (i_hrdata), + + .d_haddr (d_haddr), + .d_hexcl (d_hexcl), + .d_hwrite (d_hwrite), + .d_htrans (d_htrans), + .d_hsize (d_hsize), + .d_hburst (d_hburst), + .d_hprot (d_hprot), + .d_hmastlock (d_hmastlock), + .d_hmaster (d_hmaster), + .d_hready (d_hready), + .d_hresp (d_hresp), + .d_hexokay (d_hexokay), + .d_hwdata (d_hwdata), + .d_hrdata (d_hrdata), + + .fence_i_vld (fence_i_vld), + .fence_d_vld (fence_d_vld), + .fence_rdy (fence_rdy), + + .dbg_req_halt (hart_req_halt), + .dbg_req_halt_on_reset (hart_req_halt_on_reset), + .dbg_req_resume (hart_req_resume), + .dbg_halted (hart_halted), + .dbg_running (hart_running), + + .dbg_data0_rdata (hart_data0_rdata), + .dbg_data0_wdata (hart_data0_wdata), + .dbg_data0_wen (hart_data0_wen), + + .dbg_instr_data (hart_instr_data), + .dbg_instr_data_vld (hart_instr_data_vld), + .dbg_instr_data_rdy (hart_instr_data_rdy), + .dbg_instr_caught_exception (hart_instr_caught_exception), + .dbg_instr_caught_ebreak (hart_instr_caught_ebreak), + + .dbg_sbus_addr (sbus_addr), + .dbg_sbus_write (sbus_write), + .dbg_sbus_size (sbus_size), + .dbg_sbus_vld (sbus_vld), + .dbg_sbus_rdy (sbus_rdy), + .dbg_sbus_err (sbus_err), + .dbg_sbus_wdata (sbus_wdata), + .dbg_sbus_rdata (sbus_rdata), + + .mhartid_val (32'd0), + .eco_version (4'ha), + + .irq (irq), + .soft_irq (soft_irq[0]), + .timer_irq (timer_irq[0]) +); + +assign i_hexcl = 1'b0; + +endmodule diff --git a/vendor/Hazard3/test/sim/tb_common/hdl/tb_common.f b/vendor/Hazard3/test/sim/tb_common/hdl/tb_common.f new file mode 100644 index 0000000..d87d913 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/hdl/tb_common.f @@ -0,0 +1,7 @@ +file $HDL/debug/cdc/hazard3_reset_sync.v + +list $HDL/hazard3.f +list $HDL/debug/dm/hazard3_dm.f +list $HDL/debug/dtm/hazard3_jtag_dtm.f + +include . diff --git a/vendor/Hazard3/test/sim/tb_common/hdl/tb_multicore.f b/vendor/Hazard3/test/sim/tb_common/hdl/tb_multicore.f new file mode 100644 index 0000000..0541de6 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/hdl/tb_multicore.f @@ -0,0 +1,2 @@ +file tb_multicore.v +list tb_common.f diff --git a/vendor/Hazard3/test/sim/tb_common/hdl/tb_multicore.v b/vendor/Hazard3/test/sim/tb_common/hdl/tb_multicore.v new file mode 100644 index 0000000..e20c8e5 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/hdl/tb_multicore.v @@ -0,0 +1,358 @@ +// An integration of JTAG-DTM + DM + 2 single-ported CPUs for openocd to poke +// at over a remote bitbang socket + +`default_nettype none + +module tb #( + parameter W_ADDR = 32, // do not modify + parameter W_DATA = 32 // do not modify +) ( + // Global signals + input wire clk, + input wire rst_n, + + // JTAG port + input wire tck, + input wire trst_n, + input wire tms, + input wire tdi, + output wire tdo, + + // Core 0 bus (named I for consistency with 1-core 2-port tb) + output wire [W_ADDR-1:0] i_haddr, + output wire i_hwrite, + output wire [1:0] i_htrans, + output wire i_hexcl, + output wire [2:0] i_hsize, + output wire [2:0] i_hburst, + output wire [3:0] i_hprot, + output wire i_hmastlock, + output wire [7:0] i_hmaster, + input wire i_hready, + input wire i_hresp, + input wire i_hexokay, + output wire [W_DATA-1:0] i_hwdata, + input wire [W_DATA-1:0] i_hrdata, + + // Core 1 bus (named D for consistency with 1-core 2-port tb) + output wire [W_ADDR-1:0] d_haddr, + output wire d_hwrite, + output wire [1:0] d_htrans, + output wire d_hexcl, + output wire [2:0] d_hsize, + output wire [2:0] d_hburst, + output wire [3:0] d_hprot, + output wire d_hmastlock, + output wire [7:0] d_hmaster, + input wire d_hready, + input wire d_hresp, + input wire d_hexokay, + output wire [W_DATA-1:0] d_hwdata, + input wire [W_DATA-1:0] d_hrdata, + + // Level-sensitive interrupt sources + input wire [NUM_IRQS-1:0] irq, // -> mip.meip + input wire [1:0] soft_irq, // -> mip.msip + input wire [1:0] timer_irq // -> mip.mtip +); + +// JTAG-DTM IDCODE, selected after TAP reset, would normally be a +// JEP106-compliant ID +localparam IDCODE = 32'hdeadbeef; + +wire dmi_psel; +wire dmi_penable; +wire dmi_pwrite; +wire [8:0] dmi_paddr; +wire [31:0] dmi_pwdata; +reg [31:0] dmi_prdata; +wire dmi_pready; +wire dmi_pslverr; + +wire dmihardreset_req; +wire assert_dmi_reset = !rst_n || dmihardreset_req; +wire rst_n_dmi; + +hazard3_reset_sync dmi_reset_sync_u ( + .clk (clk), + .rst_n_in (!assert_dmi_reset), + .rst_n_out (rst_n_dmi) +); + +hazard3_jtag_dtm #( + .IDCODE (IDCODE), + .DTMCS_IDLE_HINT (8) +) inst_hazard3_jtag_dtm ( + .tck (tck), + .trst_n (trst_n), + .tms (tms), + .tdi (tdi), + .tdo (tdo), + + .dmihardreset_req (dmihardreset_req), + + .clk_dmi (clk), + .rst_n_dmi (rst_n_dmi), + + .dmi_psel (dmi_psel), + .dmi_penable (dmi_penable), + .dmi_pwrite (dmi_pwrite), + .dmi_paddr (dmi_paddr), + .dmi_pwdata (dmi_pwdata), + .dmi_prdata (dmi_prdata), + .dmi_pready (dmi_pready), + .dmi_pslverr (dmi_pslverr) +); + +localparam N_HARTS = 2; +localparam XLEN = 32; + +wire sys_reset_req; +wire sys_reset_done; +wire [N_HARTS-1:0] hart_reset_req; +wire [N_HARTS-1:0] hart_reset_done; + +wire [N_HARTS-1:0] hart_req_halt; +wire [N_HARTS-1:0] hart_req_halt_on_reset; +wire [N_HARTS-1:0] hart_req_resume; +wire [N_HARTS-1:0] hart_halted; +wire [N_HARTS-1:0] hart_running; + +wire [N_HARTS*XLEN-1:0] hart_data0_rdata; +wire [N_HARTS*XLEN-1:0] hart_data0_wdata; +wire [N_HARTS-1:0] hart_data0_wen; + +wire [N_HARTS*XLEN-1:0] hart_instr_data; +wire [N_HARTS-1:0] hart_instr_data_vld; +wire [N_HARTS-1:0] hart_instr_data_rdy; +wire [N_HARTS-1:0] hart_instr_caught_exception; +wire [N_HARTS-1:0] hart_instr_caught_ebreak; + +wire [31:0] sbus_addr; +wire sbus_write; +wire [1:0] sbus_size; +wire sbus_vld; +wire sbus_rdy; +wire sbus_err; +wire [31:0] sbus_wdata; +wire [31:0] sbus_rdata; + +hazard3_dm #( + .N_HARTS (N_HARTS), + .HAVE_SBA (1), + .NEXT_DM_ADDR (0) +) dm ( + .clk (clk), + .rst_n (rst_n), + + .dmi_psel (dmi_psel), + .dmi_penable (dmi_penable), + .dmi_pwrite (dmi_pwrite), + .dmi_paddr (dmi_paddr), + .dmi_pwdata (dmi_pwdata), + .dmi_prdata (dmi_prdata), + .dmi_pready (dmi_pready), + .dmi_pslverr (dmi_pslverr), + + .sys_reset_req (sys_reset_req), + .sys_reset_done (sys_reset_done), + .hart_reset_req (hart_reset_req), + .hart_reset_done (hart_reset_done), + + .hart_req_halt (hart_req_halt), + .hart_req_halt_on_reset (hart_req_halt_on_reset), + .hart_req_resume (hart_req_resume), + .hart_halted (hart_halted), + .hart_running (hart_running), + + .hart_data0_rdata (hart_data0_rdata), + .hart_data0_wdata (hart_data0_wdata), + .hart_data0_wen (hart_data0_wen), + + .hart_instr_data (hart_instr_data), + .hart_instr_data_vld (hart_instr_data_vld), + .hart_instr_data_rdy (hart_instr_data_rdy), + .hart_instr_caught_exception (hart_instr_caught_exception), + .hart_instr_caught_ebreak (hart_instr_caught_ebreak), + + .sbus_addr (sbus_addr), + .sbus_write (sbus_write), + .sbus_size (sbus_size), + .sbus_vld (sbus_vld), + .sbus_rdy (sbus_rdy), + .sbus_err (sbus_err), + .sbus_wdata (sbus_wdata), + .sbus_rdata (sbus_rdata) + +); + +// Generate resynchronised reset for CPU based on upstream reset and +// on reset requests from DM. + +wire assert_cpu_reset0 = !rst_n || sys_reset_req || hart_reset_req[0]; +wire assert_cpu_reset1 = !rst_n || sys_reset_req || hart_reset_req[1]; +wire rst_n_cpu0; +wire rst_n_cpu1; + +hazard3_reset_sync cpu0_reset_sync ( + .clk (clk), + .rst_n_in (!assert_cpu_reset0), + .rst_n_out (rst_n_cpu0) +); + +hazard3_reset_sync cpu1_reset_sync ( + .clk (clk), + .rst_n_in (!assert_cpu_reset1), + .rst_n_out (rst_n_cpu1) +); + +// Still some work to be done on the reset handshake -- this ought to be +// resynchronised to DM's reset domain here, and the DM should wait for a +// rising edge after it has asserted the reset pulse, to make sure the tail +// of the previous "done" is not passed on. +assign sys_reset_done = rst_n_cpu0 && rst_n_cpu1; +assign hart_reset_done = {rst_n_cpu1, rst_n_cpu0}; + +`ifndef CONFIG_HEADER +`define CONFIG_HEADER "config_default.vh" +`endif +`include `CONFIG_HEADER + +wire pwrup_req_cpu0; +wire pwrup_req_cpu1; +wire unblock_out_cpu0; +wire unblock_out_cpu1; + +hazard3_cpu_1port #( +`include "hazard3_config_inst.vh" +) cpu0 ( + .clk (clk), + .clk_always_on (clk), + .rst_n (rst_n_cpu0), + + .pwrup_req (pwrup_req_cpu0), + .pwrup_ack (pwrup_req_cpu0), + .clk_en (), + .unblock_out (unblock_out_cpu0), + .unblock_in (unblock_out_cpu1), + + .haddr (i_haddr), + .hexcl (i_hexcl), + .hwrite (i_hwrite), + .htrans (i_htrans), + .hsize (i_hsize), + .hburst (i_hburst), + .hprot (i_hprot), + .hmastlock (i_hmastlock), + .hmaster (i_hmaster), + .hready (i_hready), + .hresp (i_hresp), + .hexokay (i_hexokay), + .hwdata (i_hwdata), + .hrdata (i_hrdata), + + .fence_i_vld (), + .fence_d_vld (), + .fence_rdy (1'b1), + + .dbg_req_halt (hart_req_halt [0]), + .dbg_req_halt_on_reset (hart_req_halt_on_reset [0]), + .dbg_req_resume (hart_req_resume [0]), + .dbg_halted (hart_halted [0]), + .dbg_running (hart_running [0]), + + .dbg_data0_rdata (hart_data0_rdata [0 * XLEN +: XLEN]), + .dbg_data0_wdata (hart_data0_wdata [0 * XLEN +: XLEN]), + .dbg_data0_wen (hart_data0_wen [0]), + + .dbg_instr_data (hart_instr_data [0 * XLEN +: XLEN]), + .dbg_instr_data_vld (hart_instr_data_vld [0]), + .dbg_instr_data_rdy (hart_instr_data_rdy [0]), + .dbg_instr_caught_exception (hart_instr_caught_exception[0]), + .dbg_instr_caught_ebreak (hart_instr_caught_ebreak [0]), + + // SBA is routed through core 1, so tie off on core 0 + .dbg_sbus_addr (32'h0), + .dbg_sbus_write (1'b0), + .dbg_sbus_size (2'h0), + .dbg_sbus_vld (1'b0), + .dbg_sbus_rdy (), + .dbg_sbus_err (), + .dbg_sbus_wdata (32'h0), + .dbg_sbus_rdata (), + + .mhartid_val (32'd0), + .eco_version (4'ha), + + .irq (irq), + .soft_irq (soft_irq[0]), + .timer_irq (timer_irq[0]) +); + +hazard3_cpu_1port #( +`include "hazard3_config_inst.vh" +) cpu1 ( + .clk (clk), + .clk_always_on (clk), + .rst_n (rst_n_cpu1), + + .pwrup_req (pwrup_req_cpu1), + .pwrup_ack (pwrup_req_cpu1), + .clk_en (), + .unblock_out (unblock_out_cpu1), + .unblock_in (unblock_out_cpu0), + + .haddr (d_haddr), + .hexcl (d_hexcl), + .hwrite (d_hwrite), + .htrans (d_htrans), + .hsize (d_hsize), + .hburst (d_hburst), + .hprot (d_hprot), + .hmastlock (d_hmastlock), + .hmaster (d_hmaster), + .hready (d_hready), + .hresp (d_hresp), + .hexokay (d_hexokay), + .hwdata (d_hwdata), + .hrdata (d_hrdata), + + .fence_i_vld (), + .fence_d_vld (), + .fence_rdy (1'b1), + + .dbg_req_halt (hart_req_halt [1]), + .dbg_req_halt_on_reset (hart_req_halt_on_reset [1]), + .dbg_req_resume (hart_req_resume [1]), + .dbg_halted (hart_halted [1]), + .dbg_running (hart_running [1]), + + .dbg_data0_rdata (hart_data0_rdata [1 * XLEN +: XLEN]), + .dbg_data0_wdata (hart_data0_wdata [1 * XLEN +: XLEN]), + .dbg_data0_wen (hart_data0_wen [1]), + + .dbg_instr_data (hart_instr_data [1 * XLEN +: XLEN]), + .dbg_instr_data_vld (hart_instr_data_vld [1]), + .dbg_instr_data_rdy (hart_instr_data_rdy [1]), + .dbg_instr_caught_exception (hart_instr_caught_exception[1]), + .dbg_instr_caught_ebreak (hart_instr_caught_ebreak [1]), + + .dbg_sbus_addr (sbus_addr), + .dbg_sbus_write (sbus_write), + .dbg_sbus_size (sbus_size), + .dbg_sbus_vld (sbus_vld), + .dbg_sbus_rdy (sbus_rdy), + .dbg_sbus_err (sbus_err), + .dbg_sbus_wdata (sbus_wdata), + .dbg_sbus_rdata (sbus_rdata), + + .mhartid_val (32'd1), + .eco_version (4'ha), + + .irq (irq), + .soft_irq (soft_irq[1]), + .timer_irq (timer_irq[1]) +); + + +endmodule diff --git a/vendor/Hazard3/test/sim/tb_common/include/tb.h b/vendor/Hazard3/test/sim/tb_common/include/tb.h new file mode 100644 index 0000000..12a81a1 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/include/tb.h @@ -0,0 +1,108 @@ +#pragma once + +#include "tb_cli.h" +#include "tb_constants.h" +#include "tb_uart.h" + +#include +#include +#include + +#include +#include +#include + +struct mem_io_state; +class tb_top; + +struct bus_request { + uint32_t addr; + bus_size_t size; + bool write; + bool excl; + uint32_t wdata; + int reservation_id; + bus_request(): addr(0), size(SIZE_BYTE), write(0), excl(0), wdata(0), reservation_id(0) {} +}; + +struct bus_response { + uint32_t rdata; + int stall_cycles; + bool err; + bool exokay; + bus_response(): rdata(0), stall_cycles(0), err(false), exokay(true) {} +}; + +typedef bus_response (*mem_access_callback_t)(tb_top &tb, mem_io_state &memio, bus_request req); + +// Default callback: +bus_response tb_mem_access(tb_top &tb, mem_io_state &memio, bus_request req); + +// Abstract test harness class. Concrete implementations of this class contain +// the actual C++ cycle model as well as the glue for this interface. +class tb_top { +protected: + mem_access_callback_t mem_callback_i; + mem_access_callback_t mem_callback_d; + uint64_t rand_state[4]; +public: + FILE *logfile; + void set_mem_callback_i(mem_access_callback_t cb) {mem_callback_i = cb;} + void set_mem_callback_d(mem_access_callback_t cb) {mem_callback_d = cb;} + + tb_top(const tb_cli_args &args) { + mem_callback_i = tb_mem_access; + mem_callback_d = tb_mem_access; + seed_rand((const uint8_t*)"looks random to me", 18); + if (args.log_path != "") { + logfile = fopen(args.log_path.c_str(), "wb"); + } else { + logfile = stdout; + } + } + + void seed_rand(const uint8_t *data, size_t len); + uint32_t rand(); + + virtual void step(const tb_cli_args &args, mem_io_state &memio) = 0; + // Evaluate DUT at current signal values without advancing the core clock. + virtual void eval() = 0; + + virtual void set_trst_n(bool trst_n) = 0; + virtual void set_tck(bool tck) = 0; + virtual void set_tdi(bool tdi) = 0; + virtual void set_tms(bool tms) = 0; + virtual bool get_tdo() = 0; + + virtual void set_irq(uint32_t mask) = 0; + virtual void set_soft_irq(uint8_t mask) = 0; + virtual void set_timer_irq(uint8_t mask) = 0; +}; + +struct mem_io_state { + uint64_t mtime; + uint64_t mtimecmp[2]; + + bool exit_req; + uint32_t exit_code; + + uint8_t *mem; + + bool monitor_enabled; + bool reservation_valid[2]; + uint32_t reservation_addr[2]; + uint32_t poison_addr; + + uint8_t soft_irq_state; + uint32_t irq_state; + + tb_uart_state uart; + + mem_io_state(const tb_cli_args &args); + + ~mem_io_state() { + delete[] mem; + } + + void step(tb_top &tb); +}; diff --git a/vendor/Hazard3/test/sim/tb_common/include/tb_cli.h b/vendor/Hazard3/test/sim/tb_common/include/tb_cli.h new file mode 100644 index 0000000..753b136 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/include/tb_cli.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +struct tb_cli_args { + bool load_bin; + std::string bin_path; + bool dump_waves; + std::string waves_path; + std::vector> dump_ranges; + int64_t max_cycles; + bool propagate_return_code; + uint16_t port; + uint16_t vpi_port; + uint16_t gdb_port; + uint16_t uart0_port; + uint16_t uart1_port; + // JTAG_VPI socket polling backoff in core cycles (0 = poll every cycle). + // When idle, the testbench ramps up to this maximum backoff to reduce + // syscall overhead without adding huge latency between back-to-back packets. + uint32_t vpi_poll_cycles; + // When using --vpi-port, run N core clock cycles per JTAG TCK cycle during + // OpenOCD TMS sequences (helps DMI/APB CDC make progress and avoids BUSY). + // 0 disables this coupling (legacy behaviour). + uint32_t vpi_clk_per_tck; + bool dump_jtag; + std::string jtag_dump_path; + bool replay_jtag; + std::string jtag_replay_path; + std::string log_path; + std::string sig_path; +#ifdef CXXRTL_DEBUG_AGENT + bool run_agent; +#endif + tb_cli_args() { + load_bin = false; + dump_waves = false; + max_cycles = 0; + propagate_return_code = false; + port = 0; + vpi_port = 0; + gdb_port = 0; + uart0_port = 0; + uart1_port = 0; + vpi_poll_cycles = 256; + vpi_clk_per_tck = 2; + dump_jtag = false; + replay_jtag = false; +#ifdef CXXRTL_DEBUG_AGENT + run_agent = false; +#endif + } +}; + +void tb_parse_args(int argc, char **argv, tb_cli_args &args); diff --git a/vendor/Hazard3/test/sim/tb_common/include/tb_constants.h b/vendor/Hazard3/test/sim/tb_common/include/tb_constants.h new file mode 100644 index 0000000..8a1f44e --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/include/tb_constants.h @@ -0,0 +1,57 @@ +#pragma once + +#ifdef __x86_64__ +#define I64_FMT "%ld" +#else +#define I64_FMT "%lld" +#endif + +#define MEM_BASE 0x80000000 +#define MEM_SIZE (16 * 1024 * 1024) +#define N_RESERVATIONS (2) +#define RESERVATION_ADDR_MASK (0xfffffff8u) + +static const unsigned int IO_BASE = 0xc0000000; +enum { + IO_PRINT_CHAR = 0x000, + IO_PRINT_U32 = 0x004, + IO_EXIT = 0x008, + IO_SET_SOFTIRQ = 0x010, + IO_CLR_SOFTIRQ = 0x014, + IO_GLOBMON_EN = 0x018, + IO_POISON_ADDR = 0x01c, + IO_SET_IRQ = 0x020, + IO_CLR_IRQ = 0x030, + IO_MTIME = 0x100, + IO_MTIMEH = 0x104, + IO_MTIMECMP0 = 0x108, + IO_MTIMECMP0H = 0x10c, + IO_MTIMECMP1 = 0x110, + IO_MTIMECMP1H = 0x114 +}; + +// ---------------------------------------------------------------------------- +// Testbench UART-over-TCP MMIO +// +// Each UART is exposed as a raw TCP byte stream (one client at a time). +// Software should poll STATUS.RX_AVAIL before reading DATA. + +static constexpr uint32_t IO_UART_BASE = 0x200; +static constexpr uint32_t IO_UART_STRIDE = 0x20; +static constexpr uint32_t IO_UART_DATA = 0x00; +static constexpr uint32_t IO_UART_STATUS = 0x04; +static constexpr uint32_t IO_UART_CTRL = 0x08; +static constexpr uint32_t IO_UART_N = 2; + +static constexpr uint32_t TB_UART_STATUS_RX_AVAIL = 1u << 0; +static constexpr uint32_t TB_UART_STATUS_TX_READY = 1u << 1; +static constexpr uint32_t TB_UART_STATUS_CONNECTED = 1u << 2; +static constexpr uint32_t TB_UART_STATUS_OVERRUN = 1u << 3; + +static constexpr uint32_t TB_UART_CTRL_CLR_OVERRUN = 1u << 0; + +typedef enum { + SIZE_BYTE = 0, + SIZE_HWORD = 1, + SIZE_WORD = 2 +} bus_size_t; diff --git a/vendor/Hazard3/test/sim/tb_common/include/tb_gdb.h b/vendor/Hazard3/test/sim/tb_common/include/tb_gdb.h new file mode 100644 index 0000000..032f35b --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/include/tb_gdb.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include +#include + +enum class tb_gdb_run_result { + ok = 0, + error, + exited, + timed_out +}; + +struct tb_gdb_target { + virtual ~tb_gdb_target() = default; + + // Register numbering follows the provided target.xml: + // x0..x31 = 0..31, pc = 32. + virtual uint32_t read_reg(uint32_t regno) = 0; + virtual void write_reg(uint32_t regno, uint32_t value) = 0; + + virtual bool read_mem(uint32_t addr, uint8_t *dst, size_t len) = 0; + virtual bool write_mem(uint32_t addr, const uint8_t *src, size_t len) = 0; + + // Advance execution until one instruction retires. + virtual tb_gdb_run_result step_instruction() = 0; + + virtual uint32_t exit_code() const = 0; + + // Optional: handle GDB "monitor" commands (qRcmd). Return true if handled. + // If handled, out_console is printed on the GDB console (can be empty). + virtual bool monitor_cmd(const std::string &cmd, std::string &out_console) { + (void)cmd; + out_console.clear(); + return false; + } +}; + +class tb_gdb_server { +public: + tb_gdb_server(uint16_t port, tb_gdb_target &target); + ~tb_gdb_server(); + + // Blocks until the session ends (disconnect/kill) or the target exits. + tb_gdb_run_result serve(); + +private: + uint16_t port_; + tb_gdb_target &target_; + + int server_fd_ = -1; + int client_fd_ = -1; + bool no_ack_mode_ = false; + + std::string target_xml_; + std::string memory_map_xml_; + + // Execute breakpoints (RSP Z0/Z1): stop before executing instruction at PC. + // When resuming from a breakpoint, ignore a match at the current PC once. + std::unordered_set breakpoints_; + uint32_t ignore_breakpoint_pc_ = 0xffffffffu; + + // Socket helpers + bool open_listen_socket_(); + bool accept_client_(); + void close_client_(); + + bool recv_packet_(std::string &out_payload, bool &got_interrupt); + bool send_packet_(const std::string &payload); + bool maybe_send_ack_(bool ok); + bool check_interrupt_(); + + // RSP helpers + static uint8_t checksum_(const std::string &s); + static int hex_val_(char c); + static std::string to_hex_bytes_(const uint8_t *data, size_t len); + static bool from_hex_bytes_(const std::string &hex, std::string &out_bytes); + static void append_u32_le_hex_(std::string &out, uint32_t v); + static bool parse_u32_hex_(const std::string &s, uint32_t &out); + + std::string handle_command_(const std::string &cmd, bool &run_command_consumed); + std::string stop_reply_(int signo) const; + std::string stop_reply_exit_() const; +}; diff --git a/vendor/Hazard3/test/sim/tb_common/include/tb_jtag.h b/vendor/Hazard3/test/sim/tb_common/include/tb_jtag.h new file mode 100644 index 0000000..a9db8c6 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/include/tb_jtag.h @@ -0,0 +1,58 @@ +#include +#include + +#include +#include +#include + +#include "tb.h" +#include "tb_cli.h" + +#define TCP_BUF_SIZE 256 + +struct tb_jtag_state { + enum class transport_t { + none, + remote_bitbang, + jtag_vpi + }; + + tb_cli_args args; + transport_t transport; + + int server_fd; + int sock_fd; + struct sockaddr_in sock_addr; + int sock_opt; + socklen_t sock_addr_len; + char txbuf[TCP_BUF_SIZE], rxbuf[TCP_BUF_SIZE]; + int rx_ptr; + int rx_remaining; + int tx_ptr; + + static constexpr int VPI_XFERT_MAX_SIZE = 512; + static constexpr int VPI_PKT_SIZE = 4 + VPI_XFERT_MAX_SIZE + VPI_XFERT_MAX_SIZE + 4 + 4; + uint8_t vpi_rxbuf[VPI_PKT_SIZE]; + int vpi_rx_count; + uint32_t vpi_poll_ctr; + uint32_t vpi_poll_backoff; + + std::ofstream jtag_dump_fd; + std::ifstream jtag_replay_fd; + + tb_jtag_state(const tb_cli_args &_args); + + // Returns true if an exit command was received from the JTAG socket + // If memio/cycle_count/timed_out are provided, the testbench may advance + // the core clock while processing JTAG_VPI TMS sequences (idle/wait cycles), + // so the DMI/APB CDC can make progress and OpenOCD doesn't spin on BUSY. + bool step( + tb_top &tb, + mem_io_state *memio = nullptr, + int64_t *cycle_count = nullptr, + bool *timed_out = nullptr, + uint32_t *core_cycles_advanced = nullptr + ); + + void close(); +}; diff --git a/vendor/Hazard3/test/sim/tb_common/include/tb_uart.h b/vendor/Hazard3/test/sim/tb_common/include/tb_uart.h new file mode 100644 index 0000000..f5575e8 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/include/tb_uart.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +#include + +struct tb_cli_args; + +// Simple UART-over-TCP bridge used by the Verilator/CXXRTL testbenches. +// +// Each UART is exposed as a raw TCP byte stream (one client at a time). +// - CPU TX: MMIO write -> queued -> non-blocking send() to connected client. +// - CPU RX: client -> queued -> MMIO read pops one byte (poll STATUS first). +struct tb_uart_state { + static constexpr uint32_t N_UARTS = 2; + + struct uart { + uint16_t port = 0; + int server_fd = -1; + int client_fd = -1; + bool overrun = false; + + // Bounded FIFOs to avoid unbounded memory growth if the CPU or client + // isn't keeping up. When full, RX drops new data and TX drops old data. + std::deque rx_fifo; + std::deque tx_fifo; + size_t rx_capacity = 4096; + size_t tx_capacity = 4096; + + sockaddr_in bind_addr {}; + + void init(uint16_t port_); + void close(); + void poll_accept(uint32_t uart_idx); + void poll_rx(uint32_t uart_idx); + void poll_tx(uint32_t uart_idx); + bool connected() const { return client_fd >= 0; } + }; + + uart uarts[N_UARTS]; + + tb_uart_state() = default; + explicit tb_uart_state(const tb_cli_args &args); + ~tb_uart_state(); + + void step(); + + uint32_t read_status(uint32_t uart_idx); + uint32_t read_data(uint32_t uart_idx); + void write_data(uint32_t uart_idx, uint8_t byte); + void write_ctrl(uint32_t uart_idx, uint32_t value); +}; + diff --git a/vendor/Hazard3/test/sim/tb_common/tb_cli.cpp b/vendor/Hazard3/test/sim/tb_common/tb_cli.cpp new file mode 100644 index 0000000..13f93de --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/tb_cli.cpp @@ -0,0 +1,162 @@ +#include "tb_cli.h" +#include "tb_constants.h" +#include + +static const char *help_str = +"Usage: tb [--bin x.bin] [--port n] [--vpi-port n] [--vcd x.vcd] [--dump start end] \\\n" +" [--cycles n] [--cpuret] [--jtagdump x] [--jtagreplay x] [--vpi-poll n] \\\n" +" [--gdb-port n] [--uart0-port n] [--uart1-port n]\n" +" [--vpi-clk-per-tck n]\n" +"\n" +" --bin x.bin : Flat binary file loaded to address 0x0 in RAM\n" +" --vcd x.vcd : Path to dump waveforms to\n" +" --dump start end : Print out memory contents from start to end (exclusive)\n" +" after execution finishes. Can be passed multiple times.\n" +" --cycles n : Maximum number of cycles to run before exiting.\n" +" Default is 0 (no maximum).\n" +" --port n : Port number to listen for openocd remote bitbang. Sim\n" +" runs in lockstep with JTAG bitbang, not free-running.\n" +" --vpi-port n : Port number to listen for openocd jtag_vpi. Faster than\n" +" remote bitbang, and sim is free-running.\n" +" --vpi-poll n : When using --vpi-port, back off up to n core cycles\n" +" between socket polls when OpenOCD is idle (0 = every cycle).\n" +" --gdb-port n : Listen for GDB RSP directly (no OpenOCD/JTAG).\n" +" --uart0-port n : Expose testbench UART0 as a TCP stream.\n" +" --uart1-port n : Expose testbench UART1 as a TCP stream.\n" +" --vpi-clk-per-tck n : When using --vpi-port, run n core clock cycles per\n" +" JTAG TCK cycle during OpenOCD TMS sequences, so DMI/APB\n" +" CDC can make progress without huge BUSY delays.\n" +" --cpuret : Testbench's return code is the return code written to\n" +" IO_EXIT by the CPU, or -1 if timed out.\n" +" --jtagdump : Dump OpenOCD JTAG bitbang commands to a file so they\n" +" can be replayed. (Lower perf impact than VCD dumping)\n" +" --jtagreplay : Play back some dumped OpenOCD JTAG bitbang commands\n" +" --logfile path : File to write testbench stdout\n" +" --sigfile path : File to write only the data from --dump commands\n" +" (hex, 32 bits per line, same as riscv-arch-test)\n" +#ifdef CXXRTL_DEBUG_AGENT +" --debug : Run CXXRTL debugger\n" +#endif +; + +static void exit_help(std::string errtext = "") { + std::cerr << errtext << help_str; + exit(-1); +} + +void tb_parse_args(int argc, char **argv, tb_cli_args &args) { + for (int i = 1; i < argc; ++i) { + std::string s(argv[i]); + if (s.substr(0, 11) == "+verilator+") { + // Skip arguments passed directly to verilator context + i += 1; + } else if (s.rfind("--", 0) != 0) { + std::cerr << "Unexpected positional argument " << s << "\n"; + exit_help(""); + } else if (s == "--bin") { + if (argc - i < 2) + exit_help("Option --bin requires an argument\n"); + args.load_bin = true; + args.bin_path = argv[i + 1]; + i += 1; + } else if (s == "--vcd") { + if (argc - i < 2) + exit_help("Option --vcd requires an argument\n"); + args.dump_waves = true; + args.waves_path = argv[i + 1]; + i += 1; + } else if (s == "--logfile") { + if (argc - i < 2) + exit_help("Option --logfile requires an argument\n"); + args.log_path = argv[i + 1]; + i += 1; + } else if (s == "--sigfile") { + if (argc - i < 2) + exit_help("Option --sigfile requires an argument\n"); + args.sig_path = argv[i + 1]; + i += 1; + } else if (s == "--jtagdump") { + if (argc - i < 2) + exit_help("Option --jtagdump requires an argument\n"); + args.dump_jtag = true; + args.jtag_dump_path = argv[i + 1]; + i += 1; + } else if (s == "--jtagreplay") { + if (argc - i < 2) + exit_help("Option --jtagreplay requires an argument\n"); + args.replay_jtag = true; + args.jtag_replay_path = argv[i + 1]; + i += 1; + } else if (s == "--dump") { + if (argc - i < 3) + exit_help("Option --dump requires 2 arguments\n"); + uint32_t first = std::stoul(argv[i + 1], 0, 0); + uint32_t last = std::stoul(argv[i + 2], 0, 0); + if (first < MEM_BASE || last > MEM_BASE + MEM_SIZE || first > last) { + std::cerr << "Invalid memory range\n"; + exit(-1); + } + args.dump_ranges.push_back(std::pair( + first, last + )); + i += 2; + } else if (s == "--cycles") { + if (argc - i < 2) + exit_help("Option --cycles requires an argument\n"); + args.max_cycles = std::stol(argv[i + 1], 0, 0); + i += 1; + } else if (s == "--port") { + if (argc - i < 2) + exit_help("Option --port requires an argument\n"); + args.port = std::stol(argv[i + 1], 0, 0); + i += 1; + } else if (s == "--vpi-port") { + if (argc - i < 2) + exit_help("Option --vpi-port requires an argument\n"); + args.vpi_port = std::stol(argv[i + 1], 0, 0); + i += 1; + } else if (s == "--gdb-port") { + if (argc - i < 2) + exit_help("Option --gdb-port requires an argument\n"); + args.gdb_port = std::stol(argv[i + 1], 0, 0); + i += 1; + } else if (s == "--uart0-port") { + if (argc - i < 2) + exit_help("Option --uart0-port requires an argument\n"); + args.uart0_port = std::stol(argv[i + 1], 0, 0); + i += 1; + } else if (s == "--uart1-port") { + if (argc - i < 2) + exit_help("Option --uart1-port requires an argument\n"); + args.uart1_port = std::stol(argv[i + 1], 0, 0); + i += 1; + } else if (s == "--vpi-poll") { + if (argc - i < 2) + exit_help("Option --vpi-poll requires an argument\n"); + args.vpi_poll_cycles = (uint32_t)std::stoul(argv[i + 1], 0, 0); + i += 1; + } else if (s == "--vpi-clk-per-tck") { + if (argc - i < 2) + exit_help("Option --vpi-clk-per-tck requires an argument\n"); + args.vpi_clk_per_tck = (uint32_t)std::stoul(argv[i + 1], 0, 0); + i += 1; + } else if (s == "--cpuret") { + args.propagate_return_code = true; +#ifdef CXXRTL_DEBUG_AGENT + } else if (s == "--debug") { + args.run_agent = true; +#endif + } else { + std::cerr << "Unrecognised argument " << s << "\n"; + exit_help(""); + } + } + if (!(args.load_bin || args.port != 0 || args.vpi_port != 0 || args.gdb_port != 0 || args.replay_jtag)) + exit_help("At least one of --bin, --port, --vpi-port, --gdb-port or --jtagreplay must be specified.\n"); + if ((args.port != 0) + (args.vpi_port != 0) + (args.gdb_port != 0) > 1) + exit_help("Can't combine --port/--vpi-port/--gdb-port\n"); + if (args.dump_jtag && args.port == 0) + exit_help("--jtagdump is only supported with --port (remote bitbang)\n"); + if (args.replay_jtag && (args.port != 0 || args.vpi_port != 0 || args.gdb_port != 0)) + exit_help("Can't specify --jtagreplay together with --port/--vpi-port/--gdb-port\n"); +} diff --git a/vendor/Hazard3/test/sim/tb_common/tb_gdb.cpp b/vendor/Hazard3/test/sim/tb_common/tb_gdb.cpp new file mode 100644 index 0000000..165b971 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/tb_gdb.cpp @@ -0,0 +1,679 @@ +#include "tb_gdb.h" +#include "tb_constants.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static bool send_all(int fd, const uint8_t *buf, size_t len) { + size_t sent = 0; + while (sent < len) { +#ifdef MSG_NOSIGNAL + ssize_t n = send(fd, buf + sent, len - sent, MSG_NOSIGNAL); +#else + ssize_t n = send(fd, buf + sent, len - sent, 0); +#endif + if (n < 0) { + if (errno == EINTR) + continue; + return false; + } + sent += (size_t)n; + } + return true; +} + +tb_gdb_server::tb_gdb_server(uint16_t port, tb_gdb_target &target) + : port_{port}, target_{target} { + target_xml_ = + "\n" + "\n" + "\n" + " riscv:rv32\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; + + char mm_line[128]; + snprintf( + mm_line, + sizeof(mm_line), + " \n", + (unsigned)MEM_BASE, + (unsigned)MEM_SIZE + ); + + memory_map_xml_ = + "\n" + "\n" + "\n" + + std::string(mm_line) + + "\n"; +} + +tb_gdb_server::~tb_gdb_server() { + close_client_(); + if (server_fd_ >= 0) { + ::close(server_fd_); + server_fd_ = -1; + } +} + +bool tb_gdb_server::open_listen_socket_() { + server_fd_ = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd_ < 0) { + fprintf(stderr, "tb_gdb: socket() failed: %s\n", strerror(errno)); + return false; + } + + int opt = 1; + if (setsockopt(server_fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { + fprintf(stderr, "tb_gdb: setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno)); + return false; + } +#ifdef SO_REUSEPORT + (void)setsockopt(server_fd_, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); +#endif + + sockaddr_in addr{}; + addr.sin_family = AF_INET; + // Debug services are local by default. Remote access belongs behind an + // explicit SSH tunnel, never an unauthenticated wildcard listener. + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(port_); + if (bind(server_fd_, (sockaddr *)&addr, sizeof(addr)) < 0) { + fprintf(stderr, "tb_gdb: bind(127.0.0.1:%u) failed: %s\n", (unsigned)port_, strerror(errno)); + return false; + } + if (listen(server_fd_, 1) < 0) { + fprintf(stderr, "tb_gdb: listen() failed: %s\n", strerror(errno)); + return false; + } + return true; +} + +bool tb_gdb_server::accept_client_() { + sockaddr_in addr{}; + socklen_t addr_len = sizeof(addr); + client_fd_ = accept(server_fd_, (sockaddr *)&addr, &addr_len); + if (client_fd_ < 0) { + fprintf(stderr, "tb_gdb: accept() failed: %s\n", strerror(errno)); + return false; + } + + int flag = 1; + (void)setsockopt(client_fd_, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); + return true; +} + +void tb_gdb_server::close_client_() { + if (client_fd_ >= 0) { + ::close(client_fd_); + client_fd_ = -1; + } + no_ack_mode_ = false; + breakpoints_.clear(); + ignore_breakpoint_pc_ = 0xffffffffu; +} + +uint8_t tb_gdb_server::checksum_(const std::string &s) { + uint8_t sum = 0; + for (unsigned char c : s) + sum = (uint8_t)(sum + c); + return sum; +} + +int tb_gdb_server::hex_val_(char c) { + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return 10 + (c - 'a'); + if (c >= 'A' && c <= 'F') + return 10 + (c - 'A'); + return -1; +} + +std::string tb_gdb_server::to_hex_bytes_(const uint8_t *data, size_t len) { + static const char *hex = "0123456789abcdef"; + std::string out; + out.reserve(len * 2); + for (size_t i = 0; i < len; ++i) { + out.push_back(hex[(data[i] >> 4) & 0xf]); + out.push_back(hex[data[i] & 0xf]); + } + return out; +} + +bool tb_gdb_server::from_hex_bytes_(const std::string &hex, std::string &out_bytes) { + out_bytes.clear(); + if (hex.size() % 2 != 0) + return false; + out_bytes.reserve(hex.size() / 2); + for (size_t i = 0; i < hex.size(); i += 2) { + int hi = hex_val_(hex[i]); + int lo = hex_val_(hex[i + 1]); + if (hi < 0 || lo < 0) + return false; + out_bytes.push_back((char)((hi << 4) | lo)); + } + return true; +} + +void tb_gdb_server::append_u32_le_hex_(std::string &out, uint32_t v) { + for (int i = 0; i < 4; ++i) { + const uint8_t b = (uint8_t)((v >> (8 * i)) & 0xffu); + static const char *hex = "0123456789abcdef"; + out.push_back(hex[(b >> 4) & 0xf]); + out.push_back(hex[b & 0xf]); + } +} + +bool tb_gdb_server::parse_u32_hex_(const std::string &s, uint32_t &out) { + if (s.empty()) + return false; + uint32_t v = 0; + for (char c : s) { + int h = hex_val_(c); + if (h < 0) + return false; + v = (v << 4) | (uint32_t)h; + } + out = v; + return true; +} + +bool tb_gdb_server::maybe_send_ack_(bool ok) { + if (no_ack_mode_) + return true; + const char c = ok ? '+' : '-'; + return send_all(client_fd_, (const uint8_t *)&c, 1); +} + +bool tb_gdb_server::send_packet_(const std::string &payload) { + std::string pkt; + pkt.reserve(payload.size() + 4); + pkt.push_back('$'); + pkt += payload; + pkt.push_back('#'); + uint8_t cksum = checksum_(payload); + static const char *hex = "0123456789abcdef"; + pkt.push_back(hex[(cksum >> 4) & 0xf]); + pkt.push_back(hex[cksum & 0xf]); + return send_all(client_fd_, (const uint8_t *)pkt.data(), pkt.size()); +} + +bool tb_gdb_server::check_interrupt_() { + uint8_t c = 0; + ssize_t n = recv(client_fd_, &c, 1, MSG_DONTWAIT | MSG_PEEK); + if (n < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + return false; + return true; + } + if (n == 0) + return true; + if (c != 0x03) + return false; + (void)recv(client_fd_, &c, 1, MSG_DONTWAIT); + return true; +} + +bool tb_gdb_server::recv_packet_(std::string &out_payload, bool &got_interrupt) { + out_payload.clear(); + got_interrupt = false; + while (true) { + uint8_t c = 0; + ssize_t n = recv(client_fd_, &c, 1, 0); + if (n == 0) + return false; + if (n < 0) { + if (errno == EINTR) + continue; + return false; + } + + if (c == 0x03) { + got_interrupt = true; + return true; + } + if (c == '+' || c == '-') { + continue; + } + if (c != '$') { + continue; + } + + std::string payload; + while (true) { + n = recv(client_fd_, &c, 1, 0); + if (n == 0) + return false; + if (n < 0) { + if (errno == EINTR) + continue; + return false; + } + if (c == '#') + break; + payload.push_back((char)c); + } + + char ck[2]; + for (int i = 0; i < 2; ++i) { + n = recv(client_fd_, &c, 1, 0); + if (n == 0) + return false; + if (n < 0) { + if (errno == EINTR) { + --i; + continue; + } + return false; + } + ck[i] = (char)c; + } + + bool ok = true; + const int hi = hex_val_(ck[0]); + const int lo = hex_val_(ck[1]); + if (hi < 0 || lo < 0) { + ok = false; + } else { + const uint8_t expected = (uint8_t)((hi << 4) | lo); + ok = expected == checksum_(payload); + } + + if (!maybe_send_ack_(ok)) + return false; + if (!ok) + continue; + + out_payload = std::move(payload); + return true; + } +} + +std::string tb_gdb_server::stop_reply_(int signo) const { + char buf[8]; + snprintf(buf, sizeof(buf), "S%02x", signo & 0xff); + return std::string(buf); +} + +std::string tb_gdb_server::stop_reply_exit_() const { + char buf[8]; + snprintf(buf, sizeof(buf), "W%02x", (unsigned)(target_.exit_code() & 0xffu)); + return std::string(buf); +} + +std::string tb_gdb_server::handle_command_(const std::string &cmd, bool &should_close) { + should_close = false; + + if (cmd.empty()) + return ""; + + // General queries + if (cmd == "?") + return stop_reply_(5); // SIGTRAP + + if (cmd.rfind("qSupported", 0) == 0) { + return "PacketSize=4000;qXfer:features:read+;qXfer:memory-map:read+;QStartNoAckMode+;swbreak+;hwbreak+;vContSupported+;qRcmd+"; + } + if (cmd == "QStartNoAckMode") { + no_ack_mode_ = true; + return "OK"; + } + if (cmd == "qAttached") + return "1"; + if (cmd == "qC") + return "QC1"; + if (cmd == "qfThreadInfo") + return "m1"; + if (cmd == "qsThreadInfo") + return "l"; + if (cmd.rfind("H", 0) == 0) + return "OK"; + if (cmd.rfind("qThreadExtraInfo", 0) == 0) { + const std::string s = "hart0"; + return to_hex_bytes_((const uint8_t *)s.data(), s.size()); + } + if (cmd.rfind("qSymbol", 0) == 0) + return "OK"; + if (cmd == "qTStatus") + return ""; + + // GDB "monitor" commands (OpenOCD-style). Payload is hex-encoded bytes. + if (cmd.rfind("qRcmd,", 0) == 0) { + const std::string hex = cmd.substr(strlen("qRcmd,")); + std::string decoded; + if (!from_hex_bytes_(hex, decoded)) + return "E01"; + + std::string console; + if (!target_.monitor_cmd(decoded, console)) + return ""; + + if (!console.empty()) { + const std::string payload = "O" + to_hex_bytes_((const uint8_t *)console.data(), console.size()); + (void)send_packet_(payload); + } + return "OK"; + } + + // Extended-remote mode request + if (cmd == "!") + return "OK"; + + // target.xml for riscv32 (x0..x31 + pc) + if (cmd.rfind("qXfer:features:read:target.xml:", 0) == 0) { + const std::string args = cmd.substr(strlen("qXfer:features:read:target.xml:")); + const size_t comma = args.find(','); + if (comma == std::string::npos) + return "E01"; + uint32_t off = 0, len = 0; + if (!parse_u32_hex_(args.substr(0, comma), off) || !parse_u32_hex_(args.substr(comma + 1), len)) + return "E01"; + if (off >= target_xml_.size()) + return "l"; + const size_t end = std::min(target_xml_.size(), (size_t)off + (size_t)len); + const bool more = end < target_xml_.size(); + std::string out; + out.reserve(1 + (end - off)); + out.push_back(more ? 'm' : 'l'); + out.append(target_xml_, off, end - off); + return out; + } + + // memory-map.xml (RAM region for disassembly / memory accessibility) + if (cmd.rfind("qXfer:memory-map:read::", 0) == 0) { + const std::string args = cmd.substr(strlen("qXfer:memory-map:read::")); + const size_t comma = args.find(','); + if (comma == std::string::npos) + return "E01"; + uint32_t off = 0, len = 0; + if (!parse_u32_hex_(args.substr(0, comma), off) || !parse_u32_hex_(args.substr(comma + 1), len)) + return "E01"; + if (off >= memory_map_xml_.size()) + return "l"; + const size_t end = std::min(memory_map_xml_.size(), (size_t)off + (size_t)len); + const bool more = end < memory_map_xml_.size(); + std::string out; + out.reserve(1 + (end - off)); + out.push_back(more ? 'm' : 'l'); + out.append(memory_map_xml_, off, end - off); + return out; + } + + // Registers + if (cmd == "g") { + std::string out; + out.reserve(33 * 8); + for (uint32_t r = 0; r < 33; ++r) + append_u32_le_hex_(out, target_.read_reg(r)); + return out; + } + if (cmd.size() >= 2 && cmd[0] == 'p') { + uint32_t regno = 0; + if (!parse_u32_hex_(cmd.substr(1), regno)) + return "E01"; + std::string out; + out.reserve(8); + append_u32_le_hex_(out, target_.read_reg(regno)); + return out; + } + if (cmd.size() >= 3 && cmd[0] == 'P') { + const size_t eq = cmd.find('='); + if (eq == std::string::npos) + return "E01"; + uint32_t regno = 0; + if (!parse_u32_hex_(cmd.substr(1, eq - 1), regno)) + return "E01"; + std::string bytes; + if (!from_hex_bytes_(cmd.substr(eq + 1), bytes)) + return "E01"; + if (bytes.size() < 4) + return "E01"; + uint32_t v = (uint8_t)bytes[0] | ((uint32_t)(uint8_t)bytes[1] << 8) | ((uint32_t)(uint8_t)bytes[2] << 16) | + ((uint32_t)(uint8_t)bytes[3] << 24); + target_.write_reg(regno, v); + return "OK"; + } + if (cmd.size() >= 1 && cmd[0] == 'G') { + std::string bytes; + if (!from_hex_bytes_(cmd.substr(1), bytes)) + return "E01"; + if (bytes.size() < 33 * 4) + return "E01"; + for (uint32_t r = 0; r < 33; ++r) { + const size_t i = r * 4; + uint32_t v = (uint8_t)bytes[i + 0] | ((uint32_t)(uint8_t)bytes[i + 1] << 8) | + ((uint32_t)(uint8_t)bytes[i + 2] << 16) | ((uint32_t)(uint8_t)bytes[i + 3] << 24); + target_.write_reg(r, v); + } + return "OK"; + } + + // Memory + if (cmd.size() >= 2 && cmd[0] == 'm') { + const size_t comma = cmd.find(','); + if (comma == std::string::npos) + return "E01"; + uint32_t addr = 0, len = 0; + if (!parse_u32_hex_(cmd.substr(1, comma - 1), addr) || !parse_u32_hex_(cmd.substr(comma + 1), len)) + return "E01"; + if (len == 0) + return ""; + std::string buf(len, '\0'); + if (!target_.read_mem(addr, (uint8_t *)&buf[0], (size_t)len)) + return "E01"; + return to_hex_bytes_((const uint8_t *)buf.data(), buf.size()); + } + if (cmd.size() >= 2 && cmd[0] == 'M') { + const size_t comma = cmd.find(','); + const size_t colon = cmd.find(':'); + if (comma == std::string::npos || colon == std::string::npos || comma > colon) + return "E01"; + uint32_t addr = 0, len = 0; + if (!parse_u32_hex_(cmd.substr(1, comma - 1), addr) || !parse_u32_hex_(cmd.substr(comma + 1, colon - comma - 1), len)) + return "E01"; + if (len == 0) + return "OK"; + std::string bytes; + if (!from_hex_bytes_(cmd.substr(colon + 1), bytes)) + return "E01"; + if (bytes.size() < len) + return "E01"; + if (!target_.write_mem(addr, (const uint8_t *)bytes.data(), len)) + return "E01"; + return "OK"; + } + + // Execute breakpoints. + // + // GDB's stock stepi implementation may place a temporary software + // breakpoint (Z0) or hardware breakpoint (Z1) at the predicted next PC, + // for example when stepping across an indirect jalr into a read-only text + // section. This testbench doesn't patch target memory, so both packet types + // are handled identically as execute breakpoints checked in the run loop. + if (cmd.rfind("Z0,", 0) == 0 || cmd.rfind("z0,", 0) == 0 || + cmd.rfind("Z1,", 0) == 0 || cmd.rfind("z1,", 0) == 0) { + const bool set = cmd[0] == 'Z'; + const size_t comma1 = cmd.find(','); + const size_t comma2 = cmd.find(',', comma1 + 1); + if (comma1 == std::string::npos || comma2 == std::string::npos) + return "E01"; + uint32_t addr = 0; + if (!parse_u32_hex_(cmd.substr(comma1 + 1, comma2 - comma1 - 1), addr)) + return "E01"; + if (set) + breakpoints_.insert(addr); + else + breakpoints_.erase(addr); + return "OK"; + } + + // vCont + if (cmd == "vCont?") + return "vCont;c;s"; + + auto do_step = [&](bool single_step) -> std::string { + const uint32_t start_pc = target_.read_reg(32); + const bool ignore_start_break = single_step || start_pc == ignore_breakpoint_pc_; + const bool consume_ignore_pc = start_pc == ignore_breakpoint_pc_; + if (consume_ignore_pc) + ignore_breakpoint_pc_ = 0xffffffffu; + + if (single_step) { + const tb_gdb_run_result r = target_.step_instruction(); + if (r == tb_gdb_run_result::exited) { + // In extended-remote mode keep the connection alive after target exit, + // so the user can issue monitor commands such as "reset halt". + return stop_reply_exit_(); + } + if (r == tb_gdb_run_result::timed_out) { + should_close = true; + return stop_reply_(14); // SIGALRM + } + ignore_breakpoint_pc_ = 0xffffffffu; + return stop_reply_(5); // SIGTRAP + } + + bool first_iter = true; + while (true) { + if (check_interrupt_()) { + ignore_breakpoint_pc_ = 0xffffffffu; + return stop_reply_(2); // SIGINT + } + + const uint32_t pc = target_.read_reg(32); + if (breakpoints_.count(pc) && !(first_iter && ignore_start_break && pc == start_pc)) { + ignore_breakpoint_pc_ = pc; + return stop_reply_(5); // SIGTRAP + } + + const tb_gdb_run_result r = target_.step_instruction(); + ignore_breakpoint_pc_ = 0xffffffffu; + if (r == tb_gdb_run_result::exited) { + // In extended-remote mode keep the connection alive after target exit, + // so the user can issue monitor commands such as "reset halt". + return stop_reply_exit_(); + } + if (r == tb_gdb_run_result::timed_out) { + should_close = true; + return stop_reply_(14); // SIGALRM + } + first_iter = false; + } + }; + + if (cmd.size() >= 1 && (cmd[0] == 'c' || cmd[0] == 's')) { + // Optional resume address + if (cmd.size() > 1) { + uint32_t addr = 0; + if (!parse_u32_hex_(cmd.substr(1), addr)) + return "E01"; + target_.write_reg(32, addr); + } + return do_step(cmd[0] == 's'); + } + if (cmd.rfind("vCont;", 0) == 0) { + if (cmd == "vCont;c") + return do_step(false); + if (cmd == "vCont;s") + return do_step(true); + return ""; + } + + // Detach / kill + if (cmd == "D") { + should_close = true; + return "OK"; + } + if (cmd == "k") { + should_close = true; + return "OK"; + } + + // Unknown/unsupported -> empty response + return ""; +} + +tb_gdb_run_result tb_gdb_server::serve() { + if (!open_listen_socket_()) + return tb_gdb_run_result::error; + + fprintf(stderr, "tb_gdb: listening on 127.0.0.1:%u\n", (unsigned)port_); + if (!accept_client_()) + return tb_gdb_run_result::error; + + fprintf(stderr, "tb_gdb: client connected\n"); + if (!send_packet_(stop_reply_(5))) + return tb_gdb_run_result::error; + + while (true) { + std::string cmd; + bool got_interrupt = false; + if (!recv_packet_(cmd, got_interrupt)) + break; + if (got_interrupt) { + if (!send_packet_(stop_reply_(2))) + break; + continue; + } + + bool should_close = false; + std::string reply = handle_command_(cmd, should_close); + if (!send_packet_(reply)) + break; + + if (should_close) { + if (!reply.empty() && reply[0] == 'W') + return tb_gdb_run_result::exited; + break; + } + } + + close_client_(); + return tb_gdb_run_result::ok; +} diff --git a/vendor/Hazard3/test/sim/tb_common/tb_jtag.cpp b/vendor/Hazard3/test/sim/tb_common/tb_jtag.cpp new file mode 100644 index 0000000..831eaaf --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/tb_jtag.cpp @@ -0,0 +1,481 @@ +#include "tb_cli.h" +#include "tb_jtag.h" + +#include +#include +#include +#include +#include + +// This file contains socket management logic and parsing of OpenOCD JTAG +// protocols: +// - remote_bitbang (simple, lockstep with CPU clock) +// - jtag_vpi (batched, higher throughput) + + +static int wait_for_connection_bitbang(int server_fd, uint16_t port, struct sockaddr *sock_addr, socklen_t *sock_addr_len) { + int sock_fd; + printf("Waiting for connection on port %u\n", port); + if (listen(server_fd, 3) < 0) { + fprintf(stderr, "listen failed\n"); + exit(-1); + } + sock_fd = accept(server_fd, sock_addr, sock_addr_len); + if (sock_fd < 0) { + fprintf(stderr, "accept failed\n"); + exit(-1); + } + printf("Connected\n"); + return sock_fd; +} + +static int wait_for_connection_vpi(int server_fd, uint16_t port, struct sockaddr *sock_addr, socklen_t *sock_addr_len) { + int sock_fd; + printf("Listening on port %u\n", port); + if (listen(server_fd, 3) < 0) { + fprintf(stderr, "listen failed\n"); + exit(-1); + } + sock_fd = accept(server_fd, sock_addr, sock_addr_len); + if (sock_fd < 0) { + fprintf(stderr, "accept failed\n"); + exit(-1); + } + printf("Connected\n"); + return sock_fd; +} + +static uint32_t load_le32(const uint8_t *p) { + return (uint32_t)p[0] + | ((uint32_t)p[1] << 8) + | ((uint32_t)p[2] << 16) + | ((uint32_t)p[3] << 24); +} + +static void store_le32(uint8_t *p, uint32_t v) { + p[0] = v & 0xffu; + p[1] = (v >> 8) & 0xffu; + p[2] = (v >> 16) & 0xffu; + p[3] = (v >> 24) & 0xffu; +} + +static bool get_bit_lsb0(const uint8_t *buf, uint32_t bit_idx) { + return (buf[bit_idx / 8] >> (bit_idx % 8)) & 0x1; +} + +static void set_bit_lsb0(uint8_t *buf, uint32_t bit_idx, bool value) { + const uint8_t mask = 1u << (bit_idx % 8); + if (value) { + buf[bit_idx / 8] |= mask; + } else { + buf[bit_idx / 8] &= ~mask; + } +} + +// Perform one JTAG clock cycle (TCK low -> high -> low) and return the TDO bit +// sampled *before* the rising edge. This matches OpenOCD's scan semantics. +static bool jtag_clock(tb_top &tb, bool tms, bool tdi) { + // Sample TDO (stable between the previous negedge and the next posedge), + // then generate a posedge+negedge pair with the new TMS/TDI values. + const bool tdo = tb.get_tdo(); + tb.set_tms(tms); + tb.set_tdi(tdi); + tb.set_tck(true); + tb.eval(); + + tb.set_tck(false); + tb.eval(); + + return tdo; +} + +static bool send_all(int fd, const uint8_t *buf, size_t len) { + size_t sent = 0; + while (sent < len) { + ssize_t n = send(fd, buf + sent, len - sent, 0); + if (n < 0) { + if (errno == EINTR) + continue; + return false; + } + sent += (size_t)n; + } + return true; +} + +tb_jtag_state::tb_jtag_state(const tb_cli_args &_args) { + args = _args; + transport = transport_t::none; + server_fd = -1; + sock_fd = -1; + sock_opt = 1; + sock_addr_len = sizeof(sock_addr); + rx_ptr = 0; + rx_remaining = 0; + tx_ptr = 0; + vpi_rx_count = 0; + vpi_poll_ctr = 0; + vpi_poll_backoff = 0; + + if (args.vpi_port != 0) { + transport = transport_t::jtag_vpi; + server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd < 0) { + fprintf(stderr, "socket creation failed: %s\n", strerror(errno)); + exit(-1); + } + + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &sock_opt, sizeof(sock_opt)) < 0) { + fprintf(stderr, "setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno)); + exit(-1); + } +#ifdef SO_REUSEPORT + // Best-effort: can fail on some kernels/configs, but is not required. + (void)setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &sock_opt, sizeof(sock_opt)); +#endif + + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sock_addr.sin_port = htons(args.vpi_port); + if (bind(server_fd, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) { + fprintf(stderr, "bind failed: %s\n", strerror(errno)); + exit(-1); + } + + sock_fd = wait_for_connection_vpi(server_fd, args.vpi_port, (struct sockaddr *)&sock_addr, &sock_addr_len); + + // Low-latency local socket traffic helps performance a lot. + int flag = 1; + setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); + } else if (args.port != 0) { + transport = transport_t::remote_bitbang; + server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd < 0) { + fprintf(stderr, "socket creation failed: %s\n", strerror(errno)); + exit(-1); + } + + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &sock_opt, sizeof(sock_opt)) < 0) { + fprintf(stderr, "setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno)); + exit(-1); + } +#ifdef SO_REUSEPORT + // Best-effort: can fail on some kernels/configs, but is not required. + (void)setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &sock_opt, sizeof(sock_opt)); +#endif + + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sock_addr.sin_port = htons(args.port); + if (bind(server_fd, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) { + fprintf(stderr, "bind failed: %s\n", strerror(errno)); + exit(-1); + } + + sock_fd = wait_for_connection_bitbang(server_fd, args.port, (struct sockaddr *)&sock_addr, &sock_addr_len); + } else if (args.replay_jtag) { + transport = transport_t::remote_bitbang; + } + + if (args.dump_jtag) { + jtag_dump_fd.open(args.jtag_dump_path); + if (!jtag_dump_fd.is_open()) { + std::cerr << "Failed to open \"" << args.jtag_dump_path << "\"\n"; + exit(-1); + } + } + + if (args.replay_jtag) { + jtag_replay_fd.open(args.jtag_replay_path); + if (!jtag_replay_fd.is_open()) { + std::cerr << "Failed to open \"" << args.jtag_replay_path << "\"\n"; + exit(-1); + } + } + +} + +// Return true if an exit command was received +bool tb_jtag_state::step(tb_top &tb, mem_io_state *memio, int64_t *cycle_count, bool *timed_out, uint32_t *core_cycles_advanced) { + if (transport == transport_t::none) { + if (core_cycles_advanced) + *core_cycles_advanced = 0; + return false; + } + + if (core_cycles_advanced) + *core_cycles_advanced = 0; + + const bool can_advance_core = transport == transport_t::jtag_vpi + && memio != nullptr + && cycle_count != nullptr + && timed_out != nullptr + && args.vpi_clk_per_tck != 0; + + auto advance_one_core_cycle = [&]() -> bool { + if (!can_advance_core) + return false; + if (args.max_cycles != 0 && *cycle_count >= args.max_cycles) { + *timed_out = true; + return true; + } + memio->step(tb); + tb.step(args, *memio); + ++(*cycle_count); + if (core_cycles_advanced) + ++(*core_cycles_advanced); + if (memio->exit_req) + return true; + if (args.max_cycles != 0 && *cycle_count >= args.max_cycles) { + *timed_out = true; + return true; + } + return false; + }; + + if (transport == transport_t::jtag_vpi) { + // Socket is blocking, but reads are non-blocking via MSG_DONTWAIT so the + // CPU can free-run when openocd is idle. + // + // Important: a recv() syscall every core cycle is very expensive. When + // OpenOCD is idle (no data available), back off for N core cycles + // (configurable). When data is available, process immediately (no + // throughput throttling between packets). + if (vpi_rx_count == 0 && args.vpi_poll_cycles != 0 && vpi_poll_ctr != 0) { + --vpi_poll_ctr; + return false; + } + + // Protocol constants must match OpenOCD's jtag_vpi driver. + static constexpr uint32_t CMD_RESET = 0; + static constexpr uint32_t CMD_TMS_SEQ = 1; + static constexpr uint32_t CMD_SCAN_CHAIN = 2; + static constexpr uint32_t CMD_SCAN_CHAIN_FLIP_TMS = 3; + static constexpr uint32_t CMD_STOP_SIMU = 4; + + static constexpr int OFF_CMD = 0; + static constexpr int OFF_OUT = 4; + static constexpr int OFF_IN = 4 + VPI_XFERT_MAX_SIZE; + static constexpr int OFF_LEN = 4 + VPI_XFERT_MAX_SIZE + VPI_XFERT_MAX_SIZE; + static constexpr int OFF_NB_BITS = OFF_LEN + 4; + + while (vpi_rx_count < VPI_PKT_SIZE) { + ssize_t n = recv(sock_fd, vpi_rxbuf + vpi_rx_count, VPI_PKT_SIZE - vpi_rx_count, MSG_DONTWAIT); + if (n < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // No data available right now. If we don't have a partial + // packet, back off for a while to reduce syscall overhead. + // + // Use an exponential backoff up to vpi_poll_cycles. This + // avoids large fixed delays between back-to-back OpenOCD + // packets (e.g. GDB single-step), while still reducing + // syscall rate when OpenOCD is truly idle. + if (vpi_rx_count == 0 && args.vpi_poll_cycles != 0) { + if (vpi_poll_backoff == 0) { + vpi_poll_backoff = 1; + } else if (vpi_poll_backoff < args.vpi_poll_cycles) { + uint32_t next = vpi_poll_backoff * 2; + if (next < vpi_poll_backoff) + next = args.vpi_poll_cycles; + if (next > args.vpi_poll_cycles) + next = args.vpi_poll_cycles; + vpi_poll_backoff = next; + } + vpi_poll_ctr = vpi_poll_backoff; + } + return false; + } + fprintf(stderr, "jtag_vpi recv failed: %s\n", strerror(errno)); + return true; + } + if (n == 0) { + printf("jtag_vpi connection closed\n"); + ::close(sock_fd); + sock_fd = wait_for_connection_vpi(server_fd, args.vpi_port, (struct sockaddr *)&sock_addr, &sock_addr_len); + int flag = 1; + setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); + vpi_rx_count = 0; + vpi_poll_ctr = 0; + vpi_poll_backoff = 0; + return false; + } + vpi_rx_count += (int)n; + // Any received data means OpenOCD is active again. + vpi_poll_backoff = 0; + } + vpi_poll_ctr = 0; + vpi_poll_backoff = 0; + + const uint32_t cmd = load_le32(vpi_rxbuf + OFF_CMD); + const uint32_t length = load_le32(vpi_rxbuf + OFF_LEN); + const uint32_t nb_bits = load_le32(vpi_rxbuf + OFF_NB_BITS); + const uint8_t *buf_out = vpi_rxbuf + OFF_OUT; + + bool got_exit_cmd = false; + bool core_stop = false; + switch (cmd) { + case CMD_RESET: { + // Best-effort: reset the TAP via TRST, and also provide 5 TMS=1 clocks + // to force Test-Logic-Reset on designs without TRST. + tb.set_trst_n(false); + tb.eval(); + tb.set_trst_n(true); + tb.eval(); + for (int i = 0; i < 5; ++i) { + jtag_clock(tb, true, false); + } + break; + } + case CMD_TMS_SEQ: { + if (length > (uint32_t)VPI_XFERT_MAX_SIZE || nb_bits > (uint32_t)VPI_XFERT_MAX_SIZE * 8 || length * 8 < nb_bits) { + fprintf(stderr, "jtag_vpi: invalid tms_seq length=%u nb_bits=%u\n", length, nb_bits); + got_exit_cmd = true; + break; + } + bool stop_due_to_core = false; + for (uint32_t i = 0; i < nb_bits; ++i) { + const bool tms = get_bit_lsb0(buf_out, i); + jtag_clock(tb, tms, false); + if (can_advance_core) { + for (uint32_t j = 0; j < args.vpi_clk_per_tck; ++j) { + if (advance_one_core_cycle()) { + stop_due_to_core = true; + break; + } + } + } + if (stop_due_to_core) + break; + } + if (stop_due_to_core) { + // Simulation requested stop (timeout/exit); caller will handle. + core_stop = true; + } + break; + } + case CMD_SCAN_CHAIN: + case CMD_SCAN_CHAIN_FLIP_TMS: { + if (length > (uint32_t)VPI_XFERT_MAX_SIZE || nb_bits > (uint32_t)VPI_XFERT_MAX_SIZE * 8 || length * 8 < nb_bits) { + fprintf(stderr, "jtag_vpi: invalid scan length=%u nb_bits=%u\n", length, nb_bits); + got_exit_cmd = true; + break; + } + + uint8_t resp[VPI_PKT_SIZE]; + memset(resp, 0, sizeof(resp)); + store_le32(resp + OFF_CMD, cmd); + store_le32(resp + OFF_LEN, length); + store_le32(resp + OFF_NB_BITS, nb_bits); + + uint8_t *buf_in = resp + OFF_IN; + for (uint32_t i = 0; i < nb_bits; ++i) { + const bool tdi = get_bit_lsb0(buf_out, i); + const bool tms = (cmd == CMD_SCAN_CHAIN_FLIP_TMS) && (i + 1 == nb_bits); + const bool tdo = jtag_clock(tb, tms, tdi); + set_bit_lsb0(buf_in, i, tdo); + } + + if (!send_all(sock_fd, resp, sizeof(resp))) { + fprintf(stderr, "jtag_vpi send failed: %s\n", strerror(errno)); + got_exit_cmd = true; + } + break; + } + case CMD_STOP_SIMU: + printf("OpenOCD requested stop simulation\n"); + got_exit_cmd = true; + break; + default: + fprintf(stderr, "jtag_vpi: unknown cmd=%u\n", cmd); + got_exit_cmd = true; + break; + } + + vpi_rx_count = 0; + if (core_stop) + return false; + return got_exit_cmd; + } + + // If JTAG is enabled, we run the simulator in lockstep with the remote + // bitbang commands, to get more consistent simulation traces. This slows + // down simulation quite a bit compared with normal free-running. + // + // Most bitbang commands complete in one cycle (e.g. TCK/TMS/TDI writes) + // but reads take 0 cycles, step=false. + bool got_exit_cmd = false; + bool step = false; + while (!step) { + if (rx_remaining > 0) { + char c = rxbuf[rx_ptr++]; + --rx_remaining; + + if (c == 'r' || c == 's') { + tb.set_trst_n(true); + step = true; + } else if (c == 't' || c == 'u') { + tb.set_trst_n(false); + } else if (c >= '0' && c <= '7') { + int mask = c - '0'; + tb.set_tck(mask & 0x4); + tb.set_tms(mask & 0x2); + tb.set_tdi(mask & 0x1); + step = true; + } else if (c == 'R') { + if (!args.replay_jtag) { + txbuf[tx_ptr++] = tb.get_tdo() ? '1' : '0'; + if (tx_ptr >= TCP_BUF_SIZE || rx_remaining == 0) { + send(sock_fd, txbuf, tx_ptr, 0); + tx_ptr = 0; + } + } + } else if (c == 'Q') { + printf("OpenOCD sent quit command\n"); + got_exit_cmd = true; + step = true; + } + } else { + // Potentially the last command was not a read command, but + // OpenOCD is still waiting for a last response from its + // last command packet before it sends us any more, so now is + // the time to flush TX. + if (tx_ptr > 0) { + if (!args.replay_jtag) + send(sock_fd, txbuf, tx_ptr, 0); + tx_ptr = 0; + } + rx_ptr = 0; + if (args.replay_jtag) { + rx_remaining = jtag_replay_fd.readsome(rxbuf, TCP_BUF_SIZE); + } else { + rx_remaining = read(sock_fd, &rxbuf, TCP_BUF_SIZE); + } + if (args.dump_jtag && rx_remaining > 0) { + jtag_dump_fd.write(rxbuf, rx_remaining); + } + if (rx_remaining == 0) { + if (args.port == 0) { + // Presumably EOF, so quit. + got_exit_cmd = true; + } else { + // The socket is closed. Wait for another connection. + sock_fd = wait_for_connection_bitbang(server_fd, args.port, (struct sockaddr *)&sock_addr, &sock_addr_len); + } + } + } + } + return got_exit_cmd; +} + +void tb_jtag_state::close() { + if (sock_fd >= 0) + ::close(sock_fd); + if (server_fd >= 0) + ::close(server_fd); + if (args.dump_jtag) { + jtag_dump_fd.close(); + } + if (args.replay_jtag) { + jtag_replay_fd.close(); + } +} diff --git a/vendor/Hazard3/test/sim/tb_common/tb_memio.cpp b/vendor/Hazard3/test/sim/tb_common/tb_memio.cpp new file mode 100644 index 0000000..df0336d --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/tb_memio.cpp @@ -0,0 +1,200 @@ +#include "tb.h" + +#include +#include + +mem_io_state::mem_io_state(const tb_cli_args &args) : uart(args) { + mtime = 0; + mtimecmp[0] = 0; + mtimecmp[1] = 0; + exit_req = false; + exit_code = 0; + monitor_enabled = false; + soft_irq_state = 0; + irq_state = 0; + for (int i = 0; i < N_RESERVATIONS; ++i) { + reservation_valid[i] = false; + reservation_addr[i] = 0; + } + poison_addr = -4u; + mem = new uint8_t[MEM_SIZE]; + for (size_t i = 0; i < MEM_SIZE; ++i) + mem[i] = 0; + + if (args.load_bin) { + std::ifstream fd(args.bin_path, std::ios::binary | std::ios::ate); + if (!fd){ + std::cerr << "Failed to open \"" << args.bin_path << "\"\n"; + exit(-1); + } + std::streamsize bin_size = fd.tellg(); + if (bin_size > MEM_SIZE) { + std::cerr << "Binary file (" << bin_size << " bytes) is larger than memory (" << MEM_SIZE << " bytes)\n"; + exit(-1); + } + fd.seekg(0, std::ios::beg); + fd.read((char*)mem, bin_size); + } +} + +bus_response tb_mem_access(tb_top &tb, mem_io_state &memio, bus_request req) { + bus_response resp; + + // Global monitor. When monitor is not enabled, HEXOKAY is tied high + if (memio.monitor_enabled) { + if (req.excl) { + // Always set reservation on read. Always clear reservation on + // write. On successful write, clear others' matching reservations. + if (req.write) { + resp.exokay = memio.reservation_valid[req.reservation_id] && + memio.reservation_addr[req.reservation_id] == (req.addr & RESERVATION_ADDR_MASK); + memio.reservation_valid[req.reservation_id] = false; + if (resp.exokay) { + for (int i = 0; i < N_RESERVATIONS; ++i) { + if (i == req.reservation_id) + continue; + if (memio.reservation_addr[i] == (req.addr & RESERVATION_ADDR_MASK)) + memio.reservation_valid[i] = false; + } + } + } else { + resp.exokay = true; + memio.reservation_valid[req.reservation_id] = true; + memio.reservation_addr[req.reservation_id] = req.addr & RESERVATION_ADDR_MASK; + } + } else { + resp.exokay = false; + // Non-exclusive write still clears others' reservations + if (req.write) { + for (int i = 0; i < N_RESERVATIONS; ++i) { + if (i == req.reservation_id) + continue; + if (memio.reservation_addr[i] == (req.addr & RESERVATION_ADDR_MASK)) + memio.reservation_valid[i] = false; + } + } + } + } + + + if (req.write) { + if (memio.monitor_enabled && req.excl && !resp.exokay) { + // Failed exclusive write; do nothing + } else if ((req.addr & -4u) == memio.poison_addr) { + resp.err = true; + } else if (req.addr >= MEM_BASE && req.addr <= MEM_BASE + MEM_SIZE - (1u << (int)req.size)) { + unsigned int n_bytes = 1u << (int)req.size; + // Note we are relying on hazard3's byte lane replication + for (unsigned int i = 0; i < n_bytes; ++i) { + memio.mem[req.addr + i - MEM_BASE] = req.wdata >> (8 * i) & 0xffu; + } + } else if (req.addr == IO_BASE + IO_PRINT_CHAR) { + const uint8_t ch = (uint8_t)(req.wdata & 0xffu); + fprintf(tb.logfile, "%c", (char)ch); + memio.uart.write_data(0, ch); + } else if (req.addr == IO_BASE + IO_PRINT_U32) { + fprintf(tb.logfile, "%08x\n", req.wdata); + } else if (req.addr == IO_BASE + IO_EXIT) { + if (!memio.exit_req) { + memio.exit_req = true; + memio.exit_code = req.wdata; + } + } else if (req.addr == IO_BASE + IO_SET_SOFTIRQ) { + memio.soft_irq_state |= req.wdata; + tb.set_soft_irq(memio.soft_irq_state); + } else if (req.addr == IO_BASE + IO_CLR_SOFTIRQ) { + memio.soft_irq_state &= ~req.wdata; + tb.set_soft_irq(memio.soft_irq_state); + } else if (req.addr == IO_BASE + IO_GLOBMON_EN) { + memio.monitor_enabled = req.wdata; + } else if (req.addr == IO_BASE + IO_POISON_ADDR) { + memio.poison_addr = req.wdata & -4u; + } else if (req.addr == IO_BASE + IO_SET_IRQ) { + memio.irq_state |= req.wdata; + tb.set_irq(memio.irq_state); + } else if (req.addr == IO_BASE + IO_CLR_IRQ) { + memio.irq_state &= ~req.wdata; + tb.set_irq(memio.irq_state); + } else if (req.addr == IO_BASE + IO_MTIME) { + memio.mtime = (memio.mtime & 0xffffffff00000000u) | req.wdata; + } else if (req.addr == IO_BASE + IO_MTIMEH) { + memio.mtime = (memio.mtime & 0x00000000ffffffffu) | ((uint64_t)req.wdata << 32); + } else if (req.addr == IO_BASE + IO_MTIMECMP0) { + memio.mtimecmp[0] = (memio.mtimecmp[0] & 0xffffffff00000000u) | req.wdata; + } else if (req.addr == IO_BASE + IO_MTIMECMP0H) { + memio.mtimecmp[0] = (memio.mtimecmp[0] & 0x00000000ffffffffu) | ((uint64_t)req.wdata << 32); + } else if (req.addr == IO_BASE + IO_MTIMECMP1) { + memio.mtimecmp[1] = (memio.mtimecmp[1] & 0xffffffff00000000u) | req.wdata; + } else if (req.addr == IO_BASE + IO_MTIMECMP1H) { + memio.mtimecmp[1] = (memio.mtimecmp[1] & 0x00000000ffffffffu) | ((uint64_t)req.wdata << 32); + } else if (req.addr >= IO_BASE + IO_UART_BASE && req.addr < IO_BASE + IO_UART_BASE + IO_UART_N * IO_UART_STRIDE) { + const uint32_t rel = req.addr - (IO_BASE + IO_UART_BASE); + const uint32_t uart_idx = rel / IO_UART_STRIDE; + const uint32_t reg_off = rel % IO_UART_STRIDE; + if (reg_off == IO_UART_DATA) { + const uint8_t ch = (uint8_t)(req.wdata & 0xffu); + if (uart_idx == 0) + fprintf(tb.logfile, "%c", (char)ch); + memio.uart.write_data(uart_idx, ch); + } else if (reg_off == IO_UART_CTRL) { + memio.uart.write_ctrl(uart_idx, req.wdata); + } else { + resp.err = true; + } + } else { + resp.err = true; + } + } else { + if (req.addr == (memio.poison_addr & -4u)) { + resp.err = true; + } else if (req.addr >= MEM_BASE && req.addr <= MEM_BASE + MEM_SIZE - (1u << (int)req.size)) { + req.addr &= ~0x3u; + req.addr -= MEM_BASE; + resp.rdata = + (uint32_t)memio.mem[req.addr] | + memio.mem[req.addr + 1] << 8 | + memio.mem[req.addr + 2] << 16 | + memio.mem[req.addr + 3] << 24; + } else if (req.addr >= IO_BASE + IO_UART_BASE && req.addr < IO_BASE + IO_UART_BASE + IO_UART_N * IO_UART_STRIDE) { + const uint32_t rel = req.addr - (IO_BASE + IO_UART_BASE); + const uint32_t uart_idx = rel / IO_UART_STRIDE; + const uint32_t reg_off = rel % IO_UART_STRIDE; + if (reg_off == IO_UART_STATUS) { + resp.rdata = memio.uart.read_status(uart_idx); + } else if (reg_off == IO_UART_DATA) { + resp.rdata = memio.uart.read_data(uart_idx); + } else { + resp.err = true; + } + } else if (req.addr == IO_BASE + IO_SET_SOFTIRQ || req.addr == IO_BASE + IO_CLR_SOFTIRQ) { + resp.rdata = memio.soft_irq_state; + } else if (req.addr == IO_BASE + IO_SET_IRQ || req.addr == IO_BASE + IO_CLR_IRQ) { + resp.rdata = memio.irq_state; + } else if (req.addr == IO_BASE + IO_MTIME) { + resp.rdata = memio.mtime; + } else if (req.addr == IO_BASE + IO_MTIMEH) { + resp.rdata = memio.mtime >> 32; + } else if (req.addr == IO_BASE + IO_MTIMECMP0) { + resp.rdata = memio.mtimecmp[0]; + } else if (req.addr == IO_BASE + IO_MTIMECMP0H) { + resp.rdata = memio.mtimecmp[0] >> 32; + } else if (req.addr == IO_BASE + IO_MTIMECMP1) { + resp.rdata = memio.mtimecmp[1]; + } else if (req.addr == IO_BASE + IO_MTIMECMP1H) { + resp.rdata = memio.mtimecmp[1] >> 32; + } else { + resp.err = true; + } + } + if (resp.err) { + resp.exokay = false; + } + return resp; +} + +void mem_io_state::step(tb_top &tb) { + // Default update logic for mtime, mtimecmp + ++mtime; + tb.set_timer_irq((uint8_t)((mtime >= mtimecmp[0]) | (mtime >= mtimecmp[1]) << 1)); + uart.step(); +} diff --git a/vendor/Hazard3/test/sim/tb_common/tb_rand.cpp b/vendor/Hazard3/test/sim/tb_common/tb_rand.cpp new file mode 100644 index 0000000..43b82d2 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/tb_rand.cpp @@ -0,0 +1,70 @@ +#include "tb.h" + +#include + +// TB pseudorandom number generator, using xoroshiro256++ -- original +// copyright notice follows. + +/* Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +/* This is xoshiro256++ 1.0, one of our all-purpose, rock-solid generators. + It has excellent (sub-ns) speed, a state (256 bits) that is large + enough for any parallel application, and it passes all tests we are + aware of. + + For generating just floating-point numbers, xoshiro256+ is even faster. + + The state must be seeded so that it is not everywhere zero. If you have + a 64-bit seed, we suggest to seed a splitmix64 generator and use its + output to fill s. */ + +static inline uint64_t rotl(const uint64_t x, int k) { + return (x << k) | (x >> (64 - k)); +} + +uint32_t tb_top::rand(void) { + const uint64_t result = rotl(rand_state[0] + rand_state[3], 23) + rand_state[0]; + + const uint64_t t = rand_state[1] << 17; + + rand_state[2] ^= rand_state[0]; + rand_state[3] ^= rand_state[1]; + rand_state[1] ^= rand_state[2]; + rand_state[0] ^= rand_state[3]; + + rand_state[2] ^= t; + + rand_state[3] = rotl(rand_state[3], 45); + + return result >> 32; +} + +void tb_top::seed_rand(const uint8_t *data, size_t len) { + // Initial state must not be all-zeroes + for (unsigned int i = 0; i < 4; ++i) { + rand_state[i] = 0xf005ba11u + i; + } + // Pour + stir method: XOR data in one bit at a time, with a xoroshiro + // permutation between each. + for (size_t i = 0; i < 8u * len; ++i) { + if (data[i / 8u] & (1u << (i % 8u))) { + rand_state[0] ^= 1u; + } + (void)rand(); + } +} diff --git a/vendor/Hazard3/test/sim/tb_common/tb_uart.cpp b/vendor/Hazard3/test/sim/tb_common/tb_uart.cpp new file mode 100644 index 0000000..a70ca1b --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_common/tb_uart.cpp @@ -0,0 +1,245 @@ +#include "tb_uart.h" + +#include "tb_cli.h" +#include "tb_constants.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace { + +void close_fd(int &fd) { + if (fd < 0) + return; + (void)::close(fd); + fd = -1; +} + +bool set_nonblocking(int fd) { + int flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) + return false; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0; +} + +int send_flags() { + int flags = 0; +#ifdef MSG_NOSIGNAL + flags |= MSG_NOSIGNAL; +#endif + return flags; +} + +} // namespace + +tb_uart_state::tb_uart_state(const tb_cli_args &args) { + uarts[0].init(args.uart0_port); + uarts[1].init(args.uart1_port); + if (args.uart0_port != 0) + std::cout << "UART0 listening on port " << args.uart0_port << "\n"; + if (args.uart1_port != 0) + std::cout << "UART1 listening on port " << args.uart1_port << "\n"; +} + +tb_uart_state::~tb_uart_state() { + for (uint32_t i = 0; i < N_UARTS; ++i) + uarts[i].close(); +} + +void tb_uart_state::uart::init(uint16_t port_) { + port = port_; + if (port == 0) + return; + + server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd < 0) { + std::cerr << "UART socket creation failed: " << strerror(errno) << "\n"; + exit(-1); + } + + int opt = 1; + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { + std::cerr << "UART setsockopt(SO_REUSEADDR) failed: " << strerror(errno) << "\n"; + exit(-1); + } +#ifdef SO_REUSEPORT + // Best-effort: can fail on some kernels/configs, but is not required. + (void)setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); +#endif + + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + bind_addr.sin_port = htons(port); + if (bind(server_fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) < 0) { + std::cerr << "UART bind failed: " << strerror(errno) << "\n"; + exit(-1); + } + if (listen(server_fd, 1) < 0) { + std::cerr << "UART listen failed: " << strerror(errno) << "\n"; + exit(-1); + } + if (!set_nonblocking(server_fd)) { + std::cerr << "UART fcntl(O_NONBLOCK) failed: " << strerror(errno) << "\n"; + exit(-1); + } +} + +void tb_uart_state::uart::close() { + close_fd(client_fd); + close_fd(server_fd); + port = 0; + overrun = false; + rx_fifo.clear(); + tx_fifo.clear(); +} + +void tb_uart_state::uart::poll_accept(uint32_t uart_idx) { + if (server_fd < 0 || client_fd >= 0) + return; + + sockaddr_in peer {}; + socklen_t peer_len = sizeof(peer); + int fd = accept(server_fd, (struct sockaddr *)&peer, &peer_len); + if (fd < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + return; + std::cerr << "UART" << uart_idx << " accept failed: " << strerror(errno) << "\n"; + return; + } + + client_fd = fd; + (void)set_nonblocking(client_fd); + + // Low-latency local socket traffic helps interactivity. + int flag = 1; + setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); +#ifdef SO_NOSIGPIPE + // Best-effort: avoid SIGPIPE on some platforms. + (void)setsockopt(client_fd, SOL_SOCKET, SO_NOSIGPIPE, &flag, sizeof(flag)); +#endif + + std::cout << "UART" << uart_idx << " connected\n"; +} + +void tb_uart_state::uart::poll_rx(uint32_t uart_idx) { + poll_accept(uart_idx); + if (client_fd < 0) + return; + + uint8_t buf[1024]; + while (true) { + ssize_t n = recv(client_fd, buf, sizeof(buf), MSG_DONTWAIT); + if (n > 0) { + for (ssize_t i = 0; i < n; ++i) { + if (rx_fifo.size() >= rx_capacity) { + overrun = true; + continue; + } + rx_fifo.push_back(buf[i]); + } + continue; + } + if (n == 0) { + close_fd(client_fd); + std::cout << "UART" << uart_idx << " disconnected\n"; + return; + } + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + return; + std::cerr << "UART" << uart_idx << " recv failed: " << strerror(errno) << "\n"; + close_fd(client_fd); + std::cout << "UART" << uart_idx << " disconnected\n"; + return; + } +} + +void tb_uart_state::uart::poll_tx(uint32_t uart_idx) { + poll_accept(uart_idx); + if (client_fd < 0 || tx_fifo.empty()) + return; + + uint8_t buf[1024]; + while (!tx_fifo.empty()) { + const size_t chunk = std::min(tx_fifo.size(), sizeof(buf)); + for (size_t i = 0; i < chunk; ++i) + buf[i] = tx_fifo[i]; + + ssize_t n = send(client_fd, buf, chunk, send_flags()); + if (n > 0) { + for (ssize_t i = 0; i < n; ++i) + tx_fifo.pop_front(); + continue; + } + if (n == 0) + return; + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + return; + std::cerr << "UART" << uart_idx << " send failed: " << strerror(errno) << "\n"; + close_fd(client_fd); + std::cout << "UART" << uart_idx << " disconnected\n"; + return; + } +} + +void tb_uart_state::step() { + for (uint32_t i = 0; i < N_UARTS; ++i) { + if (!uarts[i].tx_fifo.empty()) + uarts[i].poll_tx(i); + } +} + +uint32_t tb_uart_state::read_status(uint32_t uart_idx) { + if (uart_idx >= N_UARTS) + return 0; + uart &u = uarts[uart_idx]; + u.poll_rx(uart_idx); + u.poll_tx(uart_idx); + uint32_t status = TB_UART_STATUS_TX_READY; + if (!u.rx_fifo.empty()) + status |= TB_UART_STATUS_RX_AVAIL; + if (u.connected()) + status |= TB_UART_STATUS_CONNECTED; + if (u.overrun) + status |= TB_UART_STATUS_OVERRUN; + return status; +} + +uint32_t tb_uart_state::read_data(uint32_t uart_idx) { + if (uart_idx >= N_UARTS) + return 0xffffffffu; + uart &u = uarts[uart_idx]; + u.poll_rx(uart_idx); + if (u.rx_fifo.empty()) + return 0xffffffffu; + const uint8_t byte = u.rx_fifo.front(); + u.rx_fifo.pop_front(); + return byte; +} + +void tb_uart_state::write_data(uint32_t uart_idx, uint8_t byte) { + if (uart_idx >= N_UARTS) + return; + uart &u = uarts[uart_idx]; + if (u.server_fd < 0) + return; + if (u.tx_fifo.size() >= u.tx_capacity && !u.tx_fifo.empty()) + u.tx_fifo.pop_front(); + u.tx_fifo.push_back(byte); + u.poll_tx(uart_idx); +} + +void tb_uart_state::write_ctrl(uint32_t uart_idx, uint32_t value) { + if (uart_idx >= N_UARTS) + return; + uart &u = uarts[uart_idx]; + if (value & TB_UART_CTRL_CLR_OVERRUN) + u.overrun = false; +} diff --git a/vendor/Hazard3/test/sim/tb_verilator/.gitignore b/vendor/Hazard3/test/sim/tb_verilator/.gitignore new file mode 100644 index 0000000..8431ff0 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_verilator/.gitignore @@ -0,0 +1,3 @@ +build-* +tb +tb-* diff --git a/vendor/Hazard3/test/sim/tb_verilator/Makefile b/vendor/Hazard3/test/sim/tb_verilator/Makefile new file mode 100644 index 0000000..a7af104 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_verilator/Makefile @@ -0,0 +1,53 @@ +include ../project_paths.mk + +TOP := tb +DOTF := tb.f +CONFIG := default +TBEXEC := $(patsubst %.f,%,$(DOTF)) + +ifneq ($(CONFIG),default) + TBEXEC := $(TBEXEC)-$(CONFIG) +endif + +FILE_LIST := $(shell HDL=$(HDL) $(SCRIPTS)/listfiles ../tb_common/hdl/$(DOTF)) +VINCDIR := $(shell HDL=$(HDL) $(SCRIPTS)/listfiles -f flati ../tb_common/hdl/$(DOTF)) +BUILD_DIR := build-$(patsubst %.f,%,$(DOTF)) +VOBJ_DIR := $(BUILD_DIR)/obj_dir +VLIB := $(VOBJ_DIR)/V$(TOP)__ALL.a +VERILATED_OBJS := $(VOBJ_DIR)/verilated.o $(VOBJ_DIR)/verilated_threads.o + +VERILATOR_ROOT := $(shell verilator --getenv VERILATOR_ROOT) +CXX := g++ +VERILATOR := verilator + +TB_CFILES := tb.cpp $(wildcard ../tb_common/*.cpp) +CINCLUDE := $(VOBJ_DIR) $(VERILATOR_ROOT)/include ../tb_common/include + +.PHONY: clean all lint vcc vlib + +all: $(TBEXEC) +vcc: $(BUILD_DIR)/vcc.touch +vlib: $(BUILD_DIR)/vlib.touch + +$(BUILD_DIR)/vcc.touch: $(FILE_LIST) $(wildcard *.vh) + mkdir -p $(VOBJ_DIR) + $(VERILATOR) --Mdir $(VOBJ_DIR) --cc --top-module $(TOP) $(addprefix -I,$(VINCDIR)) -DCONFIG_HEADER="\"config_$(CONFIG).vh\"" $(FILE_LIST) + touch $@ + +$(BUILD_DIR)/vlib.touch: $(BUILD_DIR)/vcc.touch + # Verilator's archive (Vtb__ALL.a) does not include the run-time objects + # (verilated.o/verilated_threads.o). Build them explicitly so the final + # tb link step succeeds across Verilator versions/distros. + $(MAKE) VERILATOR_ROOT=$(VERILATOR_ROOT) -C $(VOBJ_DIR) -f V$(TOP).mk \ + V$(TOP)__ALL.a verilated.o verilated_threads.o + touch $@ + +clean:: + rm -rf $(BUILD_DIR) $(TBEXEC) + +$(TBEXEC): $(BUILD_DIR)/vlib.touch $(TB_CFILES) + $(CXX) -O3 -std=c++14 $(addprefix -D,$(CDEFINES) $(CDEFINES_$(DOTF))) \ + $(addprefix -I ,$(CINCLUDE)) $(TB_CFILES) $(VLIB) $(VERILATED_OBJS) -o $(TBEXEC) -pthread -latomic + +lint: + $(VERILATOR) --lint-only --top-module $(TOP) $(addprefix -I,$(VINCDIR)) -DCONFIG_HEADER="\"config_$(CONFIG).vh\"" $(FILE_LIST) diff --git a/vendor/Hazard3/test/sim/tb_verilator/openocd-jtag-vpi.cfg b/vendor/Hazard3/test/sim/tb_verilator/openocd-jtag-vpi.cfg new file mode 100644 index 0000000..5d51e26 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_verilator/openocd-jtag-vpi.cfg @@ -0,0 +1,13 @@ +adapter driver jtag_vpi +jtag_vpi set_address 127.0.0.1 +jtag_vpi set_port 5555 +transport select jtag + +set _CHIPNAME hazard3 +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0xdeadbeef +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME riscv -chain-position $_TARGETNAME + +gdb report_data_abort enable +init +halt diff --git a/vendor/Hazard3/test/sim/tb_verilator/tb.cpp b/vendor/Hazard3/test/sim/tb_verilator/tb.cpp new file mode 100644 index 0000000..7fe3349 --- /dev/null +++ b/vendor/Hazard3/test/sim/tb_verilator/tb.cpp @@ -0,0 +1,438 @@ +#include "Vtb.h" +#include "Vtb___024root.h" +#include "verilated.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "tb.h" +#include "tb_cli.h" +#include "tb_gdb.h" +#include "tb_jtag.h" + +class tb_verilator_top: public tb_top { + VerilatedContext *contextp; + Vtb *top; + + // Loop-carried address-phase requests + bus_request req_i; + bus_request req_d; + bool req_i_vld = false; + bool req_d_vld = false; + +public: + tb_verilator_top(const tb_cli_args &parsed_args, int argc, char **argv); + ~tb_verilator_top() {delete top; delete contextp;} + + void step(const tb_cli_args &args, mem_io_state &memio) override; + void eval() override {top->eval();} + + void set_trst_n(bool trst_n) override {top->trst_n = trst_n;} + void set_tck(bool tck) override {top->tck = tck;} + void set_tdi(bool tdi) override {top->tdi = tdi;} + void set_tms(bool tms) override {top->tms = tms;} + bool get_tdo() override {return top->tdo;} + void set_irq(uint32_t mask) override {top->irq = mask;} + void set_soft_irq(uint8_t mask) override {top->soft_irq = mask;} + void set_timer_irq(uint8_t mask) override {top->timer_irq = mask;} + + Vtb___024root *rootp() { return top->rootp; } + + void reset_dut(const tb_cli_args &args, mem_io_state &memio, uint32_t assert_cycles = 8, uint32_t release_cycles = 0); + +}; + +tb_verilator_top::tb_verilator_top(const tb_cli_args &parsed_args, int argc, char **argv): tb_top {parsed_args} { + contextp = new VerilatedContext; + // Verilated context also gets a chance to parse the arguments. Any + // argument prefixed with "+verilator+" is ignored by our tb arg parsing. + contextp->commandArgs(argc, argv); + top = new Vtb{contextp}; + + req_i_vld = false; + req_d_vld = false; + req_i.reservation_id = 0; + req_d.reservation_id = 1; + + // Set bus interfaces to generate good IDLE responses at first + top->i_hready = true; + top->d_hready = true; + + // Reset + initial clock pulse + top->eval(); + top->clk = true; + top->tck = true; + top->eval(); + top->clk = false; + top->tck = false; + top->trst_n = true; + top->rst_n = true; + top->eval(); +} + +void tb_verilator_top::reset_dut(const tb_cli_args &args, mem_io_state &memio, uint32_t assert_cycles, uint32_t release_cycles) { + // Clear any loop-carried bus phase state. + req_i_vld = false; + req_d_vld = false; + + // Ensure a clean default handshake state at reset release. + top->i_hready = true; + top->d_hready = true; + + // Assert global reset (and JTAG TAP reset) for a few core cycles. + top->trst_n = false; + top->rst_n = false; + for (uint32_t i = 0; i < assert_cycles; ++i) + step(args, memio); + + // Release reset. By default, we don't advance any cycles after release so + // the CPU is effectively "reset+halted" until the debugger resumes. + top->trst_n = true; + top->rst_n = true; + for (uint32_t i = 0; i < release_cycles; ++i) + step(args, memio); +} + +void tb_verilator_top::step(const tb_cli_args &args, mem_io_state &memio) { + top->clk = false; + top->eval(); + top->clk = true; + top->eval(); + + // The two bus ports are handled identically. This enables swapping out of + // various `tb.v` hardware integration files containing: + // + // - A single, dual-ported processor (instruction fetch, load/store ports) + // - A single, single-ported processor (instruction fetch + load/store muxed internally) + // - A pair of single-ported processors, for dual-core debug tests + + if (top->d_hready) { + // Clear bus error by default + top->d_hresp = false; + + // Handle current data phase + req_d.wdata = top->d_hwdata; + bus_response resp; + if (req_d_vld) + resp = mem_callback_d(*this, memio, req_d); + else + resp.exokay = !memio.monitor_enabled; + if (resp.err) { + // Phase 1 of error response + top->d_hready = false; + top->d_hresp = true; + } + if (req_d_vld && !req_d.write) { + top->d_hrdata = resp.rdata; + } else { + top->d_hrdata = rand(); + } + top->d_hexokay = resp.exokay; + } else { + // hready=0. Currently this only happens when we're in the first + // phase of an error response, so go to phase 2. + top->d_hready = true; + } + + req_d_vld = false; + if (top->d_hready) { + // Progress current address phase to data phase + req_d_vld = top->d_htrans >> 1; + req_d.write = top->d_hwrite; + req_d.size = (bus_size_t)top->d_hsize; + req_d.addr = top->d_haddr; + req_d.excl = top->d_hexcl; + } + + if (top->i_hready) { + top->i_hresp = false; + + req_i.wdata = top->i_hwdata; + bus_response resp; + if (req_i_vld) + resp = mem_callback_i(*this, memio, req_i); + else + resp.exokay = !memio.monitor_enabled; + if (resp.err) { + // Phase 1 of error response + top->i_hready = false; + top->i_hresp = true; + } + if (req_i_vld && !req_i.write) { + top->i_hrdata = resp.rdata; + } else { + top->i_hrdata = rand(); + } + top->i_hexokay = resp.exokay; + } else { + // hready=0. Currently this only happens when we're in the first + // phase of an error response, so go to phase 2. + top->i_hready = true; + } + + req_i_vld = false; + if (top->i_hready) { + // Progress current address phase to data phase + req_i_vld = top->i_htrans >> 1; + req_i.write = top->i_hwrite; + req_i.size = (bus_size_t)top->i_hsize; + req_i.addr = top->i_haddr; + req_i.excl = top->i_hexcl; + } +} + +int main(int argc, char **argv) { + tb_cli_args args; + tb_parse_args(argc, argv, args); + + VerilatedContext *contextp = new VerilatedContext; + contextp->commandArgs(argc, argv); + + tb_jtag_state jtag(args); + mem_io_state memio(args); + tb_verilator_top tb(args, argc, argv); + + bool timed_out = false; + int64_t cycle = 0; + + if (args.gdb_port != 0) { + struct verilator_gdb_target final : tb_gdb_target { + tb_verilator_top &tb; + mem_io_state &memio; + const tb_cli_args &args; + Vtb___024root *root; + int64_t &cycle; + bool &timed_out; + + verilator_gdb_target(tb_verilator_top &tb_, mem_io_state &memio_, const tb_cli_args &args_, int64_t &cycle_, bool &timed_out_) + : tb(tb_), memio(memio_), args(args_), root(tb_.rootp()), cycle(cycle_), timed_out(timed_out_) {} + + uint32_t read_reg(uint32_t regno) override { + if (regno == 0) + return 0; + if (regno < 32) { + // GDB single-step stops with decode PC already advanced to the + // next instruction, while the just-computed register result can + // still be sitting in the M stage for one cycle before it is + // committed into the architectural register file. Overlay the + // pending writeback so the debugger sees a coherent post-step + // snapshot. + if (root->tb__DOT__cpu__DOT__core__DOT__m_reg_wen_if_nonzero && + regno == root->tb__DOT__cpu__DOT__core__DOT__xm_rd) { + return (uint32_t)root->tb__DOT__cpu__DOT__core__DOT__m_result; + } + return (uint32_t)root->tb__DOT__cpu__DOT__core__DOT__regs__DOT__real_dualport_reset__DOT__mem[regno]; + } + if (regno == 32) { + return (uint32_t)root->tb__DOT__cpu__DOT__core__DOT__decode_u__DOT__pc; + } + return 0; + } + + void write_reg(uint32_t regno, uint32_t value) override { + if (regno == 0) + return; + if (regno < 32) { + root->tb__DOT__cpu__DOT__core__DOT__regs__DOT__real_dualport_reset__DOT__mem[regno] = value; + tb.eval(); + return; + } + if (regno == 32) { + root->tb__DOT__cpu__DOT__core__DOT__decode_u__DOT__pc = value; + tb.eval(); + return; + } + } + + bool read_mem(uint32_t addr, uint8_t *dst, size_t len) override { + if (len == 0) + return true; + if (addr < MEM_BASE || addr + len > MEM_BASE + MEM_SIZE) + return false; + memcpy(dst, memio.mem + (addr - MEM_BASE), len); + return true; + } + + bool write_mem(uint32_t addr, const uint8_t *src, size_t len) override { + if (len == 0) + return true; + if (addr < MEM_BASE || addr + len > MEM_BASE + MEM_SIZE) + return false; + memcpy(memio.mem + (addr - MEM_BASE), src, len); + return true; + } + + tb_gdb_run_result step_instruction() override { + while (true) { + if (args.max_cycles != 0 && cycle >= args.max_cycles) { + timed_out = true; + return tb_gdb_run_result::timed_out; + } + + const bool will_retire = root->tb__DOT__cpu__DOT__core__DOT__x_instr_ret; + memio.step(tb); + tb.step(args, memio); + ++cycle; + + if (memio.exit_req) + return tb_gdb_run_result::exited; + if (args.max_cycles != 0 && cycle >= args.max_cycles) { + timed_out = true; + return tb_gdb_run_result::timed_out; + } + if (will_retire) + return tb_gdb_run_result::ok; + } + } + + uint32_t exit_code() const override { return memio.exit_code; } + + bool monitor_cmd(const std::string &cmd, std::string &out_console) override { + auto is_space = [](unsigned char c) { return std::isspace(c) != 0; }; + + // Normalise whitespace and case to make matching user-friendly. + size_t start = 0; + while (start < cmd.size() && is_space((unsigned char)cmd[start])) + ++start; + size_t end = cmd.size(); + while (end > start && is_space((unsigned char)cmd[end - 1])) + --end; + + std::string norm; + norm.reserve(end - start); + bool in_space = false; + for (size_t i = start; i < end; ++i) { + const unsigned char c = (unsigned char)cmd[i]; + if (is_space(c)) { + in_space = true; + continue; + } + if (in_space && !norm.empty()) + norm.push_back(' '); + in_space = false; + norm.push_back((char)std::tolower(c)); + } + + if (norm == "reset halt" || norm == "reset") { + // Clear external testbench state (but keep RAM contents). + memio.mtime = 0; + memio.mtimecmp[0] = 0; + memio.mtimecmp[1] = 0; + memio.exit_req = false; + memio.exit_code = 0; + memio.monitor_enabled = false; + memio.poison_addr = -4u; + memio.soft_irq_state = 0; + memio.irq_state = 0; + for (int i = 0; i < N_RESERVATIONS; ++i) { + memio.reservation_valid[i] = false; + memio.reservation_addr[i] = 0; + } + for (uint32_t i = 0; i < tb_uart_state::N_UARTS; ++i) { + memio.uart.uarts[i].overrun = false; + memio.uart.uarts[i].rx_fifo.clear(); + memio.uart.uarts[i].tx_fifo.clear(); + } + tb.set_soft_irq(0); + tb.set_timer_irq(0); + tb.set_irq(0); + + cycle = 0; + timed_out = false; + + tb.reset_dut(args, memio); + out_console = "reset halt\n"; + return true; + } + + return false; + } + }; + + verilator_gdb_target target(tb, memio, args, cycle, timed_out); + while (!memio.exit_req && !timed_out) { + tb_gdb_server gdb(args.gdb_port, target); + const tb_gdb_run_result r = gdb.serve(); + if (r == tb_gdb_run_result::error) + return 1; + } + } else { + while (args.max_cycles == 0 || cycle < args.max_cycles) { + uint32_t core_cycles_advanced = 0; + bool jtag_exit_cmd = jtag.step(tb, &memio, &cycle, &timed_out, &core_cycles_advanced); + + if (memio.exit_req) { + fprintf(tb.logfile, "CPU requested halt. Exit code %d\n", memio.exit_code); + fprintf(tb.logfile, "Ran for " I64_FMT " cycles\n", cycle); + break; + } + if (timed_out) { + fprintf(tb.logfile, "Max cycles reached\n"); + break; + } + if (jtag_exit_cmd) + break; + + // If the JTAG handler didn't advance the core clock, free-run for one cycle. + if (core_cycles_advanced == 0) { + memio.step(tb); + tb.step(args, memio); + ++cycle; + if (memio.exit_req) { + fprintf(tb.logfile, "CPU requested halt. Exit code %d\n", memio.exit_code); + fprintf(tb.logfile, "Ran for " I64_FMT " cycles\n", cycle); + break; + } + if (args.max_cycles != 0 && cycle >= args.max_cycles) { + fprintf(tb.logfile, "Max cycles reached\n"); + timed_out = true; + break; + } + } + } + } + + jtag.close(); + + for (auto r : args.dump_ranges) { + fprintf(tb.logfile, "Dumping memory from %08x to %08x:\n", r.first, r.second); + for (int i = 0; i < r.second - r.first; ++i) + fprintf(tb.logfile, "%02x%c", memio.mem[r.first + i - MEM_BASE], i % 16 == 15 ? '\n' : ' '); + fprintf(tb.logfile, "\n"); + } + + if (args.sig_path != "") { + FILE *sigfile = fopen(args.sig_path.c_str(), "wb"); + for (auto r : args.dump_ranges) { + for (uint32_t i = 0; i < r.second - r.first; i += 4) { + fprintf( + sigfile, + "%02x%02x%02x%02x\n", + memio.mem[r.first + i + 3 - MEM_BASE], + memio.mem[r.first + i + 2 - MEM_BASE], + memio.mem[r.first + i + 1 - MEM_BASE], + memio.mem[r.first + i + 0 - MEM_BASE] + ); + } + } + fclose(sigfile); + } + + if (args.propagate_return_code && timed_out) { + return -1; + } else if (args.propagate_return_code && memio.exit_req) { + return memio.exit_code; + } else { + return 0; + } +} + +// Needed on MacOS, haven't looked into why +double sc_time_stamp() { + return 0.0; +} diff --git a/vendor/lab-runtime/memops.c b/vendor/lab-runtime/memops.c new file mode 100644 index 0000000..3d71197 --- /dev/null +++ b/vendor/lab-runtime/memops.c @@ -0,0 +1,24 @@ +#include + +void *memcpy(void *dst, const void *src, size_t n) +{ + unsigned char *d = dst; + const unsigned char *s = src; + + for (size_t i = 0; i < n; ++i) { + d[i] = s[i]; + } + + return dst; +} + +void *memset(void *dst, int c, size_t n) +{ + unsigned char *d = dst; + + for (size_t i = 0; i < n; ++i) { + d[i] = (unsigned char)c; + } + + return dst; +} diff --git a/web/app.css b/web/app.css new file mode 100644 index 0000000..9e9591e --- /dev/null +++ b/web/app.css @@ -0,0 +1 @@ +.paper{display:flex;flex-direction:column;gap:3.3cm}.paper>.sheet,.paper>.sheet-spread>.sheet{width:210mm;height:297mm;min-height:297mm;max-height:297mm;margin:0 auto;overflow:hidden}.paper>.sheet.sheet--landscape,.paper>.sheet-spread>.sheet.sheet--landscape{width:297mm;min-width:297mm;max-width:297mm;height:210mm;min-height:210mm;max-height:210mm}.paper>.sheet>.sheet-content,.paper>.sheet-spread>.sheet>.sheet-content{height:297mm;min-height:297mm;max-height:297mm;overflow-x:hidden;overflow-y:hidden}.paper>.sheet.sheet--landscape>.sheet-content,.paper>.sheet-spread>.sheet.sheet--landscape>.sheet-content{height:210mm;min-height:210mm;max-height:210mm}.sheet-spread{display:contents}.paper.is-diagram-spread .sheet-spread{display:grid;grid-template-columns:repeat(var(--spread-columns, 1),max-content);width:max-content;max-width:none;margin:0 auto;align-items:flex-start;gap:2mm;zoom:var(--spread-fit-scale, 1)}.paper.is-diagram-spread .sheet-spread>.sheet{flex:0 0 auto}.viewer-chrome{position:sticky;top:0;z-index:80;width:100%}.viewer-action-toast{position:absolute;z-index:12;right:9px;bottom:9px;max-width:min(720px,calc(100vw - 18px));box-sizing:border-box;overflow:hidden;border:1px solid #42b8e7;border-radius:3px;background:#102c38ee;color:#dff7ff;padding:5px 9px;box-shadow:0 4px 14px #0007;text-overflow:ellipsis;white-space:nowrap;font:750 10px/1.25 ui-monospace,monospace}.viewer-chrome .topbar{position:sticky;top:0;z-index:90}.viewpoint-bar{display:grid;grid-template-columns:repeat(8,minmax(0,1fr));gap:1px;min-height:31px;border-top:1px solid #9aa8ae;border-bottom:1px solid #718087;background:#718087;box-shadow:0 2px 5px #0002}.viewpoint-bar button{position:relative;display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:5px;min-width:0;border:0;background:#f7f9fa;color:#1f3038;padding:5px 7px;text-align:left;cursor:pointer}.viewpoint-bar button:hover,.viewpoint-bar button:focus-visible{z-index:1;background:#e9f3f7;outline:2px solid #287495;outline-offset:-2px}.viewpoint-bar button[aria-current=location]{background:#dcecf3;box-shadow:inset 0 -3px #167ca4}.viewpoint-bar button>span{color:#1b6785;font:850 10px/1 ui-monospace,monospace}.viewpoint-bar button>strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:750 10px/1.1 system-ui,sans-serif}.viewpoint-bar button.is-unavailable{background:#eef0f1;color:#5d686d;cursor:not-allowed}.viewpoint-bar button.is-unavailable>span{color:#68747a}.viewpoint-bar button.is-unavailable>small{border:1px solid #9da7ab;border-radius:2px;padding:1px 3px;font:750 7px/1 ui-monospace,monospace}.card-library-toggle{display:inline-flex;align-items:center;flex:none;height:28px;gap:6px;border:1px solid #6e7d84;border-radius:3px;background:#f6f8f9;color:#26373f;padding:2px 8px;font:750 11px/1 ui-monospace,monospace;cursor:pointer}.card-library-toggle:hover,.card-library-toggle:focus-visible{border-color:#1d7799;background:#edf7fa;outline:none}.card-library-toggle.is-active{border-color:#126d8f;background:#174f67;color:#fff}.card-library-toggle-icon{font:700 16px/1 system-ui,sans-serif}.card-library-toggle kbd{color:#267697;font:inherit}.card-library-toggle.is-active kbd{color:#aee9ff}:root{--side-panel-top: 36px;--side-panel-width: min(960px, calc(100vw - 28px) );--side-panel-trigger-length: 56px}.side-panel-dock{position:fixed;z-index:240;top:var(--side-panel-top);bottom:0;width:0;pointer-events:none}.side-panel-dock--left{left:0}.side-panel-dock--right{right:0}.side-panel-rail{position:fixed;z-index:243;top:calc(var(--side-panel-top) + 8px);display:flex;width:13px;flex-direction:column;gap:10px;pointer-events:auto}.side-panel-dock--left .side-panel-rail{left:0}.side-panel-dock--right .side-panel-rail{right:0}.side-panel-trigger{--side-panel-accent: #167ca4;position:relative;display:grid;width:13px;height:var(--side-panel-trigger-length);place-items:center;border:0;border-radius:0;background:transparent;padding:0;cursor:pointer}.side-panel-trigger:before{content:"";display:block;width:3px;height:calc(var(--side-panel-trigger-length) - 8px);border-radius:999px;background:var(--side-panel-accent);box-shadow:0 0 0 1px #ffffff73,0 3px 10px -5px var(--side-panel-accent);opacity:.78;transition:width .14s ease,opacity .14s ease,box-shadow .14s ease}.side-panel-trigger:hover:before,.side-panel-trigger:focus-visible:before,.side-panel-trigger.is-open:before{width:4px;opacity:1}.side-panel-trigger.is-pinned:before{width:5px;box-shadow:0 0 0 1px #ffffff9e,0 3px 12px -4px var(--side-panel-accent)}.side-panel-trigger:focus-visible{outline:2px solid var(--side-panel-accent);outline-offset:-2px}.side-panel-trigger>span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}.side-panel{position:fixed;z-index:241;top:var(--side-panel-top);bottom:0;display:flex;width:var(--side-panel-width);max-width:calc(100vw - 20px);box-sizing:border-box;flex-direction:column;overflow:hidden;border-color:#819098;background:#f7f9fa;color:#17252c;pointer-events:none;transition:transform .18s cubic-bezier(.22,.78,.24,1);will-change:transform}.side-panel-dock--left .side-panel{left:0;border-right:1px solid #819098;box-shadow:15px 0 34px -24px #101b218c;transform:translate(calc(-100% - 14px))}.side-panel-dock--right .side-panel{right:0;border-left:1px solid #819098;box-shadow:-15px 0 34px -24px #101b218c;transform:translate(calc(100% + 14px))}.side-panel.is-open{transform:translate(0);pointer-events:auto}.side-panel:after{content:"";position:absolute;top:10px;width:4px;height:48px;border-radius:999px;background:var(--side-panel-accent);pointer-events:none}.side-panel-dock--left .side-panel:after{right:-2px}.side-panel-dock--right .side-panel:after{left:-2px}.side-panel-header{display:flex;min-height:38px;box-sizing:border-box;align-items:center;justify-content:space-between;gap:10px;border-bottom:1px solid #aebbc1;background:#e7edef;padding:4px 7px 4px 10px}.side-panel-header>div:first-child{display:flex;min-width:0;align-items:baseline;gap:7px}.side-panel-header small{color:#52666f;font:600 16px/1 ui-monospace,monospace;letter-spacing:.08em}.side-panel-header strong{overflow:hidden;color:#11191d;text-overflow:ellipsis;white-space:nowrap;font:600 16px/1.2 system-ui,sans-serif}.side-panel-actions{display:flex;flex:none;gap:4px}.side-panel-actions button{display:grid;min-width:38px;height:24px;place-items:center;border:1px solid #8b999f;border-radius:3px;background:#fff;color:#26373f;padding:0 6px;font:650 16px/1 ui-monospace,monospace;cursor:pointer}.side-panel-actions button:last-child{min-width:24px;padding:0;font-size:18px}.side-panel-actions button:hover,.side-panel-actions button:focus-visible,.side-panel-actions button.is-pinned{border-color:var(--side-panel-accent);background:#edf7fa;outline:none}.side-panel-body,.card-library-panel{display:flex;min-height:0;flex:1;flex-direction:column;overflow:hidden}.card-library-header{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;padding:18px 18px 15px;border-bottom:1px solid #bdc9ce;background:#e8eef1}.card-library-header small{color:#52666f;font:600 16px/1.25 ui-monospace,monospace;letter-spacing:.09em;text-transform:uppercase}.card-library-header h2{margin:4px 0 2px;font:750 22px/1.15 system-ui,sans-serif}.card-library-header p{margin:0;color:#607078;font:500 16px/1.35 system-ui,sans-serif}.card-library-header>button{display:grid;width:34px;height:34px;place-items:center;flex:none;border:1px solid #77878e;border-radius:3px;background:#fff;color:#25353c;padding:0;font:25px/1 system-ui,sans-serif;cursor:pointer}.card-library-header>button:hover,.card-library-header>button:focus-visible{border-color:#126d8f;background:#edf7fa;outline:2px solid #85c5dc;outline-offset:1px}.card-library-tree{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:6px 0 18px;scrollbar-color:#8a9aa1 #edf1f3}.card-library-tree details{border:0}.card-library-tree summary{display:grid;grid-template-columns:14px minmax(0,1fr) auto;align-items:center;gap:6px;min-height:30px;box-sizing:border-box;padding:5px 10px 5px 4px;list-style:none;cursor:pointer;user-select:none}.card-library-tree summary::-webkit-details-marker{display:none}.card-library-tree summary:hover,.card-library-tree summary:focus-visible{background:#eaf1f4;outline:none}.card-library-branch{color:#25363d;font:16px/1 ui-monospace,monospace;transform:rotate(-90deg);transition:transform .12s ease}.card-library-tree details[open]>summary .card-library-branch{transform:rotate(0)}.card-library-tree summary strong,.card-library-tree summary small{display:block}.card-library-tree summary strong{color:#070a0b;font:550 16px/1.3 system-ui,sans-serif}.card-library-tree summary small{margin-top:2px;color:#303b40;font:500 16px/1.25 ui-monospace,monospace;letter-spacing:.04em}.card-library-tree summary output{min-width:22px;color:#26363d;padding:0 2px;text-align:center;font:550 16px/1.2 ui-monospace,monospace}.card-library-subject>summary{min-height:42px;border-bottom:1px solid #9eacb2;background:#dfe8ec;padding:7px 14px}.card-library-subject>summary:hover,.card-library-subject>summary:focus-visible{background:#d5e1e5}.card-library-subject>summary strong{color:#000;font:600 16px/1.3 system-ui,sans-serif}.card-library-subject>summary small{color:#28353a;font:500 16px/1.25 ui-monospace,monospace}.card-library-level-list{margin-left:18px;border-left:1px solid #8e9da4}.card-library-level>summary{min-height:38px;border-bottom:1px solid #cbd4d8;padding-left:8px}.card-library-level>summary strong{color:#000;font:550 16px/1.3 system-ui,sans-serif}.card-library-series-list{margin-left:18px;border-left:1px solid #a5b1b6;padding:2px 0 6px}.card-library-series>summary{min-height:34px;padding-left:8px}.card-library-series>summary strong{color:#000;font:550 16px/1.3 system-ui,sans-serif}.card-library-series>summary small{color:#26343a;font:500 16px/1.25 ui-monospace,monospace}.card-library-cards,.card-library-tasks{list-style:none;margin-top:0;margin-bottom:2px;padding:0}.card-library-cards{margin-left:18px;border-left:1px solid #bac3c7}.card-library-card-entry{margin:0}.card-library-card{display:grid;grid-template-columns:19px minmax(0,1fr) auto;align-items:start;gap:5px;min-height:38px;box-sizing:border-box;border:0;border-left:3px solid transparent;color:#000;padding:5px 8px 5px 3px;text-decoration:none}.card-library-node{color:#536168;font:16px/1.35 ui-monospace,monospace}.card-library-card-copy,.card-library-card-copy strong,.card-library-card-copy>span{display:block;min-width:0}.card-library-card-copy strong{color:#000;font:550 16px/1.3 ui-monospace,monospace}.card-library-card-copy>span{margin-top:2px;color:#0b0d0e;font:500 16px/1.35 system-ui,sans-serif;white-space:normal}.card-library-card-meta{display:flex;align-items:flex-end;flex-direction:column;gap:2px}.card-library-card-meta small{color:#26343a;font:500 16px/1.25 ui-monospace,monospace}.card-library-card.is-available{cursor:pointer}.card-library-card.is-available:hover,.card-library-card.is-available:focus-visible{background:#eaf5f8;outline:none}.card-library-card.is-current{border-left-color:#16769b;background:#dff1f7}.card-library-card.is-current:hover,.card-library-card.is-current:focus-visible{background:#cfeaf3;outline:1px solid #5ba9c4;outline-offset:-1px}.card-library-card.is-unavailable{color:#000}.card-library-card.is-unavailable .card-library-card-copy strong,.card-library-card.is-unavailable .card-library-card-meta small{color:#1d292e}.card-library-card.is-unavailable .card-library-card-copy>span{color:#0b0d0e}.card-library-tasks{margin-left:36px;border-left:1px solid #c3cacc}.card-library-tasks li>a,.card-library-tasks li>span{display:grid;grid-template-columns:19px auto minmax(0,1fr);gap:5px;align-items:start;color:#000;padding:4px 8px 5px 3px;text-decoration:none}.card-library-tasks li>a:hover,.card-library-tasks li>a:focus-visible{background:#eaf5f8;outline:none}.card-library-tasks strong{color:#000;font:550 16px/1.35 ui-monospace,monospace}.card-library-tasks li>a>span:last-child,.card-library-tasks li>span>span:last-child{color:#111;font:500 16px/1.35 system-ui,sans-serif}.card-library-footer{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:10px 16px;border-top:1px solid #bdc9ce;background:#edf1f3;color:#5a6a72;font:500 16px/1.3 system-ui,sans-serif}.card-library-footer span{display:inline-flex;align-items:center;gap:4px}.card-library-footer i{width:7px;height:7px;border-radius:50%;background:#929da2}.card-library-footer i.is-online{background:#26815b}.card-library-footer kbd{margin-left:auto;border:1px solid #a2afb5;border-radius:2px;background:#fff;padding:2px 5px;font:600 16px/1 ui-monospace,monospace}.side-toc,.side-progress{min-height:0;flex:1;overflow-y:auto;background:#fff;color:#11191d}.side-toc>header,.side-progress>header{padding:16px 18px 13px;border-bottom:1px solid #bdc9ce;background:#f0f4f5}.side-toc>header small,.side-progress>header small{color:#53666f;font:750 9px/1 ui-monospace,monospace;letter-spacing:.08em}.side-toc>header h2,.side-progress>header h2{margin:4px 0 0;color:#10181c;font:750 21px/1.15 system-ui,sans-serif}.side-toc ol{margin:0;padding:7px 0 20px;list-style:none}.side-toc li{border-bottom:1px solid #e2e7e9}.side-toc a{display:grid;grid-template-columns:34px minmax(0,1fr);gap:9px;align-items:baseline;padding:10px 16px;color:#11191d;text-decoration:none}.side-toc a:hover,.side-toc a:focus-visible{background:#eaf5f8;outline:none}.side-toc a span{color:#267493;font:800 10px/1 ui-monospace,monospace}.side-toc a strong{font:650 13px/1.3 system-ui,sans-serif}.side-progress-summary{display:flex;align-items:center;justify-content:space-between;gap:16px;margin:16px;border:1px solid #b6c2c7;background:#f4f7f8;padding:14px;font:650 13px/1.3 system-ui,sans-serif}.side-progress-summary strong{color:#1d617b;font:850 20px/1 ui-monospace,monospace}.side-progress-links{display:grid;gap:8px;padding:0 16px 18px}.side-progress-links a{border:1px solid #87979e;border-radius:3px;background:#fff;color:#174f67;padding:10px 12px;text-decoration:none;font:700 12px/1.2 system-ui,sans-serif}.side-progress-links a:hover,.side-progress-links a:focus-visible{border-color:#167ca4;background:#edf7fa;outline:none}.side-progress-error{margin:0 16px 14px;border-left:3px solid #a52d2d;background:#fff1f1;color:#7d1f1f;padding:8px 10px;font:12px/1.35 system-ui,sans-serif}.classroom-panel{display:flex;min-height:0;flex:1;flex-direction:column;overflow:hidden;background:#f7f9fa;color:#11191d}.classroom-treegrid{min-height:0;flex:1;overflow:auto;margin:8px;border:1px solid rgb(31 35 40 / 18%);border-radius:6px;background:#fff;scrollbar-color:#93a2a8 #edf1f2;scrollbar-width:thin}.classroom-treegrid-row{display:grid;min-width:920px;box-sizing:border-box;grid-template-columns:minmax(410px,1fr) 100px 150px 150px 110px;align-items:center;border-bottom:1px solid rgb(31 35 40 / 8%);color:#273941;font:500 16px/1.25 ui-monospace,monospace}.classroom-treegrid-row>[role=gridcell],.classroom-treegrid-row>[role=columnheader]{min-width:0;overflow:hidden;padding:8px;text-overflow:ellipsis;white-space:nowrap}.classroom-treegrid-row>[role=gridcell]+[role=gridcell],.classroom-treegrid-row>[role=columnheader]+[role=columnheader]{border-left:1px solid rgb(31 35 40 / 8%)}.classroom-treegrid-head{position:sticky;z-index:2;top:0;border-bottom-color:#1f23282e;background:#eef1f2;color:#3e525b;font-weight:650;letter-spacing:.04em}.classroom-node-row:hover{background:#1f23280a}.classroom-node-row.is-class{background:#dfe8e5}.classroom-node-row.is-student{background:#f8faf9}.classroom-node-row.is-task{background:#edf3f5}.classroom-node-row.is-block,.classroom-node-row.is-exercises{background:#f5f7f8}.classroom-node-row.is-phase{background:#fafbfb}.classroom-node-title{display:flex;align-items:center;gap:6px;padding-left:calc(8px + (var(--classroom-depth, 0) * 20px))!important;font-family:system-ui,sans-serif}.classroom-node-title strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:16px;font-weight:500}.classroom-node-row.is-class .classroom-node-title strong,.classroom-node-row.is-student .classroom-node-title strong,.classroom-node-row.is-task .classroom-node-title strong{font-weight:650}.classroom-expander{display:inline-grid;width:24px;height:24px;flex:none;place-items:center;border:0;background:transparent;color:#405961;padding:0;font:18px/1 ui-monospace,monospace;cursor:pointer}.classroom-expander:hover,.classroom-expander:focus-visible{background:#165d6b1a;color:#165d6b;outline:1px solid rgb(22 93 107 / 28%)}.classroom-expander.is-empty{cursor:default}.classroom-status,.classroom-sync,.classroom-visit,.classroom-commit{display:inline-flex;min-width:92px;box-sizing:border-box;align-items:center;justify-content:center;border:1px solid;border-radius:5px;padding:4px 6px;font:550 16px/1 ui-monospace,monospace;letter-spacing:.015em}.classroom-status[data-status=online]{border-color:#6b9a87;background:#e7f4ee;color:#155e43}.classroom-status[data-status=away]{border-color:#baa06a;background:#fff5d9;color:#765510}.classroom-status[data-status=offline]{border-color:#a5afb3;background:#f0f2f3;color:#596970}.classroom-status[data-status=error]{border-color:#bd7b7b;background:#fff0f0;color:#8c2929}.classroom-sync[data-sync=follow]{border-color:#6c98aa;background:#e8f3f7;color:#155c77}.classroom-sync[data-sync=paused]{border-color:#baa06a;background:#fff6df;color:#765510}.classroom-sync[data-sync=local]{border-color:#9a86ae;background:#f4eef9;color:#65427c}.classroom-visit[data-visit=present],.classroom-commit[data-commit=committed]{border-color:#6b9a87;background:#e7f4ee;color:#155e43}.classroom-visit[data-visit=missed],.classroom-commit[data-commit=missing]{border-color:#bd7b7b;background:#fff0f0;color:#8c2929}.classroom-visit[data-visit=pending],.classroom-commit[data-commit=working]{border-color:#baa06a;background:#fff6df;color:#765510}.classroom-legend{display:flex;flex:none;flex-wrap:wrap;gap:7px 12px;border-top:1px solid #aebbc1;background:#edf1f2;padding:9px 11px;color:#485c65;font:500 16px/1.35 system-ui,sans-serif}.classroom-legend span{display:inline-flex;align-items:center;gap:4px}.classroom-legend i{width:12px;height:12px;border:1px solid #73858c;border-radius:50%;background:#aeb7bb}.classroom-legend i[data-status=online]{border-color:#3e7c63;background:#45a27b}.classroom-legend i[data-status=away]{border-color:#a0782d;background:#dcae51}.classroom-legend i[data-status=offline]{border-color:#859399;background:#b6c0c4}.classroom-legend b{color:#65427c;font:600 16px/1 ui-monospace,monospace}.viewer-chrome.is-nvim-fit{display:flex;width:100%;height:100vh;height:100dvh;max-height:100dvh;flex-direction:column;overflow:hidden;background:#11171a}.viewer-chrome.is-nvim-fit .topbar,.viewer-chrome.is-nvim-fit .viewpoint-bar,.viewer-chrome.is-nvim-fit .allocator-panel{flex:none}.viewer-chrome.is-nvim-fit .nvim-panel{display:flex;min-height:0;flex:1;flex-direction:column}.viewer-chrome.is-nvim-fit .nvim-screen-scroll{min-height:0;height:auto!important;flex:1}.viewer-chrome.is-nvim-fit .nvim-resize-handle{display:none}.viewer-chrome.is-nvim-fit .nvim-screen-stage{width:100%;height:100%;margin:0;zoom:1}.viewer-chrome.is-nvim-fit .nvim-screen-content{right:1mm;overflow:hidden;scrollbar-width:none}.viewer-chrome.is-nvim-fit .nvim-screen-grid{display:flex;width:100%;min-height:0;height:100%;align-items:center;justify-content:center}.viewer-chrome.is-nvim-fit .nvim-screen{width:auto!important;height:auto!important;max-width:100%;max-height:100%}.viewer-chrome.is-nvim-fit .nvim-screen-content::-webkit-scrollbar{display:none}.topbar-nvim-section{display:flex;align-items:center;min-width:0;gap:6px}.nvim-toolbar-boundary{flex:none;color:#6f7d83;font:700 14px/1 ui-monospace,monospace}.nvim-toolbar-context{flex:none;width:5ch;color:#263238;font:800 12px/1 ui-monospace,monospace;letter-spacing:.08em;text-align:center}.nvim-toolbar-actions{display:flex;align-items:center;gap:3px}.nvim-toolbar-button{display:inline-flex;align-items:center}.nvim-toolbar-key{flex:0 0 2ch;color:#267697;font:inherit;text-align:center}.nvim-toolbar-label{flex:0 0 5ch;max-width:5ch;overflow:hidden;text-align:left;white-space:nowrap}.topbar-nvim-meta{display:flex;align-items:baseline;min-width:0;max-width:min(44vw,720px);gap:6px}.topbar-nvim-meta strong,.topbar-nvim-meta small{overflow:auto;text-overflow:ellipsis;white-space:nowrap}.topbar-nvim-meta strong{color:#263238;font:700 12px/1.25 system-ui,sans-serif}.topbar-nvim-meta small{color:#66747a;font:500 10px/1.25 system-ui,sans-serif}.viewer-nvim-toggle{border:1px solid #68747a;border-radius:3px;background:#fff;color:#263238;padding:2px 8px;font:650 16px/1.3 system-ui,sans-serif;cursor:pointer}.viewer-nvim-toggle[aria-pressed=true]{border-color:#267697;background:#eaf6fb;color:#174d64}.allocator-panel{width:100%;box-sizing:border-box;border-top:1px solid #40515a;border-bottom:1px solid #40515a;background:#f7f9f5;color:#182127;box-shadow:0 4px 12px #0002;font:11px/1.25 system-ui,sans-serif}.allocator-panel>header{display:grid;grid-template-columns:125px minmax(260px,auto) 1fr;gap:8px;align-items:baseline;min-height:22px;padding:3px 8px;border-bottom:1px solid #b7c1c4;background:#e8eef0}.allocator-panel>header>span{color:#166987;font:800 9px/1.2 ui-monospace,monospace;letter-spacing:.06em}.allocator-panel>header>strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:750 11px/1.2 ui-monospace,monospace}.allocator-panel>header>small{overflow:hidden;color:#536168;text-overflow:ellipsis;white-space:nowrap}.allocator-panel-body{display:grid;grid-template-columns:230px minmax(420px,1fr) minmax(310px,.8fr);gap:9px;align-items:stretch;padding:5px 8px 7px}.allocator-metrics{display:grid;grid-template-columns:1fr 1fr;gap:3px 8px;margin:0}.allocator-metrics>div{min-width:0;border-left:2px solid #8ea2ab;padding-left:5px}.allocator-metrics dt{color:#627077;font:750 7px/1.2 ui-monospace,monospace;letter-spacing:.05em}.allocator-metrics dd{margin:1px 0 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:700 10px/1.2 ui-monospace,monospace}.allocator-arena-view{min-width:0}.allocator-arena-view.is-inactive{opacity:.42}.allocator-arena-labels{display:flex;justify-content:space-between;gap:8px;margin-bottom:3px;color:#536168;font:700 8px/1.2 ui-monospace,monospace}.allocator-arena-labels strong{color:#075f80}.allocator-pinned-arena{position:relative;display:grid;grid-template-columns:repeat(var(--allocator-size),minmax(2px,1fr));height:32px;border:1px solid #28343a;background:#fff}.allocator-pinned-arena i{min-width:0;border-right:1px solid #e1e5e6;background:#fbfcfc}.allocator-pinned-arena i:nth-child(8n){border-right-color:#87969d}.allocator-pinned-arena i.is-allocated{border-right-color:#b2d8e6;background:#43a8ce}.allocator-cursor{position:absolute;z-index:2;top:-4px;bottom:-4px;width:2px;background:#d02d25;transform:translate(-1px);box-shadow:0 0 0 1px #fff9}.allocator-cursor:before{content:"allocp";position:absolute;top:1px;left:4px;color:#8d1d17;font:800 7px/1 ui-monospace,monospace}.allocator-facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2px 7px;min-width:0}.allocator-facts>div{display:flex;min-width:0;justify-content:space-between;gap:5px;border-bottom:1px dotted #b4bec2}.allocator-facts span{overflow:hidden;color:#46565e;text-overflow:ellipsis;white-space:nowrap}.allocator-facts code{overflow:hidden;color:#142e39;text-overflow:ellipsis;white-space:nowrap;font:700 9px/1.3 ui-monospace,monospace}.allocator-facts p{grid-column:1 / -1;margin:0;color:#647278}.nvim-panel{width:100%;box-sizing:border-box;border-bottom:1px solid #52616a;background:var(--desk, #eee);color:#e7eef1;box-shadow:0 5px 16px #0004;font:12px/1.3 system-ui,sans-serif}.nvim-status{flex:none;border:1px solid #66757c;border-radius:999px;padding:1px 6px;color:#c9d3d8;font:750 9px/1.35 ui-monospace,monospace;letter-spacing:.04em}.nvim-status--connected,.nvim-status--ready{border-color:#31a86d;background:#173b2a;color:#8aefb7}.nvim-status--connecting,.nvim-status--replaying,.nvim-status--verifying{border-color:#c38f2a;background:#3e321b;color:#ffd47d}.nvim-status--failed,.nvim-status--offline,.nvim-status--unselected{border-color:#a34b4b;background:#3b2020;color:#faa}.nvim-control-indicator{flex:none;border:1px solid #60747e;border-radius:3px;background:#222e34;color:#b9c6cc;padding:3px 8px;font:700 10px/1.2 ui-monospace,monospace;letter-spacing:.04em;white-space:nowrap;cursor:pointer}.nvim-control-indicator.is-active{border-color:#3cb3e2;background:#17465a;color:#d9f5ff}.nvim-control-indicator.is-engaged{box-shadow:inset 0 0 0 1px #8ee4ff}.nvim-work-indicator{flex:none;border:1px solid #7a878d;border-radius:3px;background:#f4f6f7;color:#56646a;padding:3px 8px;font:700 10px/1.2 ui-monospace,monospace;letter-spacing:.03em;white-space:nowrap;cursor:pointer}.nvim-work-indicator.is-active{border-color:#cf3d31;background:#fff0ee;color:#9f241b}.nvim-work-indicator.is-engaged{box-shadow:inset 0 0 0 1px #cf3d31}.nvim-work-indicator:disabled{border-color:#a8b0b4;background:#e6e9ea;color:#7c878c;cursor:not-allowed;opacity:.8}.nvim-height-toggle{flex:none;border:1px solid #75848b;border-radius:3px;background:#f4f6f7;color:#425159;padding:3px 8px;font:700 10px/1.2 ui-monospace,monospace;letter-spacing:.03em;white-space:nowrap;cursor:pointer}.nvim-height-toggle.is-active{border-color:#267697;background:#eaf6fb;color:#174d64}.topbar-nvim-section .nvim-toolbar-button{flex:0 0 9.5ch;width:9.5ch;min-width:9.5ch;max-width:9.5ch;height:24px;box-sizing:border-box;justify-content:flex-start;gap:.55ch;padding:2px .55ch;font:750 12px/1 ui-monospace,monospace;letter-spacing:0}.nvim-shortcut-strip{flex:none;color:#536168;font:650 10px/1.2 ui-monospace,monospace;white-space:nowrap}.nvim-shortcut-strip kbd{color:#263238;font:inherit}.nvim-screen-scroll{position:relative;width:100%;box-sizing:border-box;height:var(--nvim-panel-height, 75vh);max-height:none;min-height:240px;overflow:auto;overscroll-behavior:contain;background:var(--desk, #eee);outline:none;scrollbar-color:#58666d #171d20}.nvim-screen-stage{position:relative;width:210mm;margin:0 auto;background:#fff;zoom:var(--viewer-canvas-scale)}.nvim-screen-surface{position:absolute;inset:0 4mm;box-sizing:border-box;padding:1mm;background:#fff}.nvim-screen-frame{position:relative;height:100%;box-sizing:border-box;border:1px solid #267697;background:#101315;box-shadow:0 0 0 3px #000}.nvim-screen-scroll[data-sync=on] .nvim-screen-frame{border-color:#35b9ef}.nvim-screen-scroll[data-control=true] .nvim-screen-frame{box-shadow:0 0 0 3px #cf3d31}.nvim-screen-scroll[data-control=true]{cursor:text}.nvim-screen-content{position:absolute;inset:1mm -5mm 1mm 1mm;overflow-x:hidden;overflow-y:scroll;overscroll-behavior:contain;background:transparent;scrollbar-width:thin;scrollbar-color:#829097 #fff}.nvim-screen-grid{width:calc(100% - 6mm);min-height:100%;background:#101315}.nvim-screen-content::-webkit-scrollbar{width:.8mm}.nvim-screen-content::-webkit-scrollbar-track{background:#fff}.nvim-screen-content::-webkit-scrollbar-thumb{border:.12mm solid #fff;border-radius:99px;background:#829097}.nvim-screen-content::-webkit-scrollbar-thumb:hover{background:#aeb9be}.nvim-screen{display:block;max-width:none;margin:0;outline:none;image-rendering:auto}.nvim-screen:focus{box-shadow:none}.nvim-screen-empty{position:absolute;inset:0;display:grid;place-content:center;gap:5px;padding:16px;color:#9cabb2;text-align:center}.nvim-screen-empty strong{color:#e0e8eb;font:750 11px/1.3 ui-monospace,monospace}.nvim-resize-handle{position:relative;width:100%;height:12px;border-top:1px solid #46565e;background:#182126;cursor:ns-resize;touch-action:none}.nvim-resize-handle:after{content:"";position:absolute;top:4px;left:50%;width:min(160px,22vw);height:3px;border-radius:999px;background:#6c7e87;transform:translate(-50%)}.nvim-resize-handle:hover,.nvim-resize-handle:focus-visible{background:#23323a;outline:none}.nvim-resize-handle:hover:after,.nvim-resize-handle:focus-visible:after{background:#35b9ef}.shortcut-help-backdrop{position:fixed;z-index:200;inset:0;display:grid;place-items:center;padding:20px;background:#10171dcc}.shortcut-help{width:min(680px,calc(100vw - 40px));max-height:calc(100vh - 40px);overflow:auto;border:1px solid #71818a;border-radius:6px;background:#f8fafb;color:#172127;box-shadow:0 18px 60px #0008;font:14px/1.4 system-ui,sans-serif}.shortcut-help>header{display:flex;align-items:start;justify-content:space-between;gap:16px;padding:16px 18px;border-bottom:1px solid #c8d1d5;background:#e8eef1}.shortcut-help header small{color:#52646d;font:750 10px/1.2 ui-monospace,monospace;letter-spacing:.08em}.shortcut-help h2{margin:4px 0 0;font-size:17px}.shortcut-help header button{width:32px;height:32px;border:1px solid #71818a;border-radius:3px;background:#fff;font:22px/1 system-ui,sans-serif;cursor:pointer}.shortcut-help dl{margin:0;padding:10px 18px}.shortcut-help dl>div{display:grid;grid-template-columns:minmax(180px,.8fr) 1.5fr;gap:14px;align-items:center;padding:8px 0;border-bottom:1px solid #dde3e6}.shortcut-help dt,.shortcut-help dd{margin:0}.shortcut-help kbd{display:inline-block;border:1px solid #839198;border-bottom-width:2px;border-radius:3px;background:#fff;padding:3px 7px;font:700 12px/1.2 ui-monospace,monospace}.shortcut-help>p{margin:0;padding:4px 18px 16px;color:#526068}.memory-react{border:1px solid #555;background:#fff;font:14px/1.35 system-ui,sans-serif;padding:10px;color:#1b1b1b}.memory-react>header{display:flex;align-items:center;justify-content:space-between;gap:12px;border-bottom:1px solid #bbb;padding-bottom:7px}.memory-react>header small{display:block;margin-bottom:2px;color:#555;font:700 10px/1.2 ui-monospace,monospace;letter-spacing:.08em}.memory-react h3{font-size:15px;margin:0}.memory-actions{display:flex;gap:5px;flex-wrap:wrap;justify-content:flex-end}.memory-actions button{border:1px solid #777;background:#fff;padding:4px 8px;cursor:pointer}.memory-actions button[aria-pressed=true]{background:#dff3fb;border-color:#237da1;font-weight:700}.memory-lanes{display:grid;grid-template-columns:repeat(auto-fit,minmax(125px,1fr));gap:6px;margin:8px 0}.memory-lane{min-width:0;display:flex;flex-direction:column;align-items:stretch;gap:3px;border:1px solid #999;background:#fafafa;padding:7px;text-align:left}.memory-lane.compile{background:#fff8dc}.memory-lane.bss{background:#edf7fb}.memory-lane.stack{background:#f3f8ef}.memory-lane.registers{background:#f4f4f4}.memory-lane.text{background:#f7f0fa}.memory-lane>b{font-size:11px;letter-spacing:.04em}.memory-lane button{border:1px solid transparent;background:transparent;padding:3px;text-align:left;cursor:pointer}.memory-lane button:hover,.memory-lane button.active{border-color:#238bb7;background:#fff}.memory-lane code{font-size:10px;white-space:normal}.arena-wrap{margin:12px 0 6px}.arena-label{display:flex;justify-content:space-between;margin-bottom:4px}.arena{--allocp: 0;--arena-size: 64;position:relative;display:grid;grid-template-columns:repeat(var(--arena-size),1fr);height:40px;border:1px solid #222;margin-bottom:28px}.arena i{border-right:1px solid #ddd}.arena .allocp{position:absolute;left:calc(var(--allocp) / var(--arena-size) * 100%);top:43px;transform:translate(-50%);color:#087da8;font:700 11px/1.2 ui-monospace,monospace;white-space:nowrap}.memory-stage{border-top:1px solid #ddd;padding-top:7px;margin:0}.memory-inspector{display:grid;grid-template-columns:auto 1fr;gap:2px 10px;margin-top:7px;padding:7px;border-left:3px solid #238bb7;background:#f5fbfd}.memory-inspector strong{grid-row:1/3}.memory-inspector code{font-size:11px}.memory-inspector span{font-size:12px}.asset-static-fallback{display:none}.uml-react{border:1px solid #555;background:#fff;font:13px/1.35 system-ui,sans-serif;color:#1b1b1b}.uml-react>header{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:6px 8px;border-bottom:1px solid #bbb;background:#fafafa}.uml-react>header small{display:block;color:#555;font:700 10px/1.2 ui-monospace,monospace;letter-spacing:.06em}.uml-react h3{margin:2px 0 0;font-size:15px}.uml-actions{display:flex;gap:3px;flex-wrap:wrap;justify-content:flex-end}.uml-actions button{border:1px solid #aaa;border-radius:2px;background:#fcfcfc;color:#444;padding:2px 6px;font:600 10px/1.25 ui-monospace,monospace;cursor:pointer}.uml-actions button:hover{border-color:#64879a;background:#f5fafc}.uml-actions button[data-completed=true]{border-color:#62a47f;background:#edf8f1;color:#14613a}.uml-actions button[aria-pressed=true]{border-color:#287495;background:#edf7fb;color:#174d64;box-shadow:inset 0 0 0 1px #b9dce9}.uml-canvas{display:block;overflow-x:hidden;overflow-y:visible;box-sizing:border-box;padding:8px 10px;text-align:center}.uml-svg-host{width:max-content;min-width:100%}.uml-canvas img,.uml-svg-host svg{display:block;width:auto!important;max-width:none!important;height:auto!important;max-height:none;margin-inline:auto;object-fit:contain}.uml-svg-host{cursor:pointer}.uml-svg-host.is-tiled{position:relative;width:var(--uml-tile-page-width);min-width:var(--uml-tile-page-width);height:var(--uml-tile-page-height);overflow:hidden;cursor:default}.uml-svg-host.is-tiled>svg{position:absolute;top:var(--uml-tile-top);left:var(--uml-tile-left);margin:0}.uml-static-crop{position:absolute;top:var(--uml-tile-top);left:var(--uml-tile-left);overflow:hidden}.uml-static-crop>img{display:block;max-width:none!important;max-height:none!important;margin:0!important;object-fit:fill!important;transform-origin:0 0}.uml-tile-part{display:inline-block;margin-left:.65em;color:#607985;font:650 9px/1.2 ui-monospace,monospace;letter-spacing:.03em;white-space:nowrap}.uml-phase-actions>span{display:inline-flex;align-items:center;border:1px solid #9eb2bd;border-radius:2px;background:#f1f5f7;color:#3d5967;padding:2px 6px;font:700 10px/1.25 ui-monospace,monospace}.uml-stage-region{cursor:pointer;transition:fill .14s ease,stroke .14s ease,stroke-width .14s ease}.uml-stage-region-completed.uml-stage-region-bg{fill:#e7f6ed!important;fill-opacity:1!important;stroke:#258154!important;stroke-width:3px!important}.uml-stage-region-completed.uml-stage-region-outline{stroke:#258154!important;stroke-width:3px!important}.uml-stage-region-active.uml-stage-region-bg{fill:#e7f6fc!important;fill-opacity:1!important;stroke:#187ea7!important;stroke-width:3px!important}.uml-stage-region-active.uml-stage-region-outline{stroke:#187ea7!important;stroke-width:3px!important}.uml-stage-region-active.uml-stage-region-completed.uml-stage-region-bg{fill:#e7f6ed!important}.uml-stage-region:focus{outline:none;stroke:#005f87!important;stroke-width:4px!important}.uml-inline-warning{display:block;padding-top:5px;color:#8b2d20;font-size:11px}.uml-stage{display:grid;grid-template-columns:auto 1fr;gap:3px 10px;margin:0 8px 8px;padding:8px;border-left:3px solid #315f78;background:#eef7fb}.uml-stage strong{grid-row:1/3}.uml-stage code{grid-column:2;font-size:11px}.uml-approval{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0 8px 8px;padding-top:6px;border-top:1px solid #d2d2d2;color:#555;font-size:11px}.uml-approval button{flex:0 0 auto;border:1px solid #477a5d;border-radius:2px;background:#edf8f1;color:#174e30;padding:3px 7px;font:600 10px/1.25 system-ui,sans-serif;cursor:pointer}.uml-approval button:disabled{border-color:#bbb;background:#f3f3f3;color:#888;cursor:not-allowed}.uml-react>.uml-snapshot-header{display:block;padding:5px 8px 4px}.uml-header-first-line{display:flex;align-items:center;justify-content:space-between;gap:10px}.uml-header-first-line h3{flex:1 1 auto;min-width:0;margin:0;color:#20282c;text-align:left;font-size:12px;line-height:1.25}.uml-header-first-line h3>span{color:#245f7a;font-family:ui-monospace,monospace;letter-spacing:.04em}.uml-header-description{max-width:620px;margin:2px 0 0;color:#555;text-align:left;font-size:8px}.uml-header-navigation{display:flex;flex:0 0 auto;align-items:center;justify-content:flex-end;gap:6px}.uml-page-continuation{display:inline-flex;flex:0 0 auto;align-items:center;gap:2px;min-height:22px;border:1px solid #86a5b3;border-radius:3px;background:#edf7fa;padding:1px 5px;color:#126b8c;font:850 15px/1 ui-monospace,monospace;animation:uml-continuation-pulse 1.15s ease-in-out infinite alternate}.uml-page-continuation i{display:inline-grid;min-width:14px;place-items:center;font-style:normal}@keyframes uml-continuation-pulse{0%{border-color:#9fb5be;background:#f6fafb;color:#4c7180;box-shadow:none}to{border-color:#167ca4;background:#dff2f8;color:#075c7c;box-shadow:0 0 0 2px #8dcde433}}@media(prefers-reduced-motion:reduce){.uml-page-continuation{animation:none}}.uml-phase-actions,.uml-step-actions{display:flex;align-items:center;flex-wrap:wrap;gap:3px}.uml-phase-actions{flex:0 0 auto;flex-wrap:nowrap;justify-content:flex-end;white-space:nowrap}.uml-phase-actions button,.uml-step-actions button,.uml-edit-toggle,.uml-snapshot-cursor button{border:1px solid #aaa;border-radius:2px;background:#fff;color:#444;cursor:pointer;font:600 9px/1.2 ui-monospace,monospace}.uml-edit-toggle{min-width:42px;padding:3px 7px;border:1px solid #8a9ca5;border-radius:2px;background:#f7f9fa;color:#334d59;cursor:pointer;font:750 9px/1.2 ui-monospace,monospace;letter-spacing:.04em}.uml-edit-toggle.is-active{border-color:#b56b16;background:#fff3df;color:#713c00;box-shadow:inset 0 0 0 1px #f0c78f}.uml-phase-actions button{padding:3px 7px}.uml-phase-actions button[aria-pressed=true]{border-color:#287495;background:#edf7fb;color:#174d64}.uml-step-actions{min-height:0;padding:0;border:0;background:transparent}.uml-step-actions>span{margin-right:4px;color:#555;font:700 9px/1.2 ui-monospace,monospace}.uml-step-actions button{min-width:25px;padding:2px 4px}.uml-step-actions button[data-completed=true]{border-color:#62a47f;background:#edf8f1;color:#14613a}.uml-step-actions button[aria-pressed=true]{border-color:#287495;background:#287495;color:#fff}.uml-step-actions button[data-mode=CODE]:after,.uml-step-actions button[data-mode=RUN]:after{margin-left:3px;font-size:7px;letter-spacing:.02em}.uml-step-actions button[data-mode=CODE]:after{content:"C"}.uml-step-actions button[data-mode=RUN]:after{content:"R"}.uml-snapshot-layout{background:#fff;border-bottom:1px solid #ddd}.uml-step-canvas{min-width:0}.uml-step-canvas .uml-svg-host svg{max-height:none}.uml-step-hit{cursor:pointer;fill:transparent}.uml-step-hit-active{fill:#3ea6ce!important;fill-opacity:.08!important}.uml-step-element{transition:fill .12s ease,stroke .12s ease,stroke-width .12s ease}line.uml-step-element-completed:not(.uml-step-element-active){stroke:#258154!important;stroke-width:1.8px!important}polygon.uml-step-element-completed:not(.uml-step-element-active){fill:#258154!important;stroke:#258154!important}text.uml-step-element-completed:not(.uml-step-element-active){fill:#1d7049!important}line.uml-step-element-active{stroke:#087ca8!important;stroke-width:2.6px!important}polygon.uml-step-element-active{fill:#087ca8!important;stroke:#087ca8!important}text.uml-step-element-active{fill:#075d7d!important;font-weight:700!important}.uml-step-hit:focus{outline:none;stroke:#bd554d;stroke-width:.8px;stroke-dasharray:3 3}.uml-class-svg-active>g>*:not(.uml-class-focus-region):not(.uml-class-step-hit):not(.uml-layout-hit):not(.uml-layout-resize){opacity:.62;transition:opacity .12s ease}.uml-class-svg-active .uml-step-element:not(.uml-step-element-active){opacity:.98!important}.uml-class-svg-active text.uml-step-element-tone-a:not(.uml-step-element-active){fill:#174f69!important}.uml-class-svg-active text.uml-step-element-tone-b:not(.uml-step-element-active){fill:#586581!important}.uml-class-svg-active rect.uml-step-element-tone-a:not(.uml-step-element-active){fill:#eaf5f8!important;stroke:#3f7890!important}.uml-class-svg-active rect.uml-step-element-tone-b:not(.uml-step-element-active){fill:#f2eef8!important;stroke:#7b86a1!important}.uml-class-svg-active path.uml-step-element-tone-a:not(.uml-step-element-active),.uml-class-svg-active line.uml-step-element-tone-a:not(.uml-step-element-active),.uml-class-svg-active polygon.uml-step-element-tone-a:not(.uml-step-element-active){stroke:#3f7890!important}.uml-class-svg-active path.uml-step-element-tone-b:not(.uml-step-element-active),.uml-class-svg-active line.uml-step-element-tone-b:not(.uml-step-element-active),.uml-class-svg-active polygon.uml-step-element-tone-b:not(.uml-step-element-active){stroke:#7b86a1!important}.uml-class-svg-active .uml-step-element-active{opacity:1!important}.uml-class-svg-active text.uml-step-element-active{fill:#b42318!important;font-weight:700!important}.uml-class-svg-active path.uml-step-element-active,.uml-class-svg-active line.uml-step-element-active,.uml-class-svg-active polygon.uml-step-element-active,.uml-class-svg-active rect.uml-step-element-active{stroke:#bd554d!important;stroke-width:1.35px!important}.uml-class-svg-active polygon.uml-step-element-active{fill:#bd554d!important}.uml-class-focus-region{display:none}.uml-step-canvas.is-layout-editing{position:relative;touch-action:none;background-color:#fbfcfd;background-image:linear-gradient(#7d98a20d 1px,transparent 1px),linear-gradient(90deg,#7d98a20d 1px,transparent 1px);background-size:12px 12px}.uml-layout-editing .uml-step-hit,.uml-layout-editing .uml-class-focus-region{pointer-events:none}.uml-layout-hit{fill:#4e9ec4;fill-opacity:.035;stroke:#247a9e;stroke-width:1.1px;stroke-dasharray:5 3;vector-effect:non-scaling-stroke;cursor:grab;pointer-events:all}.uml-layout-hit:hover{fill-opacity:.09;stroke-width:1.6px}.uml-layout-hit.is-selected{fill:#f7a928;fill-opacity:.08;stroke:#b66b00;stroke-width:1.8px;stroke-dasharray:none}.uml-layout-hit:active{cursor:grabbing}.uml-layout-resize{fill:#fff;stroke:#b66b00;stroke-width:1.5px;vector-effect:non-scaling-stroke;cursor:nwse-resize;pointer-events:all}.uml-layout-editor{display:grid;grid-template-columns:minmax(140px,.8fr) minmax(240px,1.8fr) minmax(260px,1.4fr);gap:7px 10px;align-items:end;padding:8px 10px 9px;border-top:2px solid #b97822;background:#f7f5f0;color:#26343a;text-align:left;font:10px/1.3 system-ui,sans-serif}.uml-layout-editor-title{grid-column:1/-1;display:flex;align-items:baseline;gap:9px}.uml-layout-editor-title strong{color:#713c00;font:800 11px/1.2 ui-monospace,monospace;text-transform:uppercase}.uml-layout-editor-title span{color:#65737a}.uml-layout-editor-title b{margin-left:auto;color:#9c2f1b;font-size:9px}.uml-layout-editor label{display:grid;gap:2px;color:#52636b;font:700 8px/1.2 ui-monospace,monospace;text-transform:uppercase}.uml-layout-editor input,.uml-layout-editor select,.uml-layout-editor textarea{box-sizing:border-box;width:100%;min-width:0;border:1px solid #9ca9ae;border-radius:2px;background:#fff;color:#172126;padding:4px 5px;font:10px/1.25 ui-monospace,monospace;text-transform:none}.uml-layout-editor textarea{resize:vertical;line-height:1.35}.uml-layout-number-grid,.uml-layout-style-grid{display:grid;gap:5px}.uml-layout-number-grid{grid-template-columns:repeat(4,minmax(48px,1fr))}.uml-layout-style-grid{grid-template-columns:repeat(5,minmax(54px,1fr))}.uml-layout-editor-text{align-self:stretch}.uml-layout-editor input[type=color]{min-height:27px;padding:2px}.uml-layout-editor-actions{grid-column:1/-1;display:flex;gap:5px}.uml-layout-editor-actions button{border:1px solid #87979f;border-radius:2px;background:#fff;color:#283940;padding:4px 8px;cursor:pointer;font:700 9px/1.2 ui-monospace,monospace}.uml-layout-editor-actions button.is-primary{border-color:#287495;background:#287495;color:#fff}.uml-layout-editor-actions button.is-danger{margin-left:auto;border-color:#a26156;color:#812b1c}.uml-layout-editor-actions button:disabled{opacity:.45;cursor:not-allowed}.uml-layout-editor output{grid-column:1/-1;min-height:1.2em;color:#52636b;font:9px/1.25 ui-monospace,monospace}@media(max-width:900px){.uml-layout-editor{grid-template-columns:1fr}.uml-layout-editor-title,.uml-layout-editor-actions,.uml-layout-editor output{grid-column:1}}@media print{.uml-edit-toggle,.uml-layout-editor,.uml-layout-hit,.uml-layout-resize{display:none!important}}.uml-snapshot-panel{display:grid;gap:8px;min-width:0;padding:8px;background:#fafafa;font-size:10px}.uml-snapshot-panel>header{display:flex;justify-content:space-between;gap:8px;align-items:center}.uml-snapshot-panel>header>div:first-child>span,.uml-snapshot-panel>header>div:first-child>strong{display:block}.uml-snapshot-panel>header>div:first-child>span{color:#5a5a5a;font:700 9px/1.2 ui-monospace,monospace}.uml-snapshot-panel>header>div:first-child>strong{font-size:12px}.uml-snapshot-panel>header>div:first-child>strong code{display:inline-block;min-width:24px;color:#075d7d}.uml-snapshot-cursor{display:flex;align-items:center;gap:4px}.uml-snapshot-cursor button{width:23px;height:23px;padding:0}.uml-snapshot-cursor button:disabled{opacity:.35;cursor:default}.uml-snapshot-cursor output{min-width:34px;text-align:center;font:700 9px/1 ui-monospace,monospace}.uml-snapshot-description{margin:0;color:#333}.uml-snapshot-panel section{min-width:0;border:1px solid #d0d0d0;background:#fff;padding:7px}.uml-snapshot-panel h4{margin:0 0 4px;color:#315f78;font:700 9px/1.2 ui-monospace,monospace;text-transform:uppercase}.uml-state-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:7px;min-width:0}.uml-snapshot-registers dl{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:3px;margin:0}.uml-snapshot-registers dl>div{display:grid;grid-template-columns:auto 1fr;gap:5px;min-width:0;padding:2px 4px;border-left:2px solid #bbb;background:#f6f6f6}.uml-snapshot-registers dl>div.primary{grid-column:1/-1;border-color:#287495;background:#edf7fb}.uml-snapshot-registers dt{color:#555;font-weight:700}.uml-snapshot-registers dd,.uml-snapshot-variables dd,.uml-snapshot-memory dd{min-width:0;margin:0;overflow-wrap:anywhere;text-align:right;font:600 9px/1.25 ui-monospace,monospace}.uml-snapshot-variables dl{display:grid;gap:3px;margin:0}.uml-snapshot-variables dl>div{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:5px;padding:3px 5px;border-left:2px solid #5f899b;background:#f5f8f9}.uml-snapshot-variables dt{font-weight:700}.uml-snapshot-variables dt small{display:block;color:#777;font:8px/1.2 ui-monospace,monospace}.uml-snapshot-variables [data-state=uninitialised],.uml-stack-lane article[data-state=uninitialised]{color:#777;background:#f5f5f5}.uml-snapshot-variables [data-state=uninitialised] dt:after,.uml-stack-lane article[data-state=uninitialised]>strong:after{content:" ?";color:#9a6a00}.uml-arena-snapshot>div{position:relative;height:10px;border:1px solid #666;background:repeating-linear-gradient(90deg,#fff 0 7px,#eee 7px 8px)}.uml-arena-snapshot>div span{display:block;height:100%;background:#b8ddeb}.uml-arena-snapshot>div i{position:absolute;top:-3px;bottom:-3px;width:2px;background:#075d7d;transform:translate(-1px)}.uml-arena-snapshot>p{display:grid;grid-template-columns:1fr auto 1fr;gap:4px;margin:3px 0;font-size:8px}.uml-arena-snapshot>p code:last-child{text-align:right}.uml-arena-bytes{display:block;margin-bottom:4px;overflow-wrap:anywhere;color:#666;font-size:8px}.uml-snapshot-memory dl{display:grid;gap:2px;margin:0}.uml-snapshot-memory dl>div{display:grid;grid-template-columns:1fr auto;gap:4px;padding-top:2px;border-top:1px solid #eee}.uml-snapshot-memory dt{font-weight:700}.uml-snapshot-memory dt small{display:block;color:#777;font:8px/1.2 ui-monospace,monospace}.uml-snapshot-stack>header{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.uml-snapshot-stack>header>p{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:4px 9px;margin:0;color:#555;font-size:8px}.uml-stack-axis{display:flex;justify-content:space-between;gap:8px;margin:3px 89px 2px;color:#777;font:8px/1.2 ui-monospace,monospace}.uml-stack-lane{display:flex;align-items:stretch;min-width:0;overflow-x:auto;border:1px solid #66757c;background:#eef1f2}.uml-stack-boundary{display:grid;place-content:center;flex:0 1 82px;min-width:64px;padding:5px;background:#273941;color:#fff;text-align:center;font:700 8px/1.25 ui-monospace,monospace}.uml-stack-boundary small{color:#a9d9eb;font-size:8px;text-transform:uppercase}.uml-stack-gap{display:grid;place-content:center;flex:.7 1 70px;min-width:60px;min-height:52px;border-right:1px dashed #9da8ad;color:#78858b;text-align:center;font:8px/1.2 ui-monospace,monospace}.uml-stack-gap strong,.uml-stack-gap small{display:block}.uml-stack-gap strong{color:#52636b}.uml-stack-gap small{color:#78858b;font-size:8px}.uml-stack-lane article{display:grid;align-content:center;flex:1 1 92px;min-width:76px;padding:5px 7px;border-right:1px solid #9da8ad;background:#fff;text-align:center}.uml-stack-lane article[data-state=initialised]{border-top:3px solid #2c8060}.uml-stack-lane article[data-state=saved]{border-top:3px solid #6d7196;background:#f7f6fb}.uml-stack-lane article strong,.uml-stack-lane article code,.uml-stack-lane article small{display:block;overflow-wrap:anywhere}.uml-stack-lane article strong{font:700 9px/1.2 ui-monospace,monospace}.uml-stack-lane article code{margin:2px 0;color:#1f4555;font-size:8px}.uml-stack-lane article small{color:#777;font:8px/1.2 ui-monospace,monospace}.uml-snapshot-observation p{margin:0 0 3px}.uml-snapshot-observation small{display:block;overflow-wrap:anywhere;color:#666;font:8px/1.3 ui-monospace,monospace}.task-conclusion{margin:8px 0 0;padding:7px 9px;border:1px solid #315f78;background:#f3f9fc}.task-conclusion>strong{display:block;margin-bottom:3px;color:#315f78;font:700 10px/1.2 ui-monospace,monospace}.task-conclusion p{margin:0}.asset-source-link{display:inline-block;margin:5px 0;color:#315f78;font:600 11px/1.3 system-ui,sans-serif}.section-sheet--asset .section-heading{align-items:baseline;margin:0 0 1.5mm}.section-sheet--asset .section-heading .section-index{margin-right:4mm;color:#68757b;font-size:10px;font-weight:600}.section-sheet--asset .section-heading .section-title{color:#20282c;font:750 13px/1.2 system-ui,sans-serif}.section-sheet--asset .card-section>p{max-width:110ch;margin:0 0 2mm;color:#58656b;font:9px/1.3 system-ui,sans-serif}.section-sheet--asset .card-asset{margin-top:2mm}.lesson-progress-control{display:inline-flex;align-items:center;margin-right:7px;padding:2px 7px;border:1px solid #8c8c8c;border-radius:12px;background:#fff;cursor:pointer}.lesson-progress-control:disabled{cursor:wait;opacity:.6}.lesson-progress-control[data-status=approved]{background:#e7f6ed;border-color:#18794e}.lesson-progress-topbar{display:flex;flex:none;align-items:center;gap:5px;min-width:0;padding-left:7px;border-left:1px solid #a9b3b8;color:#33444c;font:650 10px/1.2 system-ui,sans-serif;white-space:nowrap}.lesson-progress-topbar strong{color:#52636b;font:800 9px/1 ui-monospace,monospace;letter-spacing:.05em}.lesson-progress-topbar output{min-width:3.8ch;border:1px solid #91a1a8;border-radius:999px;background:#fff;padding:2px 6px;color:#1e566e;text-align:center;font:800 9px/1 ui-monospace,monospace}.lesson-progress-topbar a{color:#245f7a;text-decoration:none}.lesson-progress-topbar a:hover,.lesson-progress-topbar a:focus-visible{text-decoration:underline}.lesson-progress-error{color:#9b1c1c;font-weight:900}.step-row{display:flex;align-items:center;margin:4px 0}.section-anchor{position:relative;top:-45px}.app-load-error{max-width:760px;margin:10vh auto;padding:24px;border:1px solid #9b1c1c;background:#fff;font-family:system-ui,sans-serif}.app-load-error pre{white-space:pre-wrap}@media(max-width:800px){.viewpoint-bar{grid-template-columns:repeat(4,minmax(0,1fr))}.viewpoint-bar button{min-height:28px}.card-library-toggle>span:last-child{display:none}.card-library-toggle{gap:4px;padding-inline:6px}.topbar-nvim-meta{display:none}.topbar-nvim-section .nvim-control-indicator{max-width:42vw;overflow:hidden;text-overflow:ellipsis}.nvim-shortcut-strip,.nvim-work-indicator{display:none}.allocator-panel>header{grid-template-columns:auto 1fr}.allocator-panel>header>small{display:none}.allocator-panel-body{grid-template-columns:1fr}.allocator-metrics,.allocator-facts{display:none}.memory-lanes{grid-template-columns:1fr 1fr}.memory-react>header,.uml-react>header{align-items:flex-start;flex-direction:column}.uml-header-first-line,.uml-header-navigation{width:100%;align-items:flex-start;flex-direction:column}.uml-header-navigation{gap:4px}.memory-actions{justify-content:flex-start}.uml-snapshot-layout{display:block}.uml-step-canvas{border:0}.uml-phase-actions{width:100%;justify-content:flex-end}.uml-state-grid{grid-template-columns:1fr}.uml-snapshot-stack>header{align-items:flex-start;flex-direction:column}.uml-snapshot-stack>header>p{justify-content:flex-start}.uml-stack-axis{margin-right:0;margin-left:0}}@media print{@page card-portrait{size:A4 portrait;margin:0}@page card-landscape{size:A4 landscape;margin:0}.sheet--portrait{page:card-portrait}.sheet--landscape{page:card-landscape}.paper{display:block;gap:0}.paper>.sheet,.paper>.sheet>.sheet-content,.paper>.sheet-spread>.sheet,.paper>.sheet-spread>.sheet>.sheet-content{height:auto;min-height:0;max-height:none;overflow:visible}.sheet-spread{display:contents!important}.viewer-chrome,.side-panel-dock,.memory-react{display:none!important}.asset-static-fallback{display:block!important;width:100%}.uml-actions,.uml-phase-actions,.uml-step-actions,.uml-snapshot-panel,.uml-stage,.uml-approval,.asset-source-link,.lesson-progress-topbar,.lesson-progress-control,.uml-page-continuation{display:none!important}} diff --git a/web/app.js b/web/app.js new file mode 100644 index 0000000..32731f2 --- /dev/null +++ b/web/app.js @@ -0,0 +1,61 @@ +var Ap=Object.create;var bs=Object.defineProperty;var Pp=Object.getOwnPropertyDescriptor;var Mp=Object.getOwnPropertyNames;var Tp=Object.getPrototypeOf,Rp=Object.prototype.hasOwnProperty;var It=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Op=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Mp(t))!Rp.call(e,o)&&o!==n&&bs(e,o,{get:()=>t[o],enumerable:!(r=Pp(t,o))||r.enumerable});return e};var xo=(e,t,n)=>(n=e!=null?Ap(Tp(e)):{},Op(t||!e||!e.__esModule?bs(n,"default",{value:e,enumerable:!0}):n,e));var Ts=It(W=>{"use strict";var xr=Symbol.for("react.element"),Ip=Symbol.for("react.portal"),$p=Symbol.for("react.fragment"),Fp=Symbol.for("react.strict_mode"),Dp=Symbol.for("react.profiler"),jp=Symbol.for("react.provider"),Up=Symbol.for("react.context"),Vp=Symbol.for("react.forward_ref"),Bp=Symbol.for("react.suspense"),Hp=Symbol.for("react.memo"),Kp=Symbol.for("react.lazy"),ks=Symbol.iterator;function Wp(e){return e===null||typeof e!="object"?null:(e=ks&&e[ks]||e["@@iterator"],typeof e=="function"?e:null)}var Es={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},_s=Object.assign,Cs={};function Vn(e,t,n){this.props=e,this.context=t,this.refs=Cs,this.updater=n||Es}Vn.prototype.isReactComponent={};Vn.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};Vn.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function Ls(){}Ls.prototype=Vn.prototype;function Yi(e,t,n){this.props=e,this.context=t,this.refs=Cs,this.updater=n||Es}var Qi=Yi.prototype=new Ls;Qi.constructor=Yi;_s(Qi,Vn.prototype);Qi.isPureReactComponent=!0;var Ss=Array.isArray,zs=Object.prototype.hasOwnProperty,qi={current:null},As={key:!0,ref:!0,__self:!0,__source:!0};function Ps(e,t,n){var r,o={},i=null,l=null;if(t!=null)for(r in t.ref!==void 0&&(l=t.ref),t.key!==void 0&&(i=""+t.key),t)zs.call(t,r)&&!As.hasOwnProperty(r)&&(o[r]=t[r]);var s=arguments.length-2;if(s===1)o.children=n;else if(1{"use strict";Rs.exports=Ts()});var Hs=It(le=>{"use strict";function tl(e,t){var n=e.length;e.push(t);e:for(;0>>1,o=e[r];if(0>>1;rNo(s,n))uNo(d,s)?(e[r]=d,e[u]=n,r=u):(e[r]=s,e[l]=n,r=l);else if(uNo(d,n))e[r]=d,e[u]=n,r=u;else break e}}return t}function No(e,t){var n=e.sortIndex-t.sortIndex;return n!==0?n:e.id-t.id}typeof performance=="object"&&typeof performance.now=="function"?(Os=performance,le.unstable_now=function(){return Os.now()}):(Zi=Date,Is=Zi.now(),le.unstable_now=function(){return Zi.now()-Is});var Os,Zi,Is,At=[],Zt=[],Xp=1,mt=null,Oe=3,Co=!1,kn=!1,br=!1,Ds=typeof setTimeout=="function"?setTimeout:null,js=typeof clearTimeout=="function"?clearTimeout:null,$s=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function nl(e){for(var t=bt(Zt);t!==null;){if(t.callback===null)_o(Zt);else if(t.startTime<=e)_o(Zt),t.sortIndex=t.expirationTime,tl(At,t);else break;t=bt(Zt)}}function rl(e){if(br=!1,nl(e),!kn)if(bt(At)!==null)kn=!0,il(ol);else{var t=bt(Zt);t!==null&&ll(rl,t.startTime-e)}}function ol(e,t){kn=!1,br&&(br=!1,js(kr),kr=-1),Co=!0;var n=Oe;try{for(nl(t),mt=bt(At);mt!==null&&(!(mt.expirationTime>t)||e&&!Bs());){var r=mt.callback;if(typeof r=="function"){mt.callback=null,Oe=mt.priorityLevel;var o=r(mt.expirationTime<=t);t=le.unstable_now(),typeof o=="function"?mt.callback=o:mt===bt(At)&&_o(At),nl(t)}else _o(At);mt=bt(At)}if(mt!==null)var i=!0;else{var l=bt(Zt);l!==null&&ll(rl,l.startTime-t),i=!1}return i}finally{mt=null,Oe=n,Co=!1}}var Lo=!1,Eo=null,kr=-1,Us=5,Vs=-1;function Bs(){return!(le.unstable_now()-Vse||125r?(e.sortIndex=n,tl(Zt,e),bt(At)===null&&e===bt(Zt)&&(br?(js(kr),kr=-1):br=!0,ll(rl,n-r))):(e.sortIndex=o,tl(At,e),kn||Co||(kn=!0,il(ol))),e};le.unstable_shouldYield=Bs;le.unstable_wrapCallback=function(e){var t=Oe;return function(){var n=Oe;Oe=t;try{return e.apply(this,arguments)}finally{Oe=n}}}});var Ws=It((sh,Ks)=>{"use strict";Ks.exports=Hs()});var qd=It(lt=>{"use strict";var Zp=So(),ot=Ws();function z(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),zl=Object.prototype.hasOwnProperty,Jp=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Gs={},Ys={};function em(e){return zl.call(Ys,e)?!0:zl.call(Gs,e)?!1:Jp.test(e)?Ys[e]=!0:(Gs[e]=!0,!1)}function tm(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function nm(e,t,n,r){if(t===null||typeof t>"u"||tm(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function We(e,t,n,r,o,i,l){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=o,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=i,this.removeEmptyString=l}var Te={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){Te[e]=new We(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];Te[t]=new We(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){Te[e]=new We(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){Te[e]=new We(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){Te[e]=new We(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){Te[e]=new We(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){Te[e]=new We(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){Te[e]=new We(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){Te[e]=new We(e,5,!1,e.toLowerCase(),null,!1,!1)});var ba=/[\-:]([a-z])/g;function ka(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(ba,ka);Te[t]=new We(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(ba,ka);Te[t]=new We(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(ba,ka);Te[t]=new We(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){Te[e]=new We(e,1,!1,e.toLowerCase(),null,!1,!1)});Te.xlinkHref=new We("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){Te[e]=new We(e,1,!1,e.toLowerCase(),null,!0,!0)});function Sa(e,t,n,r){var o=Te.hasOwnProperty(t)?Te[t]:null;(o!==null?o.type!==0:r||!(2s||o[l]!==i[s]){var u=` +`+o[l].replace(" at new "," at ");return e.displayName&&u.includes("")&&(u=u.replace("",e.displayName)),u}while(1<=l&&0<=s);break}}}finally{sl=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?Pr(e):""}function rm(e){switch(e.tag){case 5:return Pr(e.type);case 16:return Pr("Lazy");case 13:return Pr("Suspense");case 19:return Pr("SuspenseList");case 0:case 2:case 15:return e=ul(e.type,!1),e;case 11:return e=ul(e.type.render,!1),e;case 1:return e=ul(e.type,!0),e;default:return""}}function Tl(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Wn:return"Fragment";case Kn:return"Portal";case Al:return"Profiler";case Na:return"StrictMode";case Pl:return"Suspense";case Ml:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case tc:return(e.displayName||"Context")+".Consumer";case ec:return(e._context.displayName||"Context")+".Provider";case Ea:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case _a:return t=e.displayName||null,t!==null?t:Tl(e.type)||"Memo";case en:t=e._payload,e=e._init;try{return Tl(e(t))}catch{}}return null}function om(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Tl(t);case 8:return t===Na?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function gn(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function rc(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function im(e){var t=rc(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var o=n.get,i=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(l){r=""+l,i.call(this,l)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(l){r=""+l},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Ao(e){e._valueTracker||(e._valueTracker=im(e))}function oc(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=rc(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function oi(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Rl(e,t){var n=t.checked;return ve({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function qs(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=gn(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function ic(e,t){t=t.checked,t!=null&&Sa(e,"checked",t,!1)}function Ol(e,t){ic(e,t);var n=gn(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Il(e,t.type,n):t.hasOwnProperty("defaultValue")&&Il(e,t.type,gn(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Xs(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Il(e,t,n){(t!=="number"||oi(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Mr=Array.isArray;function rr(e,t,n,r){if(e=e.options,t){t={};for(var o=0;o"+t.valueOf().toString()+"",t=Po.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Kr(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Or={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},lm=["Webkit","ms","Moz","O"];Object.keys(Or).forEach(function(e){lm.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Or[t]=Or[e]})});function uc(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Or.hasOwnProperty(e)&&Or[e]?(""+t).trim():t+"px"}function cc(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,o=uc(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,o):e[n]=o}}var am=ve({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Dl(e,t){if(t){if(am[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(z(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(z(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(z(61))}if(t.style!=null&&typeof t.style!="object")throw Error(z(62))}}function jl(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Ul=null;function Ca(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Vl=null,or=null,ir=null;function eu(e){if(e=uo(e)){if(typeof Vl!="function")throw Error(z(280));var t=e.stateNode;t&&(t=Mi(t),Vl(e.stateNode,e.type,t))}}function dc(e){or?ir?ir.push(e):ir=[e]:or=e}function pc(){if(or){var e=or,t=ir;if(ir=or=null,eu(e),t)for(e=0;e>>=0,e===0?32:31-(vm(e)/xm|0)|0}var Mo=64,To=4194304;function Tr(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function si(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,o=e.suspendedLanes,i=e.pingedLanes,l=n&268435455;if(l!==0){var s=l&~o;s!==0?r=Tr(s):(i&=l,i!==0&&(r=Tr(i)))}else l=n&~o,l!==0?r=Tr(l):i!==0&&(r=Tr(i));if(r===0)return 0;if(t!==0&&t!==r&&(t&o)===0&&(o=r&-r,i=t&-t,o>=i||o===16&&(i&4194240)!==0))return t;if((r&4)!==0&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function ao(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-_t(t),e[t]=n}function Sm(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=$r),uu=" ",cu=!1;function Mc(e,t){switch(e){case"keyup":return Xm.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Tc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Gn=!1;function Jm(e,t){switch(e){case"compositionend":return Tc(t);case"keypress":return t.which!==32?null:(cu=!0,uu);case"textInput":return e=t.data,e===uu&&cu?null:e;default:return null}}function ef(e,t){if(Gn)return e==="compositionend"||!Oa&&Mc(e,t)?(e=Ac(),Qo=Ma=on=null,Gn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=mu(n)}}function $c(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?$c(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Fc(){for(var e=window,t=oi();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=oi(e.document)}return t}function Ia(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function cf(e){var t=Fc(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&$c(n.ownerDocument.documentElement,n)){if(r!==null&&Ia(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var o=n.textContent.length,i=Math.min(r.start,o);r=r.end===void 0?i:Math.min(r.end,o),!e.extend&&i>r&&(o=r,r=i,i=o),o=fu(n,i);var l=fu(n,r);o&&l&&(e.rangeCount!==1||e.anchorNode!==o.node||e.anchorOffset!==o.offset||e.focusNode!==l.node||e.focusOffset!==l.offset)&&(t=t.createRange(),t.setStart(o.node,o.offset),e.removeAllRanges(),i>r?(e.addRange(t),e.extend(l.node,l.offset)):(t.setEnd(l.node,l.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Yn=null,Yl=null,Dr=null,Ql=!1;function gu(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Ql||Yn==null||Yn!==oi(r)||(r=Yn,"selectionStart"in r&&Ia(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),Dr&&Xr(Dr,r)||(Dr=r,r=di(Yl,"onSelect"),0Xn||(e.current=ta[Xn],ta[Xn]=null,Xn--)}function ae(e,t){Xn++,ta[Xn]=e.current,e.current=t}var hn={},De=vn(hn),Xe=vn(!1),An=hn;function cr(e,t){var n=e.type.contextTypes;if(!n)return hn;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var o={},i;for(i in n)o[i]=t[i];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=o),o}function Ze(e){return e=e.childContextTypes,e!=null}function mi(){de(Xe),de(De)}function Nu(e,t,n){if(De.current!==hn)throw Error(z(168));ae(De,t),ae(Xe,n)}function Gc(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var o in r)if(!(o in t))throw Error(z(108,om(e)||"Unknown",o));return ve({},n,r)}function fi(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||hn,An=De.current,ae(De,e),ae(Xe,Xe.current),!0}function Eu(e,t,n){var r=e.stateNode;if(!r)throw Error(z(169));n?(e=Gc(e,t,An),r.__reactInternalMemoizedMergedChildContext=e,de(Xe),de(De),ae(De,e)):de(Xe),ae(Xe,n)}var Ft=null,Ti=!1,wl=!1;function Yc(e){Ft===null?Ft=[e]:Ft.push(e)}function wf(e){Ti=!0,Yc(e)}function xn(){if(!wl&&Ft!==null){wl=!0;var e=0,t=ee;try{var n=Ft;for(ee=1;e>=l,o-=l,Dt=1<<32-_t(t)+o|n<R?(j=M,M=null):j=M.sibling;var $=h(p,M,f[R],k);if($===null){M===null&&(M=j);break}e&&M&&$.alternate===null&&t(p,M),c=i($,c,R),P===null?_=$:P.sibling=$,P=$,M=j}if(R===f.length)return n(p,M),pe&&Sn(p,R),_;if(M===null){for(;RR?(j=M,M=null):j=M.sibling;var ke=h(p,M,$.value,k);if(ke===null){M===null&&(M=j);break}e&&M&&ke.alternate===null&&t(p,M),c=i(ke,c,R),P===null?_=ke:P.sibling=ke,P=ke,M=j}if($.done)return n(p,M),pe&&Sn(p,R),_;if(M===null){for(;!$.done;R++,$=f.next())$=y(p,$.value,k),$!==null&&(c=i($,c,R),P===null?_=$:P.sibling=$,P=$);return pe&&Sn(p,R),_}for(M=r(p,M);!$.done;R++,$=f.next())$=g(M,p,R,$.value,k),$!==null&&(e&&$.alternate!==null&&M.delete($.key===null?R:$.key),c=i($,c,R),P===null?_=$:P.sibling=$,P=$);return e&&M.forEach(function(re){return t(p,re)}),pe&&Sn(p,R),_}function C(p,c,f,k){if(typeof f=="object"&&f!==null&&f.type===Wn&&f.key===null&&(f=f.props.children),typeof f=="object"&&f!==null){switch(f.$$typeof){case zo:e:{for(var _=f.key,P=c;P!==null;){if(P.key===_){if(_=f.type,_===Wn){if(P.tag===7){n(p,P.sibling),c=o(P,f.props.children),c.return=p,p=c;break e}}else if(P.elementType===_||typeof _=="object"&&_!==null&&_.$$typeof===en&&Lu(_)===P.type){n(p,P.sibling),c=o(P,f.props),c.ref=Cr(p,P,f),c.return=p,p=c;break e}n(p,P);break}else t(p,P);P=P.sibling}f.type===Wn?(c=zn(f.props.children,p.mode,k,f.key),c.return=p,p=c):(k=ri(f.type,f.key,f.props,null,p.mode,k),k.ref=Cr(p,c,f),k.return=p,p=k)}return l(p);case Kn:e:{for(P=f.key;c!==null;){if(c.key===P)if(c.tag===4&&c.stateNode.containerInfo===f.containerInfo&&c.stateNode.implementation===f.implementation){n(p,c.sibling),c=o(c,f.children||[]),c.return=p,p=c;break e}else{n(p,c);break}else t(p,c);c=c.sibling}c=Ll(f,p.mode,k),c.return=p,p=c}return l(p);case en:return P=f._init,C(p,c,P(f._payload),k)}if(Mr(f))return E(p,c,f,k);if(Sr(f))return N(p,c,f,k);Ho(p,f)}return typeof f=="string"&&f!==""||typeof f=="number"?(f=""+f,c!==null&&c.tag===6?(n(p,c.sibling),c=o(c,f),c.return=p,p=c):(n(p,c),c=Cl(f,p.mode,k),c.return=p,p=c),l(p)):n(p,c)}return C}var pr=Zc(!0),Jc=Zc(!1),yi=vn(null),vi=null,er=null,ja=null;function Ua(){ja=er=vi=null}function Va(e){var t=yi.current;de(yi),e._currentValue=t}function oa(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function ar(e,t){vi=e,ja=er=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(qe=!0),e.firstContext=null)}function vt(e){var t=e._currentValue;if(ja!==e)if(e={context:e,memoizedValue:t,next:null},er===null){if(vi===null)throw Error(z(308));er=e,vi.dependencies={lanes:0,firstContext:e}}else er=er.next=e;return t}var _n=null;function Ba(e){_n===null?_n=[e]:_n.push(e)}function ed(e,t,n,r){var o=t.interleaved;return o===null?(n.next=n,Ba(t)):(n.next=o.next,o.next=n),t.interleaved=n,Ht(e,r)}function Ht(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var tn=!1;function Ha(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function td(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Ut(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function dn(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,(Y&2)!==0){var o=r.pending;return o===null?t.next=t:(t.next=o.next,o.next=t),r.pending=t,Ht(e,n)}return o=r.interleaved,o===null?(t.next=t,Ba(r)):(t.next=o.next,o.next=t),r.interleaved=t,Ht(e,n)}function Xo(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,za(e,n)}}function zu(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var o=null,i=null;if(n=n.firstBaseUpdate,n!==null){do{var l={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};i===null?o=i=l:i=i.next=l,n=n.next}while(n!==null);i===null?o=i=t:i=i.next=t}else o=i=t;n={baseState:r.baseState,firstBaseUpdate:o,lastBaseUpdate:i,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function xi(e,t,n,r){var o=e.updateQueue;tn=!1;var i=o.firstBaseUpdate,l=o.lastBaseUpdate,s=o.shared.pending;if(s!==null){o.shared.pending=null;var u=s,d=u.next;u.next=null,l===null?i=d:l.next=d,l=u;var m=e.alternate;m!==null&&(m=m.updateQueue,s=m.lastBaseUpdate,s!==l&&(s===null?m.firstBaseUpdate=d:s.next=d,m.lastBaseUpdate=u))}if(i!==null){var y=o.baseState;l=0,m=d=u=null,s=i;do{var h=s.lane,g=s.eventTime;if((r&h)===h){m!==null&&(m=m.next={eventTime:g,lane:0,tag:s.tag,payload:s.payload,callback:s.callback,next:null});e:{var E=e,N=s;switch(h=t,g=n,N.tag){case 1:if(E=N.payload,typeof E=="function"){y=E.call(g,y,h);break e}y=E;break e;case 3:E.flags=E.flags&-65537|128;case 0:if(E=N.payload,h=typeof E=="function"?E.call(g,y,h):E,h==null)break e;y=ve({},y,h);break e;case 2:tn=!0}}s.callback!==null&&s.lane!==0&&(e.flags|=64,h=o.effects,h===null?o.effects=[s]:h.push(s))}else g={eventTime:g,lane:h,tag:s.tag,payload:s.payload,callback:s.callback,next:null},m===null?(d=m=g,u=y):m=m.next=g,l|=h;if(s=s.next,s===null){if(s=o.shared.pending,s===null)break;h=s,s=h.next,h.next=null,o.lastBaseUpdate=h,o.shared.pending=null}}while(!0);if(m===null&&(u=y),o.baseState=u,o.firstBaseUpdate=d,o.lastBaseUpdate=m,t=o.shared.interleaved,t!==null){o=t;do l|=o.lane,o=o.next;while(o!==t)}else i===null&&(o.shared.lanes=0);Tn|=l,e.lanes=l,e.memoizedState=y}}function Au(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=kl.transition;kl.transition={};try{e(!1),t()}finally{ee=n,kl.transition=r}}function vd(){return xt().memoizedState}function Nf(e,t,n){var r=mn(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},xd(e))wd(t,n);else if(n=ed(e,t,n,r),n!==null){var o=Ke();Ct(n,e,r,o),bd(n,t,r)}}function Ef(e,t,n){var r=mn(e),o={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(xd(e))wd(t,o);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var l=t.lastRenderedState,s=i(l,n);if(o.hasEagerState=!0,o.eagerState=s,Lt(s,l)){var u=t.interleaved;u===null?(o.next=o,Ba(t)):(o.next=u.next,u.next=o),t.interleaved=o;return}}catch{}finally{}n=ed(e,t,o,r),n!==null&&(o=Ke(),Ct(n,e,r,o),bd(n,t,r))}}function xd(e){var t=e.alternate;return e===ye||t!==null&&t===ye}function wd(e,t){jr=bi=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function bd(e,t,n){if((n&4194240)!==0){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,za(e,n)}}var ki={readContext:vt,useCallback:Ie,useContext:Ie,useEffect:Ie,useImperativeHandle:Ie,useInsertionEffect:Ie,useLayoutEffect:Ie,useMemo:Ie,useReducer:Ie,useRef:Ie,useState:Ie,useDebugValue:Ie,useDeferredValue:Ie,useTransition:Ie,useMutableSource:Ie,useSyncExternalStore:Ie,useId:Ie,unstable_isNewReconciler:!1},_f={readContext:vt,useCallback:function(e,t){return Mt().memoizedState=[e,t===void 0?null:t],e},useContext:vt,useEffect:Mu,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Jo(4194308,4,md.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Jo(4194308,4,e,t)},useInsertionEffect:function(e,t){return Jo(4,2,e,t)},useMemo:function(e,t){var n=Mt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Mt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=Nf.bind(null,ye,e),[r.memoizedState,e]},useRef:function(e){var t=Mt();return e={current:e},t.memoizedState=e},useState:Pu,useDebugValue:Za,useDeferredValue:function(e){return Mt().memoizedState=e},useTransition:function(){var e=Pu(!1),t=e[0];return e=Sf.bind(null,e[1]),Mt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=ye,o=Mt();if(pe){if(n===void 0)throw Error(z(407));n=n()}else{if(n=t(),Ae===null)throw Error(z(349));(Mn&30)!==0||id(r,t,n)}o.memoizedState=n;var i={value:n,getSnapshot:t};return o.queue=i,Mu(ad.bind(null,r,i,e),[e]),r.flags|=2048,io(9,ld.bind(null,r,i,n,t),void 0,null),n},useId:function(){var e=Mt(),t=Ae.identifierPrefix;if(pe){var n=jt,r=Dt;n=(r&~(1<<32-_t(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=ro++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=l.createElement(n,{is:r.is}):(e=l.createElement(n),n==="select"&&(l=e,r.multiple?l.multiple=!0:r.size&&(l.size=r.size))):e=l.createElementNS(e,n),e[Tt]=t,e[eo]=r,Pd(e,t,!1,!1),t.stateNode=e;e:{switch(l=jl(n,r),n){case"dialog":ce("cancel",e),ce("close",e),o=r;break;case"iframe":case"object":case"embed":ce("load",e),o=r;break;case"video":case"audio":for(o=0;ogr&&(t.flags|=128,r=!0,Lr(i,!1),t.lanes=4194304)}else{if(!r)if(e=wi(l),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Lr(i,!0),i.tail===null&&i.tailMode==="hidden"&&!l.alternate&&!pe)return $e(t),null}else 2*be()-i.renderingStartTime>gr&&n!==1073741824&&(t.flags|=128,r=!0,Lr(i,!1),t.lanes=4194304);i.isBackwards?(l.sibling=t.child,t.child=l):(n=i.last,n!==null?n.sibling=l:t.child=l,i.last=l)}return i.tail!==null?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=be(),t.sibling=null,n=he.current,ae(he,r?n&1|2:n&1),t):($e(t),null);case 22:case 23:return os(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&(t.mode&1)!==0?(tt&1073741824)!==0&&($e(t),t.subtreeFlags&6&&(t.flags|=8192)):$e(t),null;case 24:return null;case 25:return null}throw Error(z(156,t.tag))}function Rf(e,t){switch(Fa(t),t.tag){case 1:return Ze(t.type)&&mi(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return mr(),de(Xe),de(De),Ga(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return Wa(t),null;case 13:if(de(he),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(z(340));dr()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return de(he),null;case 4:return mr(),null;case 10:return Va(t.type._context),null;case 22:case 23:return os(),null;case 24:return null;default:return null}}var Wo=!1,Fe=!1,Of=typeof WeakSet=="function"?WeakSet:Set,I=null;function tr(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){we(e,t,r)}else n.current=null}function ma(e,t,n){try{n()}catch(r){we(e,t,r)}}var Bu=!1;function If(e,t){if(ql=ui,e=Fc(),Ia(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var o=r.anchorOffset,i=r.focusNode;r=r.focusOffset;try{n.nodeType,i.nodeType}catch{n=null;break e}var l=0,s=-1,u=-1,d=0,m=0,y=e,h=null;t:for(;;){for(var g;y!==n||o!==0&&y.nodeType!==3||(s=l+o),y!==i||r!==0&&y.nodeType!==3||(u=l+r),y.nodeType===3&&(l+=y.nodeValue.length),(g=y.firstChild)!==null;)h=y,y=g;for(;;){if(y===e)break t;if(h===n&&++d===o&&(s=l),h===i&&++m===r&&(u=l),(g=y.nextSibling)!==null)break;y=h,h=y.parentNode}y=g}n=s===-1||u===-1?null:{start:s,end:u}}else n=null}n=n||{start:0,end:0}}else n=null;for(Xl={focusedElem:e,selectionRange:n},ui=!1,I=t;I!==null;)if(t=I,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,I=e;else for(;I!==null;){t=I;try{var E=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(E!==null){var N=E.memoizedProps,C=E.memoizedState,p=t.stateNode,c=p.getSnapshotBeforeUpdate(t.elementType===t.type?N:St(t.type,N),C);p.__reactInternalSnapshotBeforeUpdate=c}break;case 3:var f=t.stateNode.containerInfo;f.nodeType===1?f.textContent="":f.nodeType===9&&f.documentElement&&f.removeChild(f.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(z(163))}}catch(k){we(t,t.return,k)}if(e=t.sibling,e!==null){e.return=t.return,I=e;break}I=t.return}return E=Bu,Bu=!1,E}function Ur(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var o=r=r.next;do{if((o.tag&e)===e){var i=o.destroy;o.destroy=void 0,i!==void 0&&ma(t,n,i)}o=o.next}while(o!==r)}}function Ii(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function fa(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function Rd(e){var t=e.alternate;t!==null&&(e.alternate=null,Rd(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Tt],delete t[eo],delete t[ea],delete t[vf],delete t[xf])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Od(e){return e.tag===5||e.tag===3||e.tag===4}function Hu(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Od(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function ga(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=pi));else if(r!==4&&(e=e.child,e!==null))for(ga(e,t,n),e=e.sibling;e!==null;)ga(e,t,n),e=e.sibling}function ha(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(ha(e,t,n),e=e.sibling;e!==null;)ha(e,t,n),e=e.sibling}var Pe=null,Nt=!1;function Jt(e,t,n){for(n=n.child;n!==null;)Id(e,t,n),n=n.sibling}function Id(e,t,n){if(Rt&&typeof Rt.onCommitFiberUnmount=="function")try{Rt.onCommitFiberUnmount(Li,n)}catch{}switch(n.tag){case 5:Fe||tr(n,t);case 6:var r=Pe,o=Nt;Pe=null,Jt(e,t,n),Pe=r,Nt=o,Pe!==null&&(Nt?(e=Pe,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Pe.removeChild(n.stateNode));break;case 18:Pe!==null&&(Nt?(e=Pe,n=n.stateNode,e.nodeType===8?xl(e.parentNode,n):e.nodeType===1&&xl(e,n),Qr(e)):xl(Pe,n.stateNode));break;case 4:r=Pe,o=Nt,Pe=n.stateNode.containerInfo,Nt=!0,Jt(e,t,n),Pe=r,Nt=o;break;case 0:case 11:case 14:case 15:if(!Fe&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){o=r=r.next;do{var i=o,l=i.destroy;i=i.tag,l!==void 0&&((i&2)!==0||(i&4)!==0)&&ma(n,t,l),o=o.next}while(o!==r)}Jt(e,t,n);break;case 1:if(!Fe&&(tr(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(s){we(n,t,s)}Jt(e,t,n);break;case 21:Jt(e,t,n);break;case 22:n.mode&1?(Fe=(r=Fe)||n.memoizedState!==null,Jt(e,t,n),Fe=r):Jt(e,t,n);break;default:Jt(e,t,n)}}function Ku(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Of),t.forEach(function(r){var o=Kf.bind(null,e,r);n.has(r)||(n.add(r),r.then(o,o))})}}function kt(e,t){var n=t.deletions;if(n!==null)for(var r=0;ro&&(o=l),r&=~i}if(r=o,r=be()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Ff(r/1960))-r,10e?16:e,ln===null)var r=!1;else{if(e=ln,ln=null,Ei=0,(Y&6)!==0)throw Error(z(331));var o=Y;for(Y|=4,I=e.current;I!==null;){var i=I,l=i.child;if((I.flags&16)!==0){var s=i.deletions;if(s!==null){for(var u=0;ube()-ns?Ln(e,0):ts|=n),Je(e,t)}function Hd(e,t){t===0&&((e.mode&1)===0?t=1:(t=To,To<<=1,(To&130023424)===0&&(To=4194304)));var n=Ke();e=Ht(e,t),e!==null&&(ao(e,t,n),Je(e,n))}function Hf(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Hd(e,n)}function Kf(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,o=e.memoizedState;o!==null&&(n=o.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(z(314))}r!==null&&r.delete(t),Hd(e,n)}var Kd;Kd=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||Xe.current)qe=!0;else{if((e.lanes&n)===0&&(t.flags&128)===0)return qe=!1,Mf(e,t,n);qe=(e.flags&131072)!==0}else qe=!1,pe&&(t.flags&1048576)!==0&&Qc(t,hi,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;ei(e,t),e=t.pendingProps;var o=cr(t,De.current);ar(t,n),o=Qa(null,t,r,e,o,n);var i=qa();return t.flags|=1,typeof o=="object"&&o!==null&&typeof o.render=="function"&&o.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Ze(r)?(i=!0,fi(t)):i=!1,t.memoizedState=o.state!==null&&o.state!==void 0?o.state:null,Ha(t),o.updater=Oi,t.stateNode=o,o._reactInternals=t,la(t,r,e,n),t=ua(null,t,r,!0,i,n)):(t.tag=0,pe&&i&&$a(t),He(null,t,o,n),t=t.child),t;case 16:r=t.elementType;e:{switch(ei(e,t),e=t.pendingProps,o=r._init,r=o(r._payload),t.type=r,o=t.tag=Gf(r),e=St(r,e),o){case 0:t=sa(null,t,r,e,n);break e;case 1:t=ju(null,t,r,e,n);break e;case 11:t=Fu(null,t,r,e,n);break e;case 14:t=Du(null,t,r,St(r.type,e),n);break e}throw Error(z(306,r,""))}return t;case 0:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:St(r,o),sa(e,t,r,o,n);case 1:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:St(r,o),ju(e,t,r,o,n);case 3:e:{if(Ld(t),e===null)throw Error(z(387));r=t.pendingProps,i=t.memoizedState,o=i.element,td(e,t),xi(t,r,null,n);var l=t.memoizedState;if(r=l.element,i.isDehydrated)if(i={element:r,isDehydrated:!1,cache:l.cache,pendingSuspenseBoundaries:l.pendingSuspenseBoundaries,transitions:l.transitions},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){o=fr(Error(z(423)),t),t=Uu(e,t,r,n,o);break e}else if(r!==o){o=fr(Error(z(424)),t),t=Uu(e,t,r,n,o);break e}else for(nt=cn(t.stateNode.containerInfo.firstChild),rt=t,pe=!0,Et=null,n=Jc(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(dr(),r===o){t=Kt(e,t,n);break e}He(e,t,r,n)}t=t.child}return t;case 5:return nd(t),e===null&&ra(t),r=t.type,o=t.pendingProps,i=e!==null?e.memoizedProps:null,l=o.children,Zl(r,o)?l=null:i!==null&&Zl(r,i)&&(t.flags|=32),Cd(e,t),He(e,t,l,n),t.child;case 6:return e===null&&ra(t),null;case 13:return zd(e,t,n);case 4:return Ka(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=pr(t,null,r,n):He(e,t,r,n),t.child;case 11:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:St(r,o),Fu(e,t,r,o,n);case 7:return He(e,t,t.pendingProps,n),t.child;case 8:return He(e,t,t.pendingProps.children,n),t.child;case 12:return He(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,o=t.pendingProps,i=t.memoizedProps,l=o.value,ae(yi,r._currentValue),r._currentValue=l,i!==null)if(Lt(i.value,l)){if(i.children===o.children&&!Xe.current){t=Kt(e,t,n);break e}}else for(i=t.child,i!==null&&(i.return=t);i!==null;){var s=i.dependencies;if(s!==null){l=i.child;for(var u=s.firstContext;u!==null;){if(u.context===r){if(i.tag===1){u=Ut(-1,n&-n),u.tag=2;var d=i.updateQueue;if(d!==null){d=d.shared;var m=d.pending;m===null?u.next=u:(u.next=m.next,m.next=u),d.pending=u}}i.lanes|=n,u=i.alternate,u!==null&&(u.lanes|=n),oa(i.return,n,t),s.lanes|=n;break}u=u.next}}else if(i.tag===10)l=i.type===t.type?null:i.child;else if(i.tag===18){if(l=i.return,l===null)throw Error(z(341));l.lanes|=n,s=l.alternate,s!==null&&(s.lanes|=n),oa(l,n,t),l=i.sibling}else l=i.child;if(l!==null)l.return=i;else for(l=i;l!==null;){if(l===t){l=null;break}if(i=l.sibling,i!==null){i.return=l.return,l=i;break}l=l.return}i=l}He(e,t,o.children,n),t=t.child}return t;case 9:return o=t.type,r=t.pendingProps.children,ar(t,n),o=vt(o),r=r(o),t.flags|=1,He(e,t,r,n),t.child;case 14:return r=t.type,o=St(r,t.pendingProps),o=St(r.type,o),Du(e,t,r,o,n);case 15:return Ed(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:St(r,o),ei(e,t),t.tag=1,Ze(r)?(e=!0,fi(t)):e=!1,ar(t,n),kd(t,r,o),la(t,r,o,n),ua(null,t,r,!0,e,n);case 19:return Ad(e,t,n);case 22:return _d(e,t,n)}throw Error(z(156,t.tag))};function Wd(e,t){return xc(e,t)}function Wf(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function ht(e,t,n,r){return new Wf(e,t,n,r)}function ls(e){return e=e.prototype,!(!e||!e.isReactComponent)}function Gf(e){if(typeof e=="function")return ls(e)?1:0;if(e!=null){if(e=e.$$typeof,e===Ea)return 11;if(e===_a)return 14}return 2}function fn(e,t){var n=e.alternate;return n===null?(n=ht(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function ri(e,t,n,r,o,i){var l=2;if(r=e,typeof e=="function")ls(e)&&(l=1);else if(typeof e=="string")l=5;else e:switch(e){case Wn:return zn(n.children,o,i,t);case Na:l=8,o|=8;break;case Al:return e=ht(12,n,t,o|2),e.elementType=Al,e.lanes=i,e;case Pl:return e=ht(13,n,t,o),e.elementType=Pl,e.lanes=i,e;case Ml:return e=ht(19,n,t,o),e.elementType=Ml,e.lanes=i,e;case nc:return Fi(n,o,i,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case ec:l=10;break e;case tc:l=9;break e;case Ea:l=11;break e;case _a:l=14;break e;case en:l=16,r=null;break e}throw Error(z(130,e==null?e:typeof e,""))}return t=ht(l,n,t,o),t.elementType=e,t.type=r,t.lanes=i,t}function zn(e,t,n,r){return e=ht(7,e,r,t),e.lanes=n,e}function Fi(e,t,n,r){return e=ht(22,e,r,t),e.elementType=nc,e.lanes=n,e.stateNode={isHidden:!1},e}function Cl(e,t,n){return e=ht(6,e,null,t),e.lanes=n,e}function Ll(e,t,n){return t=ht(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Yf(e,t,n,r,o){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=dl(0),this.expirationTimes=dl(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=dl(0),this.identifierPrefix=r,this.onRecoverableError=o,this.mutableSourceEagerHydrationData=null}function as(e,t,n,r,o,i,l,s,u){return e=new Yf(e,t,n,s,u),t===1?(t=1,i===!0&&(t|=8)):t=0,i=ht(3,null,null,t),e.current=i,i.stateNode=e,i.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Ha(i),e}function Qf(e,t,n){var r=3{"use strict";function Xd(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(Xd)}catch(e){console.error(e)}}Xd(),Zd.exports=qd()});var tp=It(ds=>{"use strict";var ep=Jd();ds.createRoot=ep.createRoot,ds.hydrateRoot=ep.hydrateRoot;var dh});var rp=It(Bi=>{"use strict";var eg=So(),tg=Symbol.for("react.element"),ng=Symbol.for("react.fragment"),rg=Object.prototype.hasOwnProperty,og=eg.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,ig={key:!0,ref:!0,__self:!0,__source:!0};function np(e,t,n){var r,o={},i=null,l=null;n!==void 0&&(i=""+n),t.key!==void 0&&(i=""+t.key),t.ref!==void 0&&(l=t.ref);for(r in t)rg.call(t,r)&&!ig.hasOwnProperty(r)&&(o[r]=t[r]);if(e&&e.defaultProps)for(r in t=e.defaultProps,t)o[r]===void 0&&(o[r]=t[r]);return{$$typeof:tg,type:e,key:i,ref:l,props:o,_owner:og.current}}Bi.Fragment=ng;Bi.jsx=np;Bi.jsxs=np});var ps=It((hh,op)=>{"use strict";op.exports=rp()});var S=xo(So(),1),xp=xo(tp(),1);var a=xo(ps(),1),wp={hidden:"UKRYTY",connecting:"\u0141\u0104CZENIE",connected:"ONLINE \xB7 LIVE",replaying:"ONLINE \xB7 REPLAY",verifying:"ONLINE \xB7 WERYFIKACJA",ready:"ONLINE \xB7 GOTOWY",failed:"ONLINE \xB7 B\u0141\u0104D STANU",offline:"OFFLINE \xB7 ZAPIS",unselected:"OFFLINE \xB7 BRAK CELU"},ip=.25,Fn=1,lp=4;function lg(e){let t=window.getComputedStyle(e).overflowY;return t==="auto"||t==="scroll"||t==="hidden"||t==="clip"}function bp(e){let t=e.getBoundingClientRect(),n=e.offsetHeight||e.clientHeight,r=n>0?t.height/n:1,o=t.top+e.clientTop*r;return{top:o,bottom:o+e.clientHeight*r}}function kp(e){let t=document.querySelector(".viewer-chrome");if(t?.classList.contains("is-nvim-fit"))return null;let n=0,r=window.innerHeight;if(t){let o=t.getBoundingClientRect(),i=e.getBoundingClientRect();o.lefti.left&&o.bottom>0&&o.topFn?{top:n,bottom:r}:null}function ag(e){let t=kp(e);if(!t)return null;let{top:n,bottom:r}=t;for(let o=e.parentElement;o&&o!==document.body&&o!==document.documentElement;o=o.parentElement){if(!lg(o))continue;let i=bp(o);n=Math.max(n,i.top),r=Math.min(r,i.bottom)}return r-n>Fn?{top:n,bottom:r}:null}function Wi(){return document.scrollingElement??document.documentElement}function sg(e){let t=[];for(let r=e.parentElement;r&&r!==document.body&&r!==document.documentElement;r=r.parentElement){let o=window.getComputedStyle(r).overflowY;(o==="auto"||o==="scroll")&&r.scrollHeight-r.clientHeight>1&&t.push(r)}let n=Wi();return t.includes(n)||t.push(n),t}function ug(e){if(e===Wi())return 1;let t=e.getBoundingClientRect(),n=e.offsetHeight||e.clientHeight,r=n>0?t.height/n:1;return Number.isFinite(r)&&r>0?r:1}function Hi(e,t){let n=e.style.scrollBehavior;e.style.scrollBehavior="auto",t.left!=null&&(e.scrollLeft=t.left),t.top!=null&&(e.scrollTop=t.top),e.style.scrollBehavior=n}function cg(e,t){let n=e.closest(".sheet-content");if(!n)return 0;let r=bp(n),o=0;if(r.bottom<=t.top?o=r.bottom-t.bottom:r.top>=t.bottom&&(o=r.top-t.top),Math.abs(o)<=Fn)return 0;let i=Wi(),l=i.scrollTop,s=Math.max(0,i.scrollHeight-i.clientHeight);return Hi(i,{top:Math.max(0,Math.min(s,l+o))}),i.scrollTop-l}function ap(e,t){let n=t;for(let r of sg(e)){if(Math.abs(n)<=Fn)break;let o=ug(r),i=Math.max(0,r.scrollHeight-r.clientHeight),l=r.scrollTop,s=Math.max(0,Math.min(i,l+n/o));Hi(r,{top:s}),n-=(r.scrollTop-l)*o}return t-n}function sp(e,t){let n=e.getBoundingClientRect(),r=(n.top+n.bottom)/2,o=t.bottom-t.top,i=t.top+o*ip,l=t.bottom-o*ip;return rl?r-l:0}var Yt=({html:e,className:t})=>(0,a.jsx)("div",{className:t,dangerouslySetInnerHTML:{__html:e}});function Sp(e,t){e.setAttribute("preserveAspectRatio","xMinYMin meet"),t&&(e.setAttribute("viewBox",`${t.x} ${t.y} ${t.width} ${t.height}`),e.setAttribute("width",`${t.width}px`),e.setAttribute("height",`${t.height}px`),e.dataset.tileIndex=String(t.index),e.dataset.tileRow=String(t.row),e.dataset.tileColumn=String(t.column))}function dg(e){return e.normalize("NFKD").replace(/[\u0300-\u036f]/g,"").toLowerCase().replace(/[^a-z0-9_.:-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,120)||"uml-element"}function fs(e){let t=e;if(typeof t.getBBox!="function")return null;try{let n=t.getBBox();return[n.x,n.y,n.width,n.height].every(Number.isFinite)?{x:n.x,y:n.y,width:n.width,height:n.height}:null}catch{return null}}function pg(e){let t=fs(e);if(!t)return null;let n=Number(e.dataset.umlLayoutOriginX),r=Number(e.dataset.umlLayoutOriginY),o=Number(e.dataset.umlLayoutDx),i=Number(e.dataset.umlLayoutDy),l=Number(e.dataset.umlLayoutSx),s=Number(e.dataset.umlLayoutSy);return[n,r,o,i,l,s].every(Number.isFinite)?{x:n+(t.x-n)*l+o,y:r+(t.y-r)*s+i,width:t.width*l,height:t.height*s}:t}function Np(e){e.dataset.umlLayoutRemembered!=="true"&&(e.dataset.umlLayoutRemembered="true",e.dataset.umlLayoutOriginalTransform=e.getAttribute("transform")??"",e.dataset.umlLayoutOriginalVisibility=e.getAttribute("visibility")??"",e.dataset.umlLayoutOriginalStyle=e.getAttribute("style")??"",e instanceof SVGTextElement&&(e.dataset.umlLayoutOriginalText=e.textContent??"",e.dataset.umlLayoutOriginalX=e.getAttribute("x")??"",e.dataset.umlLayoutOriginalTextAnchor=e.getAttribute("text-anchor")??"",e.dataset.umlLayoutOriginalFontFamily=e.getAttribute("font-family")??"",e.dataset.umlLayoutOriginalFontSize=e.getAttribute("font-size")??"",e.dataset.umlLayoutOriginalFontWeight=e.getAttribute("font-weight")??""),e instanceof SVGRectElement&&(e.dataset.umlLayoutOriginalFill=e.getAttribute("fill")??"",e.dataset.umlLayoutOriginalStroke=e.getAttribute("stroke")??"",e.dataset.umlLayoutOriginalStrokeWidth=e.getAttribute("stroke-width")??""))}function mg(e){e.querySelectorAll(".uml-layout-hit,.uml-layout-resize,.uml-layout-generated").forEach(t=>t.remove()),e.querySelectorAll("[data-uml-layout-remembered='true']").forEach(t=>{let n=(r,o)=>{o?t.setAttribute(r,o):t.removeAttribute(r)};n("transform",t.dataset.umlLayoutOriginalTransform),n("visibility",t.dataset.umlLayoutOriginalVisibility),n("style",t.dataset.umlLayoutOriginalStyle),t instanceof SVGTextElement&&(t.textContent=t.dataset.umlLayoutOriginalText??"",n("x",t.dataset.umlLayoutOriginalX),n("text-anchor",t.dataset.umlLayoutOriginalTextAnchor),n("font-family",t.dataset.umlLayoutOriginalFontFamily),n("font-size",t.dataset.umlLayoutOriginalFontSize),n("font-weight",t.dataset.umlLayoutOriginalFontWeight)),t instanceof SVGRectElement&&(n("fill",t.dataset.umlLayoutOriginalFill),n("stroke",t.dataset.umlLayoutOriginalStroke),n("stroke-width",t.dataset.umlLayoutOriginalStrokeWidth)),delete t.dataset.umlLayoutOriginX,delete t.dataset.umlLayoutOriginY,delete t.dataset.umlLayoutDx,delete t.dataset.umlLayoutDy,delete t.dataset.umlLayoutSx,delete t.dataset.umlLayoutSy,delete t.dataset.umlLayoutId})}function fg(e,t){let n=e.querySelector(":scope > g")??e,r=Array.from(n.children).filter(s=>s instanceof SVGElement),o=r.filter(s=>s instanceof SVGRectElement).map(s=>({rect:s,box:fs(s)})).filter(s=>!!(s.box&&s.box.width>=30&&s.box.height>=14)).sort((s,u)=>s.box.width*s.box.height-u.box.width*u.box.height),i=new Map;for(let s of r){let u=fs(s);if(!u)continue;let d=u.x+u.width/2,m=u.y+u.height/2,y=o.find(({box:h})=>d>=h.x-.5&&d<=h.x+h.width+.5&&m>=h.y-.5&&m<=h.y+h.height+.5);y&&i.set(s,y.rect)}let l=new Set;return o.map(({rect:s,box:u},d)=>{let m=r.filter(c=>i.get(c)===s);m.includes(s)||m.unshift(s);let y=m.filter(c=>c instanceof SVGTextElement).sort((c,f)=>Number(c.getAttribute("y"))-Number(f.getAttribute("y"))),h=(y[0]?.textContent??s.id??`element-${d+1}`).trim(),g=/^(\d{1,3})\b/.exec(h)?.[1]?.padStart(2,"0"),E=s.id||(g?t.get(g):"")||h,N=dg(E),C=N,p=2;for(;l.has(C);)C=`${N}-${p++}`;return l.add(C),m.forEach(Np),{id:C,box:u,rect:s,members:m,texts:y}})}function po(e){let t=e.texts[0],n=e.rect.getAttribute("style")??"",r=i=>new RegExp(`${i}\\s*:\\s*([^;]+)`,"i").exec(n)?.[1]?.trim(),o=Number(t?.getAttribute("font-size")??12);return{...e.box,z:0,scale:1,rotation:0,text:{title:(e.texts[0]?.textContent??"").trim(),description:e.texts.slice(1).map(i=>(i.textContent??"").trim()).join(` +`),lines:e.texts.map(i=>(i.textContent??"").trim()),font_family:t?.getAttribute("font-family")??"Monospace",font_size:Number.isFinite(o)?o:12,font_weight:Number(t?.getAttribute("font-weight")??400)||400,align:"left"},style:{fill:e.rect.getAttribute("fill")??r("fill")??"#edf6fa",stroke:e.rect.getAttribute("stroke")??r("stroke")??"#2c7794",stroke_width:Number(e.rect.getAttribute("stroke-width")??r("stroke-width")??1.5)}}}function gg(e,t){let n=Math.max(12,Number(t.width)||e.box.width),r=Math.max(12,Number(t.height)||e.box.height),o=Number.isFinite(Number(t.x))?Number(t.x):e.box.x,i=Number.isFinite(Number(t.y))?Number(t.y):e.box.y,l=n/Math.max(1,e.box.width),s=r/Math.max(1,e.box.height),u=o-e.box.x,d=i-e.box.y,m=`translate(${u} ${d}) translate(${e.box.x} ${e.box.y}) scale(${l} ${s}) translate(${-e.box.x} ${-e.box.y})`;e.members.forEach(g=>{Np(g);let E=g.dataset.umlLayoutOriginalTransform??"";g.setAttribute("transform",`${m}${E?` ${E}`:""}`),g.dataset.umlLayoutOriginX=String(e.box.x),g.dataset.umlLayoutOriginY=String(e.box.y),g.dataset.umlLayoutDx=String(u),g.dataset.umlLayoutDy=String(d),g.dataset.umlLayoutSx=String(l),g.dataset.umlLayoutSy=String(s),g.dataset.umlLayoutId=e.id});let y=t.text??{},h=y.lines??[y.title??"",...y.description?y.description.split(` +`):[]];e.texts.forEach((g,E)=>{E(e.tile?.page_height??0)?"landscape":"portrait",scale:1},elements:{},relations:{}}}function gs(e){if(!e)return{};let t=e.column===1&&e.columns>1?e.page_width-e.width:e.column===e.columns?0:Math.max(0,(e.page_width-e.width)/2),n=e.row===1&&e.rows>1?e.page_height-e.height:e.row===e.rows?0:Math.max(0,(e.page_height-e.height)/2);return{"--uml-tile-page-width":`${e.page_width}px`,"--uml-tile-page-height":`${e.page_height}px`,"--uml-tile-left":`${t}px`,"--uml-tile-top":`${n}px`}}function hg({html:e}){return(0,a.jsx)(Yt,{html:e})}function yg(e){let t=e.spread_count??1;if(t<=1)return null;let n=Math.max(1,Math.min(t,e.spread_index??1)),r=Math.max(1,Math.min(t,e.spread_columns??1)),o=n-1,i=Math.floor(o/r),l=o%r;return{left:l>0,right:l+10,down:n+r<=t,index:n,count:t}}function Ep({continuation:e}){if(!e)return null;let t=[e.left&&["left","\u2190","kontynuacja po lewej"],e.up&&["up","\u2191","kontynuacja powy\u017Cej"],e.down&&["down","\u2193","kontynuacja poni\u017Cej"],e.right&&["right","\u2192","kontynuacja po prawej"]].filter(Boolean);return(0,a.jsx)("span",{className:"uml-page-continuation","aria-label":`Logiczny diagram: strona ${e.index} z ${e.count}`,title:`Ten sam diagram \xB7 strona ${e.index}/${e.count}`,children:t.map(([n,r,o])=>(0,a.jsx)("i",{"data-direction":n,"aria-label":o,children:r},n))})}function up({side:e,html:t}){return(0,a.jsx)("aside",{className:`pdf-margin ${e}`,dangerouslySetInnerHTML:{__html:t}})}function hs({header:e,left:t="",right:n="",children:r,className:o=""}){return(0,a.jsx)("article",{className:`sheet ${o}`,children:(0,a.jsxs)("div",{className:"sheet-content",children:[(0,a.jsx)(hg,{html:e}),(0,a.jsx)(up,{side:"left",html:t}),(0,a.jsx)("section",{className:"pdf-main card-section",children:r}),(0,a.jsx)(up,{side:"right",html:n})]})})}function vg({viewpoints:e}){let t=e.filter(i=>i.status==="enabled"&&i.target_id),[n,r]=(0,S.useState)(t[0]?.id??"");(0,S.useEffect)(()=>{if(!t.length)return;let i=0,l=()=>{window.cancelAnimationFrame(i),i=window.requestAnimationFrame(()=>{let s=document.querySelector(".viewer-chrome")?.getBoundingClientRect().bottom??0,u=t.map(m=>({viewpoint:m,element:document.getElementById(m.target_id)})).filter(m=>!!m.element);if(!u.length)return;let d=u.reduce((m,y)=>{let h=Math.abs(y.element.getBoundingClientRect().top-s-18);return h{window.cancelAnimationFrame(i),window.removeEventListener("scroll",l),window.removeEventListener("resize",l)}},[e]);let o=i=>{if(i.status!=="enabled"||!i.target_id)return;let l=document.getElementById(i.target_id);if(!l)return;let u=document.querySelector(".viewer-chrome")?.getBoundingClientRect().height??0;r(i.id),window.scrollTo({top:Math.max(0,window.scrollY+l.getBoundingClientRect().top-u-8),behavior:"smooth"})};return(0,a.jsx)("nav",{className:"viewpoint-bar","aria-label":"Perspektywy architektury A1\u2013A8",children:e.map(i=>{let l=i.status==="unavailable";return(0,a.jsxs)("button",{type:"button",className:l?"is-unavailable":"","aria-current":!l&&n===i.id?"location":void 0,disabled:l,title:l?i.reason:i.subtitle,onClick:()=>o(i),children:[(0,a.jsx)("span",{children:i.id}),(0,a.jsx)("strong",{children:i.label}),l&&(0,a.jsx)("small",{children:"N/D"})]},i.id)})})}function xg({toc:e,libraryAvailable:t,libraryOpen:n,setLibraryOpen:r,scale:o,setScale:i,nvimVisible:l,setNvimVisible:s,nvimSyncMode:u,setNvimSyncMode:d,nvimControlMode:m,setNvimControlMode:y,nvimExpanded:h,setNvimExpanded:g,nvimFit:E,setNvimFit:N,allocatorVisible:C,setAllocatorVisible:p,hasAllocatorModel:c,diagramSpread:f,setDiagramSpread:k,hasDiagramSpread:_,nvimSummary:P,progress:M,progressError:R}){return(0,a.jsx)("nav",{className:"topbar",children:(0,a.jsxs)("div",{className:"topbar-inner",children:[t&&(0,a.jsxs)("button",{type:"button",className:`card-library-toggle${n?" is-active":""}`,"aria-controls":"side-panel-left-library","aria-expanded":n,"aria-label":n?"Alt+0: zamknij serie i karty":"Alt+0: poka\u017C serie i karty",title:n?"Alt+0 \xB7 Zamknij serie i karty":"Alt+0 \xB7 Serie i karty",onClick:()=>r(!n),children:[(0,a.jsx)("span",{"aria-hidden":"true",className:"card-library-toggle-icon",children:"\u2630"}),(0,a.jsx)("kbd",{children:"A0"}),(0,a.jsx)("span",{children:"KARTY"})]}),(0,a.jsxs)("details",{children:[(0,a.jsx)("summary",{children:"Spis tre\u015Bci"}),(0,a.jsx)("div",{className:"toc-links",children:e.map(j=>(0,a.jsx)("a",{href:`#${j.id}`,children:j.label},j.id))})]}),(0,a.jsxs)("div",{className:"topbar-nvim-section","aria-label":"Panel Neovima",children:[(0,a.jsx)("span",{className:"nvim-toolbar-boundary","aria-hidden":"true",children:"|"}),(0,a.jsx)("span",{className:"nvim-toolbar-context",children:"NVIM"}),(0,a.jsxs)("div",{className:"nvim-toolbar-actions","aria-label":"Narz\u0119dzia okna Neovima",children:[(0,a.jsxs)("button",{className:"viewer-nvim-toggle nvim-toolbar-button","aria-label":l?"Alt+1: ukryj Neovim":"Alt+1: poka\u017C Neovim",title:l?"Alt+1 \xB7 Ukryj Neovim":"Alt+1 \xB7 Poka\u017C Neovim","aria-pressed":l,onClick:()=>s(!l),children:[(0,a.jsx)("kbd",{className:"nvim-toolbar-key",children:"A1"}),(0,a.jsx)("span",{className:"nvim-toolbar-label",children:"PANEL"})]}),(0,a.jsxs)("button",{type:"button",className:`nvim-control-indicator nvim-toolbar-button${P.syncMode?" is-active":""}${P.syncReady?" is-engaged":""}`,"aria-label":`Alt+2: synchronizacja ${u?"w\u0142\u0105czona":"wy\u0142\u0105czona"}`,title:`Alt+2 \xB7 SYNC ${u?"ON":"OFF"}`,"aria-pressed":u,onClick:()=>d(!u),children:[(0,a.jsx)("kbd",{className:"nvim-toolbar-key",children:"A2"}),(0,a.jsx)("span",{className:"nvim-toolbar-label",children:"SYNC"})]}),(0,a.jsxs)("button",{type:"button",className:`nvim-work-indicator nvim-toolbar-button${m?" is-active":""}${P.controlEnabled?" is-engaged":""}`,"aria-label":`Alt+3: sterowanie ${u?m?"w\u0142\u0105czone":"wy\u0142\u0105czone":"niedost\u0119pne"}`,title:u?`Alt+3 \xB7 Sterowanie ${m?"ON":"OFF"}`:"Alt+3 \xB7 Sterowanie wymaga SYNC ON","aria-pressed":m,disabled:!u,onClick:()=>y(!m),children:[(0,a.jsx)("kbd",{className:"nvim-toolbar-key",children:"A3"}),(0,a.jsx)("span",{className:"nvim-toolbar-label",children:"CTRL"})]}),(0,a.jsxs)("button",{type:"button",className:`nvim-height-toggle nvim-toolbar-button${h?" is-active":""}`,"aria-label":`Alt+4: ${h?"przywr\xF3\u0107 normaln\u0105 wysoko\u015B\u0107":"ustaw wysoki panel"}`,title:h?"Alt+4 \xB7 Normalna wysoko\u015B\u0107":"Alt+4 \xB7 Wysoki panel 90%","aria-pressed":h,onClick:()=>g(!h),children:[(0,a.jsx)("kbd",{className:"nvim-toolbar-key",children:"A4"}),(0,a.jsx)("span",{className:"nvim-toolbar-label",children:"HIGH"})]}),(0,a.jsxs)("button",{type:"button",className:`nvim-height-toggle nvim-toolbar-button${E?" is-active":""}`,"aria-label":`Alt+5: automatyczne dopasowanie ${E?"w\u0142\u0105czone":"wy\u0142\u0105czone"}`,title:E?"Alt+5 \xB7 Wy\u0142\u0105cz auto-fit":"Alt+5 \xB7 Poka\u017C ca\u0142y Neovim","aria-pressed":E,onClick:()=>N(!E),children:[(0,a.jsx)("kbd",{className:"nvim-toolbar-key",children:"A5"}),(0,a.jsx)("span",{className:"nvim-toolbar-label",children:"FIT"})]}),c&&(0,a.jsxs)("button",{type:"button",className:`nvim-height-toggle nvim-toolbar-button${C?" is-active":""}`,"aria-label":`Alt+6: model alokatora ${C?"przypi\u0119ty":"ukryty"}`,title:C?"Alt+6 \xB7 Ukryj model alokatora":"Alt+6 \xB7 Przypnij model alokatora","aria-pressed":C,onClick:()=>p(!C),children:[(0,a.jsx)("kbd",{className:"nvim-toolbar-key",children:"A6"}),(0,a.jsx)("span",{className:"nvim-toolbar-label",children:"MODEL"})]}),_&&(0,a.jsxs)("button",{type:"button",className:`nvim-height-toggle nvim-toolbar-button${f?" is-active":""}`,"aria-label":`Alt+7: diagram UML ${f?"jako osobne kartki A4":"jako po\u0142\u0105czony spread"}`,title:f?"Alt+7 \xB7 Osobne kartki A4":"Alt+7 \xB7 Po\u0142\u0105cz strony UML","aria-pressed":f,onClick:()=>k(!f),children:[(0,a.jsx)("kbd",{className:"nvim-toolbar-key",children:"A7"}),(0,a.jsx)("span",{className:"nvim-toolbar-label",children:"UML"})]})]}),(0,a.jsx)("span",{className:"nvim-toolbar-boundary","aria-hidden":"true",children:"|"}),l&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("span",{className:`nvim-status nvim-status--${P.state}`,children:wp[P.state]}),(0,a.jsxs)("span",{className:"topbar-nvim-meta",title:P.message,children:[(0,a.jsx)("strong",{children:P.instance??"Neovim MCP"}),P.detail&&(0,a.jsx)("small",{children:P.detail})]}),(0,a.jsxs)("span",{className:"nvim-shortcut-strip","aria-label":"Skr\xF3ty panelu Neovima",children:[(0,a.jsx)("kbd",{children:"F1"})," reset \xB7 ",(0,a.jsx)("kbd",{children:"F2"})," sync \xB7 ",(0,a.jsx)("kbd",{children:"Alt+W"})," okna \xB7 ",(0,a.jsx)("kbd",{children:"F12"})," skr\xF3ty"]})]})]}),(0,a.jsx)(Wg,{progress:M,error:R}),(0,a.jsxs)("div",{className:"viewer-zoom-controls",children:[(0,a.jsxs)("output",{className:"viewer-zoom-status",children:["P\u0142\xF3tno ",Math.round(o*100),"% \xB7 Tre\u015B\u0107 100%"]}),(0,a.jsx)("button",{className:"viewer-zoom-reset",onClick:()=>i(2.18),children:"Reset zoom"})]})]})})}function cp(e,t){for(let n of e.sections)for(let r of n.assets){let o=r.interactive;if(!(o?.kind!=="uml-sequence"||!o.phases?.length)&&!(t?.task.id&&o.task?.id!==t.task.id)&&!(t?.block.id&&o.block?.id!==t.block.id))for(let i of o.phases){if(t?.phase.id&&i.id!==t.phase.id)continue;let l=t?.step.id?i.steps.find(s=>s.id===t.step.id):i.steps[0];if(l)return{task:o.task??null,block:o.block??null,phase:{id:i.id,label:i.label},step:l,focusLevel:t?.focus_level??"step",activate:!1}}}return null}function wg({visible:e,model:t}){let[n,r]=(0,S.useState)(null);if((0,S.useEffect)(()=>{let h=g=>{r(g.detail??null)};return window.addEventListener("stem:nvim-step",h),r(cp(t)),fetch("/api/navigation/current",{cache:"no-store"}).then(g=>g.ok?g.json():Promise.reject()).then(g=>{let E=cp(t,g.current??null);E&&r(E)}).catch(()=>{}),()=>window.removeEventListener("stem:nvim-step",h)},[t]),!e)return null;let o=n?.focusLevel??"card",i=n?.step.snapshot?.memory.arena,l=!!(n&&o!=="card"&&o!=="task"),s=Math.max(1,Number(i?.size_bytes??64)),u=i?.offset==null?null:Math.max(0,Math.min(s,Number(i.offset))),d=i?.bytes.trim().split(/\s+/).filter(Boolean)??[],m=Math.min(s,64),y=n?[...(n.step.snapshot?.memory.items??[]).map(h=>({name:h.name,value:h.value})),...(n.step.snapshot?.frame.fields??[]).map(h=>({name:h.name,value:h.value}))].filter((h,g,E)=>E.findIndex(N=>N.name===h.name)===g).slice(0,8):[];return(0,a.jsxs)("section",{className:"allocator-panel","aria-label":"Przypi\u0119ty model alokatora",children:[(0,a.jsxs)("header",{children:[(0,a.jsx)("span",{children:"ALLOCATOR \xB7 PINNED"}),(0,a.jsx)("strong",{children:n?`${n.phase.label} \xB7 ${String(n.step.number).padStart(2,"0")} ${n.step.label}`:"Oczekiwanie na diagram UML"}),(0,a.jsx)("small",{children:l?n.step.snapshot?.description??n.step.description:"Wybierz co najmniej BLOCK. F2 wybierze jego pierwszy krok i wykona synchronizacj\u0119."})]}),(0,a.jsxs)("div",{className:"allocator-panel-body",children:[(0,a.jsxs)("dl",{className:"allocator-metrics",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:"FOCUS"}),(0,a.jsx)("dd",{children:o.toUpperCase()})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:"BASE"}),(0,a.jsx)("dd",{children:i?.base??"\u2014"})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:"ALLOCP"}),(0,a.jsx)("dd",{children:i?.cursor??"\u2014"})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:"USED / FREE"}),(0,a.jsx)("dd",{children:u==null?"\u2014":`${u} / ${s-u} B`})]})]}),(0,a.jsxs)("div",{className:`allocator-arena-view${l?"":" is-inactive"}`,children:[(0,a.jsxs)("div",{className:"allocator-arena-labels",children:[(0,a.jsx)("span",{children:"allocbuf[0]"}),(0,a.jsx)("strong",{children:u==null?"allocp jeszcze nieustawiony":`offset ${u}`}),(0,a.jsxs)("span",{children:["allocbuf[",s-1,"]"]})]}),(0,a.jsxs)("div",{className:"allocator-pinned-arena",style:{"--allocator-size":m},children:[Array.from({length:m},(h,g)=>(0,a.jsx)("i",{className:u!=null&&g(0,a.jsxs)("div",{children:[(0,a.jsx)("span",{children:h.name}),(0,a.jsx)("code",{children:h.value})]},h.name)):(0,a.jsx)("p",{children:"Brak snapshotu."})})]})]})}var Ki=()=>({text:" ",highlight:0});function dp(e=1,t=1){return{width:e,height:t,cells:Array.from({length:t},()=>Array.from({length:e},Ki)),highlights:new Map,foreground:15263976,background:1053461,cursor:{row:0,column:0}}}function bg(e,t,n){if(t<=0||n<=0)return;let r=e.cells;e.width=t,e.height=n,e.cells=Array.from({length:n},(o,i)=>Array.from({length:t},(l,s)=>r[i]?.[s]??Ki()))}function _p(e){return`stem-card:nvim-grid:${e}`}function kg(e,t){if(t.width<=1||t.height<=1)return;let n={schema:"stem-nvim-grid.v1",snapshot_ref:e,saved_at:new Date().toISOString(),width:t.width,height:t.height,cells:t.cells.map(r=>r.map(o=>[o.text,o.highlight])),highlights:Array.from(t.highlights.entries()),foreground:t.foreground,background:t.background,cursor:{...t.cursor}};try{localStorage.setItem(_p(e),JSON.stringify(n))}catch{}}function Cp(e,t){try{return e?.schema!=="stem-nvim-grid.v1"||e.snapshot_ref!==t||!Number.isInteger(e.width)||!Number.isInteger(e.height)||e.width<2||e.height<2||e.width>400||e.height>200||e.cells.length!==e.height?null:{width:e.width,height:e.height,cells:e.cells.map(n=>n.map(([r,o])=>({text:typeof r=="string"?r:" ",highlight:Number.isInteger(o)?o:0}))),highlights:new Map(e.highlights),foreground:e.foreground,background:e.background,cursor:e.cursor}}catch{return null}}function Sg(e){try{return Cp(JSON.parse(localStorage.getItem(_p(e))??"null"),e)}catch{return null}}async function pp(e){let t=e.snapshot_ref;if(!t)return null;let n=Sg(t);if(n)return n;let r=e.view?.recorded_grid_ref;if(!r||/^(?:[a-z]+:|\/\/)/i.test(r))return null;try{let o=await fetch(r,{cache:"no-store"});return o.ok?Cp(await o.json(),t):null}catch{return null}}var Ng=[1842207,12590120,3064446,16106001,1997028,9978299,702940,12631996,6184036,15545147,5759881,16311388,5349887,12607947,5231357,16184820];function fo(e){let t=Number(e);if(!Number.isInteger(t)||t<0||t>255)return;if(t<16)return Ng[t];if(t<232){let r=t-16,o=[0,95,135,175,215,255],i=o[Math.floor(r/36)],l=o[Math.floor(r%36/6)],s=o[r%6];return i<<16|l<<8|s}let n=8+(t-232)*10;return n<<16|n<<8|n}function Eg(e){if(!e||typeof e!="object")return null;let t=e;return Object.keys(t).length?{...t,foreground:fo(t.foreground),background:fo(t.background),special:fo(t.special)}:null}function _g(e){if(!e||typeof e!="object")return null;let t=e;return Object.keys(t).length?{...t}:null}function Cg(e,t){for(let n of t){if(!Array.isArray(n)||typeof n[0]!="string")continue;let r=n[0],o=n.slice(1);for(let i of o)if(Array.isArray(i)){if(r==="grid_resize"){let[l,s,u]=i.map(Number);l===1&&bg(e,s,u)}else if(r==="default_colors_set"){let l=Number(i[0]),s=Number(i[1]),u=l>=0?l:fo(i[3]),d=s>=0?s:fo(i[4]);typeof u=="number"&&Number.isFinite(u)&&u>=0&&(e.foreground=u),typeof d=="number"&&Number.isFinite(d)&&d>=0&&(e.background=d)}else if(r==="hl_attr_define"){let l=Number(i[0]),s=Eg(i[2])??_g(i[1]);Number.isInteger(l)&&s&&typeof s=="object"&&e.highlights.set(l,s)}else if(r==="grid_clear"&&Number(i[0])===1)e.cells=Array.from({length:e.height},()=>Array.from({length:e.width},Ki));else if(r==="grid_cursor_goto"&&Number(i[0])===1)e.cursor={row:Number(i[1]),column:Number(i[2])};else if(r==="grid_line"&&Number(i[0])===1){let l=Number(i[1]),s=Number(i[2]),u=i[3];if(!e.cells[l]||!Array.isArray(u))continue;let d=0;for(let m of u){if(!Array.isArray(m))continue;let y=typeof m[0]=="string"?m[0]:" ";Number.isInteger(m[1])&&(d=Number(m[1]));let h=Number.isInteger(m[2])?Number(m[2]):1;for(let g=0;g=0&&(e.cells[l][s]={text:y,highlight:d}),s+=1}}else if(r==="grid_scroll"&&Number(i[0])===1){let l=Number(i[1]),s=Number(i[2]),u=Number(i[3]),d=Number(i[4]),m=Number(i[5]),y=Number(i[6]),h=e.cells.map(g=>g.map(E=>({...E})));for(let g=l;g=l&&N=u&&Ci.text||" ").join(""),r=[],o=0;for(;o=n&&t.cursor.rowR.column-M.column||M.row-R.row):["asm","reg","memory"].includes(_??"")&&k.sort((M,R)=>M.column-R.column||M.row-R.row);let P=k[Math.max(0,Number(d.occurrence??1)-1)];m=P?.row??-1,y=P?.column??-1,g=d.text.length}if(m<0||y<0||m>=t.height||y>=t.width)continue;let E=Math.max(0,Number(d.padding_cells??1)),N=Math.max(0,y-E)*i,C=Math.max(0,m-E)*l,p=Math.min(t.width-y+E,g+E*2)*i,c=Math.min(t.height-m+E,h+E*2)*l,f=s[u.tone??"danger"];if(o.strokeStyle=f,o.lineWidth=2,u.shape==="underline"?(o.beginPath(),o.moveTo(N,C+c-2),o.lineTo(N+p,C+c-2),o.stroke()):o.strokeRect(N+1,C+1,Math.max(1,p-2),Math.max(1,c-2)),u.label){o.font="700 10px system-ui, sans-serif";let k=o.measureText(u.label).width+8;o.fillStyle=f,o.fillRect(N+1,Math.max(0,C-15),k,14),o.fillStyle="#fff",o.fillText(u.label,N+5,Math.max(10,C-4))}}}function Pg(e){if(e.nativeEvent.isComposing||e.metaKey)return null;let n={Enter:"CR",Escape:"Esc",Backspace:"BS",Delete:"Del",Tab:"Tab",ArrowLeft:"Left",ArrowRight:"Right",ArrowUp:"Up",ArrowDown:"Down",Home:"Home",End:"End",PageUp:"PageUp",PageDown:"PageDown",Insert:"Insert"}[e.key]??(/^F\d{1,2}$/.test(e.key)?e.key:null),r=[e.ctrlKey?"C":"",e.altKey?"M":"",e.shiftKey&&n?"S":""].filter(Boolean).join("-");return n?`<${r?`${r}-`:""}${n}>`:e.key.length!==1?null:e.ctrlKey||e.altKey?`<${r}-${e.key==="<"?"lt":e.key}>`:e.key==="<"?"":e.key}function Mg({visible:e,syncMode:t,controlMode:n,expanded:r,fit:o,scale:i,onSummaryChange:l}){let s=(0,S.useRef)(null),u=(0,S.useRef)(null),d=(0,S.useRef)(null),m=(0,S.useRef)(0),y=(0,S.useRef)(0),h=(0,S.useRef)(0),g=(0,S.useRef)(dp()),E=(0,S.useRef)(null),N=(0,S.useRef)(null),C=(0,S.useRef)(null),p=(0,S.useRef)(0),c=(0,S.useRef)(null),f=(0,S.useRef)(null),[k,_]=(0,S.useState)(null),[P,M]=(0,S.useState)(!1),[R,j]=(0,S.useState)(0),[$,ke]=(0,S.useState)(!1),[re,G]=(0,S.useState)(()=>{let A=Number(localStorage.getItem("stem-card-nvim-height-vh"));return Number.isFinite(A)&&A>=25&&A<=90?A:75}),[U,Le]=(0,S.useState)({state:"hidden"}),[Ne,Q]=(0,S.useState)({state:"idle"}),et=Ne.state==="replaying"||Ne.state==="verifying",Ge=t&&U.state==="connected"&&!et,je=n&&$&&U.state==="connected"&&!et,me=()=>{s.current&&Ag(s.current,g.current,C.current===N.current?.step.snapshot_ref?N.current?.step.view?.annotations??[]:[])},Dn=()=>{m.current||(m.current=window.requestAnimationFrame(()=>{m.current=0,me()}))};if((0,S.useEffect)(()=>{let A=F=>{let B=F.detail;N.current=B,_(B),C.current=null;let te=B.step.snapshot_ref;if(U.state!=="connected"&&te&&pp(B.step).then(ie=>{!ie||N.current?.step.snapshot_ref!==te||(g.current=ie,M(!0),C.current=te,Q({state:"idle",snapshot_ref:te,message:"Pokazano ostatni zapisany, zweryfikowany ekran."}),me())}),me(),!B.activate)return;let at=B.step.mode??"RUN",zt=async()=>{if(!B.step.code_ref)return null;let ie=await fetch("/api/nvim/open",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({code_ref:B.step.code_ref,actor:document.documentElement.dataset.cardClientId??"browser"})}),q=await ie.json().catch(()=>({}));if(!ie.ok)throw new Error(q.message??q.error??`HTTP ${ie.status}`);return q};if(at==="CODE"||!te){zt().then(()=>{Q({state:"ready",phase:"code",message:B.step.code_ref?`Otwarto ${B.step.code_ref}; program nie zosta\u0142 zrestartowany.`:"Krok CODE nie ma przypisanego code_ref."})}).catch(ie=>{Q({state:"failed",phase:"code",message:ie instanceof Error?ie.message:String(ie)})});return}if(!e||U.state!=="connected"){Q({state:"failed",snapshot_ref:te,message:"Cel jest offline; pozostaje zapisany wynik tego kroku."});return}let Ee=zt(),ne=Math.max(p.current+1,Date.now());p.current=ne,c.current?.abort();let st=new AbortController;c.current=st,Q({state:"replaying",phase:"dispatch",generation:ne,snapshot_ref:te,message:"Resetuj\u0119 cel i odtwarzam wykonanie do wybranego kroku."});let Ye=0,bn=async()=>{try{let ie=await fetch("/api/checkpoint/status",{cache:"no-store",signal:st.signal});if(!ie.ok)return;let q=await ie.json(),wt=q.active?.generation===ne?q.active:null;if(!wt||p.current!==ne)return;Q({state:wt.phase==="verify"?"verifying":"replaying",phase:wt.phase,generation:ne,snapshot_ref:te,message:wt.message})}catch{}};Ye=window.setInterval(bn,120),bn(),Ee.then(()=>fetch("/api/checkpoint/activate",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({snapshot_ref:te,generation:ne}),signal:st.signal})).then(async ie=>{let q=await ie.json();if(p.current!==ne)return;if(!ie.ok||q.status!=="ready")throw new Error(q.message??`Replay zako\u0144czy\u0142 si\u0119 kodem HTTP ${ie.status}.`);let wt=d.current;if(wt?.readyState!==WebSocket.OPEN)throw new Error("Replay zako\u0144czony, ale most Neovima jest offline.");let fe=y.current;wt.send(JSON.stringify({type:"refresh"}));let Qt=Date.now()+1800;for(;y.current<=fe&&Date.now()window.setTimeout(qt,20));if(y.current<=fe)throw new Error("Neovim nie potwierdzi\u0142 pe\u0142nego redraw po replay.");if(p.current!==ne)return;C.current=te,Q({state:"ready",phase:q.phase,generation:ne,snapshot_ref:te,message:q.message??"Stan maszyny zosta\u0142 odtworzony i zweryfikowany."}),kg(te,g.current),me(),await new Promise(qt=>window.requestAnimationFrame(()=>qt()));let jn=s.current;if(jn&&N.current?.step.snapshot_ref===te)try{fetch("/api/evidence",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({actor:document.documentElement.dataset.cardClientId??"browser",snapshot_ref:te,event_id:B.step.event_id??null,strategy_ref:B.step.strategy_ref??null,code_ref:B.step.code_ref??null,caption:`${String(B.step.number).padStart(2,"0")} ${B.step.label}`,data_url:jn.toDataURL("image/png")})}).catch(()=>{})}catch{}}).catch(ie=>{st.signal.aborted||p.current!==ne||(pp(B.step).then(q=>{!q||N.current?.step.snapshot_ref!==te||(g.current=q,M(!0),C.current=te,me())}),Q({state:"failed",generation:ne,snapshot_ref:te,message:ie instanceof Error?ie.message:String(ie)}))}).finally(()=>{window.clearInterval(Ye),c.current===st&&(c.current=null)})};return window.addEventListener("stem:nvim-step",A),()=>{window.removeEventListener("stem:nvim-step",A),c.current?.abort()}},[U.state,e]),(0,S.useEffect)(()=>{localStorage.setItem("stem-card-nvim-height-vh",re.toFixed(2))},[re]),(0,S.useEffect)(()=>{let A=window.requestAnimationFrame(me);return()=>window.cancelAnimationFrame(A)},[r,o,i]),(0,S.useEffect)(()=>{let A=()=>{let F=d.current;F?.readyState===WebSocket.OPEN&&F.send(JSON.stringify({type:"refresh"})),me()};return window.addEventListener("stem:nvim-refresh",A),()=>window.removeEventListener("stem:nvim-refresh",A)},[]),(0,S.useEffect)(()=>{let A=U.state;U.state==="connected"&&(A=Ne.state==="idle"?"connected":Ne.state),l({state:A,message:Ne.message??U.message,instance:U.target?.instance??U.target?.container_name,detail:[U.target?.profile??k?.step.view?.connection_ref,k?`${String(k.step.number).padStart(2,"0")} ${k.step.label}`:void 0,Ne.phase].filter(Boolean).join(" \xB7 "),syncMode:t,syncReady:Ge,controlMode:n,controlEnabled:je,screenCursor:{...g.current.cursor},grid:{columns:g.current.width,rows:g.current.height}})},[k,Ne,je,n,R,l,U,t,Ge]),(0,S.useEffect)(()=>()=>window.clearTimeout(h.current),[]),(0,S.useEffect)(()=>{if(!je)return;let A=F=>{let B=d.current;if(B?.readyState!==WebSocket.OPEN)return;if(F.altKey&&!F.ctrlKey&&!F.metaKey&&F.key.toLowerCase()==="w"){F.preventDefault(),F.stopImmediatePropagation(),F.repeat||B.send(JSON.stringify({type:"input",keys:""}));return}let te={ArrowLeft:"h",ArrowDown:"j",ArrowUp:"k",ArrowRight:"l"};F.altKey&&!F.ctrlKey&&!F.metaKey&&te[F.key]&&(F.preventDefault(),F.stopImmediatePropagation(),F.repeat||B.send(JSON.stringify({type:"input",keys:`${te[F.key]}`})))};return window.addEventListener("keydown",A,!0),()=>window.removeEventListener("keydown",A,!0)},[je]),(0,S.useEffect)(()=>{if(!e||!t){d.current?.close(),d.current=null,ke(!1),Le(e?{state:"offline",message:"SYNC OFF: pokazuj\u0119 zapisany ekran bez po\u0142\u0105czenia z kontenerem."}:{state:"hidden"});return}let A=!1,F=0,B=()=>{if(A)return;let te=window.location.protocol==="https:"?"wss:":"ws:",at=new WebSocket(`${te}//${window.location.host}/api/nvim-ui`);d.current=at,Le({state:"connecting"}),at.addEventListener("message",zt=>{try{let Ee=JSON.parse(String(zt.data));if(Ee.type==="status"){typeof Ee.epoch=="string"&&E.current&&E.current!==Ee.epoch&&(g.current=dp(),M(!1),C.current=null),typeof Ee.epoch=="string"&&(E.current=Ee.epoch),Le(Ee);return}Ee.type==="redraw"&&Array.isArray(Ee.events)&&(Cg(g.current,Ee.events),g.current.width>1&&g.current.height>1&&M(!0),Ee.events.some(ne=>Array.isArray(ne)&&ne[0]==="flush")&&(y.current+=1,Dn(),h.current||(h.current=window.setTimeout(()=>{h.current=0,j(ne=>ne+1)},120))))}catch{}}),at.addEventListener("close",()=>{d.current===at&&(d.current=null),A||(F=window.setTimeout(B,1200))}),at.addEventListener("error",()=>{Le(zt=>({...zt,state:"offline",message:"Most Neovima jest chwilowo niedost\u0119pny."}))})};return B(),()=>{A=!0,window.clearTimeout(F),window.cancelAnimationFrame(m.current),m.current=0,d.current?.close(),d.current=null}},[t,e]),!e)return null;let xe=A=>{let F=d.current;F?.readyState===WebSocket.OPEN&&F.send(JSON.stringify(A))},wn=A=>{let F=A.currentTarget.getBoundingClientRect();return{row:Math.max(0,Math.min(g.current.height-1,Math.floor((A.clientY-F.top)/F.height*g.current.height))),column:Math.max(0,Math.min(g.current.width-1,Math.floor((A.clientX-F.left)/F.width*g.current.width)))}},se=A=>[A.shiftKey?"S":"",A.ctrlKey?"C":"",A.altKey?"A":""].filter(Boolean).join("-");return(0,a.jsxs)("section",{className:"nvim-panel","aria-label":"Neovim po\u0142\u0105czony z kontenerem",onWheel:A=>{A.stopPropagation();let F=A.target;(!(F instanceof Element)||!F.closest(".nvim-screen-scroll"))&&A.preventDefault()},children:[(0,a.jsxs)("div",{ref:u,className:"nvim-screen-scroll",style:o?void 0:{height:`${r?90:re}vh`},"data-sync":t?"on":"off","data-control":je?"true":"false",tabIndex:0,onPointerEnter:A=>{ke(!0),A.currentTarget.focus({preventScroll:!0})},onPointerLeave:A=>{ke(!1),A.currentTarget.blur()},onMouseDown:A=>A.currentTarget.focus({preventScroll:!0}),onKeyDown:A=>{if(!je)return;let F=Pg(A);F&&(A.preventDefault(),A.stopPropagation(),xe({type:"input",keys:F}))},children:[(0,a.jsx)("div",{className:"nvim-screen-stage",style:o?void 0:{height:`${(r?90:re)/Math.max(i,.25)}vh`},children:(0,a.jsx)("div",{className:"nvim-screen-surface",children:(0,a.jsx)("div",{className:"nvim-screen-frame",children:(0,a.jsx)("div",{className:"nvim-screen-content",children:(0,a.jsx)("div",{className:"nvim-screen-grid",children:(0,a.jsx)("canvas",{ref:s,className:"nvim-screen",tabIndex:-1,"aria-label":je?"Interaktywny ekran Neovima":"Ekran Neovima",onMouseDown:A=>{if(!je||U.state!=="connected")return;A.preventDefault();let F=wn(A),B=A.button===1?"middle":A.button===2?"right":"left";xe({type:"mouse",button:B,action:"press",modifier:se(A),...F})},onMouseUp:A=>{if(!je||U.state!=="connected")return;A.preventDefault();let F=wn(A),B=A.button===1?"middle":A.button===2?"right":"left";xe({type:"mouse",button:B,action:"release",modifier:se(A),...F})},onWheel:A=>{if(!je||U.state!=="connected")return;A.preventDefault();let F=wn(A);xe({type:"mouse",button:"wheel",action:A.deltaY<0?"up":"down",modifier:se(A),...F})},onContextMenu:A=>je&&A.preventDefault()})})})})})}),!P&&(0,a.jsxs)("div",{className:"nvim-screen-empty",children:[(0,a.jsx)("strong",{children:wp[U.state]}),(0,a.jsx)("span",{children:U.message??"Oczekiwanie na ekran Neovima lub zapisany stan kroku."})]})]}),(0,a.jsx)("div",{className:"nvim-resize-handle",role:"separator","aria-label":"Zmie\u0144 wysoko\u015B\u0107 panelu Neovima","aria-orientation":"horizontal","aria-valuemin":25,"aria-valuemax":90,"aria-valuenow":Math.round(re),tabIndex:0,title:"Przeci\u0105gnij, aby zmieni\u0107 wysoko\u015B\u0107. Dwuklik: 75%.",onPointerDown:A=>{A.preventDefault(),f.current={y:A.clientY,heightVh:re},A.currentTarget.setPointerCapture(A.pointerId)},onPointerMove:A=>{let F=f.current;if(!F)return;let B=(A.clientY-F.y)/window.innerHeight*100;G(Math.max(25,Math.min(90,F.heightVh+B)))},onPointerUp:A=>{f.current=null,A.currentTarget.hasPointerCapture(A.pointerId)&&A.currentTarget.releasePointerCapture(A.pointerId)},onPointerCancel:()=>{f.current=null},onDoubleClick:()=>G(75),onKeyDown:A=>{if(A.key==="ArrowUp"||A.key==="ArrowDown"){A.preventDefault();let F=A.key==="ArrowUp"?-2:2;G(B=>Math.max(25,Math.min(90,B+F)))}else A.key==="Home"&&(A.preventDefault(),G(75))}})]})}function Tg({column:e,row:t}){return e==="chapter"||e==="task"||e==="version"?(0,a.jsx)("td",{children:(0,a.jsx)("code",{children:t[e]})}):e==="idea"?(0,a.jsx)("td",{dangerouslySetInnerHTML:{__html:t.idea_html}}):e==="status"?(0,a.jsx)("td",{children:(0,a.jsx)("span",{className:"scope-status","data-status":t.status,title:t.status,children:t.status_label})}):(0,a.jsx)("td",{children:t[e]})}function Rg({front:e}){let t=e.scope_columns??["chapter","task","idea","priority"];return(0,a.jsxs)(hs,{header:e.header_html,left:e.left_margin_html,right:e.right_margin_html,className:"front-sheet",children:[(0,a.jsxs)("section",{className:"front-scope front-goal",children:[(0,a.jsx)("h2",{children:e.goal_title}),(0,a.jsx)(Yt,{html:e.goal_html})]}),(0,a.jsxs)("section",{className:"front-scope front-card-scope",children:[(0,a.jsx)("h2",{children:e.scope_title}),e.scope_html&&(0,a.jsx)(Yt,{html:e.scope_html}),(0,a.jsx)("div",{className:"scope-table-wrap",children:(0,a.jsxs)("table",{className:"scope-table scope-table--task",children:[(0,a.jsx)("thead",{children:(0,a.jsx)("tr",{children:e.scope_headers.map(n=>(0,a.jsx)("th",{children:n},n))})}),(0,a.jsx)("tbody",{children:e.scope_rows.map(n=>(0,a.jsx)("tr",{className:n.key?"scope-row--key":"",children:t.map(r=>(0,a.jsx)(Tg,{column:r,row:n},r))},`${n.chapter}-${n.task}`))})]})})]})]})}function Og({taskRef:e,step:t}){return(0,a.jsxs)("li",{id:`${e}-${t.id}`,children:[(0,a.jsx)("strong",{children:t.title}),(0,a.jsx)(Yt,{html:t.content_html})]})}function Ig({taskRef:e,block:t}){let n=t.kind==="exercise"?"\u0106wiczenie":"Block";return(0,a.jsxs)("section",{className:`task-block task-block--${t.kind}`,id:`${e}-${t.id}`,"data-stem-nav":"block",children:[(0,a.jsxs)("header",{children:[(0,a.jsx)("span",{children:n}),(0,a.jsx)("h4",{children:t.title})]}),(0,a.jsx)(Yt,{html:t.content_html}),t.steps.length>0&&(0,a.jsx)("ol",{className:"block-steps",children:t.steps.map(r=>(0,a.jsx)(Og,{taskRef:e,step:r},r.id))})]})}function $g({task:e}){return e.flow.length?(0,a.jsxs)("article",{className:"task-flow",id:e.ref,"data-stem-nav":"task",children:[(0,a.jsxs)("header",{className:"task-flow-header",children:[(0,a.jsx)("span",{children:e.ref.toUpperCase()}),(0,a.jsx)("h3",{children:e.title}),e.uuid&&(0,a.jsx)("code",{children:e.uuid})]}),e.flow.map(t=>(0,a.jsx)(Ig,{taskRef:e.ref,block:t},t.id)),(0,a.jsxs)("footer",{children:[(0,a.jsx)("strong",{children:"Task acceptance:"})," ",e.criterion]})]}):(0,a.jsxs)("article",{className:"task-legacy",id:e.ref,"data-stem-nav":"task",children:[(0,a.jsx)("strong",{children:e.ref.toUpperCase()}),(0,a.jsx)(Yt,{html:e.prompt_html??""}),(0,a.jsxs)("footer",{children:[(0,a.jsx)("strong",{children:"Task acceptance:"})," ",e.criterion]})]})}var mp={compile:"KOMPILACJA",bss:".BSS",stack:"STOS",registers:"REJESTRY",text:".TEXT"},fp=["#9bcfe2","#c4dfc0","#f4c889","#d2b6dc"];function Fg({config:e,title:t}){let n=e.arena_size??64,r=e.allocations??[5,7],o=e.symbols??[],i=`${e.storage_key??"allocator-memory-flow"}-stage`,[l,s]=(0,S.useState)(()=>Math.min(r.length,Number(localStorage.getItem(i)??0))),[u,d]=(0,S.useState)(o[0]?.id??"");(0,S.useEffect)(()=>localStorage.setItem(i,String(l)),[l,i]);let m=r.slice(0,l).reduce((C,p)=>C+p,0),y=o.find(C=>C.id===u)??o[0],h=Array.from({length:n},(C,p)=>p),g=C=>{let p=0;for(let c=0;co.some(p=>p.area===C));return(0,a.jsxs)("div",{className:"memory-react","aria-label":`Interaktywny diagram: ${t}`,children:[(0,a.jsxs)("header",{children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("small",{children:"INTERAKTYWNY MODEL PAMI\u0118CI"}),(0,a.jsx)("h3",{children:t})]}),(0,a.jsxs)("div",{className:"memory-actions",children:[(0,a.jsx)("button",{"aria-pressed":l===0,onClick:()=>s(0),children:"definicje / reset"}),r.map((C,p)=>(0,a.jsxs)("button",{"aria-pressed":l===p+1,onClick:()=>s(p+1),children:["alloc(",C,")"]},`${C}-${p}`))]})]}),(0,a.jsx)("div",{className:"memory-lanes",children:N.map(C=>(0,a.jsxs)("section",{className:`memory-lane ${C}`,children:[(0,a.jsx)("b",{children:mp[C]}),o.filter(p=>p.area===C).map(p=>(0,a.jsx)("button",{className:u===p.id?"active":"",onClick:()=>d(p.id),children:(0,a.jsx)("code",{children:p.declaration})},p.id))]},C))}),(0,a.jsxs)("div",{className:"arena-wrap",children:[(0,a.jsxs)("div",{className:"arena-label",children:[(0,a.jsxs)("code",{children:["allocbuf[",n,"]"]}),(0,a.jsxs)("span",{children:["u\u017Cyte ",m," B / wolne ",n-m," B"]})]}),(0,a.jsxs)("div",{className:"arena",style:{"--allocp":m,"--arena-size":n},children:[h.map(C=>{let p=g(C);return(0,a.jsx)("i",{style:p>=0?{background:fp[p%fp.length]}:void 0},C)}),(0,a.jsxs)("span",{className:"allocp",children:["allocp \u2191 ",m]})]})]}),(0,a.jsxs)("p",{className:"memory-stage",children:[(0,a.jsxs)("strong",{children:[l+1,"/",r.length+1]})," ",(0,a.jsx)("code",{children:E})]}),y&&(0,a.jsxs)("aside",{className:"memory-inspector",children:[(0,a.jsx)("strong",{children:y.id}),(0,a.jsx)("code",{children:y.declaration}),(0,a.jsx)("span",{children:y.description})]})]})}function Dg({config:e,asset:t,progress:n,onSetStatus:r}){let o=e.stages??[],i=`${e.storage_key??t.label}-uml-stage`,l=(0,S.useRef)(null),[s,u]=(0,S.useState)(""),[d,m]=(0,S.useState)(!1),[y,h]=(0,S.useState)(!1),[g,E]=(0,S.useState)(()=>Math.max(0,Math.min(o.length-1,Number(localStorage.getItem(i)??0))));(0,S.useEffect)(()=>localStorage.setItem(i,String(g)),[g,i]),(0,S.useEffect)(()=>{let c=!0;return m(!1),fetch(t.href,{cache:"no-store"}).then(f=>{if(!f.ok)throw new Error(`HTTP ${f.status}`);return f.text()}).then(f=>{let k=new DOMParser().parseFromString(f,"image/svg+xml"),_=k.documentElement;if(_.nodeName.toLowerCase()!=="svg"||k.querySelector("parsererror"))throw new Error("Niepoprawny SVG");k.querySelectorAll("script, foreignObject").forEach(P=>P.remove()),Sp(_,t.tile),c&&u(_.outerHTML)}).catch(()=>{c&&m(!0)}),()=>{c=!1}},[t.href,t.tile]),(0,S.useEffect)(()=>{let c=l.current?.querySelector("svg");if(!c)return;c.setAttribute("role","img"),c.setAttribute("aria-label",t.alt),c.querySelectorAll("[data-uml-stage]").forEach(_=>{_.classList.remove("uml-stage-region","uml-stage-region-bg","uml-stage-region-outline","uml-stage-region-active","uml-stage-region-completed"),_.removeAttribute("data-uml-stage"),_.removeAttribute("data-uml-stage-label"),_.removeAttribute("role"),_.removeAttribute("tabindex"),_.removeAttribute("aria-label")});let f=Array.from(c.querySelectorAll("text")),k=Array.from(c.querySelectorAll("rect"));o.forEach((_,P)=>{if(!_.svg_label)return;let M=f.find(G=>(G.textContent??"").trim().startsWith(_.svg_label)),R=Number(M?.getAttribute("y"));if(!M||!Number.isFinite(R))return;let j=k.map(G=>{let U=Number(G.getAttribute("y")),Le=Number(G.getAttribute("height")),Ne=Number(G.getAttribute("width")),Q=G.getAttribute("style")??"",et=Number(Q.match(/stroke-width:\s*([\d.]+)/)?.[1]??G.getAttribute("stroke-width")??0);return{rect:G,y:U,height:Le,width:Ne,strokeWidth:et,area:Ne*Le}}).filter(({y:G,height:U,width:Le,strokeWidth:Ne})=>Number.isFinite(G)&&Number.isFinite(U)&&Number.isFinite(Le)&&Ne>=2&&G<=R&&G+U>=R).sort((G,U)=>G.area-U.area),$=j[0];if(!$)return;let ke=j.filter(({y:G,height:U,width:Le})=>Math.abs(G-$.y)<.1&&Math.abs(U-$.height)<.1&&Math.abs(Le-$.width)<.1),re=!!(_.progress_id&&n?.items?.[_.progress_id]?.status==="approved");ke.forEach(({rect:G},U)=>{let Le=G.getAttribute("fill")!=="none";G.dataset.umlStage=String(P),G.dataset.umlStageLabel=_.label,G.classList.add("uml-stage-region",Le?"uml-stage-region-bg":"uml-stage-region-outline"),G.classList.toggle("uml-stage-region-active",P===g),G.classList.toggle("uml-stage-region-completed",re),U===0&&(G.setAttribute("role","button"),G.setAttribute("tabindex","0"),G.setAttribute("aria-label",`Poka\u017C etap ${_.label}`))})})},[t.alt,n,g,o,s]);let N=(c,f)=>{let k=l.current?.querySelector("svg");if(!k)return;let _=Array.from(k.querySelectorAll(".uml-stage-region-bg[data-uml-stage]")).filter(M=>{let R=M.getBoundingClientRect();return c>=R.left&&c<=R.right&&f>=R.top&&f<=R.bottom}).sort((M,R)=>{let j=M.getBoundingClientRect(),$=R.getBoundingClientRect();return j.width*j.height-$.width*$.height})[0],P=Number(_?.dataset.umlStage);Number.isInteger(P)&&E(P)},C=c=>{if(c.key!=="Enter"&&c.key!==" ")return;let f=c.target,k=Number(f.dataset?.umlStage);Number.isInteger(k)&&(c.preventDefault(),E(k))},p=o[g];return(0,a.jsxs)("div",{className:"uml-react",children:[(0,a.jsxs)("header",{children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("small",{children:"KANONICZNY DIAGRAM UML \xB7 PLANTUML"}),(0,a.jsx)("h3",{children:"Reset i dwa przydzia\u0142y liniowe"})]}),(0,a.jsx)("div",{className:"uml-actions",children:o.map((c,f)=>(0,a.jsxs)("button",{"aria-pressed":g===f,"data-completed":c.progress_id&&n?.items?.[c.progress_id]?.status==="approved"?"true":"false",onClick:()=>E(f),children:[c.progress_id&&n?.items?.[c.progress_id]?.status==="approved"&&(0,a.jsx)("span",{"aria-hidden":"true",children:"\u2713 "}),c.label]},c.id))})]}),(0,a.jsxs)("div",{ref:l,className:"uml-canvas",onClick:c=>N(c.clientX,c.clientY),onKeyDown:C,children:[s?(0,a.jsx)("div",{className:`uml-svg-host${t.tile?" is-tiled":""}`,style:gs(t.tile),dangerouslySetInnerHTML:{__html:s}}):(0,a.jsx)("img",{src:t.href,alt:t.alt}),d&&(0,a.jsx)("span",{className:"uml-inline-warning",children:"Widok statyczny \u2014 nie uda\u0142o si\u0119 wczyta\u0107 interaktywnego SVG."})]}),p&&(0,a.jsxs)("aside",{className:"uml-stage",children:[(0,a.jsx)("strong",{children:p.label}),(0,a.jsx)("span",{children:p.description}),p.code_ref&&(0,a.jsx)("code",{children:p.code_ref})]}),p?.progress_id&&(0,a.jsxs)("footer",{className:"uml-approval",children:[(0,a.jsx)("span",{children:n?.items?.[p.progress_id]?.status==="approved"?"Ten stan jest zatwierdzony i zapisany w JSON-ie zaj\u0119\u0107.":"Kursor wybiera stan; zatwierdzenie zapisuje go w JSON-ie zaj\u0119\u0107."}),(0,a.jsx)("button",{disabled:!n||y,onClick:async()=>{if(!p.progress_id||!n)return;let c=n.items?.[p.progress_id]?.status==="approved";h(!0);try{await r(p.progress_id,c?"pending":"approved")}finally{h(!1)}},children:y?"Zapisywanie\u2026":n?.items?.[p.progress_id]?.status==="approved"?"Cofnij zatwierdzenie":"Zatwierd\u017A stan"})]})]})}function jg({config:e,asset:t,progress:n,onSetStatus:r,continuation:o}){let i=e.phases??[],l=i.flatMap(x=>x.steps.map(b=>({phase:x,step:b}))),s=`${e.storage_key??t.label}-uml-step`,u=`${e.storage_key??t.label}-navigation-level`,d=(0,S.useRef)(null),m=(0,S.useRef)(null),y=(0,S.useRef)(new Map),h=(0,S.useRef)(null),g=(0,S.useRef)(0),E=(0,S.useRef)(!1),N=(0,S.useRef)(null),C=(0,S.useRef)(!0),[p,c]=(0,S.useState)(""),[f,k]=(0,S.useState)(!1),[_,P]=(0,S.useState)(!1),[M,R]=(0,S.useState)(null),[j,$]=(0,S.useState)(null),[ke,re]=(0,S.useState)(!1),[G,U]=(0,S.useState)(""),[Le,Ne]=(0,S.useState)(!1),[Q,et]=(0,S.useState)(null),[Ge,je]=(0,S.useState)([]),[me,Dn]=(0,S.useState)(0),[xe,wn]=(0,S.useState)(()=>Math.max(0,Math.min(l.length-1,Number(localStorage.getItem(s)??0)))),[se,A]=(0,S.useState)(()=>{let x=localStorage.getItem(u);return x==="card"||x==="task"||x==="block"||x==="phase"||x==="step"||x==="snapshot"?x:"step"});(0,S.useEffect)(()=>localStorage.setItem(s,String(xe)),[xe,s]),(0,S.useEffect)(()=>localStorage.setItem(u,se),[se,u]),(0,S.useEffect)(()=>{let x=!0;return k(!1),fetch(t.href,{cache:"no-store"}).then(b=>{if(!b.ok)throw new Error(`HTTP ${b.status}`);return b.text()}).then(b=>{let T=new DOMParser().parseFromString(b,"image/svg+xml"),O=T.documentElement;if(O.nodeName.toLowerCase()!=="svg"||T.querySelector("parsererror"))throw new Error("Niepoprawny SVG");T.querySelectorAll("script, foreignObject").forEach(v=>v.remove()),Sp(O,t.tile),x&&c(O.outerHTML)}).catch(()=>{x&&k(!0)}),()=>{x=!1}},[t.href,t.tile]),(0,S.useEffect)(()=>{let x=!0,b=ms(t,e);if(U("Wczytywanie uk\u0142adu\u2026"),Ne(!1),!t.source_href)return R(b),$(b),U("Brak \u017Ar\xF3d\u0142a PlantUML \u2014 zapis wy\u0142\u0105czony."),()=>{x=!1};let T=t.source_href.replace(/\.puml(?:\?.*)?$/,".layout.json");return fetch(`/api/uml-layout?source=${encodeURIComponent(t.source_href)}`,{cache:"no-store"}).then(async O=>{if(!O.ok)throw new Error(`HTTP ${O.status}`);return O.json()}).catch(async()=>{let O=await fetch(T,{cache:"no-store"});return O.ok?{layout:await O.json(),source_digest:"",stale:!1}:{layout:null,source_digest:"",stale:!1}}).then(O=>{if(!x)return;let v=O.layout??ms(t,e,O.source_digest??"");R(v),$(v),re(!1),Ne(!!O.stale),U(O.layout?"Uk\u0142ad wczytany z JSON.":"Uk\u0142ad PlantUML \u2014 brak r\u0119cznych zmian.")}).catch(O=>{x&&(R(b),$(b),U(`Nie wczytano uk\u0142adu: ${O instanceof Error?O.message:String(O)}`))}),()=>{x=!1}},[t.source_href,t.label,e.block?.id,e.task?.id]),(0,S.useEffect)(()=>{let x=m.current?.querySelector("svg");if(!x||!j)return;mg(x);let b=new Map(l.map(({step:L})=>[String(L.number).padStart(2,"0"),L.id])),T=fg(x,b),O=new Map(T.map(L=>[L.id,L]));y.current=O;let v=T.map(L=>L.id);je(L=>L.join("\0")===v.join("\0")?L:v),T.forEach(L=>{let H=j.elements[L.id];H&&gg(L,H)});let w=x.viewBox.baseVal;(j.canvas.width<=0||j.canvas.height<=0)&&$(L=>L&&{...L,canvas:{...L.canvas,width:w?.width||Number(x.getAttribute("width")?.replace("px",""))||0,height:w?.height||Number(x.getAttribute("height")?.replace("px",""))||0}})},[l,p,j]),(0,S.useEffect)(()=>{let x=m.current?.querySelector("svg");if(!x)return;let b=e.kind==="uml-class"&&x.contains(document.activeElement);x.setAttribute("role","img"),x.setAttribute("aria-label",t.alt),x.classList.toggle("uml-class-svg",e.kind==="uml-class"),x.classList.toggle("uml-class-svg-active",e.kind==="uml-class"&&se!=="card"),x.querySelectorAll(".uml-step-hit, .uml-class-focus-region").forEach(K=>K.remove()),x.querySelectorAll("[data-uml-step-element]").forEach(K=>{K.classList.remove("uml-step-element","uml-step-element-active","uml-step-element-completed","uml-step-element-tone-a","uml-step-element-tone-b"),K.removeAttribute("data-uml-step-element"),K.removeAttribute("data-uml-step-claim-area")});let T=Array.from(x.querySelectorAll("text")),O=Array.from(x.querySelectorAll("line")),v=Array.from(x.querySelectorAll("polygon"));if(e.kind==="uml-class"){let K=V=>V?pg(V):null,X=V=>{if(!V.length)return null;let oe=Math.min(...V.map(J=>J.x)),ct=Math.min(...V.map(J=>J.y)),Ue=Math.max(...V.map(J=>J.x+J.width)),ue=Math.max(...V.map(J=>J.y+J.height));return{x:oe,y:ct,width:Ue-oe,height:ue-ct}},D=Array.from(x.querySelectorAll("g > *")),Z=Array.from(x.querySelectorAll("[id]"));l.forEach(({step:V},oe)=>{let ct=V.svg_label??String(V.number).padStart(2,"0"),Ue=T.find(ge=>(ge.textContent??"").trim().startsWith(ct));if(!Ue)return;let ue=K(Ue);if(!ue)return;let J=Number(Ue.getAttribute("y")),Re=T.filter(ge=>Number.isFinite(J)&&Math.abs(Number(ge.getAttribute("y"))-J)<.8),Ve=V.svg_target?Z.find(ge=>ge.id===V.svg_target||ge.id.startsWith(`${V.svg_target}-`))??null:null,dt=K(Ve)??X(Re.map(ge=>K(ge)).filter(ge=>ge!==null))??ue,vr=Ve?.tagName.toLowerCase()==="rect",ys=Ve?[Ve,Ue]:Re;vr&&D.forEach(ge=>{let Xt=K(ge);if(!Xt||ge===Ve)return;let xs=Xt.x+Xt.width/2,ws=Xt.y+Xt.height/2;xs>=dt.x&&xs<=dt.x+dt.width&&ws>=dt.y&&ws<=dt.y+dt.height&&ys.push(ge)});let zp=!!(V.progress_id&&n?.items?.[V.progress_id]?.status==="approved"),vs=Math.max(1,dt.width*dt.height);ys.forEach(ge=>{let Xt=Number(ge.dataset.umlStepClaimArea);Number.isFinite(Xt)&&Xt{let D=K.svg_label??String(K.number).padStart(2,"0"),Z=T.find(J=>(J.textContent??"").trim()===D),V=Number(Z?.getAttribute("y"));if(!Z||!Number.isFinite(V))return;let oe=V+5,ct=!!(K.progress_id&&n?.items?.[K.progress_id]?.status==="approved"),Ue=T.filter(J=>Math.abs(Number(J.getAttribute("y"))-V)<.8);Ue.push(...O.filter(J=>{let Re=Number(J.getAttribute("y1")),Ve=Number(J.getAttribute("y2"));return Number.isFinite(Re)&&Number.isFinite(Ve)&&Math.abs(Re-Ve)<.8&&Math.abs((Re+Ve)/2-oe)<=14})),Ue.push(...v.filter(J=>{let Re=(J.getAttribute("points")??"").trim().split(/\s+/).map(vr=>Number(vr.split(",")[1])).filter(Number.isFinite);if(!Re.length)return!1;let Ve=Math.min(...Re),dt=Math.max(...Re);return dt-Ve<=16&&Math.abs((Ve+dt)/2-oe)<=14})),Ue.forEach(J=>{J.dataset.umlStepElement=String(X),J.classList.add("uml-step-element"),J.classList.toggle("uml-step-element-active",X===xe),J.classList.toggle("uml-step-element-completed",ct)});let ue=document.createElementNS("http://www.w3.org/2000/svg","rect");ue.setAttribute("x",String(L)),ue.setAttribute("y",String(V-12)),ue.setAttribute("width",String(H)),ue.setAttribute("height","31"),ue.setAttribute("fill","transparent"),ue.setAttribute("pointer-events","all"),ue.setAttribute("role","button"),ue.setAttribute("tabindex","0"),ue.setAttribute("aria-label",`Poka\u017C krok ${String(K.number).padStart(2,"0")}: ${K.label}`),ue.dataset.umlStep=String(X),ue.classList.add("uml-step-hit"),ue.classList.toggle("uml-step-hit-active",se!=="card"&&X===xe),ue.classList.toggle("uml-step-hit-completed",ct),x.appendChild(ue)})},[t.alt,e.kind,l,se,n,xe,p,j]),(0,S.useEffect)(()=>{let x=m.current?.querySelector("svg");if(x&&(x.querySelectorAll(".uml-layout-hit,.uml-layout-resize").forEach(b=>b.remove()),x.classList.toggle("uml-layout-editing",_),!(!_||!j)))for(let b of y.current.values()){let T=j.elements[b.id]??po(b),O=document.createElementNS("http://www.w3.org/2000/svg","rect");O.setAttribute("x",String(T.x)),O.setAttribute("y",String(T.y)),O.setAttribute("width",String(Math.max(12,T.width))),O.setAttribute("height",String(Math.max(12,T.height))),O.setAttribute("tabindex","0"),O.setAttribute("role","button"),O.setAttribute("aria-label",`Edytuj element UML ${b.id}`),O.dataset.umlLayoutId=b.id,O.dataset.umlLayoutAction="move",O.classList.add("uml-layout-hit"),b.id===Q&&O.classList.add("is-selected");let v=document.createElementNS("http://www.w3.org/2000/svg","title");if(v.textContent=`${b.id} \xB7 przeci\u0105gnij, aby zmieni\u0107 po\u0142o\u017Cenie`,O.appendChild(v),x.appendChild(O),b.id===Q){let w=Math.max(8,Math.min(14,Math.min(T.width,T.height)*.1)),L=document.createElementNS("http://www.w3.org/2000/svg","rect");L.setAttribute("x",String(T.x+T.width-w/2)),L.setAttribute("y",String(T.y+T.height-w/2)),L.setAttribute("width",String(w)),L.setAttribute("height",String(w)),L.dataset.umlLayoutId=b.id,L.dataset.umlLayoutAction="resize",L.classList.add("uml-layout-resize"),x.appendChild(L)}}},[p,_,j,Q,Ge]),(0,S.useEffect)(()=>{_&&(!Q||!y.current.has(Q))&&et(Ge[0]??null)},[_,Ge,Q]),(0,S.useEffect)(()=>{if(me===0||g.current===me)return;let x=!1,b=0,T=v=>{if(x)return;let w=m.current?.querySelector(`.uml-step-hit[data-uml-step="${xe}"]`);if(!w)return;if(document.querySelector(".viewer-chrome")?.classList.contains("is-nvim-fit")){g.current=me;return}let H=kp(w);if(!H){g.current=me;return}let K=ag(w);if(!K){let Z=cg(w,H),V=Z===0?sp(w,H):0,oe=Z===0?ap(w,V):Z;if(vFn){b=window.requestAnimationFrame(()=>T(v+1));return}g.current=me;return}let X=sp(w,K);if(Math.abs(X)<=Fn){g.current=me;return}let D=ap(w,X);if(vFn){b=window.requestAnimationFrame(()=>T(v+1));return}g.current=me},O=window.requestAnimationFrame(()=>T(0));return()=>{x=!0,window.cancelAnimationFrame(O),window.cancelAnimationFrame(b)}},[me,xe,p]);let F=(x,b,T=se,O=!0)=>{let v={task:e.task??null,block:e.block??null,phase:{id:x.phase.id,label:x.phase.label},step:x.step,focusLevel:T,activate:b&&document.documentElement.dataset.nvimSyncMode==="on"},w=()=>{window.dispatchEvent(new CustomEvent("stem:nvim-step",{detail:v}))};if(!O){w();return}let L=fetch("/api/navigation/current",{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({task:v.task,block:v.block,phase:v.phase,step:{id:v.step.id,number:v.step.number,label:v.step.label,mode:v.step.mode??(v.step.snapshot_ref?"RUN":"CODE"),code_ref:v.step.code_ref??null,event_id:v.step.event_id??null,strategy_ref:v.step.strategy_ref??null},snapshot_ref:v.step.snapshot_ref??null,focus_level:v.focusLevel,sync_requested:!!v.activate,actor:document.documentElement.dataset.cardClientId??"browser"})});v.activate?L.then(w,w):(w(),L.catch(()=>{}))},B=(x,b=!1,T=se,O=!0)=>{let v=Math.max(0,Math.min(l.length-1,x)),w=l[v];if(w){if(A(T),v===xe){F(w,b,T,O);return}Dn(L=>L+1),E.current=b,N.current=T,C.current=O,wn(v)}},te=(x,b)=>{let T=m.current?.querySelector("svg"),O=T?.getScreenCTM();if(!T||!O)return null;let v=T.createSVGPoint();return v.x=x,v.y=b,v.matrixTransform(O.inverse())},at=x=>{if(!_||!j)return;let b=x.target?.closest(".uml-layout-hit[data-uml-layout-id],.uml-layout-resize[data-uml-layout-id]"),T=b?.dataset.umlLayoutId,O=T?y.current.get(T):null,v=te(x.clientX,x.clientY);if(!T||!O||!v)return;x.preventDefault(),x.stopPropagation();let w=j.elements[T]??po(O);h.current={id:T,action:b?.dataset.umlLayoutAction==="resize"?"resize":"move",pointerId:x.pointerId,startX:v.x,startY:v.y,initial:w},et(T),$(L=>L&&{...L,elements:{...L.elements,[T]:w}}),x.currentTarget.setPointerCapture(x.pointerId)},zt=x=>{let b=h.current;if(!b||b.pointerId!==x.pointerId)return;let T=te(x.clientX,x.clientY);if(!T)return;x.preventDefault();let O=T.x-b.startX,v=T.y-b.startY,w=b.action==="resize"?{...b.initial,width:Math.max(24,b.initial.width+O),height:Math.max(18,b.initial.height+v)}:{...b.initial,x:b.initial.x+O,y:b.initial.y+v};$(L=>L&&{...L,elements:{...L.elements,[b.id]:w}}),re(!0)},Ee=x=>{let b=h.current;!b||b.pointerId!==x.pointerId||(h.current=null,x.currentTarget.hasPointerCapture(x.pointerId)&&x.currentTarget.releasePointerCapture(x.pointerId))},ne=(x,b)=>{let T=y.current.get(x);T&&($(O=>{if(!O)return O;let v=O.elements[x]??po(T);return{...O,elements:{...O.elements,[x]:b(v)}}}),re(!0))},st=async()=>{if(!j||!t.source_href)return;let x=Object.fromEntries(Array.from(y.current.values()).map(T=>[T.id,j.elements[T.id]??po(T)])),b={...j,elements:x};U("Zapisywanie uk\u0142adu\u2026");try{let T=await fetch("/api/uml-layout",{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({source:t.source_href,layout:b,actor:document.documentElement.dataset.cardClientId??"browser"})});if(!T.ok)throw new Error((await T.json().catch(()=>null))?.message??`HTTP ${T.status}`);let O=await T.json();R(O.layout),$(O.layout),re(!1),Ne(!1),U(`Zapisano ${Object.keys(O.layout.elements).length} element\xF3w w JSON.`)}catch(T){U(`B\u0142\u0105d zapisu: ${T instanceof Error?T.message:String(T)}`)}},Ye=()=>{M&&($(M),re(!1),U("Przywr\xF3cono ostatnio zapisany uk\u0142ad."))},bn=async()=>{if(!(!t.source_href||!window.confirm("Usun\u0105\u0107 r\u0119czny uk\u0142ad i wr\xF3ci\u0107 do geometrii PlantUML?"))){U("Resetowanie uk\u0142adu\u2026");try{let x=await fetch(`/api/uml-layout?source=${encodeURIComponent(t.source_href)}&actor=${encodeURIComponent(document.documentElement.dataset.cardClientId??"browser")}`,{method:"DELETE"});if(!x.ok)throw new Error(`HTTP ${x.status}`);let b=ms(t,e,M?.source_digest??"");R(b),$(b),re(!1),et(null),U("Przywr\xF3cono automatyczny uk\u0142ad PlantUML.")}catch(x){U(`B\u0142\u0105d resetu: ${x instanceof Error?x.message:String(x)}`)}}},ie=(x,b)=>{if(_)return;let T=Array.from(m.current?.querySelectorAll(".uml-step-hit[data-uml-step]")??[]).filter(v=>{let w=v.getBoundingClientRect();return x>=w.left&&x<=w.right&&b>=w.top&&b<=w.bottom}).sort((v,w)=>{let L=v.getBoundingClientRect(),H=w.getBoundingClientRect();return Math.abs(b-(L.top+L.bottom)/2)-Math.abs(b-(H.top+H.bottom)/2)}),O=Number(T[0]?.dataset.umlStep);Number.isInteger(O)&&B(O,!1,"step")},q=x=>{if(_||x.key!=="Enter"&&x.key!==" ")return;let b=x.target,T=Number(b.dataset?.umlStep);Number.isInteger(T)&&(x.preventDefault(),B(T,x.key==="Enter","step"))},wt=x=>{let b=l.findIndex(T=>T.phase.id===x.id);b>=0&&B(b,!1,"phase")},fe=l[xe];if((0,S.useEffect)(()=>{fe&&(F(fe,E.current,N.current??se,C.current),E.current=!1,N.current=null,C.current=!0)},[fe?.phase.id,fe?.step.id,e.block]),(0,S.useEffect)(()=>{let x=b=>{let T=b.detail;if(!T||e.task?.id&&T.task.id!==e.task.id||e.block?.id&&T.block.id!==e.block.id)return;let O=l.findIndex(v=>v.phase.id===T.phase.id&&v.step.id===T.step.id);if(O<0){A("card");return}B(O,!1,T.focus_level,!1)};return window.addEventListener("stem:navigation-command",x),()=>window.removeEventListener("stem:navigation-command",x)},[e.block?.id,e.task?.id,l,se,xe]),(0,S.useEffect)(()=>{let x=b=>{if(b.metaKey||_||b.target?.closest("input, textarea, select, [contenteditable='true']"))return;let O=d.current;if(!O)return;let v=Array.from(document.querySelectorAll("[data-stem-nav='uml-block']")),w=window.innerHeight/2;if(v.sort((D,Z)=>{let V=D.getBoundingClientRect(),oe=Z.getBoundingClientRect();return Math.abs((V.top+V.bottom)/2-w)-Math.abs((oe.top+oe.bottom)/2-w)})[0]!==O)return;if(b.key==="Enter"&&b.ctrlKey&&!b.altKey&&!b.shiftKey){if(!fe?.step.progress_id||!n)return;b.preventDefault(),b.stopPropagation();let D=n.items?.[fe.step.progress_id]?.status==="approved";r(fe.step.progress_id,D?"pending":"approved");return}if(b.key==="Enter"&&!b.altKey&&!b.shiftKey){if(!fe)return;b.preventDefault(),b.stopPropagation(),F(fe,!0,se);return}let H=["card","task","block","phase","step","snapshot"];if((b.key==="ArrowLeft"||b.key==="ArrowRight"||b.key==="Escape")&&!b.altKey&&!b.ctrlKey&&!b.shiftKey){let D=b.key==="ArrowRight"?1:-1,Z=H[Math.max(0,Math.min(H.length-1,H.indexOf(se)+D))];b.preventDefault(),b.stopPropagation(),A(Z),fe&&F(fe,!1,Z);return}if(b.key!=="ArrowUp"&&b.key!=="ArrowDown"||se==="card"&&!b.altKey&&!b.ctrlKey&&!b.shiftKey)return;let K=b.key==="ArrowUp"?-1:1,X=se;if(b.altKey&&b.shiftKey&&!b.ctrlKey)X="snapshot";else if(b.ctrlKey&&b.shiftKey&&!b.altKey)X="step";else if(b.altKey&&!b.ctrlKey&&!b.shiftKey)X="task";else if(b.ctrlKey&&!b.altKey&&!b.shiftKey)X="block";else if(b.shiftKey&&!b.altKey&&!b.ctrlKey)X="phase";else if(b.altKey||b.ctrlKey||b.shiftKey)return;b.preventDefault(),b.stopPropagation(),fetch("/api/navigation/move",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({level:X,delta:K,sync_requested:!1,actor:document.documentElement.dataset.cardClientId??"browser"})}).then(D=>D.ok?D.json():Promise.reject(new Error(`HTTP ${D.status}`))).then(D=>{D.current&&window.dispatchEvent(new CustomEvent("stem:navigation-command",{detail:D.current}))}).catch(()=>{})};return window.addEventListener("keydown",x),()=>window.removeEventListener("keydown",x)},[fe,l,se,r,n,_]),!fe)return null;let{phase:Qt,step:jn}=fe,qt=l.filter(x=>x.phase.id===Qt.id),go=e.block?.label.match(/^A[1-8]\b/)?.[0]??"UML",ho=(e.title??e.block?.label??t.caption).replace(/^A[1-8]\s*[·—:-]\s*/i,""),Un=Q?y.current.get(Q)??null:null,ut=Un?j?.elements[Un.id]??po(Un):null;return(0,a.jsxs)("div",{ref:d,className:"uml-react uml-snapshot-react","data-stem-nav":"uml-block","data-task-id":e.task?.id,"data-block-id":e.block?.id,"data-navigation-level":se,children:[(0,a.jsxs)("header",{className:"uml-snapshot-header",children:[(0,a.jsxs)("div",{className:"uml-header-first-line",children:[(0,a.jsxs)("h3",{children:[(0,a.jsx)("span",{children:go})," \u2014 ",ho,t.tile&&(0,a.jsxs)("small",{className:"uml-tile-part",children:["cz\u0119\u015B\u0107 ",t.tile.index,"/",t.tile.count," \xB7 r",t.tile.row," c",t.tile.column]})]}),(0,a.jsxs)("div",{className:"uml-header-navigation",children:[(0,a.jsx)(Ep,{continuation:o}),t.source_href&&(0,a.jsx)("button",{className:`uml-edit-toggle${_?" is-active":""}`,"aria-pressed":_,title:"R\u0119czna edycja po\u0142o\u017Cenia i parametr\xF3w blok\xF3w UML",onClick:()=>P(x=>!x),children:_?"ZAMKNIJ EDIT":"EDIT"}),(0,a.jsx)("div",{className:"uml-phase-actions","aria-label":"Fazy diagramu",children:i.map(x=>(0,a.jsx)("button",{"aria-pressed":Qt.id===x.id,onClick:()=>wt(x),children:x.label},x.id))}),(0,a.jsx)("div",{className:"uml-step-actions","aria-label":`Checkpointy fazy ${Qt.label}`,children:qt.map(x=>{let b=l.indexOf(x),T=!!(x.step.progress_id&&n?.items?.[x.step.progress_id]?.status==="approved");return(0,a.jsx)("button",{"aria-label":`Krok ${x.step.number}: ${x.step.label}`,"aria-pressed":se!=="card"&&b===xe,"data-completed":T?"true":"false","data-mode":x.step.mode??"RUN",title:`${x.step.mode??"RUN"} \xB7 ${x.step.label}`,onClick:()=>B(b),children:String(x.step.number).padStart(2,"0")},x.step.id)})})]})]}),e.block?.description&&(0,a.jsx)("p",{className:"uml-header-description",children:e.block.description})]}),(0,a.jsxs)("div",{className:"uml-snapshot-layout",children:[(0,a.jsxs)("div",{ref:m,className:`uml-canvas uml-step-canvas${_?" is-layout-editing":""}`,onClick:x=>ie(x.clientX,x.clientY),onKeyDown:q,onPointerDown:at,onPointerMove:zt,onPointerUp:Ee,onPointerCancel:Ee,children:[p?(0,a.jsx)("div",{className:`uml-svg-host${t.tile?" is-tiled":""}`,style:gs(t.tile),dangerouslySetInnerHTML:{__html:p}}):(0,a.jsx)("img",{src:t.href,alt:t.alt}),f&&(0,a.jsx)("span",{className:"uml-inline-warning",children:"Widok statyczny \u2014 wybierz krok przyciskiem nad diagramem."})]}),_&&j&&(0,a.jsxs)("aside",{className:"uml-layout-editor","aria-label":"Edytor uk\u0142adu UML",children:[(0,a.jsxs)("div",{className:"uml-layout-editor-title",children:[(0,a.jsx)("strong",{children:"UML layout"}),(0,a.jsx)("span",{children:ke?"niezapisane zmiany":"zapisany"}),Le&&(0,a.jsx)("b",{children:"\u0179r\xF3d\u0142o PlantUML zmieni\u0142o si\u0119"})]}),(0,a.jsxs)("label",{children:["Element",(0,a.jsx)("select",{value:Q??"",onChange:x=>et(x.target.value||null),children:Ge.map(x=>(0,a.jsx)("option",{value:x,children:x},x))})]}),Q&&ut&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"uml-layout-number-grid",children:["x","y","width","height"].map(x=>(0,a.jsxs)("label",{children:[x,(0,a.jsx)("input",{type:"number",step:"1",min:x==="width"||x==="height"?12:void 0,value:Math.round(ut[x]*100)/100,onChange:b=>ne(Q,T=>({...T,[x]:Number(b.target.value)}))})]},x))}),(0,a.jsxs)("label",{className:"uml-layout-editor-text",children:["Tekst bloku \u2014 jeden wiersz SVG na lini\u0119",(0,a.jsx)("textarea",{rows:Math.max(3,Math.min(10,ut.text?.lines?.length??3)),value:(ut.text?.lines??[]).join(` +`),onChange:x=>ne(Q,b=>({...b,text:{...b.text,title:x.target.value.split(` +`)[0]??"",description:x.target.value.split(` +`).slice(1).join(` +`),lines:x.target.value.split(` +`)}}))})]}),(0,a.jsxs)("div",{className:"uml-layout-style-grid",children:[(0,a.jsxs)("label",{children:["Font",(0,a.jsx)("input",{type:"number",min:"4",max:"96",step:"0.5",value:ut.text?.font_size??12,onChange:x=>ne(Q,b=>({...b,text:{...b.text,font_size:Number(x.target.value)}}))})]}),(0,a.jsxs)("label",{children:["Wyr\xF3wnanie",(0,a.jsxs)("select",{value:ut.text?.align??"left",onChange:x=>ne(Q,b=>({...b,text:{...b.text,align:x.target.value}})),children:[(0,a.jsx)("option",{value:"left",children:"left"}),(0,a.jsx)("option",{value:"center",children:"center"}),(0,a.jsx)("option",{value:"right",children:"right"})]})]}),(0,a.jsxs)("label",{children:["Wype\u0142nienie",(0,a.jsx)("input",{type:"color",value:ut.style?.fill??"#edf6fa",onChange:x=>ne(Q,b=>({...b,style:{...b.style,fill:x.target.value}}))})]}),(0,a.jsxs)("label",{children:["Obramowanie",(0,a.jsx)("input",{type:"color",value:ut.style?.stroke??"#2c7794",onChange:x=>ne(Q,b=>({...b,style:{...b.style,stroke:x.target.value}}))})]}),(0,a.jsxs)("label",{children:["Linia",(0,a.jsx)("input",{type:"number",min:"0.5",max:"12",step:"0.25",value:ut.style?.stroke_width??1.5,onChange:x=>ne(Q,b=>({...b,style:{...b.style,stroke_width:Number(x.target.value)}}))})]})]})]}),(0,a.jsxs)("div",{className:"uml-layout-editor-actions",children:[(0,a.jsx)("button",{className:"is-primary",disabled:!ke,onClick:()=>void st(),children:"Zapisz uk\u0142ad"}),(0,a.jsx)("button",{disabled:!ke,onClick:Ye,children:"Cofnij zmiany"}),(0,a.jsx)("button",{className:"is-danger",onClick:()=>void bn(),children:"Reset PlantUML"})]}),(0,a.jsx)("output",{children:G})]})]})]})}function Ug(e){return e.config.phases?.length?(0,a.jsx)(jg,{...e}):(0,a.jsx)(Dg,{...e})}function Vg({asset:e,continuation:t}){let n=e.tile;if(!n)return(0,a.jsx)("img",{src:e.href,alt:e.alt,loading:"lazy"});let r=e.diagram_block_label?.match(/^A[1-8]\b/)?.[0]??e.diagram_title?.match(/^A[1-8]\b/)?.[0]??e.caption.match(/^A[1-8]\b/)?.[0]??"UML",o=(e.diagram_title??e.caption).replace(/^A[1-8]\s*[·—:-]\s*/i,"");return(0,a.jsxs)("div",{className:"uml-react uml-snapshot-react uml-static-tile",children:[(0,a.jsxs)("header",{className:"uml-snapshot-header",children:[(0,a.jsxs)("div",{className:"uml-header-first-line",children:[(0,a.jsxs)("h3",{children:[(0,a.jsx)("span",{children:r})," \u2014 ",o,(0,a.jsxs)("small",{className:"uml-tile-part",children:["cz\u0119\u015B\u0107 ",n.index,"/",n.count," \xB7 r",n.row," c",n.column]})]}),(0,a.jsxs)("div",{className:"uml-header-navigation",children:[(0,a.jsx)(Ep,{continuation:t}),(0,a.jsx)("div",{className:"uml-phase-actions","aria-label":"Fazy logicznego diagramu",children:(e.diagram_phase_labels??[]).map(i=>(0,a.jsx)("span",{children:i},i))})]})]}),e.diagram_description&&(0,a.jsx)("p",{className:"uml-header-description",children:e.diagram_description})]}),(0,a.jsx)("div",{className:"uml-snapshot-layout",children:(0,a.jsx)("div",{className:"uml-canvas uml-step-canvas",children:(0,a.jsx)("div",{className:"uml-svg-host is-tiled is-static",style:gs(n),children:(0,a.jsx)("div",{className:"uml-static-crop",style:{width:`${n.width}px`,height:`${n.height}px`},children:(0,a.jsx)("img",{src:e.href,alt:e.alt,style:{width:`${n.full_width}px`,height:`${n.full_height}px`,transform:`translate(${-(n.x-n.full_x)}px, ${-(n.y-n.full_y)}px)`}})})})})})]})}function Bg({asset:e,number:t,progress:n,onSetStatus:r,continuation:o}){let i=e.interactive?.kind,l=!!i;return(0,a.jsxs)("figure",{className:`card-asset asset-${e.kind} ${l?"asset-interactive":""}`,id:e.label.replace(":","-"),children:[i==="uml-sequence"||i==="uml-class"?(0,a.jsx)(Ug,{config:e.interactive,asset:e,progress:n,onSetStatus:r,continuation:o}):i==="allocator-memory-flow"?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(Fg,{config:e.interactive,title:e.caption}),(0,a.jsx)("img",{className:"asset-static-fallback",src:e.href,alt:e.alt})]}):e.tile?(0,a.jsx)(Vg,{asset:e,continuation:o}):(0,a.jsx)("a",{href:e.href,target:"_blank",rel:"noopener",children:(0,a.jsx)("img",{src:e.href,alt:e.alt,loading:"lazy",style:{width:`${e.width*100}%`}})}),e.source_href&&(0,a.jsx)("a",{className:"asset-source-link",href:e.source_href,target:"_blank",rel:"noopener",children:"\u0179r\xF3d\u0142o PlantUML"}),(0,a.jsxs)("figcaption",{children:["Rys. ",e.figure_number??t,". ",e.caption]})]})}function Hg({steps:e,progress:t,onCycle:n}){if(!e.length)return null;let r={pending:"\u25CB niezatwierdzony",approved:"\u25CF zatwierdzony"};return(0,a.jsx)("div",{className:"step-map",children:e.map(o=>{let i=t?.items?.[o.id]?.status??"pending";return(0,a.jsxs)("div",{className:`step-row ${i==="approved"?"lesson-progress-completed":""}`,children:[(0,a.jsx)("button",{className:"lesson-progress-control","data-status":i,onClick:()=>n(o.id,i),disabled:!t,children:r[i]}),(0,a.jsx)("span",{className:"step-title",children:o.title})]},o.id)})})}function Kg({section:e,progress:t,onCycle:n,onSetStatus:r,figureOffset:o}){let i=yg(e);return(0,a.jsxs)(hs,{header:e.header_html,left:e.left_margin_html,right:e.right_margin_html,className:`section-sheet${e.assets.length?" section-sheet--asset":""} sheet--${e.page_orientation||"portrait"}`,children:[(0,a.jsx)("div",{id:e.id,className:"section-anchor"}),e.show_heading&&(0,a.jsxs)("h2",{className:"section-heading",children:[(0,a.jsx)("span",{className:"section-index",children:e.index}),(0,a.jsx)("span",{className:"section-title",children:e.title})]}),(0,a.jsx)(Hg,{steps:e.steps,progress:t,onCycle:n}),e.content_html&&(0,a.jsx)(Yt,{html:e.content_html}),(0,a.jsx)("div",{className:"tasks",children:e.tasks.map(l=>(0,a.jsx)($g,{task:l},l.ref))}),e.assets.map((l,s)=>(0,a.jsx)(Bg,{asset:l,number:o+s+1,progress:t,onSetStatus:r,continuation:i},l.label)),e.tasks.map(l=>l.conclusion_html&&(0,a.jsxs)("aside",{className:"task-conclusion",children:[(0,a.jsx)("strong",{children:"WNIOSEK"}),(0,a.jsx)(Yt,{html:l.conclusion_html})]},`${l.ref}-conclusion`))]})}function Wg({progress:e,error:t}){if(!e?.summary&&!t)return null;let n=e?.summary.counts;return(0,a.jsxs)("section",{className:"lesson-progress-topbar","aria-label":"Przebieg zaj\u0119\u0107",children:[(0,a.jsx)("strong",{children:"PRZEBIEG"}),n&&(0,a.jsxs)("output",{title:"Zatwierdzone kroki",children:[n.approved,"/",e.summary.total]}),t&&(0,a.jsx)("span",{className:"lesson-progress-error",title:t,children:"!"}),(0,a.jsx)("a",{href:"/api/report/teams.md",target:"_blank",rel:"noopener",title:"Otw\xF3rz raport do Teams",children:"Teams"}),(0,a.jsx)("a",{href:"/api/report/lesson.html",target:"_blank",rel:"noopener",title:"Otw\xF3rz \u015Blad lekcji ze zrzutami",children:"\u015Alad"})]})}var $n=[{id:"u01",name:"Alicja Nowak",laptop:3,connection:"online",sync:"follow",position:"A5\xB708"},{id:"u02",name:"Bartosz Kowalski",laptop:7,connection:"online",sync:"follow",position:"A5\xB708"},{id:"u03",name:"Celina Wi\u015Bniewska",laptop:11,connection:"away",sync:"paused",position:"A5\xB705"},{id:"u04",name:"Dawid W\xF3jcik",laptop:14,connection:"offline",sync:"follow",position:"\u2014"},{id:"u05",name:"Emilia Kami\u0144ska",laptop:18,connection:"online",sync:"local",position:"A3\xB702"},{id:"u06",name:"Filip Lewandowski",laptop:22,connection:"error",sync:"follow",position:"A5\xB706"},{id:"u07",name:"Gabriela Zieli\u0144ska",laptop:24,connection:"online",sync:"follow",position:"A5\xB708"}];function Gg(e){let t=new Map,n=(r,o)=>{let i=r.trim()||"task",l=t.get(i);return l||(l={id:i,label:o.trim()||i.toUpperCase(),blocks:[],exercises:[]},t.set(i,l)),l};for(let r of e.sections){for(let o of r.assets){let i=o.interactive;if(!i?.task||!i.block)continue;let l=n(i.task.id,i.task.label),s=`diagram:${i.block.id}`,u=l.blocks.find(d=>d.id===s);u||(u={id:s,label:i.block.label,kind:"diagram",phases:[],steps:[]},l.blocks.push(u));for(let d of i.phases??[]){let m=u.phases.find(y=>y.id===d.id);m||(m={...d,steps:[]},u.phases.push(m));for(let y of d.steps)m.steps.some(h=>h.id===y.id)||m.steps.push(y)}}for(let o of r.tasks){let i=n(o.ref,o.title);i.label=o.title||i.label;for(let l of o.flow){if(l.kind==="exercise"){i.exercises.some(u=>u.id===l.id)||i.exercises.push(l);continue}let s=`flow:${l.id}`;i.blocks.some(u=>u.id===s)||i.blocks.push({id:s,label:l.title,kind:"flow",phases:[],steps:l.steps.map((u,d)=>({id:u.id,number:d+1,label:u.title,description:u.title,mode:"RUN"}))})}}}return[...t.values()]}function gp({side:e,panels:t,activeId:n,pinnedId:r,onActiveChange:o,onPinnedChange:i}){let l=(0,S.useRef)(0),s=t.find(g=>g.id===n)??null,u=(0,S.useCallback)(()=>{window.clearTimeout(l.current)},[]),d=(0,S.useCallback)(()=>{u(),!r&&(l.current=window.setTimeout(()=>o(null),150))},[u,o,r]),m=(0,S.useCallback)(g=>{u(),r||o(g)},[u,o,r]),y=(0,S.useCallback)(g=>{if(u(),r===g){i(null),o(null);return}i(g),o(g)},[u,o,i,r]),h=(0,S.useCallback)(()=>{u(),i(null),o(null)},[u,o,i]);return(0,S.useEffect)(()=>()=>window.clearTimeout(l.current),[]),t.length?(0,a.jsxs)("div",{className:`side-panel-dock side-panel-dock--${e}`,"data-side":e,children:[(0,a.jsx)("nav",{className:"side-panel-rail","aria-label":`Panele ${e==="left"?"lewe":"prawe"}`,children:t.map(g=>{let E=n===g.id,N=r===g.id;return(0,a.jsx)("button",{type:"button",className:`side-panel-trigger${E?" is-open":""}${N?" is-pinned":""}`,style:{"--side-panel-accent":g.accent},"aria-controls":`side-panel-${e}-${g.id}`,"aria-expanded":E,"aria-pressed":N,"aria-label":`${g.title}: ${N?"odpnij":"przypnij"} panel`,title:`${g.title} \xB7 ${N?"Fixed":"Auto"}`,onMouseEnter:()=>m(g.id),onMouseLeave:d,onFocus:()=>m(g.id),onClick:()=>y(g.id),children:(0,a.jsx)("span",{children:g.railLabel})},g.id)})}),(0,a.jsx)("aside",{id:s?`side-panel-${e}-${s.id}`:void 0,className:`side-panel${s?" is-open":""}`,"aria-hidden":!s,"aria-label":s?.title,style:s?{"--side-panel-accent":s.accent}:void 0,onMouseEnter:u,onMouseLeave:d,onFocusCapture:u,children:s&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("header",{className:"side-panel-header",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("small",{children:e==="left"?"LEWY PANEL":"PRAWY PANEL"}),(0,a.jsx)("strong",{children:s.title})]}),(0,a.jsxs)("div",{className:"side-panel-actions",children:[(0,a.jsx)("button",{type:"button",className:r===s.id?"is-pinned":"",onClick:()=>y(s.id),title:r===s.id?"Tryb Fixed \u2014 kliknij, aby przej\u015B\u0107 do Auto":"Tryb Auto \u2014 kliknij, aby przypi\u0105\u0107",children:r===s.id?"Fixed":"Auto"}),(0,a.jsx)("button",{type:"button",onClick:h,"aria-label":`Zamknij ${s.title}`,children:"\xD7"})]})]}),(0,a.jsx)("div",{className:"side-panel-body",children:s.content})]})})]}):null}function Yg({library:e,onNavigate:t}){if(!e)return null;let n=e.subjects.reduce((i,l)=>i+l.levels.reduce((s,u)=>s+u.series.length,0),0),r=e.subjects.reduce((i,l)=>i+l.levels.reduce((s,u)=>s+u.series.reduce((d,m)=>d+m.cards.filter(y=>y.available).length,0),0),0),o=e.subjects.reduce((i,l)=>i+l.levels.reduce((s,u)=>s+u.series.reduce((d,m)=>d+m.cards.length,0),0),0);return(0,a.jsxs)("div",{className:"card-library-panel",children:[(0,a.jsx)("header",{className:"card-library-header",children:(0,a.jsxs)("div",{children:[(0,a.jsxs)("small",{children:[e.workspace||"STEM"," \xB7 KATALOG"]}),(0,a.jsx)("h2",{id:"card-library-title",children:"Serie i karty"}),(0,a.jsxs)("p",{children:[n," serii \xB7 ",r,"/",o," kart online"]})]})}),(0,a.jsx)("nav",{className:"card-library-tree","aria-label":"Drzewo serii i kart",children:e.subjects.map(i=>(0,a.jsxs)("details",{className:"card-library-subject",open:i.current||void 0,children:[(0,a.jsxs)("summary",{children:[(0,a.jsx)("span",{className:"card-library-branch","aria-hidden":"true",children:"\u25BE"}),(0,a.jsxs)("span",{children:[(0,a.jsx)("strong",{children:i.title}),(0,a.jsx)("small",{children:"PRZEDMIOT"})]}),(0,a.jsx)("output",{children:i.levels.length})]}),(0,a.jsx)("div",{className:"card-library-level-list",children:i.levels.map(l=>(0,a.jsxs)("details",{className:"card-library-level",open:l.current||void 0,children:[(0,a.jsxs)("summary",{children:[(0,a.jsx)("span",{className:"card-library-branch","aria-hidden":"true",children:"\u25BE"}),(0,a.jsx)("strong",{children:l.title}),(0,a.jsx)("output",{children:l.series.length})]}),(0,a.jsx)("div",{className:"card-library-series-list",children:l.series.map(s=>(0,a.jsxs)("details",{className:"card-library-series",open:s.current||void 0,children:[(0,a.jsxs)("summary",{children:[(0,a.jsx)("span",{className:"card-library-branch","aria-hidden":"true",children:"\u25BE"}),(0,a.jsxs)("span",{children:[(0,a.jsx)("strong",{children:s.title}),s.role==="reference"&&(0,a.jsx)("small",{children:"REFERENCJA"})]}),(0,a.jsx)("output",{children:s.cards.length})]}),s.cards.length>0&&(0,a.jsx)("ul",{className:"card-library-cards",children:s.cards.map((u,d)=>{let m=u.tasks??[],y=(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("span",{className:"card-library-node","aria-hidden":"true",children:d===s.cards.length-1?"\u2514\u2500":"\u251C\u2500"}),(0,a.jsxs)("span",{className:"card-library-card-copy",children:[(0,a.jsx)("strong",{children:u.id}),(0,a.jsx)("span",{children:u.title})]}),(0,a.jsxs)("span",{className:"card-library-card-meta",children:[u.version&&(0,a.jsx)("small",{children:u.version}),(0,a.jsx)("small",{children:u.current?"BIE\u017B\u0104CA":u.available?"ONLINE":"PLAN"})]})]});return(0,a.jsxs)("li",{className:"card-library-card-entry",children:[u.current||u.available?(0,a.jsx)("a",{className:`card-library-card is-available${u.current?" is-current":""}`,href:u.current?"#":u.href,onClick:t,"aria-current":u.current?"page":void 0,title:u.current?"Poka\u017C bie\u017C\u0105c\u0105 kart\u0119":`Otw\xF3rz kart\u0119 ${u.id}`,children:y}):(0,a.jsx)("span",{className:"card-library-card is-unavailable","aria-disabled":"true",title:"Karta nie ma jeszcze wygenerowanego adresu",children:y}),m.length>0&&(0,a.jsx)("ul",{className:"card-library-tasks",children:m.map((h,g)=>{let E=(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("span",{className:"card-library-node","aria-hidden":"true",children:g===m.length-1?"\u2514\u2500":"\u251C\u2500"}),(0,a.jsx)("strong",{children:h.id.toUpperCase()}),(0,a.jsx)("span",{children:h.title})]}),N=u.current?`#${h.id}`:`${u.href}#${h.id}`;return(0,a.jsx)("li",{children:u.current||u.available?(0,a.jsx)("a",{href:N,onClick:t,children:E}):(0,a.jsx)("span",{children:E})},`${u.id}:${h.id}`)})})]},`${s.id}:${u.id}`)})})]},`${l.id}:${s.id}`))})]},l.id))})]},i.id))}),(0,a.jsxs)("footer",{className:"card-library-footer",children:[(0,a.jsxs)("span",{children:[(0,a.jsx)("i",{className:"is-online"})," online"]}),(0,a.jsxs)("span",{children:[(0,a.jsx)("i",{className:"is-draft"})," plan / brak strony"]}),(0,a.jsx)("kbd",{children:"Esc"})]})]})}function Qg({toc:e,onNavigate:t}){return(0,a.jsxs)("nav",{className:"side-toc","aria-label":"Spis tre\u015Bci karty",children:[(0,a.jsxs)("header",{children:[(0,a.jsx)("small",{children:"KARTA PRACY"}),(0,a.jsx)("h2",{children:"Spis tre\u015Bci"})]}),(0,a.jsx)("ol",{children:e.map((n,r)=>(0,a.jsx)("li",{children:(0,a.jsxs)("a",{href:`#${n.id}`,onClick:t,children:[(0,a.jsx)("span",{children:String(r+1).padStart(2,"0")}),(0,a.jsx)("strong",{children:n.label})]})},n.id))})]})}function qg({progress:e,error:t}){let n=e?.summary?.counts;return(0,a.jsxs)("section",{className:"side-progress","aria-label":"Przebieg zaj\u0119\u0107 i raport",children:[(0,a.jsxs)("header",{children:[(0,a.jsx)("small",{children:"DOWODY I RAPORT"}),(0,a.jsx)("h2",{children:"Przebieg zaj\u0119\u0107"})]}),(0,a.jsxs)("div",{className:"side-progress-summary",children:[(0,a.jsx)("span",{children:"Zatwierdzone kroki"}),(0,a.jsx)("strong",{children:n?`${n.approved}/${e.summary.total}`:"\u2014"})]}),t&&(0,a.jsx)("p",{className:"side-progress-error",children:t}),(0,a.jsxs)("div",{className:"side-progress-links",children:[(0,a.jsx)("a",{href:"/api/report/teams.md",target:"_blank",rel:"noopener",children:"Raport do Teams"}),(0,a.jsx)("a",{href:"/api/report/lesson.html",target:"_blank",rel:"noopener",children:"\u015Alad lekcji ze zrzutami"})]})]})}var Xg={online:"ONLINE",away:"ZAJ\u0118TY",offline:"OFFLINE",error:"B\u0141\u0104D"},Zg={follow:"SYNC",paused:"PAUZA",local:"LOKAL."},hp={present:"BY\u0141",missed:"NIE BY\u0141",pending:"OCZEK."},Jg={committed:"COMMIT",working:"PRACA",missing:"BRAK"};function Lp(e){let t=2166136261;for(let n=0;n>>0}function yp(e,t,n){let r=Lp(`${e.id}:step:${t}`),o=r%8,i=o===0?"missed":o===1?"pending":"present",l=8+n*4+r%3,s=10+Math.floor(l/60),u=l%60,d=(e.laptop*7+r)%60;return{state:i,timestamp:i==="present"?`${String(s).padStart(2,"0")}:${String(u).padStart(2,"0")}:${String(d).padStart(2,"0")}`:"\u2014"}}function vp(e,t){let n=Lp(`${e.id}:exercise:${t}`),r=n%6,o=r===0?"missing":r===1?"working":"committed";return{state:o,commit:o==="committed"?n.toString(16).padStart(8,"0").slice(0,7):"\u2014"}}function Gt({id:e,depth:t,title:n,kind:r,refValue:o,session:i,evidence:l,state:s,hasChildren:u=!1,expanded:d=!1,onToggle:m}){return(0,a.jsxs)("div",{className:`classroom-treegrid-row classroom-node-row is-${r}`,role:"row","aria-level":t+1,"aria-expanded":u?d:void 0,"data-node-id":e,style:{"--classroom-depth":t},children:[(0,a.jsxs)("span",{className:"classroom-node-title",role:"gridcell",title:n,children:[u?(0,a.jsx)("button",{className:"classroom-expander",type:"button",onClick:m,"aria-label":`${d?"Zwi\u0144":"Rozwi\u0144"}: ${n}`,children:d?"\u25BE":"\u25B8"}):(0,a.jsx)("span",{className:"classroom-expander is-empty","aria-hidden":"true"}),(0,a.jsx)("strong",{children:n})]}),(0,a.jsx)("span",{role:"gridcell",children:o}),(0,a.jsx)("span",{role:"gridcell",children:i}),(0,a.jsx)("span",{role:"gridcell",children:l}),(0,a.jsx)("span",{role:"gridcell",children:s})]})}function eh({model:e}){let t=Gg(e),n=$n[0]?.id,r=t[0]?.id,[o,i]=(0,S.useState)(()=>new Set(["class",...n?[`student:${n}`]:[],...n&&r?[`student:${n}:task:${r}`]:[]])),l=m=>o.has(m),s=m=>{i(y=>{let h=new Set(y);return h.has(m)?h.delete(m):h.add(m),h})},u=$n.filter(m=>m.connection==="online"||m.connection==="away").length,d=$n.filter(m=>m.sync==="follow").length;return(0,a.jsxs)("section",{className:"classroom-panel","aria-label":"Klasa i stanowiska uczni\xF3w",children:[(0,a.jsxs)("div",{className:"classroom-treegrid",role:"treegrid","aria-label":"Uczniowie, struktura karty, obecno\u015B\u0107 i commity","aria-colcount":5,children:[(0,a.jsxs)("div",{className:"classroom-treegrid-row classroom-treegrid-head",role:"row",children:[(0,a.jsx)("span",{role:"columnheader",children:"DRZEWO"}),(0,a.jsx)("span",{role:"columnheader",children:"REF"}),(0,a.jsx)("span",{role:"columnheader",children:"SESJA"}),(0,a.jsx)("span",{role:"columnheader",children:"DOW\xD3D"}),(0,a.jsx)("span",{role:"columnheader",children:"STAN"})]}),(0,a.jsx)(Gt,{id:"class",depth:0,kind:"class",title:"2TP \xB7 grupa A",refValue:`${$n.length} ucz.`,session:`${u}/${$n.length}`,evidence:`${d}/${$n.length} SYNC`,state:`${t.length} TASK`,hasChildren:!0,expanded:l("class"),onToggle:()=>s("class")}),l("class")&&$n.map(m=>{let y=`student:${m.id}`;return(0,a.jsxs)(S.default.Fragment,{children:[(0,a.jsx)(Gt,{id:y,depth:1,kind:"student",title:m.name,refValue:`L${String(m.laptop).padStart(2,"0")}`,session:(0,a.jsx)("b",{className:"classroom-status","data-status":m.connection,children:Xg[m.connection]}),evidence:(0,a.jsx)("b",{className:"classroom-sync","data-sync":m.sync,children:Zg[m.sync]}),state:m.position,hasChildren:t.length>0,expanded:l(y),onToggle:()=>s(y)}),l(y)&&t.map(h=>{let g=`${y}:task:${h.id}`,E=h.exercises.filter(N=>vp(m,N.id).state==="committed").length;return(0,a.jsxs)(S.default.Fragment,{children:[(0,a.jsx)(Gt,{id:g,depth:2,kind:"task",title:h.label,refValue:h.id.toUpperCase(),session:`${h.blocks.length} BLOCK`,evidence:`${E}/${h.exercises.length} COMMIT`,state:"TASK",hasChildren:h.blocks.length+h.exercises.length>0,expanded:l(g),onToggle:()=>s(g)}),l(g)&&h.blocks.map(N=>{let C=`${g}:block:${N.id}`,p=N.phases.length||N.steps.length;return(0,a.jsxs)(S.default.Fragment,{children:[(0,a.jsx)(Gt,{id:C,depth:3,kind:"block",title:N.label,refValue:N.kind==="diagram"?"BLOCK":"TASK BLOCK",session:`${p} ${N.phases.length?"PHASE":"STEP"}`,evidence:"\u2014",state:N.kind==="diagram"?"UML":"FLOW",hasChildren:p>0,expanded:l(C),onToggle:()=>s(C)}),l(C)&&N.phases.map(c=>{let f=`${C}:phase:${c.id}`;return(0,a.jsxs)(S.default.Fragment,{children:[(0,a.jsx)(Gt,{id:f,depth:4,kind:"phase",title:`PHASE \xB7 ${c.label}`,refValue:`${c.steps.length} STEP`,session:"\u2014",evidence:"\u2014",state:"PHASE",hasChildren:c.steps.length>0,expanded:l(f),onToggle:()=>s(f)}),l(f)&&c.steps.map(k=>{let _=yp(m,`${N.id}:${c.id}:${k.id}`,k.number);return(0,a.jsx)(Gt,{id:`${f}:step:${k.id}`,depth:5,kind:"step",title:`STEP ${String(k.number).padStart(2,"0")} \xB7 ${k.label}`,refValue:k.mode??"CODE",session:(0,a.jsx)("b",{className:"classroom-visit","data-visit":_.state,children:hp[_.state]}),evidence:_.timestamp,state:_.state==="present"?"OK":_.state==="missed"?"BRAK":"\u2014"},`${f}:step:${k.id}`)})]},f)}),l(C)&&N.steps.map(c=>{let f=yp(m,`${N.id}:${c.id}`,c.number);return(0,a.jsx)(Gt,{id:`${C}:step:${c.id}`,depth:4,kind:"step",title:`STEP ${String(c.number).padStart(2,"0")} \xB7 ${c.label}`,refValue:c.mode??"RUN",session:(0,a.jsx)("b",{className:"classroom-visit","data-visit":f.state,children:hp[f.state]}),evidence:f.timestamp,state:f.state==="present"?"OK":f.state==="missed"?"BRAK":"\u2014"},`${C}:step:${c.id}`)})]},C)}),l(g)&&h.exercises.length>0&&(()=>{let N=`${g}:exercises`;return(0,a.jsxs)(S.default.Fragment,{children:[(0,a.jsx)(Gt,{id:N,depth:3,kind:"exercises",title:"EXERCISES",refValue:`${h.exercises.length} EX`,session:`${E}/${h.exercises.length}`,evidence:"GIT",state:E===h.exercises.length?"DONE":"TODO",hasChildren:!0,expanded:l(N),onToggle:()=>s(N)}),l(N)&&h.exercises.map(C=>{let p=vp(m,C.id);return(0,a.jsx)(Gt,{id:`${N}:${C.id}`,depth:4,kind:"exercise",title:`EXERCISE \xB7 ${C.title}`,refValue:C.id,session:(0,a.jsx)("b",{className:"classroom-commit","data-commit":p.state,children:Jg[p.state]}),evidence:p.commit,state:p.state==="committed"?"DONE":p.state==="working"?"W TOKU":"TODO"},`${N}:${C.id}`)})]},N)})()]},g)})]},m.id)})]}),(0,a.jsxs)("footer",{className:"classroom-legend",children:[(0,a.jsxs)("span",{children:[(0,a.jsx)("b",{children:"BY\u0141"})," obecny przy omawianym STEP"]}),(0,a.jsxs)("span",{children:[(0,a.jsx)("b",{children:"COMMIT"})," rozwi\u0105zanie EXERCISE zapisane w Git"]})]})]})}function th({open:e,onClose:t}){return e?(0,a.jsx)("div",{className:"shortcut-help-backdrop",role:"presentation",onMouseDown:t,children:(0,a.jsxs)("section",{className:"shortcut-help",role:"dialog","aria-modal":"true","aria-labelledby":"shortcut-help-title",onMouseDown:r=>r.stopPropagation(),children:[(0,a.jsxs)("header",{children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("small",{children:"F12 \xB7 NAWIGACJA DYDAKTYCZNA"}),(0,a.jsx)("h2",{id:"shortcut-help-title",children:"CARD \u2192 TASK \u2192 BLOCK \u2192 PHASE \u2192 STEP \u2192 SNAPSHOT"})]}),(0,a.jsx)("button",{onClick:t,"aria-label":"Zamknij skorowidz",children:"\xD7"})]}),(0,a.jsx)("dl",{children:[["Alt + \u2191 / \u2193","poprzedni / nast\u0119pny TASK"],["Ctrl + \u2191 / \u2193","poprzedni / nast\u0119pny BLOCK"],["Shift + \u2191 / \u2193","poprzednia / nast\u0119pna PHASE"],["Ctrl + Shift + \u2191 / \u2193","poprzedni / nast\u0119pny STEP"],["Alt + Shift + \u2191 / \u2193","poprzedni / nast\u0119pny unikalny SNAPSHOT"],["\u2191 / \u2193","poprzedni / nast\u0119pny element aktywnego poziomu"],["\u2192","zejd\u017A poziom ni\u017Cej"],["\u2190 / Esc","wr\xF3\u0107 poziom wy\u017Cej"],["Enter","wybierz element; przy SYNC ON odtw\xF3rz stan"],["F1","reset aktualnego celu Neovima/kontenera"],["F2","synchronizuj cel z kursorem drzewa UML"],["Ctrl + `","maksymalizuj pane, dopasuj siatk\u0119 i od\u015Bwie\u017C Neovima"],["Ctrl + Enter","zatwierd\u017A lub cofnij zatwierdzenie kroku"],["Alt + 0","wysu\u0144 lub schowaj panel serii i kart"],["Alt + 1","poka\u017C lub ukryj panel Neovima"],["Alt + 2","prze\u0142\u0105cz SYNC OFF / SYNC ON"],["Alt + 3","uzbr\xF3j lub rozbr\xF3j sterowanie Neovimem"],["Alt + 4","wysoki panel 90% / zapisana wysoko\u015B\u0107 normalna"],["Alt + 5","dopasuj ca\u0142y ekran Neovima; wy\u0142\u0105cz r\u0119czny suwak"],["Alt + 6","przypnij lub ukryj model alokatora"],["Alt + 7","po\u0142\u0105cz segmenty UML w szeroki spread / wr\xF3\u0107 do A4"],["Alt + 3, potem najed\u017A","przeka\u017C klawiatur\u0119 i mysz do aktywnej sesji"],["Alt + W","prefiks polece\u0144 okien Neovima ()"],["Alt + strza\u0142ka","przejd\u017A do s\u0105siedniego okna Neovima"],["F12","poka\u017C lub ukryj ten skorowidz"]].map(([r,o])=>(0,a.jsxs)("div",{children:[(0,a.jsx)("dt",{children:(0,a.jsx)("kbd",{children:r})}),(0,a.jsx)("dd",{children:o})]},r))}),(0,a.jsx)("p",{children:"Przy SYNC ON wyb\xF3r stepu odtwarza i weryfikuje przypi\u0119ty stan maszyny. SYNC OFF nie resetuje debuggera. Po najechaniu panel przekazuje do Neovima wy\u0142\u0105cznie kontrolowane zdarzenia klawiatury i myszy, wi\u0119c mo\u017Cna normalnie kontynuowa\u0107 debugowanie."})]})}):null}function nh({model:e}){let t=!!e.has_allocator_model,n=(0,S.useRef)(null),r=(0,S.useRef)(null),o=(0,S.useRef)(`browser-${globalThis.crypto?.randomUUID?.()??Math.random().toString(36).slice(2)}`),i=(0,S.useRef)(0),l=(0,S.useRef)(0),s=(0,S.useRef)(!1),u=(0,S.useRef)(!1),d=(0,S.useRef)(0),m=(0,S.useRef)(0),y=(0,S.useRef)(0),[h,g]=(0,S.useState)(2.18),[E,N]=(0,S.useState)(()=>localStorage.getItem("stem-card-nvim-visible")!=="false"),[C,p]=(0,S.useState)(()=>localStorage.getItem("stem-card-nvim-sync-mode")==="true"),[c,f]=(0,S.useState)(()=>localStorage.getItem("stem-card-nvim-sync-mode")==="true"&&localStorage.getItem("stem-card-nvim-control-mode")==="true"),[k,_]=(0,S.useState)(!1),[P,M]=(0,S.useState)(!1),[R,j]=(0,S.useState)(()=>t&&localStorage.getItem("stem-card-allocator-visible")!=="false"),[$,ke]=(0,S.useState)(()=>{let v=localStorage.getItem("stem-card-diagram-spread");return v===null?!!e.has_spread:v==="true"}),[re,G]=(0,S.useState)({state:"hidden",syncMode:!1,syncReady:!1,controlMode:!1,controlEnabled:!1}),[U,Le]=(0,S.useState)(null),[Ne,Q]=(0,S.useState)(""),[et,Ge]=(0,S.useState)(null),[je,me]=(0,S.useState)(null),[Dn,xe]=(0,S.useState)(null),[wn,se]=(0,S.useState)(null),[A,F]=(0,S.useState)(!1),[B,te]=(0,S.useState)(""),[at,zt]=(0,S.useState)(()=>window.innerWidth),Ee=et==="library",ne=!!(et||Dn),st=(0,S.useCallback)(v=>{if(v){Ge("library"),me("library");return}Ge(w=>w==="library"?null:w),me(w=>w==="library"?null:w)},[]),Ye=(0,S.useCallback)(v=>{window.clearTimeout(y.current),te(v),y.current=window.setTimeout(()=>te(""),4200)},[]),bn=(0,S.useCallback)(async v=>{Ye(`${v} \xB7 wykonywanie\u2026`);try{let L=await fetch(v==="F1"?"/api/nvim/reset":"/api/navigation/sync",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({actor:o.current})}),H=await L.json().catch(()=>({}));if(!L.ok)throw new Error(H.message??H.error??`HTTP ${L.status}`);v==="F2"&&H.current?(l.current=Math.max(l.current,Number(H.current.revision??0)),window.dispatchEvent(new CustomEvent("stem:navigation-command",{detail:H.current})),Ye(`F2 \xB7 ${String(H.current.step.number).padStart(2,"0")} ${H.current.step.label} \xB7 READY`)):Ye("F1 \xB7 reset przyj\u0119ty"),window.setTimeout(()=>window.dispatchEvent(new Event("stem:nvim-refresh")),120)}catch(w){Ye(`${v} \xB7 ${w instanceof Error?w.message:String(w)}`)}},[Ye]),ie=(0,S.useCallback)(async()=>{Ye("Ctrl+` \xB7 przeliczanie widoku\u2026");try{let v=await fetch("/api/nvim/redraw",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({actor:o.current})}),w=await v.json().catch(()=>({}));if(!v.ok)throw new Error(w.message??w.error??`HTTP ${v.status}`);let L=w.external_ui?.grid,H=L?` \xB7 ${L.width}\xD7${L.height}`:"";Ye(`Ctrl+\` \xB7 widok od\u015Bwie\u017Cony${H}`),window.dispatchEvent(new Event("stem:nvim-refresh"))}catch(v){Ye(`Ctrl+\` \xB7 ${v instanceof Error?v.message:String(v)}`)}},[Ye]),q=(0,S.useCallback)(async v=>{try{let w=await fetch("/api/viewer/state",{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({actor:o.current,viewer:v})});if(!w.ok)return;let L=await w.json();i.current=Math.max(i.current,Number(L.viewer?.revision??0))}catch{}},[]),wt=v=>{let w=Math.max(.25,Math.min(3,v));g(w),q({scale:w})};(0,S.useEffect)(()=>{document.documentElement.style.setProperty("--viewer-canvas-scale",h.toFixed(3))},[h]),(0,S.useEffect)(()=>{if(!$)return;let v=r.current;if(!v)return;let w=0,L=()=>{window.cancelAnimationFrame(w),w=window.requestAnimationFrame(()=>{v.scrollLeft=Math.max(0,(v.scrollWidth-v.clientWidth)/2)})},H=window.requestAnimationFrame(L);return window.addEventListener("resize",L),()=>{window.cancelAnimationFrame(H),window.cancelAnimationFrame(w),window.removeEventListener("resize",L)}},[$,h,at]),(0,S.useEffect)(()=>{let v=()=>zt(window.innerWidth);return window.addEventListener("resize",v),()=>window.removeEventListener("resize",v)},[]),(0,S.useEffect)(()=>{document.documentElement.dataset.nvimSyncMode=C?"on":"off"},[C]),(0,S.useEffect)(()=>{document.documentElement.dataset.cardClientId=o.current},[]);let fe=v=>{N(v),v||(_(!1),M(!1)),localStorage.setItem("stem-card-nvim-visible",String(v)),q({nvim:{visible:v,...v?{}:{expanded:!1,fit:!1}}})},Qt=v=>{let w=v&&C;f(w),localStorage.setItem("stem-card-nvim-control-mode",String(w)),q({nvim:{control:w}})},jn=v=>{p(v),localStorage.setItem("stem-card-nvim-sync-mode",String(v)),v||(f(!1),localStorage.setItem("stem-card-nvim-control-mode","false")),q({nvim:{sync:v,control:v?c:!1}})},qt=v=>{v&&fe(!0),v&&M(!1),_(v),q({nvim:{visible:v||E,expanded:v,fit:!1}})},go=v=>{v&&(fe(!0),_(!1)),M(v),q({nvim:{visible:v||E,fit:v,expanded:!1}})},ho=v=>{let w=t&&v;j(w),localStorage.setItem("stem-card-allocator-visible",String(w)),q({allocator:{visible:w}})},Un=v=>{let w=!!(e.has_spread&&v);ke(w),localStorage.setItem("stem-card-diagram-spread",String(w))};(0,S.useEffect)(()=>{let v=0,w=window.requestAnimationFrame(()=>{v=window.requestAnimationFrame(()=>{window.dispatchEvent(new Event("stem:nvim-refresh"))})});return()=>{window.cancelAnimationFrame(w),window.cancelAnimationFrame(v)}},[k,P]),(0,S.useEffect)(()=>{fetch("/api/progress").then(v=>v.ok?v.json():Promise.reject(new Error(`HTTP ${v.status}`))).then(v=>Le(v.progress)).catch(v=>Q(`Post\u0119p dzia\u0142a tylko przez serwer karty (${v.message}).`))},[]),(0,S.useEffect)(()=>{let v=!1,w=!1,L={scale:h,viewport:{width:window.innerWidth,height:window.innerHeight},nvim:{visible:E,sync:C,control:c,expanded:k,fit:P},allocator:{visible:R}},H=D=>{let Z=D.nvim.visible&&D.nvim.fit,V=D.nvim.visible&&!Z&&D.nvim.expanded;g(Math.max(.25,Math.min(3,D.scale))),N(D.nvim.visible),p(D.nvim.sync),f(D.nvim.sync&&D.nvim.control),_(V),M(Z);let oe=t&&(D.allocator?.visible??!0);j(oe),localStorage.setItem("stem-card-nvim-visible",String(D.nvim.visible)),localStorage.setItem("stem-card-nvim-sync-mode",String(D.nvim.sync)),localStorage.setItem("stem-card-nvim-control-mode",String(D.nvim.sync&&D.nvim.control)),localStorage.setItem("stem-card-allocator-visible",String(oe));let ct=++m.current;s.current=!0,window.cancelAnimationFrame(d.current),d.current=window.requestAnimationFrame(()=>{if(ct!==m.current)return;let Ue=Wi();Hi(Ue,{left:D.scroll.x,top:D.scroll.y});let ue=Array.from(document.querySelectorAll(".paper .sheet")),Re=ue[Math.max(0,Math.min(ue.length-1,D.scroll.page_index))]?.querySelector(".sheet-content");Re&&Number.isFinite(D.scroll.sheet_scroll_top)&&Hi(Re,{top:D.scroll.sheet_scroll_top??0}),d.current=window.requestAnimationFrame(()=>{ct===m.current&&(s.current=!1,u.current&&(u.current=!1,window.dispatchEvent(new Event("stem:viewer-scroll-settled"))))})})},K=async()=>{try{let D=await fetch("/api/control/state",{cache:"no-store"});if(!D.ok||v)return;let Z=await D.json(),V=Z.viewer;w||(w=!0,Number(V?.revision??0)===0&&q(L)),V&&V.revision>i.current&&(i.current=V.revision,V.actor!==o.current&&H(V));let oe=Z.navigation;oe&&oe.revision>l.current&&(l.current=oe.revision,oe.actor!==o.current&&(s.current&&(u.current=!0),window.dispatchEvent(new CustomEvent("stem:navigation-command",{detail:oe}))))}catch{}};K();let X=window.setInterval(K,300);return()=>{v=!0,window.clearInterval(X),m.current+=1,window.cancelAnimationFrame(d.current),s.current=!1,u.current=!1}},[]),(0,S.useEffect)(()=>{q({nvim:{connection:re.state,screen_cursor:re.screenCursor,grid:re.grid}})},[re.grid?.columns,re.grid?.rows,re.screenCursor?.column,re.screenCursor?.row,re.state,q]),(0,S.useEffect)(()=>{let v=0,w=null,L=Array.from(document.querySelectorAll(".paper .sheet")),H=L.map(X=>X.querySelector(".sheet-content")).filter(X=>!!X),K=X=>{if(s.current)return;let D=X?.currentTarget;D instanceof HTMLElement&&D.classList.contains("sheet-content")&&(w=D),window.clearTimeout(v),v=window.setTimeout(()=>{let Z=w?.closest(".sheet"),V=Z?L.indexOf(Z):-1,oe=V>=0?V:L.reduce((Ue,ue,J)=>{let Re=ue.getBoundingClientRect(),Ve=Math.abs((Re.top+Re.bottom)/2-window.innerHeight/2);return Ve=0?w:L[oe]?.querySelector(".sheet-content")??null;q({scroll:{x:window.scrollX,y:window.scrollY,page_index:oe,sheet_scroll_top:ct?.scrollTop??0},viewport:{width:window.innerWidth,height:window.innerHeight}}),w=null},120)};return window.addEventListener("scroll",K,{passive:!0}),window.addEventListener("resize",K),window.addEventListener("stem:viewer-scroll-settled",K),H.forEach(X=>X.addEventListener("scroll",K,{passive:!0})),K(),()=>{window.clearTimeout(v),window.removeEventListener("scroll",K),window.removeEventListener("resize",K),window.removeEventListener("stem:viewer-scroll-settled",K),H.forEach(X=>X.removeEventListener("scroll",K))}},[q]),(0,S.useEffect)(()=>{let v=w=>{if(w.repeat)return;let L=w.altKey&&!w.ctrlKey&&!w.metaKey?w.code:"";if(L==="Digit0"&&e.library){w.preventDefault(),w.stopImmediatePropagation(),F(!1),st(!Ee);return}if(L==="Digit1"){w.preventDefault(),w.stopImmediatePropagation(),fe(!E);return}if(L==="Digit2"){w.preventDefault(),w.stopImmediatePropagation(),jn(!C);return}if(L==="Digit3"){if(w.preventDefault(),w.stopImmediatePropagation(),!C)return;fe(!0),Qt(!c);return}if(L==="Digit4"){w.preventDefault(),w.stopImmediatePropagation(),qt(!k);return}if(L==="Digit5"){w.preventDefault(),w.stopImmediatePropagation(),go(!P);return}if(L==="Digit6"&&t){w.preventDefault(),w.stopImmediatePropagation(),ho(!R);return}if(L==="Digit7"&&e.has_spread){w.preventDefault(),w.stopImmediatePropagation(),Un(!$);return}if((w.key==="F1"||w.key==="F2")&&!w.altKey&&!w.ctrlKey&&!w.metaKey&&!w.shiftKey){w.preventDefault(),w.stopImmediatePropagation(),bn(w.key);return}if(w.ctrlKey&&!w.altKey&&!w.metaKey&&!w.shiftKey&&(w.code==="Backquote"||w.key==="`")){w.preventDefault(),w.stopImmediatePropagation(),ie();return}if(w.key==="F12"){w.preventDefault(),w.stopImmediatePropagation(),st(!1),Ge(null),me(null),xe(null),se(null),F(H=>!H);return}if(w.key==="Escape"&&(ne||A)){w.preventDefault(),w.stopImmediatePropagation(),Ge(null),me(null),xe(null),se(null),F(!1);return}};return window.addEventListener("keydown",v,!0),()=>window.removeEventListener("keydown",v,!0)},[R,$,t,Ee,e.has_spread,e.library,c,k,P,C,E,bn,ie,A,ne]),(0,S.useEffect)(()=>{let v=n.current?.querySelector(".topbar");if(!v)return;let w=()=>{document.documentElement.style.setProperty("--side-panel-top",`${Math.ceil(v.getBoundingClientRect().bottom)}px`)};w();let L=new ResizeObserver(w);return L.observe(v),window.addEventListener("resize",w),()=>{L.disconnect(),window.removeEventListener("resize",w),document.documentElement.style.removeProperty("--side-panel-top")}},[]),(0,S.useEffect)(()=>()=>window.clearTimeout(y.current),[]),(0,S.useEffect)(()=>{let v=w=>{!w.altKey||w.ctrlKey||(w.preventDefault(),g(L=>{let H=Math.max(.25,Math.min(3,L*Math.exp(-Math.max(-120,Math.min(120,w.deltaY))*.001)));return q({scale:H}),H}))};return window.addEventListener("wheel",v,{passive:!1}),()=>window.removeEventListener("wheel",v)},[q]);let ut=async(v,w)=>{try{let L=await fetch(`/api/progress/${encodeURIComponent(v)}`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({status:w})});if(!L.ok)throw new Error(await L.text());Le((await L.json()).progress),Q("")}catch(L){Q(`Nie zapisano post\u0119pu: ${L instanceof Error?L.message:String(L)}`)}},x=(v,w)=>{ut(v,{pending:"approved",approved:"pending"}[w])},b=0,T=e.sections.map(v=>{let w=b;return b+=v.assets.length,{section:v,figureOffset:w}}),O=[];return T.forEach(v=>{let w=v.section.spread_group??"",L=O[O.length-1];if(w&&L?.spreadGroup===w){L.pages.push(v);return}O.push({key:w||v.section.id,spreadGroup:w,spreadLabel:v.section.spread_label??"",spreadColumns:v.section.spread_columns??1,pages:[v]})}),(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{ref:n,className:`viewer-chrome${k?" is-nvim-expanded":""}${P?" is-nvim-fit":""}`,children:[(0,a.jsx)(xg,{toc:e.toc,libraryAvailable:!!e.library,libraryOpen:Ee,setLibraryOpen:st,scale:h,setScale:wt,nvimVisible:E,setNvimVisible:fe,nvimSyncMode:C,setNvimSyncMode:jn,nvimControlMode:c,setNvimControlMode:Qt,nvimExpanded:k,setNvimExpanded:qt,nvimFit:P,setNvimFit:go,allocatorVisible:R,setAllocatorVisible:ho,hasAllocatorModel:t,diagramSpread:$,setDiagramSpread:Un,hasDiagramSpread:!!e.has_spread,nvimSummary:re,progress:U,progressError:Ne}),e.viewpoints?.length?(0,a.jsx)(vg,{viewpoints:e.viewpoints}):null,(0,a.jsx)(wg,{visible:t&&R,model:e}),(0,a.jsx)(Mg,{visible:E,syncMode:C,controlMode:c,expanded:k,fit:P,scale:h,onSummaryChange:G}),B&&(0,a.jsx)("output",{className:"viewer-action-toast",children:B})]}),(0,a.jsxs)("main",{ref:r,className:`paper${$?" is-diagram-spread":""}`,children:[(0,a.jsx)(Rg,{front:e.front}),O.map(v=>{let w=Array.from({length:Math.ceil(v.pages.length/v.spreadColumns)},(D,Z)=>v.pages.slice(Z*v.spreadColumns,(Z+1)*v.spreadColumns)),H=Math.max(1,...w.map(D=>D.reduce((Z,V)=>Z+(V.section.page_orientation==="landscape"?297:210),Math.max(0,D.length-1)*2)))*(96/25.4)*h,K=Math.min(1,Math.max(.25,(at-24)/H)),X=v.pages.map(({section:D,figureOffset:Z})=>(0,a.jsx)(Kg,{section:D,progress:U,onCycle:x,onSetStatus:ut,figureOffset:Z},D.id));return v.spreadGroup?(0,a.jsx)("section",{className:"sheet-spread","data-spread-group":v.spreadGroup,"aria-label":v.spreadLabel||"Wielostronicowy diagram UML",style:{"--spread-columns":v.spreadColumns,"--spread-fit-scale":K},children:X},v.key):X[0]}),e.dictionary&&(0,a.jsxs)(hs,{header:e.dictionary.header_html,className:"dictionary-sheet",children:[(0,a.jsx)("h2",{children:"S\u0142ownik WE/EN/EK/KW"}),(0,a.jsx)(Yt,{html:e.dictionary.content_html})]})]}),(0,a.jsx)(gp,{side:"left",panels:[...e.library?[{id:"library",railLabel:"K",title:"Serie i karty",accent:"#167ca4",content:(0,a.jsx)(Yg,{library:e.library,onNavigate:()=>st(!1)})}]:[],{id:"contents",railLabel:"S",title:"Spis tre\u015Bci",accent:"#16805d",content:(0,a.jsx)(Qg,{toc:e.toc,onNavigate:()=>{Ge(null),me(null)}})}],activeId:et,pinnedId:je,onActiveChange:Ge,onPinnedChange:me}),(0,a.jsx)(gp,{side:"right",panels:[{id:"classroom",railLabel:"U",title:"Klasa i stanowiska",accent:"#267357",content:(0,a.jsx)(eh,{model:e})},{id:"progress",railLabel:"R",title:"Przebieg i raport",accent:"#b45b26",content:(0,a.jsx)(qg,{progress:U,error:Ne})}],activeId:Dn,pinnedId:wn,onActiveChange:xe,onPinnedChange:se}),(0,a.jsx)(th,{open:A,onClose:()=>F(!1)})]})}async function rh(){let e=(0,xp.createRoot)(document.getElementById("root"));try{let t=await fetch("./card-data.json",{cache:"no-store"});if(!t.ok)throw new Error(`Nie mo\u017Cna wczyta\u0107 card-data.json: ${t.status}`);let n=await t.json();e.render((0,a.jsx)(nh,{model:n}))}catch(t){e.render((0,a.jsxs)("main",{className:"app-load-error",children:[(0,a.jsx)("h1",{children:"Nie uda\u0142o si\u0119 uruchomi\u0107 karty"}),(0,a.jsx)("pre",{children:t instanceof Error?t.message:String(t)})]}))}}rh(); +/*! Bundled license information: + +react/cjs/react.production.min.js: + (** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +scheduler/cjs/scheduler.production.min.js: + (** + * @license React + * scheduler.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +react-dom/cjs/react-dom.production.min.js: + (** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +react/cjs/react-jsx-runtime.production.min.js: + (** + * @license React + * react-jsx-runtime.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) +*/ diff --git a/web/card-data.json b/web/card-data.json new file mode 100644 index 0000000..e107006 --- /dev/null +++ b/web/card-data.json @@ -0,0 +1,4221 @@ +{ + "schema": "esc-card-react-view.v1", + "card": { + "id": "mpabi-freertos-c-09-event-groups", + "series": "freertos-c", + "series_title": "FreeRTOS C", + "number": "09", + "count": "15", + "slug": "event-groups", + "title": "Event Groups: ALL-ready i bariera", + "topic": "bit masks, wait semantics, automatic clear i xEventGroupSync", + "project": "Freestanding C nad FreeRTOS", + "subject": "Informatyka", + "level": "Rok 2 · L09 · RV32I/Hazard3", + "revision_date": "2026-07-19T00:00:00+02:00", + "status": "Gotowa", + "version": "v00.01", + "uuid": "e5c4c0f3-3dd0-5168-9945-14d48744ec6f", + "author": "M. Pabiszczak", + "year": "2026" + }, + "library": { + "schema": "stem-card-library.v2", + "workspace": "edu", + "subjects": [ + { + "id": "inf", + "title": "INF · Informatyka", + "current": true, + "levels": [ + { + "id": "year-1", + "title": "Rok 1", + "current": false, + "series": [ + { + "id": "console-bash", + "title": "Bash · Console Tools", + "current": false, + "catalog_only": true, + "role": "", + "status": "", + "cards": [ + { + "id": "L01", + "title": "BusyBox — the Swiss Army Knife", + "repo": "lab-console-busybox", + "href": "", + "available": false, + "current": false, + "number": "01", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L02", + "title": "Tmux — sessions, windows and panes", + "repo": "lab-console-tmux", + "href": "", + "available": false, + "current": false, + "number": "02", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L03", + "title": "Neovim — navigation and editing", + "repo": "lab-console-neovim", + "href": "", + "available": false, + "current": false, + "number": "03", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L04", + "title": "Git Basics I — status, add and commit", + "repo": "lab-console-git-basics-1", + "href": "", + "available": false, + "current": false, + "number": "04", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L05", + "title": "Git Basics II — log, diff and restore", + "repo": "lab-console-git-basics-2", + "href": "", + "available": false, + "current": false, + "number": "05", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L06", + "title": "Git Branching I — branch, switch and merge", + "repo": "lab-console-git-branching-1", + "href": "", + "available": false, + "current": false, + "number": "06", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L07", + "title": "Git Branching II — rebase and conflicts", + "repo": "lab-console-git-branching-2", + "href": "", + "available": false, + "current": false, + "number": "07", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L08", + "title": "Docker Fundamentals", + "repo": "lab-console-docker", + "href": "", + "available": false, + "current": false, + "number": "08", + "version": "", + "status": "planned", + "tasks": [] + } + ] + }, + { + "id": "rv32i-asm", + "title": "ASM · RV32I From Blinker to RISC-V", + "current": false, + "catalog_only": true, + "role": "", + "status": "", + "cards": [ + { + "id": "L01", + "title": "Number Systems and Storage", + "repo": "lab-rv32i-asm-number-systems", + "href": "", + "available": false, + "current": false, + "number": "01", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L02", + "title": "FPGA Bring-up and Programming", + "repo": "lab-rv32i-asm-fpga-bringup", + "href": "", + "available": false, + "current": false, + "number": "02", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L03", + "title": "Blinker and Synchronous Logic", + "repo": "lab-rv32i-asm-blinker", + "href": "", + "available": false, + "current": false, + "number": "03", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L04", + "title": "The RISC-V ISA and Instruction Decoder", + "repo": "lab-rv32i-asm-decoder", + "href": "", + "available": false, + "current": false, + "number": "04", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L05", + "title": "ALU and the Verilog Assembler", + "repo": "lab-rv32i-asm-alu", + "href": "", + "available": false, + "current": false, + "number": "05", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L06", + "title": "Control Flow — Jumps and Branches", + "repo": "lab-rv32i-asm-control-flow", + "href": "", + "available": false, + "current": false, + "number": "06", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L07", + "title": "Addresses and Memory", + "repo": "lab-rv32i-asm-addresses-memory", + "href": "", + "available": false, + "current": false, + "number": "07", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L08", + "title": "Subroutines and the RISC-V ABI", + "repo": "lab-rv32i-asm-subroutines-abi", + "href": "", + "available": false, + "current": false, + "number": "08", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "L09", + "title": "Load and Store", + "repo": "lab-rv32i-asm-load-store", + "href": "", + "available": false, + "current": false, + "number": "09", + "version": "", + "status": "planned", + "tasks": [] + } + ] + }, + { + "id": "c", + "title": "C · Freestanding RV32I and K&R", + "current": false, + "catalog_only": true, + "role": "", + "status": "", + "cards": [ + { + "id": "C01", + "title": "ASM, C and GCC", + "repo": "lab-rv32i-strlen-asm-c-gcc", + "href": "", + "available": false, + "current": false, + "number": "01", + "version": "", + "status": "existing", + "tasks": [] + }, + { + "id": "C02", + "title": ".bss, .data and static storage", + "repo": "lab-rv32i-strlen-bss-data-stack", + "href": "", + "available": false, + "current": false, + "number": "02", + "version": "", + "status": "existing", + "tasks": [] + }, + { + "id": "C03", + "title": "ABI and Stack Frame", + "repo": "lab-rv32i-strlen-abi-stack", + "href": "", + "available": false, + "current": false, + "number": "03", + "version": "", + "status": "existing", + "tasks": [] + }, + { + "id": "C04", + "title": "Types, Operators and Expressions", + "repo": "lab-rv32i-c-types-operators-expressions", + "href": "", + "available": false, + "current": false, + "number": "04", + "version": "", + "status": "existing", + "tasks": [] + }, + { + "id": "C05", + "title": "Control Flow", + "repo": "lab-rv32i-c-control-flow", + "href": "", + "available": false, + "current": false, + "number": "05", + "version": "", + "status": "existing", + "tasks": [] + }, + { + "id": "C06", + "title": "Functions and Program Structure", + "repo": "lab-rv32i-c-functions-program-structure", + "href": "", + "available": false, + "current": false, + "number": "06", + "version": "", + "status": "existing", + "tasks": [] + }, + { + "id": "C07", + "title": "Pointers, Arrays and Linear Allocator", + "repo": "lab-rv32i-c-pointers-arrays", + "href": "https://4abec2d9-79e9-0dca-9040-c60669f51ff8/98dbe323-c624-53d6-9fe2-4ee688c44b38", + "available": true, + "current": false, + "number": "07", + "version": "v00.01", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "task01" + }, + { + "id": "task02", + "title": "task02" + }, + { + "id": "task03", + "title": "task03" + }, + { + "id": "task04", + "title": "K&R 5.4 -- Linear Memory Allocator" + }, + { + "id": "task05", + "title": "task05" + }, + { + "id": "task06", + "title": "task06" + }, + { + "id": "task07", + "title": "task07" + }, + { + "id": "task08", + "title": "task08" + }, + { + "id": "task09", + "title": "task09" + }, + { + "id": "task10", + "title": "task10" + }, + { + "id": "task11", + "title": "task11" + }, + { + "id": "task12", + "title": "task12" + } + ] + }, + { + "id": "C08", + "title": "Structures and Linked State", + "repo": "lab-rv32i-c-structures", + "href": "", + "available": false, + "current": false, + "number": "08", + "version": "", + "status": "existing", + "tasks": [] + }, + { + "id": "C09", + "title": "smalloc, sbrk and minimal libc", + "repo": "lab-rv32i-c-smalloc-sbrk-libc", + "href": "", + "available": false, + "current": false, + "number": "09", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "C10", + "title": "RV32I Traps and Interrupt Control", + "repo": "lab-rv32i-c-interrupts", + "href": "", + "available": false, + "current": false, + "number": "10", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "C11", + "title": "Machine Timer and Periodic Work", + "repo": "lab-rv32i-c-timer", + "href": "", + "available": false, + "current": false, + "number": "11", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "C12", + "title": "UART — polling and interrupts", + "repo": "lab-rv32i-c-uart", + "href": "", + "available": false, + "current": false, + "number": "12", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "C13", + "title": "GPIO, Edges and Debounce", + "repo": "lab-rv32i-c-gpio", + "href": "", + "available": false, + "current": false, + "number": "13", + "version": "", + "status": "planned", + "tasks": [] + } + ] + } + ] + }, + { + "id": "year-2", + "title": "Rok 2", + "current": true, + "series": [ + { + "id": "cpp", + "title": "C++", + "current": false, + "catalog_only": true, + "role": "", + "status": "", + "cards": [ + { + "id": "CPP01", + "title": "Translation Units, Linkage and Namespaces", + "repo": "lab-cpp-build-namespaces", + "href": "", + "available": false, + "current": false, + "number": "01", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP02", + "title": "Classes, Encapsulation and Invariants", + "repo": "lab-cpp-classes-invariants", + "href": "", + "available": false, + "current": false, + "number": "02", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP03", + "title": "Construction, Destruction and RAII", + "repo": "lab-cpp-lifetime-raii", + "href": "", + "available": false, + "current": false, + "number": "03", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP04", + "title": "References, const and Value Categories", + "repo": "lab-cpp-references-const", + "href": "", + "available": false, + "current": false, + "number": "04", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP05", + "title": "Copy, Move and the Rule of Zero/Five", + "repo": "lab-cpp-copy-move", + "href": "", + "available": false, + "current": false, + "number": "05", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP06", + "title": "Ownership and Resource Types", + "repo": "lab-cpp-ownership", + "href": "", + "available": false, + "current": false, + "number": "06", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP07", + "title": "Function and Class Templates", + "repo": "lab-cpp-templates", + "href": "", + "available": false, + "current": false, + "number": "07", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP08", + "title": "Static Polymorphism and CRTP", + "repo": "lab-cpp-static-polymorphism", + "href": "", + "available": false, + "current": false, + "number": "08", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP09", + "title": "Inheritance, Interfaces and Dynamic Dispatch", + "repo": "lab-cpp-dynamic-polymorphism", + "href": "", + "available": false, + "current": false, + "number": "09", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP10", + "title": "Operator Overloading and Strong Types", + "repo": "lab-cpp-strong-types", + "href": "", + "available": false, + "current": false, + "number": "10", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP11", + "title": "Containers, Iterators and Algorithms", + "repo": "lab-cpp-containers-iterators", + "href": "", + "available": false, + "current": false, + "number": "11", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP12", + "title": "Lambdas, Function Objects and Callables", + "repo": "lab-cpp-lambdas-callables", + "href": "", + "available": false, + "current": false, + "number": "12", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP13", + "title": "Error Models with and without Exceptions", + "repo": "lab-cpp-error-models", + "href": "", + "available": false, + "current": false, + "number": "13", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP14", + "title": "Object Layout, ABI and Binary Evidence", + "repo": "lab-cpp-abi-evidence", + "href": "", + "available": false, + "current": false, + "number": "14", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "CPP15", + "title": "Resource-type Integration Project", + "repo": "lab-cpp-resource-integration", + "href": "", + "available": false, + "current": false, + "number": "15", + "version": "", + "status": "planned", + "tasks": [] + } + ] + }, + { + "id": "freertos-c", + "title": "FreeRTOS C", + "current": true, + "catalog_only": true, + "role": "", + "status": "", + "cards": [ + { + "id": "FC01", + "title": "heap_1–heap_5 and heap_4 mechanics", + "repo": "lab-rv32i-freertos-heap4", + "href": "", + "available": false, + "current": false, + "number": "01", + "version": "", + "status": "existing", + "tasks": [] + }, + { + "id": "FC02", + "title": "First Task, TCB and Task Stack", + "repo": "lab-rv32i-freertos-c-first-task", + "href": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/a2538a1a-3c37-4c14-a6ea-db5aba8fd038", + "available": true, + "current": false, + "number": "02", + "version": "v00.01", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "Pierwszy task FreeRTOS — callback, context, TCB i stos" + } + ] + }, + { + "id": "FC03", + "title": "Tick, Scheduler, Priorities and Time Slicing", + "repo": "lab-rv32i-freertos-c-scheduler", + "href": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/2214a13f-34c6-40a1-a561-70a00ec98285", + "available": true, + "current": false, + "number": "03", + "version": "v00.01", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "Kontrolowany eksperyment schedulera FreeRTOS" + } + ] + }, + { + "id": "FC04", + "title": "Delay, Blocking and Task States", + "repo": "lab-rv32i-freertos-c-delay-state", + "href": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/4a7ef04c-ab08-5783-9192-5b02a90f05f8", + "available": true, + "current": false, + "number": "04", + "version": "v00.01", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "Delay, blokowanie, zawieszenie i self-delete" + } + ] + }, + { + "id": "FC05", + "title": "Queues and Byte-copy Semantics", + "repo": "lab-rv32i-freertos-c-queue", + "href": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/8ee8f36a-471b-51bc-97b8-e204bd541c61", + "available": true, + "current": false, + "number": "05", + "version": "v00.01", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "Kolejka długości 1: empty, full, copy i FIFO" + } + ] + }, + { + "id": "FC06", + "title": "Software Timers and the Daemon Task", + "repo": "lab-rv32i-freertos-c-software-timers", + "href": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/ab188300-f03d-5ddb-8316-b9534876a5bc", + "available": true, + "current": false, + "number": "06", + "version": "v00.01", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "One-shot, auto-reload i timer daemon" + } + ] + }, + { + "id": "FC07", + "title": "Interrupt-safe API and Semaphore Handoff", + "repo": "lab-rv32i-freertos-c-isr-semaphore", + "href": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/a6da1075-8d1f-5615-b0e6-0fc33e59e1e3", + "available": true, + "current": false, + "number": "07", + "version": "v00.01", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "External IRQ → FromISR semaphore → high-priority task" + } + ] + }, + { + "id": "FC08", + "title": "Critical Sections, Mutexes and Priority Inheritance", + "repo": "lab-rv32i-freertos-c-resource-management", + "href": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/2392f81a-ed6c-510b-86b5-06c5e2f1c6aa", + "available": true, + "current": false, + "number": "08", + "version": "v00.01", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "Low/Medium/High: ownership i dziedziczenie priorytetu" + } + ] + }, + { + "id": "FC09", + "title": "Event Groups and Multi-event Synchronization", + "repo": "lab-rv32i-freertos-c-event-groups", + "href": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f", + "available": true, + "current": true, + "number": "09", + "version": "v00.01", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "ALL-ready i trzyosobowa bariera" + } + ] + }, + { + "id": "FC10", + "title": "Direct-to-task Notifications", + "repo": "lab-rv32i-freertos-c-notifications", + "href": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/3b57334e-2c35-5207-a51d-9321906db078", + "available": true, + "current": false, + "number": "10", + "version": "v00.01", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "Count, overwrite i bit flags na jednym slocie" + } + ] + }, + { + "id": "FC11", + "title": "Static Allocation and Bounded Storage", + "repo": "lab-rv32i-freertos-c-static-allocation", + "href": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/f4d7caf3-d665-5a2d-b9eb-39a120a64ff7", + "available": true, + "current": false, + "number": "11", + "version": "v00.01", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "Same behavior, two storage policies" + } + ] + }, + { + "id": "FC12", + "title": "Assertions, Hooks, Stack Checks and Runtime Diagnostics", + "repo": "lab-rv32i-freertos-c-diagnostics", + "href": "https://33c1da5c-dd20-5ed1-a829-64a5306fc804/f4d7caf3-d665-5a2d-b9eb-39a120a64ff7", + "available": true, + "current": false, + "number": "12", + "version": "v00.01", + "status": "planned", + "tasks": [ + { + "id": "task01", + "title": "Same behavior, two storage policies" + } + ] + }, + { + "id": "FC13", + "title": "UART Interrupt-to-task Pipeline", + "repo": "lab-rv32i-freertos-c-uart", + "href": "", + "available": false, + "current": false, + "number": "13", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "FC14", + "title": "GPIO, Hardware Timer and Deferred Work", + "repo": "lab-rv32i-freertos-c-gpio-timer", + "href": "", + "available": false, + "current": false, + "number": "14", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "FC15", + "title": "Integrated Deterministic FreeRTOS C Application", + "repo": "lab-rv32i-freertos-c-integration", + "href": "", + "available": false, + "current": false, + "number": "15", + "version": "", + "status": "planned", + "tasks": [] + } + ] + }, + { + "id": "freertos-cpp", + "title": "FreeRTOS C++", + "current": false, + "catalog_only": true, + "role": "", + "status": "", + "cards": [ + { + "id": "K02", + "title": "First Task, Stack, Tick, Scheduler and Vector V0", + "repo": "lab-rv32i-freertos-task-stack-vector", + "href": "https://3400a752-f4d6-579c-a033-73677fef4b74/948ce9fd-3d0c-5721-a0ae-ef422008fec8", + "available": true, + "current": false, + "number": "02", + "version": "v00.04", + "status": "existing", + "tasks": [ + { + "id": "task01", + "title": "C++ Task wrapper — OOP i debugowanie cyklu życia" + } + ] + }, + { + "id": "K03", + "title": "Vector V1 — Destructor, Move, Ownership and RAII", + "repo": "lab-rv32i-freertos-vector-raii", + "href": "", + "available": false, + "current": false, + "number": "03", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K04", + "title": "Scheduler States, Priorities and Typed Ticks", + "repo": "lab-rv32i-freertos-scheduler-states", + "href": "", + "available": false, + "current": false, + "number": "04", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K05", + "title": "C++ Heap Bridge, Heap Models and HeapStats", + "repo": "lab-rv32i-freertos-heap-models", + "href": "", + "available": false, + "current": false, + "number": "05", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K06", + "title": "MemoryResource and FreeRtosAllocator", + "repo": "lab-rv32i-freertos-allocator-resource", + "href": "", + "available": false, + "current": false, + "number": "06", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K07", + "title": "TaskHandle_t, Explicit Start and Static Trampoline", + "repo": "lab-rv32i-freertos-task-wrapper", + "href": "", + "available": false, + "current": false, + "number": "07", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K08", + "title": "DynamicTask, StaticTask, Deletion and Lifetime", + "repo": "lab-rv32i-freertos-static-task", + "href": "", + "available": false, + "current": false, + "number": "08", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K09", + "title": "Scheduler Facade and Scoped Kernel Guards", + "repo": "lab-rv32i-freertos-kernel-facade", + "href": "", + "available": false, + "current": false, + "number": "09", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K10", + "title": "Queue and StaticQueue", + "repo": "lab-rv32i-freertos-queue", + "href": "", + "available": false, + "current": false, + "number": "10", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K11", + "title": "Mutex, LockGuard and Priority Inheritance", + "repo": "lab-rv32i-freertos-mutex", + "href": "", + "available": false, + "current": false, + "number": "11", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K12", + "title": "Semaphores and Task Notifications", + "repo": "lab-rv32i-freertos-semaphore-notify", + "href": "", + "available": false, + "current": false, + "number": "12", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K13", + "title": "EventGroup and Coordinated State", + "repo": "lab-rv32i-freertos-event-group", + "href": "", + "available": false, + "current": false, + "number": "13", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K14", + "title": "Software Timer and the Timer Daemon Task", + "repo": "lab-rv32i-freertos-sw-timer", + "href": "", + "available": false, + "current": false, + "number": "14", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K15", + "title": "Explicit FromISR, UART and GPIO Wrappers", + "repo": "lab-rv32i-freertos-isr-drivers", + "href": "", + "available": false, + "current": false, + "number": "15", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "K16", + "title": "Integrated Real-time Application and Evidence", + "repo": "lab-rv32i-freertos-integration", + "href": "", + "available": false, + "current": false, + "number": "16", + "version": "", + "status": "planned", + "tasks": [] + } + ] + }, + { + "id": "posix-threads", + "title": "POSIX Threads", + "current": false, + "catalog_only": true, + "role": "", + "status": "", + "cards": [ + { + "id": "PT01", + "title": "Process and Thread Execution Context", + "repo": "lab-posix-process-thread", + "href": "", + "available": false, + "current": false, + "number": "01", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT02", + "title": "pthread_create, pthread_join and Lifecycle", + "repo": "lab-posix-pthread-create-join", + "href": "", + "available": false, + "current": false, + "number": "02", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT03", + "title": "Arguments, Return Values and Ownership", + "repo": "lab-posix-thread-arguments", + "href": "", + "available": false, + "current": false, + "number": "03", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT04", + "title": "Data Races and the Happens-before Question", + "repo": "lab-posix-data-races", + "href": "", + "available": false, + "current": false, + "number": "04", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT05", + "title": "Mutex, Critical Section and Deadlock", + "repo": "lab-posix-mutex", + "href": "", + "available": false, + "current": false, + "number": "05", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT06", + "title": "Condition Variables and Predicates", + "repo": "lab-posix-condition-variable", + "href": "", + "available": false, + "current": false, + "number": "06", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT07", + "title": "Bounded Producer–Consumer Queue", + "repo": "lab-posix-producer-consumer", + "href": "", + "available": false, + "current": false, + "number": "07", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT08", + "title": "POSIX Semaphores", + "repo": "lab-posix-semaphore", + "href": "", + "available": false, + "current": false, + "number": "08", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT09", + "title": "Reader–Writer Lock", + "repo": "lab-posix-rwlock", + "href": "", + "available": false, + "current": false, + "number": "09", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT10", + "title": "C Atomics and Memory Ordering", + "repo": "lab-posix-atomics", + "href": "", + "available": false, + "current": false, + "number": "10", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT11", + "title": "Cancellation, Cleanup and Safe Shutdown", + "repo": "lab-posix-cancellation-cleanup", + "href": "", + "available": false, + "current": false, + "number": "11", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT12", + "title": "Thread-local Storage", + "repo": "lab-posix-thread-local", + "href": "", + "available": false, + "current": false, + "number": "12", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT13", + "title": "Scheduling Policy, Priority and Affinity", + "repo": "lab-posix-scheduling", + "href": "", + "available": false, + "current": false, + "number": "13", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT14", + "title": "GDB, Sanitizers and Race Evidence", + "repo": "lab-posix-thread-debugging", + "href": "", + "available": false, + "current": false, + "number": "14", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "PT15", + "title": "POSIX Threads vs FreeRTOS Integration", + "repo": "lab-posix-freertos-comparison", + "href": "", + "available": false, + "current": false, + "number": "15", + "version": "", + "status": "planned", + "tasks": [] + } + ] + }, + { + "id": "oop", + "title": "OOP and Design Patterns", + "current": false, + "catalog_only": true, + "role": "reference", + "status": "", + "cards": [ + { + "id": "OOP01", + "title": "Strategy — Replaceable Behaviour", + "repo": "lab-cpp-oop-strategy", + "href": "", + "available": false, + "current": false, + "number": "01", + "version": "", + "status": "design", + "tasks": [] + }, + { + "id": "OOP02", + "title": "Adapter — Stable Boundary", + "repo": "lab-cpp-oop-adapter", + "href": "", + "available": false, + "current": false, + "number": "02", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP03", + "title": "Template Method — Fixed Skeleton", + "repo": "lab-cpp-oop-template-method", + "href": "", + "available": false, + "current": false, + "number": "03", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP04", + "title": "Static vs Dynamic Dispatch", + "repo": "lab-cpp-oop-dispatch", + "href": "", + "available": false, + "current": false, + "number": "04", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP05", + "title": "RAII and Ownership", + "repo": "lab-cpp-oop-raii", + "href": "", + "available": false, + "current": false, + "number": "05", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP06", + "title": "State — Explicit Behaviour", + "repo": "lab-cpp-oop-state", + "href": "", + "available": false, + "current": false, + "number": "06", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP07", + "title": "Facade — Subsystem Boundary", + "repo": "lab-cpp-oop-facade", + "href": "", + "available": false, + "current": false, + "number": "07", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP08", + "title": "Command — Request as Object", + "repo": "lab-cpp-oop-command", + "href": "", + "available": false, + "current": false, + "number": "08", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP09", + "title": "Observer — Subscription and Telemetry", + "repo": "lab-cpp-oop-observer", + "href": "", + "available": false, + "current": false, + "number": "09", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP10", + "title": "Factory Method — Resource Policy", + "repo": "lab-cpp-oop-factory-method", + "href": "", + "available": false, + "current": false, + "number": "10", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP11", + "title": "Builder — Valid Configuration", + "repo": "lab-cpp-oop-builder", + "href": "", + "available": false, + "current": false, + "number": "11", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP12", + "title": "Decorator — Measurement and Diagnostics", + "repo": "lab-cpp-oop-decorator", + "href": "", + "available": false, + "current": false, + "number": "12", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP13", + "title": "Composite — Component Tree", + "repo": "lab-cpp-oop-composite", + "href": "", + "available": false, + "current": false, + "number": "13", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP14", + "title": "Chain of Responsibility", + "repo": "lab-cpp-oop-chain", + "href": "", + "available": false, + "current": false, + "number": "14", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP15", + "title": "Pattern-language Integration", + "repo": "lab-cpp-oop-integration", + "href": "", + "available": false, + "current": false, + "number": "15", + "version": "", + "status": "planned", + "tasks": [] + } + ] + } + ] + }, + { + "id": "year-3", + "title": "Rok 3 · plan", + "current": false, + "series": [ + { + "id": "modern-cpp-stl", + "title": "Modern C++ / STL", + "current": false, + "catalog_only": true, + "role": "", + "status": "scope-reserved", + "cards": [] + }, + { + "id": "std-threads", + "title": "std::thread / std::jthread", + "current": false, + "catalog_only": true, + "role": "", + "status": "scope-reserved", + "cards": [] + }, + { + "id": "boost-asio", + "title": "Boost.Asio", + "current": false, + "catalog_only": true, + "role": "", + "status": "scope-reserved", + "cards": [] + }, + { + "id": "oop", + "title": "OOP and Design Patterns", + "current": false, + "catalog_only": true, + "role": "reference", + "status": "", + "cards": [ + { + "id": "OOP01", + "title": "Strategy — Replaceable Behaviour", + "repo": "lab-cpp-oop-strategy", + "href": "", + "available": false, + "current": false, + "number": "01", + "version": "", + "status": "design", + "tasks": [] + }, + { + "id": "OOP02", + "title": "Adapter — Stable Boundary", + "repo": "lab-cpp-oop-adapter", + "href": "", + "available": false, + "current": false, + "number": "02", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP03", + "title": "Template Method — Fixed Skeleton", + "repo": "lab-cpp-oop-template-method", + "href": "", + "available": false, + "current": false, + "number": "03", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP04", + "title": "Static vs Dynamic Dispatch", + "repo": "lab-cpp-oop-dispatch", + "href": "", + "available": false, + "current": false, + "number": "04", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP05", + "title": "RAII and Ownership", + "repo": "lab-cpp-oop-raii", + "href": "", + "available": false, + "current": false, + "number": "05", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP06", + "title": "State — Explicit Behaviour", + "repo": "lab-cpp-oop-state", + "href": "", + "available": false, + "current": false, + "number": "06", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP07", + "title": "Facade — Subsystem Boundary", + "repo": "lab-cpp-oop-facade", + "href": "", + "available": false, + "current": false, + "number": "07", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP08", + "title": "Command — Request as Object", + "repo": "lab-cpp-oop-command", + "href": "", + "available": false, + "current": false, + "number": "08", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP09", + "title": "Observer — Subscription and Telemetry", + "repo": "lab-cpp-oop-observer", + "href": "", + "available": false, + "current": false, + "number": "09", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP10", + "title": "Factory Method — Resource Policy", + "repo": "lab-cpp-oop-factory-method", + "href": "", + "available": false, + "current": false, + "number": "10", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP11", + "title": "Builder — Valid Configuration", + "repo": "lab-cpp-oop-builder", + "href": "", + "available": false, + "current": false, + "number": "11", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP12", + "title": "Decorator — Measurement and Diagnostics", + "repo": "lab-cpp-oop-decorator", + "href": "", + "available": false, + "current": false, + "number": "12", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP13", + "title": "Composite — Component Tree", + "repo": "lab-cpp-oop-composite", + "href": "", + "available": false, + "current": false, + "number": "13", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP14", + "title": "Chain of Responsibility", + "repo": "lab-cpp-oop-chain", + "href": "", + "available": false, + "current": false, + "number": "14", + "version": "", + "status": "planned", + "tasks": [] + }, + { + "id": "OOP15", + "title": "Pattern-language Integration", + "repo": "lab-cpp-oop-integration", + "href": "", + "available": false, + "current": false, + "number": "15", + "version": "", + "status": "planned", + "tasks": [] + } + ] + } + ] + }, + { + "id": "year-4", + "title": "Rok 4 · plan", + "current": false, + "series": [ + { + "id": "transformers-gpu", + "title": "Transformers on GPU", + "current": false, + "catalog_only": true, + "role": "", + "status": "scope-reserved", + "cards": [] + } + ] + } + ] + }, + { + "id": "fiz", + "title": "FIZ · Fizyka", + "current": false, + "levels": [ + { + "id": "all", + "title": "Katalog", + "current": false, + "series": [ + { + "id": "fiz", + "title": "Fizyka", + "current": false, + "catalog_only": false, + "role": "", + "status": "", + "cards": [ + { + "id": "ruch-1d", + "title": "Ruch 1D", + "repo": "ruch-1d", + "href": "", + "available": false, + "current": false, + "number": "", + "version": "", + "status": "", + "tasks": [] + }, + { + "id": "ruch-2d", + "title": "Ruch 2D", + "repo": "ruch-2d", + "href": "", + "available": false, + "current": false, + "number": "", + "version": "", + "status": "", + "tasks": [] + }, + { + "id": "ruch-po-okregu", + "title": "Ruch po okręgu", + "repo": "ruch-po-okregu", + "href": "", + "available": false, + "current": false, + "number": "", + "version": "", + "status": "", + "tasks": [] + } + ] + } + ] + } + ] + } + ] + }, + "total_pages": 8, + "front": { + "header_html": "
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TITLEEvent Groups: ALL-ready i bariera
VER.v00.01
DATETIME2026-07-19T00:00:00+02:00
PROJECTFreestanding C nad FreeRTOS
SERIESFreeRTOS C
CARD09/15
SHEET1/8
SUBJ.Inf.
PROG.
CORE
SCOPE
LEVEL
POS.
GITEA UUIDe5c4c0f3-3dd0-5168-9945-14d48744ec6fCARD UUID3f4c1b8b-d54b-58d5-b92d-87a7233cbd9fAUTHOR  M. Pabiszczak
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
", + "left_margin_html": "
TECH
", + "right_margin_html": "
OG
", + "goal_title": "Cel karty", + "goal_html": "

Uczeń projektuje rozłączne maski EventGroup i dowodzi różnicy między oczekiwaniem ALL a trzyosobową barierą.

", + "scope_title": "Zakres karty", + "scope_html": "

Koordynator p3 blokuje się na READY_ALL. Worker A ustawia READY_A i blokuje się w xEventGroupSync. Worker B potwierdza maskę częściową, ustawia READY_B, po czym koordynator wnosi SYNC_C. Ostatni SYNC_B zwalnia wszystkich; każdy wynik zawiera 0x70, a automatycznie wyczyszczona grupa kończy z 0.

", + "scope_columns": [ + "chapter", + "task", + "idea", + "priority", + "status", + "version" + ], + "scope_headers": [ + "Lekcja", + "Task", + "Najważniejsza idea", + "Priorytet", + "Status", + "Version" + ], + "scope_rows": [ + { + "chapter": "FC09", + "task": "Task01", + "idea_html": "

bit masks, wait semantics, automatic clear i xEventGroupSync

", + "priority": "główny", + "status": "ready", + "status_label": "opracowane", + "version": "v00.01", + "key": true + } + ] + }, + "sections": [ + { + "id": "section-1-content", + "index": 1, + "title": "A2 — Structure", + "show_heading": false, + "toc_entry": true, + "header_html": "
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TITLEEvent Groups: ALL-ready i bariera
VER.v00.01
DATETIME2026-07-19T00:00:00+02:00
PROJECTFreestanding C nad FreeRTOS
SERIESFreeRTOS C
CARD09/15
SHEET2/8
SUBJ.Inf.
PROG.
CORE
SCOPE
LEVEL
POS.
GITEA UUIDe5c4c0f3-3dd0-5168-9945-14d48744ec6fCARD UUID3f4c1b8b-d54b-58d5-b92d-87a7233cbd9fAUTHOR  M. Pabiszczak
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
", + "left_margin_html": "
TECH
-
", + "right_margin_html": "
OG
-
", + "content_html": "", + "tasks": [], + "assets": [ + { + "label": "fig:a2-structure", + "logical_label": "fig:a2-structure", + "figure_number": 1, + "caption": "A2 STRUCTURE — READY and SYNC masks. — część 1/1 (r1 c1)", + "alt": "Struktura EventGroup.", + "href": "figures/a2-structure.svg", + "source_href": "figures/a2-structure.puml", + "width": 1.0, + "kind": "diagram", + "interactive": { + "kind": "uml-class", + "storage_key": "fc09-a2-structure-tile-1", + "title": "A2 · masks and participants", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a2", + "label": "A2 STRUCTURE", + "description": "One group, two disjoint protocols and three participants." + }, + "phases": [ + { + "id": "structure", + "label": "GROUP / MASKS / PARTICIPANTS", + "steps": [ + { + "id": "group", + "number": 1, + "label": "one real EventGroup", + "mode": "CODE", + "strategy_ref": "code.protocol", + "svg_label": "01", + "code_ref": "src/tasks/task01_event_groups.c:261", + "description": "Kernel object holds bits and wait lists.", + "evidence": "xEventGroupCreate symbol.", + "strategy": { + "id": "task01/a2/structure/group", + "kind": "code", + "action": "open", + "expected_observations": [ + "disjoint low-bit masks", + "finite waits" + ], + "assertions": [ + "READY_ALL=0x03", + "SYNC_ALL=0x70" + ], + "evidence_fields": [ + "mask", + "API", + "code_ref" + ], + "prerequisites": [ + "źródło FC09" + ], + "layout": [ + "mask table", + "EventGroupExperiment", + "kernel config" + ], + "commands": [ + "rg -n 'READY_|SYNC_|xEventGroup' include src" + ], + "template_id": "code.protocol" + } + }, + { + "id": "ready", + "number": 2, + "label": "READY bits are disjoint", + "mode": "CODE", + "strategy_ref": "code.protocol", + "svg_label": "02", + "code_ref": "include/task01_event_groups.h:11", + "description": "A and B own one bit each.", + "evidence": "ALL=0x03.", + "strategy": { + "id": "task01/a2/structure/ready", + "kind": "code", + "action": "open", + "expected_observations": [ + "disjoint low-bit masks", + "finite waits" + ], + "assertions": [ + "READY_ALL=0x03", + "SYNC_ALL=0x70" + ], + "evidence_fields": [ + "mask", + "API", + "code_ref" + ], + "prerequisites": [ + "źródło FC09" + ], + "layout": [ + "mask table", + "EventGroupExperiment", + "kernel config" + ], + "commands": [ + "rg -n 'READY_|SYNC_|xEventGroup' include src" + ], + "template_id": "code.protocol" + } + }, + { + "id": "sync", + "number": 3, + "label": "SYNC is a separate mask", + "mode": "CODE", + "strategy_ref": "code.protocol", + "svg_label": "03", + "code_ref": "include/task01_event_groups.h:14", + "description": "Barrier cannot consume readiness meaning.", + "evidence": "ALL=0x70.", + "strategy": { + "id": "task01/a2/structure/sync", + "kind": "code", + "action": "open", + "expected_observations": [ + "disjoint low-bit masks", + "finite waits" + ], + "assertions": [ + "READY_ALL=0x03", + "SYNC_ALL=0x70" + ], + "evidence_fields": [ + "mask", + "API", + "code_ref" + ], + "prerequisites": [ + "źródło FC09" + ], + "layout": [ + "mask table", + "EventGroupExperiment", + "kernel config" + ], + "commands": [ + "rg -n 'READY_|SYNC_|xEventGroup' include src" + ], + "template_id": "code.protocol" + } + }, + { + "id": "coordinator", + "number": 4, + "label": "coordinator waits ALL", + "mode": "RUN", + "event_id": "E02", + "strategy_ref": "run.ready", + "svg_label": "04", + "code_ref": "src/tasks/task01_event_groups.c:97", + "snapshot_ref": "task01.coordinator-wait", + "description": "Partial READY cannot release p3.", + "evidence": "Blocked path.", + "strategy": { + "id": "task01/a2/structure/coordinator", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "a", + "number": 5, + "label": "worker A owns READY_A/SYNC_A", + "mode": "RUN", + "event_id": "E04", + "strategy_ref": "run.ready", + "svg_label": "05", + "code_ref": "src/tasks/task01_event_groups.c:140", + "snapshot_ref": "task01.sync-a", + "description": "A blocks at barrier.", + "evidence": "partial mask follows.", + "strategy": { + "id": "task01/a2/structure/a", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "b", + "number": 6, + "label": "worker B contributes final bits", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "06", + "code_ref": "src/tasks/task01_event_groups.c:183", + "snapshot_ref": "task01.b-release", + "description": "B completes READY then SYNC.", + "evidence": "returns 0x70.", + "strategy": { + "id": "task01/a2/structure/b", + "kind": "run", + "action": "replay", + "expected_observations": [ + "all contain 0x70", + "final=0" + ], + "assertions": [ + "each participant syncs once", + "auto clear" + ], + "evidence_fields": [ + "result masks", + "final bits", + "order" + ], + "prerequisites": [ + "READY_ALL consumed" + ], + "layout": [ + "three sync results", + "final group bits" + ], + "commands": [ + "print/x g_fc09.sync_a_result", + "print/x g_fc09.sync_b_result", + "print/x g_fc09.sync_c_result" + ], + "template_id": "run.barrier" + } + } + ] + } + ] + }, + "tile": { + "index": 1, + "count": 1, + "row": 1, + "column": 1, + "rows": 1, + "columns": 1, + "x": 0.0, + "y": 0.0, + "width": 744.0, + "height": 217.0, + "full_x": 0.0, + "full_y": 0.0, + "full_width": 744.0, + "full_height": 217.0, + "page_width": 940.0, + "page_height": 560.0, + "overview": true, + "step_ids": [ + "a", + "b", + "coordinator", + "group", + "ready", + "sync" + ] + }, + "diagram_title": "A2 · masks and participants", + "diagram_block_label": "A2 STRUCTURE", + "diagram_description": "One group, two disjoint protocols and three participants.", + "diagram_phase_labels": [ + "GROUP / MASKS / PARTICIPANTS" + ] + } + ], + "steps": [], + "spread_group": "", + "spread_label": "A2 STRUCTURE — READY and SYNC masks.", + "spread_columns": 1, + "spread_index": 1, + "spread_count": 1, + "page_orientation": "landscape" + }, + { + "id": "section-2-content", + "index": 2, + "title": "A4 — Application", + "show_heading": false, + "toc_entry": true, + "header_html": "
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TITLEEvent Groups: ALL-ready i bariera
VER.v00.01
DATETIME2026-07-19T00:00:00+02:00
PROJECTFreestanding C nad FreeRTOS
SERIESFreeRTOS C
CARD09/15
SHEET3/8
SUBJ.Inf.
PROG.
CORE
SCOPE
LEVEL
POS.
GITEA UUIDe5c4c0f3-3dd0-5168-9945-14d48744ec6fCARD UUID3f4c1b8b-d54b-58d5-b92d-87a7233cbd9fAUTHOR  M. Pabiszczak
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
", + "left_margin_html": "
TECH
-
", + "right_margin_html": "
OG
-
", + "content_html": "", + "tasks": [], + "assets": [ + { + "label": "fig:a4-application", + "logical_label": "fig:a4-application", + "figure_number": 2, + "caption": "A4 APPLICATION — deterministic release topology. — część 1/1 (r1 c1)", + "alt": "Topologia trzech tasków i verifiera.", + "href": "figures/a4-application.svg", + "source_href": "figures/a4-application.puml", + "width": 1.0, + "kind": "diagram", + "interactive": { + "kind": "uml-class", + "storage_key": "fc09-a4-application-tile-1", + "title": "A4 · participant topology", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a4", + "label": "A4 APPLICATION", + "description": "Priorities create a deterministic observation order." + }, + "phases": [ + { + "id": "topology", + "label": "BLOCK / SET / PREEMPT / VERIFY", + "steps": [ + { + "id": "c", + "number": 1, + "label": "coordinator blocks first", + "mode": "RUN", + "event_id": "E02", + "strategy_ref": "run.ready", + "svg_label": "01", + "code_ref": "src/tasks/task01_event_groups.c:96", + "snapshot_ref": "task01.coordinator-wait", + "description": "Priority 3.", + "evidence": "coordinator stack.", + "strategy": { + "id": "task01/a4/topology/c", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "a", + "number": 2, + "label": "A publishes partial protocols", + "mode": "RUN", + "event_id": "E04", + "strategy_ref": "run.ready", + "svg_label": "02", + "code_ref": "src/tasks/task01_event_groups.c:145", + "snapshot_ref": "task01.sync-a", + "description": "Priority 2 then block.", + "evidence": "READY_A and SYNC_A.", + "strategy": { + "id": "task01/a4/topology/a", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "b", + "number": 3, + "label": "B observes two blocked tasks", + "mode": "RUN", + "event_id": "E05", + "strategy_ref": "run.ready", + "svg_label": "03", + "code_ref": "src/tasks/task01_event_groups.c:175", + "snapshot_ref": "task01.partial", + "description": "No timing guess is needed.", + "evidence": "0x11 and eBlocked.", + "strategy": { + "id": "task01/a4/topology/b", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "cp", + "number": 4, + "label": "coordinator preempts on READY_ALL", + "mode": "RUN", + "event_id": "E06", + "strategy_ref": "run.ready", + "svg_label": "04", + "code_ref": "src/tasks/task01_event_groups.c:107", + "snapshot_ref": "task01.ready-all", + "description": "Higher p3 consumes READY epoch.", + "evidence": "ready_result contains 0x03.", + "strategy": { + "id": "task01/a4/topology/cp", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "bf", + "number": 5, + "label": "B sets the final barrier bit", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "05", + "code_ref": "src/tasks/task01_event_groups.c:190", + "snapshot_ref": "task01.b-release", + "description": "All three waiters return.", + "evidence": "SYNC_ALL.", + "strategy": { + "id": "task01/a4/topology/bf", + "kind": "run", + "action": "replay", + "expected_observations": [ + "all contain 0x70", + "final=0" + ], + "assertions": [ + "each participant syncs once", + "auto clear" + ], + "evidence_fields": [ + "result masks", + "final bits", + "order" + ], + "prerequisites": [ + "READY_ALL consumed" + ], + "layout": [ + "three sync results", + "final group bits" + ], + "commands": [ + "print/x g_fc09.sync_a_result", + "print/x g_fc09.sync_b_result", + "print/x g_fc09.sync_c_result" + ], + "template_id": "run.barrier" + } + }, + { + "id": "v", + "number": 6, + "label": "p0 verifier observes final zero", + "mode": "RUN", + "event_id": "E11", + "strategy_ref": "run.pass", + "svg_label": "06", + "code_ref": "src/tasks/task01_event_groups.c:230", + "snapshot_ref": "task01.pass", + "description": "Auto-clear is part of acceptance.", + "evidence": "PASS and digest.", + "strategy": { + "id": "task01/a4/topology/v", + "kind": "run", + "action": "replay", + "expected_observations": [ + "PASS=1", + "11 committed records" + ], + "assertions": [ + "three-run deterministic", + "distinct stacks" + ], + "evidence_fields": [ + "PASS", + "digest", + "hashes", + "timestamp" + ], + "prerequisites": [ + "all participants done" + ], + "layout": [ + "PASS", + "bounded log", + "digest" + ], + "commands": [ + "print g_fc09_pass", + "print g_fc09.evidence_digest" + ], + "template_id": "run.pass" + } + } + ] + } + ] + }, + "tile": { + "index": 1, + "count": 1, + "row": 1, + "column": 1, + "rows": 1, + "columns": 1, + "x": 0.0, + "y": 0.0, + "width": 314.0, + "height": 684.0, + "full_x": 0.0, + "full_y": 0.0, + "full_width": 314.0, + "full_height": 684.0, + "page_width": 660.0, + "page_height": 760.0, + "overview": true, + "step_ids": [ + "a", + "b", + "bf", + "c", + "cp", + "v" + ] + }, + "diagram_title": "A4 · participant topology", + "diagram_block_label": "A4 APPLICATION", + "diagram_description": "Priorities create a deterministic observation order.", + "diagram_phase_labels": [ + "BLOCK / SET / PREEMPT / VERIFY" + ] + } + ], + "steps": [], + "spread_group": "", + "spread_label": "A4 APPLICATION — deterministic release topology.", + "spread_columns": 1, + "spread_index": 1, + "spread_count": 1, + "page_orientation": "portrait" + }, + { + "id": "section-3-content", + "index": 3, + "title": "A5 — Flow", + "show_heading": false, + "toc_entry": true, + "header_html": "
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TITLEEvent Groups: ALL-ready i bariera
VER.v00.01
DATETIME2026-07-19T00:00:00+02:00
PROJECTFreestanding C nad FreeRTOS
SERIESFreeRTOS C
CARD09/15
SHEET4/8
SUBJ.Inf.
PROG.
CORE
SCOPE
LEVEL
POS.
GITEA UUIDe5c4c0f3-3dd0-5168-9945-14d48744ec6fCARD UUID3f4c1b8b-d54b-58d5-b92d-87a7233cbd9fAUTHOR  M. Pabiszczak
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
", + "left_margin_html": "
TECH
-
", + "right_margin_html": "
OG
-
", + "content_html": "", + "tasks": [], + "assets": [ + { + "label": "fig:a5-flow", + "logical_label": "fig:a5-flow", + "figure_number": 3, + "caption": "A5 FLOW — ALL-ready followed by xEventGroupSync. — część 1/1 (r1 c1)", + "alt": "Sekwencja EventGroup.", + "href": "figures/a5-flow.svg", + "source_href": "figures/a5-flow.puml", + "width": 1.0, + "kind": "diagram", + "interactive": { + "kind": "uml-sequence", + "storage_key": "fc09-a5-flow-tile-1", + "title": "A5 · ready and barrier flow", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a5", + "label": "A5 FLOW", + "description": "Complete E01–E11 protocol flow." + }, + "phases": [ + { + "id": "flow", + "label": "READY ALL / BARRIER / CLEAR", + "steps": [ + { + "id": "config", + "number": 1, + "label": "group starts at zero", + "mode": "RUN", + "event_id": "E01", + "strategy_ref": "run.ready", + "svg_label": "01", + "code_ref": "src/tasks/task01_event_groups.c:266", + "snapshot_ref": "task01.config", + "description": "Clean RAM.", + "evidence": "bits=0.", + "strategy": { + "id": "task01/a5/flow/config", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "wait", + "number": 2, + "label": "coordinator waits ALL", + "mode": "RUN", + "event_id": "E02", + "strategy_ref": "run.ready", + "svg_label": "02", + "code_ref": "src/tasks/task01_event_groups.c:98", + "snapshot_ref": "task01.coordinator-wait", + "description": "Finite timeout.", + "evidence": "p3 blocks.", + "strategy": { + "id": "task01/a5/flow/wait", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "seta", + "number": 3, + "label": "A sets READY_A", + "mode": "RUN", + "event_id": "E03", + "strategy_ref": "run.ready", + "svg_label": "03", + "code_ref": "src/tasks/task01_event_groups.c:140", + "snapshot_ref": "task01.ready-a", + "description": "ALL remains false.", + "evidence": "bits 0x01.", + "strategy": { + "id": "task01/a5/flow/seta", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "synca", + "number": 4, + "label": "A contributes SYNC_A", + "mode": "RUN", + "event_id": "E04", + "strategy_ref": "run.ready", + "svg_label": "04", + "code_ref": "src/tasks/task01_event_groups.c:146", + "snapshot_ref": "task01.sync-a", + "description": "A blocks.", + "evidence": "next partial=0x11.", + "strategy": { + "id": "task01/a5/flow/synca", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "partial", + "number": 5, + "label": "B proves partial state", + "mode": "RUN", + "event_id": "E05", + "strategy_ref": "run.ready", + "svg_label": "05", + "code_ref": "src/tasks/task01_event_groups.c:175", + "snapshot_ref": "task01.partial", + "description": "Neither protocol is complete.", + "evidence": "0x11 and A Blocked.", + "strategy": { + "id": "task01/a5/flow/partial", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "ready", + "number": 6, + "label": "coordinator receives READY_ALL", + "mode": "RUN", + "event_id": "E06", + "strategy_ref": "run.ready", + "svg_label": "06", + "code_ref": "src/tasks/task01_event_groups.c:107", + "snapshot_ref": "task01.ready-all", + "description": "Ready bits clear on exit.", + "evidence": "result 0x03.", + "strategy": { + "id": "task01/a5/flow/ready", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "before", + "number": 7, + "label": "coordinator waits at barrier", + "mode": "RUN", + "event_id": "E07", + "strategy_ref": "run.barrier", + "svg_label": "07", + "code_ref": "src/tasks/task01_event_groups.c:184", + "snapshot_ref": "task01.before-final", + "description": "B sees p3 Blocked.", + "evidence": "state eBlocked.", + "strategy": { + "id": "task01/a5/flow/before", + "kind": "run", + "action": "replay", + "expected_observations": [ + "all contain 0x70", + "final=0" + ], + "assertions": [ + "each participant syncs once", + "auto clear" + ], + "evidence_fields": [ + "result masks", + "final bits", + "order" + ], + "prerequisites": [ + "READY_ALL consumed" + ], + "layout": [ + "three sync results", + "final group bits" + ], + "commands": [ + "print/x g_fc09.sync_a_result", + "print/x g_fc09.sync_b_result", + "print/x g_fc09.sync_c_result" + ], + "template_id": "run.barrier" + } + }, + { + "id": "c", + "number": 8, + "label": "coordinator leaves barrier", + "mode": "RUN", + "event_id": "E08", + "strategy_ref": "run.barrier", + "svg_label": "08", + "code_ref": "src/tasks/task01_event_groups.c:120", + "snapshot_ref": "task01.coordinator-release", + "description": "Final B bit released it.", + "evidence": "result contains 0x70.", + "strategy": { + "id": "task01/a5/flow/c", + "kind": "run", + "action": "replay", + "expected_observations": [ + "all contain 0x70", + "final=0" + ], + "assertions": [ + "each participant syncs once", + "auto clear" + ], + "evidence_fields": [ + "result masks", + "final bits", + "order" + ], + "prerequisites": [ + "READY_ALL consumed" + ], + "layout": [ + "three sync results", + "final group bits" + ], + "commands": [ + "print/x g_fc09.sync_a_result", + "print/x g_fc09.sync_b_result", + "print/x g_fc09.sync_c_result" + ], + "template_id": "run.barrier" + } + }, + { + "id": "a", + "number": 9, + "label": "A leaves barrier", + "mode": "RUN", + "event_id": "E09", + "strategy_ref": "run.barrier", + "svg_label": "09", + "code_ref": "src/tasks/task01_event_groups.c:155", + "snapshot_ref": "task01.a-release", + "description": "A returns same complete mask.", + "evidence": "0x70.", + "strategy": { + "id": "task01/a5/flow/a", + "kind": "run", + "action": "replay", + "expected_observations": [ + "all contain 0x70", + "final=0" + ], + "assertions": [ + "each participant syncs once", + "auto clear" + ], + "evidence_fields": [ + "result masks", + "final bits", + "order" + ], + "prerequisites": [ + "READY_ALL consumed" + ], + "layout": [ + "three sync results", + "final group bits" + ], + "commands": [ + "print/x g_fc09.sync_a_result", + "print/x g_fc09.sync_b_result", + "print/x g_fc09.sync_c_result" + ], + "template_id": "run.barrier" + } + }, + { + "id": "b", + "number": 10, + "label": "B returns and sync bits clear", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "10", + "code_ref": "src/tasks/task01_event_groups.c:199", + "snapshot_ref": "task01.b-release", + "description": "Last contributor also receives full mask.", + "evidence": "0x70.", + "strategy": { + "id": "task01/a5/flow/b", + "kind": "run", + "action": "replay", + "expected_observations": [ + "all contain 0x70", + "final=0" + ], + "assertions": [ + "each participant syncs once", + "auto clear" + ], + "evidence_fields": [ + "result masks", + "final bits", + "order" + ], + "prerequisites": [ + "READY_ALL consumed" + ], + "layout": [ + "three sync results", + "final group bits" + ], + "commands": [ + "print/x g_fc09.sync_a_result", + "print/x g_fc09.sync_b_result", + "print/x g_fc09.sync_c_result" + ], + "template_id": "run.barrier" + } + }, + { + "id": "pass", + "number": 11, + "label": "verifier commits PASS", + "mode": "RUN", + "event_id": "E11", + "strategy_ref": "run.pass", + "svg_label": "11", + "code_ref": "src/tasks/task01_event_groups.c:230", + "snapshot_ref": "task01.pass", + "description": "Final group value is zero.", + "evidence": "digest 2391788d.", + "strategy": { + "id": "task01/a5/flow/pass", + "kind": "run", + "action": "replay", + "expected_observations": [ + "PASS=1", + "11 committed records" + ], + "assertions": [ + "three-run deterministic", + "distinct stacks" + ], + "evidence_fields": [ + "PASS", + "digest", + "hashes", + "timestamp" + ], + "prerequisites": [ + "all participants done" + ], + "layout": [ + "PASS", + "bounded log", + "digest" + ], + "commands": [ + "print g_fc09_pass", + "print g_fc09.evidence_digest" + ], + "template_id": "run.pass" + } + } + ] + } + ] + }, + "tile": { + "index": 1, + "count": 1, + "row": 1, + "column": 1, + "rows": 1, + "columns": 1, + "x": 0.0, + "y": 0.0, + "width": 572.0, + "height": 530.0, + "full_x": 0.0, + "full_y": 0.0, + "full_width": 572.0, + "full_height": 530.0, + "page_width": 940.0, + "page_height": 560.0, + "overview": true, + "step_ids": [ + "a", + "b", + "before", + "c", + "config", + "partial", + "pass", + "ready", + "seta", + "synca", + "wait" + ] + }, + "diagram_title": "A5 · ready and barrier flow", + "diagram_block_label": "A5 FLOW", + "diagram_description": "Complete E01–E11 protocol flow.", + "diagram_phase_labels": [ + "READY ALL / BARRIER / CLEAR" + ] + } + ], + "steps": [], + "spread_group": "", + "spread_label": "A5 FLOW — ALL-ready followed by xEventGroupSync.", + "spread_columns": 1, + "spread_index": 1, + "spread_count": 1, + "page_orientation": "landscape" + }, + { + "id": "section-4-content", + "index": 4, + "title": "A6 — State", + "show_heading": false, + "toc_entry": true, + "header_html": "
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TITLEEvent Groups: ALL-ready i bariera
VER.v00.01
DATETIME2026-07-19T00:00:00+02:00
PROJECTFreestanding C nad FreeRTOS
SERIESFreeRTOS C
CARD09/15
SHEET5/8
SUBJ.Inf.
PROG.
CORE
SCOPE
LEVEL
POS.
GITEA UUIDe5c4c0f3-3dd0-5168-9945-14d48744ec6fCARD UUID3f4c1b8b-d54b-58d5-b92d-87a7233cbd9fAUTHOR  M. Pabiszczak
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
", + "left_margin_html": "
TECH
-
", + "right_margin_html": "
OG
-
", + "content_html": "", + "tasks": [], + "assets": [ + { + "label": "fig:a6-state", + "logical_label": "fig:a6-state", + "figure_number": 4, + "caption": "A6 STATE — zero, partial, ready and barrier release. — część 1/1 (r1 c1)", + "alt": "Maszyna stanu bitów.", + "href": "figures/a6-state.svg", + "source_href": "figures/a6-state.puml", + "width": 1.0, + "kind": "diagram", + "interactive": { + "kind": "uml-class", + "storage_key": "fc09-a6-state-tile-1", + "title": "A6 · bit-state machine", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a6", + "label": "A6 STATE", + "description": "The same integer carries two explicit protocols." + }, + "phases": [ + { + "id": "state", + "label": "ZERO / PARTIAL / COMPLETE / CLEAR", + "steps": [ + { + "id": "zero", + "number": 1, + "label": "zero initial and final", + "mode": "RUN", + "event_id": "E01", + "strategy_ref": "run.ready", + "svg_label": "01", + "code_ref": "src/tasks/task01_event_groups.c:261", + "snapshot_ref": "task01.config", + "description": "No retained epoch.", + "evidence": "0x00.", + "strategy": { + "id": "task01/a6/state/zero", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "partial", + "number": 3, + "label": "READY_A is insufficient", + "mode": "RUN", + "event_id": "E05", + "strategy_ref": "run.ready", + "svg_label": "03", + "code_ref": "src/tasks/task01_event_groups.c:175", + "snapshot_ref": "task01.partial", + "description": "Both waiters remain blocked.", + "evidence": "0x11.", + "strategy": { + "id": "task01/a6/state/partial", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "all", + "number": 6, + "label": "READY_ALL releases and clears", + "mode": "RUN", + "event_id": "E06", + "strategy_ref": "run.ready", + "svg_label": "06", + "code_ref": "src/tasks/task01_event_groups.c:97", + "snapshot_ref": "task01.ready-all", + "description": "Return contains pre-clear bits.", + "evidence": "0x03.", + "strategy": { + "id": "task01/a6/state/all", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "barrier", + "number": 10, + "label": "SYNC_ALL releases and auto-clears", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "10", + "code_ref": "src/tasks/task01_event_groups.c:190", + "snapshot_ref": "task01.b-release", + "description": "All participants see complete mask.", + "evidence": "0x70 then 0.", + "strategy": { + "id": "task01/a6/state/barrier", + "kind": "run", + "action": "replay", + "expected_observations": [ + "all contain 0x70", + "final=0" + ], + "assertions": [ + "each participant syncs once", + "auto clear" + ], + "evidence_fields": [ + "result masks", + "final bits", + "order" + ], + "prerequisites": [ + "READY_ALL consumed" + ], + "layout": [ + "three sync results", + "final group bits" + ], + "commands": [ + "print/x g_fc09.sync_a_result", + "print/x g_fc09.sync_b_result", + "print/x g_fc09.sync_c_result" + ], + "template_id": "run.barrier" + } + } + ] + } + ] + }, + "tile": { + "index": 1, + "count": 1, + "row": 1, + "column": 1, + "rows": 1, + "columns": 1, + "x": 0.0, + "y": 0.0, + "width": 311.0, + "height": 700.0, + "full_x": 0.0, + "full_y": 0.0, + "full_width": 311.0, + "full_height": 700.0, + "page_width": 660.0, + "page_height": 760.0, + "overview": true, + "step_ids": [ + "all", + "barrier", + "partial", + "zero" + ] + }, + "diagram_title": "A6 · bit-state machine", + "diagram_block_label": "A6 STATE", + "diagram_description": "The same integer carries two explicit protocols.", + "diagram_phase_labels": [ + "ZERO / PARTIAL / COMPLETE / CLEAR" + ] + } + ], + "steps": [], + "spread_group": "", + "spread_label": "A6 STATE — zero, partial, ready and barrier release.", + "spread_columns": 1, + "spread_index": 1, + "spread_count": 1, + "page_orientation": "portrait" + }, + { + "id": "section-5-content", + "index": 5, + "title": "A7 — Runtime", + "show_heading": false, + "toc_entry": true, + "header_html": "
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TITLEEvent Groups: ALL-ready i bariera
VER.v00.01
DATETIME2026-07-19T00:00:00+02:00
PROJECTFreestanding C nad FreeRTOS
SERIESFreeRTOS C
CARD09/15
SHEET6/8
SUBJ.Inf.
PROG.
CORE
SCOPE
LEVEL
POS.
GITEA UUIDe5c4c0f3-3dd0-5168-9945-14d48744ec6fCARD UUID3f4c1b8b-d54b-58d5-b92d-87a7233cbd9fAUTHOR  M. Pabiszczak
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
", + "left_margin_html": "
TECH
-
", + "right_margin_html": "
OG
-
", + "content_html": "", + "tasks": [], + "assets": [ + { + "label": "fig:a7-runtime", + "logical_label": "fig:a7-runtime", + "figure_number": 5, + "caption": "A7 RUNTIME — event value and waiting TCBs. — część 1/1 (r1 c1)", + "alt": "Runtime EventGroup.", + "href": "figures/a7-runtime.svg", + "source_href": "figures/a7-runtime.puml", + "width": 1.0, + "kind": "diagram", + "interactive": { + "kind": "uml-class", + "storage_key": "fc09-a7-runtime-tile-1", + "title": "A7 · runtime evidence", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a7", + "label": "A7 RUNTIME", + "description": "One handle, two wait masks, three stacks and one canonical log." + }, + "phases": [ + { + "id": "runtime", + "label": "GROUP / TCB / STACK / LOG", + "steps": [ + { + "id": "group", + "number": 1, + "label": "real group handle", + "mode": "RUN", + "event_id": "E01", + "strategy_ref": "run.ready", + "svg_label": "01", + "code_ref": "src/tasks/task01_event_groups.c:261", + "snapshot_ref": "task01.config", + "description": "Persistent kernel object.", + "evidence": "non-null handle.", + "strategy": { + "id": "task01/a7/runtime/group", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "bits", + "number": 2, + "label": "value evolves by API calls", + "mode": "RUN", + "event_id": "E05", + "strategy_ref": "run.ready", + "svg_label": "02", + "code_ref": "src/tasks/task01_event_groups.c:175", + "snapshot_ref": "task01.partial", + "description": "No shadow counter.", + "evidence": "0x11.", + "strategy": { + "id": "task01/a7/runtime/bits", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "c", + "number": 3, + "label": "coordinator has distinct stack", + "mode": "RUN", + "event_id": "E02", + "strategy_ref": "run.ready", + "svg_label": "03", + "code_ref": "src/tasks/task01_event_groups.c:95", + "snapshot_ref": "task01.coordinator-wait", + "description": "Blocked context preserved.", + "evidence": "coordinator_sp.", + "strategy": { + "id": "task01/a7/runtime/c", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "a", + "number": 4, + "label": "A waits at barrier", + "mode": "RUN", + "event_id": "E05", + "strategy_ref": "run.ready", + "svg_label": "04", + "code_ref": "src/tasks/task01_event_groups.c:176", + "snapshot_ref": "task01.partial", + "description": "Public eBlocked proof.", + "evidence": "worker_a_sp.", + "strategy": { + "id": "task01/a7/runtime/a", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "b", + "number": 5, + "label": "B provides final bit", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "05", + "code_ref": "src/tasks/task01_event_groups.c:190", + "snapshot_ref": "task01.b-release", + "description": "Third stack.", + "evidence": "worker_b_sp.", + "strategy": { + "id": "task01/a7/runtime/b", + "kind": "run", + "action": "replay", + "expected_observations": [ + "all contain 0x70", + "final=0" + ], + "assertions": [ + "each participant syncs once", + "auto clear" + ], + "evidence_fields": [ + "result masks", + "final bits", + "order" + ], + "prerequisites": [ + "READY_ALL consumed" + ], + "layout": [ + "three sync results", + "final group bits" + ], + "commands": [ + "print/x g_fc09.sync_a_result", + "print/x g_fc09.sync_b_result", + "print/x g_fc09.sync_c_result" + ], + "template_id": "run.barrier" + } + }, + { + "id": "log", + "number": 6, + "label": "commit-last bounded evidence", + "mode": "RUN", + "event_id": "E11", + "strategy_ref": "run.pass", + "svg_label": "06", + "code_ref": "src/tasks/task01_event_groups.c:55", + "snapshot_ref": "task01.pass", + "description": "Exact E01–E11.", + "evidence": "digest.", + "strategy": { + "id": "task01/a7/runtime/log", + "kind": "run", + "action": "replay", + "expected_observations": [ + "PASS=1", + "11 committed records" + ], + "assertions": [ + "three-run deterministic", + "distinct stacks" + ], + "evidence_fields": [ + "PASS", + "digest", + "hashes", + "timestamp" + ], + "prerequisites": [ + "all participants done" + ], + "layout": [ + "PASS", + "bounded log", + "digest" + ], + "commands": [ + "print g_fc09_pass", + "print g_fc09.evidence_digest" + ], + "template_id": "run.pass" + } + } + ] + } + ] + }, + "tile": { + "index": 1, + "count": 1, + "row": 1, + "column": 1, + "rows": 1, + "columns": 1, + "x": 0.0, + "y": 0.0, + "width": 708.0, + "height": 346.0, + "full_x": 0.0, + "full_y": 0.0, + "full_width": 708.0, + "full_height": 346.0, + "page_width": 940.0, + "page_height": 560.0, + "overview": true, + "step_ids": [ + "a", + "b", + "bits", + "c", + "group", + "log" + ] + }, + "diagram_title": "A7 · runtime evidence", + "diagram_block_label": "A7 RUNTIME", + "diagram_description": "One handle, two wait masks, three stacks and one canonical log.", + "diagram_phase_labels": [ + "GROUP / TCB / STACK / LOG" + ] + } + ], + "steps": [], + "spread_group": "", + "spread_label": "A7 RUNTIME — event value and waiting TCBs.", + "spread_columns": 1, + "spread_index": 1, + "spread_count": 1, + "page_orientation": "landscape" + }, + { + "id": "section-6-content", + "index": 6, + "title": "A8 — Patterns", + "show_heading": false, + "toc_entry": true, + "header_html": "
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TITLEEvent Groups: ALL-ready i bariera
VER.v00.01
DATETIME2026-07-19T00:00:00+02:00
PROJECTFreestanding C nad FreeRTOS
SERIESFreeRTOS C
CARD09/15
SHEET7/8
SUBJ.Inf.
PROG.
CORE
SCOPE
LEVEL
POS.
GITEA UUIDe5c4c0f3-3dd0-5168-9945-14d48744ec6fCARD UUID3f4c1b8b-d54b-58d5-b92d-87a7233cbd9fAUTHOR  M. Pabiszczak
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
", + "left_margin_html": "
TECH
-
", + "right_margin_html": "
OG
-
", + "content_html": "", + "tasks": [], + "assets": [ + { + "label": "fig:a8-patterns", + "logical_label": "fig:a8-patterns", + "figure_number": 6, + "caption": "A8 PATTERNS — ANY, ALL, consume and barrier. — część 1/1 (r1 c1)", + "alt": "Wzorce EventGroup.", + "href": "figures/a8-patterns.svg", + "source_href": "figures/a8-patterns.puml", + "width": 1.0, + "kind": "diagram", + "interactive": { + "kind": "uml-class", + "storage_key": "fc09-a8-patterns-tile-1", + "title": "A8 · event synchronization choices", + "task": { + "id": "task01", + "label": "Task01 · EventGroup" + }, + "block": { + "id": "a8", + "label": "A8 PATTERNS", + "description": "Choose bit protocol semantics explicitly." + }, + "phases": [ + { + "id": "patterns", + "label": "ANY / ALL / CLEAR / SYNC", + "steps": [ + { + "id": "any", + "number": 1, + "label": "ANY releases on first selected bit", + "mode": "CODE", + "strategy_ref": "code.protocol", + "svg_label": "01", + "code_ref": "include/task01_event_groups.h:11", + "description": "Useful for alternatives.", + "evidence": "exercise variant.", + "strategy": { + "id": "task01/a8/patterns/any", + "kind": "code", + "action": "open", + "expected_observations": [ + "disjoint low-bit masks", + "finite waits" + ], + "assertions": [ + "READY_ALL=0x03", + "SYNC_ALL=0x70" + ], + "evidence_fields": [ + "mask", + "API", + "code_ref" + ], + "prerequisites": [ + "źródło FC09" + ], + "layout": [ + "mask table", + "EventGroupExperiment", + "kernel config" + ], + "commands": [ + "rg -n 'READY_|SYNC_|xEventGroup' include src" + ], + "template_id": "code.protocol" + } + }, + { + "id": "all", + "number": 2, + "label": "ALL joins independent readiness", + "mode": "RUN", + "event_id": "E06", + "strategy_ref": "run.ready", + "svg_label": "02", + "code_ref": "src/tasks/task01_event_groups.c:98", + "snapshot_ref": "task01.ready-all", + "description": "FC09 readiness contract.", + "evidence": "0x03.", + "strategy": { + "id": "task01/a8/patterns/all", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "clear", + "number": 3, + "label": "clear consumes the readiness epoch", + "mode": "RUN", + "event_id": "E06", + "strategy_ref": "run.ready", + "svg_label": "03", + "code_ref": "src/tasks/task01_event_groups.c:100", + "snapshot_ref": "task01.ready-all", + "description": "Old readiness is not retained.", + "evidence": "final zero.", + "strategy": { + "id": "task01/a8/patterns/clear", + "kind": "run", + "action": "replay", + "expected_observations": [ + "partial=0x11", + "A Blocked", + "coordinator still Blocked" + ], + "assertions": [ + "ALL not satisfied by READY_A", + "reserved bits unused" + ], + "evidence_fields": [ + "bits", + "state", + "PC", + "SP", + "timestamp" + ], + "prerequisites": [ + "clean Hazard3 RAM" + ], + "layout": [ + "group bits", + "coordinator/A states", + "event log" + ], + "commands": [ + "print g_fc09.partial_bits", + "print g_fc09.worker_a_state_at_partial" + ], + "template_id": "run.ready" + } + }, + { + "id": "sync", + "number": 4, + "label": "sync is a reusable barrier", + "mode": "RUN", + "event_id": "E10", + "strategy_ref": "run.barrier", + "svg_label": "04", + "code_ref": "src/tasks/task01_event_groups.c:190", + "snapshot_ref": "task01.b-release", + "description": "Set own bit and wait ALL atomically.", + "evidence": "three 0x70 results.", + "strategy": { + "id": "task01/a8/patterns/sync", + "kind": "run", + "action": "replay", + "expected_observations": [ + "all contain 0x70", + "final=0" + ], + "assertions": [ + "each participant syncs once", + "auto clear" + ], + "evidence_fields": [ + "result masks", + "final bits", + "order" + ], + "prerequisites": [ + "READY_ALL consumed" + ], + "layout": [ + "three sync results", + "final group bits" + ], + "commands": [ + "print/x g_fc09.sync_a_result", + "print/x g_fc09.sync_b_result", + "print/x g_fc09.sync_c_result" + ], + "template_id": "run.barrier" + } + } + ] + } + ] + }, + "tile": { + "index": 1, + "count": 1, + "row": 1, + "column": 1, + "rows": 1, + "columns": 1, + "x": 0.0, + "y": 0.0, + "width": 438.0, + "height": 433.0, + "full_x": 0.0, + "full_y": 0.0, + "full_width": 438.0, + "full_height": 433.0, + "page_width": 940.0, + "page_height": 560.0, + "overview": true, + "step_ids": [ + "all", + "any", + "clear", + "sync" + ] + }, + "diagram_title": "A8 · event synchronization choices", + "diagram_block_label": "A8 PATTERNS", + "diagram_description": "Choose bit protocol semantics explicitly.", + "diagram_phase_labels": [ + "ANY / ALL / CLEAR / SYNC" + ] + } + ], + "steps": [], + "spread_group": "", + "spread_label": "A8 PATTERNS — ANY, ALL, consume and barrier.", + "spread_columns": 1, + "spread_index": 1, + "spread_count": 1, + "page_orientation": "landscape" + }, + { + "id": "section-7-task01", + "index": 7, + "title": "Task01 — EventGroup", + "show_heading": true, + "toc_entry": true, + "header_html": "
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
TITLEEvent Groups: ALL-ready i bariera
VER.v00.01
DATETIME2026-07-19T00:00:00+02:00
PROJECTFreestanding C nad FreeRTOS
SERIESFreeRTOS C
CARD09/15
SHEET8/8
SUBJ.Inf.
PROG.
CORE
SCOPE
LEVEL
POS.
GITEA UUIDe5c4c0f3-3dd0-5168-9945-14d48744ec6fCARD UUID3f4c1b8b-d54b-58d5-b92d-87a7233cbd9fAUTHOR  M. Pabiszczak
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
", + "left_margin_html": "
TECH
-
", + "right_margin_html": "
OG
-
", + "content_html": "", + "tasks": [ + { + "ref": "task01", + "title": "ALL-ready i trzyosobowa bariera", + "uuid": "3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f", + "criterion": "PASS; partial=0x11; READY result contains 0x03; all SYNC results contain 0x70; final=0; deterministic digest.", + "conclusion_html": "

EventGroup przechowuje bity stanu protokołu, nie historię zdarzeń. Rozłączne maski i jawne reguły clear są warunkiem poprawnej synchronizacji.

", + "prompt_html": "

Przejdź po A2, A4, A5, A6, A7 i A8. Odtwórz E01–E11 i udowodnij, że maska częściowa nie zwalnia tasków, a bariera zwalnia wszystkich dokładnie raz.

", + "flow": [ + { + "id": "predict", + "kind": "block", + "title": "A — mask table", + "content_html": "

Przypisz READY i SYNC do właścicieli.

", + "steps": [ + { + "id": "partial", + "title": "Przewidź 0x11.", + "content_html": "

Wskaż, które taski muszą być Blocked.

" + }, + { + "id": "clear", + "title": "Przewidź final zero.", + "content_html": "

Rozdziel clear-on-exit od barrier auto-clear.

" + } + ] + }, + { + "id": "replay", + "kind": "block", + "title": "B — replay Hazard3", + "content_html": "

RUN zaczyna się od czystej RAM.

", + "steps": [ + { + "id": "ready", + "title": "Zbadaj E02–E06.", + "content_html": "

Porównaj maski i task states.

" + }, + { + "id": "barrier", + "title": "Zbadaj E07–E10.", + "content_html": "

Porównaj trzy wartości zwrotne.

" + }, + { + "id": "pass", + "title": "Zbadaj E11.", + "content_html": "

Zapisz final bits, SP, PASS i digest.

" + } + ] + }, + { + "id": "any-all", + "kind": "exercise", + "title": "Ćwiczenie — ANY kontra ALL", + "content_html": "", + "steps": [] + } + ] + } + ], + "assets": [], + "steps": [], + "spread_group": "", + "spread_label": "", + "spread_columns": 1, + "spread_index": 1, + "spread_count": 1, + "page_orientation": "portrait" + } + ], + "viewpoints": [ + { + "id": "A1", + "label": "CONTEXT", + "subtitle": "Granice", + "status": "unavailable", + "reason": "Bez nowej granicy sprzętowej lub callbacku.", + "target_id": "" + }, + { + "id": "A2", + "label": "STRUCTURE", + "subtitle": "EventGroup, masks i participants", + "status": "enabled", + "target_label": "fig:a2-structure", + "target_id": "fig-a2-structure" + }, + { + "id": "A3", + "label": "DISPATCH", + "subtitle": "Binding", + "status": "unavailable", + "reason": "Task-context EventGroup nie wprowadza callbacku.", + "target_id": "" + }, + { + "id": "A4", + "label": "APPLICATION", + "subtitle": "Coordinator, A, B, verifier", + "status": "enabled", + "target_label": "fig:a4-application", + "target_id": "fig-a4-application" + }, + { + "id": "A5", + "label": "FLOW", + "subtitle": "E01–E11", + "status": "enabled", + "target_label": "fig:a5-flow", + "target_id": "fig-a5-flow" + }, + { + "id": "A6", + "label": "STATE", + "subtitle": "Bit value i blocked tasks", + "status": "enabled", + "target_label": "fig:a6-state", + "target_id": "fig-a6-state" + }, + { + "id": "A7", + "label": "RUNTIME", + "subtitle": "Handle, wait lists, stacks, digest", + "status": "enabled", + "target_label": "fig:a7-runtime", + "target_id": "fig-a7-runtime" + }, + { + "id": "A8", + "label": "PATTERNS", + "subtitle": "ANY, ALL, clear i barrier", + "status": "enabled", + "target_label": "fig:a8-patterns", + "target_id": "fig-a8-patterns" + } + ], + "has_spread": false, + "has_allocator_model": false, + "dictionary": null, + "toc": [ + { + "id": "section-1-content", + "label": "1. A2 — Structure" + }, + { + "id": "section-2-content", + "label": "2. A4 — Application" + }, + { + "id": "section-3-content", + "label": "3. A5 — Flow" + }, + { + "id": "section-4-content", + "label": "4. A6 — State" + }, + { + "id": "section-5-content", + "label": "5. A7 — Runtime" + }, + { + "id": "section-6-content", + "label": "6. A8 — Patterns" + }, + { + "id": "section-7-task01", + "label": "7. Task01 — EventGroup" + } + ] +} diff --git a/web/figures/a2-structure.puml b/web/figures/a2-structure.puml new file mode 100644 index 0000000..c7c61b3 --- /dev/null +++ b/web/figures/a2-structure.puml @@ -0,0 +1,20 @@ +@startuml +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 12 +skinparam rectangleBorderColor #2c7794 +skinparam rectangleBackgroundColor #edf6fa +title A2 STRUCTURE — one EventGroup and disjoint masks +rectangle "01 EventGroup\nEventBits_t value" as group +rectangle "02 READY mask\nA=0x01 B=0x02\nALL=0x03" as ready +rectangle "03 SYNC mask\nA=0x10 B=0x20 C=0x40\nALL=0x70" as sync +rectangle "04 coordinator p3\nwait ALL-ready\nsync C" as coordinator +rectangle "05 worker A p2\nset READY_A\nsync A" as a +rectangle "06 worker B p1\nset READY_B\nsync B" as b +ready --> group +sync --> group +coordinator --> group +a --> group +b --> group +@enduml diff --git a/web/figures/a2-structure.svg b/web/figures/a2-structure.svg new file mode 100644 index 0000000..c6c9714 --- /dev/null +++ b/web/figures/a2-structure.svg @@ -0,0 +1,43 @@ +A2 STRUCTURE — one EventGroup and disjoint masks01 EventGroupEventBits_t value02 READY maskA=0x01 B=0x02ALL=0x0303 SYNC maskA=0x10 B=0x20 C=0x40ALL=0x7004 coordinator p3wait ALL-readysync C05 worker A p2set READY_Async A06 worker B p1set READY_Bsync B \ No newline at end of file diff --git a/web/figures/a4-application.puml b/web/figures/a4-application.puml new file mode 100644 index 0000000..7e87593 --- /dev/null +++ b/web/figures/a4-application.puml @@ -0,0 +1,20 @@ +@startuml +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 12 +skinparam rectangleBorderColor #2c7794 +skinparam rectangleBackgroundColor #edf6fa +title A4 APPLICATION — deterministic participant topology +rectangle "01 coordinator p3\nBlocked: wait READY_ALL" as c +rectangle "02 worker A p2\nREADY_A then Blocked at barrier" as a +rectangle "03 worker B p1\nobserves partial mask\nsets final READY_B" as b +rectangle "04 coordinator\npreempts B\ncontributes SYNC_C" as cp +rectangle "05 worker B\ncontributes final SYNC_B" as bf +rectangle "06 verifier p0\nchecks masks, states, stacks, order" as v +c --> a +a --> b +b --> cp +cp --> bf +bf --> v +@enduml diff --git a/web/figures/a4-application.svg b/web/figures/a4-application.svg new file mode 100644 index 0000000..ea417e0 --- /dev/null +++ b/web/figures/a4-application.svg @@ -0,0 +1,43 @@ +A4 APPLICATION — deterministic participant topology01 coordinator p3Blocked: wait READY_ALL02 worker A p2READY_A then Blocked at barrier03 worker B p1observes partial masksets final READY_B04 coordinatorpreempts Bcontributes SYNC_C05 worker Bcontributes final SYNC_B06 verifier p0checks masks, states, stacks, order \ No newline at end of file diff --git a/web/figures/a5-flow.puml b/web/figures/a5-flow.puml new file mode 100644 index 0000000..ee7adfd --- /dev/null +++ b/web/figures/a5-flow.puml @@ -0,0 +1,29 @@ +@startuml +scale 520 height +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 11 +skinparam sequenceArrowColor #8095a1 +skinparam sequenceLifeLineBorderColor #8095a1 +title A5 FLOW — ALL-ready wait and reusable barrier +participant Coordinator as C +participant "Worker A" as A +participant "Worker B" as B +participant EventGroup as G +participant Verifier as V +C -> G : 02 wait READY_ALL; block +A -> G : 03 set READY_A +A -> G : 04 sync(SYNC_A); block +B -> B : 05 observe READY_A|SYNC_A; A Blocked +B -> G : set READY_B +G --> C : 06 READY_ALL; clear READY bits +C -> G : sync(SYNC_C); block +B -> B : 07 coordinator Blocked +B -> G : sync(SYNC_B) = final bit +G --> C : 08 SYNC_ALL +G --> A : 09 SYNC_ALL +G --> B : 10 SYNC_ALL; clear sync bits +B -> V : workers complete +V -> G : 11 final bits = 0; PASS +@enduml diff --git a/web/figures/a5-flow.svg b/web/figures/a5-flow.svg new file mode 100644 index 0000000..c823d98 --- /dev/null +++ b/web/figures/a5-flow.svg @@ -0,0 +1,41 @@ +A5 FLOW — ALL-ready wait and reusable barrierCoordinatorCoordinatorWorker AWorker AWorker BWorker BEventGroupEventGroupVerifierVerifier02 wait READY_ALL; block03 set READY_A04 sync(SYNC_A); block05 observe READY_A|SYNC_A; A Blockedset READY_B06 READY_ALL; clear READY bitssync(SYNC_C); block07 coordinator Blockedsync(SYNC_B) = final bit08 SYNC_ALL09 SYNC_ALL10 SYNC_ALL; clear sync bitsworkers complete11 final bits = 0; PASS \ No newline at end of file diff --git a/web/figures/a6-state.puml b/web/figures/a6-state.puml new file mode 100644 index 0000000..5649de7 --- /dev/null +++ b/web/figures/a6-state.puml @@ -0,0 +1,24 @@ +@startuml +scale 700 height +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 12 +skinparam stateBorderColor #2c7794 +skinparam stateBackgroundColor #edf6fa +title A6 STATE — bit mask and blocked participants +[*] --> Zero : 01 0x00 +Zero --> ReadyA : 03 set 0x01 +ReadyA --> Partial : 04 sync A +Partial : bits 0x11 +Partial : coordinator Blocked +Partial : worker A Blocked +Partial --> ReadyAll : set READY_B +ReadyAll : return 0x03 to coordinator +ReadyAll --> BarrierAC : clear READY; set SYNC_C +BarrierAC : bits 0x50 +BarrierAC --> Release : 10 set SYNC_B +Release : all return 0x70 +Release --> Zero : auto-clear sync mask +Zero --> [*] : 11 PASS +@enduml diff --git a/web/figures/a6-state.svg b/web/figures/a6-state.svg new file mode 100644 index 0000000..e7e8e54 --- /dev/null +++ b/web/figures/a6-state.svg @@ -0,0 +1,44 @@ +A6 STATE — bit mask and blocked participantsZeroReadyAPartialbits 0x11coordinator Blockedworker A BlockedReadyAllreturn 0x03 to coordinatorBarrierACbits 0x50Releaseall return 0x7001 0x0003 set 0x0104 sync Aset READY_Bclear READY; set SYNC_C10 set SYNC_Bauto-clear sync mask11 PASS \ No newline at end of file diff --git a/web/figures/a7-runtime.puml b/web/figures/a7-runtime.puml new file mode 100644 index 0000000..f7b3626 --- /dev/null +++ b/web/figures/a7-runtime.puml @@ -0,0 +1,22 @@ +@startuml +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 12 +skinparam rectangleBorderColor #2c7794 +skinparam rectangleBackgroundColor #edf6fa +title A7 RUNTIME — event value, wait lists and task stacks +rectangle "01 g_fc09.group\nreal EventGroup handle" as group +rectangle "02 event value\n0x00 → 0x01 → 0x11\n→ 0x70 → 0x00" as bits +rectangle "03 coordinator TCB/stack\nwait mask 0x03\nthen 0x70" as c +rectangle "04 worker A TCB/stack\nbarrier waiter" as a +rectangle "05 worker B TCB/stack\nfinal bit setter" as b +rectangle "06 g_fc09_events[11]\ncommit-last records\ndigest 2391788d" as log +group --> bits +group --> c +group --> a +group --> b +c --> log +a --> log +b --> log +@enduml diff --git a/web/figures/a7-runtime.svg b/web/figures/a7-runtime.svg new file mode 100644 index 0000000..312fe71 --- /dev/null +++ b/web/figures/a7-runtime.svg @@ -0,0 +1,47 @@ +A7 RUNTIME — event value, wait lists and task stacks01 g_fc09.groupreal EventGroup handle02 event value0x00 → 0x01 → 0x11→ 0x70 → 0x0003 coordinator TCB/stackwait mask 0x03then 0x7004 worker A TCB/stackbarrier waiter05 worker B TCB/stackfinal bit setter06 g_fc09_events[11]commit-last recordsdigest 2391788d \ No newline at end of file diff --git a/web/figures/a8-patterns.puml b/web/figures/a8-patterns.puml new file mode 100644 index 0000000..5dd1200 --- /dev/null +++ b/web/figures/a8-patterns.puml @@ -0,0 +1,20 @@ +@startuml +skinparam backgroundColor transparent +skinparam shadowing false +skinparam defaultFontName Monospace +skinparam defaultFontSize 12 +skinparam rectangleBorderColor #2c7794 +skinparam rectangleBackgroundColor #edf6fa +title A8 PATTERNS — event latch and reusable barrier +rectangle "01 ANY wait\nrelease on first selected bit" as any +rectangle "02 ALL wait\nrelease on complete mask" as all +rectangle "03 clear-on-exit\nconsume readiness epoch" as clear +rectangle "04 xEventGroupSync\nset own bit + wait ALL\nautomatic barrier clear" as barrier +any -[hidden]right- all +all --> clear +clear --> barrier +note bottom of barrier + FC09 uses disjoint READY and SYNC masks. + Reserved control bits are never selected. +end note +@enduml diff --git a/web/figures/a8-patterns.svg b/web/figures/a8-patterns.svg new file mode 100644 index 0000000..208692f --- /dev/null +++ b/web/figures/a8-patterns.svg @@ -0,0 +1,39 @@ +A8 PATTERNS — event latch and reusable barrier01 ANY waitrelease on first selected bit02 ALL waitrelease on complete mask03 clear-on-exitconsume readiness epoch04 xEventGroupSyncset own bit + wait ALLautomatic barrier clearFC09 uses disjoint READY and SYNC masks.Reserved control bits are never selected. \ No newline at end of file diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..8f9f9f5 --- /dev/null +++ b/web/index.html @@ -0,0 +1,15 @@ + + + + + + + Karta pracy + + + + +
+ + + diff --git a/web/style.css b/web/style.css new file mode 100644 index 0000000..bffe90d --- /dev/null +++ b/web/style.css @@ -0,0 +1,39 @@ + +:root{--ink:#111;--muted:#666;--line:#b9b9b9;--line-soft:#dddddd;--paper:#fff;--desk:#eeeeee;--og:#008000;--tech:#d36b00;--kw:#0018c8;--we:#e00000;--viewer-canvas-scale:1;--viewer-content-scale:1} +*{box-sizing:border-box} html{scroll-behavior:smooth} body{margin:0;background:var(--desk);color:var(--ink);font-family:"Latin Modern Roman","LM Roman 12",Georgia,"Times New Roman",serif;line-height:1.25} +.topbar{position:sticky;top:0;z-index:20;background:rgba(245,245,245,.94);border-bottom:1px solid #d0d0d0;padding:3px 0;font-family:system-ui,-apple-system,Segoe UI,sans-serif} +.topbar-inner{width:calc(100% - 8px);max-width:none;margin:0 4px;display:flex;align-items:center;justify-content:space-between;gap:6px}.topbar details{min-width:0;flex:1}.topbar summary{cursor:pointer;font-size:16px;line-height:1.35;font-weight:650}.toc-links{display:flex;flex-wrap:wrap;gap:6px;margin-top:5px}.toc-links a{font-size:16px;color:#333;text-decoration:none;border:1px solid #ccc;background:white;border-radius:3px;padding:2px 5px}.viewer-zoom-controls{display:flex;align-items:center;gap:7px;flex:none}.viewer-zoom-status{font:600 16px/1.4 ui-monospace,SFMono-Regular,Consolas,monospace;color:#444;white-space:nowrap}.viewer-zoom-reset{border:1px solid #bbb;border-radius:3px;background:#fff;color:#333;padding:1px 6px;font:600 16px/1.35 system-ui,-apple-system,Segoe UI,sans-serif;cursor:pointer}.viewer-zoom-reset:hover{background:#eee}.viewer-zoom-reset:focus-visible{outline:2px solid #555;outline-offset:1px} +.paper{padding:18px 12px 44px;overflow-x:auto}.sheet{position:relative;width:210mm;min-height:297mm;margin:0 auto 18px;background:var(--paper);box-shadow:0 2px 12px rgba(0,0,0,.13);display:block;padding:0;overflow:visible;zoom:var(--viewer-canvas-scale)}.sheet-content{position:relative;width:100%;min-height:297mm;padding:13mm 21.5mm 15mm;transform:scale(var(--sheet-effective-scale,1));transform-origin:top left}.resource-header-enabled .sheet-content{padding-top:5mm}.sheet-content>.page-resource-header{margin-bottom:3mm} +@media(max-width:900px){.sheet{height:auto}}@media print{.sheet{height:auto}} +.pdf-main{min-width:0;width:100%}.running-header{display:flex;justify-content:space-between;gap:18px;align-items:flex-end;border-bottom:1px solid #111;padding-bottom:3px;margin-bottom:26px;font-size:15px}.running-header span{white-space:nowrap} +.hero h1{font-size:24px;line-height:1.15;margin:0 0 4px;border-bottom:1px solid #111;padding-bottom:4px}.hero .byline{float:right;margin-top:-31px;font-size:15px} +.meta{clear:both;display:grid;grid-template-columns:148px minmax(0,1fr);gap:4px 18px;padding:34px 0 24px;border-bottom:1px solid #111;font-size:17px}.meta dt{color:#333}.meta dd{margin:0;font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace} +.front-scope{padding:0 0 3mm;border-bottom:1px solid var(--line-soft)}.front-scope h2{font-size:24px;line-height:1.15;margin:0 0 3mm}.front-scope p{font-size:16px} +.scope-table-wrap{margin:3mm 0;overflow:hidden}.scope-table{width:100%;border-collapse:collapse;font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;line-height:1.22}.scope-table th,.scope-table td{border:0;border-bottom:1px solid #888;padding:4px 7px;text-align:left;vertical-align:top}.scope-table th{border-bottom:2px solid #555;background:#f3f3f3;font-weight:700}.scope-table th:first-child,.scope-table td:first-child{width:7%}.scope-table--task th:nth-child(2),.scope-table--task td:nth-child(2){width:11%}.scope-table--task th:nth-child(4),.scope-table--task td:nth-child(4){width:19%}.scope-table--task th:nth-child(5),.scope-table--task td:nth-child(5){width:14%}.scope-table--task th:nth-child(6),.scope-table--task td:nth-child(6){width:10%}.scope-table tr.scope-row--key td{font-weight:700;background:#f4f4f4;border-top:2px solid #111;border-bottom:2px solid #111}.scope-table tr.scope-row--key td:first-child{border-left:2px solid #111}.scope-table tr.scope-row--key td:last-child{border-right:2px solid #111}.scope-table td p{font:inherit;margin:0}.scope-table code{font-size:inherit}.scope-status{display:inline-block;white-space:nowrap;border:1px solid #aaa;border-radius:2px;padding:1px 4px;font-weight:600;background:#f5f5f5}.scope-status[data-status="in-progress"]{border-color:#307da1;background:#eaf5fa;color:#174f69}.scope-status[data-status="ready"]{border-color:#4c7d56;background:#edf6ee;color:#295b33}.scope-status[data-status="draft"],.scope-status[data-status="review"]{border-color:#9a7932;background:#faf4e5;color:#694f16}.scope-status[data-status="blocked"]{border-color:#9c4b4b;background:#faeded;color:#712f2f} +.card-section h2{font-size:24px;line-height:1.15;margin:0 0 4mm}.section-heading{display:flex;align-items:flex-start;gap:0}.section-heading .section-index{font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:23px;margin-right:12mm;font-weight:400}.section-heading .section-title{min-width:0}.task-identity{margin-left:auto;padding-left:22px;text-align:right;font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:10px;line-height:1.25;color:#555;white-space:nowrap}.task-identity strong,.task-identity-name,.task-identity code{display:block}.task-identity strong{font-size:12px;color:#111}.task-identity-name{max-width:240px;overflow:hidden;text-overflow:ellipsis}.task-identity code{font-size:9px;color:#777} +p{font-size:16px;margin:0 0 3mm}.pdf-main ul{font-size:16px;margin:2mm 0 3mm 7mm;padding:0}.pdf-main li{margin:1.2mm 0} +.pdf-margin{position:absolute;top:34mm;width:18mm;font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:5px;line-height:1.12;color:#333;max-height:245mm;overflow:hidden}.pdf-margin.left{left:2mm;text-align:left}.pdf-margin.right{right:2mm;text-align:left} +.margin-title{font-weight:700;color:#666;margin-bottom:3px;letter-spacing:.02em}.margin-list{display:flex;flex-direction:column;align-items:flex-start}.margin-tag{display:block;margin:0 0 1px;padding:0;border:0;background:transparent;font:inherit;line-height:inherit;text-align:left;cursor:pointer;margin-left:var(--indent)}.margin-tag.we{color:var(--we)}.margin-tag.og,.margin-tag.tech{color:var(--og)}.margin-tag.kw{color:var(--kw)}.margin-tag .local{color:#777}.margin-tag:hover{text-decoration:underline}.margin-empty{color:#aaa} +.step-map{font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;line-height:1.3;color:#5c5c5c;background:transparent;border:0;border-top:1px solid #cfcfcf;border-bottom:1px solid #e0e0e0;border-radius:0;padding:8px 0;margin:0 0 16px}.step-map summary{cursor:pointer;font-weight:400;color:#555} +.tree-legend{display:flex;flex-wrap:wrap;gap:4px;margin:7px 0}.steps{display:grid;gap:6px}.step-row{border-top:1px dashed #c9c9c9;padding-top:6px}.step-row:first-child{border-top:0}.step-title span{font-weight:700;margin-right:7px}.step-trees{display:flex;flex-wrap:wrap;gap:4px;margin-top:3px}.step-refs{display:flex;flex-wrap:wrap;gap:6px;margin-top:3px}.step-refs a{font-size:10px;color:#666} +.tree-token{font:10px/1 "Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;border:1px solid #c8c8c8;background:#fff;border-radius:3px;padding:3px 5px;cursor:pointer}.tree-token.active{background:#111;color:white;border-color:#111} +.equation{display:grid;grid-template-columns:1fr auto;align-items:center;gap:14px;margin:18px 0;padding:4px 0;background:transparent;border:0}.equation .display{font-size:17px}.equation-number{font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace;font-size:15px} +figure{margin:24px 0;text-align:center}figure img{max-width:100%;display:block;margin:0 auto;border:0;border-radius:0}figcaption{font-size:14px;color:#444;margin-top:8px;text-align:left}.card-asset.asset-screenshot img{border:1px solid #aaa}.card-asset a{display:block;cursor:zoom-in} +.table-wrap{overflow:auto;margin:14px 0}table{border-collapse:collapse;width:100%;font-size:16px}th,td{border:1px solid #111;padding:5px 8px;text-align:left;vertical-align:top}th{font-weight:400;background:white} +.empty-check{display:inline-block;width:1em;height:1em;border:1px solid #111;vertical-align:-.12em}.answer-line,.dotfill{display:block;border-bottom:1px dotted #111;height:1.45em;min-width:10em}.write-row{display:block;min-height:2.8em} +code{font-family:"Latin Modern Mono",ui-monospace,SFMono-Regular,Consolas,monospace}.tasks{font-size:16px}.task-legacy{margin:2.5mm 0}.task-legacy>strong{display:block;font:700 10px/1.2 "Latin Modern Mono",ui-monospace,monospace;color:#555}.task-flow{border:1px solid #222;margin:1mm 0 2mm;background:#fff}.task-flow-header{display:grid;grid-template-columns:auto 1fr auto;align-items:baseline;gap:6px;padding:5px 7px;background:#eaeaea;border-bottom:1px solid #222}.task-flow-header span{font:700 10px/1.2 "Latin Modern Mono",ui-monospace,monospace;color:#222}.task-flow-header h3{font-size:18px;margin:0}.task-flow-header code{font-size:8px;color:#555}.task-block{margin:6px 7px;padding:5px 6px;border:1px solid #777;background:#fff}.task-block header,.task-exercise header{display:flex;align-items:baseline;gap:5px;margin-bottom:3px}.task-block header span,.task-exercise header span{font:700 9px/1.2 "Latin Modern Mono",ui-monospace,monospace;text-transform:uppercase;color:#555}.task-block h4,.task-exercise h4{font-size:16px;margin:0}.block-steps{margin:4px 0 0 16px!important;padding:0;display:grid;gap:3px}.block-steps li{margin:0;border:1px solid #b9b9b9;padding:4px 5px;background:#fff}.block-steps li::marker{font-family:"Latin Modern Mono",ui-monospace,monospace;font-weight:700}.block-steps li p:last-child{margin-bottom:0}.task-exercise{margin:6px 7px;border:1px solid #777;border-left:1px solid #222;padding:5px 6px;background:#fafafa}.task-exercise header{flex-wrap:wrap}.exercise-based-on{margin-left:auto;font-size:8px;color:#777}.exercise-evidence{border-top:1px dashed #bbb;margin-top:5px;padding-top:4px}.exercise-evidence>strong{font-size:11px;text-transform:uppercase}.exercise-criterion{font-size:13px;margin:4px 0 0}.task-flow>footer{padding:5px 7px;border-top:1px solid #222;font-size:12px;background:#f6f6f6}.task-conclusion{margin:8px 0;padding:7px 9px;border:1px solid #315f78;background:#f3f9fc}.task-conclusion>strong{display:block;margin-bottom:3px;font:700 10px/1.2 "Latin Modern Mono",ui-monospace,monospace;color:#315f78}.task-conclusion p{margin:0}.we-block{border-top:1px solid #ddd;padding:8px 0}.we-block summary{cursor:pointer}.dictionary-sheet .pdf-margin{display:none} +.inspector{position:fixed;right:14px;bottom:14px;width:min(360px,calc(100vw - 28px));max-height:46vh;overflow:auto;background:white;border:1px solid #cfcfcf;box-shadow:0 4px 18px rgba(0,0,0,.18);border-radius:6px;padding:12px;font-family:system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px;z-index:30}.inspector h2{font-size:14px;margin:0 0 8px}.inspector .empty{color:#777}.tree-card h3{font-size:15px;margin:6px 0}.tree-card code{display:inline-block;margin:4px 0}.tree-card .line{font-weight:700}.tree-card .line.OG{color:var(--og)}.tree-card .line.TECH{color:var(--tech)}.kw-list{padding-left:18px}.kw-list code{color:var(--kw)} +@media(max-width:900px){.sheet{display:block;min-height:0}.sheet-content{min-height:0;padding:26px 18px}.pdf-margin{position:static;max-height:none;overflow:visible;margin:10px 0;padding:8px;border:1px solid #ddd}.pdf-margin.left{border-left:3px solid var(--tech)}.pdf-margin.right{border-left:3px solid var(--og)}.hero .byline{float:none;margin:0 0 14px}.meta{grid-template-columns:1fr}.section-heading{flex-wrap:wrap}.task-identity{width:100%;padding:6px 0 0}.inspector{position:static;width:auto;max-height:none;margin:0 12px 18px}.topbar{position:sticky;top:0}.viewer-zoom-status{font-size:16px}} +@media print{html,body{background:white}.topbar,.inspector{display:none}.paper{padding:0}.sheet{position:relative;display:block;width:auto;min-height:0;margin:0;box-shadow:none;break-after:page;padding:0;overflow:visible;zoom:1!important}.sheet-content{position:relative;width:auto;min-height:0;padding:0;transform:none!important}.sheet:last-child{break-after:auto}.resource-header-enabled .sheet-content{padding:0}.sheet-content>.page-resource-header,.sheet-content>.page-resource-footer{display:none!important}.pdf-margin{position:absolute;top:0;width:18mm;max-height:none;overflow:hidden;margin:0;padding:0;border:0}.pdf-margin.left{left:-19.5mm}.pdf-margin.right{right:-19.5mm}.task-block,.task-exercise,.block-steps li,figure{break-inside:avoid}} + +.page-resource-header{width:100%;height:24mm;min-height:24mm;max-height:24mm;background:#fff;color:#111;overflow:hidden;font-family:ui-monospace,SFMono-Regular,"Latin Modern Mono",Consolas,monospace;line-height:1.08} +.resource-header-table{width:100%;height:100%;border-collapse:collapse;table-layout:fixed;border:1px solid #111}.resource-header-table>tbody>tr{height:100%}.resource-header-center{height:100%;padding:0;vertical-align:middle;overflow:hidden}.resource-header-grid{display:grid;grid-template-rows:3fr 3fr 3fr 2fr 2fr;width:100%;height:100%}.resource-header-row{display:grid;min-width:0;min-height:0}.resource-header-field{display:flex;flex-direction:column;align-items:stretch;justify-content:space-between;gap:.08mm;min-width:0;padding:.28mm .55mm;border-right:1px solid #888;border-bottom:1px solid #aaa;overflow:hidden;white-space:nowrap}.resource-header-row .resource-header-field:last-child{border-right:0}.resource-header-row:last-child .resource-header-field{border-bottom:0}.resource-header-field a{display:flex;flex-direction:column;align-items:flex-start;gap:.08mm;min-width:0;color:#111;text-decoration:none}.resource-label{align-self:flex-start;font:800 3.2pt/1 ui-monospace,SFMono-Regular,"Latin Modern Mono",Consolas,monospace;text-transform:uppercase;flex:none}.resource-value{align-self:flex-end;max-width:100%;text-align:right;font:700 6.4pt/1 ui-monospace,SFMono-Regular,"Latin Modern Mono",Consolas,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.resource-header-field--title .resource-value{font-size:7.7pt;font-weight:800}.resource-header-field--emphasis .resource-value{font-size:6.6pt;font-weight:800}.resource-header-field--compact{gap:.05mm;padding:.2mm .2mm}.resource-header-field--compact .resource-label{font-size:2.85pt}.resource-header-field--compact .resource-value{font-size:4.45pt}.resource-header-field--identity{display:flex;flex-direction:column;align-items:stretch;justify-content:center;gap:.15mm;padding:.12mm .45mm;font:700 3.55pt/1.1 ui-monospace,SFMono-Regular,"Latin Modern Mono",Consolas,monospace}.resource-header-field--identity>span{display:flex;align-items:baseline;gap:.3mm;min-width:0}.resource-header-field--identity b{font-weight:800;flex:none}.resource-header-field--identity .resource-identity-value{margin-left:auto;text-align:right}.resource-header-field--identity .resource-author{margin-left:auto;text-align:right}.resource-header-field--url-row{display:flex;align-items:stretch;justify-content:center;padding:.2mm .55mm}.resource-header-field--url-row a{display:block;overflow:hidden;text-overflow:clip;font:700 4.9pt/1.05 ui-monospace,SFMono-Regular,"Latin Modern Mono",Consolas,monospace}.resource-header-field--url-row b{font-weight:800} +.resource-qr-cell{height:100%;padding:0;vertical-align:middle;overflow:hidden}.resource-qr-cell--left{border-right:1px solid #777}.resource-qr-cell--right{border-left:1px solid #777}.resource-qr{box-sizing:border-box;width:100%;height:100%;display:flex;align-items:center;justify-content:center;padding:.4mm;color:#333;text-decoration:none}.resource-qr .title-qr{display:block;width:22mm;height:22mm;max-width:22mm;max-height:22mm;flex:none}.resource-qr-empty{text-align:center}.resource-qr-empty span,.resource-qr-empty strong{display:block;font:600 5pt/1.15 ui-monospace,SFMono-Regular,Consolas,monospace} + +@page{ + size:A4; + margin:29mm 21.5mm 5mm; + @top-left{content:"";width:24mm;height:24mm;box-sizing:border-box;padding:0;border:1px solid #111;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:22mm 22mm;vertical-align:middle;text-align:center;font:0/0 sans-serif;color:transparent;background-image:url("data:image/svg+xml;base64,PHN2ZyBjbGFzcz0idGl0bGUtcXIiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iS29kIFFSOiBodHRwczovL3pzbC1naXRlYS5tcGFiaS5wbC9lZHUtZnJlZXJ0b3MtYy9sYWItcnYzMmktZnJlZXJ0b3MtYy1ldmVudC1ncm91cHMiIHZlcnNpb249JzEuMScgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyB4bWxuczp4bGluaz0naHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluaycgd2lkdGg9Jzg1LjAzOTE5NnB0JyBoZWlnaHQ9Jzg1LjAzOTE5NnB0JyB2aWV3Qm94PSctNzIuMDAwMDA0IC03Mi4wMDAwMDQgODUuMDM5MTk2IDg1LjAzOTE5Nic+CjxnIGlkPSdwYWdlMSc+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjkuNDIzMDU5JyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY2Ljg0NjExNCcgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02NC4yNjkxNjgnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjEuNjkyMjIzJyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTU5LjExNTI3OCcgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01Ni41MzgzMzInIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDguODA3NDk2JyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQ2LjIzMDU1MScgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00MS4wNzY2NicgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zMy4zNDU4MjQnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjUuNjE0OTg4JyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIzLjAzODA0MycgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNS4zMDcyMDcnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTIuNzMwMjYxJyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQuOTk5NDI1JyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIuNDIyNDgnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScuMTU0NDY1JyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMi43MzE0MTEnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSc1LjMwODM1NicgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzcuODg1MzAxJyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMTAuNDYyMjQ3JyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTY5LjQyMzA1OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01Ni41MzgzMzInIHk9Jy02OS40MjMwNTknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTEuMzg0NDQyJyB5PSctNjkuNDIzMDU5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQzLjY1MzYwNScgeT0nLTY5LjQyMzA1OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00MS4wNzY2NicgeT0nLTY5LjQyMzA1OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zOC40OTk3MTUnIHk9Jy02OS40MjMwNTknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzMuMzQ1ODI0JyB5PSctNjkuNDIzMDU5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTI4LjE5MTkzMycgeT0nLTY5LjQyMzA1OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNy44ODQxNTInIHk9Jy02OS40MjMwNTknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTIuNzMwMjYxJyB5PSctNjkuNDIzMDU5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEwLjE1MzMxNicgeT0nLTY5LjQyMzA1OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ljk5OTQyNScgeT0nLTY5LjQyMzA1OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzEwLjQ2MjI0NycgeT0nLTY5LjQyMzA1OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy02Ni44NDYxMTQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjYuODQ2MTE0JyB5PSctNjYuODQ2MTE0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY0LjI2OTE2OCcgeT0nLTY2Ljg0NjExNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02MS42OTIyMjMnIHk9Jy02Ni44NDYxMTQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTYuNTM4MzMyJyB5PSctNjYuODQ2MTE0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTUxLjM4NDQ0MicgeT0nLTY2Ljg0NjExNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00OC44MDc0OTYnIHk9Jy02Ni44NDYxMTQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDYuMjMwNTUxJyB5PSctNjYuODQ2MTE0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQxLjA3NjY2JyB5PSctNjYuODQ2MTE0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTM1LjkyMjc2OScgeT0nLTY2Ljg0NjExNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zMy4zNDU4MjQnIHk9Jy02Ni44NDYxMTQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjguMTkxOTMzJyB5PSctNjYuODQ2MTE0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIwLjQ2MTA5NycgeT0nLTY2Ljg0NjExNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ljk5OTQyNScgeT0nLTY2Ljg0NjExNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy4xNTQ0NjUnIHk9Jy02Ni44NDYxMTQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nLTY2Ljg0NjExNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzUuMzA4MzU2JyB5PSctNjYuODQ2MTE0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMTAuNDYyMjQ3JyB5PSctNjYuODQ2MTE0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTY0LjI2OTE2OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02Ni44NDYxMTQnIHk9Jy02NC4yNjkxNjgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjQuMjY5MTY4JyB5PSctNjQuMjY5MTY4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTYxLjY5MjIyMycgeT0nLTY0LjI2OTE2OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01Ni41MzgzMzInIHk9Jy02NC4yNjkxNjgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDguODA3NDk2JyB5PSctNjQuMjY5MTY4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQ2LjIzMDU1MScgeT0nLTY0LjI2OTE2OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00My42NTM2MDUnIHk9Jy02NC4yNjkxNjgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDEuMDc2NjYnIHk9Jy02NC4yNjkxNjgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjUuNjE0OTg4JyB5PSctNjQuMjY5MTY4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTE1LjMwNzIwNycgeT0nLTY0LjI2OTE2OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ljk5OTQyNScgeT0nLTY0LjI2OTE2OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy4xNTQ0NjUnIHk9Jy02NC4yNjkxNjgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nLTY0LjI2OTE2OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzUuMzA4MzU2JyB5PSctNjQuMjY5MTY4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMTAuNDYyMjQ3JyB5PSctNjQuMjY5MTY4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTYxLjY5MjIyMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02Ni44NDYxMTQnIHk9Jy02MS42OTIyMjMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjQuMjY5MTY4JyB5PSctNjEuNjkyMjIzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTYxLjY5MjIyMycgeT0nLTYxLjY5MjIyMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01Ni41MzgzMzInIHk9Jy02MS42OTIyMjMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTEuMzg0NDQyJyB5PSctNjEuNjkyMjIzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQzLjY1MzYwNScgeT0nLTYxLjY5MjIyMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zNS45MjI3NjknIHk9Jy02MS42OTIyMjMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzMuMzQ1ODI0JyB5PSctNjEuNjkyMjIzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTMwLjc2ODg3OScgeT0nLTYxLjY5MjIyMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yNS42MTQ5ODgnIHk9Jy02MS42OTIyMjMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTIuNzMwMjYxJyB5PSctNjEuNjkyMjIzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEwLjE1MzMxNicgeT0nLTYxLjY5MjIyMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ljk5OTQyNScgeT0nLTYxLjY5MjIyMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy4xNTQ0NjUnIHk9Jy02MS42OTIyMjMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nLTYxLjY5MjIyMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzUuMzA4MzU2JyB5PSctNjEuNjkyMjIzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMTAuNDYyMjQ3JyB5PSctNjEuNjkyMjIzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTU5LjExNTI3OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01Ni41MzgzMzInIHk9Jy01OS4xMTUyNzgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTEuMzg0NDQyJyB5PSctNTkuMTE1Mjc4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQ4LjgwNzQ5NicgeT0nLTU5LjExNTI3OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zOC40OTk3MTUnIHk9Jy01OS4xMTUyNzgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzMuMzQ1ODI0JyB5PSctNTkuMTE1Mjc4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTI4LjE5MTkzMycgeT0nLTU5LjExNTI3OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yMy4wMzgwNDMnIHk9Jy01OS4xMTUyNzgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTcuODg0MTUyJyB5PSctNTkuMTE1Mjc4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTE1LjMwNzIwNycgeT0nLTU5LjExNTI3OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ljk5OTQyNScgeT0nLTU5LjExNTI3OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzEwLjQ2MjI0NycgeT0nLTU5LjExNTI3OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy01Ni41MzgzMzInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjkuNDIzMDU5JyB5PSctNTYuNTM4MzMyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY2Ljg0NjExNCcgeT0nLTU2LjUzODMzMicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02NC4yNjkxNjgnIHk9Jy01Ni41MzgzMzInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjEuNjkyMjIzJyB5PSctNTYuNTM4MzMyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTU5LjExNTI3OCcgeT0nLTU2LjUzODMzMicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01Ni41MzgzMzInIHk9Jy01Ni41MzgzMzInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTEuMzg0NDQyJyB5PSctNTYuNTM4MzMyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQ2LjIzMDU1MScgeT0nLTU2LjUzODMzMicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00MS4wNzY2NicgeT0nLTU2LjUzODMzMicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zNS45MjI3NjknIHk9Jy01Ni41MzgzMzInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzAuNzY4ODc5JyB5PSctNTYuNTM4MzMyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTI1LjYxNDk4OCcgeT0nLTU2LjUzODMzMicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yMC40NjEwOTcnIHk9Jy01Ni41MzgzMzInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTUuMzA3MjA3JyB5PSctNTYuNTM4MzMyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEwLjE1MzMxNicgeT0nLTU2LjUzODMzMicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ljk5OTQyNScgeT0nLTU2LjUzODMzMicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yLjQyMjQ4JyB5PSctNTYuNTM4MzMyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLjE1NDQ2NScgeT0nLTU2LjUzODMzMicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzIuNzMxNDExJyB5PSctNTYuNTM4MzMyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nNS4zMDgzNTYnIHk9Jy01Ni41MzgzMzInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSc3Ljg4NTMwMScgeT0nLTU2LjUzODMzMicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzEwLjQ2MjI0NycgeT0nLTU2LjUzODMzMicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01MS4zODQ0NDInIHk9Jy01My45NjEzODcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDguODA3NDk2JyB5PSctNTMuOTYxMzg3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQzLjY1MzYwNScgeT0nLTUzLjk2MTM4NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zNS45MjI3NjknIHk9Jy01My45NjEzODcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzMuMzQ1ODI0JyB5PSctNTMuOTYxMzg3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTI4LjE5MTkzMycgeT0nLTUzLjk2MTM4NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yMy4wMzgwNDMnIHk9Jy01My45NjEzODcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjAuNDYxMDk3JyB5PSctNTMuOTYxMzg3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTE3Ljg4NDE1MicgeT0nLTUzLjk2MTM4NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNS4zMDcyMDcnIHk9Jy01My45NjEzODcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTIuNzMwMjYxJyB5PSctNTMuOTYxMzg3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEwLjE1MzMxNicgeT0nLTUzLjk2MTM4NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy01MS4zODQ0NDInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjkuNDIzMDU5JyB5PSctNTEuMzg0NDQyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY0LjI2OTE2OCcgeT0nLTUxLjM4NDQ0MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01Ni41MzgzMzInIHk9Jy01MS4zODQ0NDInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTMuOTYxMzg3JyB5PSctNTEuMzg0NDQyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQ2LjIzMDU1MScgeT0nLTUxLjM4NDQ0MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00My42NTM2MDUnIHk9Jy01MS4zODQ0NDInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzguNDk5NzE1JyB5PSctNTEuMzg0NDQyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTM1LjkyMjc2OScgeT0nLTUxLjM4NDQ0MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yNS42MTQ5ODgnIHk9Jy01MS4zODQ0NDInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjMuMDM4MDQzJyB5PSctNTEuMzg0NDQyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTE3Ljg4NDE1MicgeT0nLTUxLjM4NDQ0MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xMC4xNTMzMTYnIHk9Jy01MS4zODQ0NDInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNC45OTk0MjUnIHk9Jy01MS4zODQ0NDInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMi40MjI0OCcgeT0nLTUxLjM4NDQ0MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy4xNTQ0NjUnIHk9Jy01MS4zODQ0NDInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSc1LjMwODM1NicgeT0nLTUxLjM4NDQ0MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzcuODg1MzAxJyB5PSctNTEuMzg0NDQyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY5LjQyMzA1OScgeT0nLTQ4LjgwNzQ5NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02NC4yNjkxNjgnIHk9Jy00OC44MDc0OTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTkuMTE1Mjc4JyB5PSctNDguODA3NDk2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTUzLjk2MTM4NycgeT0nLTQ4LjgwNzQ5NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01MS4zODQ0NDInIHk9Jy00OC44MDc0OTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDYuMjMwNTUxJyB5PSctNDguODA3NDk2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQxLjA3NjY2JyB5PSctNDguODA3NDk2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTM4LjQ5OTcxNScgeT0nLTQ4LjgwNzQ5NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zMC43Njg4NzknIHk9Jy00OC44MDc0OTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTcuODg0MTUyJyB5PSctNDguODA3NDk2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTE1LjMwNzIwNycgeT0nLTQ4LjgwNzQ5NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xMC4xNTMzMTYnIHk9Jy00OC44MDc0OTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNy41NzYzNzEnIHk9Jy00OC44MDc0OTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNC45OTk0MjUnIHk9Jy00OC44MDc0OTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nLTQ4LjgwNzQ5NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzEwLjQ2MjI0NycgeT0nLTQ4LjgwNzQ5NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02Ni44NDYxMTQnIHk9Jy00Ni4yMzA1NTEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjEuNjkyMjIzJyB5PSctNDYuMjMwNTUxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTU5LjExNTI3OCcgeT0nLTQ2LjIzMDU1MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01Ni41MzgzMzInIHk9Jy00Ni4yMzA1NTEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTMuOTYxMzg3JyB5PSctNDYuMjMwNTUxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTUxLjM4NDQ0MicgeT0nLTQ2LjIzMDU1MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00OC44MDc0OTYnIHk9Jy00Ni4yMzA1NTEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDYuMjMwNTUxJyB5PSctNDYuMjMwNTUxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTM1LjkyMjc2OScgeT0nLTQ2LjIzMDU1MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yMy4wMzgwNDMnIHk9Jy00Ni4yMzA1NTEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTUuMzA3MjA3JyB5PSctNDYuMjMwNTUxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEwLjE1MzMxNicgeT0nLTQ2LjIzMDU1MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ljk5OTQyNScgeT0nLTQ2LjIzMDU1MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzIuNzMxNDExJyB5PSctNDYuMjMwNTUxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMTAuNDYyMjQ3JyB5PSctNDYuMjMwNTUxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTQzLjY1MzYwNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02NC4yNjkxNjgnIHk9Jy00My42NTM2MDUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTMuOTYxMzg3JyB5PSctNDMuNjUzNjA1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTUxLjM4NDQ0MicgeT0nLTQzLjY1MzYwNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00MS4wNzY2NicgeT0nLTQzLjY1MzYwNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zOC40OTk3MTUnIHk9Jy00My42NTM2MDUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjUuNjE0OTg4JyB5PSctNDMuNjUzNjA1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIwLjQ2MTA5NycgeT0nLTQzLjY1MzYwNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNy44ODQxNTInIHk9Jy00My42NTM2MDUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTUuMzA3MjA3JyB5PSctNDMuNjUzNjA1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQuOTk5NDI1JyB5PSctNDMuNjUzNjA1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nNy44ODUzMDEnIHk9Jy00My42NTM2MDUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScxMC40NjIyNDcnIHk9Jy00My42NTM2MDUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjkuNDIzMDU5JyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjEuNjkyMjIzJyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTYuNTM4MzMyJyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTMuOTYxMzg3JyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTEuMzg0NDQyJyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzguNDk5NzE1JyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzUuOTIyNzY5JyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjguMTkxOTMzJyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjAuNDYxMDk3JyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTcuODg0MTUyJyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTUuMzA3MjA3JyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNy41NzYzNzEnIHk9Jy00MS4wNzY2NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ljk5OTQyNScgeT0nLTQxLjA3NjY2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIuNDIyNDgnIHk9Jy00MS4wNzY2NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzIuNzMxNDExJyB5PSctNDEuMDc2NjYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScxMC40NjIyNDcnIHk9Jy00MS4wNzY2NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy0zOC40OTk3MTUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjYuODQ2MTE0JyB5PSctMzguNDk5NzE1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY0LjI2OTE2OCcgeT0nLTM4LjQ5OTcxNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01OS4xMTUyNzgnIHk9Jy0zOC40OTk3MTUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTMuOTYxMzg3JyB5PSctMzguNDk5NzE1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTUxLjM4NDQ0MicgeT0nLTM4LjQ5OTcxNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00OC44MDc0OTYnIHk9Jy0zOC40OTk3MTUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDYuMjMwNTUxJyB5PSctMzguNDk5NzE1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQxLjA3NjY2JyB5PSctMzguNDk5NzE1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTM4LjQ5OTcxNScgeT0nLTM4LjQ5OTcxNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yOC4xOTE5MzMnIHk9Jy0zOC40OTk3MTUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjUuNjE0OTg4JyB5PSctMzguNDk5NzE1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIzLjAzODA0MycgeT0nLTM4LjQ5OTcxNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yMC40NjEwOTcnIHk9Jy0zOC40OTk3MTUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTUuMzA3MjA3JyB5PSctMzguNDk5NzE1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEwLjE1MzMxNicgeT0nLTM4LjQ5OTcxNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ljk5OTQyNScgeT0nLTM4LjQ5OTcxNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy4xNTQ0NjUnIHk9Jy0zOC40OTk3MTUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nLTM4LjQ5OTcxNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzUuMzA4MzU2JyB5PSctMzguNDk5NzE1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nNy44ODUzMDEnIHk9Jy0zOC40OTk3MTUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScxMC40NjIyNDcnIHk9Jy0zOC40OTk3MTUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjkuNDIzMDU5JyB5PSctMzUuOTIyNzY5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY0LjI2OTE2OCcgeT0nLTM1LjkyMjc2OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02MS42OTIyMjMnIHk9Jy0zNS45MjI3NjknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTYuNTM4MzMyJyB5PSctMzUuOTIyNzY5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQ4LjgwNzQ5NicgeT0nLTM1LjkyMjc2OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ni4yMzA1NTEnIHk9Jy0zNS45MjI3NjknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDMuNjUzNjA1JyB5PSctMzUuOTIyNzY5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTM1LjkyMjc2OScgeT0nLTM1LjkyMjc2OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yNS42MTQ5ODgnIHk9Jy0zNS45MjI3NjknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjAuNDYxMDk3JyB5PSctMzUuOTIyNzY5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEyLjczMDI2MScgeT0nLTM1LjkyMjc2OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xMC4xNTMzMTYnIHk9Jy0zNS45MjI3NjknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMi40MjI0OCcgeT0nLTM1LjkyMjc2OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy4xNTQ0NjUnIHk9Jy0zNS45MjI3NjknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nLTM1LjkyMjc2OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzcuODg1MzAxJyB5PSctMzUuOTIyNzY5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTMzLjM0NTgyNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02OS40MjMwNTknIHk9Jy0zMy4zNDU4MjQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTkuMTE1Mjc4JyB5PSctMzMuMzQ1ODI0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTUzLjk2MTM4NycgeT0nLTMzLjM0NTgyNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00OC44MDc0OTYnIHk9Jy0zMy4zNDU4MjQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDYuMjMwNTUxJyB5PSctMzMuMzQ1ODI0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQxLjA3NjY2JyB5PSctMzMuMzQ1ODI0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTM4LjQ5OTcxNScgeT0nLTMzLjM0NTgyNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zNS45MjI3NjknIHk9Jy0zMy4zNDU4MjQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzMuMzQ1ODI0JyB5PSctMzMuMzQ1ODI0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTMwLjc2ODg3OScgeT0nLTMzLjM0NTgyNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yOC4xOTE5MzMnIHk9Jy0zMy4zNDU4MjQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjMuMDM4MDQzJyB5PSctMzMuMzQ1ODI0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIwLjQ2MTA5NycgeT0nLTMzLjM0NTgyNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNy44ODQxNTInIHk9Jy0zMy4zNDU4MjQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTUuMzA3MjA3JyB5PSctMzMuMzQ1ODI0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEwLjE1MzMxNicgeT0nLTMzLjM0NTgyNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yLjQyMjQ4JyB5PSctMzMuMzQ1ODI0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLjE1NDQ2NScgeT0nLTMzLjM0NTgyNCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzIuNzMxNDExJyB5PSctMzMuMzQ1ODI0JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nNy44ODUzMDEnIHk9Jy0zMy4zNDU4MjQnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PSctMzAuNzY4ODc5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY5LjQyMzA1OScgeT0nLTMwLjc2ODg3OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02NC4yNjkxNjgnIHk9Jy0zMC43Njg4NzknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTkuMTE1Mjc4JyB5PSctMzAuNzY4ODc5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTU2LjUzODMzMicgeT0nLTMwLjc2ODg3OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00My42NTM2MDUnIHk9Jy0zMC43Njg4NzknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDEuMDc2NjYnIHk9Jy0zMC43Njg4NzknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjguMTkxOTMzJyB5PSctMzAuNzY4ODc5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIwLjQ2MTA5NycgeT0nLTMwLjc2ODg3OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNy44ODQxNTInIHk9Jy0zMC43Njg4NzknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTUuMzA3MjA3JyB5PSctMzAuNzY4ODc5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEyLjczMDI2MScgeT0nLTMwLjc2ODg3OScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xMC4xNTMzMTYnIHk9Jy0zMC43Njg4NzknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNy41NzYzNzEnIHk9Jy0zMC43Njg4NzknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNC45OTk0MjUnIHk9Jy0zMC43Njg4NzknIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScuMTU0NDY1JyB5PSctMzAuNzY4ODc5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMTAuNDYyMjQ3JyB5PSctMzAuNzY4ODc5JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTI4LjE5MTkzMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00OC44MDc0OTYnIHk9Jy0yOC4xOTE5MzMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDMuNjUzNjA1JyB5PSctMjguMTkxOTMzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQxLjA3NjY2JyB5PSctMjguMTkxOTMzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTM4LjQ5OTcxNScgeT0nLTI4LjE5MTkzMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zNS45MjI3NjknIHk9Jy0yOC4xOTE5MzMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzAuNzY4ODc5JyB5PSctMjguMTkxOTMzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTI1LjYxNDk4OCcgeT0nLTI4LjE5MTkzMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yMC40NjEwOTcnIHk9Jy0yOC4xOTE5MzMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTcuODg0MTUyJyB5PSctMjguMTkxOTMzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEyLjczMDI2MScgeT0nLTI4LjE5MTkzMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy03LjU3NjM3MScgeT0nLTI4LjE5MTkzMycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzIuNzMxNDExJyB5PSctMjguMTkxOTMzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nNS4zMDgzNTYnIHk9Jy0yOC4xOTE5MzMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PSctMjUuNjE0OTg4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY5LjQyMzA1OScgeT0nLTI1LjYxNDk4OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02NC4yNjkxNjgnIHk9Jy0yNS42MTQ5ODgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjEuNjkyMjIzJyB5PSctMjUuNjE0OTg4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTU2LjUzODMzMicgeT0nLTI1LjYxNDk4OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01My45NjEzODcnIHk9Jy0yNS42MTQ5ODgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTEuMzg0NDQyJyB5PSctMjUuNjE0OTg4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQ2LjIzMDU1MScgeT0nLTI1LjYxNDk4OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00MS4wNzY2NicgeT0nLTI1LjYxNDk4OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yOC4xOTE5MzMnIHk9Jy0yNS42MTQ5ODgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTcuODg0MTUyJyB5PSctMjUuNjE0OTg4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcuNTc2MzcxJyB5PSctMjUuNjE0OTg4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIuNDIyNDgnIHk9Jy0yNS42MTQ5ODgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nLTI1LjYxNDk4OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzcuODg1MzAxJyB5PSctMjUuNjE0OTg4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMTAuNDYyMjQ3JyB5PSctMjUuNjE0OTg4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTIzLjAzODA0MycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02NC4yNjkxNjgnIHk9Jy0yMy4wMzgwNDMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjEuNjkyMjIzJyB5PSctMjMuMDM4MDQzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTU5LjExNTI3OCcgeT0nLTIzLjAzODA0MycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01My45NjEzODcnIHk9Jy0yMy4wMzgwNDMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDguODA3NDk2JyB5PSctMjMuMDM4MDQzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTM1LjkyMjc2OScgeT0nLTIzLjAzODA0MycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zMy4zNDU4MjQnIHk9Jy0yMy4wMzgwNDMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjguMTkxOTMzJyB5PSctMjMuMDM4MDQzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIzLjAzODA0MycgeT0nLTIzLjAzODA0MycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNy44ODQxNTInIHk9Jy0yMy4wMzgwNDMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTUuMzA3MjA3JyB5PSctMjMuMDM4MDQzJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEyLjczMDI2MScgeT0nLTIzLjAzODA0MycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ljk5OTQyNScgeT0nLTIzLjAzODA0MycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy4xNTQ0NjUnIHk9Jy0yMy4wMzgwNDMnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nLTIzLjAzODA0MycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy0yMC40NjEwOTcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjYuODQ2MTE0JyB5PSctMjAuNDYxMDk3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTYxLjY5MjIyMycgeT0nLTIwLjQ2MTA5NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01OS4xMTUyNzgnIHk9Jy0yMC40NjEwOTcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTYuNTM4MzMyJyB5PSctMjAuNDYxMDk3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTUzLjk2MTM4NycgeT0nLTIwLjQ2MTA5NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00OC44MDc0OTYnIHk9Jy0yMC40NjEwOTcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDYuMjMwNTUxJyB5PSctMjAuNDYxMDk3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQzLjY1MzYwNScgeT0nLTIwLjQ2MTA5NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zOC40OTk3MTUnIHk9Jy0yMC40NjEwOTcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzUuOTIyNzY5JyB5PSctMjAuNDYxMDk3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTMwLjc2ODg3OScgeT0nLTIwLjQ2MTA5NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yNS42MTQ5ODgnIHk9Jy0yMC40NjEwOTcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjAuNDYxMDk3JyB5PSctMjAuNDYxMDk3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTE3Ljg4NDE1MicgeT0nLTIwLjQ2MTA5NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xMC4xNTMzMTYnIHk9Jy0yMC40NjEwOTcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNy41NzYzNzEnIHk9Jy0yMC40NjEwOTcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nLTIwLjQ2MTA5NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzcuODg1MzAxJyB5PSctMjAuNDYxMDk3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY5LjQyMzA1OScgeT0nLTE3Ljg4NDE1MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02Ni44NDYxMTQnIHk9Jy0xNy44ODQxNTInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjQuMjY5MTY4JyB5PSctMTcuODg0MTUyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTU5LjExNTI3OCcgeT0nLTE3Ljg4NDE1MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01MS4zODQ0NDInIHk9Jy0xNy44ODQxNTInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDEuMDc2NjYnIHk9Jy0xNy44ODQxNTInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzguNDk5NzE1JyB5PSctMTcuODg0MTUyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTM1LjkyMjc2OScgeT0nLTE3Ljg4NDE1MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zMC43Njg4NzknIHk9Jy0xNy44ODQxNTInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjguMTkxOTMzJyB5PSctMTcuODg0MTUyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTI1LjYxNDk4OCcgeT0nLTE3Ljg4NDE1MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yMC40NjEwOTcnIHk9Jy0xNy44ODQxNTInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTcuODg0MTUyJyB5PSctMTcuODg0MTUyJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTE1LjMwNzIwNycgeT0nLTE3Ljg4NDE1MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xMC4xNTMzMTYnIHk9Jy0xNy44ODQxNTInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNy41NzYzNzEnIHk9Jy0xNy44ODQxNTInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNC45OTk0MjUnIHk9Jy0xNy44ODQxNTInIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSc1LjMwODM1NicgeT0nLTE3Ljg4NDE1MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzEwLjQ2MjI0NycgeT0nLTE3Ljg4NDE1MicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy0xNS4zMDcyMDcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjkuNDIzMDU5JyB5PSctMTUuMzA3MjA3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTYxLjY5MjIyMycgeT0nLTE1LjMwNzIwNycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01OS4xMTUyNzgnIHk9Jy0xNS4zMDcyMDcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTYuNTM4MzMyJyB5PSctMTUuMzA3MjA3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTUzLjk2MTM4NycgeT0nLTE1LjMwNzIwNycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yOC4xOTE5MzMnIHk9Jy0xNS4zMDcyMDcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjUuNjE0OTg4JyB5PSctMTUuMzA3MjA3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIzLjAzODA0MycgeT0nLTE1LjMwNzIwNycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNy44ODQxNTInIHk9Jy0xNS4zMDcyMDcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTAuMTUzMzE2JyB5PSctMTUuMzA3MjA3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIuNDIyNDgnIHk9Jy0xNS4zMDcyMDcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nLTE1LjMwNzIwNycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzEwLjQ2MjI0NycgeT0nLTE1LjMwNzIwNycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02OS40MjMwNTknIHk9Jy0xMi43MzAyNjEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjQuMjY5MTY4JyB5PSctMTIuNzMwMjYxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTYxLjY5MjIyMycgeT0nLTEyLjczMDI2MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01MS4zODQ0NDInIHk9Jy0xMi43MzAyNjEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDguODA3NDk2JyB5PSctMTIuNzMwMjYxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQzLjY1MzYwNScgeT0nLTEyLjczMDI2MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00MS4wNzY2NicgeT0nLTEyLjczMDI2MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zOC40OTk3MTUnIHk9Jy0xMi43MzAyNjEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzUuOTIyNzY5JyB5PSctMTIuNzMwMjYxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTI1LjYxNDk4OCcgeT0nLTEyLjczMDI2MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNy44ODQxNTInIHk9Jy0xMi43MzAyNjEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTIuNzMwMjYxJyB5PSctMTIuNzMwMjYxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQuOTk5NDI1JyB5PSctMTIuNzMwMjYxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLjE1NDQ2NScgeT0nLTEyLjczMDI2MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzIuNzMxNDExJyB5PSctMTIuNzMwMjYxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nNy44ODUzMDEnIHk9Jy0xMi43MzAyNjEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScxMC40NjIyNDcnIHk9Jy0xMi43MzAyNjEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PSctMTAuMTUzMzE2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY2Ljg0NjExNCcgeT0nLTEwLjE1MzMxNicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01OS4xMTUyNzgnIHk9Jy0xMC4xNTMzMTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTYuNTM4MzMyJyB5PSctMTAuMTUzMzE2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQ2LjIzMDU1MScgeT0nLTEwLjE1MzMxNicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00MS4wNzY2NicgeT0nLTEwLjE1MzMxNicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zOC40OTk3MTUnIHk9Jy0xMC4xNTMzMTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjguMTkxOTMzJyB5PSctMTAuMTUzMzE2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTI1LjYxNDk4OCcgeT0nLTEwLjE1MzMxNicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yMy4wMzgwNDMnIHk9Jy0xMC4xNTMzMTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjAuNDYxMDk3JyB5PSctMTAuMTUzMzE2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTE3Ljg4NDE1MicgeT0nLTEwLjE1MzMxNicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xMC4xNTMzMTYnIHk9Jy0xMC4xNTMzMTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNy41NzYzNzEnIHk9Jy0xMC4xNTMzMTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNC45OTk0MjUnIHk9Jy0xMC4xNTMzMTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMi40MjI0OCcgeT0nLTEwLjE1MzMxNicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy4xNTQ0NjUnIHk9Jy0xMC4xNTMzMTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nLTEwLjE1MzMxNicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01MS4zODQ0NDInIHk9Jy03LjU3NjM3MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00MS4wNzY2NicgeT0nLTcuNTc2MzcxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTI4LjE5MTkzMycgeT0nLTcuNTc2MzcxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTI1LjYxNDk4OCcgeT0nLTcuNTc2MzcxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIzLjAzODA0MycgeT0nLTcuNTc2MzcxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIwLjQ2MTA5NycgeT0nLTcuNTc2MzcxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTE3Ljg4NDE1MicgeT0nLTcuNTc2MzcxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTE1LjMwNzIwNycgeT0nLTcuNTc2MzcxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEyLjczMDI2MScgeT0nLTcuNTc2MzcxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEwLjE1MzMxNicgeT0nLTcuNTc2MzcxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLjE1NDQ2NScgeT0nLTcuNTc2MzcxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMi43MzE0MTEnIHk9Jy03LjU3NjM3MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzUuMzA4MzU2JyB5PSctNy41NzYzNzEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScxMC40NjIyNDcnIHk9Jy03LjU3NjM3MScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02OS40MjMwNTknIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02Ni44NDYxMTQnIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02NC4yNjkxNjgnIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02MS42OTIyMjMnIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01OS4xMTUyNzgnIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01Ni41MzgzMzInIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01MS4zODQ0NDInIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ni4yMzA1NTEnIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00My42NTM2MDUnIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zNS45MjI3NjknIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zMC43Njg4NzknIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yNS42MTQ5ODgnIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xMC4xNTMzMTYnIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ljk5OTQyNScgeT0nLTQuOTk5NDI1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLjE1NDQ2NScgeT0nLTQuOTk5NDI1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMi43MzE0MTEnIHk9Jy00Ljk5OTQyNScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzcuODg1MzAxJyB5PSctNC45OTk0MjUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PSctMi40MjI0OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01Ni41MzgzMzInIHk9Jy0yLjQyMjQ4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQ4LjgwNzQ5NicgeT0nLTIuNDIyNDgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDMuNjUzNjA1JyB5PSctMi40MjI0OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00MS4wNzY2NicgeT0nLTIuNDIyNDgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMzguNDk5NzE1JyB5PSctMi40MjI0OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zNS45MjI3NjknIHk9Jy0yLjQyMjQ4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTMwLjc2ODg3OScgeT0nLTIuNDIyNDgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjguMTkxOTMzJyB5PSctMi40MjI0OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yMy4wMzgwNDMnIHk9Jy0yLjQyMjQ4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIwLjQ2MTA5NycgeT0nLTIuNDIyNDgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTcuODg0MTUyJyB5PSctMi40MjI0OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNS4zMDcyMDcnIHk9Jy0yLjQyMjQ4JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEwLjE1MzMxNicgeT0nLTIuNDIyNDgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScuMTU0NDY1JyB5PSctMi40MjI0OCcgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9JzEwLjQ2MjI0NycgeT0nLTIuNDIyNDgnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PScuMTU0NDY1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY2Ljg0NjExNCcgeT0nLjE1NDQ2NScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02NC4yNjkxNjgnIHk9Jy4xNTQ0NjUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjEuNjkyMjIzJyB5PScuMTU0NDY1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTU2LjUzODMzMicgeT0nLjE1NDQ2NScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00OC44MDc0OTYnIHk9Jy4xNTQ0NjUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDYuMjMwNTUxJyB5PScuMTU0NDY1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQzLjY1MzYwNScgeT0nLjE1NDQ2NScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00MS4wNzY2NicgeT0nLjE1NDQ2NScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yOC4xOTE5MzMnIHk9Jy4xNTQ0NjUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjMuMDM4MDQzJyB5PScuMTU0NDY1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTE3Ljg4NDE1MicgeT0nLjE1NDQ2NScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNS4zMDcyMDcnIHk9Jy4xNTQ0NjUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTAuMTUzMzE2JyB5PScuMTU0NDY1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcuNTc2MzcxJyB5PScuMTU0NDY1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQuOTk5NDI1JyB5PScuMTU0NDY1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIuNDIyNDgnIHk9Jy4xNTQ0NjUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScuMTU0NDY1JyB5PScuMTU0NDY1JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMi43MzE0MTEnIHk9Jy4xNTQ0NjUnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSc3Ljg4NTMwMScgeT0nLjE1NDQ2NScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9JzIuNzMxNDExJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTY2Ljg0NjExNCcgeT0nMi43MzE0MTEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjQuMjY5MTY4JyB5PScyLjczMTQxMScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02MS42OTIyMjMnIHk9JzIuNzMxNDExJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTU2LjUzODMzMicgeT0nMi43MzE0MTEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTEuMzg0NDQyJyB5PScyLjczMTQxMScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00OC44MDc0OTYnIHk9JzIuNzMxNDExJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQ2LjIzMDU1MScgeT0nMi43MzE0MTEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDMuNjUzNjA1JyB5PScyLjczMTQxMScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zNS45MjI3NjknIHk9JzIuNzMxNDExJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTMwLjc2ODg3OScgeT0nMi43MzE0MTEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjguMTkxOTMzJyB5PScyLjczMTQxMScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNy44ODQxNTInIHk9JzIuNzMxNDExJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEyLjczMDI2MScgeT0nMi43MzE0MTEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNC45OTk0MjUnIHk9JzIuNzMxNDExJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIuNDIyNDgnIHk9JzIuNzMxNDExJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLjE1NDQ2NScgeT0nMi43MzE0MTEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSc3Ljg4NTMwMScgeT0nMi43MzE0MTEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScxMC40NjIyNDcnIHk9JzIuNzMxNDExJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nNS4zMDgzNTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNjYuODQ2MTE0JyB5PSc1LjMwODM1NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02NC4yNjkxNjgnIHk9JzUuMzA4MzU2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTYxLjY5MjIyMycgeT0nNS4zMDgzNTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTYuNTM4MzMyJyB5PSc1LjMwODM1NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00OC44MDc0OTYnIHk9JzUuMzA4MzU2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQxLjA3NjY2JyB5PSc1LjMwODM1NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zOC40OTk3MTUnIHk9JzUuMzA4MzU2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTI1LjYxNDk4OCcgeT0nNS4zMDgzNTYnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjAuNDYxMDk3JyB5PSc1LjMwODM1NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xMC4xNTMzMTYnIHk9JzUuMzA4MzU2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTcuNTc2MzcxJyB5PSc1LjMwODM1NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy4xNTQ0NjUnIHk9JzUuMzA4MzU2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMi43MzE0MTEnIHk9JzUuMzA4MzU2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nNy44ODUzMDEnIHk9JzUuMzA4MzU2JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nMTAuNDYyMjQ3JyB5PSc1LjMwODM1NicgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9JzcuODg1MzAxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTU2LjUzODMzMicgeT0nNy44ODUzMDEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNTEuMzg0NDQyJyB5PSc3Ljg4NTMwMScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ni4yMzA1NTEnIHk9JzcuODg1MzAxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTQzLjY1MzYwNScgeT0nNy44ODUzMDEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctNDEuMDc2NjYnIHk9JzcuODg1MzAxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTMzLjM0NTgyNCcgeT0nNy44ODUzMDEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMjguMTkxOTMzJyB5PSc3Ljg4NTMwMScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yNS42MTQ5ODgnIHk9JzcuODg1MzAxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTIzLjAzODA0MycgeT0nNy44ODUzMDEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMTcuODg0MTUyJyB5PSc3Ljg4NTMwMScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xMi43MzAyNjEnIHk9JzcuODg1MzAxJyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nLTEwLjE1MzMxNicgeT0nNy44ODUzMDEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PSctMi40MjI0OCcgeT0nNy44ODUzMDEnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScuMTU0NDY1JyB5PSc3Ljg4NTMwMScgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02OS40MjMwNTknIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02Ni44NDYxMTQnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02NC4yNjkxNjgnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy02MS42OTIyMjMnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01OS4xMTUyNzgnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01Ni41MzgzMzInIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy01MS4zODQ0NDInIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00OC44MDc0OTYnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00Ni4yMzA1NTEnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy00My42NTM2MDUnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zOC40OTk3MTUnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zNS45MjI3NjknIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zMy4zNDU4MjQnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0zMC43Njg4NzknIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yNS42MTQ5ODgnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yMC40NjEwOTcnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xNy44ODQxNTInIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0xMC4xNTMzMTYnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjxyZWN0IHg9Jy0yLjQyMjQ4JyB5PScxMC40NjIyNDcnIGhlaWdodD0nMi41NzY5NDUnIHdpZHRoPScyLjU3Njk0NScvPgo8cmVjdCB4PScyLjczMTQxMScgeT0nMTAuNDYyMjQ3JyBoZWlnaHQ9JzIuNTc2OTQ1JyB3aWR0aD0nMi41NzY5NDUnLz4KPHJlY3QgeD0nNy44ODUzMDEnIHk9JzEwLjQ2MjI0NycgaGVpZ2h0PScyLjU3Njk0NScgd2lkdGg9JzIuNTc2OTQ1Jy8+CjwvZz4KPC9zdmc+")} + @top-center{content:" TITLE VER. DATETIME \A \A Event Groups: ALL-ready i bariera v00.01 2026-07-19T00:00:00+02:00 \A PROJECT SERIES CARD SHEET \A \A Freestanding C nad FreeRTOS FreeRTOS C 09/15 " counter(page) "/" counter(pages) " \A SUBJ. PROG. CORE SCOPE LEVEL POS. GITEA UUID e5c4c0f3-3dd0-5168-9945-14d48744ec6f\A CARD UUID 3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f AUTHOR M. Pabiszczak\A Inf. — — — — — \A \A GITEA https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-c-event-groups \A \A HTML https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/3f4c1b8b-d54b-58d5-b92d-87a7233cbd9f ";width:119mm;height:24mm;box-sizing:border-box;padding:.2mm .7mm 0;border-block:1px solid #111;background-color:#fff;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMTkgMjQiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiPjxyZWN0IHdpZHRoPSIxMTkiIGhlaWdodD0iMjQiIGZpbGw9IndoaXRlIi8+PHBhdGggZD0iTTAgNS41Mzg0NkgxMTkgTTAgMTEuMDc2OUgxMTkgTTAgMTYuNjE1NEgxMTkgTTAgMjAuMzA3N0gxMTkgTTU5LjUgMFY1LjUzODQ2IE03Ny41IDBWNS41Mzg0NiBNNTkuNSA1LjUzODQ2VjExLjA3NjkgTTg5LjUgNS41Mzg0NlYxMS4wNzY5IE0xMDYuNSA1LjUzODQ2VjExLjA3NjkgTTcgMTEuMDc2OVYxNi42MTU0IE0xMyAxMS4wNzY5VjE2LjYxNTQgTTMwLjUgMTEuMDc2OVYxNi42MTU0IE00MS41IDExLjA3NjlWMTYuNjE1NCBNNTAuNSAxMS4wNzY5VjE2LjYxNTQgTTU5LjUgMTEuMDc2OVYxNi42MTU0IiBmaWxsPSJub25lIiBzdHJva2U9IiM4ODgiIHN0cm9rZS13aWR0aD0iMC4xNCIvPjwvc3ZnPg==");background-repeat:no-repeat;background-position:center;background-size:100% 100%;white-space:pre;vertical-align:top;text-align:left;font:700 5.2px/1.81538mm ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:-.02px;color:#111;overflow:hidden} + @top-right{content:"";width:24mm;height:24mm;box-sizing:border-box;padding:0;border:1px solid #111;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:22mm 22mm;vertical-align:middle;text-align:center;font:0/0 sans-serif;color:transparent;background-image:url("data:image/svg+xml;base64,PHN2ZyBjbGFzcz0idGl0bGUtcXIiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iS29kIFFSOiBodHRwczovL2RjZTdmYjlkLTdiMmYtNWQ0OS05NmEyLTNhMzBkMzA3MGI4NC5tcGFiaS5wbC8zZjRjMWI4Yi1kNTRiLTU4ZDUtYjkyZC04N2E3MjMzY2JkOWYiIHZlcnNpb249JzEuMScgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyB4bWxuczp4bGluaz0naHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluaycgd2lkdGg9Jzg1LjAzOTIyN3B0JyBoZWlnaHQ9Jzg1LjAzOTIyN3B0JyB2aWV3Qm94PSctNzIuMDAwMDA0IC03Mi4wMDAwMDQgODUuMDM5MjI3IDg1LjAzOTIyNyc+CjxnIGlkPSdwYWdlMSc+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjkuNzAxNjQ3JyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY3LjQwMzI4OScgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02NS4xMDQ5MzInIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjIuODA2NTc0JyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTYwLjUwODIxNycgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01OC4yMDk4NTknIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDkuMDE2NDI5JyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ0LjQxOTcxNScgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNS4yMjYyODUnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjguMzMxMjEyJyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE5LjEzNzc4MicgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNi44Mzk0MjUnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTIuMjQyNzEnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMy4wNDkyOCcgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0uNzUwOTIyJyB5PSctNzIuMDAwMDA0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMS41NDc0MzUnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSczLjg0NTc5MycgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzYuMTQ0MTUnIHk9Jy03Mi4wMDAwMDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSc4LjQ0MjUwNycgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEwLjc0MDg2NScgeT0nLTcyLjAwMDAwNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy02OS43MDE2NDcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTguMjA5ODU5JyB5PSctNjkuNzAxNjQ3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUzLjYxMzE0NCcgeT0nLTY5LjcwMTY0NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00OS4wMTY0MjknIHk9Jy02OS43MDE2NDcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDIuMTIxMzU3JyB5PSctNjkuNzAxNjQ3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM3LjUyNDY0MicgeT0nLTY5LjcwMTY0NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNS4yMjYyODUnIHk9Jy02OS43MDE2NDcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjYuMDMyODU1JyB5PSctNjkuNzAxNjQ3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIxLjQzNjE0JyB5PSctNjkuNzAxNjQ3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE0LjU0MTA2NycgeT0nLTY5LjcwMTY0NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy05Ljk0NDM1MicgeT0nLTY5LjcwMTY0NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03LjY0NTk5NScgeT0nLTY5LjcwMTY0NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zLjA0OTI4JyB5PSctNjkuNzAxNjQ3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMTAuNzQwODY1JyB5PSctNjkuNzAxNjQ3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTY3LjQwMzI4OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Ny40MDMyODknIHk9Jy02Ny40MDMyODknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjUuMTA0OTMyJyB5PSctNjcuNDAzMjg5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTYyLjgwNjU3NCcgeT0nLTY3LjQwMzI4OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01OC4yMDk4NTknIHk9Jy02Ny40MDMyODknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDQuNDE5NzE1JyB5PSctNjcuNDAzMjg5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI2LjAzMjg1NScgeT0nLTY3LjQwMzI4OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMy43MzQ0OTcnIHk9Jy02Ny40MDMyODknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTYuODM5NDI1JyB5PSctNjcuNDAzMjg5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTEyLjI0MjcxJyB5PSctNjcuNDAzMjg5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTkuOTQ0MzUyJyB5PSctNjcuNDAzMjg5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcuNjQ1OTk1JyB5PSctNjcuNDAzMjg5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMuMDQ5MjgnIHk9Jy02Ny40MDMyODknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxLjU0NzQzNScgeT0nLTY3LjQwMzI4OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzMuODQ1NzkzJyB5PSctNjcuNDAzMjg5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nNi4xNDQxNScgeT0nLTY3LjQwMzI4OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEwLjc0MDg2NScgeT0nLTY3LjQwMzI4OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy02NS4xMDQ5MzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjcuNDAzMjg5JyB5PSctNjUuMTA0OTMyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY1LjEwNDkzMicgeT0nLTY1LjEwNDkzMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Mi44MDY1NzQnIHk9Jy02NS4xMDQ5MzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTguMjA5ODU5JyB5PSctNjUuMTA0OTMyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUzLjYxMzE0NCcgeT0nLTY1LjEwNDkzMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01MS4zMTQ3ODcnIHk9Jy02NS4xMDQ5MzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDkuMDE2NDI5JyB5PSctNjUuMTA0OTMyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ0LjQxOTcxNScgeT0nLTY1LjEwNDkzMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Mi4xMjEzNTcnIHk9Jy02NS4xMDQ5MzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzkuODIzJyB5PSctNjUuMTA0OTMyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM3LjUyNDY0MicgeT0nLTY1LjEwNDkzMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMi45Mjc5MjcnIHk9Jy02NS4xMDQ5MzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzAuNjI5NTcnIHk9Jy02NS4xMDQ5MzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjguMzMxMjEyJyB5PSctNjUuMTA0OTMyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIxLjQzNjE0JyB5PSctNjUuMTA0OTMyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE5LjEzNzc4MicgeT0nLTY1LjEwNDkzMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNi44Mzk0MjUnIHk9Jy02NS4xMDQ5MzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTQuNTQxMDY3JyB5PSctNjUuMTA0OTMyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTEyLjI0MjcxJyB5PSctNjUuMTA0OTMyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMuMDQ5MjgnIHk9Jy02NS4xMDQ5MzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxLjU0NzQzNScgeT0nLTY1LjEwNDkzMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzMuODQ1NzkzJyB5PSctNjUuMTA0OTMyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nNi4xNDQxNScgeT0nLTY1LjEwNDkzMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEwLjc0MDg2NScgeT0nLTY1LjEwNDkzMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy02Mi44MDY1NzQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjcuNDAzMjg5JyB5PSctNjIuODA2NTc0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY1LjEwNDkzMicgeT0nLTYyLjgwNjU3NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Mi44MDY1NzQnIHk9Jy02Mi44MDY1NzQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTguMjA5ODU5JyB5PSctNjIuODA2NTc0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUxLjMxNDc4NycgeT0nLTYyLjgwNjU3NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNS4yMjYyODUnIHk9Jy02Mi44MDY1NzQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzIuOTI3OTI3JyB5PSctNjIuODA2NTc0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI4LjMzMTIxMicgeT0nLTYyLjgwNjU3NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMy43MzQ0OTcnIHk9Jy02Mi44MDY1NzQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjEuNDM2MTQnIHk9Jy02Mi44MDY1NzQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTkuMTM3NzgyJyB5PSctNjIuODA2NTc0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMuMDQ5MjgnIHk9Jy02Mi44MDY1NzQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxLjU0NzQzNScgeT0nLTYyLjgwNjU3NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzMuODQ1NzkzJyB5PSctNjIuODA2NTc0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nNi4xNDQxNScgeT0nLTYyLjgwNjU3NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEwLjc0MDg2NScgeT0nLTYyLjgwNjU3NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy02MC41MDgyMTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTguMjA5ODU5JyB5PSctNjAuNTA4MjE3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUzLjYxMzE0NCcgeT0nLTYwLjUwODIxNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01MS4zMTQ3ODcnIHk9Jy02MC41MDgyMTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDYuNzE4MDcyJyB5PSctNjAuNTA4MjE3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ0LjQxOTcxNScgeT0nLTYwLjUwODIxNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zOS44MjMnIHk9Jy02MC41MDgyMTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzcuNTI0NjQyJyB5PSctNjAuNTA4MjE3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM1LjIyNjI4NScgeT0nLTYwLjUwODIxNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMC42Mjk1NycgeT0nLTYwLjUwODIxNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMy43MzQ0OTcnIHk9Jy02MC41MDgyMTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTYuODM5NDI1JyB5PSctNjAuNTA4MjE3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTkuOTQ0MzUyJyB5PSctNjAuNTA4MjE3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMuMDQ5MjgnIHk9Jy02MC41MDgyMTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxMC43NDA4NjUnIHk9Jy02MC41MDgyMTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PSctNTguMjA5ODU5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY5LjcwMTY0NycgeT0nLTU4LjIwOTg1OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Ny40MDMyODknIHk9Jy01OC4yMDk4NTknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjUuMTA0OTMyJyB5PSctNTguMjA5ODU5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTYyLjgwNjU3NCcgeT0nLTU4LjIwOTg1OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02MC41MDgyMTcnIHk9Jy01OC4yMDk4NTknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTguMjA5ODU5JyB5PSctNTguMjA5ODU5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUzLjYxMzE0NCcgeT0nLTU4LjIwOTg1OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00OS4wMTY0MjknIHk9Jy01OC4yMDk4NTknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDQuNDE5NzE1JyB5PSctNTguMjA5ODU5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM5LjgyMycgeT0nLTU4LjIwOTg1OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNS4yMjYyODUnIHk9Jy01OC4yMDk4NTknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzAuNjI5NTcnIHk9Jy01OC4yMDk4NTknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjYuMDMyODU1JyB5PSctNTguMjA5ODU5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIxLjQzNjE0JyB5PSctNTguMjA5ODU5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE2LjgzOTQyNScgeT0nLTU4LjIwOTg1OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xMi4yNDI3MScgeT0nLTU4LjIwOTg1OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03LjY0NTk5NScgeT0nLTU4LjIwOTg1OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zLjA0OTI4JyB5PSctNTguMjA5ODU5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLS43NTA5MjInIHk9Jy01OC4yMDk4NTknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxLjU0NzQzNScgeT0nLTU4LjIwOTg1OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzMuODQ1NzkzJyB5PSctNTguMjA5ODU5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nNi4xNDQxNScgeT0nLTU4LjIwOTg1OScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzguNDQyNTA3JyB5PSctNTguMjA5ODU5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMTAuNzQwODY1JyB5PSctNTguMjA5ODU5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUxLjMxNDc4NycgeT0nLTU1LjkxMTUwMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00OS4wMTY0MjknIHk9Jy01NS45MTE1MDInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDYuNzE4MDcyJyB5PSctNTUuOTExNTAyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMwLjYyOTU3JyB5PSctNTUuOTExNTAyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI4LjMzMTIxMicgeT0nLTU1LjkxMTUwMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMy43MzQ0OTcnIHk9Jy01NS45MTE1MDInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTkuMTM3NzgyJyB5PSctNTUuOTExNTAyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE2LjgzOTQyNScgeT0nLTU1LjkxMTUwMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xMi4yNDI3MScgeT0nLTU1LjkxMTUwMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy01My42MTMxNDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjkuNzAxNjQ3JyB5PSctNTMuNjEzMTQ0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY3LjQwMzI4OScgeT0nLTUzLjYxMzE0NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02NS4xMDQ5MzInIHk9Jy01My42MTMxNDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjIuODA2NTc0JyB5PSctNTMuNjEzMTQ0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU4LjIwOTg1OScgeT0nLTUzLjYxMzE0NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01NS45MTE1MDInIHk9Jy01My42MTMxNDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTMuNjEzMTQ0JyB5PSctNTMuNjEzMTQ0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUxLjMxNDc4NycgeT0nLTUzLjYxMzE0NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Ni43MTgwNzInIHk9Jy01My42MTMxNDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDIuMTIxMzU3JyB5PSctNTMuNjEzMTQ0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM3LjUyNDY0MicgeT0nLTUzLjYxMzE0NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNS4yMjYyODUnIHk9Jy01My42MTMxNDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzIuOTI3OTI3JyB5PSctNTMuNjEzMTQ0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIxLjQzNjE0JyB5PSctNTMuNjEzMTQ0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE2LjgzOTQyNScgeT0nLTUzLjYxMzE0NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNC41NDEwNjcnIHk9Jy01My42MTMxNDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctOS45NDQzNTInIHk9Jy01My42MTMxNDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNy42NDU5OTUnIHk9Jy01My42MTMxNDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNS4zNDc2MzcnIHk9Jy01My42MTMxNDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctLjc1MDkyMicgeT0nLTUzLjYxMzE0NCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzMuODQ1NzkzJyB5PSctNTMuNjEzMTQ0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nOC40NDI1MDcnIHk9Jy01My42MTMxNDQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PSctNTEuMzE0Nzg3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY5LjcwMTY0NycgeT0nLTUxLjMxNDc4NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Mi44MDY1NzQnIHk9Jy01MS4zMTQ3ODcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjAuNTA4MjE3JyB5PSctNTEuMzE0Nzg3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUzLjYxMzE0NCcgeT0nLTUxLjMxNDc4NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00OS4wMTY0MjknIHk9Jy01MS4zMTQ3ODcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDQuNDE5NzE1JyB5PSctNTEuMzE0Nzg3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQyLjEyMTM1NycgeT0nLTUxLjMxNDc4NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zOS44MjMnIHk9Jy01MS4zMTQ3ODcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzAuNjI5NTcnIHk9Jy01MS4zMTQ3ODcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjguMzMxMjEyJyB5PSctNTEuMzE0Nzg3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI2LjAzMjg1NScgeT0nLTUxLjMxNDc4NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xOS4xMzc3ODInIHk9Jy01MS4zMTQ3ODcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTIuMjQyNzEnIHk9Jy01MS4zMTQ3ODcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctLjc1MDkyMicgeT0nLTUxLjMxNDc4NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzMuODQ1NzkzJyB5PSctNTEuMzE0Nzg3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nNi4xNDQxNScgeT0nLTUxLjMxNDc4NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzguNDQyNTA3JyB5PSctNTEuMzE0Nzg3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY5LjcwMTY0NycgeT0nLTQ5LjAxNjQyOScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Ny40MDMyODknIHk9Jy00OS4wMTY0MjknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjAuNTA4MjE3JyB5PSctNDkuMDE2NDI5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU4LjIwOTg1OScgeT0nLTQ5LjAxNjQyOScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01My42MTMxNDQnIHk9Jy00OS4wMTY0MjknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTEuMzE0Nzg3JyB5PSctNDkuMDE2NDI5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ5LjAxNjQyOScgeT0nLTQ5LjAxNjQyOScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNy41MjQ2NDInIHk9Jy00OS4wMTY0MjknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzUuMjI2Mjg1JyB5PSctNDkuMDE2NDI5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIzLjczNDQ5NycgeT0nLTQ5LjAxNjQyOScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMS40MzYxNCcgeT0nLTQ5LjAxNjQyOScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNC41NDEwNjcnIHk9Jy00OS4wMTY0MjknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctOS45NDQzNTInIHk9Jy00OS4wMTY0MjknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNy42NDU5OTUnIHk9Jy00OS4wMTY0MjknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMy4wNDkyOCcgeT0nLTQ5LjAxNjQyOScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0uNzUwOTIyJyB5PSctNDkuMDE2NDI5JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMS41NDc0MzUnIHk9Jy00OS4wMTY0MjknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSczLjg0NTc5MycgeT0nLTQ5LjAxNjQyOScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzYuMTQ0MTUnIHk9Jy00OS4wMTY0MjknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxMC43NDA4NjUnIHk9Jy00OS4wMTY0MjknIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PSctNDYuNzE4MDcyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY3LjQwMzI4OScgeT0nLTQ2LjcxODA3MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02NS4xMDQ5MzInIHk9Jy00Ni43MTgwNzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTUuOTExNTAyJyB5PSctNDYuNzE4MDcyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ5LjAxNjQyOScgeT0nLTQ2LjcxODA3MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00NC40MTk3MTUnIHk9Jy00Ni43MTgwNzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzkuODIzJyB5PSctNDYuNzE4MDcyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMwLjYyOTU3JyB5PSctNDYuNzE4MDcyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI2LjAzMjg1NScgeT0nLTQ2LjcxODA3MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMy43MzQ0OTcnIHk9Jy00Ni43MTgwNzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTYuODM5NDI1JyB5PSctNDYuNzE4MDcyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTEyLjI0MjcxJyB5PSctNDYuNzE4MDcyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTkuOTQ0MzUyJyB5PSctNDYuNzE4MDcyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLS43NTA5MjInIHk9Jy00Ni43MTgwNzInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSczLjg0NTc5MycgeT0nLTQ2LjcxODA3MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzguNDQyNTA3JyB5PSctNDYuNzE4MDcyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY3LjQwMzI4OScgeT0nLTQ0LjQxOTcxNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Mi44MDY1NzQnIHk9Jy00NC40MTk3MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTguMjA5ODU5JyB5PSctNDQuNDE5NzE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU1LjkxMTUwMicgeT0nLTQ0LjQxOTcxNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01My42MTMxNDQnIHk9Jy00NC40MTk3MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTEuMzE0Nzg3JyB5PSctNDQuNDE5NzE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ0LjQxOTcxNScgeT0nLTQ0LjQxOTcxNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Mi4xMjEzNTcnIHk9Jy00NC40MTk3MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzkuODIzJyB5PSctNDQuNDE5NzE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM3LjUyNDY0MicgeT0nLTQ0LjQxOTcxNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNS4yMjYyODUnIHk9Jy00NC40MTk3MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzIuOTI3OTI3JyB5PSctNDQuNDE5NzE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMwLjYyOTU3JyB5PSctNDQuNDE5NzE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI2LjAzMjg1NScgeT0nLTQ0LjQxOTcxNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNi44Mzk0MjUnIHk9Jy00NC40MTk3MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTQuNTQxMDY3JyB5PSctNDQuNDE5NzE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTkuOTQ0MzUyJyB5PSctNDQuNDE5NzE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMuMDQ5MjgnIHk9Jy00NC40MTk3MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctLjc1MDkyMicgeT0nLTQ0LjQxOTcxNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEuNTQ3NDM1JyB5PSctNDQuNDE5NzE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMy44NDU3OTMnIHk9Jy00NC40MTk3MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSc2LjE0NDE1JyB5PSctNDQuNDE5NzE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMTAuNzQwODY1JyB5PSctNDQuNDE5NzE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTQyLjEyMTM1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Ny40MDMyODknIHk9Jy00Mi4xMjEzNTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjAuNTA4MjE3JyB5PSctNDIuMTIxMzU3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI4LjMzMTIxMicgeT0nLTQyLjEyMTM1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xOS4xMzc3ODInIHk9Jy00Mi4xMjEzNTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTIuMjQyNzEnIHk9Jy00Mi4xMjEzNTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNy42NDU5OTUnIHk9Jy00Mi4xMjEzNTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNS4zNDc2MzcnIHk9Jy00Mi4xMjEzNTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSc2LjE0NDE1JyB5PSctNDIuMTIxMzU3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nOC40NDI1MDcnIHk9Jy00Mi4xMjEzNTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjUuMTA0OTMyJyB5PSctMzkuODIzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU4LjIwOTg1OScgeT0nLTM5LjgyMycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01NS45MTE1MDInIHk9Jy0zOS44MjMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTEuMzE0Nzg3JyB5PSctMzkuODIzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ5LjAxNjQyOScgeT0nLTM5LjgyMycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Ni43MTgwNzInIHk9Jy0zOS44MjMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDQuNDE5NzE1JyB5PSctMzkuODIzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQyLjEyMTM1NycgeT0nLTM5LjgyMycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zOS44MjMnIHk9Jy0zOS44MjMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzcuNTI0NjQyJyB5PSctMzkuODIzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI2LjAzMjg1NScgeT0nLTM5LjgyMycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMS40MzYxNCcgeT0nLTM5LjgyMycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNi44Mzk0MjUnIHk9Jy0zOS44MjMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctOS45NDQzNTInIHk9Jy0zOS44MjMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMy4wNDkyOCcgeT0nLTM5LjgyMycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0uNzUwOTIyJyB5PSctMzkuODIzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nNi4xNDQxNScgeT0nLTM5LjgyMycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzguNDQyNTA3JyB5PSctMzkuODIzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMTAuNzQwODY1JyB5PSctMzkuODIzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTM3LjUyNDY0MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02NS4xMDQ5MzInIHk9Jy0zNy41MjQ2NDInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjIuODA2NTc0JyB5PSctMzcuNTI0NjQyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU1LjkxMTUwMicgeT0nLTM3LjUyNDY0MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00OS4wMTY0MjknIHk9Jy0zNy41MjQ2NDInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDYuNzE4MDcyJyB5PSctMzcuNTI0NjQyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI2LjAzMjg1NScgeT0nLTM3LjUyNDY0MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMy43MzQ0OTcnIHk9Jy0zNy41MjQ2NDInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjEuNDM2MTQnIHk9Jy0zNy41MjQ2NDInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTYuODM5NDI1JyB5PSctMzcuNTI0NjQyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcuNjQ1OTk1JyB5PSctMzcuNTI0NjQyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLS43NTA5MjInIHk9Jy0zNy41MjQ2NDInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxLjU0NzQzNScgeT0nLTM3LjUyNDY0MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzMuODQ1NzkzJyB5PSctMzcuNTI0NjQyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMTAuNzQwODY1JyB5PSctMzcuNTI0NjQyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY3LjQwMzI4OScgeT0nLTM1LjIyNjI4NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02MC41MDgyMTcnIHk9Jy0zNS4yMjYyODUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTguMjA5ODU5JyB5PSctMzUuMjI2Mjg1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU1LjkxMTUwMicgeT0nLTM1LjIyNjI4NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01MS4zMTQ3ODcnIHk9Jy0zNS4yMjYyODUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDYuNzE4MDcyJyB5PSctMzUuMjI2Mjg1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM3LjUyNDY0MicgeT0nLTM1LjIyNjI4NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNS4yMjYyODUnIHk9Jy0zNS4yMjYyODUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzIuOTI3OTI3JyB5PSctMzUuMjI2Mjg1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMwLjYyOTU3JyB5PSctMzUuMjI2Mjg1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI2LjAzMjg1NScgeT0nLTM1LjIyNjI4NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNC41NDEwNjcnIHk9Jy0zNS4yMjYyODUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTIuMjQyNzEnIHk9Jy0zNS4yMjYyODUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctOS45NDQzNTInIHk9Jy0zNS4yMjYyODUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNy42NDU5OTUnIHk9Jy0zNS4yMjYyODUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNS4zNDc2MzcnIHk9Jy0zNS4yMjYyODUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMy4wNDkyOCcgeT0nLTM1LjIyNjI4NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEuNTQ3NDM1JyB5PSctMzUuMjI2Mjg1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMy44NDU3OTMnIHk9Jy0zNS4yMjYyODUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSc2LjE0NDE1JyB5PSctMzUuMjI2Mjg1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMTAuNzQwODY1JyB5PSctMzUuMjI2Mjg1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY5LjcwMTY0NycgeT0nLTMyLjkyNzkyNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Ny40MDMyODknIHk9Jy0zMi45Mjc5MjcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjUuMTA0OTMyJyB5PSctMzIuOTI3OTI3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU1LjkxMTUwMicgeT0nLTMyLjkyNzkyNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00NC40MTk3MTUnIHk9Jy0zMi45Mjc5MjcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDIuMTIxMzU3JyB5PSctMzIuOTI3OTI3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMyLjkyNzkyNycgeT0nLTMyLjkyNzkyNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMC42Mjk1NycgeT0nLTMyLjkyNzkyNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yOC4zMzEyMTInIHk9Jy0zMi45Mjc5MjcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjMuNzM0NDk3JyB5PSctMzIuOTI3OTI3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIxLjQzNjE0JyB5PSctMzIuOTI3OTI3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE5LjEzNzc4MicgeT0nLTMyLjkyNzkyNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzYuMTQ0MTUnIHk9Jy0zMi45Mjc5MjcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSc4LjQ0MjUwNycgeT0nLTMyLjkyNzkyNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02OS43MDE2NDcnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Mi44MDY1NzQnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02MC41MDgyMTcnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01OC4yMDk4NTknIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01NS45MTE1MDInIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01MS4zMTQ3ODcnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Mi4xMjEzNTcnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zOS44MjMnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNy41MjQ2NDInIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNS4yMjYyODUnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xMi4yNDI3MScgeT0nLTMwLjYyOTU3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTkuOTQ0MzUyJyB5PSctMzAuNjI5NTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNy42NDU5OTUnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01LjM0NzYzNycgeT0nLTMwLjYyOTU3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMuMDQ5MjgnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzYuMTQ0MTUnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzguNDQyNTA3JyB5PSctMzAuNjI5NTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxMC43NDA4NjUnIHk9Jy0zMC42Mjk1NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Ny40MDMyODknIHk9Jy0yOC4zMzEyMTInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjIuODA2NTc0JyB5PSctMjguMzMxMjEyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU1LjkxMTUwMicgeT0nLTI4LjMzMTIxMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01MS4zMTQ3ODcnIHk9Jy0yOC4zMzEyMTInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDkuMDE2NDI5JyB5PSctMjguMzMxMjEyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ0LjQxOTcxNScgeT0nLTI4LjMzMTIxMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Mi4xMjEzNTcnIHk9Jy0yOC4zMzEyMTInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzUuMjI2Mjg1JyB5PSctMjguMzMxMjEyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI2LjAzMjg1NScgeT0nLTI4LjMzMTIxMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMy43MzQ0OTcnIHk9Jy0yOC4zMzEyMTInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjEuNDM2MTQnIHk9Jy0yOC4zMzEyMTInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctOS45NDQzNTInIHk9Jy0yOC4zMzEyMTInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctLjc1MDkyMicgeT0nLTI4LjMzMTIxMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEwLjc0MDg2NScgeT0nLTI4LjMzMTIxMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy0yNi4wMzI4NTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjcuNDAzMjg5JyB5PSctMjYuMDMyODU1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY1LjEwNDkzMicgeT0nLTI2LjAzMjg1NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02MC41MDgyMTcnIHk9Jy0yNi4wMzI4NTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTguMjA5ODU5JyB5PSctMjYuMDMyODU1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUzLjYxMzE0NCcgeT0nLTI2LjAzMjg1NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01MS4zMTQ3ODcnIHk9Jy0yNi4wMzI4NTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDkuMDE2NDI5JyB5PSctMjYuMDMyODU1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ0LjQxOTcxNScgeT0nLTI2LjAzMjg1NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zOS44MjMnIHk9Jy0yNi4wMzI4NTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzcuNTI0NjQyJyB5PSctMjYuMDMyODU1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM1LjIyNjI4NScgeT0nLTI2LjAzMjg1NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMi45Mjc5MjcnIHk9Jy0yNi4wMzI4NTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjEuNDM2MTQnIHk9Jy0yNi4wMzI4NTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTkuMTM3NzgyJyB5PSctMjYuMDMyODU1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE2LjgzOTQyNScgeT0nLTI2LjAzMjg1NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNC41NDEwNjcnIHk9Jy0yNi4wMzI4NTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNy42NDU5OTUnIHk9Jy0yNi4wMzI4NTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMy4wNDkyOCcgeT0nLTI2LjAzMjg1NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEuNTQ3NDM1JyB5PSctMjYuMDMyODU1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMy44NDU3OTMnIHk9Jy0yNi4wMzI4NTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSc2LjE0NDE1JyB5PSctMjYuMDMyODU1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nOC40NDI1MDcnIHk9Jy0yNi4wMzI4NTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxMC43NDA4NjUnIHk9Jy0yNi4wMzI4NTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PSctMjMuNzM0NDk3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY3LjQwMzI4OScgeT0nLTIzLjczNDQ5NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02NS4xMDQ5MzInIHk9Jy0yMy43MzQ0OTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjIuODA2NTc0JyB5PSctMjMuNzM0NDk3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU1LjkxMTUwMicgeT0nLTIzLjczNDQ5NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMC42Mjk1NycgeT0nLTIzLjczNDQ5NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yOC4zMzEyMTInIHk9Jy0yMy43MzQ0OTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjYuMDMyODU1JyB5PSctMjMuNzM0NDk3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIzLjczNDQ5NycgeT0nLTIzLjczNDQ5NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xOS4xMzc3ODInIHk9Jy0yMy43MzQ0OTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTIuMjQyNzEnIHk9Jy0yMy43MzQ0OTcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctLjc1MDkyMicgeT0nLTIzLjczNDQ5NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzguNDQyNTA3JyB5PSctMjMuNzM0NDk3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY5LjcwMTY0NycgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTYyLjgwNjU3NCcgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTYwLjUwODIxNycgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU4LjIwOTg1OScgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUzLjYxMzE0NCcgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUxLjMxNDc4NycgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ5LjAxNjQyOScgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ2LjcxODA3MicgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ0LjQxOTcxNScgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM3LjUyNDY0MicgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM1LjIyNjI4NScgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMyLjkyNzkyNycgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMwLjYyOTU3JyB5PSctMjEuNDM2MTQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjYuMDMyODU1JyB5PSctMjEuNDM2MTQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjEuNDM2MTQnIHk9Jy0yMS40MzYxNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNi44Mzk0MjUnIHk9Jy0yMS40MzYxNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNC41NDEwNjcnIHk9Jy0yMS40MzYxNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy05Ljk0NDM1MicgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcuNjQ1OTk1JyB5PSctMjEuNDM2MTQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNS4zNDc2MzcnIHk9Jy0yMS40MzYxNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0uNzUwOTIyJyB5PSctMjEuNDM2MTQnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSczLjg0NTc5MycgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nOC40NDI1MDcnIHk9Jy0yMS40MzYxNCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEwLjc0MDg2NScgeT0nLTIxLjQzNjE0JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTE5LjEzNzc4MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02OS43MDE2NDcnIHk9Jy0xOS4xMzc3ODInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjUuMTA0OTMyJyB5PSctMTkuMTM3NzgyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUzLjYxMzE0NCcgeT0nLTE5LjEzNzc4MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00OS4wMTY0MjknIHk9Jy0xOS4xMzc3ODInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDYuNzE4MDcyJyB5PSctMTkuMTM3NzgyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM5LjgyMycgeT0nLTE5LjEzNzc4MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNy41MjQ2NDInIHk9Jy0xOS4xMzc3ODInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzAuNjI5NTcnIHk9Jy0xOS4xMzc3ODInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjYuMDMyODU1JyB5PSctMTkuMTM3NzgyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIzLjczNDQ5NycgeT0nLTE5LjEzNzc4MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xOS4xMzc3ODInIHk9Jy0xOS4xMzc3ODInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTIuMjQyNzEnIHk9Jy0xOS4xMzc3ODInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctOS45NDQzNTInIHk9Jy0xOS4xMzc3ODInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNy42NDU5OTUnIHk9Jy0xOS4xMzc3ODInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNS4zNDc2MzcnIHk9Jy0xOS4xMzc3ODInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMy4wNDkyOCcgeT0nLTE5LjEzNzc4MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEuNTQ3NDM1JyB5PSctMTkuMTM3NzgyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMTAuNzQwODY1JyB5PSctMTkuMTM3NzgyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcyLjAwMDAwNCcgeT0nLTE2LjgzOTQyNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02OS43MDE2NDcnIHk9Jy0xNi44Mzk0MjUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjcuNDAzMjg5JyB5PSctMTYuODM5NDI1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY1LjEwNDkzMicgeT0nLTE2LjgzOTQyNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01OC4yMDk4NTknIHk9Jy0xNi44Mzk0MjUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTEuMzE0Nzg3JyB5PSctMTYuODM5NDI1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ5LjAxNjQyOScgeT0nLTE2LjgzOTQyNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Ni43MTgwNzInIHk9Jy0xNi44Mzk0MjUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzcuNTI0NjQyJyB5PSctMTYuODM5NDI1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMyLjkyNzkyNycgeT0nLTE2LjgzOTQyNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMC42Mjk1NycgeT0nLTE2LjgzOTQyNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yOC4zMzEyMTInIHk9Jy0xNi44Mzk0MjUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTkuMTM3NzgyJyB5PSctMTYuODM5NDI1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE2LjgzOTQyNScgeT0nLTE2LjgzOTQyNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNC41NDEwNjcnIHk9Jy0xNi44Mzk0MjUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMy4wNDkyOCcgeT0nLTE2LjgzOTQyNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0uNzUwOTIyJyB5PSctMTYuODM5NDI1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMS41NDc0MzUnIHk9Jy0xNi44Mzk0MjUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSczLjg0NTc5MycgeT0nLTE2LjgzOTQyNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzYuMTQ0MTUnIHk9Jy0xNi44Mzk0MjUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSc4LjQ0MjUwNycgeT0nLTE2LjgzOTQyNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEwLjc0MDg2NScgeT0nLTE2LjgzOTQyNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy0xNC41NDEwNjcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjkuNzAxNjQ3JyB5PSctMTQuNTQxMDY3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY1LjEwNDkzMicgeT0nLTE0LjU0MTA2NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01MS4zMTQ3ODcnIHk9Jy0xNC41NDEwNjcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDkuMDE2NDI5JyB5PSctMTQuNTQxMDY3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ0LjQxOTcxNScgeT0nLTE0LjU0MTA2NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Mi4xMjEzNTcnIHk9Jy0xNC41NDEwNjcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzkuODIzJyB5PSctMTQuNTQxMDY3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM1LjIyNjI4NScgeT0nLTE0LjU0MTA2NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMi45Mjc5MjcnIHk9Jy0xNC41NDEwNjcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjguMzMxMjEyJyB5PSctMTQuNTQxMDY3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIzLjczNDQ5NycgeT0nLTE0LjU0MTA2NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMS40MzYxNCcgeT0nLTE0LjU0MTA2NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xOS4xMzc3ODInIHk9Jy0xNC41NDEwNjcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTYuODM5NDI1JyB5PSctMTQuNTQxMDY3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTEyLjI0MjcxJyB5PSctMTQuNTQxMDY3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUuMzQ3NjM3JyB5PSctMTQuNTQxMDY3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMy44NDU3OTMnIHk9Jy0xNC41NDEwNjcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSc4LjQ0MjUwNycgeT0nLTE0LjU0MTA2NycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02NS4xMDQ5MzInIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01OC4yMDk4NTknIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01NS45MTE1MDInIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Mi4xMjEzNTcnIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zOS44MjMnIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNy41MjQ2NDInIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNS4yMjYyODUnIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMC42Mjk1NycgeT0nLTEyLjI0MjcxJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIzLjczNDQ5NycgeT0nLTEyLjI0MjcxJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIxLjQzNjE0JyB5PSctMTIuMjQyNzEnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTYuODM5NDI1JyB5PSctMTIuMjQyNzEnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctOS45NDQzNTInIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03LjY0NTk5NScgeT0nLTEyLjI0MjcxJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUuMzQ3NjM3JyB5PSctMTIuMjQyNzEnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMy4wNDkyOCcgeT0nLTEyLjI0MjcxJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMS41NDc0MzUnIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzYuMTQ0MTUnIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzguNDQyNTA3JyB5PSctMTIuMjQyNzEnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxMC43NDA4NjUnIHk9Jy0xMi4yNDI3MScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy05Ljk0NDM1MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Ny40MDMyODknIHk9Jy05Ljk0NDM1MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02NS4xMDQ5MzInIHk9Jy05Ljk0NDM1MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02MC41MDgyMTcnIHk9Jy05Ljk0NDM1MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01My42MTMxNDQnIHk9Jy05Ljk0NDM1MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00NC40MTk3MTUnIHk9Jy05Ljk0NDM1MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Mi4xMjEzNTcnIHk9Jy05Ljk0NDM1MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNS4yMjYyODUnIHk9Jy05Ljk0NDM1MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yOC4zMzEyMTInIHk9Jy05Ljk0NDM1MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMy43MzQ0OTcnIHk9Jy05Ljk0NDM1MicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy05Ljk0NDM1MicgeT0nLTkuOTQ0MzUyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUuMzQ3NjM3JyB5PSctOS45NDQzNTInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMy4wNDkyOCcgeT0nLTkuOTQ0MzUyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMTAuNzQwODY1JyB5PSctOS45NDQzNTInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjUuMTA0OTMyJyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjIuODA2NTc0JyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjAuNTA4MjE3JyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTguMjA5ODU5JyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTUuOTExNTAyJyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDQuNDE5NzE1JyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzkuODIzJyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzcuNTI0NjQyJyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzUuMjI2Mjg1JyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzIuOTI3OTI3JyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzAuNjI5NTcnIHk9Jy03LjY0NTk5NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMS40MzYxNCcgeT0nLTcuNjQ1OTk1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE0LjU0MTA2NycgeT0nLTcuNjQ1OTk1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcuNjQ1OTk1JyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNS4zNDc2MzcnIHk9Jy03LjY0NTk5NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zLjA0OTI4JyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctLjc1MDkyMicgeT0nLTcuNjQ1OTk1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMS41NDc0MzUnIHk9Jy03LjY0NTk5NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzMuODQ1NzkzJyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSc2LjE0NDE1JyB5PSctNy42NDU5OTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTMuNjEzMTQ0JyB5PSctNS4zNDc2MzcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTEuMzE0Nzg3JyB5PSctNS4zNDc2MzcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjguMzMxMjEyJyB5PSctNS4zNDc2MzcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjYuMDMyODU1JyB5PSctNS4zNDc2MzcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjMuNzM0NDk3JyB5PSctNS4zNDc2MzcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTkuMTM3NzgyJyB5PSctNS4zNDc2MzcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTYuODM5NDI1JyB5PSctNS4zNDc2MzcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTIuMjQyNzEnIHk9Jy01LjM0NzYzNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03LjY0NTk5NScgeT0nLTUuMzQ3NjM3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMS41NDc0MzUnIHk9Jy01LjM0NzYzNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9Jy0zLjA0OTI4JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY5LjcwMTY0NycgeT0nLTMuMDQ5MjgnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjcuNDAzMjg5JyB5PSctMy4wNDkyOCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02NS4xMDQ5MzInIHk9Jy0zLjA0OTI4JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTYyLjgwNjU3NCcgeT0nLTMuMDQ5MjgnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjAuNTA4MjE3JyB5PSctMy4wNDkyOCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01OC4yMDk4NTknIHk9Jy0zLjA0OTI4JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUzLjYxMzE0NCcgeT0nLTMuMDQ5MjgnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTEuMzE0Nzg3JyB5PSctMy4wNDkyOCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Ni43MTgwNzInIHk9Jy0zLjA0OTI4JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ0LjQxOTcxNScgeT0nLTMuMDQ5MjgnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzcuNTI0NjQyJyB5PSctMy4wNDkyOCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMi45Mjc5MjcnIHk9Jy0zLjA0OTI4JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMwLjYyOTU3JyB5PSctMy4wNDkyOCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNi44Mzk0MjUnIHk9Jy0zLjA0OTI4JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE0LjU0MTA2NycgeT0nLTMuMDQ5MjgnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTIuMjQyNzEnIHk9Jy0zLjA0OTI4JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcuNjQ1OTk1JyB5PSctMy4wNDkyOCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zLjA0OTI4JyB5PSctMy4wNDkyOCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEuNTQ3NDM1JyB5PSctMy4wNDkyOCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzguNDQyNTA3JyB5PSctMy4wNDkyOCcgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEwLjc0MDg2NScgeT0nLTMuMDQ5MjgnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PSctLjc1MDkyMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01OC4yMDk4NTknIHk9Jy0uNzUwOTIyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUxLjMxNDc4NycgeT0nLS43NTA5MjInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDkuMDE2NDI5JyB5PSctLjc1MDkyMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Ni43MTgwNzInIHk9Jy0uNzUwOTIyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM5LjgyMycgeT0nLS43NTA5MjInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzcuNTI0NjQyJyB5PSctLjc1MDkyMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMC42Mjk1NycgeT0nLS43NTA5MjInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjMuNzM0NDk3JyB5PSctLjc1MDkyMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMS40MzYxNCcgeT0nLS43NTA5MjInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTYuODM5NDI1JyB5PSctLjc1MDkyMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xMi4yNDI3MScgeT0nLS43NTA5MjInIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctOS45NDQzNTInIHk9Jy0uNzUwOTIyJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcuNjQ1OTk1JyB5PSctLjc1MDkyMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEuNTQ3NDM1JyB5PSctLjc1MDkyMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzMuODQ1NzkzJyB5PSctLjc1MDkyMicgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9JzEuNTQ3NDM1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY3LjQwMzI4OScgeT0nMS41NDc0MzUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjUuMTA0OTMyJyB5PScxLjU0NzQzNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Mi44MDY1NzQnIHk9JzEuNTQ3NDM1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU4LjIwOTg1OScgeT0nMS41NDc0MzUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTMuNjEzMTQ0JyB5PScxLjU0NzQzNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01MS4zMTQ3ODcnIHk9JzEuNTQ3NDM1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ2LjcxODA3MicgeT0nMS41NDc0MzUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzUuMjI2Mjg1JyB5PScxLjU0NzQzNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMi45Mjc5MjcnIHk9JzEuNTQ3NDM1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI4LjMzMTIxMicgeT0nMS41NDc0MzUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjYuMDMyODU1JyB5PScxLjU0NzQzNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xOS4xMzc3ODInIHk9JzEuNTQ3NDM1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE0LjU0MTA2NycgeT0nMS41NDc0MzUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctOS45NDQzNTInIHk9JzEuNTQ3NDM1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTcuNjQ1OTk1JyB5PScxLjU0NzQzNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01LjM0NzYzNycgeT0nMS41NDc0MzUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMy4wNDkyOCcgeT0nMS41NDc0MzUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctLjc1MDkyMicgeT0nMS41NDc0MzUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxLjU0NzQzNScgeT0nMS41NDc0MzUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSc2LjE0NDE1JyB5PScxLjU0NzQzNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9JzMuODQ1NzkzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY3LjQwMzI4OScgeT0nMy44NDU3OTMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjUuMTA0OTMyJyB5PSczLjg0NTc5MycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Mi44MDY1NzQnIHk9JzMuODQ1NzkzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTU4LjIwOTg1OScgeT0nMy44NDU3OTMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTMuNjEzMTQ0JyB5PSczLjg0NTc5MycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01MS4zMTQ3ODcnIHk9JzMuODQ1NzkzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQ5LjAxNjQyOScgeT0nMy44NDU3OTMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDQuNDE5NzE1JyB5PSczLjg0NTc5MycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00Mi4xMjEzNTcnIHk9JzMuODQ1NzkzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM5LjgyMycgeT0nMy44NDU3OTMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzAuNjI5NTcnIHk9JzMuODQ1NzkzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTI4LjMzMTIxMicgeT0nMy44NDU3OTMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjMuNzM0NDk3JyB5PSczLjg0NTc5MycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMS40MzYxNCcgeT0nMy44NDU3OTMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTkuMTM3NzgyJyB5PSczLjg0NTc5MycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0xNi44Mzk0MjUnIHk9JzMuODQ1NzkzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTEyLjI0MjcxJyB5PSczLjg0NTc5MycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy05Ljk0NDM1MicgeT0nMy44NDU3OTMnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNS4zNDc2MzcnIHk9JzMuODQ1NzkzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMuMDQ5MjgnIHk9JzMuODQ1NzkzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMS41NDc0MzUnIHk9JzMuODQ1NzkzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nOC40NDI1MDcnIHk9JzMuODQ1NzkzJyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMTAuNzQwODY1JyB5PSczLjg0NTc5MycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9JzYuMTQ0MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNjcuNDAzMjg5JyB5PSc2LjE0NDE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTY1LjEwNDkzMicgeT0nNi4xNDQxNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Mi44MDY1NzQnIHk9JzYuMTQ0MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNTguMjA5ODU5JyB5PSc2LjE0NDE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUzLjYxMzE0NCcgeT0nNi4xNDQxNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01MS4zMTQ3ODcnIHk9JzYuMTQ0MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDkuMDE2NDI5JyB5PSc2LjE0NDE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQyLjEyMTM1NycgeT0nNi4xNDQxNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zOS44MjMnIHk9JzYuMTQ0MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzcuNTI0NjQyJyB5PSc2LjE0NDE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTM1LjIyNjI4NScgeT0nNi4xNDQxNScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yMy43MzQ0OTcnIHk9JzYuMTQ0MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTYuODM5NDI1JyB5PSc2LjE0NDE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLS43NTA5MjInIHk9JzYuMTQ0MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSc2LjE0NDE1JyB5PSc2LjE0NDE1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nOC40NDI1MDcnIHk9JzYuMTQ0MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PScxMC43NDA4NjUnIHk9JzYuMTQ0MTUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNzIuMDAwMDA0JyB5PSc4LjQ0MjUwNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01OC4yMDk4NTknIHk9JzguNDQyNTA3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTUzLjYxMzE0NCcgeT0nOC40NDI1MDcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNDkuMDE2NDI5JyB5PSc4LjQ0MjUwNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00NC40MTk3MTUnIHk9JzguNDQyNTA3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTQyLjEyMTM1NycgeT0nOC40NDI1MDcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMzcuNTI0NjQyJyB5PSc4LjQ0MjUwNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zNS4yMjYyODUnIHk9JzguNDQyNTA3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMwLjYyOTU3JyB5PSc4LjQ0MjUwNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0yOC4zMzEyMTInIHk9JzguNDQyNTA3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIzLjczNDQ5NycgeT0nOC40NDI1MDcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMjEuNDM2MTQnIHk9JzguNDQyNTA3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTE5LjEzNzc4MicgeT0nOC40NDI1MDcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTYuODM5NDI1JyB5PSc4LjQ0MjUwNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03LjY0NTk5NScgeT0nOC40NDI1MDcnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctNS4zNDc2MzcnIHk9JzguNDQyNTA3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMuMDQ5MjgnIHk9JzguNDQyNTA3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMS41NDc0MzUnIHk9JzguNDQyNTA3JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nMTAuNzQwODY1JyB5PSc4LjQ0MjUwNycgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy03Mi4wMDAwMDQnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02OS43MDE2NDcnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Ny40MDMyODknIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02NS4xMDQ5MzInIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02Mi44MDY1NzQnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy02MC41MDgyMTcnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01OC4yMDk4NTknIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01My42MTMxNDQnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy01MS4zMTQ3ODcnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00OS4wMTY0MjknIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy00NC40MTk3MTUnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zOS44MjMnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMi45Mjc5MjcnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0zMC42Mjk1NycgeT0nMTAuNzQwODY1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTIxLjQzNjE0JyB5PScxMC43NDA4NjUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTYuODM5NDI1JyB5PScxMC43NDA4NjUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTQuNTQxMDY3JyB5PScxMC43NDA4NjUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSctMTIuMjQyNzEnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy05Ljk0NDM1MicgeT0nMTAuNzQwODY1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nLTMuMDQ5MjgnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9Jy0uNzUwOTIyJyB5PScxMC43NDA4NjUnIGhlaWdodD0nMi4yOTgzNTcnIHdpZHRoPScyLjI5ODM1NycvPgo8cmVjdCB4PSczLjg0NTc5MycgeT0nMTAuNzQwODY1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nNi4xNDQxNScgeT0nMTAuNzQwODY1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPHJlY3QgeD0nOC40NDI1MDcnIHk9JzEwLjc0MDg2NScgaGVpZ2h0PScyLjI5ODM1Nycgd2lkdGg9JzIuMjk4MzU3Jy8+CjxyZWN0IHg9JzEwLjc0MDg2NScgeT0nMTAuNzQwODY1JyBoZWlnaHQ9JzIuMjk4MzU3JyB3aWR0aD0nMi4yOTgzNTcnLz4KPC9nPgo8L3N2Zz4=")} +}