| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 | //// 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
 |