Skip to content

Commit afaf660

Browse files
committed
test/benchmark/ci: CHAR_BIT != 8 test vectors, NO_MALLOC benchmark, TI C2000 compile CI and docs
1 parent 4a688b0 commit afaf660

8 files changed

Lines changed: 460 additions & 96 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: TI C2000 (C28x) compile-only
2+
3+
# Compile-guard for the TI C2000 C28x port (CHAR_BIT == 16). It builds the
4+
# wolfCrypt subset that carries the CHAR_BIT != 8 gated fixes with the TI cl2000
5+
# code generation tools - no linking, no C2000Ware, no hardware. Purpose: catch
6+
# compile regressions in the octet/SP/ML-DSA gated paths. On-target run-tests
7+
# live on a hardware-in-the-loop runner (there is no public C28x simulator).
8+
9+
# START OF COMMON SECTION
10+
on:
11+
# Only build when something that can affect the C28x compile changes, so the
12+
# job (and the CGT it pulls) does not burn runner minutes on unrelated PRs.
13+
push:
14+
branches: [ 'master', 'main', 'release/**' ]
15+
paths:
16+
- 'wolfcrypt/src/**'
17+
- 'wolfssl/wolfcrypt/**'
18+
- 'IDE/C2000/**'
19+
- '.github/workflows/ti-c2000-compile.yml'
20+
pull_request:
21+
types: [opened, synchronize, reopened, ready_for_review]
22+
branches: [ '*' ]
23+
paths:
24+
- 'wolfcrypt/src/**'
25+
- 'wolfssl/wolfcrypt/**'
26+
- 'IDE/C2000/**'
27+
- '.github/workflows/ti-c2000-compile.yml'
28+
29+
concurrency:
30+
group: ${{ github.workflow }}-${{ github.ref }}
31+
cancel-in-progress: true
32+
# END OF COMMON SECTION
33+
34+
jobs:
35+
ti_c2000_compile:
36+
name: cl2000 compile-only
37+
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }}
38+
runs-on: ubuntu-22.04
39+
timeout-minutes: 20
40+
env:
41+
# TI C2000 code generation tools. The CGT is a free download from TI.
42+
# Update CGT_VER / CGT_URL when bumping the toolchain. If the TI URL
43+
# starts requiring a click-through, host the installer as a repo/org
44+
# cache artifact and point CGT_URL at it (the cache step below makes the
45+
# download a once-per-version cost).
46+
CGT_VER: "22.6.2.LTS"
47+
CGT_URL: "https://dr-download.ti.com/software-development/ide-configuration-compiler-or-debugger/MD-LP0nQ4O8eX/22.6.2.LTS/ti_cgt_c2000_22.6.2.LTS_linux-x64_installer.bin"
48+
CGT_DIR: "${{ github.workspace }}/ti-cgt-c2000"
49+
steps:
50+
- uses: actions/checkout@v4
51+
name: Checkout wolfSSL
52+
53+
- name: Cache TI C2000 CGT
54+
id: cgt-cache
55+
uses: actions/cache@v4
56+
with:
57+
path: ${{ env.CGT_DIR }}
58+
key: ti-cgt-c2000-${{ env.CGT_VER }}
59+
60+
- name: Download + install TI C2000 CGT
61+
if: steps.cgt-cache.outputs.cache-hit != 'true'
62+
run: |
63+
set -e
64+
curl -fsSL "$CGT_URL" -o /tmp/cgt.bin
65+
chmod +x /tmp/cgt.bin
66+
/tmp/cgt.bin --mode unattended --prefix "$CGT_DIR"
67+
68+
- name: Locate cl2000
69+
id: find-cl
70+
run: |
71+
CL=$(find "$CGT_DIR" -type f -name cl2000 | head -1)
72+
test -n "$CL" || { echo "cl2000 not found under $CGT_DIR"; exit 1; }
73+
echo "cgt_root=$(dirname "$(dirname "$CL")")" >> "$GITHUB_OUTPUT"
74+
75+
- name: Compile-only guard
76+
run: |
77+
CGT_ROOT="${{ steps.find-cl.outputs.cgt_root }}" \
78+
IDE/C2000/compile.sh

.wolfssl_known_macro_extras

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,7 @@ WOLFSSL_CLANG_TIDY
747747
WOLFSSL_CLIENT_EXAMPLE
748748
WOLFSSL_CONTIKI
749749
WOLFSSL_CRL_ALLOW_MISSING_CDP
750+
WOLFSSL_DILITHIUM_VERIFY_SMALLEST_MEM
750751
WOLFSSL_DISABLE_EARLY_SANITY_CHECKS
751752
WOLFSSL_DRBG_SHA256
752753
WOLFSSL_DTLS13_ECHO_LEGACY_SESSION_ID
@@ -1115,6 +1116,12 @@ __SUNPRO_CC
11151116
__SVR4
11161117
__TASKING__
11171118
__TI_COMPILER_VERSION__
1119+
__TMS320C2000__
1120+
__TMS320C2800__
1121+
__TMS320C28XX__
1122+
__TMS320C54X__
1123+
__TMS320C5500__
1124+
__TMS320C55X__
11181125
__TURBOC__
11191126
__UNIX__
11201127
__USE_GNU

IDE/C2000/README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# TI C2000 C28x (CHAR_BIT == 16) support
2+
3+
wolfCrypt builds and runs on the TI C2000 C28x DSP family, a word-addressed
4+
architecture where `CHAR_BIT == 16` (a C `char`/`unsigned char` is 16 bits and
5+
is the smallest addressable unit). Support is gated behind `WOLFSSL_WIDE_BYTE`,
6+
which `wolfssl/wolfcrypt/types.h` auto-enables when `CHAR_BIT != 8` or a known
7+
16-bit-char TI toolchain macro is seen (`__TMS320C28XX__`, `__TMS320C2000__`,
8+
etc.). On normal 8-bit-byte targets none of this code changes behavior.
9+
10+
## Validated on hardware (LAUNCHXL-F28P55X, TMS320F28P550SJ, cl2000)
11+
12+
- SHA-224/256, SHA-384/512, SHA-512/224, SHA-512/256
13+
- SHA3-224/256/384/512, SHAKE128/256 (split-64 Keccak permutation for
14+
`WC_16BIT_CPU`, ~53% faster than the generic C path)
15+
- ML-DSA-87 (Dilithium) verify and full keygen/sign/verify; ML-KEM-768 (FIPS 203)
16+
- AES-128/192/256 CBC/CTR/CFB/GCM; AES-CMAC, AES-CCM, AES-GMAC
17+
- HMAC + HKDF; ChaCha20-Poly1305; Poly1305
18+
- X25519 + Ed25519; ECDSA + ECDH (SECP256R1, SP math)
19+
- RSA-2048 PKCS#1 v1.5 verify (SP math)
20+
21+
## What `WOLFSSL_WIDE_BYTE` fixes
22+
23+
The `CHAR_BIT != 8` work falls into a few recurring classes, each a no-op on
24+
8-bit targets:
25+
26+
- Byte/word aliasing. Serializing a `word32`/`word64` via a `byte*` cast moves
27+
cells, not octets. Replaced with shift-based octet I/O. Shared helpers live in
28+
`wolfcrypt/src/misc.c`: `WordsFromBytesBE32`/`BytesFromWordsBE32`,
29+
`BytesFromWordsLE32`, the 64-bit variants, and octet-correct
30+
`readUnalignedWord32`/`readUnalignedWord64`. `sp_int.c sp_read_unsigned_bin`
31+
uses the endian-/`CHAR_BIT`-agnostic shift loop for its leftover bytes.
32+
- `(byte)x` not truncating to an octet (it keeps 16 bits). Masked with
33+
`WC_OCTET(x)` = `(byte)((x) & 0xFF)` (types.h). Used across the ML-KEM/ML-DSA
34+
encoders, the SP `*_to_bin` serializers, AES `GETBYTE`, base64, and DRBG.
35+
- Integer-promotion bugs. `1U << n` is 16-bit on C28x (use `1UL`); a bit width
36+
written `sizeof(t) * 8` is wrong when `CHAR_BIT != 8` (use `CHAR_BIT *
37+
sizeof(t)`); a `byte` operand promotes to a 16-bit `int`.
38+
- `sizeof` counting cells, not octets. e.g. `CHACHA_CHUNK_BYTES` is `16 * 4`,
39+
not `16 * sizeof(word32)` (= 32 on C28x, which halves the ChaCha block).
40+
41+
The SP backend file `wolfcrypt/src/sp_c32.c` is generated; the `& 0xFF` octet
42+
masks added to its `sp_*_to_bin_*` serializers should be folded into the SP
43+
generator templates for a permanent upstream fix.
44+
45+
## Enabling on your build
46+
47+
Define a user-settings header (see `IDE/C2000/user_settings.h` for a
48+
minimal CHAR_BIT!=8 config) and build with `WOLFSSL_USER_SETTINGS`. For the SP
49+
math backend on a 16-bit-int target also set `WOLFSSL_SP_MATH`,
50+
`SP_WORD_SIZE 32`, and `WOLFSSL_SP_ALLOW_16BIT_CPU`.
51+
52+
## Reference example
53+
54+
A complete bare-metal example with KATs, benchmark, linker scripts, and per-
55+
algorithm build toggles is in wolfSSL Examples:
56+
`embedded/ti-c2000-f28p55x/` (see its `README.md` for the `make` options:
57+
`ECC`, `MLKEM`, `AES`, `AESEXTRA`, `X25519`, `HKDF`, `CHACHA`, `RSA`, `SIGN`,
58+
`BENCH`).
59+
60+
Representative throughput on the F28P55X at 150 MHz: SHA-256 ~284 KiB/s; SHA3-256
61+
~264 KiB/s; SHAKE128 ~319 KiB/s; RNG Hash-DRBG ~122 KiB/s. ML-DSA-87 verify
62+
~225 ms/op in ~10.7 KB RAM (zero heap, with `WOLFSSL_MLDSA_VERIFY_SMALLEST_MEM`
63+
+ `WOLFSSL_MLDSA_ASSIGN_KEY`).
64+
65+
## Continuous integration
66+
67+
`IDE/C2000/compile.sh` runs `cl2000 --compile_only` over the
68+
`CHAR_BIT != 8` wolfCrypt subset to guard these paths without hardware;
69+
`.github/workflows/ti-c2000-compile.yml` runs it in CI (it fetches/caches the
70+
TI C2000 code generation tools).

