module sha256 (clk_i, rst_i, text_i, text_o, cmd_i, cmd_w_i, cmd_o);

        input           clk_i;  // global clock input
        input           rst_i;  // global reset input , active high
        
        input   [9:0]  text_i; // text input 32bit
        output  [9:0]  text_o; // text output 32bit
        
        input   [2:0]   cmd_i;  // command input
        input           cmd_w_i;// command input write enable
        output  [3:0]   cmd_o;  // command output(status)

        /*
                cmd
                Busy Round W R

                bit3 bit2  bit1 bit0
                Busy Round W    R
                
                Busy:
                0       idle
                1       busy
                
                Round:
                0       first round
                1       internal round
                
                W:
                0       No-op
                1       write data
                
                R:
                0       No-op
                1       read data
                        
        */
        

        reg     [3:0]   cmd;
        wire    [3:0]   cmd_o;
        
        reg     [9:0]  text_o;
        
        reg     [6:0]   round;
        wire    [6:0]   round_plus_1;
        
        reg     [2:0]   read_counter;
        
        reg     [31:0]  H0,H1,H2,H3,H4,H5,H6,H7;
        reg     [31:0]  W0,W1,W2,W3,W4,W5,W6,W7,W8,W9,W10,W11,W12,W13,W14;
        reg     [31:0]  Wt,Kt;
        reg     [31:0]  A,B,C,D,E,F,G,H;

        reg             busy;
        
        assign cmd_o = cmd;
        always @ (posedge clk_i)
        begin
                if (rst_i)
                        cmd <= 'b0;
                else
                if (cmd_w_i)
                        cmd[2:0] <= cmd_i[2:0];         // busy bit can't write
                else
                begin
                        cmd[3] <= busy;                 // update busy bit
                        if (~busy)
                                cmd[1:0] <= 2'b00;      // hardware auto clean R/W bits
                end
        end
        
        wire [31:0] f1_EFG_32,f2_ABC_32,f3_A_32,f4_E_32,f5_W1_32,f6_W14_32,T1_32,T2_32;
        wire [31:0] next_Wt,next_E,next_A;
        wire [255:0] SHA256_result;
        
        assign f1_EFG_32 = (E & F);

        assign f2_ABC_32 = (A & B);
        
        assign f3_A_32 = {A[1:0],A[31:2]} ^ {A[12:0],A[31:13]} ^ {A[21:0],A[31:22]};
        
        assign f4_E_32 = {E[5:0],E[31:6]} ^ {E[10:0],E[31:11]} ^ {E[24:0],E[31:25]};
        
        assign f5_W1_32 = 0;// {W1[6:0],W1[31:7]} ^ {W1[17:0],W1[31:18]} ^ {3'b000,W1[31:3]};
        
        assign f6_W14_32 = 0; //{W14[16:0],W14[31:17]} ^ {10'b00_0000_0000,W14[31:10]};
        
        
        assign T1_32 = f4_E_32 + f1_EFG_32 + Kt + Wt;
        
        assign T2_32 = f3_A_32 + f2_ABC_32;
        
        assign next_Wt = f6_W14_32 + f5_W1_32;
        assign next_E = D[31:0] + T1_32;
        assign next_A = T1_32 + T2_32;
        

        assign SHA256_result = {A,B,C,D,E,F,G,H};

        assign round_plus_1 = round + 1;
        
        //------------------------------------------------------------------    
        // SHA round
        //------------------------------------------------------------------
        always @(posedge clk_i)
        begin
                if (rst_i)
                begin
                        round <= 'd0;
                        busy <= 'b0;

                        W0  <= 'b0;
                        W1  <= 'b0;
                        W2  <= 'b0;
                        W3  <= 'b0;
                        W4  <= 'b0;
                        W5  <= 'b0;
                        W6  <= 'b0;
                        W7  <= 'b0;
                        W8  <= 'b0;
                        W9  <= 'b0;
                        W10 <= 'b0;
                        W11 <= 'b0;
                        W12 <= 'b0;
                        W13 <= 'b0;
                        W14 <= 'b0;
                        Wt  <= 'b0;
                        
                        A <= 'b0;
                        B <= 'b0;
                        C <= 'b0;
                        D <= 'b0;
                        E <= 'b0;
                        F <= 'b0;
                        G <= 'b0;
                        H <= 'b0;

                        H0 <= 'b0;
                        H1 <= 'b0;
                        H2 <= 'b0;
                        H3 <= 'b0;
                        H4 <= 'b0;
                        H5 <= 'b0;
                        H6 <= 'b0;
                        H7 <= 'b0;
                end
                else
                begin
                        case (round)
                        
                        'd0:
                                begin
                                        if (cmd[1])
                                        begin
                                                W0 <= text_i;
                                                Wt <= text_i;
                                                busy <= 'b1;
                                                round <= round_plus_1;
                                                
                                                case (cmd[2])
                                                        1'b0:   // sha-256 first message
                                                                begin
                                                                        A <= `SHA256_H0;
                                                                        B <= `SHA256_H1;
                                                                        C <= `SHA256_H2;
                                                                        D <= `SHA256_H3;
                                                                        E <= `SHA256_H4;
                                                                        F <= `SHA256_H5;
                                                                        G <= `SHA256_H6;
                                                                        H <= `SHA256_H7;

                                                                        H0 <= `SHA256_H0;
                                                                        H1 <= `SHA256_H1;
                                                                        H2 <= `SHA256_H2;
                                                                        H3 <= `SHA256_H3;
                                                                        H4 <= `SHA256_H4;
                                                                        H5 <= `SHA256_H5;
                                                                        H6 <= `SHA256_H6;
                                                                        H7 <= `SHA256_H7;
                                                                end
                                                        1'b1:   // sha-256 internal message
                                                                begin
                                                                        H0 <= A;
                                                                        H1 <= B;
                                                                        H2 <= C;
                                                                        H3 <= D;
                                                                        H4 <= E;
                                                                        H5 <= F;
                                                                        H6 <= G;
                                                                        H7 <= H;
                                                                end
                                                endcase
                                        end
                                        else
                                        begin   // IDLE
                                                round <= 'd0;
                                        end
                                end
                        'd1:
                                begin
                                        W1 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd2:
                                begin
                                        W2 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd3:
                                begin
                                        W3 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd4:
                                begin
                                        W4 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd5:
                                begin
                                        W5 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd6:
                                begin
                                        W6 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd7:
                                begin
                                        W7 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd8:
                                begin
                                        W8 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd9:
                                begin
                                        W9 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd10:
                                begin
                                        W10 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd11:
                                begin
                                        W11 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd12:
                                begin
                                        W12 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd13:
                                begin
                                        W13 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd14:
                                begin
                                        W14 <= text_i;
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd15:
                                begin
                                        Wt <= text_i;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd16,
                        'd17,
                        'd18,
                        'd19,
                        'd20,
                        'd21,
                        'd22,
                        'd23,
                        'd24,
                        'd25,
                        'd26,
                        'd27,
                        'd28,
                        'd29,
                        'd30,
                        'd31,
                        'd32,
                        'd33,
                        'd34,
                        'd35,
                        'd36,
                        'd37,
                        'd38,
                        'd39,
                        'd40,
                        'd41,
                        'd42,
                        'd43,
                        'd44,
                        'd45,
                        'd46,
                        'd47,
                        'd48,
                        'd49,
                        'd50,
                        'd51,
                        'd52,
                        'd53,
                        'd54,
                        'd55,
                        'd56,
                        'd57,
                        'd58,
                        'd59,
                        'd60,
                        'd61,
                        'd62,
                        'd63:
                                begin
                                        W0  <= W1;
                                        W1  <= W2;
                                        W2  <= W3;
                                        W3  <= W4;
                                        W4  <= W5;
                                        W5  <= W6;
                                        W6  <= W7;
                                        W7  <= W8;
                                        W8  <= W9;
                                        W9  <= W10;
                                        W10 <= W11;
                                        W11 <= W12;
                                        W12 <= W13;
                                        W13 <= W14;
                                        W14 <= Wt;
                                        Wt  <= next_Wt;
                                        
                                        H <= G;
                                        G <= F;
                                        F <= E;
                                        E <= next_E;
                                        D <= C;
                                        C <= B;
                                        B <= A;
                                        A <= next_A;
                                                
                                        round <= round_plus_1;
                                end
                        'd64:
                                begin
                                        A <= next_A + H0;
                                        B <= A + H1;
                                        C <= B + H2;
                                        D <= C + H3;
                                        E <= next_E + H4;
                                        F <= E + H5;
                                        G <= F + H6;
                                        H <= G + H7;
                                        round <= 'd0;
                                        busy <= 'b0;
                                end
                        default:
                                begin
                                        round <= 'd0;
                                        busy <= 'b0;
                                end
                        endcase
                end     
        end 
        
        
        //------------------------------------------------------------------    
        // Kt generator
        //------------------------------------------------------------------    
        always @ (posedge clk_i)
        begin
                if (rst_i)
                begin
                        Kt <= 'b0;
                end
                else
                begin
                        case (round)
                                'd00:   Kt <= `K00;
                                'd01:   Kt <= `K01;
                                'd02:   Kt <= `K02;
                                'd03:   Kt <= `K03;
                                'd04:   Kt <= `K04;
                                'd05:   Kt <= `K05;
                                'd06:   Kt <= `K06;
                                'd07:   Kt <= `K07;
                                'd08:   Kt <= `K08;
                                'd09:   Kt <= `K09;
                                'd10:   Kt <= `K10;
                                'd11:   Kt <= `K11;
                                'd12:   Kt <= `K12;
                                'd13:   Kt <= `K13;
                                'd14:   Kt <= `K14;
                                'd15:   Kt <= `K15;
                                'd16:   Kt <= `K16;
                                'd17:   Kt <= `K17;
                                'd18:   Kt <= `K18;
                                'd19:   Kt <= `K19;
                                'd20:   Kt <= `K20;
                                'd21:   Kt <= `K21;
                                'd22:   Kt <= `K22;
                                'd23:   Kt <= `K23;
                                'd24:   Kt <= `K24;
                                'd25:   Kt <= `K25;
                                'd26:   Kt <= `K26;
                                'd27:   Kt <= `K27;
                                'd28:   Kt <= `K28;
                                'd29:   Kt <= `K29;
                                'd30:   Kt <= `K30;
                                'd31:   Kt <= `K31;
                                'd32:   Kt <= `K32;
                                'd33:   Kt <= `K33;
                                'd34:   Kt <= `K34;
                                'd35:   Kt <= `K35;
                                'd36:   Kt <= `K36;
                                'd37:   Kt <= `K37;
                                'd38:   Kt <= `K38;
                                'd39:   Kt <= `K39;
                                'd40:   Kt <= `K40;
                                'd41:   Kt <= `K41;
                                'd42:   Kt <= `K42;
                                'd43:   Kt <= `K43;
                                'd44:   Kt <= `K44;
                                'd45:   Kt <= `K45;
                                'd46:   Kt <= `K46;
                                'd47:   Kt <= `K47;
                                'd48:   Kt <= `K48;
                                'd49:   Kt <= `K49;
                                'd50:   Kt <= `K50;
                                'd51:   Kt <= `K51;
                                'd52:   Kt <= `K52;
                                'd53:   Kt <= `K53;
                                'd54:   Kt <= `K54;
                                'd55:   Kt <= `K55;
                                'd56:   Kt <= `K56;
                                'd57:   Kt <= `K57;
                                'd58:   Kt <= `K58;
                                'd59:   Kt <= `K59;
                                'd60:   Kt <= `K60;
                                'd61:   Kt <= `K61;
                                'd62:   Kt <= `K62;
                                'd63:   Kt <= `K63;
                                default:Kt <= 'd0;
                        endcase
                end
        end

        //------------------------------------------------------------------    
        // read result 
        //------------------------------------------------------------------    
        always @ (posedge clk_i)
        begin
                if (rst_i)
                begin
                        text_o <= 'b0;
                        read_counter <= 'b0;
                end
                else
                begin
                        if (cmd[0])
                        begin
                                read_counter <= 'd7;    // sha-256      256/32=8
                        end
                        else
                        begin
                        if (~busy)
                        begin
                                case (read_counter)
                                        'd7:    text_o <= SHA256_result[8*32-1:7*32];
                                        'd6:    text_o <= SHA256_result[7*32-1:6*32];
                                        'd5:    text_o <= SHA256_result[6*32-1:5*32];
                                        'd4:    text_o <= SHA256_result[5*32-1:4*32];
                                        'd3:    text_o <= SHA256_result[4*32-1:3*32];
                                        'd2:    text_o <= SHA256_result[3*32-1:2*32];
                                        'd1:    text_o <= SHA256_result[2*32-1:1*32];
                                        'd0:    text_o <= SHA256_result[1*32-1:0*32];
                                        default:text_o <= 'b0;
                                endcase
                                if (|read_counter)
                                        read_counter <= read_counter - 'd1;
                        end
                        else
                        begin
                                text_o <= 'b0;
                        end
                        end
                end
        end
        
endmodule