SyncValsverifier → artifact → classifier → verdict
SyncVals · Trajectory

serial-receiver-framed

claude-code claude-opus-4-8 ✗ failed GOOD_FAILURE ↑ View task
Solved from the instruction alone, tests/ and solution/ were withheld from the agent's workspace and restored only for grading.
Reward = tests/test.sh exit code (0 → resolved); the classification below is post-hoc and cannot change it.
Classification , post-hoc; cannot change the reward
GOOD_FAILUREHonest miss, the agent ran correctly but couldn't solve it. Expected for a hard task; the task is sound.
SubtypeImplementation Bugs
EvidenceVerifier output: "TEST reference_equivalence: FAIL (14 mismatches in 155243 samples)" after dut_present, dut_forbidden, harness_prepare, dut_compiles, and simulation_completed passed. The task instruction explicitly specified cycle behavior such as "Each bit is 16*latched_baud clocks; sample start/data/parity/stop at the midpoint" and "A good frame commits on the edge completing the last stop bit". The agent wrote a plausible UART receiver but used timing-sensitive logic such as `if (cnt == half) shreg[bitpos] <= in;` and then stopped after private tests, concluding "Root cause found: it's a classic testbench race" without changing `/app/dut.sv`.
Root causeThe agent produced a near-miss implementation with cycle-level timing/control bugs, then misdiagnosed its private test failures as testbench races. The official tests check behavior directly against a reference model for requirements that are stated in the instruction, so this is normal hard-task failure rather than a task specification problem.
RecommendationN/A - task is fine
Trajectory
Tool-by-tool agent trajectory
27 tool calls · 4 tool types · 45 steps
Write /app/dut.sv with synthesizable TopModule(clk, reset, in, baud_div_i[15:0], frame_fmt_i[2:0], clear_err_i, data_ack_i, out_byte[7:0], done, parity_err_o, frame_err_o, break_o, ovf_o, byte_count_o[7:0]). Synchronous active-high reset clears state/outputs. Serial idles high; only an idle high-to-low transition starts a frame. Latch baud and format on that edge; frame baud 0 means 1, and later changes are ignored. Each bit is 16*latched_baud clocks; sample start/data/parity/stop at the midpoint. A high start midpoint aborts with no flags. LSB-first formats: 000 8N1, 001 7N1 with out_byte[7]=0, 010 8E1, 011 8O1, 100 8N2; others are 000. Even parity makes XOR(data, parity)=0; odd makes it 1. parity_err_o asserts on the failing parity midpoint; frame_err_o asserts on any low stop midpoint. Error frames still consume remaining stop slots, return to idle only after the final stop slot, and do not update byte, done, or count. A good frame commits on the edge completing the last stop bit: byte, level done, and saturated count update then. Later data_ack_i clears done; ack on commit does not clear new done. A good commit while old done is high and ack is low sets sticky ovf_o and replaces the byte. Sticky error flags clear with clear_err_i; same-cycle error or overflow wins. break_o uses live nonzero baud, asserts after in is low for 11*16*baud_div_i clocks, and deasserts one clock after high. Forbidden: simulator/file/print tasks, preprocessor, DPI, force/release, plusargs, memory/dump calls, hidden grader names, and testbench hierarchy.
I'll analyze this UART receiver spec carefully and implement it. Let me write the module.
write · /app/dut.sv
// Synthesizable UART-style receiver
module TopModule(
    input  wire        clk,
    input  wire        reset,       // synchronous, active-high
    input  wire        in,          // serial input, idles high
    input  wire [15:0] baud_div_i,
    input  wire [2:0]  frame_fmt_i,
    input  wire        clear_err_i,
    input  wire        data_ack_i,
    output reg  [7:0]  out_byte,
    output reg         done,
    output reg         parity_err_o,
    output reg         frame_err_o,
    output reg         break_o,
    output reg         ovf_o,
    output reg  [7:0]  byte_count_o
);

    localparam IDLE  = 3'd0,
               START = 3'd1,
               DATA  = 3'd2,
               PAR   = 3'd3,
               STOP  = 3'd4;

    reg  [2:0]  state;
    reg  [15:0] baud_lat;   // latched baud (>=1)
    reg  [2:0]  fmt_lat;    // latched format
    reg  [20:0] cnt;        // clock counter within a bit
    reg  [3:0]  bitpos;     // data-bit index
    reg  [1:0]  stopidx;    // stop-bit index
    reg  [7:0]  shreg;      // data shift/collect register
    reg         frame_bad;  // this frame has an error -> no commit
    reg         in_d;       // previous serial sample

    // break detection (independent, live baud)
    reg  [23:0] low_cnt;

    wire [20:0] bit_len = {baud_lat, 4'b0};        // 16 * baud
    wire [20:0] half    = {2'b0, baud_lat, 3'b0};  //  8 * baud
    wire [23:0] brk_thr = {8'b0, baud_div_i} * 24'd176; // 11*16*baud

    // Format decode (combinational from latched format)
    reg  [3:0] ndata;
    reg        has_par, par_odd;
    reg  [1:0] nstop;
    always @* begin
        case (fmt_lat)
            3'b001: begin ndata = 4'd7; has_par = 1'b0; par_odd = 1'b0; nstop = 2'd1; end // 7N1
            3'b010: begin ndata = 4'd8; has_par = 1'b1; par_odd = 1'b0; nstop = 2'd1; end // 8E1
            3'b011: begin ndata = 4'd8; has_par = 1'b1; par_odd = 1'b1; nstop = 2'd1; end // 8O1
            3'b100: begin ndata = 4'd8; has_par = 1'b0; par_odd = 1'b0; nstop = 2'd2; end // 8N2
            default:begin ndata = 4'd8; has_par = 1'b0; par_odd = 1'b0; nstop = 2'd1; end // 8N1 / others
        endcase
    end

    // XOR(data, parity_bit) at the parity midpoint (in == parity bit then)
    wire par_calc = (^shreg) ^ in;

    always @(posedge clk) begin
        if (reset) begin
            state        <= IDLE;
            baud_lat     <= 16'd0;
            fmt_lat      <= 3'd0;
            cnt          <= 21'd0;
            bitpos       <= 4'd0;
            stopidx      <= 2'd0;
            shreg        <= 8'd0;
            frame_bad    <= 1'b0;
            in_d         <= 1'b1;
            out_byte     <= 8'd0;
            done         <= 1'b0;
            parity_err_o <= 1'b0;
            frame_err_o  <= 1'b0;
            break_o      <= 1'b0;
            ovf_o        <= 1'b0;
            byte_count_o <= 8'd0;
            low_cnt      <= 24'd0;
        end else begin
            in_d <= in;

            // Sticky error/overflow clears; a same-cycle set later overrides.
            if (clear_err_i) begin
                parity_err_o <= 1'b0;
                frame_err_o  <= 1'b0;
                ovf_o        <= 1'b0;
            end

            // ack clears done; a commit later in this block overrides.
            if (data_ack_i) done <= 1'b0;

            // ---------------- break detection ----------------
            if (in == 1'b0) begin
                if (low_cnt < brk_thr) low_cnt <= low_cnt + 24'd1;
            end else begin
                low_cnt <= 24'd0;
            end

            if (in == 1'b0 && baud_div_i != 16'd0 && (low_cnt + 24'd1 >= brk_thr))
                break_o <= 1'b1;
            else if (in_d == 1'b1)          // one clock after line returns high
                break_o <= 1'b0;

            // ---------------- receive FSM ----------------
            case (state)
                IDLE: begin
                    cnt <= 21'd0;
                    if (in_d == 1'b1 && in == 1'b0) begin // idle high->low starts a frame
                        baud_lat  <= (baud_div_i == 16'd0) ? 16'd1 : baud_div_i;
                        fmt_lat   <= frame_fmt_i;
                        shreg     <= 8'd0;
                        bitpos    <= 4'd0;
                        frame_bad <= 1'b0;
                        cnt       <= 21'd0;
                        state     <= START;
                    end
                end

                START: begin
                    cnt <= cnt + 21'd1;
                    if (cnt == half && in == 1'b1) begin
                        state <= IDLE;               // high start midpoint -> abort, no flags
                        cnt   <= 21'd0;
                    end else if (cnt == bit_len - 21'd1) begin
                        cnt    <= 21'd0;
                        bitpos <= 4'd0;
                        state  <= DATA;
                    end
                end

                DATA: begin
                    cnt <= cnt + 21'd1;
                    if (cnt == half) shreg[bitpos] <= in; // LSB-first
                    if (cnt == bit_len - 21'd1) begin
                        cnt <= 21'd0;
                        if (bitpos == ndata - 4'd1) begin
                            stopidx <= 2'd0;
                            state   <= has_par ? PAR : STOP;
                        end else begin
                            bitpos <= bitpos + 4'd1;
                        end
                    end
                end

                PAR: begin
                    cnt <= cnt + 21'd1;
                    if (cnt == half) begin
                        // even -> par_calc must be 0 ; odd -> par_calc must be 1
                        if (par_calc != par_odd) begin
                            parity_err_o <= 1'b1;
                            frame_bad    <= 1'b1;
                        end
                    end
                    if (cnt == bit_len - 21'd1) begin
                        cnt     <= 21'd0;
                        stopidx <= 2'd0;
                        state   <= STOP;
                    end
                end

                STOP: begin
                    cnt <= cnt + 21'd1;
                    if (cnt == half && in == 1'b0) begin
                        frame_err_o <= 1'b1;
                        frame_bad   <= 1'b1;
                    end
                    if (cnt == bit_len - 21'd1) begin
                        cnt <= 21'd0;
                        if (stopidx == nstop - 2'd1) begin
                            state <= IDLE;
                            if (!frame_bad) begin       // good frame commit
                                out_byte     <= (ndata == 4'd7) ? {1'b0, shreg[6:0]} : shreg;
                                byte_count_o <= (byte_count_o == 8'hFF) ? 8'hFF
                                                                        : byte_count_o + 8'd1;
                                if (done && !data_ack_i) ovf_o <= 1'b1; // unacked old byte
                                done <= 1'b1;
                            end
                        end else begin
                            stopidx <= stopidx + 2'd1;
                        end
                    end
                end

                default: state <= IDLE;
            endcase
        end
    end

endmodule
Let me verify it compiles cleanly.

Lint/compile the SystemVerilog

shell
(iverilog -g2012 -o /tmp/dut.out dut.sv 2>&1 && echo "IVERILOG OK") || (which verilator >/dev/null 2>&1 && verilator --lint-only -Wall dut.sv 2>&1 || echo "no simulator found")
output
IVERILOG OK

[stdout]
IVERILOG OK
Compiles. Let me write a quick self-test bench (separate file) to sanity-check basic reception, overflow, and parity.
write · /tmp/tb.sv
module tb;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1;
  reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;

  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),
    .frame_fmt_i(fmt),.clear_err_i(clear_err_i),.data_ack_i(data_ack_i),
    .out_byte(out_byte),.done(done),.parity_err_o(parity_err_o),
    .frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));

  always #5 clk=~clk;
  integer i;
  // send one bit (value b) lasting 16 clocks
  task send_bit(input b); integer k; begin
    for(k=0;k<16;k=k+1) begin in=b; @(posedge clk); end
  end endtask
  // send a byte 8N1 (start,8 data lsb-first,stop)
  task send_8n1(input [7:0] v; input stopgood); integer k; begin
    send_bit(0);
    for(k=0;k<8;k=k+1) send_bit(v[k]);
    send_bit(stopgood);
  end endtask

  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0;
    in=1; repeat(4) @(posedge clk);
    // Frame 1: 8N1 0xA5
    send_8n1(8'hA5,1'b1);
    repeat(2) @(posedge clk);
    $display("F1 out=%h done=%b count=%d ferr=%b",out_byte,done,byte_count_o,frame_err_o);
    if(out_byte!==8'hA5) $display("FAIL byte");
    if(done!==1'b1) $display("FAIL done");
    if(byte_count_o!==8'd1) $display("FAIL count");
    // Frame 2 without ack -> overflow, replace byte 0x3C
    in=1; repeat(4) @(posedge clk);
    send_8n1(8'h3C,1'b1);
    repeat(2) @(posedge clk);
    $display("F2 out=%h ovf=%b count=%d",out_byte,ovf_o,byte_count_o);
    if(ovf_o!==1'b1) $display("FAIL ovf");
    if(out_byte!==8'h3C) $display("FAIL replace");
    if(byte_count_o!==8'd2) $display("FAIL count2");
    // ack clears
    data_ack_i=1; @(posedge clk); data_ack_i=0; @(posedge clk);
    if(done!==1'b0) $display("FAIL ackclear");
    // clear errors
    clear_err_i=1; @(posedge clk); clear_err_i=0; @(posedge clk);
    if(ovf_o!==1'b0) $display("FAIL clrovf");
    // Frame with framing error (stop low)
    in=1; repeat(4) @(posedge clk);
    send_8n1(8'h55,1'b0);
    repeat(2) @(posedge clk);
    $display("F3 ferr=%b count=%d",frame_err_o,byte_count_o);
    if(frame_err_o!==1'b1) $display("FAIL ferr");
    if(byte_count_o!==8'd2) $display("FAIL count nochange");
    // Even parity good frame 8E1: data 0xFF -> parity 0 (even # ones=8)
    in=1; repeat(4) @(posedge clk);
    clear_err_i=1; @(posedge clk); clear_err_i=0;
    fmt=3'b010; in=1; repeat(4)@(posedge clk);
    send_bit(0); for(i=0;i<8;i=i+1) send_bit(1'b1); send_bit(0); send_bit(1);
    repeat(2) @(posedge clk);
    $display("F4 8E1 out=%h perr=%b",out_byte,parity_err_o);
    if(parity_err_o!==1'b0) $display("FAIL parity good");
    if(out_byte!==8'hFF) $display("FAIL 8e1 byte");
    // Even parity bad: data 0xFF parity 1 -> error
    fmt=3'b010; in=1; repeat(4)@(posedge clk);
    send_bit(0); for(i=0;i<8;i=i+1) send_bit(1'b1); send_bit(1); send_bit(1);
    repeat(2) @(posedge clk);
    $display("F5 perr=%b",parity_err_o);
    if(parity_err_o!==1'b1) $display("FAIL parity bad");
    // break test small baud
    fmt=0; in=1; repeat(4)@(posedge clk);
    in=0; repeat(16*11+4) @(posedge clk);
    $display("BREAK brk=%b (expect1)",break_o);
    if(break_o!==1'b1) $display("FAIL break");
    in=1; @(posedge clk);
    $display("after high brk=%b (expect still1)",break_o);
    @(posedge clk);
    $display("2 after high brk=%b (expect0)",break_o);
    if(break_o!==1'b0) $display("FAIL break deassert");
    $display("DONE");
    $finish;
  end
endmodule

Run self-test simulation

shell
cd /tmp && iverilog -g2012 -o /tmp/sim.out /tmp/tb.sv /app/dut.sv 2>&1 && vvp /tmp/sim.out 2>&1
output
Exit code 2
/tmp/tb.sv:21: error: ';' is an invalid port declaration separator.
/tmp/tb.sv:21: syntax error

[error] tool reported failure

/tmp/tb.sv

− removed
  task send_8n1(input [7:0] v; input stopgood); integer k; begin
+ added
  task send_8n1(input [7:0] v, input stopgood); integer k; begin

Run self-test simulation

shell
cd /tmp && iverilog -g2012 -o /tmp/sim.out /tmp/tb.sv /app/dut.sv 2>&1 && vvp /tmp/sim.out 2>&1
output
F1 out=a5 done=1 count=  1 ferr=0
F2 out=3c ovf=1 count=  2
FAIL ackclear
FAIL clrovf
F3 ferr=1 count=  2
F4 8E1 out=ff perr=0
F5 perr=1
BREAK brk=1 (expect1)
after high brk=1 (expect still1)
2 after high brk=1 (expect0)
FAIL break deassert
DONE
/tmp/tb.sv:84: $finish called at 10585 (1s)
Shell cwd was reset to /app

[stdout]
F1 out=a5 done=1 count=  1 ferr=0
F2 out=3c ovf=1 count=  2
FAIL ackclear
FAIL clrovf
F3 ferr=1 count=  2
F4 8E1 out=ff perr=0
F5 perr=1
BREAK brk=1 (expect1)
after high brk=1 (expect still1)
2 after high brk=1 (expect0)
FAIL break deassert
DONE
/tmp/tb.sv:84: $finish called at 10585 (1s)
[stderr]

Shell cwd was reset to /app
Most checks pass. The three "FAIL" lines look like testbench sampling races (reading regs in the same timestep as the clock edge, before NBA updates settle). Let me confirm by sampling after a small delay.

Patch tb sampling races

shell
cd /tmp && sed -i 's/@(posedge clk); if(done!==1.b0)/@(posedge clk); #1; if(done!==1'"'"'b0)/' /tmp/tb.sv
sed -i 's/@(posedge clk); if(ovf_o!==1.b0)/@(posedge clk); #1; if(ovf_o!==1'"'"'b0)/' /tmp/tb.sv
# break deassert: add settle
sed -i 's|@(posedge clk);\n    \$display("2 after high|XX|' /tmp/tb.sv
grep -n "after high" /tmp/tb.sv
output
79:    $display("after high brk=%b (expect still1)",break_o);
81:    $display("2 after high brk=%b (expect0)",break_o);
Shell cwd was reset to /app

[stdout]
79:    $display("after high brk=%b (expect still1)",break_o);
81:    $display("2 after high brk=%b (expect0)",break_o);
[stderr]

Shell cwd was reset to /app

/tmp/tb.sv

contents
78	    in=1; @(posedge clk);
79	    $display("after high brk=%b (expect still1)",break_o);
80	    @(posedge clk);
81	    $display("2 after high brk=%b (expect0)",break_o);
82	    if(break_o!==1'b0) $display("FAIL break deassert");
83	    $display("DONE");

/tmp/tb.sv

− removed
    @(posedge clk);
    $display("2 after high brk=%b (expect0)",break_o);
+ added
    @(posedge clk); #1;
    $display("2 after high brk=%b (expect0)",break_o);

Re-run self-test

shell
cd /tmp && iverilog -g2012 -o /tmp/sim.out /tmp/tb.sv /app/dut.sv 2>&1 && vvp /tmp/sim.out 2>&1
output
F1 out=a5 done=1 count=  1 ferr=0
F2 out=3c ovf=1 count=  2
FAIL ackclear
FAIL clrovf
F3 ferr=1 count=  2
F4 8E1 out=ff perr=0
F5 perr=1
BREAK brk=1 (expect1)
after high brk=1 (expect still1)
2 after high brk=0 (expect0)
DONE
/tmp/tb.sv:84: $finish called at 10586 (1s)
Shell cwd was reset to /app

[stdout]
F1 out=a5 done=1 count=  1 ferr=0
F2 out=3c ovf=1 count=  2
FAIL ackclear
FAIL clrovf
F3 ferr=1 count=  2
F4 8E1 out=ff perr=0
F5 perr=1
BREAK brk=1 (expect1)
after high brk=1 (expect still1)
2 after high brk=0 (expect0)
DONE
/tmp/tb.sv:84: $finish called at 10586 (1s)
[stderr]

Shell cwd was reset to /app
Break deassert confirmed fine (was a race). The ackclear/clrovf sed didn't match. Let me check those lines.

Find the check lines

shell
cd /tmp && grep -n "ackclear\|clrovf" /tmp/tb.sv
output
47:    if(done!==1'b0) $display("FAIL ackclear");
50:    if(ovf_o!==1'b0) $display("FAIL clrovf");
Shell cwd was reset to /app

[stdout]
47:    if(done!==1'b0) $display("FAIL ackclear");
50:    if(ovf_o!==1'b0) $display("FAIL clrovf");
[stderr]

Shell cwd was reset to /app

/tmp/tb.sv

contents
44	    if(byte_count_o!==8'd2) $display("FAIL count2");
45	    // ack clears
46	    data_ack_i=1; @(posedge clk); data_ack_i=0; @(posedge clk);
47	    if(done!==1'b0) $display("FAIL ackclear");
48	    // clear errors
49	    clear_err_i=1; @(posedge clk); clear_err_i=0; @(posedge clk);
50	    if(ovf_o!==1'b0) $display("FAIL clrovf");
51	    // Frame with framing error (stop low)

/tmp/tb.sv

− removed
    data_ack_i=1; @(posedge clk); data_ack_i=0; @(posedge clk);
    if(done!==1'b0) $display("FAIL ackclear");
    // clear errors
    clear_err_i=1; @(posedge clk); clear_err_i=0; @(posedge clk);
    if(ovf_o!==1'b0) $display("FAIL clrovf");
+ added
    data_ack_i=1; @(posedge clk); data_ack_i=0; @(posedge clk); #1;
    if(done!==1'b0) $display("FAIL ackclear");
    // clear errors
    clear_err_i=1; @(posedge clk); clear_err_i=0; @(posedge clk); #1;
    if(ovf_o!==1'b0) $display("FAIL clrovf");

Re-run self-test

shell
cd /tmp && iverilog -g2012 -o /tmp/sim.out /tmp/tb.sv /app/dut.sv 2>&1 && vvp /tmp/sim.out 2>&1
output
F1 out=a5 done=1 count=  1 ferr=0
F2 out=3c ovf=1 count=  2
FAIL ackclear
FAIL clrovf
F3 ferr=1 count=  2
F4 8E1 out=ff perr=0
F5 perr=1
BREAK brk=1 (expect1)
after high brk=1 (expect still1)
2 after high brk=0 (expect0)
DONE
/tmp/tb.sv:84: $finish called at 10586 (1s)
Shell cwd was reset to /app

[stdout]
F1 out=a5 done=1 count=  1 ferr=0
F2 out=3c ovf=1 count=  2
FAIL ackclear
FAIL clrovf
F3 ferr=1 count=  2
F4 8E1 out=ff perr=0
F5 perr=1
BREAK brk=1 (expect1)
after high brk=1 (expect still1)
2 after high brk=0 (expect0)
DONE
/tmp/tb.sv:84: $finish called at 10586 (1s)
[stderr]

Shell cwd was reset to /app
Still failing , could be real. Let me add a monitor to trace `done`/`ovf` around the ack.

Trace done around ack

shell
cd /tmp && cat > /tmp/tb2.sv <<'EOF'
module tb2;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1; reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;
  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),.frame_fmt_i(fmt),
    .clear_err_i(clear_err_i),.data_ack_i(data_ack_i),.out_byte(out_byte),.done(done),
    .parity_err_o(parity_err_o),.frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));
  always #5 clk=~clk;
  integer k;
  task sb(input b); begin for(k=0;k<16;k=k+1) begin in=b; @(posedge clk); end end endtask
  task s8(input [7:0] v); integer j; begin sb(0); for(j=0;j<8;j=j+1) sb(v[j]); sb(1); end endtask
  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0; in=1; repeat(4)@(posedge clk);
    s8(8'hA5); repeat(3)@(posedge clk);
    $display("t=%0t after F1 done=%b ovf=%b",$time,done,ovf_o);
    data_ack_i=1; @(posedge clk); #1; $display("t=%0t ack pulse done=%b",$time,done);
    data_ack_i=0; @(posedge clk); #1; $display("t=%0t after ack done=%b",$time,done);
    $finish;
  end
endmodule
EOF
iverilog -g2012 -o /tmp/s2.out /tmp/tb2.sv /app/dut.sv 2>&1 && vvp /tmp/s2.out 2>&1
output
t=1685 after F1 done=1 ovf=0
t=1696 ack pulse done=0
t=1706 after ack done=0
/tmp/tb2.sv:20: $finish called at 1706 (1s)
Shell cwd was reset to /app

[stdout]
t=1685 after F1 done=1 ovf=0
t=1696 ack pulse done=0
t=1706 after ack done=0
/tmp/tb2.sv:20: $finish called at 1706 (1s)
[stderr]

Shell cwd was reset to /app
Ack clears `done` correctly in isolation. Let me reproduce the exact overflow-then-ack sequence from the failing test.

Reproduce overflow then ack/clear

shell
cd /tmp && cat > /tmp/tb3.sv <<'EOF'
module tb3;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1; reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;
  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),.frame_fmt_i(fmt),
    .clear_err_i(clear_err_i),.data_ack_i(data_ack_i),.out_byte(out_byte),.done(done),
    .parity_err_o(parity_err_o),.frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));
  always #5 clk=~clk;
  integer k;
  task sb(input b); begin for(k=0;k<16;k=k+1) begin in=b; @(posedge clk); end end endtask
  task s8(input [7:0] v); integer j; begin sb(0); for(j=0;j<8;j=j+1) sb(v[j]); sb(1); end endtask
  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0; in=1; repeat(4)@(posedge clk);
    s8(8'hA5); repeat(2)@(posedge clk);
    in=1; repeat(4)@(posedge clk);
    s8(8'h3C); repeat(2)@(posedge clk);
    $display("after F2 done=%b ovf=%b count=%0d",done,ovf_o,byte_count_o);
    data_ack_i=1; @(posedge clk); data_ack_i=0; @(posedge clk); #1;
    $display("after ack done=%b (expect0)",done);
    clear_err_i=1; @(posedge clk); clear_err_i=0; @(posedge clk); #1;
    $display("after clr ovf=%b (expect0)",ovf_o);
    $finish;
  end
endmodule
EOF
iverilog -g2012 -o /tmp/s3.out /tmp/tb3.sv /app/dut.sv 2>&1 && vvp /tmp/s3.out 2>&1
output
after F2 done=1 ovf=1 count=2
after ack done=1 (expect0)
after clr ovf=1 (expect0)
/tmp/tb3.sv:24: $finish called at 3376 (1s)
Shell cwd was reset to /app

[stdout]
after F2 done=1 ovf=1 count=2
after ack done=1 (expect0)
after clr ovf=1 (expect0)
/tmp/tb3.sv:24: $finish called at 3376 (1s)
[stderr]

Shell cwd was reset to /app
Reproduced a real bug: after overflow, ack fails to clear `done`. Let me monitor the ack cycle.

Monitor state during ack

shell
cd /tmp && cat > /tmp/tb4.sv <<'EOF'
module tb4;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1; reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;
  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),.frame_fmt_i(fmt),
    .clear_err_i(clear_err_i),.data_ack_i(data_ack_i),.out_byte(out_byte),.done(done),
    .parity_err_o(parity_err_o),.frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));
  always #5 clk=~clk;
  integer k;
  task sb(input b); begin for(k=0;k<16;k=k+1) begin in=b; @(posedge clk); end end endtask
  task s8(input [7:0] v); integer j; begin sb(0); for(j=0;j<8;j=j+1) sb(v[j]); sb(1); end endtask
  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0; in=1; repeat(4)@(posedge clk);
    s8(8'hA5); repeat(2)@(posedge clk);
    in=1; repeat(4)@(posedge clk);
    s8(8'h3C); repeat(2)@(posedge clk);
    $monitor("t=%0t done=%b ovf=%b ack=%b state=%b in=%b",$time,done,ovf_o,data_ack_i,d.state,in);
    data_ack_i=1; @(posedge clk); data_ack_i=0; @(posedge clk);@(posedge clk);
    $finish;
  end
endmodule
EOF
iverilog -g2012 -o /tmp/s4.out /tmp/tb4.sv /app/dut.sv 2>&1 && vvp /tmp/s4.out 2>&1
output
t=3335 done=1 ovf=1 ack=1 state=000 in=1
t=3345 done=1 ovf=1 ack=0 state=000 in=1
/tmp/tb4.sv:21: $finish called at 3365 (1s)
Shell cwd was reset to /app

[stdout]
t=3335 done=1 ovf=1 ack=1 state=000 in=1
t=3345 done=1 ovf=1 ack=0 state=000 in=1
/tmp/tb4.sv:21: $finish called at 3365 (1s)
[stderr]

Shell cwd was reset to /app
This may actually be correct behavior (ack coinciding with commit). Let me get a full continuous trace to be sure.

Continuous trace at end of F2 then ack

shell
cd /tmp && cat > /tmp/tb5.sv <<'EOF'
module tb5;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1; reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;
  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),.frame_fmt_i(fmt),
    .clear_err_i(clear_err_i),.data_ack_i(data_ack_i),.out_byte(out_byte),.done(done),
    .parity_err_o(parity_err_o),.frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));
  always #5 clk=~clk;
  integer k;
  task sb(input b); begin for(k=0;k<16;k=k+1) begin in=b; @(posedge clk); end end endtask
  task s8(input [7:0] v); integer j; begin sb(0); for(j=0;j<8;j=j+1) sb(v[j]); sb(1); end endtask
  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0; in=1; repeat(4)@(posedge clk);
    s8(8'hA5); repeat(2)@(posedge clk);
    in=1; repeat(4)@(posedge clk);
    s8(8'h3C);
    // now trace continuously
    repeat(6) begin @(posedge clk); #1;
      $display("t=%0t st=%b done=%b ovf=%b cnt_bl done",$time,d.state,done,ovf_o); end
    // do ack well clear of commit
    data_ack_i=1; @(posedge clk); #1; data_ack_i=0;
    $display("after ack done=%b (expect0)",done);
    $finish;
  end
endmodule
EOF
iverilog -g2012 -o /tmp/s5.out /tmp/tb5.sv /app/dut.sv 2>&1 && vvp /tmp/s5.out 2>&1
output
t=3326 st=000 done=1 ovf=1 cnt_bl done
t=3336 st=000 done=1 ovf=1 cnt_bl done
t=3346 st=000 done=1 ovf=1 cnt_bl done
t=3356 st=000 done=1 ovf=1 cnt_bl done
t=3366 st=000 done=1 ovf=1 cnt_bl done
t=3376 st=000 done=1 ovf=1 cnt_bl done
after ack done=0 (expect0)
/tmp/tb5.sv:25: $finish called at 3386 (1s)
Shell cwd was reset to /app

[stdout]
t=3326 st=000 done=1 ovf=1 cnt_bl done
t=3336 st=000 done=1 ovf=1 cnt_bl done
t=3346 st=000 done=1 ovf=1 cnt_bl done
t=3356 st=000 done=1 ovf=1 cnt_bl done
t=3366 st=000 done=1 ovf=1 cnt_bl done
t=3376 st=000 done=1 ovf=1 cnt_bl done
after ack done=0 (expect0)
/tmp/tb5.sv:25: $finish called at 3386 (1s)
[stderr]

Shell cwd was reset to /app
`done` clears correctly when ack isn't coincident with the commit edge (the earlier "failures" were the ack/clear pulse landing exactly on F2's commit edge , where per spec `done` correctly stays set). Let me confirm `clear_err_i` clears sticky flags cleanly.

Test clear, 7N1, 8N2, abort

shell
cd /tmp && cat > /tmp/tb6.sv <<'EOF'
module tb6;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1; reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;
  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),.frame_fmt_i(fmt),
    .clear_err_i(clear_err_i),.data_ack_i(data_ack_i),.out_byte(out_byte),.done(done),
    .parity_err_o(parity_err_o),.frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));
  always #5 clk=~clk;
  integer k;
  task sb(input b); begin for(k=0;k<16;k=k+1) begin in=b; @(posedge clk); end end endtask
  // 8N1 with chosen stop level
  task s8s(input [7:0] v, input st); integer j; begin sb(0); for(j=0;j<8;j=j+1) sb(v[j]); sb(st); end endtask
  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0; in=1; repeat(4)@(posedge clk);
    // framing error frame (stop=0), no byte/done/count update
    s8s(8'h77,1'b0); in=1; repeat(6)@(posedge clk); #1;
    $display("frame_err=%b done=%b count=%0d (exp 1,0,0)",frame_err_o,done,byte_count_o);
    clear_err_i=1; @(posedge clk); clear_err_i=0; repeat(2)@(posedge clk); #1;
    $display("after clear frame_err=%b (exp 0)",frame_err_o);
    // 7N1 check upper bit zero, value 0x7F -> out 0x7F, and 0xFF data(7 used) -> 0x7F
    fmt=3'b001; in=1; repeat(4)@(posedge clk);
    sb(0); for(k=0;k<7;k=k+1) sb(1'b1); sb(1);  // 7 ones
    repeat(3)@(posedge clk); #1;
    $display("7N1 out=%h (exp 7f) count=%0d",out_byte,byte_count_o);
    // 8N2: two stop bits, second low => frame err
    fmt=3'b100; in=1; repeat(4)@(posedge clk);
    sb(0); for(k=0;k<8;k=k+1) sb(1'b0); sb(1); sb(0); // stop1 high stop2 low
    repeat(3)@(posedge clk); #1;
    $display("8N2 badstop2 frame_err=%b (exp1)",frame_err_o);
    // 8N2 good
    clear_err_i=1; @(posedge clk); clear_err_i=0; in=1; repeat(4)@(posedge clk);
    sb(0); for(k=0;k<8;k=k+1) sb(k[0]); sb(1); sb(1);
    repeat(3)@(posedge clk); #1;
    $display("8N2 good out=%h frame_err=%b (exp 0)",out_byte,frame_err_o);
    // abort: start goes high at midpoint
    fmt=0; in=1; repeat(4)@(posedge clk);
    in=0; repeat(4)@(posedge clk); in=1; repeat(20)@(posedge clk); #1;
    $display("abort: done unchanged frame_err=%b parity=%b",frame_err_o,parity_err_o);
    $finish;
  end
endmodule
EOF
iverilog -g2012 -o /tmp/s6.out /tmp/tb6.sv /app/dut.sv 2>&1 && vvp /tmp/s6.out 2>&1
output
frame_err=1 done=0 count=0 (exp 1,0,0)
after clear frame_err=1 (exp 0)
7N1 out=00 (exp 7f) count=0
8N2 badstop2 frame_err=1 (exp1)
8N2 good out=00 frame_err=1 (exp 0)
abort: done unchanged frame_err=1 parity=0
/tmp/tb6.sv:41: $finish called at 4006 (1s)
Shell cwd was reset to /app

[stdout]
frame_err=1 done=0 count=0 (exp 1,0,0)
after clear frame_err=1 (exp 0)
7N1 out=00 (exp 7f) count=0
8N2 badstop2 frame_err=1 (exp1)
8N2 good out=00 frame_err=1 (exp 0)
abort: done unchanged frame_err=1 parity=0
/tmp/tb6.sv:41: $finish called at 4006 (1s)
[stderr]

Shell cwd was reset to /app
More failures surfaced. Let me trace state through a framing-error frame + clear to find the root cause.

Trace framing-error frame and clear

shell
cd /tmp && cat > /tmp/tb7.sv <<'EOF'
module tb7;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1; reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;
  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),.frame_fmt_i(fmt),
    .clear_err_i(clear_err_i),.data_ack_i(data_ack_i),.out_byte(out_byte),.done(done),
    .parity_err_o(parity_err_o),.frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));
  always #5 clk=~clk;
  integer k;
  task sb(input b); begin for(k=0;k<16;k=k+1) begin in=b; @(posedge clk); end end endtask
  task s8s(input [7:0] v, input st); integer j; begin sb(0); for(j=0;j<8;j=j+1) sb(v[j]); sb(st); end endtask
  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0; in=1; repeat(4)@(posedge clk);
    s8s(8'h77,1'b0);
    in=1;
    repeat(8) begin @(posedge clk); #1; $display("t=%0t st=%b ferr=%b bad=%b in_d=%b",$time,d.state,frame_err_o,d.frame_bad,d.in_d); end
    clear_err_i=1; @(posedge clk); #1; clear_err_i=0;
    $display("clr cycle st=%b ferr=%b",d.state,frame_err_o);
    @(posedge clk); #1; $display("post-clr st=%b ferr=%b",d.state,frame_err_o);
    $finish;
  end
endmodule
EOF
iverilog -g2012 -o /tmp/s7.out /tmp/tb7.sv /app/dut.sv 2>&1 && vvp /tmp/s7.out 2>&1
output
t=1666 st=000 ferr=1 bad=1 in_d=1
t=1676 st=000 ferr=1 bad=1 in_d=1
t=1686 st=000 ferr=1 bad=1 in_d=1
t=1696 st=000 ferr=1 bad=1 in_d=1
t=1706 st=000 ferr=1 bad=1 in_d=1
t=1716 st=000 ferr=1 bad=1 in_d=1
t=1726 st=000 ferr=1 bad=1 in_d=1
t=1736 st=000 ferr=1 bad=1 in_d=1
clr cycle st=000 ferr=0
post-clr st=000 ferr=0
/tmp/tb7.sv:22: $finish called at 1756 (1s)
Shell cwd was reset to /app

[stdout]
t=1666 st=000 ferr=1 bad=1 in_d=1
t=1676 st=000 ferr=1 bad=1 in_d=1
t=1686 st=000 ferr=1 bad=1 in_d=1
t=1696 st=000 ferr=1 bad=1 in_d=1
t=1706 st=000 ferr=1 bad=1 in_d=1
t=1716 st=000 ferr=1 bad=1 in_d=1
t=1726 st=000 ferr=1 bad=1 in_d=1
t=1736 st=000 ferr=1 bad=1 in_d=1
clr cycle st=000 ferr=0
post-clr st=000 ferr=0
/tmp/tb7.sv:22: $finish called at 1756 (1s)
[stderr]

Shell cwd was reset to /app
Clear and framing-error work in isolation. Let me isolate the 7N1 commit issue.

Isolate 7N1 commit

shell
cd /tmp && cat > /tmp/tb8.sv <<'EOF'
module tb8;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1; reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;
  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),.frame_fmt_i(fmt),
    .clear_err_i(clear_err_i),.data_ack_i(data_ack_i),.out_byte(out_byte),.done(done),
    .parity_err_o(parity_err_o),.frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));
  always #5 clk=~clk;
  integer k;
  task sb(input b); begin for(k=0;k<16;k=k+1) begin in=b; @(posedge clk); end end endtask
  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0;
    fmt=3'b001; in=1; repeat(4)@(posedge clk);
    sb(0); for(k=0;k<7;k=k+1) sb(1'b1); sb(1);
    repeat(3)@(posedge clk); #1;
    $display("7N1 out=%h done=%b count=%0d bad=%b ndata=%b",out_byte,done,byte_count_o,d.frame_bad,d.ndata);
    $finish;
  end
endmodule
EOF
iverilog -g2012 -o /tmp/s8.out /tmp/tb8.sv /app/dut.sv 2>&1 && vvp /tmp/s8.out 2>&1
output
7N1 out=00 done=0 count=0 bad=0 ndata=0111
/tmp/tb8.sv:19: $finish called at 566 (1s)
Shell cwd was reset to /app

[stdout]
7N1 out=00 done=0 count=0 bad=0 ndata=0111
/tmp/tb8.sv:19: $finish called at 566 (1s)
[stderr]

Shell cwd was reset to /app

Trace state transitions in 7N1

shell
cd /tmp && cat > /tmp/tb9.sv <<'EOF'
module tb9;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1; reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;
  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),.frame_fmt_i(fmt),
    .clear_err_i(clear_err_i),.data_ack_i(data_ack_i),.out_byte(out_byte),.done(done),
    .parity_err_o(parity_err_o),.frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));
  always #5 clk=~clk;
  integer k;
  reg [3:0] pst;
  task sb(input b); begin for(k=0;k<16;k=k+1) begin in=b; @(posedge clk); end end endtask
  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0;
    fmt=3'b001; in=1; repeat(4)@(posedge clk);
    fork
      begin sb(0); for(k=0;k<7;k=k+1) sb(1'b1); sb(1); repeat(4)@(posedge clk); $finish; end
      begin forever begin @(posedge clk); #1;
        if(d.state!==pst) $display("t=%0t st=%b->? bitpos=%0d stopidx=%0d cnt=%0d shreg=%h done=%b",$time,d.state,d.bitpos,d.stopidx,d.cnt,d.shreg,done);
        pst<=d.state; end end
    join
  end
endmodule
EOF
iverilog -g2012 -o /tmp/s9.out /tmp/tb9.sv /app/dut.sv 2>&1 && vvp /tmp/s9.out 2>&1
output
t=66 st=001->? bitpos=0 stopidx=0 cnt=0 shreg=00 done=0
t=226 st=010->? bitpos=0 stopidx=0 cnt=0 shreg=00 done=0
/tmp/tb9.sv:18: $finish called at 575 (1s)
Shell cwd was reset to /app

[stdout]
t=66 st=001->? bitpos=0 stopidx=0 cnt=0 shreg=00 done=0
t=226 st=010->? bitpos=0 stopidx=0 cnt=0 shreg=00 done=0
/tmp/tb9.sv:18: $finish called at 575 (1s)
[stderr]

Shell cwd was reset to /app
Found it , my testbench had a variable collision (`k` used by both `sb` and the outer loop). The DUT is fine; my tests were malformed. Let me rewrite the comprehensive test correctly.

Comprehensive correct testbench

shell
cd /tmp && cat > /tmp/tf.sv <<'EOF'
module tf;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1; reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;
  integer errs=0;
  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),.frame_fmt_i(fmt),
    .clear_err_i(clear_err_i),.data_ack_i(data_ack_i),.out_byte(out_byte),.done(done),
    .parity_err_o(parity_err_o),.frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));
  always #5 clk=~clk;
  task sb(input b); integer i; begin for(i=0;i<16;i=i+1) begin in=b; @(posedge clk); end end endtask
  // generic frame: nd data bits (lsb-first) from v, optional parity bit pb, ns stop bits value stv
  task frame(input [7:0] v, input integer nd, input haspar, input pb, input integer ns, input stv);
    integer i; begin
      sb(0);
      for(i=0;i<nd;i=i+1) sb(v[i]);
      if(haspar) sb(pb);
      for(i=0;i<ns;i=i+1) sb(stv);
    end
  endtask
  task chk(input cond, input [255:0] msg); begin if(!cond) begin errs=errs+1; $display("FAIL: %0s",msg); end end endtask
  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0; in=1; repeat(4)@(posedge clk);

    // 8N1 0xA5 good
    fmt=0; frame(8'hA5,8,0,0,1,1); in=1; repeat(3)@(posedge clk); #1;
    chk(out_byte===8'hA5,"8N1 byte"); chk(done===1,"8N1 done"); chk(byte_count_o===8'd1,"8N1 count");

    // 8N1 0x3C without ack -> overflow, replace
    frame(8'h3C,8,0,0,1,1); in=1; repeat(3)@(posedge clk); #1;
    chk(out_byte===8'h3C,"ovf replace"); chk(ovf_o===1,"ovf set"); chk(byte_count_o===8'd2,"count2");

    // ack clears done
    data_ack_i=1; @(posedge clk); data_ack_i=0; @(posedge clk); #1; chk(done===0,"ack clears done");
    // clear ovf
    clear_err_i=1; @(posedge clk); clear_err_i=0; @(posedge clk); #1; chk(ovf_o===0,"clear ovf");

    // framing error (stop low): no commit, no count change
    frame(8'h11,8,0,0,1,0); in=1; repeat(4)@(posedge clk); #1;
    chk(frame_err_o===1,"frame_err set"); chk(byte_count_o===8'd2,"count unchanged on ferr");
    clear_err_i=1; @(posedge clk); clear_err_i=0; @(posedge clk); #1; chk(frame_err_o===0,"clear ferr");

    // 7N1: 7 ones -> 0x7F, bit7=0
    fmt=3'b001; in=1; repeat(4)@(posedge clk);
    frame(8'hFF,7,0,0,1,1); in=1; repeat(3)@(posedge clk); #1;
    chk(out_byte===8'h7F,"7N1 byte 7f"); chk(byte_count_o===8'd3,"7N1 count");

    // 8E1 good: data 0xFF (8 ones, even) parity=0
    fmt=3'b010; in=1; repeat(4)@(posedge clk);
    frame(8'hFF,8,1,0,1,1); in=1; repeat(3)@(posedge clk); #1;
    chk(out_byte===8'hFF,"8E1 byte"); chk(parity_err_o===0,"8E1 par ok"); chk(byte_count_o===8'd4,"8E1 count");
    // 8E1 bad: data 0xFF parity=1 -> perr, no commit
    frame(8'hFF,8,1,1,1,1); in=1; repeat(3)@(posedge clk); #1;
    chk(parity_err_o===1,"8E1 par err"); chk(byte_count_o===8'd4,"count unchanged perr");
    clear_err_i=1; @(posedge clk); clear_err_i=0; @(posedge clk); #1;

    // 8O1 good: data 0x01 (odd #ones=1) parity=0 -> XOR=1 odd ok
    fmt=3'b011; in=1; repeat(4)@(posedge clk);
    frame(8'h01,8,1,0,1,1); in=1; repeat(3)@(posedge clk); #1;
    chk(out_byte===8'h01,"8O1 byte"); chk(parity_err_o===0,"8O1 par ok");

    // 8N2 good: two stop high
    fmt=3'b100; in=1; repeat(4)@(posedge clk);
    frame(8'h5A,8,0,0,2,1); in=1; repeat(3)@(posedge clk); #1;
    chk(out_byte===8'h5A,"8N2 byte"); chk(frame_err_o===0,"8N2 ok");
    // 8N2 second stop low -> frame_err
    frame(8'h5A,8,0,0,2,0); in=1; repeat(4)@(posedge clk); #1;
    chk(frame_err_o===1,"8N2 stop2 low ferr");
    clear_err_i=1; @(posedge clk); clear_err_i=0; @(posedge clk); #1;

    // abort: high at start midpoint
    fmt=0; in=1; repeat(4)@(posedge clk);
    in=0; repeat(4)@(posedge clk); in=1; repeat(24)@(posedge clk); #1;
    chk(frame_err_o===0 && parity_err_o===0,"abort no flags");

    if(errs==0) $display("ALL PASS"); else $display("%0d FAILURES",errs);
    $finish;
  end
endmodule
EOF
iverilog -g2012 -o /tmp/tf.out /tmp/tf.sv /app/dut.sv 2>&1 && vvp /tmp/tf.out 2>&1
output
FAIL: ack clears done
FAIL: clear ovf
FAIL: clear ferr
FAIL: 8O1 par ok
FAIL: 8N2 ok
FAIL: abort no flags
6 FAILURES
/tmp/tf.sv:78: $finish called at 15966 (1s)
Shell cwd was reset to /app

[stdout]
FAIL: ack clears done
FAIL: clear ovf
FAIL: clear ferr
FAIL: 8O1 par ok
FAIL: 8N2 ok
FAIL: abort no flags
6 FAILURES
/tmp/tf.sv:78: $finish called at 15966 (1s)
[stderr]

Shell cwd was reset to /app
Core formats all pass now. The remaining failures look like they cascade from `clear`/`ack` pulses. Let me trace the ack-clears-done spot precisely in this context.

Trace ack-clears-done cleanly

shell
cd /tmp && cat > /tmp/tg.sv <<'EOF'
module tg;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1; reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;
  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),.frame_fmt_i(fmt),
    .clear_err_i(clear_err_i),.data_ack_i(data_ack_i),.out_byte(out_byte),.done(done),
    .parity_err_o(parity_err_o),.frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));
  always #5 clk=~clk;
  task sb(input b); integer i; begin for(i=0;i<16;i=i+1) begin in=b; @(posedge clk); end end endtask
  task frame(input [7:0] v, input integer nd, input haspar, input pb, input integer ns, input stv);
    integer i; begin sb(0); for(i=0;i<nd;i=i+1) sb(v[i]); if(haspar) sb(pb); for(i=0;i<ns;i=i+1) sb(stv); end
  endtask
  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0; in=1; repeat(4)@(posedge clk);
    fmt=0; frame(8'hA5,8,0,0,1,1); in=1; repeat(3)@(posedge clk); #1;
    $display("done=%b st=%b",done,d.state);
    data_ack_i=1; @(posedge clk); #1;
    $display("during ack: done=%b st=%b ack=%b",done,d.state,data_ack_i);
    data_ack_i=0; @(posedge clk); #1;
    $display("after ack: done=%b",done);
    $finish;
  end
endmodule
EOF
iverilog -g2012 -o /tmp/tg.out /tmp/tg.sv /app/dut.sv 2>&1 && vvp /tmp/tg.out 2>&1
output
done=1 st=000
during ack: done=0 st=000 ack=1
after ack: done=0
/tmp/tg.sv:23: $finish called at 1706 (1s)
Shell cwd was reset to /app

[stdout]
done=1 st=000
during ack: done=0 st=000 ack=1
after ack: done=0
/tmp/tg.sv:23: $finish called at 1706 (1s)
[stderr]

Shell cwd was reset to /app
Ack works after a single frame. Let me reproduce the exact tf sequence (overflow frame before ack) to see if overflow changes ack behavior.

Overflow frame then ack

shell
cd /tmp && cat > /tmp/th.sv <<'EOF'
module th;
  reg clk=0, reset, in=1, clear_err_i=0, data_ack_i=0;
  reg [15:0] baud=1; reg [2:0] fmt=0;
  wire [7:0] out_byte, byte_count_o;
  wire done, parity_err_o, frame_err_o, break_o, ovf_o;
  TopModule d(.clk(clk),.reset(reset),.in(in),.baud_div_i(baud),.frame_fmt_i(fmt),
    .clear_err_i(clear_err_i),.data_ack_i(data_ack_i),.out_byte(out_byte),.done(done),
    .parity_err_o(parity_err_o),.frame_err_o(frame_err_o),.break_o(break_o),.ovf_o(ovf_o),
    .byte_count_o(byte_count_o));
  always #5 clk=~clk;
  task sb(input b); integer i; begin for(i=0;i<16;i=i+1) begin in=b; @(posedge clk); end end endtask
  task frame(input [7:0] v, input integer nd, input haspar, input pb, input integer ns, input stv);
    integer i; begin sb(0); for(i=0;i<nd;i=i+1) sb(v[i]); if(haspar) sb(pb); for(i=0;i<ns;i=i+1) sb(stv); end
  endtask
  initial begin
    reset=1; @(posedge clk); @(posedge clk); reset=0; in=1; repeat(4)@(posedge clk);
    fmt=0; frame(8'hA5,8,0,0,1,1); in=1; repeat(3)@(posedge clk); #1;
    $display("F1 done=%b out=%h cnt=%0d",done,out_byte,byte_count_o);
    frame(8'h3C,8,0,0,1,1); in=1; repeat(3)@(posedge clk); #1;
    $display("F2 done=%b ovf=%b out=%h cnt=%0d st=%b",done,ovf_o,out_byte,byte_count_o,d.state);
    data_ack_i=1; @(posedge clk); #1; $display("ackcyc done=%b st=%b",done,d.state);
    data_ack_i=0; @(posedge clk); #1; $display("postack done=%b",done);
    $finish;
  end
endmodule
EOF
iverilog -g2012 -o /tmp/th.out /tmp/th.sv /app/dut.sv 2>&1 && vvp /tmp/th.out 2>&1
output
F1 done=1 out=a5 cnt=1
F2 done=1 ovf=1 out=3c cnt=2 st=000
ackcyc done=0 st=000
postack done=0
/tmp/th.sv:23: $finish called at 3336 (1s)
Shell cwd was reset to /app

[stdout]
F1 done=1 out=a5 cnt=1
F2 done=1 ovf=1 out=3c cnt=2 st=000
ackcyc done=0 st=000
postack done=0
/tmp/th.sv:23: $finish called at 3336 (1s)
[stderr]

Shell cwd was reset to /app
Isolated ack/clear all work. The tf failures must be phase-drift coincidences accumulating across many frames. Let me instrument tf directly at the failing points to confirm it's timing, not a DUT bug.

Instrument tf failing points

shell
cd /tmp && sed -i 's|chk(done===0,"ack clears done");|chk(done===0,"ack clears done"); $display("  [dbg ack] done=%b st=%b bad=%b",done,d.state,d.frame_bad);|' /tmp/tf.sv
sed -i 's|chk(ovf_o===0,"clear ovf");|chk(ovf_o===0,"clear ovf"); $display("  [dbg clrovf] ovf=%b st=%b",ovf_o,d.state);|' /tmp/tf.sv
sed -i 's|chk(frame_err_o===0,"clear ferr");|chk(frame_err_o===0,"clear ferr"); $display("  [dbg clrferr] ferr=%b st=%b cnt=%0d",frame_err_o,d.state,d.cnt);|' /tmp/tf.sv
iverilog -g2012 -o /tmp/tf.out /tmp/tf.sv /app/dut.sv 2>&1 && vvp /tmp/tf.out 2>&1 | head -30
output
FAIL: ack clears done
  [dbg ack] done=1 st=000 bad=0
FAIL: clear ovf
  [dbg clrovf] ovf=1 st=000
FAIL: clear ferr
  [dbg clrferr] ferr=1 st=000 cnt=0
FAIL: 8O1 par ok
FAIL: 8N2 ok
FAIL: abort no flags
6 FAILURES
/tmp/tf.sv:78: $finish called at 15966 (1s)
Shell cwd was reset to /app

[stdout]
FAIL: ack clears done
  [dbg ack] done=1 st=000 bad=0
FAIL: clear ovf
  [dbg clrovf] ovf=1 st=000
FAIL: clear ferr
  [dbg clrferr] ferr=1 st=000 cnt=0
FAIL: 8O1 par ok
FAIL: 8N2 ok
FAIL: abort no flags
6 FAILURES
/tmp/tf.sv:78: $finish called at 15966 (1s)
[stderr]

Shell cwd was reset to /app
Root cause found: it's a classic **testbench race** , `x=1; @(posedge clk); x=0;` deasserts the control signal in the same timestep the DUT samples it, so the DUT may see `0`. My isolated tests used `#1` and passed. The DUT is correct. Let me fix the pulse pattern in tf and confirm a clean pass.

Fix tb race and rerun full suite

shell
cd /tmp && python3 - <<'PY'
import re
s=open('/tmp/tf.sv').read()
# add #1 after each "@(posedge clk); XXX_i=0;" pattern -> "@(posedge clk); #1; XXX_i=0;"
s=s.replace("data_ack_i=1; @(posedge clk); data_ack_i=0;","data_ack_i=1; @(posedge clk); #1; data_ack_i=0;")
s=s.replace("clear_err_i=1; @(posedge clk); clear_err_i=0;","clear_err_i=1; @(posedge clk); #1; clear_err_i=0;")
# remove earlier debug displays to keep output clean
s=re.sub(r' \$display\("  \[dbg[^;]*\);','',s)
open('/tmp/tf.sv','w').write(s)
PY
iverilog -g2012 -o /tmp/tf.out /tmp/tf.sv /app/dut.sv 2>&1 && vvp /tmp/tf.out 2>&1
output
ALL PASS
/tmp/tf.sv:78: $finish called at 15966 (1s)
Shell cwd was reset to /app

[stdout]
ALL PASS
/tmp/tf.sv:78: $finish called at 15966 (1s)
[stderr]

Shell cwd was reset to /app
Final diff , what the agent changed

No file changes, the agent left the workspace untouched.

Verifier output , the proof behind the reward
tests/test.sh
TEST dut_present: PASS (/app/dut.sv exists)
TEST dut_forbidden: PASS (no forbidden constructs)
TEST harness_prepare: PASS (harness names nonce-renamed)
TEST dut_compiles: PASS (compiled with held-back reference/testbench)
TEST simulation_completed: PASS (Mismatches: 14 in 155243 samples)
TEST reference_equivalence: FAIL (14 mismatches in 155243 samples)

Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_6c9533cca45d4e81. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.

Trial trial_6c9533cca45d4e81 · verifier authoritative; classifier explanatory.