IDE/C2000/compile.sh

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/bin/sh
2+
# compile.sh - compile-only guard for the TI C2000 (C28x, CHAR_BIT==16) port.
3+
#
4+
# Builds the wolfCrypt subset that compiles under CHAR_BIT==16 with the TI cl2000
5+
# code generation tools, using IDE/C2000/user_settings.h. No linking, no
6+
# C2000Ware, no hardware: this only catches compile regressions in the
7+
# CHAR_BIT != 8 gated code paths (SHA-2/3/SHAKE, ML-DSA-87 verify, SP-ECC).
8+
#
9+
# Usage:
10+
# CGT_ROOT=/path/to/ti-cgt-c2000_xx.y.z IDE/C2000/compile.sh
11+
#
12+
# CGT_ROOT must point at a TI C2000 codegen install (the dir containing
13+
# bin/cl2000). The CGT is a free download from TI; in CI it is fetched/cached
14+
# by .github/workflows/ti-c2000-compile.yml.
15+
16+
set -e
17+
18+
: "${CGT_ROOT:?set CGT_ROOT to the ti-cgt-c2000 install (dir with bin/cl2000)}"
19+
20+
# Repo root = two levels up from this script.
21+
SELF_DIR=$(cd "$(dirname "$0")" && pwd)
22+
WOLFROOT=$(cd "$SELF_DIR/../.." && pwd)
23+
CL="$CGT_ROOT/bin/cl2000"
24+
25+
if [ ! -x "$CL" ]; then
26+
echo "ERROR: cl2000 not found/executable at $CL" >&2
27+
exit 2
28+
fi
29+
30+
OUT=$(mktemp -d)
31+
trap 'rm -rf "$OUT"' EXIT
32+
33+
INCS="-I$CGT_ROOT/include -I$WOLFROOT -I$SELF_DIR"
34+
CFLAGS="-v28 --abi=eabi --float_support=fpu32 --tmu_support=tmu1 -O2 \
35+
--define=WOLFSSL_USER_SETTINGS --display_error_number --diag_warning=225"
36+
37+
# wolfCrypt sources to compile-guard under CHAR_BIT==16. This is the set that
38+
# carries the CHAR_BIT != 8 gated fixes (plus their direct deps) - the
39+
# regression surface for this port. hash.c (an unmodified dispatch wrapper) is
40+
# intentionally omitted: its wc_OidGetHash() OID switch needs the fuller ASN/OID
41+
# config of a real build to avoid a 16-bit-int case-label fold, and it is
42+
# covered by the on-target example build, not by this minimal guard.
43+
SRCS="error wc_port memory logging misc coding \
44+
sha256 sha512 sha3 wc_mldsa random ecc sp_int sp_c32"
45+
46+
rc=0
47+
for s in $SRCS; do
48+
printf 'CC %s.c ... ' "$s"
49+
if "$CL" $CFLAGS $INCS --compile_only --skip_assembler \
50+
--asm_directory="$OUT" --obj_directory="$OUT" \
51+
"$WOLFROOT/wolfcrypt/src/$s.c" > "$OUT/$s.log" 2>&1; then
52+
echo "ok"
53+
else
54+
echo "FAIL"
55+
cat "$OUT/$s.log"
56+
rc=1
57+
fi
58+
done
59+
60+
if [ "$rc" -eq 0 ]; then
61+
echo "TI C2000 compile-only guard: PASS"
62+
else
63+
echo "TI C2000 compile-only guard: FAIL" >&2
64+
fi
65+
exit "$rc"

