module timer(
  paddr,
  pclk,
  penable,
  prdata,
  presetn,
  psel,
  pwdata,
  pwrite,
  timer_int
);

// &Ports; @24
input   [15:0]  paddr;                 
input           pclk;                  
input           penable;               
input           presetn;               
input           psel;                  
input   [31:0]  pwdata;
input           pwrite;
output  [31:0]  prdata;                
output  [3 :0]  timer_int;             

// &Regs; @25
reg     [31:0]  prdata;                
reg     [2 :0]  timer_1_control_reg;   
reg     [31:0]  timer_1_load_count;    
reg             timer_1_raw_int_status; 
reg     [2 :0]  timer_2_control_reg;   
reg     [31:0]  timer_2_load_count;    
reg             timer_2_raw_int_status; 
reg     [2 :0]  timer_3_control_reg;   
reg     [31:0]  timer_3_load_count;    
reg             timer_3_raw_int_status; 
reg     [2 :0]  timer_4_control_reg;   
reg     [31:0]  timer_4_load_count;    
reg             timer_4_raw_int_status; 

// &Wires; @26
wire    [15:0]  paddr;                 
wire            pclk;                  
wire            presetn;               
wire            psel;                  
wire    [31:0]  pwdata;                
wire            pwrite;  
wire    [31:0]  timer_1_current_value; 
wire            timer_1_int_clear;     
wire            timer_1_int_status;    
wire            timer_1_interrupt;     
wire    [31:0]  timer_2_current_value; 
wire            timer_2_int_clear;     
wire            timer_2_int_status;    
wire            timer_2_interrupt;     
wire    [31:0]  timer_3_current_value; 
wire            timer_3_int_clear;     
wire            timer_3_int_status;    
wire            timer_3_interrupt;     
wire    [31:0]  timer_4_current_value; 
wire            timer_4_int_clear;     
wire            timer_4_int_status;    
wire            timer_4_interrupt;     
wire    [3 :0]  timer_int;             
wire            timers_int_clear;      


// &Force("bus","paddr",15,0); @28
// &Force("bus","pwdata",31,0); @29
// &Force("input","penable"); @30
// &Force("output","prdata"); @31
// &Force("bus","prdata",31,0); @32
// &Force("output","timer_int"); @33
// &Force("bus","timer_int",3,0); @34

// &Instance("counter", "timer_1"); @36
counter  timer_1 (
  .interrupt              (timer_1_interrupt     ),
  .pclk                   (pclk                  ),
  .presetn                (presetn               ),
  .timer_current_value    (timer_1_current_value ),
  .timer_enable           (timer_1_control_reg[0]),
  .timer_load_count       (timer_1_load_count    ),
  .timer_mode             (timer_1_control_reg[1])
);

// &Connect(.pclk                    (pclk                     ), @37
//          .presetn                 (presetn                  ), @38
//          .timer_enable            (timer_1_control_reg[0]   ), @39
//          .timer_mode              (timer_1_control_reg[1]   ), @40
//          .timer_load_count        (timer_1_load_count       ), @41
//          .timer_current_value     (timer_1_current_value    ), @42
//          .interrupt               (timer_1_interrupt        ) @43
//          ); @44

// &Instance("counter", "timer_2"); @46
counter  timer_2 (
  .interrupt              (timer_2_interrupt     ),
  .pclk                   (pclk             ),
  .presetn                (presetn          ),
  .timer_current_value    (timer_2_current_value ),
  .timer_enable           (timer_2_control_reg[0]),
  .timer_load_count       (timer_2_load_count    ),
  .timer_mode             (timer_2_control_reg[1])
);

// &Connect(.pclk                    (pclk                     ), @47
//          .presetn                 (presetn                  ), @48
//          .timer_enable            (timer_2_control_reg[0]   ), @49
//          .timer_mode              (timer_2_control_reg[1]   ), @50
//          .timer_load_count        (timer_2_load_count       ), @51
//          .timer_current_value     (timer_2_current_value    ), @52
//          .interrupt               (timer_2_interrupt        ) @53
//          ); @54

// &Instance("counter", "timer_3"); @56
counter  timer_3 (
  .interrupt              (timer_3_interrupt     ),
  .pclk                   (pclk             ),
  .presetn                (presetn          ),
  .timer_current_value    (timer_3_current_value ),
  .timer_enable           (timer_3_control_reg[0]),
  .timer_load_count       (timer_3_load_count    ),
  .timer_mode             (timer_3_control_reg[1])
);

