module testbench();

timeunit 10ns;

timeprecision 1ns;

logic clk;
logic rw;
logic clk_50;
logic rst;

logic [2:0] addr;
logic [7:0] data;
logic cs;

logic i_sd_cmd;
logic o_sd_cmd;

logic i_sd_data;
logic o_sd_data;

logic cpu_phi2;

always @(posedge clk) begin
    cpu_phi2 <= cpu_phi2 === '0;
end

sd_controller dut(
    .sd_clk(cpu_phi2),
    .*);

always #1 clk_50 = clk_50 === 1'b0;
always #100 clk = clk === 1'b0;

task write_reg(logic [3:0] _addr, logic [7:0] _data);
    @(posedge clk);
    cs = '1;
    addr = _addr;
    rw = '0;
    data = '1;
    @(posedge clk);
    data = _data;
    @(posedge clk);
    cs = '0;
    rw = '1;
endtask

task verify_cmd(logic [5:0] cmd, logic [31:0] arg, logic [47:0] verify);
    write_reg(0, arg[7:0]);
    write_reg(1, arg[15:8]);
    write_reg(2, arg[23:16]);
    write_reg(3, arg[31:24]);
    write_reg(4, cmd);

    $display("arg: %x", dut.arg);
    $display("dut.cmd: %x", dut.cmd);

    @(posedge clk);
    @(posedge clk);

    while (dut.state.macro == dut.TXCMD) begin
        assert(o_sd_cmd == verify[47-dut.state.count]) else begin
            $error("cmd output error: Expected %h:%b, got %h:%b", 
                47-dut.state.count, verify[47-dut.state.count],
                47-dut.state.count, o_sd_cmd);
        end
        @(negedge clk);
    end
endtask

localparam cmd0 = 48'h400000000095;
localparam cmd8 = 48'h48000001aa87;
localparam cmd55 = 48'h770000000065;
localparam cmd41 = 48'h694018000019;

initial begin
    rst <= '1;
    repeat(5) @(posedge clk);
    rst <= '0;

    verify_cmd(0, 0, cmd0);
    verify_cmd(8, 'h1aa, cmd8);
    verify_cmd('d55, 0, cmd55);
    verify_cmd('d41, 'h40180000, cmd41);

    $finish();
end

endmodule