`define ADDR_DIV 16'hFF04 `define ADDR_TIMA 16'hFF05 `define ADDR_TMA 16'hFF06 `define ADDR_TAC 16'hFF07 module Timer( input clk, input wr, input rd, input [15:0] addr, inout [7:0] data, output reg irq = 0); reg rdlatch = 0; reg [15:0] addrlatch = 0; reg [7:0] tima = 0, tma = 0, tac = 0, div = 0; reg ovf = 0; reg [9:0] clkdv = 0; wire is_tima = addrlatch == `ADDR_TIMA; wire is_tma = addrlatch == `ADDR_TMA; wire is_tac = addrlatch == `ADDR_TAC; assign data = rdlatch ? is_tima ? tima : is_tma ? tma : is_tac ? tac : 8'bzzzzzzzz : 8'bzzzzzzzz; wire cksel = tac[2] ? (tac[1:0] == 2'b00) ? (clkdv == 10'b0) : (tac[1:0] == 2'b01) ? (clkdv[3:0] == 4'b0) : (tac[1:0] == 2'b10) ? (clkdv[5:0] == 6'b0) : (clkdv[7:0] == 8'b0) : 0; always @ (posedge clk) begin rdlatch <= rd; addrlatch <= addr; if(wr) begin case(addr) `ADDR_DIV: div <= 8'b0; `ADDR_TIMA: tima <= data; `ADDR_TMA: tma <= data; `ADDR_TAC: tac <= data; endcase end else begin if(ovf) begin tima <= tma; ovf <= 0; irq <= 1; end else begin if(cksel) {ovf,tima} <= {1'b0,tima} + 1; if(irq) irq <= 0; end if(clkdv[7:0] == 8'b0) div <= div + 1; end clkdv <= clkdv + 1; end endmodule