// &Connect(.pclk                    (pclk                     ), @57
//          .presetn                 (presetn                  ), @58
//          .timer_enable            (timer_3_control_reg[0]   ), @59
//          .timer_mode              (timer_3_control_reg[1]   ), @60
//          .timer_load_count        (timer_3_load_count       ), @61
//          .timer_current_value     (timer_3_current_value    ), @62
//          .interrupt               (timer_3_interrupt        ) @63
//          ); @64

// &Instance("counter", "timer_4"); @66
counter  timer_4 (
  .interrupt              (timer_4_interrupt     ),
  .pclk                   (pclk             ),
  .presetn                (presetn          ),
  .timer_current_value    (timer_4_current_value ),
  .timer_enable           (timer_4_control_reg[0]),
  .timer_load_count       (timer_4_load_count    ),
  .timer_mode             (timer_4_control_reg[1])
);

// &Connect(.pclk                    (pclk                     ), @67
//          .presetn                 (presetn                  ), @68
//          .timer_enable            (timer_4_control_reg[0]   ), @69
//          .timer_mode              (timer_4_control_reg[1]   ), @70
//          .timer_load_count        (timer_4_load_count       ), @71
//          .timer_current_value     (timer_4_current_value    ), @72
//          .interrupt               (timer_4_interrupt        ) @73
//          ); @74

//==========================================================
//                  APB PWDATA
//==========================================================
always @(posedge pclk or negedge presetn)
begin
  if(!presetn)
  begin
    timer_1_load_count <= 32'b0;
    timer_1_control_reg <= 3'b0;
    timer_2_load_count <= 32'b0;
    timer_2_control_reg <= 3'b0;
    timer_3_load_count <= 32'b0;
    timer_3_control_reg <= 3'b0;
    timer_4_load_count <= 32'b0;
    timer_4_control_reg <= 3'b0;
  end
  else
  begin
    if(psel && pwrite && penable)
    begin
      case(paddr[7:2])
        6'b000000:
        begin
          timer_1_load_count <= pwdata;
        end
        6'b000010:
        begin
          timer_1_control_reg <= pwdata[2:0];
        end
        6'b000101:
        begin
          timer_2_load_count <= pwdata;
        end
        6'b000111:
        begin
          timer_2_control_reg <= pwdata[2:0];
        end
        6'b001010:
        begin
          timer_3_load_count <= pwdata;
        end
        6'b001100:
        begin
          timer_3_control_reg <= pwdata[2:0];
        end
        6'b001111:
        begin
          timer_4_load_count <= pwdata;
        end
        6'b010001:
        begin
          timer_4_control_reg <= pwdata[2:0];
        end
      endcase
    end
  end
end
    
