test: add noisy, max_iter, and back-to-back cocotb tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
cah
2026-02-25 21:04:51 -07:00
parent 90bd916ee3
commit c74ab93ae5
10 changed files with 770 additions and 0 deletions

View File

@@ -0,0 +1,180 @@
// SPDX-FileCopyrightText: 2024 LDPC Optical Project
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
/*
* LDPC Back-to-Back Decode Test Firmware
*
* Runs on PicoRV32 inside Caravel. Exercises the LDPC decoder with two
* sequential decodes to verify no state leakage between runs:
* 1. First decode: all-zero codeword (LLRs=+31), check decoded=0
* 2. Second decode: noisy correctable codeword (test vector 0),
* check decoded matches expected
* 3. Signals pass (0xAB) only if BOTH decodes are correct
*/
#include <firmware_apis.h>
// LDPC register word offsets (byte_addr / 4)
#define LDPC_CTRL 0 // 0x00
#define LDPC_STATUS 1 // 0x04
#define LDPC_LLR_BASE 4 // 0x10
#define LDPC_DECODED 20 // 0x50
#define LDPC_VERSION 21 // 0x54
#define LLR_WORD_COUNT 52 // 260 LLRs / 5 per word, rounded up
// CTRL register fields
#define CTRL_START (1 << 0)
#define CTRL_EARLY_TERM (1 << 1)
#define CTRL_MAX_ITER(n) (((n) & 0x1F) << 8)
// STATUS register fields
#define STATUS_BUSY (1 << 0)
#define STATUS_CONVERGED (1 << 1)
#define STATUS_ITER_MASK (0x1F << 8)
#define STATUS_SYN_MASK (0xFF << 16)
// Expected values
#define EXPECTED_VERSION 0x1D010001
#define PASS_SIGNATURE 0xAB
#define FAIL_SIGNATURE 0xFF
// All-zero codeword: every LLR = +31 (6'b011111 = 0x1F)
// Pack 5x 0x1F per word:
// (0x1F << 0) | (0x1F << 6) | (0x1F << 12) | (0x1F << 18) | (0x1F << 24)
// = 0x1F7DF7DF
#define ALL_ZERO_LLR_WORD 0x1F7DF7DF
// Test vector 0: noisy but correctable codeword
// Expected decoded word: 0x3FD74222
#define EXPECTED_DECODED_VEC0 0x3FD74222
static const unsigned int noisy_llr_words[LLR_WORD_COUNT] = {
0x1F7DF81F, 0x20C9F7E0, 0x207CC7DF, 0x1F82081F, 0x328207E0, 0x20820820, 0x208207CC, 0x1F81F7DF,
0x1F7F27DF, 0x1F7CC81F, 0x0C81F81F, 0x207E07F2, 0x1F820820, 0x207DF7CC, 0x1F81F7E0, 0x2082081F,
0x0C31F81F, 0x2081F7DF, 0x1FCA081F, 0x20820820, 0x1F7DF7DF, 0x207E07E0, 0x208207CC, 0x1F8207DF,
0x0C7DF7DF, 0x2030C820, 0x207DF7E0, 0x1F82081F, 0x203207DF, 0x20832820, 0x2081F820, 0x20820832,
0x1F82081F, 0x207E081F, 0x207DF820, 0x1F7E0320, 0x1F7E07E0, 0x1F81F820, 0x20CA07CC, 0x0C81F7E0,
0x1F820820, 0x1FCA07DF, 0x1F7E080C, 0x208207F2, 0x207E081F, 0x20820820, 0x207E07DF, 0x2082081F,
0x1F7E07DF, 0x1F7DF7E0, 0x207DF820, 0x00000020
};
void main(){
unsigned int status;
unsigned int decoded;
unsigned int version;
int pass = 1;
// --- Setup management GPIO for synchronization ---
ManagmentGpio_outputEnable();
ManagmentGpio_write(0);
enableHkSpi(0);
// Configure all GPIOs as management standard output (driven by firmware)
GPIOs_configureAll(GPIO_MODE_MGMT_STD_OUTPUT);
GPIOs_loadConfigs();
// Clear GPIO output
GPIOs_writeLow(0x00000000);
// Enable Wishbone interface to user project
User_enableIF();
// Signal firmware ready
ManagmentGpio_write(1);
// --- Step 1: Read and verify VERSION register ---
version = USER_readWord(LDPC_VERSION);
if (version != EXPECTED_VERSION) {
pass = 0;
}
// ===== DECODE 1: All-zero codeword =====
// Write all-zero LLRs (+31 confidence in bit=0)
for (int i = 0; i < LLR_WORD_COUNT - 1; i++) {
USER_writeWord(ALL_ZERO_LLR_WORD, LDPC_LLR_BASE + i);
}
// Last word: only LLR[255] = +31, remaining slots zero
USER_writeWord(0x0000001F, LDPC_LLR_BASE + LLR_WORD_COUNT - 1);
// Start decode: start=1, early_term_en=1, max_iter=30
USER_writeWord(CTRL_START | CTRL_EARLY_TERM | CTRL_MAX_ITER(30), LDPC_CTRL);
// Poll until done
do {
status = USER_readWord(LDPC_STATUS);
} while (status & STATUS_BUSY);
// Check converged
if (!(status & STATUS_CONVERGED)) {
pass = 0;
}
// Check syndrome weight = 0
if ((status & STATUS_SYN_MASK) != 0) {
pass = 0;
}
// Check decoded = 0 (all-zero codeword)
decoded = USER_readWord(LDPC_DECODED);
if (decoded != 0x00000000) {
pass = 0;
}
// ===== DECODE 2: Noisy correctable codeword =====
// Write noisy LLRs
for (int i = 0; i < LLR_WORD_COUNT; i++) {
USER_writeWord(noisy_llr_words[i], LDPC_LLR_BASE + i);
}
// Start decode: start=1, early_term_en=1, max_iter=30
USER_writeWord(CTRL_START | CTRL_EARLY_TERM | CTRL_MAX_ITER(30), LDPC_CTRL);
// Poll until done
do {
status = USER_readWord(LDPC_STATUS);
} while (status & STATUS_BUSY);
// Check converged
if (!(status & STATUS_CONVERGED)) {
pass = 0;
}
// Check syndrome weight = 0
if ((status & STATUS_SYN_MASK) != 0) {
pass = 0;
}
// Check decoded matches expected
decoded = USER_readWord(LDPC_DECODED);
if (decoded != EXPECTED_DECODED_VEC0) {
pass = 0;
}
// --- Signal result via GPIO[7:0] ---
if (pass) {
GPIOs_writeLow(PASS_SIGNATURE); // 0xAB = pass (both decodes correct)
} else {
GPIOs_writeLow(FAIL_SIGNATURE); // 0xFF = fail
}
// Signal test complete via management GPIO
ManagmentGpio_write(0);
return;
}