IDE/C2000/user_settings.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/* user_settings.h - minimal wolfCrypt config for the TI C2000 (C28x,
2+
* CHAR_BIT==16) compile-only CI guard.
3+
*
4+
* This is NOT a board config: it has no BSP/device dependencies. Its only job
5+
* is to enable the subset of wolfCrypt that exercises the CHAR_BIT != 8 gated
6+
* code paths (SHA-2/3/SHAKE, ML-DSA-87 verify, ECDSA/ECDH P-256 via SP math)
7+
* so that IDE/C2000/compile.sh can compile them with cl2000 and catch
8+
* regressions. cl2000 predefines __TMS320C28XX__, so types.h auto-enables
9+
* WOLFSSL_WIDE_BYTE; we do not set it here.
10+
*/
11+
#ifndef TI_C2000_CI_USER_SETTINGS_H
12+
#define TI_C2000_CI_USER_SETTINGS_H
13+
14+
#define WOLFCRYPT_ONLY /* crypto only - no TLS (no MD5/SHA1 dep) */
15+
#define WOLFSSL_GENERAL_ALIGNMENT 2
16+
#define HAVE_LIMITS_H
17+
#define WOLFSSL_NO_ASM
18+
#define NO_INLINE
19+
#define SINGLE_THREADED
20+
#define NO_FILESYSTEM
21+
#define NO_WOLFSSL_DIR
22+
#define NO_MAIN_DRIVER
23+
#define NO_DEV_RANDOM
24+
#define WOLFSSL_IGNORE_FILE_WARN
25+
#define BENCH_EMBEDDED
26+
#define NO_WOLFSSL_MEMORY
27+
#define WOLFSSL_GENSEED_FORTEST /* dev-only seed; no TRNG on this part */
28+
29+
/* Hashes */
30+
#define WOLFSSL_SHA512
31+
#define WOLFSSL_SHA384
32+
#define WOLFSSL_SHA3
33+
#define WOLFSSL_SHAKE128
34+
#define WOLFSSL_SHAKE256
35+
36+
/* ML-DSA-87 verify (smallest-mem streaming verifier) */
37+
#define WOLFSSL_HAVE_MLDSA
38+
#define WOLFSSL_NO_ML_DSA_44
39+
#define WOLFSSL_NO_ML_DSA_65
40+
#define WOLFSSL_MLDSA_NO_ASN1
41+
#define WOLFSSL_MLDSA_VERIFY_ONLY
42+
#define WOLFSSL_MLDSA_VERIFY_SMALL_MEM
43+
#define WOLFSSL_MLDSA_VERIFY_NO_MALLOC
44+
#define WOLFSSL_MLDSA_VERIFY_SMALLEST_MEM
45+
#undef WOLFSSL_MLDSA_ALIGNMENT
46+
#define WOLFSSL_MLDSA_ALIGNMENT 16
47+
#define WOLFSSL_SMALL_STACK
48+
49+
/* ECDSA / ECDH P-256 via SP single-precision math (sp_c32.c) */
50+
#define HAVE_ECC
51+
#define ECC_USER_CURVES
52+
#define HAVE_ECC256
53+
#define HAVE_ECC_VERIFY
54+
#define HAVE_ECC_SIGN
55+
#define HAVE_ECC_DHE
56+
#define ECC_TIMING_RESISTANT
57+
#define WOLFSSL_SP_MATH
58+
#define WOLFSSL_HAVE_SP_ECC
59+
#define WOLFSSL_SP_NO_MALLOC
60+
#define WOLFSSL_SP_SMALL
61+
#define SP_WORD_SIZE 32
62+
#define WOLFSSL_SP_ALLOW_16BIT_CPU
63+
64+
/* Off: anything that pulls in big-int/ASN/symmetric not under test here. */
65+
#define NO_RSA
66+
#define NO_DH
67+
#define NO_DSA
68+
#define NO_ASN
69+
#define NO_CERTS
70+
#define NO_PWDBASED
71+
#define NO_PKCS7
72+
#define NO_PKCS12
73+
#define NO_SIG_WRAPPER
74+
#define NO_AES
75+
#define NO_DES3
76+
#define NO_RC4
77+
#define NO_MD4
78+
#define NO_MD5
79+
#define NO_SHA
80+
#define NO_HMAC
81+
#define NO_ASN_TIME
82+
#define WOLFSSL_USER_CURRTIME
83+
84+
#endif /* TI_C2000_CI_USER_SETTINGS_H */