//==========================================================
//                  APB PRDATA
//==========================================================
always @(posedge pclk)
begin
  if(psel && !pwrite)
  begin
    if (!penable)
	begin
      case(paddr[7:2])
        6'b000000:
        begin
          prdata <= timer_1_load_count;
        end
        6'b000001:
        begin
          prdata <= timer_1_current_value;
        end
        6'b000010:
        begin
          prdata <= {29'b0, timer_1_control_reg};
        end
        6'b000011:
        begin
          prdata <= 32'b0;
        end
        6'b000100:
        begin
          prdata <= {31'b0, timer_1_int_status};
        end
        6'b000101:
        begin
          prdata <= timer_2_load_count;
        end
        6'b000110:
        begin
          prdata <= timer_2_current_value;
        end
        6'b000111:
        begin
          prdata <= {29'b0, timer_2_control_reg};
        end
        6'b001000:
        begin
          prdata <= 32'b0;
        end
        6'b001001:
        begin
          prdata <= {31'b0, timer_2_int_status};
        end
        6'b001010:
        begin
          prdata <= timer_3_load_count;
        end
        6'b001011:
        begin
          prdata <= timer_3_current_value;
        end
        6'b001100:
        begin
          prdata <= {29'b0, timer_3_control_reg};
        end
        6'b001101:
        begin
          prdata <= 32'b0;
        end
        6'b001110:
        begin
          prdata <= {31'b0, timer_3_int_status};
        end
        6'b001111:
        begin
          prdata <= timer_4_load_count;
        end
        6'b010000:
        begin
          prdata <= timer_4_current_value;
        end
        6'b010001:
        begin
          prdata <= {29'b0, timer_4_control_reg};
        end
        6'b010010:
        begin
          prdata <= 32'b0;
        end
        6'b010011:
        begin
          prdata <= {31'b0, timer_4_int_status};
        end
        6'b101000:
        begin
          prdata <= {28'b0, timer_int};
        end
        6'b101001:
        begin
          prdata <= 32'b0;
        end
        6'b101010:
        begin
          prdata <= {28'b0, timer_4_raw_int_status, timer_3_raw_int_status, timer_2_raw_int_status, timer_1_raw_int_status};
        end
        default:
        begin
          prdata <= 32'bx;
        end
      endcase
	end
	else
	begin
	  prdata <= 32'bx;
	end
  end
end

assign timer_int = {timer_4_int_status, timer_3_int_status, timer_2_int_status, timer_1_int_status};

//==========================================================
//             Clear Interrupt when reads EOI
//==========================================================
assign timer_1_int_clear = (paddr[7:2] == 6'b000011) && psel && !pwrite;
assign timer_2_int_clear = (paddr[7:2] == 6'b001000) && psel && !pwrite;
assign timer_3_int_clear = (paddr[7:2] == 6'b001101) && psel && !pwrite;
assign timer_4_int_clear = (paddr[7:2] == 6'b010010) && psel && !pwrite;
assign timers_int_clear = (paddr[7:2] == 6'b101001) && psel && !pwrite;

//==========================================================
//             Timer 1 interrupt set and clear
//==========================================================
assign timer_1_int_status = timer_1_raw_int_status && !timer_1_control_reg[2];
always @(posedge pclk or negedge presetn)
begin
  if(!presetn)
  begin
    timer_1_raw_int_status <= 1'b0;
  end
  else
  begin
    if(timer_1_control_reg[0])
    begin
      if(timer_1_interrupt)
      begin
        timer_1_raw_int_status <= 1'b1;
      end
      else
      begin
        if(timer_1_int_clear || timers_int_clear)
        begin
          timer_1_raw_int_status <= 1'b0;
        end
      end
    end
    else
    begin
      timer_1_raw_int_status <= 1'b0;
    end
  end
end

//==========================================================
//             Timer 2 interrupt set and clear
//==========================================================
assign timer_2_int_status = timer_2_raw_int_status && !timer_2_control_reg[2];
always @(posedge pclk or negedge presetn)
begin
  if(!presetn)
  begin
    timer_2_raw_int_status <= 1'b0;
  end
  else
  begin
    if(timer_2_control_reg[0])
    begin
      if(timer_2_interrupt)
      begin
        timer_2_raw_int_status <= 1'b1;
      end
      else
      begin
        if(timer_2_int_clear || timers_int_clear)
        begin
          timer_2_raw_int_status <= 1'b0;
        end
      end
    end
    else
    begin
      timer_2_raw_int_status <= 1'b0;
    end
  end
end

//==========================================================
//             Timer 3 interrupt set and clear
//==========================================================
assign timer_3_int_status = timer_3_raw_int_status && !timer_3_control_reg[2];
always @(posedge pclk or negedge presetn)
begin
  if(!presetn)
  begin
    timer_3_raw_int_status <= 1'b0;
  end
  else
  begin
    if(timer_3_control_reg[0])
    begin
      if(timer_3_interrupt)
      begin
        timer_3_raw_int_status <= 1'b1;
      end
      else
      begin
        if(timer_3_int_clear || timers_int_clear)
        begin
          timer_3_raw_int_status <= 1'b0;
        end
      end
    end
    else
    begin
      timer_3_raw_int_status <= 1'b0;
    end
  end
end

//==========================================================
//             Timer 4 interrupt set and clear
//==========================================================
assign timer_4_int_status = timer_4_raw_int_status && !timer_4_control_reg[2];
always @(posedge pclk or negedge presetn)
begin
  if(!presetn)
  begin
    timer_4_raw_int_status <= 1'b0;
  end
  else
  begin
    if(timer_4_control_reg[0])
    begin
      if(timer_4_interrupt)
      begin
        timer_4_raw_int_status <= 1'b1;
      end
      else
      begin
        if(timer_4_int_clear || timers_int_clear)
        begin
          timer_4_raw_int_status <= 1'b0;
        end
      end
    end
    else
    begin
      timer_4_raw_int_status <= 1'b0;
    end
  end
end

// &ModuleEnd; @385
endmodule