View File

@@ -0,0 +1,84 @@
# SPDX-FileCopyrightText: 2024 LDPC Optical Project
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# SPDX-License-Identifier: Apache-2.0
"""
LDPC Back-to-Back Decode Test - cocotb Monitor
Tests the LDPC decoder with two sequential decodes to verify no state
leakage between runs. Firmware:
1. First decode: all-zero codeword, check converged=1, decoded=0
2. Second decode: noisy correctable codeword, check converged=1,
decoded matches expected
3. Signals pass (0xAB) only if BOTH decodes are correct
The cocotb monitor waits for the management GPIO to signal firmware ready,
then waits for the management GPIO to drop (test complete), and reads
GPIO[7:0] for the pass/fail result.
"""
from caravel_cocotb.caravel_interfaces import test_configure
from caravel_cocotb.caravel_interfaces import report_test
import cocotb
PASS_SIGNATURE = 0xAB
FAIL_SIGNATURE = 0xFF
@cocotb.test()
@report_test
async def ldpc_back_to_back(dut):
caravelEnv = await test_configure(dut, timeout_cycles=500000)
cocotb.log.info("[TEST] Starting LDPC back-to-back decode test")
# Release CSB to allow GPIO to function
await caravelEnv.release_csb()
# Wait for firmware to signal ready (mgmt_gpio = 1)
cocotb.log.info("[TEST] Waiting for firmware ready (mgmt_gpio=1)...")
await caravelEnv.wait_mgmt_gpio(1)
cocotb.log.info("[TEST] Firmware ready, back-to-back decodes in progress...")
# Wait for firmware to signal test complete (mgmt_gpio = 0)
await caravelEnv.wait_mgmt_gpio(0)
cocotb.log.info("[TEST] Firmware signaled test complete")
# Read GPIO[7:0] for pass/fail result
gpio_val = caravelEnv.monitor_gpio(7, 0)
if not gpio_val.is_resolvable:
cocotb.log.error(
f"[TEST] FAIL - GPIO[7:0] is unresolvable: {gpio_val.binstr}"
)
assert False, "GPIO[7:0] has X/Z values"
result = gpio_val.integer
cocotb.log.info(f"[TEST] GPIO[7:0] = 0x{result:02X}")
if result == PASS_SIGNATURE:
cocotb.log.info(
"[TEST] PASS - Both back-to-back decodes correct (0xAB)"
)
elif result == FAIL_SIGNATURE:
cocotb.log.error(
"[TEST] FAIL - Back-to-back decode test failed (0xFF)"
)
assert False, "Firmware reported LDPC back-to-back decode failure"
else:
cocotb.log.error(
f"[TEST] FAIL - Unexpected GPIO value: 0x{result:02X}"
)
assert False, f"Unexpected GPIO result: 0x{result:02X}"

