data/full_repos/permissive/131196995/hardware/cpu.v
None: at end of input
1: b'%Error: data/full_repos/permissive/131196995/hardware/cpu.v:9: Unsupported or unknown PLI call: $dumpfile\n        $dumpfile("cpu.vcd");\n        ^~~~~~~~~\n%Error: data/full_repos/permissive/131196995/hardware/cpu.v:10: Unsupported or unknown PLI call: $dumpvars\n        $dumpvars(0,main);\n        ^~~~~~~~~\n%Error: Exiting due to 2 error(s)\n        ... See the manual and https://verilator.org for more assistance.\n'
module main();




    initial begin
        $dumpfile("cpu.vcd");
        $dumpvars(0,main);
    end


    wire clk;
    clock c0(clk);

    reg halt = 0;

    counter ctr(halt,clk);




    


   reg branch_predictor_ON = 1;


    reg [32:0] branch_cache[7:0];
    reg [2:0] branch_c = 0;
    
    function automatic [16:0] branch_prediction;
        input [15:0] current_PC;
        begin
            if (!branch_predictor_ON)
                branch_prediction = 0;
            else if (branch_cache[branch_c][32] && branch_cache[branch_c][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c][15:0]};
            else if (branch_cache[branch_c-1][32] && branch_cache[branch_c-1][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-1][15:0]};
            else if (branch_cache[branch_c-2][32] && branch_cache[branch_c-2][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-2][15:0]};
            else if (branch_cache[branch_c-3][32] && branch_cache[branch_c-3][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-3][15:0]};
            else if (branch_cache[branch_c-4][32] && branch_cache[branch_c-4][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-4][15:0]};
            else if (branch_cache[branch_c-5][32] && branch_cache[branch_c-5][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-5][15:0]};
            else if (branch_cache[branch_c-6][32] && branch_cache[branch_c-6][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-6][15:0]};
            else if (branch_cache[branch_c-7][32] && branch_cache[branch_c-7][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-7][15:0]};
            else
                branch_prediction = 0;
        end
    endfunction





    wire [15:1] mem_raddr0;
    wire [15:0] mem_rdata0;

    wire [15:1] mem_raddr1;
    wire [15:0] mem_rdata1;

    wire mem_should_write;
    wire [15:1] mem_waddr;
    wire [15:0] mem_wdata;

    mem mem(clk,
         mem_raddr0,mem_rdata0,
         mem_raddr1,mem_rdata1,
         mem_should_write,mem_waddr,mem_wdata);
    




    wire [3:0] regs_raddr0;
    wire [15:0] regs_rdata0;

    wire [3:0] regs_raddr1;
    wire [15:0] regs_rdata1;

    wire regs_should_write;
    wire [3:0] regs_waddr;
    wire [15:0] regs_wdata;

    
    regs regs(clk,
        regs_raddr0,regs_rdata0,
        regs_raddr1,regs_rdata1,
        regs_should_write,regs_waddr,regs_wdata);






    wire [16:0] f0_predict = branch_prediction(f0_PC);
    wire f0_V = load_mis ? 0 : 1; 
    reg [15:0] f0_PC = 16'h0000;
    wire [15:0] f0_nextpc = wb_flush_should ? wb_flush_to
                        : load_mis ? f0_PC 
                        : f0_mis ? f0_PC+1
                        : f0_predict[16] ? f0_predict[15:0]
                        : f0_PC + 2;

    wire f0_mis = f0_V && f0_PC[0];


    assign mem_raddr0 = load_mis ? (load_mem_regs_rdata0[15:1] + 1) % 32768
                    : f1_mis ? f1_PC[15:1]+1 % 32768
                    : f0_PC[15:1] % 32768;

    always @(posedge clk) begin
        f0_PC <= f0_nextpc;
        f1_mis <= f0_mis;
    end




    
    reg f1_V = 0;
    reg f1_mis = 0;
    reg [15:0] f1_PC;

    always @(posedge clk) begin
        if (wb_flush_should)
            f1_V <= 0;
        else 
            f1_V <= f0_mis ? 0 : f0_V;

        f1_PC <= f0_PC;
        dec_mis <= f1_mis;
    end




    
    reg dec_V = 0;
    reg dec_mis;
    reg [15:0] dec_PC;
    wire [15:0] dec_ins = load_mis_prev ? {load_ins[7:0], mem_rdata0[15:8]} : mem_rdata0;



    wire [3:0] dec_opcode = dec_ins[15:12];
    wire [3:0] dec_dest = dec_ins[3:0];

    wire [7:0] dec_src = dec_ins[11:4];
    wire [3:0] dec_src_hi = dec_ins[11:8];
    wire [3:0] dec_src_lo = dec_ins[7:4];

    wire dec_is_sub = dec_opcode == 0;
    wire dec_is_mul = dec_opcode == 1;
    wire dec_is_add = dec_opcode == 2;
    wire dec_is_movl = dec_opcode == 8;
    wire dec_is_movh = dec_opcode == 9;
    wire dec_is_jump = dec_opcode == 14 && (dec_src_lo >= 0 && dec_src_lo <= 3);
    wire dec_is_ld = dec_opcode == 15 && dec_src_lo == 0;
    wire dec_is_st = dec_opcode == 15 && dec_src_lo == 1;


    assign regs_raddr0 =  dec_is_sub || dec_is_add || dec_is_mul ? dec_src_hi
                        : dec_is_movh ? dec_dest 
                        : dec_is_jump ? dec_src_hi
                        : dec_is_ld ? dec_src_hi
                        : dec_is_st ? dec_src_hi
                        : 0;

    assign regs_raddr1 =  dec_is_sub || dec_is_add || dec_is_mul ? dec_src_lo
                        : dec_is_movh ? 0
                        : dec_is_jump ? dec_dest
                        : dec_is_ld ? 0
                        : dec_is_st ? dec_dest
                        : 0;

    always @(posedge clk) begin
        if (wb_flush_should)
            dec_V <= 0;
        else
            dec_V <= f1_V;

        dec_PC <= f1_PC;


        load_ins <= dec_ins;
        load_mis_prev <= dec_mis;
    end





    reg load_V = 0;
    reg [15:0] load_PC;
    reg [15:0] load_ins;
    reg load_mis_prev;

    wire [15:0] load_regs_rdata0 = regs_rdata0;
    wire [15:0] load_regs_rdata1 = regs_rdata1;

    wire [15:0] load_regs_fwd_rdata0 = load2_V && load2_reg_should && load2_reg_addr == load_regs0_needed ? load2_reg_data
                        : wb_V && wb_reg_should && wb_reg_addr == load_regs0_needed ? wb_reg_data 
                        : load_regs_rdata0;
    wire [15:0] load_regs_fwd_rdata1 = load2_V && load2_reg_should && load2_reg_addr == load_regs1_needed ? load2_reg_data
                        : wb_V && wb_reg_should && wb_reg_addr == load_regs1_needed ? wb_reg_data 
                        : load_regs_rdata1;

    wire [3:0] load_regs0_needed =  load_is_sub || load_is_add || load_is_mul ? load_src_hi
                        : load_is_movh ? load_dest 
                        : load_is_jump ? load_src_hi
                        : load_is_ld ? load_src_hi
                        : load_is_st ? load_src_hi
                        : 0;

    wire[3:0] load_regs1_needed =  load_is_sub || load_is_add || load_is_mul ? load_src_lo
                        : load_is_movh ? 0
                        : load_is_jump ? load_dest
                        : load_is_ld ? 0
                        : load_is_st ? load_dest
                        : 0;

    wire [3:0] load_opcode = load_ins[15:12];
    wire [3:0] load_dest = load_ins[3:0];

    wire [7:0] load_src = load_ins[11:4];
    wire [3:0] load_src_hi = load_ins[11:8];
    wire [3:0] load_src_lo = load_ins[7:4];

    wire load_is_sub = load_opcode == 0;
    wire load_is_mul = load_opcode == 1;
    wire load_is_add = load_opcode == 2;
    wire load_is_movl = load_opcode == 8;
    wire load_is_movh = load_opcode == 9;
    wire load_is_jump = load_opcode == 14 && (load_src_lo >= 0 && load_src_lo <= 3);
    wire load_is_ld = load_opcode == 15 && load_src_lo == 0;
    wire load_is_st = load_opcode == 15 && load_src_lo == 1;




    wire load_is_screwed = load_V && load_is_ld && load2_V && load2_is_ld && load2_reg_should && load2_reg_addr == load_regs0_needed && load2_is_ld ||
                        load_V && load_is_ld && load2_V && load2_is_ld && load2_reg_should && load2_reg_addr == load_regs1_needed && load2_is_ld;



    wire [15:0] load_mem_regs_rdata0 = load_src_hi == 0 ? 0 : load_regs_fwd_rdata0;
    wire load_mis = load_V && (load_is_ld || load_is_st) && !(load_mem_regs_rdata0[0]===1'bX) && load_mem_regs_rdata0[0];

    assign mem_raddr1 = load_mem_regs_rdata0[15:1] % 32768;

    always @(posedge clk) begin
        if (wb_flush_should) 
            load_V <= 0;
        else
            load_V <= dec_V;
        load_PC <= dec_PC;

        load2_ins <= load_ins;
        load2_regs_rdata0 <= load_regs_fwd_rdata0;
        load2_regs_rdata1 <= load_regs_fwd_rdata1;
        load2_mis <= load_mis;

        load2_past_wen0 <= mem_should_write;
        load2_past_waddr0 <= mem_waddr;
        load2_past_wdata0 <= mem_wdata;

        load2_is_screwed <= load_is_screwed;
    end 






    reg load2_V = 0;
    reg [15:0] load2_PC;
    reg [15:0] load2_ins;
    reg load2_mis = 0;
    reg load2_is_screwed = 0;

    reg [15:0] load2_regs_rdata0;
    reg [15:0] load2_regs_rdata1;

    reg load2_past_wen0;
    reg [15:1] load2_past_waddr0;
    reg [15:0] load2_past_wdata0;

    wire [15:0] load2_regs_fwd_rdata0 = wb_V && wb_reg_should && wb_reg_addr == load2_regs0_needed ? wb_reg_data : load2_regs_rdata0;
    wire [15:0] load2_regs_fwd_rdata1 = wb_V && wb_reg_should && wb_reg_addr == load2_regs1_needed ? wb_reg_data : load2_regs_rdata1;

    wire [3:0] load2_regs0_needed =  load2_is_sub || load2_is_add || load2_is_mul ? load2_src_hi
                        : load2_is_movh ? load2_dest 
                        : load2_is_jump ? load2_src_hi
                        : load2_is_ld ? load2_src_hi
                        : load2_is_st ? load2_src_hi
                        : 0;

    wire[3:0] load2_regs1_needed =  load2_is_sub || load2_is_add || load2_is_mul ? load2_src_lo
                        : load2_is_movh ? 0
                        : load2_is_jump ? load2_dest
                        : load2_is_ld ? 0
                        : load2_is_st ? load2_dest
                        : 0;


    wire [3:0] load2_opcode = load2_ins[15:12];
    wire [3:0] load2_dest = load2_ins[3:0];

    wire [7:0] load2_src = load2_ins[11:4];
    wire [3:0] load2_src_hi = load2_ins[11:8];
    wire [3:0] load2_src_lo = load2_ins[7:4];

    wire load2_is_sub = load2_opcode == 0;
    wire load2_is_mul = load2_opcode == 1;
    wire load2_is_add = load2_opcode == 2;
    wire load2_is_movl = load2_opcode == 8;
    wire load2_is_movh = load2_opcode == 9;
    wire load2_is_jump = load2_opcode == 14 && (load2_src_lo >= 0 && load2_src_lo <= 3);
    wire load2_is_ld = load2_opcode == 15 && load2_src_lo == 0;
    wire load2_is_st = load2_opcode == 15 && load2_src_lo == 1;


    wire [15:0] load2_sub_tmp1 = load2_src_hi == 0 ? 0 : load2_regs_fwd_rdata0;
    wire [15:0] load2_sub_tmp2 = load2_src_lo == 0 ? 0 : load2_regs_fwd_rdata1;
    wire [15:0] load2_sub_res = load2_sub_tmp1 - load2_sub_tmp2;

    wire [15:0] load2_movl_res = { {8{load2_src[7]}}, load2_src[7:0] };
    wire [15:0] load2_movh_tmp = load2_dest == 0 ? 0 

                            : load2_regs_fwd_rdata0;
    wire [15:0] load2_movh_res = { load2_src[7:0], load2_movh_tmp[7:0] };

    wire load2_reg_should = load2_V && load2_dest != 0 && (load2_is_sub || load2_is_add || load2_is_mul || load2_is_movl || load2_is_movh || load2_is_ld);
    wire [3:0] load2_reg_addr = load2_dest;
    wire [15:0] load2_reg_data = load2_is_sub || load2_is_add || load2_is_mul ? load2_sub_res
                    : load2_is_movl ? load2_movl_res
                    : load2_is_movh ? load2_movh_res
                    : load2_is_ld ? 0
                    : 1;


    always @(posedge clk) begin
        if (wb_flush_should)
            load2_V <= 0;
        else
            load2_V <= load_V;
        load2_PC <= load_PC;

        wb_ins <= load2_ins;
        wb_regs_rdata0 <= load2_regs_fwd_rdata0;
        wb_regs_rdata1 <= load2_regs_fwd_rdata1;
        wb_mis <= load2_mis;

        wb_past_wen0 <= load2_past_wen0;
        wb_past_waddr0 <= load2_past_waddr0;
        wb_past_wdata0 <= load2_past_wdata0;

        wb_past_wen1 <= mem_should_write;
        wb_past_waddr1 <= mem_waddr;


        wb_past_wdata1 <= mem_wdata;

        wb_is_screwed <= load2_is_screwed;

    end






    reg wb_V = 0;
    reg [15:0] wb_PC;
    reg [15:0] wb_ins;
    reg wb_mis;
    reg wb_is_screwed;

    reg [15:0] wb_regs_rdata0;
    reg [15:0] wb_regs_rdata1;


    reg wb_past_wen0 = 0;
    reg [15:1] wb_past_waddr0;
    reg [15:0] wb_past_wdata0;
    reg wb_past_wen1 = 0;
    reg [15:1] wb_past_waddr1;
    reg [15:0] wb_past_wdata1;


    wire [3:0] wb_opcode = wb_ins[15:12];
    wire [3:0] wb_dest = wb_ins[3:0];

    wire [7:0] wb_src = wb_ins[11:4];
    wire [3:0] wb_src_hi = wb_ins[11:8];
    wire [3:0] wb_src_lo = wb_ins[7:4];

    wire wb_is_sub = wb_opcode == 0;
    wire wb_is_mul = wb_opcode == 1;
    wire wb_is_add = wb_opcode == 2;
    wire wb_is_movl = wb_opcode == 8;
    wire wb_is_movh = wb_opcode == 9;
    wire wb_is_jump = wb_opcode == 14 && (wb_src_lo >= 0 && wb_src_lo <= 3);
    wire wb_is_ld = wb_opcode == 15 && wb_src_lo == 0;
    wire wb_is_st = wb_opcode == 15 && wb_src_lo == 1;

    wire wb_should_halt = !(wb_is_sub || wb_is_add || wb_is_mul || wb_is_movl || wb_is_movh || wb_is_jump || wb_is_ld || wb_is_st);


    wire [15:0] wb_sub_tmp1 = wb_src_hi == 0 ? 0 : wb_regs_rdata0;
    wire [15:0] wb_sub_tmp2 = wb_src_lo == 0 ? 0 : wb_regs_rdata1;
    wire [15:0] wb_sub_res = wb_sub_tmp1 - wb_sub_tmp2;
    wire [15:0] wb_mul_res = wb_sub_tmp1 * wb_sub_tmp2;
    wire [15:0] wb_add_res = wb_sub_tmp1 + wb_sub_tmp2;

    wire [15:0] wb_movl_res = { {8{wb_src[7]}}, wb_src[7:0] };
    wire [15:0] wb_movh_tmp = wb_dest == 0 ? 0 

                            : wb_regs_rdata0;
    wire [15:0] wb_movh_res = { wb_src[7:0], wb_movh_tmp[7:0] };
    wire [15:0] wb_jmp_fixed_data = wb_src_hi == 0 ? 0 : wb_regs_rdata0;
    wire wb_jmp_cond = wb_src_lo == 0 ? $signed(wb_jmp_fixed_data)==0 
                : wb_src_lo == 1 ? $signed(wb_jmp_fixed_data)!=0
                : wb_src_lo == 2 ? $signed(wb_jmp_fixed_data) < 0
                : wb_src_lo == 3 ? $signed(wb_jmp_fixed_data) >= 0
                : 1;

    wire [15:0] wb_mem_rdata1 = sm_V && sm_write && mem_waddr == (wb_regs_rdata0[15:1] % 32678) ? mem_wdata
                            : wb_past_wen1 && wb_past_waddr1 == (wb_regs_rdata0[15:1] % 32768) ? wb_past_wdata1
                            : wb_past_wen0 && wb_past_waddr0 == (wb_regs_rdata0[15:1] % 32768) ? wb_past_wdata0
                            : mem_rdata1;

    wire [15:0] wb_mem_rdata0 = sm_V && sm_write && mem_waddr == ((wb_regs_rdata0[15:1]+1) % 32768) ? mem_wdata
                            : wb_past_wen1 && wb_past_waddr1 == ((wb_regs_rdata0[15:1]+1) % 32768) ? wb_past_wdata1
                            : wb_past_wen0 && wb_past_waddr0 == ((wb_regs_rdata0[15:1]+1) % 32768) ? wb_past_wdata0
                            : mem_rdata0;

    wire [15:0] wb_ld_res = wb_mis ? {wb_mem_rdata1[7:0], wb_mem_rdata0[15:8]}
                            : wb_mem_rdata1;


    wire wb_print_should = wb_dest == 0 && (wb_is_sub || wb_is_add || wb_is_mul || wb_is_movl || wb_is_movh || wb_is_ld);
    wire [15:0] wb_print_this = wb_is_sub ? wb_sub_res
                            : wb_is_add ? wb_add_res
                            : wb_is_mul ? wb_mul_res
                            : wb_is_movl ? wb_movl_res
                            : wb_is_movh ? wb_movh_res
                            : wb_is_ld ? wb_ld_res
                            : 1;


    wire wb_reg_should = wb_dest != 0 && (wb_is_sub || wb_is_add || wb_is_mul || wb_is_movl || wb_is_movh || wb_is_ld);
    wire [3:0] wb_reg_addr = wb_dest;
    wire [15:0] wb_reg_data = wb_is_sub ? wb_sub_res
                    : wb_is_add ? wb_add_res
                    : wb_is_mul ? wb_mul_res
                    : wb_is_movl ? wb_movl_res
                    : wb_is_movh ? wb_movh_res
                    : wb_is_ld ? wb_ld_res
                    : 1;


    assign regs_should_write = wb_V && wb_reg_should;
    assign regs_waddr = wb_V && wb_reg_should ? wb_reg_addr
                    : 0;
    assign regs_wdata = wb_V && wb_reg_should ? wb_reg_data
                    : 0;



    wire [15:0] wb_mem_reg1 = wb_src_hi == 0 ? 0 : wb_regs_rdata0;
    wire [15:0] wb_mem_reg2 = wb_dest == 0 ? 0 : wb_regs_rdata1;

    wire wb_mem_write_isvalid = !(^mem_waddr === 1'bX);


    assign mem_should_write = wb_V && wb_is_st && wb_mem_write_isvalid || sm_V && sm_write;
    assign mem_waddr = wb_V && wb_is_st ? wb_mem_reg1[15:1] % 32768


                    : sm_V && sm_write ? sm_waddr
                    : 0;
    assign mem_wdata = wb_V && wb_is_st ? wb_mem_data


                    : sm_V && sm_write ? sm_wdata
                    : 0;

    wire [15:8] wb_temp_mis_1 = (^wb_mem_rdata1[15:8] === 1'bX) ? 0 : wb_mem_rdata1[15:8];
    wire [15:8] wb_temp_mis_2 = (^wb_mem_rdata0[7:0] === 1'bX) ? 0 : wb_mem_rdata0[7:0];
    wire [15:0] wb_mem_data = wb_mis ? { wb_temp_mis_1, wb_mem_reg2[15:8] }
                            : wb_mem_reg2;

    wire wb_st_mis = wb_V && wb_is_st && wb_mem_write_isvalid && wb_mis;
    wire [15:1] wb_mem_addr_2 = (wb_mem_reg1[15:1] + 1) % 32768;
    wire [15:0] wb_mem_data_2 = { wb_mem_reg2[7:0], wb_temp_mis_2 };



    wire wb_jump_should = wb_V && wb_is_jump && wb_jmp_cond && wb_jump_to != load2_PC;
    wire [15:0] wb_jump_to = wb_dest == 0 ? 0 : wb_regs_rdata1;


    wire wb_jump_mispredict = wb_V && wb_is_jump && !wb_jmp_cond && load2_PC != wb_PC+2;
    wire [15:0] wb_jump_mis_to = wb_PC+2;




    wire wb_st_overwriting_mem = wb_V && mem_should_write && $signed(wb_mem_reg1) <= f0_PC && $signed(wb_mem_reg1) > wb_PC
                                || wb_V && wb_st_mis && $signed(wb_mem_addr_2) <= f0_PC && $signed(wb_mem_addr_2) > wb_PC;
    wire [15:0] wb_st_overwriting_mem_to = wb_PC + 2;


    wire wb_ld_flush_condition = wb_V && wb_is_st && wb_mis;
    wire [15:0] wb_ld_flush_condition_to = wb_PC + 2;


    wire wb_flush_should = wb_V && (wb_jump_should || wb_st_overwriting_mem || wb_ld_flush_condition || load2_is_screwed || wb_jump_mispredict);
    wire [15:0] wb_flush_to = wb_jump_should ? wb_jump_to
                            : wb_st_overwriting_mem ? wb_st_overwriting_mem_to
                            : wb_ld_flush_condition ? wb_ld_flush_condition_to
                            : wb_jump_mispredict ? wb_jump_mis_to 
                            : load2_is_screwed ? wb_PC + 2
                            : 1;

    always @(posedge clk) begin
        wb_V <= load2_V;
        wb_PC <= load2_PC;

        sm_write <= wb_st_mis;
        sm_waddr <= wb_mem_addr_2;
        sm_wdata <= wb_mem_data_2;
    
        if (halt != 1 && wb_V) begin
            if (wb_print_should)
                $write("%c", wb_print_this);

            if (wb_flush_should) begin
                wb_V <= 0;
                load2_V <= 0;
                load_V <= 0;
                dec_V <= 0;
                f1_V <= 0;
            end

            if (wb_jump_should) begin
                branch_cache[branch_c] <= {1'b1, wb_PC, wb_jump_to};
                branch_c <= branch_c + 1;
            end
        end 

        if (wb_V && wb_should_halt)
            halt <= 1;
    end








    
    reg sm_V = 0;
    reg sm_write;
    reg [15:1] sm_waddr;
    reg [15:0] sm_wdata;

    wire sm_did_it = sm_V && sm_write && !(wb_V && wb_is_st) && !(sm2_V && sm2_write) && !(sm3_write && sm3_V);

    always @(posedge clk) begin
        sm_V <= wb_V;

        sm2_write <= sm_did_it ? 0 : sm_write;
        sm2_waddr <= sm_waddr;
        sm2_wdata <= sm_wdata;
    end





    reg sm2_V = 0;
    reg sm2_write;
    reg [15:1] sm2_waddr;
    reg [15:0] sm2_wdata;

    wire sm2_did_it = sm_V && sm2_write && !(wb_V && wb_is_st) && !sm3_write;

    always @(posedge clk) begin
        sm2_V <= sm_V;

        sm3_write <= sm2_did_it ? 0 : sm2_write;
        sm3_waddr <= sm2_waddr;
        sm3_wdata <= sm2_wdata;
    end





    reg sm3_V = 0;
    reg sm3_write;
    reg [15:1] sm3_waddr;
    reg [15:0] sm3_wdata;

    wire sm3_did_it = sm3_V && sm3_write && !(wb_V && wb_is_st);

    always @(posedge clk) begin
        sm3_V <= sm2_V;
    end

endmodule
module main();





    initial begin
        $dumpfile("cpu.vcd");
        $dumpvars(0,main);
    end


    wire clk;
    clock c0(clk);

    reg halt = 0;

    counter ctr(halt,clk);




    


   reg branch_predictor_ON = 1;


    reg [32:0] branch_cache[7:0];
    reg [2:0] branch_c = 0;
    
    function automatic [16:0] branch_prediction;
        input [15:0] current_PC;
        begin
            if (!branch_predictor_ON)
                branch_prediction = 0;
            else if (branch_cache[branch_c][32] && branch_cache[branch_c][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c][15:0]};
            else if (branch_cache[branch_c-1][32] && branch_cache[branch_c-1][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-1][15:0]};
            else if (branch_cache[branch_c-2][32] && branch_cache[branch_c-2][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-2][15:0]};
            else if (branch_cache[branch_c-3][32] && branch_cache[branch_c-3][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-3][15:0]};
            else if (branch_cache[branch_c-4][32] && branch_cache[branch_c-4][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-4][15:0]};
            else if (branch_cache[branch_c-5][32] && branch_cache[branch_c-5][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-5][15:0]};
            else if (branch_cache[branch_c-6][32] && branch_cache[branch_c-6][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-6][15:0]};
            else if (branch_cache[branch_c-7][32] && branch_cache[branch_c-7][31:16] == current_PC)
                branch_prediction = {1'b1, branch_cache[branch_c-7][15:0]};
            else
                branch_prediction = 0;
        end
    endfunction





    wire [15:1] mem_raddr0;
    wire [15:0] mem_rdata0;

    wire [15:1] mem_raddr1;
    wire [15:0] mem_rdata1;

    wire mem_should_write;
    wire [15:1] mem_waddr;
    wire [15:0] mem_wdata;

    mem mem(clk,
         mem_raddr0,mem_rdata0,
         mem_raddr1,mem_rdata1,
         mem_should_write,mem_waddr,mem_wdata);
    




    wire [3:0] regs_raddr0;
    wire [15:0] regs_rdata0;

    wire [3:0] regs_raddr1;
    wire [15:0] regs_rdata1;

    wire regs_should_write;
    wire [3:0] regs_waddr;
    wire [15:0] regs_wdata;

    
    regs regs(clk,
        regs_raddr0,regs_rdata0,
        regs_raddr1,regs_rdata1,
        regs_should_write,regs_waddr,regs_wdata);






    wire [16:0] f0_predict = branch_prediction(f0_PC);
    wire f0_V = load_mis ? 0 : 1; 
    reg [15:0] f0_PC = 16'h0000;
    wire [15:0] f0_nextpc = wb_flush_should ? wb_flush_to
                        : load_mis ? f0_PC 
                        : f0_mis ? f0_PC+1
                        : f0_predict[16] ? f0_predict[15:0]
                        : f0_PC + 2;

    wire f0_mis = f0_V && f0_PC[0];


    assign mem_raddr0 = load_mis ? (load_mem_regs_rdata0[15:1] + 1) % 32768
                    : f1_mis ? f1_PC[15:1]+1 % 32768
                    : f0_PC[15:1] % 32768;

    always @(posedge clk) begin
        f0_PC <= f0_nextpc;
        f1_mis <= f0_mis;
    end




    
    reg f1_V = 0;
    reg f1_mis = 0;
    reg [15:0] f1_PC;

    always @(posedge clk) begin
        if (wb_flush_should)
            f1_V <= 0;
        else 
            f1_V <= f0_mis ? 0 : f0_V;

        f1_PC <= f0_PC;
        dec_mis <= f1_mis;
    end




    
    reg dec_V = 0;
    reg dec_mis;
    reg [15:0] dec_PC;
    wire [15:0] dec_ins = load_mis_prev ? {load_ins[7:0], mem_rdata0[15:8]} : mem_rdata0;



    wire [3:0] dec_opcode = dec_ins[15:12];
    wire [3:0] dec_dest = dec_ins[3:0];

    wire [7:0] dec_src = dec_ins[11:4];
    wire [3:0] dec_src_hi = dec_ins[11:8];
    wire [3:0] dec_src_lo = dec_ins[7:4];

    wire dec_is_sub = dec_opcode == 0;
    wire dec_is_mul = dec_opcode == 1;
    wire dec_is_add = dec_opcode == 2;
    wire dec_is_movl = dec_opcode == 8;
    wire dec_is_movh = dec_opcode == 9;
    wire dec_is_jump = dec_opcode == 14 && (dec_src_lo >= 0 && dec_src_lo <= 3);
    wire dec_is_ld = dec_opcode == 15 && dec_src_lo == 0;
    wire dec_is_st = dec_opcode == 15 && dec_src_lo == 1;


    assign regs_raddr0 =  dec_is_sub || dec_is_add || dec_is_mul ? dec_src_hi
                        : dec_is_movh ? dec_dest 
                        : dec_is_jump ? dec_src_hi
                        : dec_is_ld ? dec_src_hi
                        : dec_is_st ? dec_src_hi
                        : 0;

    assign regs_raddr1 =  dec_is_sub || dec_is_add || dec_is_mul ? dec_src_lo
                        : dec_is_movh ? 0
                        : dec_is_jump ? dec_dest
                        : dec_is_ld ? 0
                        : dec_is_st ? dec_dest
                        : 0;

    always @(posedge clk) begin
        if (wb_flush_should)
            dec_V <= 0;
        else
            dec_V <= f1_V;

        dec_PC <= f1_PC;


        load_ins <= dec_ins;
        load_mis_prev <= dec_mis;
    end





    reg load_V = 0;
    reg [15:0] load_PC;
    reg [15:0] load_ins;
    reg load_mis_prev;

    wire [15:0] load_regs_rdata0 = regs_rdata0;
    wire [15:0] load_regs_rdata1 = regs_rdata1;

    wire [15:0] load_regs_fwd_rdata0 = load2_V && load2_reg_should && load2_reg_addr == load_regs0_needed ? load2_reg_data
                        : wb_V && wb_reg_should && wb_reg_addr == load_regs0_needed ? wb_reg_data 
                        : load_regs_rdata0;
    wire [15:0] load_regs_fwd_rdata1 = load2_V && load2_reg_should && load2_reg_addr == load_regs1_needed ? load2_reg_data
                        : wb_V && wb_reg_should && wb_reg_addr == load_regs1_needed ? wb_reg_data 
                        : load_regs_rdata1;

    wire [3:0] load_regs0_needed =  load_is_sub || load_is_add || load_is_mul ? load_src_hi
                        : load_is_movh ? load_dest 
                        : load_is_jump ? load_src_hi
                        : load_is_ld ? load_src_hi
                        : load_is_st ? load_src_hi
                        : 0;

    wire[3:0] load_regs1_needed =  load_is_sub || load_is_add || load_is_mul ? load_src_lo
                        : load_is_movh ? 0
                        : load_is_jump ? load_dest
                        : load_is_ld ? 0
                        : load_is_st ? load_dest
                        : 0;

    wire [3:0] load_opcode = load_ins[15:12];
    wire [3:0] load_dest = load_ins[3:0];

    wire [7:0] load_src = load_ins[11:4];
    wire [3:0] load_src_hi = load_ins[11:8];
    wire [3:0] load_src_lo = load_ins[7:4];

    wire load_is_sub = load_opcode == 0;
    wire load_is_mul = load_opcode == 1;
    wire load_is_add = load_opcode == 2;
    wire load_is_movl = load_opcode == 8;
    wire load_is_movh = load_opcode == 9;
    wire load_is_jump = load_opcode == 14 && (load_src_lo >= 0 && load_src_lo <= 3);
    wire load_is_ld = load_opcode == 15 && load_src_lo == 0;
    wire load_is_st = load_opcode == 15 && load_src_lo == 1;




    wire load_is_screwed = load_V && load_is_ld && load2_V && load2_is_ld && load2_reg_should && load2_reg_addr == load_regs0_needed && load2_is_ld ||
                        load_V && load_is_ld && load2_V && load2_is_ld && load2_reg_should && load2_reg_addr == load_regs1_needed && load2_is_ld;



    wire [15:0] load_mem_regs_rdata0 = load_src_hi == 0 ? 0 : load_regs_fwd_rdata0;
    wire load_mis = load_V && (load_is_ld || load_is_st) && !(load_mem_regs_rdata0[0]===1'bX) && load_mem_regs_rdata0[0];

    assign mem_raddr1 = load_mem_regs_rdata0[15:1] % 32768;

    always @(posedge clk) begin
        if (wb_flush_should) 
            load_V <= 0;
        else
            load_V <= dec_V;
        load_PC <= dec_PC;

        load2_ins <= load_ins;
        load2_regs_rdata0 <= load_regs_fwd_rdata0;
        load2_regs_rdata1 <= load_regs_fwd_rdata1;
        load2_mis <= load_mis;

        load2_past_wen0 <= mem_should_write;
        load2_past_waddr0 <= mem_waddr;
        load2_past_wdata0 <= mem_wdata;

        load2_is_screwed <= load_is_screwed;
    end 






    reg load2_V = 0;
    reg [15:0] load2_PC;
    reg [15:0] load2_ins;
    reg load2_mis = 0;
    reg load2_is_screwed = 0;

    reg [15:0] load2_regs_rdata0;
    reg [15:0] load2_regs_rdata1;

    reg load2_past_wen0;
    reg [15:1] load2_past_waddr0;
    reg [15:0] load2_past_wdata0;

    wire [15:0] load2_regs_fwd_rdata0 = wb_V && wb_reg_should && wb_reg_addr == load2_regs0_needed ? wb_reg_data : load2_regs_rdata0;
    wire [15:0] load2_regs_fwd_rdata1 = wb_V && wb_reg_should && wb_reg_addr == load2_regs1_needed ? wb_reg_data : load2_regs_rdata1;

    wire [3:0] load2_regs0_needed =  load2_is_sub || load2_is_add || load2_is_mul ? load2_src_hi
                        : load2_is_movh ? load2_dest 
                        : load2_is_jump ? load2_src_hi
                        : load2_is_ld ? load2_src_hi
                        : load2_is_st ? load2_src_hi
                        : 0;

    wire[3:0] load2_regs1_needed =  load2_is_sub || load2_is_add || load2_is_mul ? load2_src_lo
                        : load2_is_movh ? 0
                        : load2_is_jump ? load2_dest
                        : load2_is_ld ? 0
                        : load2_is_st ? load2_dest
                        : 0;


    wire [3:0] load2_opcode = load2_ins[15:12];
    wire [3:0] load2_dest = load2_ins[3:0];

    wire [7:0] load2_src = load2_ins[11:4];
    wire [3:0] load2_src_hi = load2_ins[11:8];
    wire [3:0] load2_src_lo = load2_ins[7:4];

    wire load2_is_sub = load2_opcode == 0;
    wire load2_is_mul = load2_opcode == 1;
    wire load2_is_add = load2_opcode == 2;
    wire load2_is_movl = load2_opcode == 8;
    wire load2_is_movh = load2_opcode == 9;
    wire load2_is_jump = load2_opcode == 14 && (load2_src_lo >= 0 && load2_src_lo <= 3);
    wire load2_is_ld = load2_opcode == 15 && load2_src_lo == 0;
    wire load2_is_st = load2_opcode == 15 && load2_src_lo == 1;


    wire [15:0] load2_sub_tmp1 = load2_src_hi == 0 ? 0 : load2_regs_fwd_rdata0;
    wire [15:0] load2_sub_tmp2 = load2_src_lo == 0 ? 0 : load2_regs_fwd_rdata1;
    wire [15:0] load2_sub_res = load2_sub_tmp1 - load2_sub_tmp2;

    wire [15:0] load2_movl_res = { {8{load2_src[7]}}, load2_src[7:0] };
    wire [15:0] load2_movh_tmp = load2_dest == 0 ? 0 

                            : load2_regs_fwd_rdata0;
    wire [15:0] load2_movh_res = { load2_src[7:0], load2_movh_tmp[7:0] };

    wire load2_reg_should = load2_V && load2_dest != 0 && (load2_is_sub || load2_is_add || load2_is_mul || load2_is_movl || load2_is_movh || load2_is_ld);
    wire [3:0] load2_reg_addr = load2_dest;
    wire [15:0] load2_reg_data = load2_is_sub || load2_is_add || load2_is_mul ? load2_sub_res
                    : load2_is_movl ? load2_movl_res
                    : load2_is_movh ? load2_movh_res
                    : load2_is_ld ? 0
                    : 1;


    always @(posedge clk) begin
        if (wb_flush_should)
            load2_V <= 0;
        else
            load2_V <= load_V;
        load2_PC <= load_PC;

        wb_ins <= load2_ins;
        wb_regs_rdata0 <= load2_regs_fwd_rdata0;
        wb_regs_rdata1 <= load2_regs_fwd_rdata1;
        wb_mis <= load2_mis;

        wb_past_wen0 <= load2_past_wen0;
        wb_past_waddr0 <= load2_past_waddr0;
        wb_past_wdata0 <= load2_past_wdata0;

        wb_past_wen1 <= mem_should_write;
        wb_past_waddr1 <= mem_waddr;


        wb_past_wdata1 <= mem_wdata;

        wb_is_screwed <= load2_is_screwed;

    end






    reg wb_V = 0;
    reg [15:0] wb_PC;
    reg [15:0] wb_ins;
    reg wb_mis;
    reg wb_is_screwed;

    reg [15:0] wb_regs_rdata0;
    reg [15:0] wb_regs_rdata1;


    reg wb_past_wen0 = 0;
    reg [15:1] wb_past_waddr0;
    reg [15:0] wb_past_wdata0;
    reg wb_past_wen1 = 0;
    reg [15:1] wb_past_waddr1;
    reg [15:0] wb_past_wdata1;


    wire [3:0] wb_opcode = wb_ins[15:12];
    wire [3:0] wb_dest = wb_ins[3:0];

    wire [7:0] wb_src = wb_ins[11:4];
    wire [3:0] wb_src_hi = wb_ins[11:8];
    wire [3:0] wb_src_lo = wb_ins[7:4];

    wire wb_is_sub = wb_opcode == 0;
    wire wb_is_mul = wb_opcode == 1;
    wire wb_is_add = wb_opcode == 2;
    wire wb_is_movl = wb_opcode == 8;
    wire wb_is_movh = wb_opcode == 9;
    wire wb_is_jump = wb_opcode == 14 && (wb_src_lo >= 0 && wb_src_lo <= 3);
    wire wb_is_ld = wb_opcode == 15 && wb_src_lo == 0;
    wire wb_is_st = wb_opcode == 15 && wb_src_lo == 1;

    wire wb_should_halt = !(wb_is_sub || wb_is_add || wb_is_mul || wb_is_movl || wb_is_movh || wb_is_jump || wb_is_ld || wb_is_st);


    wire [15:0] wb_sub_tmp1 = wb_src_hi == 0 ? 0 : wb_regs_rdata0;
    wire [15:0] wb_sub_tmp2 = wb_src_lo == 0 ? 0 : wb_regs_rdata1;
    wire [15:0] wb_sub_res = wb_sub_tmp1 - wb_sub_tmp2;
    wire [15:0] wb_mul_res = wb_sub_tmp1 * wb_sub_tmp2;
    wire [15:0] wb_add_res = wb_sub_tmp1 + wb_sub_tmp2;

    wire [15:0] wb_movl_res = { {8{wb_src[7]}}, wb_src[7:0] };
    wire [15:0] wb_movh_tmp = wb_dest == 0 ? 0 

                            : wb_regs_rdata0;
    wire [15:0] wb_movh_res = { wb_src[7:0], wb_movh_tmp[7:0] };
    wire [15:0] wb_jmp_fixed_data = wb_src_hi == 0 ? 0 : wb_regs_rdata0;
    wire wb_jmp_cond = wb_src_lo == 0 ? $signed(wb_jmp_fixed_data)==0 
                : wb_src_lo == 1 ? $signed(wb_jmp_fixed_data)!=0
                : wb_src_lo == 2 ? $signed(wb_jmp_fixed_data) < 0
                : wb_src_lo == 3 ? $signed(wb_jmp_fixed_data) >= 0
                : 1;

    wire [15:0] wb_mem_rdata1 = sm_V && sm_write && mem_waddr == (wb_regs_rdata0[15:1] % 32678) ? mem_wdata
                            : wb_past_wen1 && wb_past_waddr1 == (wb_regs_rdata0[15:1] % 32768) ? wb_past_wdata1
                            : wb_past_wen0 && wb_past_waddr0 == (wb_regs_rdata0[15:1] % 32768) ? wb_past_wdata0
                            : mem_rdata1;

    wire [15:0] wb_mem_rdata0 = sm_V && sm_write && mem_waddr == ((wb_regs_rdata0[15:1]+1) % 32768) ? mem_wdata
                            : wb_past_wen1 && wb_past_waddr1 == ((wb_regs_rdata0[15:1]+1) % 32768) ? wb_past_wdata1
                            : wb_past_wen0 && wb_past_waddr0 == ((wb_regs_rdata0[15:1]+1) % 32768) ? wb_past_wdata0
                            : mem_rdata0;

    wire [15:0] wb_ld_res = wb_mis ? {wb_mem_rdata1[7:0], wb_mem_rdata0[15:8]}
                            : wb_mem_rdata1;


    wire wb_print_should = wb_dest == 0 && (wb_is_sub || wb_is_add || wb_is_mul || wb_is_movl || wb_is_movh || wb_is_ld);
    wire [15:0] wb_print_this = wb_is_sub ? wb_sub_res
                            : wb_is_add ? wb_add_res
                            : wb_is_mul ? wb_mul_res
                            : wb_is_movl ? wb_movl_res
                            : wb_is_movh ? wb_movh_res
                            : wb_is_ld ? wb_ld_res
                            : 1;


    wire wb_reg_should = wb_dest != 0 && (wb_is_sub || wb_is_add || wb_is_mul || wb_is_movl || wb_is_movh || wb_is_ld);
    wire [3:0] wb_reg_addr = wb_dest;
    wire [15:0] wb_reg_data = wb_is_sub ? wb_sub_res
                    : wb_is_add ? wb_add_res
                    : wb_is_mul ? wb_mul_res
                    : wb_is_movl ? wb_movl_res
                    : wb_is_movh ? wb_movh_res
                    : wb_is_ld ? wb_ld_res
                    : 1;


    assign regs_should_write = wb_V && wb_reg_should;
    assign regs_waddr = wb_V && wb_reg_should ? wb_reg_addr
                    : 0;
    assign regs_wdata = wb_V && wb_reg_should ? wb_reg_data
                    : 0;



    wire [15:0] wb_mem_reg1 = wb_src_hi == 0 ? 0 : wb_regs_rdata0;
    wire [15:0] wb_mem_reg2 = wb_dest == 0 ? 0 : wb_regs_rdata1;

    wire wb_mem_write_isvalid = !(^mem_waddr === 1'bX);


    assign mem_should_write = wb_V && wb_is_st && wb_mem_write_isvalid || sm_V && sm_write;
    assign mem_waddr = wb_V && wb_is_st ? wb_mem_reg1[15:1] % 32768


                    : sm_V && sm_write ? sm_waddr
                    : 0;
    assign mem_wdata = wb_V && wb_is_st ? wb_mem_data


                    : sm_V && sm_write ? sm_wdata
                    : 0;

    wire [15:8] wb_temp_mis_1 = (^wb_mem_rdata1[15:8] === 1'bX) ? 0 : wb_mem_rdata1[15:8];
    wire [15:8] wb_temp_mis_2 = (^wb_mem_rdata0[7:0] === 1'bX) ? 0 : wb_mem_rdata0[7:0];
    wire [15:0] wb_mem_data = wb_mis ? { wb_temp_mis_1, wb_mem_reg2[15:8] }
                            : wb_mem_reg2;

    wire wb_st_mis = wb_V && wb_is_st && wb_mem_write_isvalid && wb_mis;
    wire [15:1] wb_mem_addr_2 = (wb_mem_reg1[15:1] + 1) % 32768;
    wire [15:0] wb_mem_data_2 = { wb_mem_reg2[7:0], wb_temp_mis_2 };



    wire wb_jump_should = wb_V && wb_is_jump && wb_jmp_cond && wb_jump_to != load2_PC;
    wire [15:0] wb_jump_to = wb_dest == 0 ? 0 : wb_regs_rdata1;


    wire wb_jump_mispredict = wb_V && wb_is_jump && !wb_jmp_cond && load2_PC != wb_PC+2;
    wire [15:0] wb_jump_mis_to = wb_PC+2;




    wire wb_st_overwriting_mem = wb_V && mem_should_write && $signed(wb_mem_reg1) <= f0_PC && $signed(wb_mem_reg1) > wb_PC
                                || wb_V && wb_st_mis && $signed(wb_mem_addr_2) <= f0_PC && $signed(wb_mem_addr_2) > wb_PC;
    wire [15:0] wb_st_overwriting_mem_to = wb_PC + 2;


    wire wb_ld_flush_condition = wb_V && wb_is_st && wb_mis;
    wire [15:0] wb_ld_flush_condition_to = wb_PC + 2;


    wire wb_flush_should = wb_V && (wb_jump_should || wb_st_overwriting_mem || wb_ld_flush_condition || load2_is_screwed || wb_jump_mispredict);
    wire [15:0] wb_flush_to = wb_jump_should ? wb_jump_to
                            : wb_st_overwriting_mem ? wb_st_overwriting_mem_to
                            : wb_ld_flush_condition ? wb_ld_flush_condition_to
                            : wb_jump_mispredict ? wb_jump_mis_to 
                            : load2_is_screwed ? wb_PC + 2
                            : 1;

    always @(posedge clk) begin
        wb_V <= load2_V;
        wb_PC <= load2_PC;

        sm_write <= wb_st_mis;
        sm_waddr <= wb_mem_addr_2;
        sm_wdata <= wb_mem_data_2;
    
        if (halt != 1 && wb_V) begin
            if (wb_print_should)
                $write("%c", wb_print_this);

            if (wb_flush_should) begin
                wb_V <= 0;
                load2_V <= 0;
                load_V <= 0;
                dec_V <= 0;
                f1_V <= 0;
            end

            if (wb_jump_should) begin
                branch_cache[branch_c] <= {1'b1, wb_PC, wb_jump_to};
                branch_c <= branch_c + 1;
            end
        end 

        if (wb_V && wb_should_halt)
            halt <= 1;
    end








    
    reg sm_V = 0;
    reg sm_write;
    reg [15:1] sm_waddr;
    reg [15:0] sm_wdata;

    wire sm_did_it = sm_V && sm_write && !(wb_V && wb_is_st) && !(sm2_V && sm2_write) && !(sm3_write && sm3_V);

    always @(posedge clk) begin
        sm_V <= wb_V;

        sm2_write <= sm_did_it ? 0 : sm_write;
        sm2_waddr <= sm_waddr;
        sm2_wdata <= sm_wdata;
    end





    reg sm2_V = 0;
    reg sm2_write;
    reg [15:1] sm2_waddr;
    reg [15:0] sm2_wdata;

    wire sm2_did_it = sm_V && sm2_write && !(wb_V && wb_is_st) && !sm3_write;

    always @(posedge clk) begin
        sm2_V <= sm_V;

        sm3_write <= sm2_did_it ? 0 : sm2_write;
        sm3_waddr <= sm2_waddr;
        sm3_wdata <= sm2_wdata;
    end





    reg sm3_V = 0;
    reg sm3_write;
    reg [15:1] sm3_waddr;
    reg [15:0] sm3_wdata;

    wire sm3_did_it = sm3_V && sm3_write && !(wb_V && wb_is_st);

    always @(posedge clk) begin
        sm3_V <= sm2_V;
    end

endmodule