The toolchain and a first build
Companion videos
Watch on YouTube (opens in a new tab)
-
TESAIoT Developer Hub for Firmware/Middleware/Application Developer Thai Embedded Systems Association (TESA) -
Download (from TESA Developer Hub), Build and Program via ModusToolbox to TESAIoT Dev Kit Thai Embedded Systems Association (TESA) -
Remote Flashing for TESAIoT Dev Kit via TESA Developer Hub Thai Embedded Systems Association (TESA)
Videos by Thai Embedded Systems Association (TESA) · The whole series in the playlist AIoT Foundation
Objectives
Section titled “Objectives”By the end of this lesson, you will be able to
- Run the readiness check, fetch every project’s dependencies, and successfully build and flash the firmware template.
- Explain what the CM33_S, CM33_NS and CM55 cores each run in this template.
- Record the tool versions and the SDK commit used to build, so others can reproduce it.
Takes about 70 minutes (concepts 15 · practice 25 · lab 25 · check 5), not counting the roughly 1.9 GB of dependencies to download, which depends on your internet speed. It’s a good idea to start the download at the beginning of the lesson and read the concepts section while you wait.
Before you start
Section titled “Before you start”Two review questions from module 1.
- Where does the initial value of a
.datavariable live before the board is powered on, and who copies it into RAM? - The board’s external flash splits its space between separate images for CM33 secure, CM33 non-secure and CM55 (see the header of
10_littlefs_basics.c, which you read in lesson 1.2). Why do you think it needs three separate images?
What you need:
- ModusToolbox™ 3.6 (the SDK’s README pins this exact version), with the Arm GCC 14.2.1 that comes with it.
- Git and bash 4 or later (on macOS, install a newer bash via Homebrew, per the SDK’s README).
- About 4 GB of free space, and the TESAIoT Dev Kit board with a USB-C cable connected to KitProg3.
See it work first
Section titled “See it work first”The template you’ll build comes as a zip file from an SDK release, not from git clone, because the public repository at commit ef72c1b does not store the six prebuilt libraries (.a files — the repository’s top-level .gitignore excludes *.a). Those libraries come bundled in the zip. This course uses release fw-c-only-v1.10.0, which we’ve verified matches commit ef72c1b byte-for-byte in every file this lesson references (checked with cmp on 2026-09-26).
Download these three files from the fw-c-only-v1.10.0 release page into an empty folder to use as your workspace: bento-firmware-template-mtb-only.zip, SHA256SUMS.txt and manifest.json, then run
shasum -a 256 -c SHA256SUMS.txt # on Linux, sha256sum -c also works (a .hex file you didn't download will show "not found" — that's fine)unzip bento-firmware-template-mtb-only.zipcd bento-firmware-template-mtb-only./setup.sh --checkPredict before you run it: which lines will show FAIL or warn on a fresh machine that has never used this SDK before? Most will see variant: mtb-only, a compiler line, and mtb_shared not found, followed by a size of about 1.9 GB. This script deliberately prints every command it’s about to run first, so you can follow along by hand if you don’t have the script. If the compiler line doesn’t pass, fix your PATH per the SDK’s README before continuing.
If you want to read the full source and documentation, clone the repository separately and git checkout ef72c1b658178eee8c38b1e47d28b006f80a59b5 — but don’t build from that clone, since it won’t have the prebuilt libraries to link against.
Concepts
Section titled “Concepts”1. This template is three projects, on three cores
Section titled “1. This template is three projects, on three cores”| Project | Core | What it runs (mtb-only variant) |
|---|---|---|
proj_cm33_s |
Cortex-M33, secure side | Secure boot and setting up TrustZone protection. The template’s README says “you will not touch this” |
proj_cm33_ns |
Cortex-M33, non-secure side | FreeRTOS, WiFi, sensors on the I2C bus, cloud connectivity, and it owns the board’s one UART console |
proj_cm55 |
Cortex-M55 (has the NPU) | The LVGL display and every UI screen, Edge AI, radar |
Per Infineon’s README for the hello-world example for this chip, the boot order is an extended boot: the CM33 secure project launches from a fixed location in external flash, CM33 secure sets up protection and launches CM33 non-secure, and CM33 non-secure is the one that starts CM55. All three images are written to external QSPI flash and run from there execute-in-place. The build produces a single file, build/app_combined.hex, which combines all three.
The two cores of our work talk to each other through an IPC mailbox. The six prebuilt libraries are also split by core: three for CM55 are hard-float, and three for CM33_NS are soft-float. The SDK’s documentation says that linking across cores fails right at the link step, “which is the good outcome.” One thing to remember that matters for later lessons: CM55 has no console. printf on CM55 goes nowhere (Appendix X #1) — every message from the board goes out through CM33_NS.
2. Dependencies are fetched separately per project, and must be patched before building
Section titled “2. Dependencies are fetched separately per project, and must be patched before building”ModusToolbox tracks the libraries each project declares in its deps/*.mtb files, and make getlibs fetches them into an mtb_shared folder next to the template (the template’s README, section 2). This template has no top-level getlibs — it must be run in all three projects. Running it only in proj_cm33_ns gets you 33 of 41 assets, and the build later stops in ninja because it can’t find optiga-trust-m’s files.
After fetching, you must apply the SDK team’s patches to mtb_shared, in the order listed in third_party_patches/series, using patch -F0, then verify the result with SHA-256 (third_party_patches/README.md). That README explains why -F0 isn’t optional: GNU patch’s default behaviour tolerates a mismatched context and still reports success, and ten of the eleven patches “fail silently” when missing — for example, mTLS silently falls back to a software key and the broker rejects the device. The exit status tells you the patch landed somewhere; the digest tells you it landed in the right place.
3. A reproducible build starts from a pinned version
Section titled “3. A reproducible build starts from a pinned version”The SDK’s README pins ModusToolbox at 3.6, with the reasoning that a newer Configurator regenerates the BSP settings from design.modus and then issues a notice that turns -Werror=cpp into an error in a file you never touched. A newer version is therefore not automatically better. This course cites the SDK at commit ef72c1b in every link, for the same reason: a file that exists at one commit can change or disappear at the next one — and note that “the source you read” (a git commit) and “the package you build” (a release zip) are two separate things. Always record both.
A reproducible build needs to answer three questions: which tool versions were used, which source commit, and what was changed but not yet committed — plus evidence of what it produced, such as the size and SHA-256 of app_combined.hex. The SDK’s README follows the same rule for the files it distributes: every release ships a prebuilt hex, meant to be checked with SHA-256 against that same release’s SHA256SUMS.txt.
Worked example
Section titled “Worked example”The full sequence from the zip to a working board, run inside the bento-firmware-template-mtb-only folder. Each step matches the SDK documentation’s chapter A1 (A1 — From the zip to your first program).
Step 1: check what you received, and check your machine (inside the folder extracted from the zip)
(cd lib && ./verify.sh) # signature and digest of every prebuilt library, must end with exit 0./bento.sh doctor # the toolchain and anything the template doesn't bundleIf verify.sh fails, stop — the package you received isn’t the one that was signed (SDK documentation, chapter A1), or you’re running inside a git clone that has no .a files.
Step 2: fetch dependencies per project, then patch and prove they landed correctly
for p in proj_cm33_s proj_cm33_ns proj_cm55; do (cd $p && make getlibs); done
(cd ../mtb_shared \ && for p in $(cat ../bento-firmware-template-mtb-only/third_party_patches/series); do patch -p1 -F0 --forward < "../bento-firmware-template-mtb-only/third_party_patches/$p" || exit 1 done \ && shasum -a 256 -c ../bento-firmware-template-mtb-only/third_party_patches/PATCHED.sha256)On Linux, if you don’t have shasum, use sha256sum -c instead. Every line of verification must show OK.
Step 3: build, flash, and look for evidence the board is running
make build -j # the first build of all three cores takes about ten minutesmake program # writes through KitProg3Then unplug the USB cable all the way, wait a moment, and plug it back in. Open a serial console at 115200 8N1 before plugging it in. On this variant, a successful boot prints almost nothing — the only evidence is the line [HB] t=...s tasks=... every ten seconds, and the version tag on the Home screen ending in -mtb_only.
Traps the SDK documentation has collected (Appendix X) that you’ll run into on day one:
- #21, a black screen after flashing. Resetting through the debugger leaves the display off, which looks like a failed flash even though it isn’t. Unplug and replug every time.
- #22 (mtb-only). The display sometimes needs a second unplug-replug on a cold boot. If
[HB]is running but the screen is black, try once more before concluding anything. - #16, never attach a debugger to an mtb-only board that’s already running. It leaves CM33 stuck in the boot ROM’s loop. We’ll come back to this in lesson 3.1.
- The SDK’s README warns: never call openocd directly with
-f target/cat1d.cfg. The SDK team tried it and gotwrote 0 bytesfollowed by a checksum mismatch, which corrupted the firmware that was running. Usemake programonly.
Practice
Section titled “Practice”This lesson practices with tools, not code. Answer these by reading from your own machine and from files in the SDK, not from memory.
- What does
./setup.sh --checkverify, and what does it not verify (look at the setup.sh file — does it touch the patches at all)? - After getlibs, how many top-level folders do you count in
../mtb_shared? - How many patch files are listed in
third_party_patches/series, and which one does the README say is the only one that stops the build? - Open resources/build-record.md and fill in the “machine and tools” and “source built” sections completely.
Solution
Section titled “Solution”Try it yourself for at least 15 minutes first.
- As of this commit,
setup.shchecks the variant,arm-none-eabi-gccon the PATH,make, andmtb_shared(plus the MicroPython port, for the mtb-mpy variant). Its normal mode runs getlibs across all three projects, and--buildcontinues on to build — but it does not apply patches. Patching is your responsibility, and skipping it gets the build rejected by theverify_asset_patches.shchecker, which names the missing file. - The number depends on your fetch. What matters is that getlibs finishes in all three projects without error. If you get fewer than a classmate, check whether you ran it in all three projects.
- Count from the
seriesfile on your machine. That folder’s README states “Onlysecure-sockets/0002stops a build” — the rest fail silently. - Compare with one classmate. Whichever fields differ are what might make your build results differ too.
Check your understanding
Section titled “Check your understanding”Answer the 5 questions in quiz.yaml (shown at the bottom of this page on the website), covering all three objectives. Getting 4 or more right counts as finishing the lesson.
Task: build and flash the mtb-only template for the first time, and write a build record a classmate can reproduce.
- Follow the three steps in the worked example section until you see at least two
[HB]lines. - Check that the
t=number increases by 10 each time, and that the task count is stable once booting finishes (it will rise for a moment as various tasks are created). - Fill in every field of resources/build-record.md, including the SHA-256 of
build/app_combined.hex. - Have a classmate build from your record on their machine, then compare SHA-256 values. If they don’t match, find out where they diverge before deciding what’s wrong (a build across different machines can produce different bytes for reasons like embedded timestamps or paths — if that happens, write down what you found; that’s a valuable result on its own).
Evidence to keep in your portfolio: the getlibs log and the patch verification log (the OK lines), the [HB] lines from the console, a screenshot of the Home screen showing the version tag, and the completed build record file.
Going further
Section titled “Going further”- Open A0 — What you can build, and where each piece lives and check which prebuilt library links into which core — does it match the table in concept 1?
- Compare with Infineon’s mtb-example-psoc-edge-hello-world @ release-v2.1.0, which has the same three-project structure but is much smaller. That example says it was tested with ModusToolbox 3.7, while the SDK’s template is pinned at 3.6 — if you needed both on one machine, how would you manage the version difference?
- An overview of building and flashing a different way, using the Developer Hub’s master template, is in TESAIoT Firmware Stack, lesson 1.1.
Next lesson: lesson 2.2, Make and build flags
Reflect
Section titled “Reflect”- Which step today would have cost you the most time if no one had told you about it first, and how would you write it down in a team notebook?
- How is “the build succeeded” different from “the board is running”, and what evidence did you use today to decide the board was running?
References
Section titled “References”- SDK: the mtb-only template README (first run, CLI, three cores)
- SDK: the repository’s main README (tool versions and prebuilt firmware)
- SDK: third_party_patches/README.md
- A0 — What you can build, and where each piece lives (SDK docs built from commit ef72c1b)
- A1 — From the zip to your first program (SDK docs built from commit ef72c1b)
- Appendix X — Traps and anti-patterns (SDK docs built from commit ef72c1b)
- Infineon ModusToolbox software (GitHub)
- Infineon mtb-example-psoc-edge-hello-world @ release-v2.1.0
Examples on the TESAIoT Developer Hub
Section titled “Examples on the TESAIoT Developer Hub”Try the real thing on the TESAIoT Dev Kit: open an example on the Developer Hub to read the code, download it, or flash a prebuilt firmware image.
Review questions
Answer on your own first, then open the answer.
-
Order the steps from clone to a running board for the mtb-only template. (Objective 1)
- make build -j
- ใส่แพตช์ตาม series ด้วย patch -F0 แล้วตรวจด้วย PATCHED.sha256
- ถอดสาย USB ให้สุด เสียบใหม่ แล้วรอบรรทัด [HB] บน console 115200 8N1
- รัน make getlibs ในทั้ง proj_cm33_s, proj_cm33_ns และ proj_cm55
- make program
- ./setup.sh --check หรือ ./bento.sh doctor
Show answer
Correct order: F. ./setup.sh --check หรือ ./bento.sh doctor → D. รัน make getlibs ในทั้ง proj_cm33_s, proj_cm33_ns และ proj_cm55 → B. ใส่แพตช์ตาม series ด้วย patch -F0 แล้วตรวจด้วย PATCHED.sha256 → A. make build -j → E. make program → C. ถอดสาย USB ให้สุด เสียบใหม่ แล้วรอบรรทัด [HB] บน console 115200 8N1
ตรวจเครื่องก่อน ดึง dependency ให้ครบทุกโปรเจกต์ ใส่แพตช์ลงใน mtb_shared ที่เพิ่งดึงมา แล้วจึง build และ flash สุดท้ายต้องบูตเย็นด้วยการถอดสาย เพราะรีเซ็ตจาก debugger ทำให้จอดำ
-
A learner runs make getlibs only in proj_cm33_ns, then builds. What does the template README say happens? (Objective 1)
- build ผ่านแต่จอไม่ติด
- ได้ 33 จาก 41 asset แล้ว build หยุดใน ninja เพราะหาไฟล์ของ optiga-trust-m ไม่เจอ
- make ดึงของที่ขาดให้เองระหว่าง build
- ไม่มีผลอะไร เพราะ getlibs ระดับโปรเจกต์ไหนก็ดึงของทั้งแอปพลิเคชัน
Show answer
Answer: B. ได้ 33 จาก 41 asset แล้ว build หยุดใน ninja เพราะหาไฟล์ของ optiga-trust-m ไม่เจอ
แต่ละโปรเจกต์ประกาศ deps/*.mtb ของตัวเอง และแม่แบบนี้ไม่มี getlibs ระดับบนสุด จึงต้องรันครบทั้งสามโปรเจกต์ทุกครั้ง
-
In the mtb-only template, which core owns the board's only UART console, and which one draws the screen? (Objective 2)
- CM33_S ถือ console, CM33_NS วาดจอ
- CM55 ถือ console, CM33_NS วาดจอ
- CM33_NS ถือ console, CM55 วาดจอด้วย LVGL
- ทั้งสามคอร์พิมพ์ console ได้พร้อมกัน
Show answer
Answer: C. CM33_NS ถือ console, CM55 วาดจอด้วย LVGL
CM33_S ทำ secure boot, CM33_NS รัน FreeRTOS WiFi เซนเซอร์และเป็นเจ้าของ UART ส่วน CM55 ทำจอ LVGL, UI และ Edge AI และไม่มี console printf บน CM55 จึงไม่ออกไปไหน
-
Which describes the start-up order of the three cores correctly? (Objective 2)
- CM55 บูตก่อน แล้วปลุก CM33 ทั้งสองฝั่ง
- extended boot ปล่อย CM33 secure, CM33 secure ตั้งการป้องกันแล้วปล่อย CM33 non-secure, แล้ว CM33 non-secure เปิด CM55
- ทั้งสามคอร์เริ่มพร้อมกันจาก RAM
- CM33 non-secure บูตก่อน แล้วค่อยเปิดฝั่ง secure ทีหลัง
Show answer
Answer: B. extended boot ปล่อย CM33 secure, CM33 secure ตั้งการป้องกันแล้วปล่อย CM33 non-secure, แล้ว CM33 non-secure เปิด CM55
README ของตัวอย่าง hello world ของ Infineon อธิบายลำดับนี้ ทั้งสาม image อยู่ใน flash QSPI ภายนอกและรันแบบ execute in place
-
A colleague must rebuild your exact firmware next month. What belongs in the build record? (choose all that apply) (Objective 3)
- รุ่นของ ModusToolbox และผลของ arm-none-eabi-gcc --version
- ชื่อ release และ SHA-256 ของ zip ที่แตกมา build พร้อม commit ของโปรเจกต์จาก git rev-parse HEAD และผลของ git status --short
- คำสั่ง build ทั้งบรรทัดรวมตัวแปร เช่น ENABLE_PAGE_EXAMPLES=1
- SHA-256 ของ app_combined.hex ที่ได้
- คำว่า 'ใช้รุ่นล่าสุด' แทนเลขรุ่น เพราะใหม่กว่าย่อมดีกว่า
Show answer
Answer: A. รุ่นของ ModusToolbox และผลของ arm-none-eabi-gcc --version · B. ชื่อ release และ SHA-256 ของ zip ที่แตกมา build พร้อม commit ของโปรเจกต์จาก git rev-parse HEAD และผลของ git status --short · C. คำสั่ง build ทั้งบรรทัดรวมตัวแปร เช่น ENABLE_PAGE_EXAMPLES=1 · D. SHA-256 ของ app_combined.hex ที่ได้
รุ่นเครื่องมือ แพ็กเกจและ commit ที่ใช้ ไฟล์ที่ยังไม่ commit ตัวแปรของ build และ digest ของผลลัพธ์ ทำให้ทำซ้ำและตรวจได้ ส่วน 'รุ่นล่าสุด' เปลี่ยนไปทุกเดือน และ README ของ SDK บอกชัดว่า ModusToolbox ที่ใหม่กว่า 3.6 ทำให้ build ล้มในไฟล์ที่คุณไม่เคยแตะ
Cite this lesson
If you teach from this lesson or reuse it in slides or documents, credit it with the text below. If you changed it, add (adapted) after the title.
"The toolchain and a first build" from TESA Open Knowledge by the Thai Embedded Systems Association (TESA), https://github.com/tesaiot/tesa-qualification-program, licensed under CC BY-NC 4.0
Thai attribution: "ชุดเครื่องมือและการ build ครั้งแรก" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
This lesson adapts the source below; keep its credit too.
https://github.com/tesaiot/tesaiot-pse84-devkit-sdk/tree/ef72c1b658178eee8c38b1e47d28b006f80a59b5 · SDK examples and docs are linked at this commit, not copied into this course. Lessons quote short excerpts (at most 25 lines) with a link to the file at this commit and the credit (Apache-2.0, tesaiot-pse84-devkit-sdk).
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0
Content is licensed CC BY-NC 4.0. Reuse it non-commercially and credit the Thai Embedded Systems Association (TESA) every time. · How to cite TESA