View File

@@ -0,0 +1,19 @@
---
# SPDX-FileCopyrightText: 2024 LDPC Optical Project
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# SPDX-License-Identifier: Apache-2.0
Tests:
- {name: ldpc_back_to_back, sim: RTL}

View File

@@ -0,0 +1,139 @@
// SPDX-FileCopyrightText: 2024 LDPC Optical Project
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
/*
* LDPC Max Iteration Test Firmware
*
* Runs on PicoRV32 inside Caravel. Exercises the LDPC decoder with an
* uncorrectable codeword to verify it hits max iterations:
* 1. Writes uncorrectable noisy LLRs (test vector 11)
* 2. Starts decode with early termination, max_iter=30
* 3. Checks converged=0 (not converged), iterations_used=30 (hit max)
* 4. Signals pass (0xAB) or fail (0xFF) on GPIO[7:0]
*/
#include <firmware_apis.h>
// LDPC register word offsets (byte_addr / 4)
#define LDPC_CTRL 0 // 0x00
#define LDPC_STATUS 1 // 0x04
#define LDPC_LLR_BASE 4 // 0x10
#define LDPC_DECODED 20 // 0x50
#define LDPC_VERSION 21 // 0x54
#define LLR_WORD_COUNT 52 // 260 LLRs / 5 per word, rounded up
// CTRL register fields
#define CTRL_START (1 << 0)
#define CTRL_EARLY_TERM (1 << 1)
#define CTRL_MAX_ITER(n) (((n) & 0x1F) << 8)
// STATUS register fields
#define STATUS_BUSY (1 << 0)
#define STATUS_CONVERGED (1 << 1)
#define STATUS_ITER_MASK (0x1F << 8)
#define STATUS_SYN_MASK (0xFF << 16)
// Expected values
#define EXPECTED_VERSION 0x1D010001
#define PASS_SIGNATURE 0xAB
#define FAIL_SIGNATURE 0xFF
// Test vector 11: uncorrectable codeword (too many errors)
// Expected: converged=False, iterations=30, syndrome_weight=67
static const unsigned int uncorrectable_llr_words[LLR_WORD_COUNT] = {
0x0C30C80C, 0x0C30C30C, 0x2730C820, 0x0C80C30C, 0x0C32730C, 0x0C30C9E7, 0x0CE8C33A, 0x0C9CC320,
0x2032030C, 0x0C32030C, 0x0C30C9E0, 0x209CC320, 0x0C9E730C, 0x0C33A9CC, 0x3A80C30C, 0x0C30C80C,
0x279CC320, 0x0CEA080C, 0x0C30C9CC, 0x279E0320, 0x2730C30C, 0x0CE8C30C, 0x0C80C9CC, 0x0C9CC30C,
0x3AE8CEA0, 0x20E8C320, 0x0C33A80C, 0x0CEBA33A, 0x0C30C9CC, 0x27EA0E8C, 0x0C30CEBA, 0x0CE8C30C,
0x0CEA7EA7, 0x0C30C30C, 0x0C83A327, 0x0CEBA30C, 0x0C83AEA0, 0x2033A80C, 0x0C80C30C, 0x0C30C30C,
0x0C82730C, 0x3A30C33A, 0x3A820E8C, 0x0C30C320, 0x0C30C9E7, 0x279CC320, 0x2080C30C, 0x27320327,
0x3A32083A, 0x0C33A80C, 0x0C9CC30C, 0x0000000C
};
void main(){
unsigned int status;
unsigned int version;
unsigned int iter_used;
int pass = 1;
// --- Setup management GPIO for synchronization ---
ManagmentGpio_outputEnable();
ManagmentGpio_write(0);
enableHkSpi(0);
// Configure all GPIOs as management standard output (driven by firmware)
GPIOs_configureAll(GPIO_MODE_MGMT_STD_OUTPUT);
GPIOs_loadConfigs();
// Clear GPIO output
GPIOs_writeLow(0x00000000);
// Enable Wishbone interface to user project
User_enableIF();
// Signal firmware ready
ManagmentGpio_write(1);
// --- Step 1: Read and verify VERSION register ---
version = USER_readWord(LDPC_VERSION);
if (version != EXPECTED_VERSION) {
pass = 0;
}
// --- Step 2: Write uncorrectable noisy LLRs ---
for (int i = 0; i < LLR_WORD_COUNT; i++) {
USER_writeWord(uncorrectable_llr_words[i], LDPC_LLR_BASE + i);
}
// --- Step 3: Start decode ---
// start=1, early_term_en=1, max_iter=30
USER_writeWord(CTRL_START | CTRL_EARLY_TERM | CTRL_MAX_ITER(30), LDPC_CTRL);
// --- Step 4: Poll STATUS until not busy ---
do {
status = USER_readWord(LDPC_STATUS);
} while (status & STATUS_BUSY);
// --- Step 5: Check results ---
// Should NOT have converged
if (status & STATUS_CONVERGED) {
pass = 0; // Unexpectedly converged
}
// Should have used all 30 iterations
iter_used = (status & STATUS_ITER_MASK) >> 8;
if (iter_used != 30) {
pass = 0; // Did not hit max iterations
}
// Syndrome weight should be non-zero (not converged)
if ((status & STATUS_SYN_MASK) == 0) {
pass = 0; // Syndrome weight unexpectedly zero
}
// --- Step 6: Signal result via GPIO[7:0] ---
if (pass) {
GPIOs_writeLow(PASS_SIGNATURE); // 0xAB = pass
} else {
GPIOs_writeLow(FAIL_SIGNATURE); // 0xFF = fail
}
// Signal test complete via management GPIO
ManagmentGpio_write(0);
return;
}

