20 tasks across 8 weeks covering RTL integration, verification, OpenLane hardening, firmware, PCBA, and final submission. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1283 lines
34 KiB
Markdown
1283 lines
34 KiB
Markdown
# ChipFoundry Contest Submission Implementation Plan
|
|
|
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
|
|
**Goal:** Integrate the LDPC decoder into the Caravel chip_ignite template, verify with cocotb, harden with OpenLane, and submit proposal by March 25.
|
|
|
|
**Architecture:** Wire existing `ldpc_decoder_top` into `user_project_wrapper.v`, inverting reset polarity and connecting lower 8 address bits. PicoRV32 firmware drives test vectors over Wishbone. cocotb monitors UART/GPIO for pass/fail.
|
|
|
|
**Tech Stack:** SystemVerilog RTL, OpenLane 2.0 (LibreLane), cocotb + caravel_cocotb, PicoRV32 bare-metal C, Python 3 (test vector generation), KiCad (PCBA, later weeks)
|
|
|
|
---
|
|
|
|
## Week 1: RTL Integration & Standalone Verification
|
|
|
|
### Task 1: Copy RTL into chip_ignite and adapt for Caravel
|
|
|
|
**Files:**
|
|
- Copy: `rtl/ldpc_decoder_core.sv` -> `chip_ignite/verilog/rtl/ldpc_decoder_core.sv`
|
|
- Copy: `rtl/wishbone_interface.sv` -> `chip_ignite/verilog/rtl/wishbone_interface.sv`
|
|
- Create: `chip_ignite/verilog/rtl/ldpc_decoder_top.sv` (adapted from `rtl/ldpc_decoder_top.sv`)
|
|
- Modify: `chip_ignite/verilog/rtl/user_project_wrapper.v`
|
|
|
|
**Step 1: Copy unmodified RTL files**
|
|
|
|
```bash
|
|
cp rtl/ldpc_decoder_core.sv chip_ignite/verilog/rtl/
|
|
cp rtl/wishbone_interface.sv chip_ignite/verilog/rtl/
|
|
```
|
|
|
|
**Step 2: Create Caravel-adapted ldpc_decoder_top.sv**
|
|
|
|
The original `ldpc_decoder_top.sv` uses `rst_n` (active-low) and 8-bit address. Caravel provides `wb_rst_i` (active-high) and 32-bit address. Create an adapted version:
|
|
|
|
```systemverilog
|
|
// LDPC Decoder Top - Caravel-adapted wrapper
|
|
// Handles reset polarity inversion and address bus width adaptation
|
|
//
|
|
// Changes from standalone version:
|
|
// - rst_n derived from wb_rst_i (active-high -> active-low)
|
|
// - wb_adr_i connected from lower 8 bits of 32-bit Caravel address
|
|
// - USE_POWER_PINS support for Caravel integration
|
|
|
|
module ldpc_decoder_top #(
|
|
parameter N_BASE = 8,
|
|
parameter M_BASE = 7,
|
|
parameter Z = 32,
|
|
parameter N = N_BASE * Z, // 256
|
|
parameter K = Z, // 32
|
|
parameter M = M_BASE * Z, // 224
|
|
parameter Q = 6,
|
|
parameter MAX_ITER = 30,
|
|
parameter DC = 8,
|
|
parameter DV_MAX = 7
|
|
)(
|
|
`ifdef USE_POWER_PINS
|
|
inout vccd1,
|
|
inout vssd1,
|
|
`endif
|
|
input logic clk,
|
|
input logic rst_n,
|
|
|
|
// Wishbone B4 slave interface
|
|
input logic wb_cyc_i,
|
|
input logic wb_stb_i,
|
|
input logic wb_we_i,
|
|
input logic [3:0] wb_sel_i, // byte selects (unused, for Caravel compat)
|
|
input logic [31:0] wb_adr_i, // full 32-bit address from Caravel
|
|
input logic [31:0] wb_dat_i,
|
|
output logic [31:0] wb_dat_o,
|
|
output logic wb_ack_o,
|
|
|
|
// Interrupt
|
|
output logic irq_o
|
|
);
|
|
|
|
// Internal signals
|
|
logic ctrl_start;
|
|
logic ctrl_early_term;
|
|
logic [4:0] ctrl_max_iter;
|
|
logic stat_busy;
|
|
logic stat_converged;
|
|
logic [4:0] stat_iter_used;
|
|
logic signed [Q-1:0] llr_input [N];
|
|
logic [K-1:0] decoded_bits;
|
|
logic [7:0] syndrome_weight;
|
|
|
|
wishbone_interface #(
|
|
.N(N), .K(K), .Q(Q)
|
|
) u_wb (
|
|
.clk (clk),
|
|
.rst_n (rst_n),
|
|
.wb_cyc_i (wb_cyc_i),
|
|
.wb_stb_i (wb_stb_i),
|
|
.wb_we_i (wb_we_i),
|
|
.wb_adr_i (wb_adr_i[7:0]), // lower 8 bits only
|
|
.wb_dat_i (wb_dat_i),
|
|
.wb_dat_o (wb_dat_o),
|
|
.wb_ack_o (wb_ack_o),
|
|
.ctrl_start (ctrl_start),
|
|
.ctrl_early_term(ctrl_early_term),
|
|
.ctrl_max_iter (ctrl_max_iter),
|
|
.stat_busy (stat_busy),
|
|
.stat_converged (stat_converged),
|
|
.stat_iter_used (stat_iter_used),
|
|
.llr_input (llr_input),
|
|
.decoded_bits (decoded_bits),
|
|
.syndrome_weight(syndrome_weight),
|
|
.irq_o (irq_o)
|
|
);
|
|
|
|
ldpc_decoder_core #(
|
|
.N_BASE (N_BASE),
|
|
.M_BASE (M_BASE),
|
|
.Z (Z),
|
|
.Q (Q),
|
|
.MAX_ITER (MAX_ITER),
|
|
.DC (DC),
|
|
.DV_MAX (DV_MAX)
|
|
) u_core (
|
|
.clk (clk),
|
|
.rst_n (rst_n),
|
|
.start (ctrl_start),
|
|
.early_term_en (ctrl_early_term),
|
|
.max_iter (ctrl_max_iter),
|
|
.llr_in (llr_input),
|
|
.busy (stat_busy),
|
|
.converged (stat_converged),
|
|
.iter_used (stat_iter_used),
|
|
.decoded_bits (decoded_bits),
|
|
.syndrome_weight(syndrome_weight)
|
|
);
|
|
|
|
endmodule
|
|
```
|
|
|
|
**Step 3: Modify user_project_wrapper.v**
|
|
|
|
Replace the `user_proj_example` instantiation with `ldpc_decoder_top`:
|
|
|
|
```verilog
|
|
/* In user_project_wrapper.v, replace the user_proj_example instantiation block with: */
|
|
|
|
ldpc_decoder_top mprj (
|
|
`ifdef USE_POWER_PINS
|
|
.vccd1(vccd1),
|
|
.vssd1(vssd1),
|
|
`endif
|
|
.clk (wb_clk_i),
|
|
.rst_n (~wb_rst_i), // Caravel active-high -> LDPC active-low
|
|
|
|
// Wishbone slave
|
|
.wb_cyc_i (wbs_cyc_i),
|
|
.wb_stb_i (wbs_stb_i),
|
|
.wb_we_i (wbs_we_i),
|
|
.wb_sel_i (wbs_sel_i),
|
|
.wb_adr_i (wbs_adr_i),
|
|
.wb_dat_i (wbs_dat_i),
|
|
.wb_dat_o (wbs_dat_o),
|
|
.wb_ack_o (wbs_ack_o),
|
|
|
|
// Interrupt (active high, directly to Caravel IRQ[0])
|
|
.irq_o (user_irq[0])
|
|
);
|
|
|
|
// Tie off unused outputs
|
|
assign la_data_out = 128'b0;
|
|
assign io_out = {`MPRJ_IO_PADS{1'b0}};
|
|
assign io_oeb = {`MPRJ_IO_PADS{1'b1}}; // all inputs
|
|
assign user_irq[2:1] = 2'b0;
|
|
```
|
|
|
|
**Step 4: Update RTL include file**
|
|
|
|
Edit `chip_ignite/verilog/includes/includes.rtl.caravel_user_project`:
|
|
|
|
```
|
|
-v $(USER_PROJECT_VERILOG)/rtl/defines.v
|
|
-v $(USER_PROJECT_VERILOG)/rtl/user_project_wrapper.v
|
|
-v $(USER_PROJECT_VERILOG)/rtl/ldpc_decoder_top.sv
|
|
-v $(USER_PROJECT_VERILOG)/rtl/ldpc_decoder_core.sv
|
|
-v $(USER_PROJECT_VERILOG)/rtl/wishbone_interface.sv
|
|
```
|
|
|
|
Remove the `user_proj_example.v` line.
|
|
|
|
**Step 5: Lint check**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
verilator --lint-only -Wall \
|
|
verilog/rtl/ldpc_decoder_top.sv \
|
|
verilog/rtl/ldpc_decoder_core.sv \
|
|
verilog/rtl/wishbone_interface.sv \
|
|
-DUSE_POWER_PINS \
|
|
--top-module ldpc_decoder_top
|
|
```
|
|
|
|
Expected: 0 errors, warnings acceptable (unused `wb_sel_i`).
|
|
|
|
**Step 6: Commit**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
git add verilog/rtl/ldpc_decoder_top.sv \
|
|
verilog/rtl/ldpc_decoder_core.sv \
|
|
verilog/rtl/wishbone_interface.sv \
|
|
verilog/rtl/user_project_wrapper.v \
|
|
verilog/includes/includes.rtl.caravel_user_project
|
|
git commit -m "feat: integrate LDPC decoder into Caravel wrapper"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 2: Generate test vectors from Python model
|
|
|
|
**Files:**
|
|
- Run: `model/ldpc_sim.py --gen-vectors`
|
|
- Create: `model/gen_firmware_vectors.py`
|
|
- Output: `data/test_vectors.json`
|
|
- Output: `chip_ignite/verilog/dv/cocotb/ldpc_tests/test_data.py`
|
|
|
|
**Step 1: Generate test vectors**
|
|
|
|
```bash
|
|
cd /home/cah/r2d2/code/fpga/claude_project/ldpc_optical
|
|
python3 model/ldpc_sim.py --gen-vectors --lam-s 2.0 --lam-b 0.1 --seed 42
|
|
```
|
|
|
|
Expected: Creates `data/test_vectors.json` with 20 test vectors.
|
|
|
|
**Step 2: Verify test vectors are usable**
|
|
|
|
```bash
|
|
python3 -c "
|
|
import json
|
|
with open('data/test_vectors.json') as f:
|
|
vecs = json.load(f)
|
|
print(f'Vectors: {len(vecs)}')
|
|
v = vecs[0]
|
|
print(f'LLR count: {len(v[\"llr_quantized\"])}')
|
|
print(f'Info bits: {len(v[\"info_bits\"])}')
|
|
print(f'Converged: {v[\"converged\"]}')
|
|
print(f'Iterations: {v[\"iterations\"]}')
|
|
"
|
|
```
|
|
|
|
Expected: 20 vectors, 256 LLRs each, 32 info bits each.
|
|
|
|
**Step 3: Create Python converter for cocotb test data**
|
|
|
|
Create `model/gen_firmware_vectors.py`:
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
"""Convert test_vectors.json to Python test data module for cocotb tests
|
|
and C header for PicoRV32 firmware."""
|
|
|
|
import json
|
|
import sys
|
|
import os
|
|
|
|
def pack_llrs_to_words(llr_quantized, q_bits=6):
|
|
"""Pack 256 6-bit signed LLRs into 52 32-bit words (5 LLRs per word)."""
|
|
words = []
|
|
for i in range(0, 256, 5):
|
|
word = 0
|
|
for p in range(5):
|
|
idx = i + p
|
|
if idx < 256:
|
|
# Convert signed to unsigned 6-bit
|
|
val = llr_quantized[idx] & 0x3F
|
|
word |= val << (p * q_bits)
|
|
words.append(word)
|
|
return words
|
|
|
|
def gen_cocotb_test_data(vectors, output_path):
|
|
"""Generate Python module with test vector data for cocotb."""
|
|
with open(output_path, 'w') as f:
|
|
f.write("# Auto-generated test vectors from ldpc_sim.py\n")
|
|
f.write("# DO NOT EDIT - regenerate with: python3 model/gen_firmware_vectors.py\n\n")
|
|
f.write("TEST_VECTORS = [\n")
|
|
for i, v in enumerate(vectors):
|
|
llr_words = pack_llrs_to_words(v['llr_quantized'])
|
|
info_bits = v['info_bits']
|
|
# Pack 32 info bits into a single uint32
|
|
decoded_word = sum(b << j for j, b in enumerate(info_bits))
|
|
f.write(f" {{\n")
|
|
f.write(f" 'index': {i},\n")
|
|
f.write(f" 'llr_words': {llr_words},\n")
|
|
f.write(f" 'decoded_word': 0x{decoded_word:08X},\n")
|
|
f.write(f" 'converged': {v['converged']},\n")
|
|
f.write(f" 'iterations': {v['iterations']},\n")
|
|
f.write(f" 'syndrome_weight': {v['syndrome_weight']},\n")
|
|
f.write(f" }},\n")
|
|
f.write("]\n")
|
|
|
|
def gen_c_header(vectors, output_path):
|
|
"""Generate C header with test vector data for PicoRV32 firmware."""
|
|
with open(output_path, 'w') as f:
|
|
f.write("// Auto-generated test vectors from ldpc_sim.py\n")
|
|
f.write("// DO NOT EDIT - regenerate with: python3 model/gen_firmware_vectors.py\n\n")
|
|
f.write("#ifndef TEST_VECTORS_H\n#define TEST_VECTORS_H\n\n")
|
|
f.write(f"#define NUM_TEST_VECTORS {len(vectors)}\n")
|
|
f.write("#define LLR_WORDS_PER_VECTOR 52\n\n")
|
|
|
|
for i, v in enumerate(vectors):
|
|
llr_words = pack_llrs_to_words(v['llr_quantized'])
|
|
info_bits = v['info_bits']
|
|
decoded_word = sum(b << j for j, b in enumerate(info_bits))
|
|
f.write(f"// Vector {i}: converged={v['converged']}, "
|
|
f"iters={v['iterations']}, syndrome={v['syndrome_weight']}\n")
|
|
f.write(f"static const uint32_t tv{i}_llr[52] = {{\n ")
|
|
for j, w in enumerate(llr_words):
|
|
f.write(f"0x{w:08X}")
|
|
if j < len(llr_words) - 1:
|
|
f.write(", ")
|
|
if (j + 1) % 8 == 0 and j < len(llr_words) - 1:
|
|
f.write("\n ")
|
|
f.write(f"\n}};\n")
|
|
f.write(f"static const uint32_t tv{i}_decoded = 0x{decoded_word:08X};\n")
|
|
f.write(f"static const int tv{i}_converged = {int(v['converged'])};\n\n")
|
|
|
|
f.write("#endif // TEST_VECTORS_H\n")
|
|
|
|
if __name__ == '__main__':
|
|
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
json_path = os.path.join(project_root, 'data', 'test_vectors.json')
|
|
|
|
with open(json_path) as f:
|
|
vectors = json.load(f)
|
|
|
|
# Generate cocotb test data
|
|
cocotb_path = os.path.join(project_root,
|
|
'chip_ignite/verilog/dv/cocotb/ldpc_tests/test_data.py')
|
|
os.makedirs(os.path.dirname(cocotb_path), exist_ok=True)
|
|
gen_cocotb_test_data(vectors, cocotb_path)
|
|
print(f"Generated {cocotb_path}")
|
|
|
|
# Generate C header
|
|
c_path = os.path.join(project_root,
|
|
'chip_ignite/firmware/ldpc_demo/test_vectors.h')
|
|
os.makedirs(os.path.dirname(c_path), exist_ok=True)
|
|
gen_c_header(vectors, c_path)
|
|
print(f"Generated {c_path}")
|
|
```
|
|
|
|
**Step 4: Run the converter**
|
|
|
|
```bash
|
|
python3 model/gen_firmware_vectors.py
|
|
```
|
|
|
|
Expected: Creates `chip_ignite/verilog/dv/cocotb/ldpc_tests/test_data.py` and `chip_ignite/firmware/ldpc_demo/test_vectors.h`.
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add model/gen_firmware_vectors.py
|
|
cd chip_ignite
|
|
git add verilog/dv/cocotb/ldpc_tests/test_data.py firmware/ldpc_demo/test_vectors.h
|
|
git commit -m "feat: add test vector generation for cocotb and firmware"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 3: Write standalone Verilator testbench
|
|
|
|
**Files:**
|
|
- Create: `tb/tb_ldpc_decoder.sv`
|
|
- Create: `tb/Makefile`
|
|
|
|
This is a fast-iteration standalone testbench (no Caravel dependency). Tests the decoder core directly via Wishbone.
|
|
|
|
**Step 1: Write the testbench**
|
|
|
|
Create `tb/tb_ldpc_decoder.sv`:
|
|
|
|
```systemverilog
|
|
// Standalone Verilator testbench for LDPC decoder
|
|
// Tests Wishbone register access and decode operation
|
|
// Uses test vectors generated by model/ldpc_sim.py
|
|
|
|
`timescale 1ns/1ps
|
|
|
|
module tb_ldpc_decoder;
|
|
|
|
parameter CLK_PERIOD = 20; // 50 MHz
|
|
|
|
logic clk;
|
|
logic rst_n;
|
|
logic wb_cyc;
|
|
logic wb_stb;
|
|
logic wb_we;
|
|
logic [7:0] wb_adr;
|
|
logic [31:0] wb_dat_w;
|
|
logic [31:0] wb_dat_r;
|
|
logic wb_ack;
|
|
logic irq;
|
|
|
|
// DUT: standalone ldpc_decoder_top (original, not Caravel-adapted)
|
|
ldpc_decoder_top dut (
|
|
.clk (clk),
|
|
.rst_n (rst_n),
|
|
.wb_cyc_i (wb_cyc),
|
|
.wb_stb_i (wb_stb),
|
|
.wb_we_i (wb_we),
|
|
.wb_adr_i (wb_adr),
|
|
.wb_dat_i (wb_dat_w),
|
|
.wb_dat_o (wb_dat_r),
|
|
.wb_ack_o (wb_ack),
|
|
.irq_o (irq)
|
|
);
|
|
|
|
// Clock generation
|
|
initial clk = 0;
|
|
always #(CLK_PERIOD/2) clk = ~clk;
|
|
|
|
// Wishbone write task
|
|
task automatic wb_write(input [7:0] addr, input [31:0] data);
|
|
@(posedge clk);
|
|
wb_cyc <= 1;
|
|
wb_stb <= 1;
|
|
wb_we <= 1;
|
|
wb_adr <= addr;
|
|
wb_dat_w <= data;
|
|
do @(posedge clk); while (!wb_ack);
|
|
wb_cyc <= 0;
|
|
wb_stb <= 0;
|
|
wb_we <= 0;
|
|
endtask
|
|
|
|
// Wishbone read task
|
|
task automatic wb_read(input [7:0] addr, output [31:0] data);
|
|
@(posedge clk);
|
|
wb_cyc <= 1;
|
|
wb_stb <= 1;
|
|
wb_we <= 0;
|
|
wb_adr <= addr;
|
|
do @(posedge clk); while (!wb_ack);
|
|
data = wb_dat_r;
|
|
wb_cyc <= 0;
|
|
wb_stb <= 0;
|
|
endtask
|
|
|
|
// Test vector data (clean codeword: all-zero message, all LLRs = +31)
|
|
initial begin
|
|
logic [31:0] rdata;
|
|
int errors;
|
|
|
|
errors = 0;
|
|
rst_n = 0;
|
|
wb_cyc = 0;
|
|
wb_stb = 0;
|
|
wb_we = 0;
|
|
wb_adr = 0;
|
|
wb_dat_w = 0;
|
|
|
|
// Reset
|
|
repeat(10) @(posedge clk);
|
|
rst_n = 1;
|
|
repeat(5) @(posedge clk);
|
|
|
|
// ---- Test 1: Read version register ----
|
|
$display("[TEST 1] Read VERSION register");
|
|
wb_read(8'h54, rdata);
|
|
if (rdata !== 32'hLD01_0001) begin
|
|
$display(" FAIL: VERSION = 0x%08X, expected 0xLD010001", rdata);
|
|
errors++;
|
|
end else begin
|
|
$display(" PASS: VERSION = 0x%08X", rdata);
|
|
end
|
|
|
|
// ---- Test 2: Decode all-zero codeword (clean) ----
|
|
$display("[TEST 2] Decode clean all-zero codeword");
|
|
|
|
// Write LLRs: all +31 (= bit 0 very likely)
|
|
// Packed: 5 LLRs of +31 = {6'h1F, 6'h1F, 6'h1F, 6'h1F, 6'h1F}
|
|
// = {5'b11111 x5} in 30 bits = 0x1F7DF7DF
|
|
for (int i = 0; i < 52; i++) begin
|
|
wb_write(8'h10 + i*4, 32'h1F7D_F7DF);
|
|
end
|
|
|
|
// Start decode: early_term=1, max_iter=30, start=1
|
|
wb_write(8'h00, 32'h0000_1E03); // [12:8]=30, [1]=1, [0]=1
|
|
|
|
// Poll status until not busy
|
|
do begin
|
|
repeat(10) @(posedge clk);
|
|
wb_read(8'h04, rdata);
|
|
end while (rdata[0]); // bit 0 = busy
|
|
|
|
$display(" STATUS = 0x%08X", rdata);
|
|
$display(" Busy=%0d, Converged=%0d, Iters=%0d, Syndrome=%0d",
|
|
rdata[0], rdata[1], rdata[12:8], rdata[23:16]);
|
|
|
|
if (!rdata[1]) begin
|
|
$display(" FAIL: did not converge");
|
|
errors++;
|
|
end
|
|
if (rdata[23:16] != 0) begin
|
|
$display(" FAIL: syndrome weight = %0d, expected 0", rdata[23:16]);
|
|
errors++;
|
|
end
|
|
|
|
// Read decoded bits
|
|
wb_read(8'h50, rdata);
|
|
$display(" Decoded = 0x%08X", rdata);
|
|
if (rdata !== 32'h0000_0000) begin
|
|
$display(" FAIL: decoded = 0x%08X, expected 0x00000000", rdata);
|
|
errors++;
|
|
end else begin
|
|
$display(" PASS: decoded bits correct");
|
|
end
|
|
|
|
// ---- Summary ----
|
|
repeat(10) @(posedge clk);
|
|
if (errors == 0)
|
|
$display("\n=== ALL TESTS PASSED ===\n");
|
|
else
|
|
$display("\n=== %0d ERRORS ===\n", errors);
|
|
|
|
$finish;
|
|
end
|
|
|
|
// Timeout
|
|
initial begin
|
|
#(CLK_PERIOD * 100_000);
|
|
$display("TIMEOUT");
|
|
$finish;
|
|
end
|
|
|
|
// VCD dump
|
|
initial begin
|
|
$dumpfile("ldpc_decoder.vcd");
|
|
$dumpvars(0, tb_ldpc_decoder);
|
|
end
|
|
|
|
endmodule
|
|
```
|
|
|
|
**Step 2: Create Makefile**
|
|
|
|
Create `tb/Makefile`:
|
|
|
|
```makefile
|
|
RTL_DIR = ../rtl
|
|
RTL_FILES = $(RTL_DIR)/ldpc_decoder_top.sv \
|
|
$(RTL_DIR)/ldpc_decoder_core.sv \
|
|
$(RTL_DIR)/wishbone_interface.sv
|
|
|
|
.PHONY: lint sim clean
|
|
|
|
lint:
|
|
verilator --lint-only -Wall $(RTL_FILES) --top-module ldpc_decoder_top
|
|
|
|
sim: obj_dir/Vtb_ldpc_decoder
|
|
./obj_dir/Vtb_ldpc_decoder
|
|
|
|
obj_dir/Vtb_ldpc_decoder: tb_ldpc_decoder.sv $(RTL_FILES)
|
|
verilator --binary --timing --trace \
|
|
-o Vtb_ldpc_decoder \
|
|
-Wno-WIDTHEXPAND -Wno-WIDTHTRUNC \
|
|
--unroll-count 1024 \
|
|
tb_ldpc_decoder.sv $(RTL_FILES) \
|
|
--top-module tb_ldpc_decoder
|
|
|
|
clean:
|
|
rm -rf obj_dir *.vcd
|
|
```
|
|
|
|
**Step 3: Run lint**
|
|
|
|
```bash
|
|
cd tb
|
|
make lint
|
|
```
|
|
|
|
Expected: Clean (0 errors).
|
|
|
|
**Step 4: Run simulation**
|
|
|
|
```bash
|
|
cd tb
|
|
make sim
|
|
```
|
|
|
|
Expected:
|
|
```
|
|
[TEST 1] Read VERSION register
|
|
PASS: VERSION = 0xLD010001
|
|
[TEST 2] Decode clean all-zero codeword
|
|
...
|
|
PASS: decoded bits correct
|
|
|
|
=== ALL TESTS PASSED ===
|
|
```
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add tb/tb_ldpc_decoder.sv tb/Makefile
|
|
git commit -m "test: add standalone Verilator testbench for LDPC decoder"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 4: Extend Verilator testbench with generated test vectors
|
|
|
|
**Files:**
|
|
- Create: `tb/tb_ldpc_vectors.sv`
|
|
- Modify: `tb/Makefile` (add target)
|
|
- Depends on: `data/test_vectors.json` from Task 2
|
|
|
|
**Step 1: Create a script to convert JSON vectors to $readmemh format**
|
|
|
|
Create `model/gen_verilator_vectors.py`:
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
"""Convert test_vectors.json to hex files for Verilator $readmemh."""
|
|
|
|
import json
|
|
import os
|
|
|
|
def main():
|
|
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
json_path = os.path.join(project_root, 'data', 'test_vectors.json')
|
|
out_dir = os.path.join(project_root, 'tb', 'vectors')
|
|
os.makedirs(out_dir, exist_ok=True)
|
|
|
|
with open(json_path) as f:
|
|
vectors = json.load(f)
|
|
|
|
# Write LLR file: one line per LLR, 6-bit hex (2 chars), 256 lines per vector
|
|
with open(os.path.join(out_dir, 'llr_input.hex'), 'w') as f:
|
|
for v in vectors:
|
|
for llr in v['llr_quantized']:
|
|
f.write(f"{llr & 0x3F:02X}\n")
|
|
|
|
# Write expected results: decoded_word, converged, iterations, syndrome_weight
|
|
with open(os.path.join(out_dir, 'expected.hex'), 'w') as f:
|
|
for v in vectors:
|
|
decoded = sum(b << j for j, b in enumerate(v['info_bits']))
|
|
f.write(f"{decoded:08X}\n")
|
|
f.write(f"{int(v['converged']):02X}\n")
|
|
f.write(f"{v['iterations']:02X}\n")
|
|
f.write(f"{v['syndrome_weight']:02X}\n")
|
|
|
|
# Write metadata
|
|
with open(os.path.join(out_dir, 'num_vectors.txt'), 'w') as f:
|
|
f.write(f"{len(vectors)}\n")
|
|
|
|
print(f"Generated {len(vectors)} vectors in {out_dir}/")
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
```
|
|
|
|
**Step 2: Run it**
|
|
|
|
```bash
|
|
python3 model/gen_verilator_vectors.py
|
|
```
|
|
|
|
Expected: Creates `tb/vectors/llr_input.hex`, `tb/vectors/expected.hex`.
|
|
|
|
**Step 3: Write vector-driven testbench**
|
|
|
|
Create `tb/tb_ldpc_vectors.sv` — a testbench that reads hex files and tests each vector against RTL. Structure matches `tb_ldpc_decoder.sv` but loops over all 20 vectors, compares decoded bits, convergence, and iteration count against the Python model reference.
|
|
|
|
**Step 4: Run vector testbench**
|
|
|
|
```bash
|
|
cd tb
|
|
make sim_vectors
|
|
```
|
|
|
|
Expected: All 20 vectors pass bit-exact match against Python model.
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add model/gen_verilator_vectors.py tb/tb_ldpc_vectors.sv tb/vectors/ tb/Makefile
|
|
git commit -m "test: add vector-driven Verilator testbench with Python model cross-check"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 5: Write cocotb firmware for LDPC Wishbone test
|
|
|
|
**Files:**
|
|
- Create: `chip_ignite/verilog/dv/cocotb/ldpc_tests/ldpc_basic/ldpc_basic.c`
|
|
- Create: `chip_ignite/verilog/dv/cocotb/ldpc_tests/ldpc_basic/ldpc_basic.py`
|
|
- Create: `chip_ignite/verilog/dv/cocotb/ldpc_tests/ldpc_basic/ldpc_basic.yaml`
|
|
|
|
The cocotb Caravel test pattern requires paired firmware (C) + monitor (Python).
|
|
|
|
**Step 1: Write firmware C code**
|
|
|
|
```c
|
|
// ldpc_basic.c - PicoRV32 firmware for LDPC decoder Wishbone test
|
|
// Writes LLRs, starts decode, reads result, signals pass/fail on GPIO
|
|
|
|
#include <defs.h>
|
|
#include <stub.c>
|
|
|
|
// LDPC decoder registers (Caravel user project base = 0x30000000)
|
|
#define LDPC_BASE 0x30000000
|
|
#define LDPC_CTRL (*(volatile uint32_t*)(LDPC_BASE + 0x00))
|
|
#define LDPC_STATUS (*(volatile uint32_t*)(LDPC_BASE + 0x04))
|
|
#define LDPC_LLR(i) (*(volatile uint32_t*)(LDPC_BASE + 0x10 + (i)*4))
|
|
#define LDPC_DECODED (*(volatile uint32_t*)(LDPC_BASE + 0x50))
|
|
#define LDPC_VERSION (*(volatile uint32_t*)(LDPC_BASE + 0x54))
|
|
|
|
void main()
|
|
{
|
|
// Configure GPIO[7:0] as user output
|
|
reg_mprj_io_7 = GPIO_MODE_USER_STD_OUTPUT;
|
|
reg_mprj_io_6 = GPIO_MODE_USER_STD_OUTPUT;
|
|
reg_mprj_io_5 = GPIO_MODE_USER_STD_OUTPUT;
|
|
reg_mprj_io_4 = GPIO_MODE_USER_STD_OUTPUT;
|
|
reg_mprj_io_3 = GPIO_MODE_USER_STD_OUTPUT;
|
|
reg_mprj_io_2 = GPIO_MODE_USER_STD_OUTPUT;
|
|
reg_mprj_io_1 = GPIO_MODE_USER_STD_OUTPUT;
|
|
reg_mprj_io_0 = GPIO_MODE_USER_STD_OUTPUT;
|
|
|
|
// Apply GPIO config
|
|
reg_mprj_xfer = 1;
|
|
while (reg_mprj_xfer == 1);
|
|
|
|
// Signal test start via management GPIO
|
|
reg_mprj_datal = 0x00000000;
|
|
reg_gpio_out = 1; // mgmt GPIO = 1 -> "firmware ready"
|
|
|
|
// --- Test: Read version register ---
|
|
uint32_t version = LDPC_VERSION;
|
|
if (version != 0xLD010001) {
|
|
reg_mprj_datal = 0x000000FF; // all 1s = FAIL
|
|
while(1);
|
|
}
|
|
|
|
// --- Test: Decode all-zero codeword ---
|
|
// Write LLRs: all +31 (strong confidence in bit=0)
|
|
// Pack: 5x 6'b011111 = 0x1F7DF7DF
|
|
for (int i = 0; i < 52; i++) {
|
|
LDPC_LLR(i) = 0x1F7DF7DF;
|
|
}
|
|
|
|
// Start decode: early_term=1, max_iter=30, start=1
|
|
LDPC_CTRL = 0x00001E03;
|
|
|
|
// Poll until not busy
|
|
while (LDPC_STATUS & 0x1);
|
|
|
|
uint32_t status = LDPC_STATUS;
|
|
uint32_t decoded = LDPC_DECODED;
|
|
|
|
// Check: converged, syndrome=0, decoded=0x00000000
|
|
int pass = 1;
|
|
if (!(status & 0x2)) pass = 0; // not converged
|
|
if ((status >> 16) & 0xFF) pass = 0; // syndrome != 0
|
|
if (decoded != 0x00000000) pass = 0; // wrong decoded bits
|
|
|
|
if (pass) {
|
|
reg_mprj_datal = 0x000000AB; // 0xAB = PASS signature
|
|
} else {
|
|
reg_mprj_datal = 0x000000FF; // 0xFF = FAIL signature
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Write cocotb monitor**
|
|
|
|
```python
|
|
# ldpc_basic.py - cocotb test monitoring LDPC decode via GPIO
|
|
from caravel_cocotb.caravel_interfaces import test_configure
|
|
from caravel_cocotb.caravel_interfaces import report_test
|
|
import cocotb
|
|
|
|
@cocotb.test()
|
|
@report_test
|
|
async def ldpc_basic(dut):
|
|
caravelEnv = await test_configure(dut, timeout_cycles=100000)
|
|
await caravelEnv.release_csb()
|
|
|
|
# Wait for firmware to signal ready
|
|
await caravelEnv.wait_mgmt_gpio(1)
|
|
cocotb.log.info("[TEST] Firmware started, waiting for result...")
|
|
|
|
# Wait for GPIO output to change (firmware writes result)
|
|
for _ in range(50000):
|
|
await cocotb.triggers.ClockCycles(caravelEnv.clk, 10)
|
|
gpio_val = caravelEnv.monitor_gpio(7, 0).integer
|
|
if gpio_val != 0:
|
|
break
|
|
|
|
cocotb.log.info(f"[TEST] GPIO[7:0] = 0x{gpio_val:02X}")
|
|
|
|
if gpio_val == 0xAB:
|
|
cocotb.log.info("[TEST] LDPC basic decode: PASS")
|
|
else:
|
|
cocotb.log.error(f"[TEST] LDPC basic decode: FAIL (GPIO=0x{gpio_val:02X})")
|
|
```
|
|
|
|
**Step 3: Write test YAML**
|
|
|
|
```yaml
|
|
# ldpc_basic.yaml
|
|
Tests:
|
|
- {name: ldpc_basic, sim: RTL}
|
|
```
|
|
|
|
**Step 4: Register test in cocotb_tests.py**
|
|
|
|
Add to `chip_ignite/verilog/dv/cocotb/cocotb_tests.py`:
|
|
```python
|
|
from ldpc_tests.ldpc_basic.ldpc_basic import ldpc_basic
|
|
```
|
|
|
|
**Step 5: Run cocotb test**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
cf verify ldpc_basic
|
|
```
|
|
|
|
Expected: PASS with GPIO[7:0] = 0xAB.
|
|
|
|
**Step 6: Commit**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
git add verilog/dv/cocotb/ldpc_tests/ verilog/dv/cocotb/cocotb_tests.py
|
|
git commit -m "test: add cocotb LDPC basic decode test with firmware"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 6: Write additional cocotb tests
|
|
|
|
**Files:**
|
|
- Create: `chip_ignite/verilog/dv/cocotb/ldpc_tests/ldpc_noisy/ldpc_noisy.c`
|
|
- Create: `chip_ignite/verilog/dv/cocotb/ldpc_tests/ldpc_noisy/ldpc_noisy.py`
|
|
- Create: `chip_ignite/verilog/dv/cocotb/ldpc_tests/ldpc_noisy/ldpc_noisy.yaml`
|
|
|
|
Same pattern as Task 5 but uses a noisy test vector from `test_vectors.h`. Firmware loads the first noisy vector, decodes, compares against expected. Reports pass/fail on GPIO.
|
|
|
|
Additional tests following same pattern:
|
|
- `ldpc_max_iter` — uncorrectable vector, verify decoder hits max iterations and reports non-zero syndrome
|
|
- `ldpc_back_to_back` — two decodes in sequence, verify no state leakage
|
|
|
|
**Step 1-4: Write firmware + cocotb + YAML for each test (same pattern as Task 5)**
|
|
|
|
**Step 5: Run all tests**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
cf verify ldpc_basic
|
|
cf verify ldpc_noisy
|
|
cf verify ldpc_max_iter
|
|
cf verify ldpc_back_to_back
|
|
```
|
|
|
|
Expected: All PASS.
|
|
|
|
**Step 6: Commit**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
git add verilog/dv/cocotb/ldpc_tests/
|
|
git commit -m "test: add noisy, max_iter, and back-to-back cocotb tests"
|
|
```
|
|
|
|
---
|
|
|
|
## Week 2: OpenLane Hardening
|
|
|
|
### Task 7: Create OpenLane configuration for LDPC decoder macro
|
|
|
|
**Files:**
|
|
- Create: `chip_ignite/openlane/ldpc_decoder_top/config.json`
|
|
- Create: `chip_ignite/openlane/ldpc_decoder_top/pin_order.cfg`
|
|
- Create: `chip_ignite/openlane/ldpc_decoder_top/base_ldpc.sdc`
|
|
|
|
**Step 1: Write config.json**
|
|
|
|
```json
|
|
{
|
|
"DESIGN_NAME": "ldpc_decoder_top",
|
|
"VERILOG_FILES": [
|
|
"dir::../../verilog/rtl/defines.v",
|
|
"dir::../../verilog/rtl/ldpc_decoder_top.sv",
|
|
"dir::../../verilog/rtl/ldpc_decoder_core.sv",
|
|
"dir::../../verilog/rtl/wishbone_interface.sv"
|
|
],
|
|
"CLOCK_PERIOD": 20,
|
|
"CLOCK_PORT": "clk",
|
|
"CLOCK_NET": "u_core.clk",
|
|
"DIE_AREA": [0, 0, 1400, 1200],
|
|
"FP_PIN_ORDER_CFG": "dir::pin_order.cfg",
|
|
"FP_SIZING": "absolute",
|
|
"FP_PDN_MULTILAYER": false,
|
|
"VDD_NETS": ["vccd1"],
|
|
"GND_NETS": ["vssd1"],
|
|
"FALLBACK_SDC_FILE": "dir::base_ldpc.sdc",
|
|
"MAX_TRANSITION_CONSTRAINT": 1.5,
|
|
"MAX_FANOUT_CONSTRAINT": 16,
|
|
"SYNTH_STRATEGY": "AREA 2",
|
|
"PL_TARGET_DENSITY_PCT": 40,
|
|
"DIODE_INSERTION_STRATEGY": "heuristic",
|
|
"RUN_LINTER": false,
|
|
"pdk::sky130*": {
|
|
"SYNTH_MAX_FANOUT": 10,
|
|
"scl::sky130_fd_sc_hd": {
|
|
"CLOCK_PERIOD": 20
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 2: Write pin_order.cfg**
|
|
|
|
```
|
|
#BUS_SORT
|
|
#S
|
|
clk
|
|
rst_n
|
|
wb_cyc_i
|
|
wb_stb_i
|
|
wb_we_i
|
|
wb_sel_i\[.*\]
|
|
wb_adr_i\[.*\]
|
|
wb_dat_i\[.*\]
|
|
|
|
#N
|
|
wb_dat_o\[.*\]
|
|
wb_ack_o
|
|
irq_o
|
|
```
|
|
|
|
**Step 3: Write SDC constraints**
|
|
|
|
```sdc
|
|
# base_ldpc.sdc - Timing constraints for LDPC decoder
|
|
create_clock -name clk -period 20.0 [get_ports {clk}]
|
|
set_input_delay -clock clk -max 5.0 [all_inputs]
|
|
set_input_delay -clock clk -min 1.0 [all_inputs]
|
|
set_output_delay -clock clk -max 5.0 [all_outputs]
|
|
set_output_delay -clock clk -min 0.0 [all_outputs]
|
|
```
|
|
|
|
**Step 4: Run hardening**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
cf harden ldpc_decoder_top
|
|
```
|
|
|
|
Expected: Synthesis + P&R completes. Note WNS (worst negative slack) — should be positive at 50 MHz.
|
|
|
|
**Step 5: Check results**
|
|
|
|
```bash
|
|
# Check timing summary in OpenLane logs
|
|
grep -r "wns" chip_ignite/openlane/ldpc_decoder_top/runs/*/logs/
|
|
# Check area
|
|
grep -r "DIEAREA" chip_ignite/gds/ldpc_decoder_top.gds || true
|
|
```
|
|
|
|
**Step 6: Commit**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
git add openlane/ldpc_decoder_top/
|
|
git commit -m "feat: add OpenLane hardening config for LDPC decoder (50 MHz)"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 8: Update wrapper hardening config
|
|
|
|
**Files:**
|
|
- Modify: `chip_ignite/openlane/user_project_wrapper/config.json`
|
|
|
|
**Step 1: Update MACROS section**
|
|
|
|
Replace `user_proj_example` references with `ldpc_decoder_top`:
|
|
|
|
- Change macro name from `user_proj_example` to `ldpc_decoder_top`
|
|
- Update GDS/LEF/netlist/SPEF paths
|
|
- Adjust placement coordinates if needed (keep `(60, 15)` as starting point)
|
|
|
|
**Step 2: Update VERILOG_FILES**
|
|
|
|
Point to LDPC RTL instead of example.
|
|
|
|
**Step 3: Run wrapper hardening**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
cf harden user_project_wrapper
|
|
```
|
|
|
|
**Step 4: Commit**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
git add openlane/user_project_wrapper/config.json
|
|
git commit -m "feat: update wrapper config for LDPC decoder macro"
|
|
```
|
|
|
|
---
|
|
|
|
## Week 3: Gate-Level Sim & Precheck
|
|
|
|
### Task 9: Run gate-level simulation
|
|
|
|
**Step 1: Update GL include file**
|
|
|
|
Edit `chip_ignite/verilog/includes/includes.gl.caravel_user_project`:
|
|
```
|
|
-v $(USER_PROJECT_VERILOG)/gl/user_project_wrapper.v
|
|
-v $(USER_PROJECT_VERILOG)/gl/ldpc_decoder_top.v
|
|
```
|
|
|
|
**Step 2: Run GLS for basic test**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
cf verify ldpc_basic --sim gl
|
|
```
|
|
|
|
Expected: PASS (same as RTL sim but with gate-level netlist).
|
|
|
|
**Step 3: Run all GLS tests**
|
|
|
|
```bash
|
|
cf verify ldpc_noisy --sim gl
|
|
cf verify ldpc_max_iter --sim gl
|
|
cf verify ldpc_back_to_back --sim gl
|
|
```
|
|
|
|
**Step 4: Commit**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
git add verilog/includes/
|
|
git commit -m "test: gate-level simulation passing for all LDPC tests"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 10: Timing push to 75 MHz (stretch goal)
|
|
|
|
**Step 1: Check current timing margin**
|
|
|
|
If WNS > 5 ns at 50 MHz (20 ns period), there's room to push.
|
|
|
|
**Step 2: Tighten clock to 75 MHz**
|
|
|
|
Update `openlane/ldpc_decoder_top/config.json`:
|
|
```json
|
|
"CLOCK_PERIOD": 13.3
|
|
```
|
|
|
|
**Step 3: Re-run hardening**
|
|
|
|
```bash
|
|
cf harden ldpc_decoder_top
|
|
cf harden user_project_wrapper
|
|
```
|
|
|
|
**Step 4: Check timing**
|
|
|
|
If WNS is negative, either:
|
|
- Pipeline the critical path in `ldpc_decoder_core.sv` (CN update min-finding)
|
|
- Or revert to 50 MHz (20 ns) and document the achieved frequency
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git commit -am "perf: tighten clock to 75 MHz (or document 50 MHz)"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 11: Run precheck
|
|
|
|
**Step 1: Configure GPIO**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
cf gpio-config
|
|
```
|
|
|
|
Set GPIO[0:7] as user standard output. Set remaining as management input (default).
|
|
|
|
**Step 2: Run precheck**
|
|
|
|
```bash
|
|
cf precheck
|
|
```
|
|
|
|
Expected: All checks pass (DRC, LVS, power, clock, GPIO, wrapper, manifest).
|
|
|
|
**Step 3: Fix any issues and re-run until clean**
|
|
|
|
**Step 4: Commit**
|
|
|
|
```bash
|
|
git commit -am "chore: precheck passing"
|
|
```
|
|
|
|
---
|
|
|
|
## Week 4: Proposal Submission
|
|
|
|
### Task 12: Add LICENSE and AI disclosure
|
|
|
|
**Files:**
|
|
- Create: `chip_ignite/LICENSE` (Apache 2.0)
|
|
- Create: `chip_ignite/docs/ai-disclosure.md`
|
|
|
|
**Step 1: Add Apache 2.0 license**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
# Copy standard Apache 2.0 license text
|
|
cat > LICENSE << 'HEREDOC'
|
|
Apache License
|
|
Version 2.0, January 2004
|
|
http://www.apache.org/licenses/
|
|
...
|
|
HEREDOC
|
|
```
|
|
|
|
**Step 2: Write AI disclosure**
|
|
|
|
```markdown
|
|
# AI Disclosure
|
|
|
|
This project was developed with assistance from Claude (Anthropic), an AI assistant.
|
|
|
|
## Usage
|
|
|
|
Claude was used for:
|
|
- RTL design review and optimization
|
|
- Testbench generation
|
|
- Documentation writing
|
|
- Build system configuration
|
|
- Design space exploration (density evolution analysis, code rate comparison)
|
|
|
|
## Session Logs
|
|
|
|
Session transcripts are available upon request. Key AI-assisted contributions:
|
|
- `rtl/ldpc_decoder_core.sv` — algorithmic implementation reviewed and refined with AI
|
|
- `model/*.py` — analysis scripts co-developed with AI
|
|
- `docs/` — documentation drafted with AI assistance
|
|
- `openlane/` — configuration guided by AI
|
|
|
|
## Human Contributions
|
|
|
|
All architectural decisions, algorithm selection, code review, and final verification
|
|
were performed by the human designer. The AI served as a coding assistant and
|
|
technical reference.
|
|
```
|
|
|
|
**Step 3: Commit**
|
|
|
|
```bash
|
|
git add LICENSE docs/ai-disclosure.md
|
|
git commit -m "docs: add Apache 2.0 license and AI disclosure"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 13: Write proposal README
|
|
|
|
**Files:**
|
|
- Modify: `chip_ignite/README.md`
|
|
|
|
**Step 1: Write comprehensive README**
|
|
|
|
Cover:
|
|
- Project title and one-line description
|
|
- Application: photon-starved free-space optical communication
|
|
- Architecture diagram (ASCII)
|
|
- Code parameters table
|
|
- Performance specs (clock frequency, throughput, latency, area)
|
|
- Verification status (test list, pass/fail)
|
|
- Timing report summary (WNS/TNS)
|
|
- Directory structure
|
|
- How to build and run
|
|
- Roadmap (Approaches B and C from design doc)
|
|
- License and AI disclosure link
|
|
|
|
**Step 2: Commit**
|
|
|
|
```bash
|
|
git add README.md
|
|
git commit -m "docs: write proposal README for ChipFoundry contest"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 14: Submit proposal
|
|
|
|
**Step 1: Push to GitHub**
|
|
|
|
```bash
|
|
cd chip_ignite
|
|
git push origin main
|
|
```
|
|
|
|
**Step 2: Submit repo URL via contest form**
|
|
|
|
Go to https://chipfoundry.io/challenges/application/#submit_form and submit the GitHub repo URL.
|
|
|
|
---
|
|
|
|
## Weeks 5-8: Post-Proposal (Final Submission)
|
|
|
|
### Task 15: Write PicoRV32 demo firmware
|
|
|
|
**Files:**
|
|
- Create: `chip_ignite/firmware/ldpc_demo/ldpc_demo.c`
|
|
- Create: `chip_ignite/firmware/ldpc_demo/Makefile`
|
|
- Depends on: `chip_ignite/firmware/ldpc_demo/test_vectors.h` from Task 2
|
|
|
|
Full demo firmware that runs 3 scenarios on boot (clean, noisy, stress test) and prints results over UART. Uses the Caravel BSP for UART and GPIO.
|
|
|
|
### Task 16: Design PCBA in KiCad
|
|
|
|
**Files:**
|
|
- Create: `chip_ignite/pcba/ldpc_eval_board.kicad_pro`
|
|
- Create: `chip_ignite/pcba/ldpc_eval_board.kicad_sch`
|
|
- Create: `chip_ignite/pcba/ldpc_eval_board.kicad_pcb`
|
|
- Create: `chip_ignite/pcba/BOM.csv`
|
|
|
|
Schematic sections:
|
|
1. Caravel QFN-64 with decoupling
|
|
2. Power (LDO 3.3V + 1.8V, USB or barrel jack)
|
|
3. 25 MHz crystal oscillator
|
|
4. FTDI USB-UART bridge
|
|
5. SPI flash (firmware storage)
|
|
6. Status LEDs + reset button
|
|
7. GMAPD connector (DNP, reference only)
|
|
8. TIA + comparator section (DNP, reference only)
|
|
|
|
### Task 17: Design mechanical enclosure
|
|
|
|
**Files:**
|
|
- Create: `chip_ignite/mechanical/enclosure.scad`
|
|
- Output: `chip_ignite/mechanical/enclosure.stl`
|
|
|
|
Parametric OpenSCAD box with PCB standoffs, USB/barrel cutouts.
|
|
|
|
### Task 18: Record demo video
|
|
|
|
3-minute screen recording:
|
|
1. Show architecture diagram and explain the application (30s)
|
|
2. Run cocotb simulation showing decode pass (30s)
|
|
3. Show OpenLane GDS layout viewer (30s)
|
|
4. Walk through firmware UART output (30s)
|
|
5. Show KiCad 3D board render (30s)
|
|
6. Summarize specs and roadmap (30s)
|
|
|
|
### Task 19: Create how-to screenshots
|
|
|
|
Step-by-step PNG screenshots:
|
|
1. Clone repo and run `cf setup`
|
|
2. Run `cf harden ldpc_decoder_top`
|
|
3. Run `cf verify ldpc_basic`
|
|
4. Run `cf precheck`
|
|
5. Build and flash firmware
|
|
|
|
### Task 20: Final submission
|
|
|
|
- Verify all deliverables present
|
|
- Run `cf precheck` one final time
|
|
- Push to GitHub
|
|
- Submit via contest form
|