IDE/include.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ include IDE/XCODE/include.am
6767
include IDE/XilinxSDK/include.am
6868
include IDE/zephyr/include.am
6969

70+
EXTRA_DIST+= IDE/C2000
7071
EXTRA_DIST+= IDE/Espressif
7172
EXTRA_DIST+= IDE/HEXIWEAR
7273
EXTRA_DIST+= IDE/IAR-EWARM

wolfcrypt/benchmark/benchmark.c

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2357,6 +2357,15 @@ static const char* bench_result_words2[][6] = {
23572357

23582358
static int numBlocks = NUM_BLOCKS;
23592359
static word32 bench_size = BENCH_SIZE;
2360+
#ifdef WOLFSSL_NO_MALLOC
2361+
/* No heap: use file-scope static buffers for the core bench buffers.
2362+
* Sized to hold bench_buf_size (BENCH_SIZE + BENCH_CIPHER_ADD rounded up
2363+
* to the 16-byte AES block size) plus the +16 slack used at the alloc
2364+
* site, plus 64 bytes so the buffer can be 64-byte aligned at runtime. */
2365+
#define BENCH_MAX_PAD (BENCH_CIPHER_ADD + 16 + 64)
2366+
static THREAD_LS_T XGEN_ALIGN byte bench_plain_buf[BENCH_SIZE + BENCH_MAX_PAD];
2367+
static THREAD_LS_T XGEN_ALIGN byte bench_cipher_buf[BENCH_SIZE + BENCH_MAX_PAD];
2368+
#endif
23602369
static int base2 = 1;
23612370
static int digest_stream = 1;
23622371
#ifndef NO_HMAC
@@ -3805,7 +3814,23 @@ static void* benchmarks_do(void* args)
38053814
if (bench_buf_size % 16)
38063815
bench_buf_size += 16 - (bench_buf_size % 16);
38073816

3808-
#ifdef WOLFSSL_AFALG_XILINX_AES
3817+
#ifdef WOLFSSL_NO_MALLOC
3818+
/* No heap: point at the file-scope static buffers. bench_size can be
3819+
* raised at runtime (benchmark_configure(), the -base16/auth size paths,
3820+
* or hash-file input), so confirm the requested size still fits the fixed
3821+
* static capacity before using them - otherwise the later XMEMSET / crypto
3822+
* writes would run past the end of the buffer. */
3823+
if ((unsigned long)bench_buf_size + 16UL >
3824+
(unsigned long)sizeof(bench_plain_buf)) {
3825+
printf("%sBenchmark size %lu exceeds WOLFSSL_NO_MALLOC static buffer "
3826+
"(%lu); rebuild with a larger BENCH_SIZE\n", err_prefix,
3827+
(unsigned long)bench_buf_size,
3828+
(unsigned long)sizeof(bench_plain_buf));
3829+
goto exit;
3830+
}
3831+
bench_plain = bench_plain_buf;
3832+
bench_cipher = bench_cipher_buf;
3833+
#elif defined(WOLFSSL_AFALG_XILINX_AES)
38093834
bench_plain = (byte*)aligned_alloc(64, (size_t)bench_buf_size + 16); /* native heap */
38103835
bench_cipher = (byte*)aligned_alloc(64, (size_t)bench_buf_size + 16); /* native heap */
38113836
#else
@@ -3918,7 +3943,8 @@ static void* benchmarks_do(void* args)
39183943
}
39193944
#endif
39203945