View File

@@ -0,0 +1,84 @@
# SPDX-FileCopyrightText: 2024 LDPC Optical Project
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# SPDX-License-Identifier: Apache-2.0
"""
LDPC Max Iteration Test - cocotb Monitor
Tests the LDPC decoder with an uncorrectable codeword. Firmware:
1. Verifies the VERSION register
2. Loads uncorrectable noisy LLRs (test vector 11)
3. Runs a decode with early termination, max_iter=30
4. Checks converged=0, iterations_used=30, syndrome_weight>0
5. Signals pass (0xAB) or fail (0xFF) on GPIO[7:0]
The cocotb monitor waits for the management GPIO to signal firmware ready,
then waits for the management GPIO to drop (test complete), and reads
GPIO[7:0] for the pass/fail result.
"""
from caravel_cocotb.caravel_interfaces import test_configure
from caravel_cocotb.caravel_interfaces import report_test
import cocotb
PASS_SIGNATURE = 0xAB
FAIL_SIGNATURE = 0xFF
@cocotb.test()
@report_test
async def ldpc_max_iter(dut):
caravelEnv = await test_configure(dut, timeout_cycles=300000)
cocotb.log.info("[TEST] Starting LDPC max iteration test")
# Release CSB to allow GPIO to function
await caravelEnv.release_csb()
# Wait for firmware to signal ready (mgmt_gpio = 1)
cocotb.log.info("[TEST] Waiting for firmware ready (mgmt_gpio=1)...")
await caravelEnv.wait_mgmt_gpio(1)
cocotb.log.info("[TEST] Firmware ready, decode in progress...")
# Wait for firmware to signal test complete (mgmt_gpio = 0)
await caravelEnv.wait_mgmt_gpio(0)
cocotb.log.info("[TEST] Firmware signaled test complete")
# Read GPIO[7:0] for pass/fail result
gpio_val = caravelEnv.monitor_gpio(7, 0)
if not gpio_val.is_resolvable:
cocotb.log.error(
f"[TEST] FAIL - GPIO[7:0] is unresolvable: {gpio_val.binstr}"
)
assert False, "GPIO[7:0] has X/Z values"
result = gpio_val.integer
cocotb.log.info(f"[TEST] GPIO[7:0] = 0x{result:02X}")
if result == PASS_SIGNATURE:
cocotb.log.info(
"[TEST] PASS - Firmware confirmed max iteration behavior (0xAB)"
)
elif result == FAIL_SIGNATURE:
cocotb.log.error(
"[TEST] FAIL - Firmware reported max iteration test failure (0xFF)"
)
assert False, "Firmware reported LDPC max iteration test failure"
else:
cocotb.log.error(
f"[TEST] FAIL - Unexpected GPIO value: 0x{result:02X}"
)
assert False, f"Unexpected GPIO result: 0x{result:02X}"

View File

@@ -0,0 +1,19 @@
---
# SPDX-FileCopyrightText: 2024 LDPC Optical Project
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# SPDX-License-Identifier: Apache-2.0
Tests:
- {name: ldpc_max_iter, sim: RTL}

View File

@@ -0,0 +1,141 @@
// SPDX-FileCopyrightText: 2024 LDPC Optical Project
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
/*
* LDPC Noisy Decode Test Firmware
*
* Runs on PicoRV32 inside Caravel. Exercises the LDPC decoder with a noisy
* but correctable codeword:
* 1. Writes noisy LLRs (test vector 0 from gen_firmware_vectors.py)
* 2. Starts decode with early termination, max_iter=30
* 3. Checks converged=1, syndrome_weight=0, decoded matches expected
* 4. Signals pass (0xAB) or fail (0xFF) on GPIO[7:0]
*/
#include <firmware_apis.h>
// LDPC register word offsets (byte_addr / 4)
#define LDPC_CTRL 0 // 0x00
#define LDPC_STATUS 1 // 0x04
#define LDPC_LLR_BASE 4 // 0x10
#define LDPC_DECODED 20 // 0x50
#define LDPC_VERSION 21 // 0x54
#define LLR_WORD_COUNT 52 // 260 LLRs / 5 per word, rounded up
// CTRL register fields
#define CTRL_START (1 << 0)
#define CTRL_EARLY_TERM (1 << 1)
#define CTRL_MAX_ITER(n) (((n) & 0x1F) << 8)
// STATUS register fields
#define STATUS_BUSY (1 << 0)
#define STATUS_CONVERGED (1 << 1)
#define STATUS_ITER_MASK (0x1F << 8)
#define STATUS_SYN_MASK (0xFF << 16)
// Expected values
#define EXPECTED_VERSION 0x1D010001
#define PASS_SIGNATURE 0xAB
#define FAIL_SIGNATURE 0xFF
// Test vector 0: noisy but correctable codeword
// Expected decoded word: 0x3FD74222
#define EXPECTED_DECODED 0x3FD74222
static const unsigned int noisy_llr_words[LLR_WORD_COUNT] = {
0x1F7DF81F, 0x20C9F7E0, 0x207CC7DF, 0x1F82081F, 0x328207E0, 0x20820820, 0x208207CC, 0x1F81F7DF,
0x1F7F27DF, 0x1F7CC81F, 0x0C81F81F, 0x207E07F2, 0x1F820820, 0x207DF7CC, 0x1F81F7E0, 0x2082081F,
0x0C31F81F, 0x2081F7DF, 0x1FCA081F, 0x20820820, 0x1F7DF7DF, 0x207E07E0, 0x208207CC, 0x1F8207DF,
0x0C7DF7DF, 0x2030C820, 0x207DF7E0, 0x1F82081F, 0x203207DF, 0x20832820, 0x2081F820, 0x20820832,
0x1F82081F, 0x207E081F, 0x207DF820, 0x1F7E0320, 0x1F7E07E0, 0x1F81F820, 0x20CA07CC, 0x0C81F7E0,
0x1F820820, 0x1FCA07DF, 0x1F7E080C, 0x208207F2, 0x207E081F, 0x20820820, 0x207E07DF, 0x2082081F,
0x1F7E07DF, 0x1F7DF7E0, 0x207DF820, 0x00000020
};
void main(){
unsigned int status;
unsigned int decoded;
unsigned int version;
int pass = 1;
// --- Setup management GPIO for synchronization ---
ManagmentGpio_outputEnable();
ManagmentGpio_write(0);
enableHkSpi(0);
// Configure all GPIOs as management standard output (driven by firmware)
GPIOs_configureAll(GPIO_MODE_MGMT_STD_OUTPUT);
GPIOs_loadConfigs();
// Clear GPIO output
GPIOs_writeLow(0x00000000);
// Enable Wishbone interface to user project
User_enableIF();
// Signal firmware ready
ManagmentGpio_write(1);
// --- Step 1: Read and verify VERSION register ---
version = USER_readWord(LDPC_VERSION);
if (version != EXPECTED_VERSION) {
pass = 0;
}
// --- Step 2: Write noisy LLRs ---
for (int i = 0; i < LLR_WORD_COUNT; i++) {
USER_writeWord(noisy_llr_words[i], LDPC_LLR_BASE + i);
}
// --- Step 3: Start decode ---
// start=1, early_term_en=1, max_iter=30
USER_writeWord(CTRL_START | CTRL_EARLY_TERM | CTRL_MAX_ITER(30), LDPC_CTRL);
// --- Step 4: Poll STATUS until not busy ---
do {
status = USER_readWord(LDPC_STATUS);
} while (status & STATUS_BUSY);
// --- Step 5: Check results ---
// Check converged (bit 1)
if (!(status & STATUS_CONVERGED)) {
pass = 0;
}
// Check syndrome weight = 0 (bits [23:16])
if ((status & STATUS_SYN_MASK) != 0) {
pass = 0;
}
// Read decoded info bits
decoded = USER_readWord(LDPC_DECODED);
if (decoded != EXPECTED_DECODED) {
pass = 0;
}
// --- Step 6: Signal result via GPIO[7:0] ---
if (pass) {
GPIOs_writeLow(PASS_SIGNATURE); // 0xAB = pass
} else {
GPIOs_writeLow(FAIL_SIGNATURE); // 0xFF = fail
}
// Signal test complete via management GPIO
ManagmentGpio_write(0);
return;
}

