module rtc_read_cntr (
input clk, reset_p,
input read_en,
inout rtc_dat,
output reg rtc_clk,
output reg rtc_rst,
output reg [7:0] sec, min, hour,
output reg [7:0] date, month, day, year,
output reg data_valid, busy
);
localparam IDLE = 9'b0_0000_0001;
localparam RST_SET = 9'b0_0000_0010;
localparam ADDR_IN = 9'b0_0000_0100;
localparam ADDR_SET = 9'b0_0000_1000;
localparam ADDR_RESET = 9'b0_0001_0000;
localparam DATA_IN = 9'b0_0010_0000;
localparam DATA_SET = 9'b0_0100_0000;
localparam DATA_RESET = 9'b0_1000_0000;
localparam RST_RESET = 9'b1_0000_0000;
reg rtc_dat_dir, rtc_dat_out;
assign rtc_dat = rtc_dat_dir ? rtc_dat_out : 1'bz;
reg [9:0] state, next_state;
always @(negedge clk, posedge reset_p) begin
if (reset_p) state = IDLE;
else state = next_state;
end
reg [7:0] com_addr [0:6];
initial begin
com_addr[0] = 8'h81; // SEC
com_addr[1] = 8'h83; // MIN
com_addr[2] = 8'h85; // HOUR
com_addr[3] = 8'h87; // DATE
com_addr[4] = 8'h89; // MONTH
com_addr[5] = 8'h8B; // DAY
com_addr[6] = 8'h8D; // YEAR
end
reg [6:0] clk_div;
reg sclk_en;
always @(posedge clk, posedge reset_p) begin
if (reset_p) begin
clk_div <= 0;
sclk_en <= 0;
end
else begin
clk_div <= clk_div + 1;
sclk_en <= (clk_div == 0);
end
end
reg [3:0] cnt_addr, cnt_bit;
reg [7:0] buff_addr, buff_data;
always @(posedge clk, posedge reset_p) begin
if (reset_p) begin
rtc_rst = 0;
next_state = IDLE;
cnt_addr = 0;
buff_addr = 0;
buff_data = 0;
rtc_clk = 0;
rtc_dat_dir = 0;
rtc_dat_out = 0;
cnt_bit = 0;
data_valid = 0;
busy = 0;
end
else if (sclk_en) begin
case (state)
IDLE : begin
rtc_rst = 0;
cnt_addr = 0;
rtc_clk = 0;
cnt_bit = 0;
buff_addr = 0;
buff_data = 0;
data_valid = 0;
if (read_en) begin
busy = 1;
next_state = RST_SET;
end
end
RST_SET : begin
rtc_rst = 1;
rtc_clk = 0;
rtc_dat_dir = 1;
buff_addr = com_addr[cnt_addr];
next_state = ADDR_IN;
end
ADDR_IN : begin
rtc_dat_out = buff_addr[0];
buff_addr = {1'b0, buff_addr[7:1]};
cnt_bit = cnt_bit + 1;
next_state = ADDR_SET;
end
ADDR_SET : begin
rtc_clk = 1;
next_state = ADDR_RESET;
end
ADDR_RESET : begin
rtc_clk = 0;
if (cnt_bit >= 8) begin
cnt_bit = 0;
rtc_dat_dir = 0;
next_state = DATA_IN;
end
else next_state = ADDR_IN;
end
DATA_IN : begin
buff_data = {rtc_dat, buff_data[7:1]};
cnt_bit = cnt_bit + 1;
next_state = DATA_SET;
end
DATA_SET : begin
rtc_clk = 1;
next_state = DATA_RESET;
end
DATA_RESET : begin
rtc_clk = 0;
if (cnt_bit >= 8) begin
cnt_bit = 0;
next_state = RST_RESET;
end
else next_state = DATA_IN;
end
RST_RESET : begin
rtc_rst = 0;
case (cnt_addr)
4'd0 : sec = buff_data;
4'd1 : min = buff_data;
4'd2 : hour = buff_data;
4'd3 : date = buff_data;
4'd4 : month = buff_data;
4'd5 : day = buff_data;
4'd6 : year = buff_data;
endcase
if (cnt_addr >= 6) begin
cnt_addr = 0;
data_valid = 1;
busy = 0;
next_state = IDLE;
end
else begin
cnt_addr = cnt_addr + 1;
next_state = RST_SET;
end
end
endcase
end
end
endmodule