3921-
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(HAVE_INTEL_QA_SYNC)
3946+
#if (defined(WOLFSSL_ASYNC_CRYPT) || defined(HAVE_INTEL_QA_SYNC)) && \
3947+
!defined(WOLFSSL_NO_MALLOC)
39223948
bench_key = (byte*)XMALLOC(sizeof(bench_key_buf),
39233949
HEAP_HINT, DYNAMIC_TYPE_WOLF_BIGINT);
39243950
bench_iv = (byte*)XMALLOC(sizeof(bench_iv_buf),
@@ -4744,9 +4770,12 @@ static void* benchmarks_do(void* args)
47444770

47454771
exit:
47464772
/* free benchmark buffers */
4773+
#ifndef WOLFSSL_NO_MALLOC
4774+
/* under WOLFSSL_NO_MALLOC these point at file-scope static buffers */
47474775
XFREE(bench_plain, HEAP_HINT, DYNAMIC_TYPE_WOLF_BIGINT);
47484776
XFREE(bench_cipher, HEAP_HINT, DYNAMIC_TYPE_WOLF_BIGINT);
4749-
#ifdef WOLFSSL_ASYNC_CRYPT
4777+
#endif
4778+
#if defined(WOLFSSL_ASYNC_CRYPT) && !defined(WOLFSSL_NO_MALLOC)
47504779
XFREE(bench_key, HEAP_HINT, DYNAMIC_TYPE_WOLF_BIGINT);
47514780
XFREE(bench_iv, HEAP_HINT, DYNAMIC_TYPE_WOLF_BIGINT);
47524781
#endif

0 commit comments

Comments
 (0)