OpenCores
URL https://opencores.org/ocsvn/datetime/datetime/trunk

Subversion Repositories datetime

[/] [datetime/] [trunk/] [rtl/] [verilog/] [Datetime.v] - Rev 2

Compare with Previous | Blame | View Log

`timescale 1ns / 1ps
//=============================================================================
//	(C) 2007,2012  Robert T Finch
//	All rights reserved.
//	robfinch<remove>@opencores.org
//
//	Datetime.v
//	
//
// This source file is free software: you can redistribute it and/or modify 
// it under the terms of the GNU Lesser General Public License as published 
// by the Free Software Foundation, either version 3 of the License, or     
// (at your option) any later version.                                      
//                                                                          
// This source file is distributed in the hope that it will be useful,      
// but WITHOUT ANY WARRANTY; without even the implied warranty of           
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            
// GNU General Public License for more details.                             
//                                                                          
// You should have received a copy of the GNU General Public License        
// along with this program.  If not, see <http://www.gnu.org/licenses/>.    
//
//
//
//	BCD representations are used
//	- BCD allows direct visual indication of values without
//	needing to convert hexidecimal values
//
//
//	Address  Reg
//		
//	DC0400    0	DT - Date/Time register
//		      	[ 7: 0]	read / write jiffies
//	    		[15: 8] read / write seconds
//	    		[23:16] read / write minutes
//      		[31:24] read / write hours
//				[39:32] read/write day
//				[47:40] read/write month
//				[63:48] read/write year
//
//	DC0408	  1 ALM - alarm register same format as 0, but contain alarm setting
//
//  DC0410	  2	CR - control register
//      		[ 7: 0] write - which bytes to match for alarm
//      		[8] - time of day enable
//      		[10: 9] - 00=100 Hz, 01=60Hz, 10=50Hz
//     			[16] - mars timekeeping
//
//	DC0418	 3 SN
//				writing this register triggers a snapshot
//				- trigger a snapshot before reading the date / time
//				registers
//				- the snapshot allows the date / time value to be
//				read without having to worry about an update occuring
//				during the read
//				- a copy of the current date and time is stored in
//				the output registers
//
//=============================================================================
//
module Datetime
#(
	parameter pIOAddress = 24'hDC0400,
	parameter pMars = 1'b0
)
(
// Syscon
input rst_i,		// reset
input clk_i,		// system clock
 
// System bus
input cyc_i,		// valid bus cycle
input stb_i,		// data transfer strobe
output ack_o,		// transfer acknowledge
input we_i,			// 1=write
input [7:0] sel_i,	// byte select
input [23:0] adr_i,	// address
input [63:0] dat_i,	// data input
output reg [63:0] dat_o,	// data output
 
input tod,			// tod pulse (eg 60 Hz)
output reg alarm		// alarm match
);
 
reg [1:0] tod_freq;
wire [7:0] max_jcnt = tod_freq==2'b00 ? 8'h99 : tod_freq==2'b01 ? 8'h59 : tod_freq==2'b10 ? 8'h49 : 8'h99;
reg tod_en;
reg mars;
reg snapshot;
wire IsLeapYear;
wire IsDuckYear;
 
// internal counters
reg [3:0] dayL, dayH;		// 1-99
reg [3:0] monthL, monthH;	// 1-99
reg [3:0] yearN0, yearN1, yearN2, yearN3;	// 
reg [3:0] jiffyL, secL, minL, hourL;
reg [3:0] jiffyH, secH, minH, hourH;
 
// output latches
reg [3:0] dayLo, dayHo;		// 1-99
reg [3:0] monthLo, monthHo;	// 1-99
reg [3:0] yearN0o, yearN1o, yearN2o, yearN3o;	// 
reg [3:0] jiffyLo, secLo, minLo, hourLo;
reg [3:0] jiffyHo, secHo, minHo, hourHo;
 
// alarm
reg [7:0] alarm_care;
wire [63:0] alarm_carex = {
	{8{alarm_care[7]}},
	{8{alarm_care[6]}},
	{8{alarm_care[5]}},
	{8{alarm_care[4]}},
	{8{alarm_care[3]}},
	{8{alarm_care[2]}},
	{8{alarm_care[1]}},
	{8{alarm_care[0]}}
};
reg [3:0] alm_dayL, alm_dayH;		// 1-99
reg [3:0] alm_monthL, alm_monthH;	// 1-99
reg [3:0] alm_yearN0, alm_yearN1, alm_yearN2, alm_yearN3;	// 
reg [3:0] alm_jiffyL, alm_secL, alm_minL, alm_hourL;
reg [3:0] alm_jiffyH, alm_secH, alm_minH, alm_hourH;
 
 
// update detects
wire incJiffyH = jiffyL == 4'd9;
wire incSecL = {jiffyH,jiffyL}==max_jcnt;
wire incSecH = incSecL && secL==4'h9;
wire incMinL = incSecH && secH==4'h5;
wire incMinH = incMinL && minL==4'h9;
wire incHourL = incMinH && minH==4'h5;
wire incHourH = incHourL && hourL==4'h9;
 
wire incDayL    = mars ?
					{hourH,hourL,minH,minL,secH,secL,jiffyH,jiffyL} == {24'h243721,max_jcnt} :
					{hourH,hourL,minH,minL,secH,secL,jiffyH,jiffyL} == {24'h235959,max_jcnt}
					;
wire incDayH = incDayL && dayL==4'h9;
 
// 668.5991	
reg incMarsMonth;
always @(monthH,monthL,dayH,dayL)
	begin
	case({monthH,monthL})	// synopsys full_case parallel_case
	8'h01:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h02:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h03:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h04:	incMarsMonth = {dayH,dayL}==8'h34;
	8'h05:	incMarsMonth = {dayH,dayL}==8'h34;
	8'h06:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h07:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h08:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h09:	incMarsMonth = {dayH,dayL}==8'h34;
	8'h10:	incMarsMonth = {dayH,dayL}==8'h34;
	8'h11:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h12:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h13:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h14:	incMarsMonth = {dayH,dayL}==8'h34;
	8'h15:	incMarsMonth = {dayH,dayL}==8'h34;
	8'h16:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h17:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h18:	incMarsMonth = {dayH,dayL}==8'h33;
	8'h19:	incMarsMonth = {dayH,dayL}==8'h34;
	8'h20:	incMarsMonth = IsDuckYear ? {dayH,dayL}==8'h34 : {dayH,dayL}==8'h35;
	endcase
	end
 
reg incEarthMonth;
always @(monthH,monthL,dayH,dayL)
	begin
	case({monthH,monthL})	// synopsys full_case parallel_case
	8'h01:	incEarthMonth = {dayH,dayL}==8'h31;
	8'h02:	incEarthMonth = IsLeapYear ? {dayH,dayL}==8'h29 : {dayH,dayL}==8'h28;
	8'h03:	incEarthMonth = {dayH,dayL}==8'h31;
	8'h04:	incEarthMonth = {dayH,dayL}==8'h30;
	8'h05:	incEarthMonth = {dayH,dayL}==8'h31;
	8'h06:	incEarthMonth = {dayH,dayL}==8'h30;
	8'h07:	incEarthMonth = {dayH,dayL}==8'h31;
	8'h08:	incEarthMonth = {dayH,dayL}==8'h31;
	8'h09:	incEarthMonth = {dayH,dayL}==8'h30;
	8'h10:	incEarthMonth = {dayH,dayL}==8'h31;
	8'h11:	incEarthMonth = {dayH,dayL}==8'h30;
	8'h12:	incEarthMonth = {dayH,dayL}==8'h31;
	endcase
	end
 
wire incMonthL  = incDayL && (mars ? incMarsMonth : incEarthMonth);
wire incMonthH  = incMonthL && monthL==4'd9;
wire incYearN0	= incMonthL && (mars ? {monthH,monthL} == 8'h20 : {monthH,monthL} == 8'h12);
wire incYearN1  = incYearN0 && yearN0 == 4'h9;
wire incYearN2  = incYearN1 && yearN1 == 4'h9;
wire incYearN3  = incYearN2 && yearN2 == 4'h9;
 
 
wire cs = cyc_i && stb_i && (adr_i[23:5]==pIOAddress[23:5]);
 
reg ack1;
always @(posedge clk_i)
	ack1 <= cs;
assign ack_o = cs ? (we_i ? 1'b1 : ack1) : 1'b0;
 
// Synchronize external tod signal
wire tods;
sync2s sync0(.rst(rst_i), .clk(clk_i), .i(tod), .o(tods));
 
// Edge detect the incoming tod signal.
wire tod_edge;
edge_det ed_tod(.rst(rst_i), .clk(clk_i), .ce(1'b1), .i(tods), .pe(tod_edge), .ne(), .ee());
 
// Output alarm pulse on match
wire isAlarm =
		{
			alm_jiffyH,alm_jiffyL,
			alm_secH,alm_secL,
			alm_minH,alm_minL,
			alm_hourH, alm_hourL,
			alm_dayH,alm_dayL,
			alm_monthH,alm_monthL,
			alm_yearN1,alm_yearN0,
			alm_yearN3,alm_yearN2
		} & alarm_carex ==
		{
			jiffyH,jiffyL,
			secH,secL,
			minH,minL,
			hourH,hourL,
			dayH,dayL,
			monthH,monthL,
			yearN1,yearN0,
			yearN3,yearN3
		} & alarm_carex;
 
 
reg oalarm;
 
always @(posedge clk_i)
	if (rst_i) begin
		oalarm <= 1'b0;
		mars <= pMars;
		tod_en <= 1'b1;
		tod_freq = 2'b00;
 
		jiffyL <= 4'h0;
		jiffyH <= 4'h0;
		secL <= 4'h0;
		secH <= 4'h0;
		minL <= 4'h6;
		minH <= 4'h0;
		hourL <= 4'h3;
		hourH <= 4'h1;
 
		dayL <= 4'h0;
		dayH <= 4'h1;
		monthL <= 4'h6;
		monthH <= 4'h0;
		yearN0 <= 4'h2;
		yearN1 <= 4'h1;
		yearN2 <= 4'h0;
		yearN3 <= 4'h2;
 
		alarm_care <= 8'hFF;
		alm_jiffyL <= 4'h0;
		alm_jiffyH <= 4'h0;
		alm_secL <= 4'h0;
		alm_secH <= 4'h0;
		alm_minL <= 4'h0;
		alm_minH <= 4'h0;
		alm_hourL <= 4'h0;
		alm_hourH <= 4'h0;
 
		alm_dayL <= 4'h0;
		alm_dayH <= 4'h0;
		alm_monthL <= 4'h0;
		alm_monthH <= 4'h0;
		alm_yearN0 <= 4'h0;
		alm_yearN1 <= 4'h0;
		alm_yearN2 <= 4'h0;
		alm_yearN3 <= 4'h0;
 
		snapshot <= 1'b0;
 
	end
	else begin
 
		oalarm <= isAlarm;
		snapshot <= 1'b0;	// ensure it only pulses
 
		if (isAlarm & !oalarm)
			alarm <= 1'b1;
 
		// Handle register updates
		if (cs & we_i) begin
			case(adr_i[4:3])
 
			2'd0:	begin
					if (sel_i[0]) begin jiffyL <= dat_i[3:0]; jiffyH <= dat_i[7:4]; end
					if (sel_i[1]) begin secL <= dat_i[3:0]; secH <= dat_i[7:4]; end
					if (sel_i[2]) begin minL <= dat_i[3:0]; minH <= dat_i[7:4]; end
					if (sel_i[3]) begin hourL <= dat_i[3:0]; hourH <= dat_i[7:4]; end
					if (sel_i[4]) begin dayL <= dat_i[3:0]; dayH <= dat_i[7:4]; end
					if (sel_i[5]) begin monthL <= dat_i[3:0]; monthH <= dat_i[7:4]; end
					if (sel_i[6]) begin yearN0 <= dat_i[3:0]; yearN1 <= dat_i[7:4]; end
					if (sel_i[7]) begin yearN2 <= dat_i[3:0]; yearN3 <= dat_i[7:4]; end
					end
 
			2'd1:	begin
					if (sel_i[0]) begin alm_jiffyL <= dat_i[3:0]; alm_jiffyH <= dat_i[7:4]; end
					if (sel_i[1]) begin alm_secL <= dat_i[3:0]; alm_secH <= dat_i[7:4]; end
					if (sel_i[2]) begin alm_minL <= dat_i[3:0]; alm_minH <= dat_i[7:4]; end
					if (sel_i[3]) begin alm_hourL <= dat_i[3:0]; alm_hourH <= dat_i[7:4]; end
					if (sel_i[4]) begin alm_dayL <= dat_i[3:0]; alm_dayH <= dat_i[7:4]; end
					if (sel_i[5]) begin alm_monthL <= dat_i[3:0]; alm_monthH <= dat_i[7:4]; end
					if (sel_i[6]) begin alm_yearN0 <= dat_i[3:0]; alm_yearN1 <= dat_i[7:4]; end
					if (sel_i[7]) begin alm_yearN2 <= dat_i[3:0]; alm_yearN3 <= dat_i[7:4]; end
					end
 
			2'd2:	begin
					if (sel_i[0]) alarm_care <= dat_i[7:0];
					if (sel_i[1]) 	begin
									tod_en <= dat_i[8];
									tod_freq <= dat_i[10:9];
									end
					if (sel_i[2]) mars <= dat_i[16];
					end
 
			// writing to register 3 triggers a snapshot
			2'd3:	snapshot <= 1'b1;
 
			endcase
		end
		if (cs) begin
			case(adr_i[4:3])
			2'd0:	dat_o <= {yearN3o,yearN2o,yearN1o,yearN0o,monthHo,monthLo,dayHo,dayLo,hourHo,hourLo,minHo,minLo,secHo,secLo,jiffyHo,jiffyLo};
			2'd1:	begin
						dat_o <= {alm_yearN3,alm_yearN2,alm_yearN1,alm_yearN0,alm_monthH,alm_monthL,alm_dayH,alm_dayL,alm_hourH,alm_hourL,alm_minH,alm_minL,alm_secH,alm_secL,alm_jiffyH,alm_jiffyL};
						alarm <= 1'b0;
					end
			2'd2:	dat_o <= {mars,5'b0,tod_freq,tod_en,alarm_care}; 
			2'd3:	dat_o <= 0;
			endcase
		end
		else
			dat_o <= 64'd0;
 
 
		// Clock updates
		if (tod_en & tod_edge) begin
 
			jiffyL <= jiffyL + 4'h1;
 
			if (incJiffyH) begin
				jiffyL <= 4'h0;
				jiffyH <= jiffyH + 4'h1;
			end
 
			// Seconds
			if (incSecL) begin
				jiffyH <= 4'h0;
				secL <= secL + 4'h1;
			end
			if (incSecH) begin
				secL <= 4'h0;
				secH <= secH + 4'h1;
			end
 
			if (incMinL) begin
				minL <= minL + 4'h1;
				secH <= 4'h0;
			end
			if (incMinH) begin
				minL <= 4'h0;
				minH <= minH + 4'h1;
			end
 
			if (incHourL) begin
				minH <= 4'h0;
				hourL <= hourL + 4'h1;
			end
			if (incHourH) begin
				hourL <= 4'h0;
				hourH <= hourH + 4'h1;
			end
 
			// day increment
			// reset the entire time when the day increments
			// - the day may not be exactly 24 hours long
			if (incDayL) begin
				dayL <= dayL + 4'h1;
				jiffyL <= 4'h0;
				jiffyH <= 4'h0;
				secL <= 4'h0;
				secH <= 4'h0;
				minL <= 4'h0;
				minH <= 4'h0;
				hourL <= 4'h0;
				hourH <= 4'h0;
			end
			if (incDayH) begin
				dayL <= 4'h0;
				dayH <= dayH + 4'h1;
			end
 
			if (incMonthL) begin
				dayL <= 4'h1;
				dayH <= 4'h0;
				monthL <= monthL + 4'h1;
			end
			if (incMonthH) begin
				monthL <= 4'h0;
				monthH <= monthH + 4'h1;
			end
 
			if (incYearN0) begin
				monthL <= 4'h1;
				monthH <= 4'h0;
			end
			if (incYearN1) begin
				yearN0 <= 4'h0;
				yearN1 <= yearN1 + 4'h1;
			end
			if (incYearN2) begin
				yearN1 <= 4'h0;
				yearN2 <= yearN2 + 4'h1;
			end
			if (incYearN3) begin
				yearN2 <= 4'h0;
				yearN3 <= yearN3 + 4'h1;
			end
		end
	end
 
 
// Take snapshot of date / time
always @(posedge clk_i)
	if (rst_i) begin
		jiffyLo <= 4'h0;
		jiffyHo <= 4'h0;
		secLo <= 4'h0;
		secHo <= 4'h0;
		minLo <= 4'h0;
		minHo <= 4'h0;
		hourLo <= 4'h0;
		hourHo <= 4'h0;
		dayLo <= 4'h0;
		dayHo <= 4'h0;
		monthLo <= 4'h0;
		monthHo <= 4'h0;
		yearN0o <= 4'h0;
		yearN1o <= 4'h0;
		yearN2o <= 4'h0;
		yearN3o <= 4'h0;
	end
	else if (snapshot) begin
		jiffyLo <= jiffyL;
		jiffyHo <= jiffyH;
		secLo <= secL;
		secHo <= secH;
		minLo <= minL;
		minHo <= minH;
		hourLo <= hourL;
		hourHo <= hourH;
		dayLo <= dayL;
		dayHo <= dayH;
		monthLo <= monthL;
		monthHo <= monthH;
		yearN0o <= yearN0;
		yearN1o <= yearN1;
		yearN2o <= yearN2;
		yearN3o <= yearN3;
	end
 
//   int IsLeapYear() const {
//      return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
//   };
//
// We don't need to convert the whole year to binary to determine if it's divisble by four,
// only the lowest two order nybbles need be converted.
wire [7:0] binyear = 
			yearN0
			+ {yearN1,3'b0} + {yearN1,1'b0}	// yearN1 * 10
//			+ {yearN2,6'b0} + {yearN2,5'b0} + {yearN2,2'b0}	// yearN2 * 100
//			+ {yearN3,9'b0} + {yearN3,8'b0} + {yearN3,7'b0} + {yearN3,6'b0} + {yearN3,5'b0} + {yearN3,3'b0}
			;
 
// convert century to binary to see if it's divisible by four
wire [7:0] bincent = {yearN3,3'b0} + {yearN3,1'b0} + yearN2;
 
assign IsLeapYear = binyear[1:0]==2'b00 && ({yearN1,yearN0}!=8'h00 || (bincent[1:0]==2'b00 && {yearN1,yearN0}==8'h00));
 
assign IsDuckYear = yearN0==4'h1 || yearN0==4'h3 || yearN0==4'h6 || yearN0==4'h8;
 
endmodule
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.