// retroDE_ps2 — tb_gs_alpha_blend (Brick 2a) // // Unit TB for the gs_alpha_blend datapath. Drives (Cs, Cd, As) and // checks Cv == ((Cs - Cd) * As) >> 7 + Cd per channel, with clamping, // against an independent software reference computed in the TB. // // Covers: // - As = 0 -> Cv == Cd (fully transparent source) // - As = 128 -> Cv == Cs (fully opaque source) // - As = 64 -> ~50% blend, mid value, NEITHER Cs nor Cd // - dest brighter than source (negative diff path) // - clamp-high and clamp-low edge values // - randomized sweep cross-checked against the reference model `timescale 1ns/1ps module tb_gs_alpha_blend; logic [7:0] cs_r, cs_g, cs_b, as; logic [7:0] cd_r, cd_g, cd_b; logic [7:0] cv_r, cv_g, cv_b, a_out; gs_alpha_blend dut ( .cs_r(cs_r), .cs_g(cs_g), .cs_b(cs_b), .as(as), .cd_r(cd_r), .cd_g(cd_g), .cd_b(cd_b), .cv_r(cv_r), .cv_g(cv_g), .cv_b(cv_b), .a_out(a_out) ); int errors = 0; // Independent reference: signed math + clamp, As clamped at 128. function automatic logic [7:0] ref_blend(input logic [7:0] cs, input logic [7:0] cd, input logic [7:0] alpha); int a_eff; int diff; int prod; int shifted; int sum; a_eff = (alpha > 128) ? 128 : alpha; diff = int'(cs) - int'(cd); prod = diff * a_eff; // Arithmetic >> 7 (floor toward -inf for negatives, matching >>>). shifted = prod >>> 7; sum = shifted + int'(cd); if (sum < 0) return 8'd0; else if (sum > 255) return 8'd255; else return 8'(sum); endfunction task automatic check(input logic [7:0] sr, sg, sb, a, input logic [7:0] dr, dg, db, input string label); logic [7:0] er, eg, eb; cs_r = sr; cs_g = sg; cs_b = sb; as = a; cd_r = dr; cd_g = dg; cd_b = db; #1; er = ref_blend(sr, dr, a); eg = ref_blend(sg, dg, a); eb = ref_blend(sb, db, a); if (cv_r !== er || cv_g !== eg || cv_b !== eb) begin $error("[%s] Cs=(%02x,%02x,%02x) As=%0d Cd=(%02x,%02x,%02x): got (%02x,%02x,%02x) exp (%02x,%02x,%02x)", label, sr, sg, sb, a, dr, dg, db, cv_r, cv_g, cv_b, er, eg, eb); errors = errors + 1; end if (a_out !== a) begin $error("[%s] a_out passthrough got %02x exp %02x", label, a_out, a); errors = errors + 1; end endtask initial begin // As=0 -> Cv == Cd exactly. check(8'hFF, 8'h80, 8'h10, 8'd0, 8'h11, 8'h22, 8'h33, "as0_eq_cd"); #1; if (cv_r !== 8'h11 || cv_g !== 8'h22 || cv_b !== 8'h33) begin $error("as=0 must equal Cd"); errors = errors + 1; end // As=128 -> Cv == Cs exactly. cs_r=8'hAB; cs_g=8'hCD; cs_b=8'hEF; as=8'd128; cd_r=8'h00; cd_g=8'h7F; cd_b=8'hFF; #1; if (cv_r !== 8'hAB || cv_g !== 8'hCD || cv_b !== 8'hEF) begin $error("as=128 must equal Cs: got (%02x,%02x,%02x)", cv_r, cv_g, cv_b); errors = errors + 1; end // As=64 (~0.5): mid blend, must be strictly between Cs and Cd. cs_r=8'd200; cs_g=8'd200; cs_b=8'd200; as=8'd64; cd_r=8'd0; cd_g=8'd0; cd_b=8'd0; #1; // (200-0)*64>>7 + 0 = 12800>>7 = 100. if (cv_r !== 8'd100) begin $error("as=64 mid blend: got %0d exp 100", cv_r); errors = errors + 1; end if (cv_r === cs_r || cv_r === cd_r) begin $error("as=64 blend collapsed to pure src or dest"); errors = errors + 1; end // dest brighter than source -> negative diff path. check(8'd10, 8'd10, 8'd10, 8'd64, 8'd250, 8'd250, 8'd250, "neg_diff"); // clamp edges and assorted values via reference cross-check. check(8'hFF, 8'hFF, 8'hFF, 8'd128, 8'h00, 8'h00, 8'h00, "white_over_black"); check(8'h00, 8'h00, 8'h00, 8'd128, 8'hFF, 8'hFF, 8'hFF, "black_over_white"); check(8'h40, 8'h80, 8'hC0, 8'd40, 8'h90, 8'h20, 8'h10, "assorted1"); check(8'hF0, 8'h0F, 8'h55, 8'd96, 8'h0A, 8'hF5, 8'hAA, "assorted2"); // As > 128 must clamp to 128 (== Cs). cs_r=8'h33; cs_g=8'h44; cs_b=8'h55; as=8'd200; cd_r=8'h99; cd_g=8'hAA; cd_b=8'hBB; #1; if (cv_r !== 8'h33 || cv_g !== 8'h44 || cv_b !== 8'h55) begin $error("as>128 must clamp to Cs: got (%02x,%02x,%02x)", cv_r, cv_g, cv_b); errors = errors + 1; end // Randomized sweep. for (int i = 0; i < 2000; i++) begin check(8'($urandom), 8'($urandom), 8'($urandom), 8'($urandom % 129), 8'($urandom), 8'($urandom), 8'($urandom), "rand"); end if (errors == 0) $display("[tb_gs_alpha_blend] PASS"); else $display("[tb_gs_alpha_blend] FAIL errors=%0d", errors); $finish; end endmodule : tb_gs_alpha_blend