feat: add LDPC demo firmware and cocotb test
Demo firmware runs clean decode + noisy decode (vector 0) and reports pass/fail on GPIO[7:0]. All 5 cocotb tests pass: ldpc_basic, ldpc_noisy, ldpc_max_iter, ldpc_back_to_back, and ldpc_demo. Also adds .cf/project.json with GPIO configuration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
49
.cf/project.json
Normal file
49
.cf/project.json
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"project_name": "ldpc_decoder_optical",
|
||||||
|
"description": "LDPC decoder for photon-starved optical communication",
|
||||||
|
"project_type": "digital",
|
||||||
|
"pdk": "sky130A",
|
||||||
|
"created": "2026-02-25",
|
||||||
|
"project": {
|
||||||
|
"gpio_config": {
|
||||||
|
"0": "13'h1809",
|
||||||
|
"1": "13'h1809",
|
||||||
|
"2": "13'h1809",
|
||||||
|
"3": "13'h1809",
|
||||||
|
"4": "13'h1809",
|
||||||
|
"5": "13'h1809",
|
||||||
|
"6": "13'h1809",
|
||||||
|
"7": "13'h1809",
|
||||||
|
"8": "13'h1809",
|
||||||
|
"9": "13'h1809",
|
||||||
|
"10": "13'h1809",
|
||||||
|
"11": "13'h1809",
|
||||||
|
"12": "13'h1809",
|
||||||
|
"13": "13'h1809",
|
||||||
|
"14": "13'h1809",
|
||||||
|
"15": "13'h1809",
|
||||||
|
"16": "13'h1809",
|
||||||
|
"17": "13'h1809",
|
||||||
|
"18": "13'h1809",
|
||||||
|
"19": "13'h1809",
|
||||||
|
"20": "13'h1809",
|
||||||
|
"21": "13'h1809",
|
||||||
|
"22": "13'h1809",
|
||||||
|
"23": "13'h1809",
|
||||||
|
"24": "13'h1809",
|
||||||
|
"25": "13'h1809",
|
||||||
|
"26": "13'h1809",
|
||||||
|
"27": "13'h1809",
|
||||||
|
"28": "13'h1809",
|
||||||
|
"29": "13'h1809",
|
||||||
|
"30": "13'h1809",
|
||||||
|
"31": "13'h1809",
|
||||||
|
"32": "13'h1809",
|
||||||
|
"33": "13'h1809",
|
||||||
|
"34": "13'h1809",
|
||||||
|
"35": "13'h1809",
|
||||||
|
"36": "13'h1809",
|
||||||
|
"37": "13'h1809"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
337
firmware/ldpc_demo/ldpc_demo.c
Normal file
337
firmware/ldpc_demo/ldpc_demo.c
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 LDPC Optical Project
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LDPC Decoder Demo Firmware
|
||||||
|
*
|
||||||
|
* Runs on PicoRV32 inside Caravel. Demonstrates the LDPC decoder by running
|
||||||
|
* three scenarios and reporting results via UART and GPIO:
|
||||||
|
*
|
||||||
|
* Scenario 1: Clean all-zero codeword (should decode in 1 iteration)
|
||||||
|
* Scenario 2: Noisy but correctable codeword (test vector 0)
|
||||||
|
* Scenario 3: Stress test - all 20 test vectors back to back
|
||||||
|
*
|
||||||
|
* UART output format (115200 baud, 8N1):
|
||||||
|
* LDPC Decoder Demo v1.0
|
||||||
|
* VERSION: 1D010001
|
||||||
|
* --- Scenario 1: Clean decode ---
|
||||||
|
* LLR: all +31 (zero codeword)
|
||||||
|
* STATUS: 00001E02 DECODED: 00000000
|
||||||
|
* PASS: converged in 1 iter, syndrome=0
|
||||||
|
* --- Scenario 2: Noisy decode ---
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* GPIO[7:0] final status:
|
||||||
|
* 0xAB = all scenarios passed
|
||||||
|
* 0xFF = at least one scenario failed
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <firmware_apis.h>
|
||||||
|
#include "test_vectors.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
|
||||||
|
|
||||||
|
// 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_SHIFT 8
|
||||||
|
#define STATUS_ITER_MASK (0x1F << STATUS_ITER_SHIFT)
|
||||||
|
#define STATUS_SYN_SHIFT 16
|
||||||
|
#define STATUS_SYN_MASK (0xFF << STATUS_SYN_SHIFT)
|
||||||
|
|
||||||
|
#define EXPECTED_VERSION 0x1D010001
|
||||||
|
#define PASS_SIGNATURE 0xAB
|
||||||
|
#define FAIL_SIGNATURE 0xFF
|
||||||
|
|
||||||
|
// All-zero codeword LLR word (5x +31)
|
||||||
|
#define ALL_ZERO_LLR_WORD 0x1F7DF7DF
|
||||||
|
|
||||||
|
// Simple hex print (8 chars, uppercase)
|
||||||
|
static void print_hex(unsigned int val) {
|
||||||
|
static const char hex[] = "0123456789ABCDEF";
|
||||||
|
for (int i = 28; i >= 0; i -= 4) {
|
||||||
|
UART_sendChar(hex[(val >> i) & 0xF]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_dec(unsigned int val) {
|
||||||
|
if (val == 0) {
|
||||||
|
UART_sendChar('0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char buf[10];
|
||||||
|
int n = 0;
|
||||||
|
while (val > 0) {
|
||||||
|
buf[n++] = '0' + (val % 10);
|
||||||
|
val /= 10;
|
||||||
|
}
|
||||||
|
for (int i = n - 1; i >= 0; i--) {
|
||||||
|
UART_sendChar(buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void println(const char *s) {
|
||||||
|
while (*s) UART_sendChar(*s++);
|
||||||
|
UART_sendChar('\r');
|
||||||
|
UART_sendChar('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_str(const char *s) {
|
||||||
|
while (*s) UART_sendChar(*s++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write LLR words to decoder and start decode
|
||||||
|
static unsigned int run_decode(const unsigned int *llr_words, int count) {
|
||||||
|
// Write LLRs
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
USER_writeWord(llr_words[i], LDPC_LLR_BASE + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start: early_term=1, max_iter=30, start=1
|
||||||
|
USER_writeWord(CTRL_START | CTRL_EARLY_TERM | CTRL_MAX_ITER(30), LDPC_CTRL);
|
||||||
|
|
||||||
|
// Poll until not busy
|
||||||
|
unsigned int status;
|
||||||
|
do {
|
||||||
|
status = USER_readWord(LDPC_STATUS);
|
||||||
|
} while (status & STATUS_BUSY);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
int total_pass = 1;
|
||||||
|
int scenario_pass;
|
||||||
|
unsigned int status, decoded, version;
|
||||||
|
|
||||||
|
// --- Hardware setup ---
|
||||||
|
ManagmentGpio_outputEnable();
|
||||||
|
ManagmentGpio_write(0);
|
||||||
|
enableHkSpi(0);
|
||||||
|
|
||||||
|
// GPIO[7:0] as management output, GPIO[5:6] for UART
|
||||||
|
GPIOs_configure(5, GPIO_MODE_MGMT_STD_OUTPUT); // UART TX
|
||||||
|
GPIOs_configure(6, GPIO_MODE_MGMT_STD_INPUT_NOPULL); // UART RX
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if (i != 5 && i != 6)
|
||||||
|
GPIOs_configure(i, GPIO_MODE_MGMT_STD_OUTPUT);
|
||||||
|
}
|
||||||
|
GPIOs_loadConfigs();
|
||||||
|
GPIOs_writeLow(0x00000000);
|
||||||
|
|
||||||
|
// Enable UART (115200 baud) and Wishbone
|
||||||
|
UART_enableTX(1);
|
||||||
|
User_enableIF();
|
||||||
|
|
||||||
|
// Signal ready
|
||||||
|
ManagmentGpio_write(1);
|
||||||
|
|
||||||
|
// --- Banner ---
|
||||||
|
println("LDPC Decoder Demo v1.0");
|
||||||
|
println("Rate 1/8, n=256, k=32, Z=32");
|
||||||
|
println("Offset min-sum, layered scheduling");
|
||||||
|
println("");
|
||||||
|
|
||||||
|
// --- Version check ---
|
||||||
|
version = USER_readWord(LDPC_VERSION);
|
||||||
|
print_str("VERSION: ");
|
||||||
|
print_hex(version);
|
||||||
|
println("");
|
||||||
|
if (version != EXPECTED_VERSION) {
|
||||||
|
println("ERROR: unexpected version");
|
||||||
|
total_pass = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Scenario 1: Clean all-zero codeword
|
||||||
|
// ============================================================
|
||||||
|
println("--- Scenario 1: Clean decode ---");
|
||||||
|
println("Input: all-zero codeword, LLR=+31");
|
||||||
|
|
||||||
|
// Build LLR words on stack (all +31)
|
||||||
|
unsigned int clean_llr[LLR_WORDS_PER_VECTOR];
|
||||||
|
for (int i = 0; i < LLR_WORDS_PER_VECTOR - 1; i++) {
|
||||||
|
clean_llr[i] = ALL_ZERO_LLR_WORD;
|
||||||
|
}
|
||||||
|
clean_llr[LLR_WORDS_PER_VECTOR - 1] = 0x0000001F; // last word: 1 LLR
|
||||||
|
|
||||||
|
status = run_decode(clean_llr, LLR_WORDS_PER_VECTOR);
|
||||||
|
decoded = USER_readWord(LDPC_DECODED);
|
||||||
|
|
||||||
|
print_str("STATUS: ");
|
||||||
|
print_hex(status);
|
||||||
|
print_str(" DECODED: ");
|
||||||
|
print_hex(decoded);
|
||||||
|
println("");
|
||||||
|
|
||||||
|
scenario_pass = 1;
|
||||||
|
if (!(status & STATUS_CONVERGED)) {
|
||||||
|
println("FAIL: did not converge");
|
||||||
|
scenario_pass = 0;
|
||||||
|
}
|
||||||
|
if ((status & STATUS_SYN_MASK) != 0) {
|
||||||
|
println("FAIL: nonzero syndrome");
|
||||||
|
scenario_pass = 0;
|
||||||
|
}
|
||||||
|
if (decoded != 0x00000000) {
|
||||||
|
println("FAIL: wrong decoded bits");
|
||||||
|
scenario_pass = 0;
|
||||||
|
}
|
||||||
|
if (scenario_pass) {
|
||||||
|
unsigned int iters = (status & STATUS_ITER_MASK) >> STATUS_ITER_SHIFT;
|
||||||
|
print_str("PASS: converged in ");
|
||||||
|
print_dec(iters);
|
||||||
|
println(" iterations");
|
||||||
|
} else {
|
||||||
|
total_pass = 0;
|
||||||
|
}
|
||||||
|
println("");
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Scenario 2: Noisy but correctable codeword (vector 0)
|
||||||
|
// ============================================================
|
||||||
|
println("--- Scenario 2: Noisy decode ---");
|
||||||
|
print_str("Expected decoded: ");
|
||||||
|
print_hex(tv0_decoded);
|
||||||
|
println("");
|
||||||
|
|
||||||
|
status = run_decode(tv0_llr, LLR_WORDS_PER_VECTOR);
|
||||||
|
decoded = USER_readWord(LDPC_DECODED);
|
||||||
|
|
||||||
|
print_str("STATUS: ");
|
||||||
|
print_hex(status);
|
||||||
|
print_str(" DECODED: ");
|
||||||
|
print_hex(decoded);
|
||||||
|
println("");
|
||||||
|
|
||||||
|
scenario_pass = 1;
|
||||||
|
if (!(status & STATUS_CONVERGED)) {
|
||||||
|
println("FAIL: did not converge");
|
||||||
|
scenario_pass = 0;
|
||||||
|
}
|
||||||
|
if (decoded != tv0_decoded) {
|
||||||
|
println("FAIL: decoded mismatch");
|
||||||
|
scenario_pass = 0;
|
||||||
|
}
|
||||||
|
if (scenario_pass) {
|
||||||
|
unsigned int iters = (status & STATUS_ITER_MASK) >> STATUS_ITER_SHIFT;
|
||||||
|
print_str("PASS: corrected in ");
|
||||||
|
print_dec(iters);
|
||||||
|
println(" iterations");
|
||||||
|
} else {
|
||||||
|
total_pass = 0;
|
||||||
|
}
|
||||||
|
println("");
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Scenario 3: Stress test - all 20 vectors
|
||||||
|
// ============================================================
|
||||||
|
println("--- Scenario 3: Stress test (20 vectors) ---");
|
||||||
|
|
||||||
|
// Pointers to all test vector LLR arrays
|
||||||
|
const unsigned int * const tv_llr[NUM_TEST_VECTORS] = {
|
||||||
|
tv0_llr, tv1_llr, tv2_llr, tv3_llr, tv4_llr,
|
||||||
|
tv5_llr, tv6_llr, tv7_llr, tv8_llr, tv9_llr,
|
||||||
|
tv10_llr, tv11_llr, tv12_llr, tv13_llr, tv14_llr,
|
||||||
|
tv15_llr, tv16_llr, tv17_llr, tv18_llr, tv19_llr
|
||||||
|
};
|
||||||
|
const unsigned int tv_decoded[NUM_TEST_VECTORS] = {
|
||||||
|
tv0_decoded, tv1_decoded, tv2_decoded, tv3_decoded, tv4_decoded,
|
||||||
|
tv5_decoded, tv6_decoded, tv7_decoded, tv8_decoded, tv9_decoded,
|
||||||
|
tv10_decoded, tv11_decoded, tv12_decoded, tv13_decoded, tv14_decoded,
|
||||||
|
tv15_decoded, tv16_decoded, tv17_decoded, tv18_decoded, tv19_decoded
|
||||||
|
};
|
||||||
|
const int tv_converged[NUM_TEST_VECTORS] = {
|
||||||
|
tv0_converged, tv1_converged, tv2_converged, tv3_converged, tv4_converged,
|
||||||
|
tv5_converged, tv6_converged, tv7_converged, tv8_converged, tv9_converged,
|
||||||
|
tv10_converged, tv11_converged, tv12_converged, tv13_converged, tv14_converged,
|
||||||
|
tv15_converged, tv16_converged, tv17_converged, tv18_converged, tv19_converged
|
||||||
|
};
|
||||||
|
|
||||||
|
int pass_count = 0;
|
||||||
|
int fail_count = 0;
|
||||||
|
|
||||||
|
for (int v = 0; v < NUM_TEST_VECTORS; v++) {
|
||||||
|
status = run_decode(tv_llr[v], LLR_WORDS_PER_VECTOR);
|
||||||
|
decoded = USER_readWord(LDPC_DECODED);
|
||||||
|
|
||||||
|
unsigned int iters = (status & STATUS_ITER_MASK) >> STATUS_ITER_SHIFT;
|
||||||
|
int converged = (status & STATUS_CONVERGED) ? 1 : 0;
|
||||||
|
|
||||||
|
print_str("V");
|
||||||
|
print_dec(v);
|
||||||
|
print_str(": ");
|
||||||
|
|
||||||
|
// For converged vectors, check decoded matches expected
|
||||||
|
if (tv_converged[v]) {
|
||||||
|
if (converged && decoded == tv_decoded[v]) {
|
||||||
|
print_str("PASS ");
|
||||||
|
print_dec(iters);
|
||||||
|
println(" iters");
|
||||||
|
pass_count++;
|
||||||
|
} else {
|
||||||
|
print_str("FAIL got=");
|
||||||
|
print_hex(decoded);
|
||||||
|
print_str(" exp=");
|
||||||
|
print_hex(tv_decoded[v]);
|
||||||
|
println("");
|
||||||
|
fail_count++;
|
||||||
|
total_pass = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unconverged vector: just check it didn't falsely converge
|
||||||
|
// (or if it did converge with correct result, that's also OK)
|
||||||
|
if (!converged) {
|
||||||
|
print_str("OK uncorrectable ");
|
||||||
|
print_dec(iters);
|
||||||
|
println(" iters");
|
||||||
|
pass_count++;
|
||||||
|
} else if (decoded == tv_decoded[v]) {
|
||||||
|
print_str("OK converged ");
|
||||||
|
print_dec(iters);
|
||||||
|
println(" iters");
|
||||||
|
pass_count++;
|
||||||
|
} else {
|
||||||
|
print_str("FAIL false-converge got=");
|
||||||
|
print_hex(decoded);
|
||||||
|
println("");
|
||||||
|
fail_count++;
|
||||||
|
total_pass = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print_str("Results: ");
|
||||||
|
print_dec(pass_count);
|
||||||
|
print_str("/");
|
||||||
|
print_dec(pass_count + fail_count);
|
||||||
|
println(" passed");
|
||||||
|
println("");
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Final result
|
||||||
|
// ============================================================
|
||||||
|
if (total_pass) {
|
||||||
|
println("=== ALL SCENARIOS PASSED ===");
|
||||||
|
GPIOs_writeLow(PASS_SIGNATURE);
|
||||||
|
} else {
|
||||||
|
println("=== SOME SCENARIOS FAILED ===");
|
||||||
|
GPIOs_writeLow(FAIL_SIGNATURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal test complete
|
||||||
|
ManagmentGpio_write(0);
|
||||||
|
|
||||||
|
// Halt
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
@@ -9,3 +9,4 @@ from ldpc_tests.ldpc_basic.ldpc_basic import ldpc_basic
|
|||||||
from ldpc_tests.ldpc_noisy.ldpc_noisy import ldpc_noisy
|
from ldpc_tests.ldpc_noisy.ldpc_noisy import ldpc_noisy
|
||||||
from ldpc_tests.ldpc_max_iter.ldpc_max_iter import ldpc_max_iter
|
from ldpc_tests.ldpc_max_iter.ldpc_max_iter import ldpc_max_iter
|
||||||
from ldpc_tests.ldpc_back_to_back.ldpc_back_to_back import ldpc_back_to_back
|
from ldpc_tests.ldpc_back_to_back.ldpc_back_to_back import ldpc_back_to_back
|
||||||
|
from ldpc_tests.ldpc_demo.ldpc_demo import ldpc_demo
|
||||||
|
|||||||
116
verilog/dv/cocotb/ldpc_tests/ldpc_demo/ldpc_demo.c
Normal file
116
verilog/dv/cocotb/ldpc_tests/ldpc_demo/ldpc_demo.c
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 LDPC Optical Project
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Symlink/copy to the actual demo firmware
|
||||||
|
// This file just includes the demo firmware from its canonical location
|
||||||
|
// so that caravel_cocotb can find it in the test directory.
|
||||||
|
|
||||||
|
// Note: caravel_cocotb expects the C file in the test directory.
|
||||||
|
// We keep the actual firmware in firmware/ldpc_demo/ for standalone builds.
|
||||||
|
// For cocotb, we duplicate the essential parts here.
|
||||||
|
|
||||||
|
#include <firmware_apis.h>
|
||||||
|
|
||||||
|
// Pull in test vectors (these are in firmware/ldpc_demo/)
|
||||||
|
// For cocotb, we need the vectors accessible from include path
|
||||||
|
// The test vector data is embedded directly via test_data definitions
|
||||||
|
|
||||||
|
// 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 CTRL_START (1 << 0)
|
||||||
|
#define CTRL_EARLY_TERM (1 << 1)
|
||||||
|
#define CTRL_MAX_ITER(n) (((n) & 0x1F) << 8)
|
||||||
|
|
||||||
|
#define STATUS_BUSY (1 << 0)
|
||||||
|
#define STATUS_CONVERGED (1 << 1)
|
||||||
|
#define STATUS_ITER_SHIFT 8
|
||||||
|
#define STATUS_ITER_MASK (0x1F << STATUS_ITER_SHIFT)
|
||||||
|
|
||||||
|
#define EXPECTED_VERSION 0x1D010001
|
||||||
|
#define PASS_SIGNATURE 0xAB
|
||||||
|
#define FAIL_SIGNATURE 0xFF
|
||||||
|
|
||||||
|
#define ALL_ZERO_LLR_WORD 0x1F7DF7DF
|
||||||
|
#define LLR_WORD_COUNT 52
|
||||||
|
|
||||||
|
// Write LLRs and start decode, return STATUS
|
||||||
|
static unsigned int run_decode(const unsigned int *llr, int count) {
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
USER_writeWord(llr[i], LDPC_LLR_BASE + i);
|
||||||
|
USER_writeWord(CTRL_START | CTRL_EARLY_TERM | CTRL_MAX_ITER(30), LDPC_CTRL);
|
||||||
|
unsigned int st;
|
||||||
|
do { st = USER_readWord(LDPC_STATUS); } while (st & STATUS_BUSY);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
int pass = 1;
|
||||||
|
unsigned int status, decoded, version;
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
ManagmentGpio_outputEnable();
|
||||||
|
ManagmentGpio_write(0);
|
||||||
|
enableHkSpi(0);
|
||||||
|
|
||||||
|
GPIOs_configureAll(GPIO_MODE_MGMT_STD_OUTPUT);
|
||||||
|
GPIOs_loadConfigs();
|
||||||
|
GPIOs_writeLow(0x00000000);
|
||||||
|
User_enableIF();
|
||||||
|
ManagmentGpio_write(1);
|
||||||
|
|
||||||
|
// Check version
|
||||||
|
version = USER_readWord(LDPC_VERSION);
|
||||||
|
if (version != EXPECTED_VERSION) pass = 0;
|
||||||
|
|
||||||
|
// Scenario 1: clean all-zero codeword
|
||||||
|
{
|
||||||
|
unsigned int llr[LLR_WORD_COUNT];
|
||||||
|
for (int i = 0; i < LLR_WORD_COUNT - 1; i++) llr[i] = ALL_ZERO_LLR_WORD;
|
||||||
|
llr[LLR_WORD_COUNT - 1] = 0x0000001F;
|
||||||
|
|
||||||
|
status = run_decode(llr, LLR_WORD_COUNT);
|
||||||
|
decoded = USER_readWord(LDPC_DECODED);
|
||||||
|
|
||||||
|
if (!(status & STATUS_CONVERGED)) pass = 0;
|
||||||
|
if (decoded != 0x00000000) pass = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scenario 2: noisy decode (vector 0 from test_data.py)
|
||||||
|
// Inline test vector 0 LLR words
|
||||||
|
{
|
||||||
|
static const unsigned int tv0_llr[52] = {
|
||||||
|
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
|
||||||
|
};
|
||||||
|
status = run_decode(tv0_llr, 52);
|
||||||
|
decoded = USER_readWord(LDPC_DECODED);
|
||||||
|
|
||||||
|
if (!(status & STATUS_CONVERGED)) pass = 0;
|
||||||
|
if (decoded != 0x3FD74222) pass = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report result
|
||||||
|
if (pass) {
|
||||||
|
GPIOs_writeLow(PASS_SIGNATURE);
|
||||||
|
} else {
|
||||||
|
GPIOs_writeLow(FAIL_SIGNATURE);
|
||||||
|
}
|
||||||
|
ManagmentGpio_write(0);
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
76
verilog/dv/cocotb/ldpc_tests/ldpc_demo/ldpc_demo.py
Normal file
76
verilog/dv/cocotb/ldpc_tests/ldpc_demo/ldpc_demo.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# 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 Demo Firmware Test - cocotb Monitor
|
||||||
|
|
||||||
|
Runs the demo firmware which exercises:
|
||||||
|
1. Clean all-zero codeword decode
|
||||||
|
2. Noisy correctable codeword decode (test vector 0)
|
||||||
|
|
||||||
|
Monitors GPIO[7:0] for pass (0xAB) / fail (0xFF) via management GPIO.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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_demo(dut):
|
||||||
|
caravelEnv = await test_configure(dut, timeout_cycles=500000)
|
||||||
|
|
||||||
|
cocotb.log.info("[TEST] Starting LDPC demo firmware test")
|
||||||
|
|
||||||
|
await caravelEnv.release_csb()
|
||||||
|
|
||||||
|
# Wait for firmware ready
|
||||||
|
cocotb.log.info("[TEST] Waiting for firmware ready (mgmt_gpio=1)...")
|
||||||
|
await caravelEnv.wait_mgmt_gpio(1)
|
||||||
|
cocotb.log.info("[TEST] Firmware ready, running demo scenarios...")
|
||||||
|
|
||||||
|
# Wait for test complete
|
||||||
|
await caravelEnv.wait_mgmt_gpio(0)
|
||||||
|
cocotb.log.info("[TEST] Firmware signaled test complete")
|
||||||
|
|
||||||
|
# Read 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 - Demo firmware all scenarios passed (0xAB)")
|
||||||
|
elif result == FAIL_SIGNATURE:
|
||||||
|
cocotb.log.error(
|
||||||
|
"[TEST] FAIL - Demo firmware reported failure (0xFF)"
|
||||||
|
)
|
||||||
|
assert False, "Demo firmware reported failure"
|
||||||
|
else:
|
||||||
|
cocotb.log.error(
|
||||||
|
f"[TEST] FAIL - Unexpected GPIO value: 0x{result:02X}"
|
||||||
|
)
|
||||||
|
assert False, f"Unexpected GPIO result: 0x{result:02X}"
|
||||||
19
verilog/dv/cocotb/ldpc_tests/ldpc_demo/ldpc_demo.yaml
Normal file
19
verilog/dv/cocotb/ldpc_tests/ldpc_demo/ldpc_demo.yaml
Normal 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_demo, sim: RTL}
|
||||||
Reference in New Issue
Block a user