Skip to content

Silent bit-truncation in cellift/src/ift_rom_mem.sv taint preload #2

Description

@Yihao23

Observed Behavior

cellift/src/ift_rom_mem.sv line 171 has a SystemVerilog dimension-mismatch bug that
silently truncates every preloaded taint word to its LSB.

When the read_taints() initial block reports a successful load via $display (e.g. Bank 0: tainting addr/wbytes 0x20000120 to boot rom addr 0x120: 01f00000), the value stored in
the mem_taints associative array is actually 32'h00000000 for that key — only bit 0 of
the intended 32-bit word is written, and bits [31:1] are silently dropped.

The downstream effects observed in the VCD/FST trace:

  • i_instr_rom.rdata_o_taint_before_conservative stays at 0 for the entire simulation.
  • i_instr_rom.rdata_t0 likewise stays at 0.
  • instr_rdata_t0 at the core input is 0, so no instruction-fetch taint propagates into
    the ibex core.

SIMROMTAINT is therefore effectively a no-op for any taint mask whose bit 0 is 0.

Expected Behavior

For a taint preload line such as

0 0x80000480 4 0000f001

(taints the rs2 field of the instruction at PC 0x80000480, word-level mask
0x01F00000), mem_taints[<key>] should hold 32'h01F00000 after the initial block runs.
The downstream i_instr_rom.rdata_t0 should be 0x01F00000 when PC = 0x80000480 is
fetched, and instr_rdata_t0 at the core boundary should reflect this taint.

The companion file cellift/src/ift_sram_mem.sv already demonstrates the correct behavior
with a 32-bit OR (mem_taints[<key>] |= word).

Steps to reproduce the issue

Setup

  1. Build a cellift-ibex simulation that fetches PC = 0x80000480. (Any firmware containing
    an instruction at that PC works.)

  2. Create a taint preload file (e.g. /tmp/rom_taint.txt) with a single line:

    0 0x80000480 4 0000f001
    

    (Word mask 0x01F00000 = rs2 field bits [24:20]. Bit 0 of this word is 0, which
    is what triggers the bug.)

  3. Export the env var and run the test:

    export SIMROMTAINT=/tmp/rom_taint.txt
    cd cellift-ibex/cellift
    bash tests.sh

What you will see

The taint loader reports success on stdout:

Preloading boot rom taints with: /tmp/rom_taint.txt (bank 0) (offset 80000000)
Bank 0: tainting addr/wbytes 0000000020000120 to boot rom addr 0000000000000120: 01f00000
Done preloading boot rom taints (bank 0).

The ROM read fires correctly for that address:

ROM_READ: word_addr=00000120 rdata=022243b3

But in the VCD/FST trace (out.trace):

  • i_instr_rom.rdata_o_taint_before_conservative stays 0 for the entire run.
  • i_instr_rom.rdata_t0 stays 0.
  • instr_rdata_t0 at the core input stays 0.

Discriminating test pair

Re-run with the following two masks to confirm the LSB-truncation mechanism:

meminit hex string intended 32-bit mask bit 0 observed instr_rdata_t0
01000000 0x00000001 1 0x00000001 (bit 0 propagates)
feffffff 0xFFFFFFFE 0 0x00000000 (full mask silently dropped)

Without the bug, the feffffff row should produce instr_rdata_t0 = 0xFFFFFFFE.

Root cause

// cellift/src/ift_rom_mem.sv, line 144
logic [Width-1:0] mem_taints [bit [31:0]];   // one-dimensional, 32-bit per entry

// cellift/src/ift_rom_mem.sv, line 171
mem_taints[((section_addr-AddrOffset)/WidthBytes+i)/NumBanks][taint_id] = word;
//                                                            ^^^^^^^^^^
//   With NumTaints == 1, taint_id = 0, so [0] is a 1-bit slice of the 32-bit
//   entry. SystemVerilog assigns only word[0] into that bit; word[31:1] is
//   silently discarded.

Suggested fix

Replace line 171 with the SRAM-style 32-bit OR (drops the spurious [taint_id]):

mem_taints[((section_addr-AddrOffset)/WidthBytes+i)/NumBanks] |= word;

This matches the behavior implied by the surrounding $display and brings the ROM preload
in line with ift_sram_mem.sv.

My Environment

EDA tool and version:
Verilator (any recent version) + cellift-yosys (this repository's bundled version).

Operating system:
Ubuntu 22.04

Version of the Ibex source code:
comsec-group/cellift-ibex master branch.

Scope / Affected designs

  • Affected: cellift-ibex only, and only the SIMROMTAINT preload path through
    ift_rom_mem.sv.
  • Not affected (SRAM-side): SIMSRAMTAINT via ift_sram_mem.sv is correct (uses
    32-bit |=).

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions