// // sysclock.sv // // Very simple unit that keeps track of time in "human" format based // on 32.768 kHz signal from the RTC. The registers have to be set from // software, presumably from reading the RTC. // // Register 0 contains the 2 s granular date and time in FAT filesystem format. // Register 1 contains two copies of a 16-bit 32 kHz counter: // the upper half contains the current counter, and the lower half is // a holding register updated when register 0 is read, and, if written // to in adcance, will write the counter when register 0 is written. module sysclock ( input rst_n, input sys_clk, input rtc_clk, input valid, input addr, output reg [31:0] rdata, input [31:0] wdata, input [3:0] wstrb, output periodic ); parameter PERIODIC_HZ_LG2 = 5; wire rtc_clk_sync; reg rtc_clk_q; reg rtc_clk_stb; synchronizer rtc_sync ( .rst_n ( 1'b1 ), .clk ( sys_clk ), .d ( rtc_clk ), .q ( rtc_clk_sync ) ); always @(posedge sys_clk) begin rtc_clk_q <= rtc_clk_sync; rtc_clk_stb <= rtc_clk_sync & ~rtc_clk_q; end function logic [4:0] maxday(input [3:0] mon, input [6:0] year); case (mon) 4'd4, // April 4'd6, // June 4'd9, // September 4'd11: begin // November maxday = 5'd30; end 4'd2: begin // February if ((|year[1:0]) | (year == (2100 - 1980))) maxday = 5'd28; else maxday = 5'd29; end default: begin maxday = 5'd31; end endcase // case (mon) endfunction // mdays function logic [7:0] tick(input [7:0] me, input [7:0] start, input wrap_pre, input wrap_me); tick = wrap_me ? start : me + wrap_pre; endfunction // tick // Counter read/writes holding register reg [15:0] tm_hold; reg [ 1:0] tm_whold; // Byte enables for hold register reg [15:0] tm_tick; reg [31:0] tm_dt; // Day and time in FAT filesystem format wire [4:0] tm_2sec = tm_dt[4:0]; wire [5:0] tm_min = tm_dt[10:5]; wire [4:0] tm_hour = tm_dt[15:11]; wire [4:0] tm_mday = tm_dt[20:16]; wire [3:0] tm_mon = tm_dt[24:21]; wire [6:0] tm_year = tm_dt[31:25]; wire wrap_tick = rtc_clk_stb & |tm_tick; wire wrap_sec = wrap_tick & (tm_2sec >= 5'd29); wire wrap_min = wrap_sec & (tm_min >= 6'd59); wire wrap_hour = wrap_min & (tm_hour >= 5'd23); wire wrap_mday = wrap_hour & (tm_mday >= maxday(tm_mon, tm_year)); wire wrap_mon = wrap_mday & (tm_mon >= 4'd12); // Yes, it may jump if the counter is written... assign periodic = tm_tick[14 - PERIODIC_HZ_LG2]; always @(posedge sys_clk) begin tm_tick <= tm_tick + rtc_clk_stb; tm_dt[4:0] <= tick(tm_2sec, 5'd0, wrap_tick, wrap_sec); tm_dt[10:5] <= tick(tm_min, 6'd0, wrap_sec, wrap_min); tm_dt[15:11] <= tick(tm_hour, 4'd0, wrap_min, wrap_hour); tm_dt[20:16] <= tick(tm_mday, 5'd1, wrap_hour, wrap_mday); tm_dt[24:21] <= tick(tm_mon, 4'd1, wrap_mday, wrap_mon); tm_dt[31:25] <= tick(tm_year, 7'hxx, wrap_mon, 1'b0); if (~rst_n) begin tm_hold <= tm_tick; tm_whold <= 2'b00; end else if (valid) case (addr) 1'b0: begin // Datetime register if (wstrb[0]) tm_dt[7:0] <= wdata[7:0]; if (wstrb[1]) tm_dt[15:8] <= wdata[15:8]; if (wstrb[2]) tm_dt[23:16] <= wdata[23:16]; if (wstrb[3]) tm_dt[31:24] <= wdata[31:24]; if (tm_whold[0]) tm_tick[7:0] <= tm_hold[7:0]; if (tm_whold[1]) tm_tick[15:8] <= tm_hold[15:8]; tm_hold <= tm_tick; tm_whold <= 2'b00; end // case: 1'b0 1'b1: begin // Tick register if (wstrb[0]) tm_hold[7:0] <= wdata[7:0]; if (wstrb[1]) tm_hold[15:8] <= wdata[15:8]; if (wstrb[2]) tm_tick[7:0] <= wdata[23:16]; if (wstrb[3]) tm_tick[15:8] <= wdata[31:24]; tm_whold <= tm_whold | wstrb[1:0]; end endcase // case (addr) end // always @ (posedge sys_clk) // Read data MUX always @(*) case (addr) 1'b0: rdata = tm_dt; 1'b1: rdata = { tm_tick, tm_hold }; endcase // case (addr) endmodule // sysclock