481b5d223fbdcb060c2a62fbcbd6bc67ceb5dba6
15
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
07a48e401f |
fix: close 7 zero-value/mid-run guard gaps found in re-certification campaign
Fixes all 7 bugs found in the FPGA-Neural re-certification campaign (docs/validation/bugs.md, CERTIFICATION.md), per campaign policy that fixes land as a commit separate from the analysis work (commits 313a199..77e74db): - BUG-005 (CRITICAL): layer_sequencer.v -- RUN_NETWORK(num_layers=0) ran through 256 fabricated layers reading arbitrary PSRAM data as descriptors. Now an immediate no-op. - BUG-007 (CRITICAL): spi_engine.v -- SET_NET_TYPE received mid-run remapped the arbiter mux and hung the in-progress engine. Now rejected while graph_busy/seq_busy, verified not to partially apply. - BUG-002 (MEDIA): neuron_parallel.v -- N_INPUTS=0 bypassed the elaboration-time guard, leaving x_bus/w_bus undriven. Guard extended to reject N_INPUTS==0. - BUG-003 (MEDIA): neuron_parallel.v -- n_inputs_real=0 at runtime had inconsistent behavior across repeated runs. Now an explicit early-out via the existing "finishing" completion path. - BUG-004 (BASSA): neuron_memory.v -- n_neurons_real=0 silently ignored the limit. Fixed at all three entry points into the vulnerable termination checks (STATE_READ_X, STATE_READ_W, and the X->W dispatch). - BUG-006 (BASSA): graph_engine.v -- num_neurons_graph=0 relied on an incidental guard rather than a real one. Now an explicit no-op. - BUG-001 (INFO): removed sim/top.v, confirmed dead code from the pre-INT8 Q8.8 era. Every bug-reproduction testbench is rewritten from observe-only to hard-assert the fixed behavior (sim/*_bug00[2-7]*_tb.v), verified individually and via a full regression (44 testbenches, 43 PASS, 0 FAIL/ERROR, 1 benchmark by design). Re-verified on the real toolchain (Yosys synth_ecp5 + nextpnr-ecp5): 0 constraint errors, Fmax 68.65 MHz (was 67.91 MHz, within known placement noise), critical path structurally unchanged (neuron_parallel/mac8 accumulator carry chain). Updates docs/validation/bugs.md and CERTIFICATION.md to reflect the resolved state, and docs/FPGA-NeuralNetwork-Engine.md + the LaTeX datasheet (IT/EN) with inline notes on each fixed edge case, closing the datasheet/RTL gap flagged in C.13 of the original certification. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
f0a66363de |
test: certify spi_neuron_top mux/pins (C.8), find critical BUG-007
Legitimate dispatch mux and data_ready_n/irq_n pins certified via existing pre-session tests. BUG-007 (CRITICAL), confirmed end-to-end over real simulated SPI: SET_NET_TYPE has no check against graph_busy/seq_busy in rtl/spi_engine.v, and rtl/spi_neuron_top.v's arbiter Port C mux selects between graph_engine/layer_sequencer purely combinationally on the current net_type value -- not latched to whichever engine actually started the in-flight run. Started a valid graph RUN_NETWORK, sent SET_NET_TYPE(dense) immediately after (before completion): STATUS.busy gets stuck (30+ consecutive polls with no done/err, vs. ~12-25us normal completion) -- the graph engine is left waiting for a ram_ready that never arrives via the now-disconnected mux path. Also verified recovery: RESET during the hang brings the system back to a fully working state (a subsequent legitimate dense op completes normally) -- not a permanent lockup, but plain STATUS polling alone would never unstick without a host-side RESET fallback. Full regression: 40/40 real tests pass, 1 new observational test deterministically reproduces BUG-007 and verifies RESET recovery. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
6db44efcfd |
test: certify graph_engine gather/guard (C.6), find related BUG-006
Gather/padding/src_id<out_id guard certified via existing solid pre-session tests (graph_engine_tb.v checks act_buffer contents via hierarchical reference, not just final output; graph_engine_guard_tb.v covers 4 adversarial cases incl. recovery). BUG-006 (LOW severity): num_neurons_graph=0 shares BUG-005's exact root cause (neuron_idx is a full 16-bit register, no guard), but graph_engine's existing per-edge src_id<out_id guard incidentally catches most garbage-data patterns fast (err at cycle 58 for a non-trivial test pattern, vs. layer_sequencer's 21761-cycle full run in BUG-005) -- not a designed protection for this case, so not closed as a non-issue, but lower severity given the observed practical risk. Not run to full 65536-iteration completion (impractical for this campaign's time budget) -- limitation stated explicitly. Full regression: 40/40 real tests pass, 1 new observational test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
f81d7fa1b3 |
test: certify layer_sequencer chain (C.5), find critical BUG-005
Layer chain / ping-pong / busy-done certified via the existing pre-session test, which already verifies the actual ping-pong buffer address used (not just the value) and correct busy/done timing. New finding, BUG-005 (CRITICAL): run_num_layers=0 has no guard at compile time or runtime, and unlike BUG-002's 1-bit group_index (which can never represent the wraparound value), layer_idx here is a full 8-bit register that naturally reaches it. Confirmed empirically with a minimal neuron_memory stub: RUN_NETWORK(0) runs through all 256 possible layer indices (21761 cycles), reading arbitrary PSRAM bytes far past the real descriptor table as if they were valid layer descriptors, running real neuron_memory passes on them, and writing results to ping-pong buffer addresses derived from that arbitrary data. More severe than BUG-002/003/004: reachable via a single documented SPI opcode (RUN_NETWORK), real PSRAM corruption risk rather than just a hang or wrong result. Root cause fully isolated, not just the symptom. Full regression: 40/40 real tests pass, 1 new observational test (no pass/fail by design) deterministically reproduces BUG-005. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
b528901510 |
test: certify mem_arbiter priority order (C.4)
Priority B>C>A>D confirmed correct with distinguishable per-port data (not just "someone got served" but "the right requester got its own data back") across 4 contention scenarios. D-alone case confirms low priority does not mean never granted. Found and fixed a real race in the test harness itself: blocking assignments withdrew loser requests in the same clock edge meant to grant the winner, racing the DUT's own synchronous block -- dut.owner never left SEL_NONE, every wait() blocked forever. Fixed by switching request-signal drives to non-blocking assignments throughout. Documented (not filed as a bug) that D can starve indefinitely under sustained continuous B contention -- standard behavior for a fixed-priority arbiter with no aging, and explicitly outside the header's own stated operating assumption (B/C temporally disjoint in normal operation). Flagged the header's "never starves or corrupts A/B/C" wording as ambiguous about whether it promises D's own progress. Full regression: 40/40 real tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
3dd75e9e0d |
test: certify memory subsystem addressing (C.3)
int8_memory_access.v byte<->word conversion and byte-lane selection: exhaustive 2048-address test + 6 real read/write round-trips through the FSM handshake. 2054/2054 checks, 0 mismatches, after fixing two bugs in the test harness itself (a same-timestep race reading a non-blocking update one iteration late, and a behavioral memory stub that ignored byte-lane enables on write) -- both documented as test-side, not RTL, issues. memory_interface.v and psram_controller.v not re-verified from scratch: cited against coverage already established/re-confirmed earlier in this same session (page-mode/tCEM against the ISSI datasheet, a real pre-existing power-up request-loss bug found and fixed), re-run clean via the Phase 0 regression harness rather than trusted from WORKLOG text alone. Full regression: 39/39 real tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
14c8d87194 |
test: certify runtime width early-termination (C.2), document BUG-003/004
n_inputs_real/n_neurons_real early termination for valid values is certified real: a "poison" region (data that would saturate the result if read past the claimed limit) confirms no over-read, cycle counts scale proportionally. n_inputs_real non-multiple-of-PARALLEL at runtime matches the documented silent-truncation risk exactly. n_inputs_real=0 / n_neurons_real=0 (BUG-003/004): confirmed incorrect behavior in every repetition, but the exact triggering mechanism was NOT fully isolated -- nearly-identical repeated tests produced different symptoms (clean hang vs. silently processing the full build width vs. a third cycle count matching neither). Reported in full, including the inconsistency itself, rather than picking the cleanest result. The two new permanent testbenches reflect this honestly: the solid early-termination checks are hard assertions, the n_*_real=0 probe is deliberately observe-only given the non-deterministic result. Full regression: 38/38 real tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
20b0b1f4c0 |
test: certify mac_unit/mac8 datapath (C.1), confirm real N_INPUTS=0 guard gap
mac_unit.v: exhaustive unit test (all 65536 (x,w) combinations at DATA_WIDTH=8, plus 486 boundary acc_in vectors) against an independent Python oracle (tools/validation/mac_oracle.py). 66022/66022 match, 0 reserves. mac8.v: first-ever dedicated unit test (previously only indirect coverage at whatever single PARALLEL neuron_parallel_tb.v happens to use). Verified at PARALLEL=2/8/32 with structural adversarial vectors (catches swapped/duplicated tree wiring), 300 random INT8 pairs per PARALLEL with realistic accumulating acc_in, and worst-case magnitude adversarial vectors. 939/939 match. Confirms BUG-002 (N_INPUTS=0 bypasses the N_INPUTS%PARALLEL elaboration guard) is real, on both simulation and real Yosys synthesis -- root cause: [DATA_WIDTH*N_INPUTS-1:0] becomes [-1:0] for N_INPUTS=0, which both tools treat as a genuine 2-bit undriven vector rather than collapsing to zero width. Includes a documented self-correction: the first verification attempt produced a false "hang" using an invalid one-shot late check of a single-cycle done pulse -- caught by reproducing the same false result on a known-good sanity config before trusting it. Full regression re-run clean after adding 3 new testbenches: 36/36 real tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
313a1994a4 |
docs: certification campaign Phase 0 -- real inventory, new regression harness
Builds tools/run_regression.py (no reproducible regression script existed before -- every prior "N testbenches, all pass" claim was assembled by hand). Resolves each testbench's dependencies by static analysis of instantiation sites, not from memory, then compiles+runs everything fresh. Independently re-verifies the 33-testbench regression clean (0 real failures) after fixing two false negatives in the harness itself (two tests are deliberate compile-time-failure negative tests, one file is a benchmark with no pass/fail verdict by design -- confirmed by reading each file's own header, not assumed). Findings recorded in docs/validation/: sim/top.v is dead code (references a removed FRAC_BITS parameter from the pre-INT8 Q8.8 era); mac_unit.v/ mac8.v have no dedicated unit testbench, only indirect coverage; the N_INPUTS%PARALLEL elaboration guard does not mathematically cover N_INPUTS=0 (open finding, not yet confirmed reachable -- BUG-002). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
97a21be240 |
feat: flash boot/persistence subsystem (SPI master, copy engine, CRC32 slot catalog)
Adds FPGA-exclusive access to the onboard W25Q128JV SPI NOR flash for weights/bias/network persistence, layered as spi_flash_master (raw SPI, USRMCLK-driven) -> flash_copy_engine (flash<->PSRAM streaming, erase- before-write, Page Program loop) -> flash_slot_manager (16-slot catalog with CRC32), exposed via 8 new SPI opcodes (0x40-0x47). Fixes two pre-existing bugs found during bring-up: a psram_controller.v request lost during power-up, and a one-cycle-pulse race in the PSRAM arbiter request handshake. Full simulation + real Yosys/nextpnr-ecp5 synthesis verification (0 errors, Fmax 66.68MHz) in WORKLOG.md and docs/FPGA-Neural-Flash-Subsystem-Verification.md. Also updates docs/pinout to reflect the 56-signal real .lpf (3 new flash pins) and documents the WRITE_RAM/READ_RAM host backpressure risk found while testing this subsystem. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v |
||
|
|
55c827bedf |
feat: PSRAM page-mode reads + graph engine (Type #2) + real pinout/IRQ pins
PSRAM page-mode read burst support in psram_controller.v: enables the ISSI IS66WVE4M16EBLL-70BLI's page mode via its configuration-register software-access sequence at boot (disabled by default on the real chip), then keeps CE#/OE# asserted after a read so a same-page continuation only pays tAPA (20ns) instead of a full tAA (70ns) random access, with automatic tCEM-safe session closing. Only a WRITE closes the page -- byte-enable changes do not, since int8_memory_access.v alternates them on nearly every access and an early implementation attempt that treated them as a close condition measured a real regression (53.25->61.25 cycles/edge) before being corrected (53.25->37.53 cycles/edge, +42% gather bandwidth). sim/psram_model.v gained independent tAPA/tAA and tCEM enforcement (with a real Verilog same-timestep event-ordering race found and fixed via a #0 sync) so the regression proves real timing compliance, not just data correctness. New sim/psram_page_mode_tb.v; full 26-file regression suite re-run clean. Real nextpnr-ecp5 Fmax re-measured on the full spi_neuron_top system: 75.73MHz (P2, up from 55.59MHz) and 65.13MHz (P8) -- still under the 80MHz target but not regressed, with the critical path confirmed (not assumed) to remain entirely inside neuron_parallel's accumulate chain, never psram_controller. Also includes this session's other already-validated work: the graph engine (Type #2 sparse-graph network: act_buffer, graph_engine, netasm host assembler), real CABGA381 pinout (.lpf, place&route verified) and physical IRQ_N/DATA_READY_N pins, and Phase 7 timing closure logs -- all previously uncommitted, documented in WORKLOG.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LH3jPeJ3eFMfF2v8SQhpkk |
||
|
|
7e2711fa27 |
feat: widen ADDR_WIDTH to 23 bits for full 8MB PSRAM addressing
Bumps ADDR_WIDTH's default from 22 to 23 bits across every RTL module (neuron_memory, layer_sequencer, spi_engine, spi_neuron_top, mem_arbiter, int8_memory_access, memory_interface, psram_controller, memory_model) and every testbench that mirrors it, so the system's byte-address space reaches the full 8 MiB the recommended PSRAM part (ISSI IS66WVE4M16EBLL-70BLI, docs/FPGA-Neural-Hardware-Design.md §3) actually provides -- previously only 4 MiB (half the chip) was reachable, since int8_memory_access.v's byte->word address shift (addr >> 1) turned the old 22-bit byte address into only 21 real word bits, one short of the chip's real 22-bit word address (A0-A21). At 23 bits, that same shift lands exactly on all 22 chip address lines, so the whole part is usable now instead of deferred to a future widening. Also fixes a stray 22'd11-sized literal in layer_sequencer.v's descriptor-table address increment (numerically already safe via Verilog's zero-extension, but now correctly unsized so it always matches ADDR_WIDTH instead of silently assuming 22). Updated docs/FPGA-NeuralNetwork-Engine.md's SPI protocol address-field note (23 bits, top 1 reserved bit instead of 2) and docs/FPGA-Neural-Hardware-Design.md's PSRAM section (the "chip has one spare address line" framing is gone now that all 22 are wired and used). Full regression (all 11 ADDR_WIDTH-touching testbenches, plus a Yosys elaboration check of spi_neuron_top with the new default and no override) passes clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt |
||
|
|
a918c3f1e9 |
feat: configurable activation functions + runtime-configurable network topology
Two related Phase 5 additions, both threaded the same way (a new runtime field defaulting to the pre-existing behavior, settable per-layer via the descriptor table or per-run via SET_BASE): Configurable activation functions: - neuron_parallel.v gains a 2-bit `activation` port (ACT_NONE = linear + two-sided INT8 saturate, ACT_RELU = the original hardwired behavior, kept as the default so every pre-existing caller/testbench is unaffected), threaded through neuron_memory.v. - spi_engine.v: SET_BASE sel=6 (single-layer path); the descriptor table gains a 7th byte (multi-layer path). - Verified in neuron_parallel_tb.v (negative pass-through + negative saturation to -128) and end-to-end in spi_neuron_top_runnetwork_tb.v (a real negative accumulator that ACT_RELU would clamp to 0 comes through unclamped under ACT_NONE, over real SPI/RAM). Runtime network width (one bitstream, any topology up to its build-time max, entirely host-configured over SPI): - neuron_parallel.v gains n_inputs_real, bounding its MAC group loop (n_inputs_real/PARALLEL groups instead of the fixed build-time count). neuron_memory.v gains n_inputs_real/n_neurons_real, bounding its X/W RAM-read loop and its neuron loop. All default to the build-time max, so unconnected callers are unaffected. n_inputs_real must stay a multiple of PARALLEL (same constraint N_INPUTS itself is held to at elaboration time, now the caller's runtime responsibility). - spi_engine.v: SET_BASE sel=7/8 (single-layer path); the descriptor table grows to 11 bytes/layer (+n_inputs_real +n_neurons_real, multi-layer path) -- layer_sequencer.v also now copies only n_neurons_real bytes into the ping-pong buffer, not the full build width. - This is real early termination, not bookkeeping: no RAM zero-padding needed for the unused tail, and it measurably completes faster. neuron_parallel_tb.v TEST 7: 3 cycles vs 6 for a reduced-vs-full run, with garbage loaded into the skipped lanes to prove they're never read. neuron_memory_tb.v TEST 5: through the real PSRAM stack, 209 cycles vs 788. layer_sequencer_tb.v proves a reduced n_neurons_real shortens the ping-pong copy-out itself (bytes beyond the real count stay untouched, not just differing). docs/FPGA-NeuralNetwork-Engine.md: §8.1 opcode/SET_BASE table, new "Runtime network width" subsection, Phase 5 checklist, Current Status table, and the "Core architectural principle" statement updated to reflect that topology (not just trained parameters) is now host-configured at runtime up to a build-time ceiling. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt |
||
|
|
233d6ff7fb |
feat: complete Phase 5 multi-layer network (RUN_NETWORK) + fix STATUS race
Wires the already-present layer_sequencer.v into the SPI stack: - spi_engine.v: RUN_NETWORK opcode (0x23) + SET_BASE selectors for table_base/buf_a_base/buf_b_base; STATUS.busy/done extended to track the sequencer (seq_busy/seq_done) alongside neuron_memory directly, so done latches on the last layer only. - spi_neuron_top.v: instantiates layer_sequencer, muxes neuron_memory's control inputs between it (while seq_busy) and spi_engine's direct-drive path (legacy single-layer mode), wires the sequencer's own RAM master to mem_arbiter's Port C. Found and fixed a real race while writing the end-to-end test: STATUS's sticky/clear-on-read done bit read its value live/combinationally during transmission and cleared unconditionally on any STATUS read. A done_event landing mid-transmission of a STATUS response byte could be silently dropped -- the host would receive a stale byte while the sticky bit was cleared regardless, hanging any host polling STATUS in a loop. Present since Phase 4, not RUN_NETWORK-specific; only surfaced under this test's continuous polling. Fixed by latching a status_snapshot at opcode-accept time and gating the clear on what was actually transmitted. Tests: spi_engine_tb.v gains RUN_NETWORK/SET_BASE opcode tests (K/L); new layer_sequencer_tb.v unit-tests the sequencer FSM directly (descriptor table, ping-pong buffer addressing, byte-exact copy-out); new spi_neuron_top_runnetwork_tb.v drives a real 2-layer network over simulated SPI end to end (real neuron_memory + PSRAM, hand-computed expected output) and confirms the legacy single-layer path still works afterward. All existing testbenches still pass. |
||
|
|
a2bd60e305 |
feat: complete Phase 4 SPI RTL (engine, arbiter, top) + real-RAM e2e test
Implements the rest of the SPI interface (docs §8.1) on top of spi_slave.v from the previous commit: - rtl/spi_engine.v: opcode FSM + register bank, all 8 opcodes (NOP, WRITE_RAM, READ_RAM, RESET, SET_BASE, START, STATUS, READ_OUTPUT, READ_CONFIG). tx_byte is driven combinationally from live state (not reactively on tx_byte_req), applying the prefetch-vs-consume contract documented on spi_slave.v. STATUS.done is a sticky, clear-on-read latch. RAM master port uses the same byte-level convention as neuron_memory.v's external mem_* port. - rtl/mem_arbiter.v: fixed-priority (neuron_memory > spi_engine) grant-and-forward arbiter sharing one byte-level memory port between spi_engine's WRITE_RAM/READ_RAM and neuron_memory's own X/W/bias reads during a run. - rtl/spi_neuron_top.v: full integration -- spi_slave -> spi_engine -> mem_arbiter -> a single shared int8_memory_access -> memory_interface -> psram_controller -> PSRAM pins. neuron_memory's rst is global rst OR'd with the RESET opcode's soft-reset pulse. The host has no direct electrical path to the RAM, only through this chain. Testing: - sim/spi_engine_tb.v: 10 tests (one per opcode + WRITE_RAM/READ_RAM, START idle-vs-busy, STATUS sticky/clear-on-read, extra-MOSI-bytes- ignored, back-to-back transactions) against a synthetic 2-cycle- latency RAM model, isolating the opcode FSM from PSRAM timing. Found and fixed two testbench-only bugs (RTL needed no change): the same delta-zero clock-edge race as spi_slave_tb.v (blocking `nm_done=1` landing on the same sim time as a posedge -- fixed via negedge-based pulsing) and a missing RAM sentinel initialization. - sim/spi_neuron_top_tb.v: end-to-end test against the **real** psram_model.v (not a mock) -- RESET/READ_CONFIG/WRITE_RAM/ READ_RAM/SET_BASE/START/STATUS/READ_OUTPUT all driven purely over simulated SPI. 3/3 scenarios (sum, saturation, ReLU) pass on the first attempt; confirms the arbiter and shared byte<->word bridge are correct against real PSRAM timing, not just a synthetic mock. Real-toolchain verification (Yosys + nextpnr-ecp5 + ecppack): spi_slave.v and spi_engine.v synthesize clean and comfortably clear 80 MHz in isolation (403 MHz / 191 MHz, no DSP usage). The full spi_neuron_top.v integration, however, does NOT meet 80 MHz (~52-56 MHz depending on PARALLEL) -- the critical path is entirely inside neuron_parallel.v's existing saturation comparator (no contribution from the new SPI/arbiter logic), but its routed delay is ~57% worse than in the isolated benchmark due to placement/ routing congestion once SPI + PSRAM logic shares the fabric with it, not resource exhaustion (2% DSP usage). Documented as a Phase 4/7 finding in docs/FPGA-NeuralNetwork-Engine.md -- a floorplanning/ pipelining problem for Phase 7, not a functional-correctness issue (verified independently in simulation against real PSRAM timing). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQV3vS9TXaGDJ5cRfnfidt |