View File

@@ -0,0 +1,82 @@
# SPDX-FileCopyrightText: 2024 LDPC Optical Project
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# SPDX-License-Identifier: Apache-2.0
"""
LDPC Noisy Decode Test - cocotb Monitor
Tests the LDPC decoder with a noisy but correctable codeword. Firmware:
1. Verifies the VERSION register
2. Loads noisy LLRs (test vector 0)
3. Runs a decode with early termination, max_iter=30
4. Checks converged=1, syndrome_weight=0, decoded matches expected
5. Signals pass (0xAB) or fail (0xFF) on GPIO[7:0]
The cocotb monitor waits for the management GPIO to signal firmware ready,
then waits for the management GPIO to drop (test complete), and reads
GPIO[7:0] for the pass/fail result.
"""
from caravel_cocotb.caravel_interfaces import test_configure
from caravel_cocotb.caravel_interfaces import report_test
import cocotb
PASS_SIGNATURE = 0xAB
FAIL_SIGNATURE = 0xFF
@cocotb.test()
@report_test
async def ldpc_noisy(dut):
caravelEnv = await test_configure(dut, timeout_cycles=300000)
cocotb.log.info("[TEST] Starting LDPC noisy decode test")
# Release CSB to allow GPIO to function
await caravelEnv.release_csb()
# Wait for firmware to signal ready (mgmt_gpio = 1)
cocotb.log.info("[TEST] Waiting for firmware ready (mgmt_gpio=1)...")
await caravelEnv.wait_mgmt_gpio(1)
cocotb.log.info("[TEST] Firmware ready, decode in progress...")
# Wait for firmware to signal test complete (mgmt_gpio = 0)
await caravelEnv.wait_mgmt_gpio(0)
cocotb.log.info("[TEST] Firmware signaled test complete")
# Read GPIO[7:0] for pass/fail result
gpio_val = caravelEnv.monitor_gpio(7, 0)
if not gpio_val.is_resolvable:
cocotb.log.error(
f"[TEST] FAIL - GPIO[7:0] is unresolvable: {gpio_val.binstr}"
)
assert False, "GPIO[7:0] has X/Z values"
result = gpio_val.integer
cocotb.log.info(f"[TEST] GPIO[7:0] = 0x{result:02X}")
if result == PASS_SIGNATURE:
cocotb.log.info("[TEST] PASS - Firmware reported noisy decode success (0xAB)")
elif result == FAIL_SIGNATURE:
cocotb.log.error(
"[TEST] FAIL - Firmware reported noisy decode failure (0xFF)"
)
assert False, "Firmware reported LDPC noisy decode failure"
else:
cocotb.log.error(
f"[TEST] FAIL - Unexpected GPIO value: 0x{result:02X}"
)
assert False, f"Unexpected GPIO result: 0x{result:02X}"

View File

@@ -0,0 +1,19 @@
---
# SPDX-FileCopyrightText: 2024 LDPC Optical Project
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# SPDX-License-Identifier: Apache-2.0
Tests:
- {name: ldpc_noisy, sim: RTL}