/* ============================================================================ (C) 2007 Robert T Finch All rights reserved. rob@birdcomputer.ca BCDateTime.v Day of year core. This source code is available for evaluation and validation purposes only. This copyright statement and disclaimer must remain present in the file. NO WARRANTY. THIS Work, IS PROVIDEDED "AS IS" WITH NO WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED. The user must assume the entire risk of using the Work. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY INCIDENTAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES WHATSOEVER RELATING TO THE USE OF THIS WORK, OR YOUR RELATIONSHIP WITH THE AUTHOR. IN ADDITION, IN NO EVENT DOES THE AUTHOR AUTHORIZE YOU TO USE THE WORK IN APPLICATIONS OR SYSTEMS WHERE THE WORK'S FAILURE TO PERFORM CAN REASONABLY BE EXPECTED TO RESULT IN A SIGNIFICANT PHYSICAL INJURY, OR IN LOSS OF LIFE. ANY SUCH USE BY YOU IS ENTIRELY AT YOUR OWN RISK, AND YOU AGREE TO HOLD THE AUTHOR AND CONTRIBUTORS HARMLESS FROM ANY CLAIMS OR LOSSES RELATING TO SUCH UNAUTHORIZED USE. BCD representations are used - BCD allows direct visual indication of values without needing to convert hexidecimal values Reg 0 [ 7: 0] read / write jiffies [15: 8] read / write seconds [23:16] read / write minutes [31:24] read / write hours 1 [ 7: 0] read/write day [15: 8] read/write month [31:15] read/write year 2-3 same as 0-1, but contain alarm setting 4 5 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 Spartan3 306 LUTs / 191 slices / 95MHz (incl. mars) ============================================================================ */ module BCDateTime #( parameter pJIFFY = 8'h59 // a BCD (binary coded decimal) number ) ( // Syscon input rst, // reset input clk, // system clock input tod, // tod pulse (eg 60 Hz) // System bus input cyc, // valid bus cycle input cs, // chip select input wr, // 1=write input [3:0] mask, // byte select output rdy, // ready output input [2:0] addr, // register address input [31:0] din, // data input output reg [31:0] dout, // data output output alarm // alarm match ); reg tod_en; reg mars; reg snapshot; // 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 [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}==pJIFFY; 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} == 32'h24372159 : {hourH,hourL,minH,minL,secH,secL,jiffyH,jiffyL} == 32'h23595959 ; wire incDayH = incDayL && dayL==4'h9; reg incMarsMonth; always @(monthH,monthL,dayH,dayL) begin case({monthH,monthL}) // synopsys full_case parallel_case 8'h01: incMarsMonth = {dayH,dayL}==8'h34; 8'h02: incMarsMonth = {dayH,dayL}==8'h32; 8'h03: incMarsMonth = {dayH,dayL}==8'h34; 8'h04: incMarsMonth = {dayH,dayL}==8'h33; 8'h05: incMarsMonth = {dayH,dayL}==8'h34; 8'h06: incMarsMonth = {dayH,dayL}==8'h33; 8'h07: incMarsMonth = {dayH,dayL}==8'h34; 8'h08: incMarsMonth = {dayH,dayL}==8'h33; 8'h09: incMarsMonth = {dayH,dayL}==8'h34; 8'h10: incMarsMonth = {dayH,dayL}==8'h33; 8'h11: incMarsMonth = {dayH,dayL}==8'h34; 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'h33; 8'h16: incMarsMonth = {dayH,dayL}==8'h34; 8'h17: incMarsMonth = {dayH,dayL}==8'h33; 8'h18: incMarsMonth = {dayH,dayL}==8'h34; 8'h19: incMarsMonth = {dayH,dayL}==8'h33; 8'h20: incMarsMonth = {dayH,dayL}==8'h34; 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 = {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 = incDayH && (mars ? incMarsMonth : incEarthMonth); wire incMonthH = incMonthL && monthL==4'd9; wire incYearN0 = incMonthH && (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; assign rdy = cyc & cs; // Synchronize external tod signal wire doys; sync2s sync0(.rst(rst), .clk(clk), .i(tod), .o(tods)); // Edge detect the incoming tod signal. wire tod_edge; edge_det ed_tod(.rst(rst), .clk(clk), .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 }== { jiffyH,jiffyL, secH,secL, minH,minL, hourH,hourL }; edge_det ed_alarm(.rst(rst), .clk(clk), .ce(ce), .i(isAlarm), .pe(alarm), .ne(), .ee() ); // register reads always @* begin case(addr) // synopsys full_case parallel_case 3'd0: dout <= {hourHo,hourLo,minHo,minLo,secHo,secLo,jiffyHo,jiffyLo}; 3'd1: dout <= {yearN3o,yearN2o,yearN1o,yearN0o,monthHo,monthLo,dayHo,dayLo}; 3'd2: dout <= {alm_hourH,alm_hourL,alm_minH,alm_minL,alm_secH,alm_secL,alm_jiffyH,alm_jiffyL}; 3'd3: dout <= {alm_yearN3,alm_yearN2,alm_yearN1,alm_yearN0,alm_monthH,alm_monthL,alm_dayH,alm_dayL}; 3'd4: dout <= tod_en; 3'd5: dout <= 0; endcase end always @(posedge clk) if (rst) begin jiffyL <= 0; jiffyH <= 0; secL <= 0; secH <= 0; minL <= 0; minH <= 0; hourL <= 0; hourH <= 0; dayL <= 1; dayH <= 0; monthL <= 1; monthH <= 0; yearN0 <= 0; yearN1 <= 0; yearN2 <= 0; yearN3 <= 0; alm_jiffyL <= 0; alm_jiffyH <= 0; alm_secL <= 0; alm_secH <= 0; alm_minL <= 0; alm_minH <= 0; alm_hourL <= 0; alm_hourH <= 0; alm_dayL <= 0; alm_dayH <= 0; alm_monthL <= 0; alm_monthH <= 0; alm_yearN0 <= 0; alm_yearN1 <= 0; alm_yearN2 <= 0; alm_yearN3 <= 0; snapshot <= 0; end else begin snapshot <= 0; // ensure it only pulses // Handle register updates if (cyc & cs & wr) begin case(addr) // synopsys full_case parallel_case 3'd0: begin if (mask[0]) begin jiffyL <= din[3:0]; jiffyH <= din[7:4]; end if (mask[1]) begin secL <= din[3:0]; secH <= din[7:4]; end if (mask[2]) begin minL <= din[3:0]; minH <= din[7:4]; end if (mask[3]) begin hourL <= din[3:0]; hourH <= din[7:4]; end end 3'd1: begin if (mask[0]) begin dayL <= din[3:0]; dayH <= din[7:4]; end if (mask[1]) begin monthL <= din[3:0]; monthH <= din[7:4]; end if (mask[2]) begin yearN0 <= din[3:0]; yearN1 <= din[7:4]; end if (mask[3]) begin yearN2 <= din[3:0]; yearN3 <= din[7:4]; end end 3'd2: begin if (mask[0]) begin alm_jiffyL <= din[3:0]; alm_jiffyH <= din[7:4]; end if (mask[1]) begin alm_secL <= din[3:0]; alm_secH <= din[7:4]; end if (mask[2]) begin alm_minL <= din[3:0]; alm_minH <= din[7:4]; end if (mask[3]) begin alm_hourL <= din[3:0]; alm_hourH <= din[7:4]; end end 3'd3: begin if (mask[0]) begin alm_dayL <= din[3:0]; alm_dayH <= din[7:4]; end if (mask[1]) begin alm_monthL <= din[3:0]; alm_monthH <= din[7:4]; end if (mask[2]) begin alm_yearN0 <= din[3:0]; alm_yearN1 <= din[7:4]; end if (mask[3]) begin alm_yearN2 <= din[3:0]; alm_yearN3 <= din[7:4]; end end 3'd4: begin if (mask[0]) tod_en <= din[0]; end // writing to register 5 triggers a snapshot 3'd5: snapshot <= 1; endcase end // Clock updates if (tod_en & tod_edge) begin jiffyL <= jiffyL + 1; if (incJiffyH) begin jiffyL <= 0; jiffyH <= jiffyH + 1; end // Seconds if (incSecL) begin jiffyH <= 0; secL <= secL + 1; end if (incSecH) begin secL <= 0; secH <= secH + 1; end if (incMinL) begin minL <= minL + 1; secH <= 0; end if (incMinH) begin minL <= 0; minH <= minH + 1; end if (incHourL) begin minH <= 0; hourL <= hourL + 1; end if (incHourH) begin hourL <= 0; hourH <= hourH + 1; 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 + 1; jiffyL <= 0; jiffyH <= 0; secL <= 0; secH <= 0; minL <= 0; minH <= 0; hourL <= 0; hourH <= 0; end if (incDayH) begin dayL <= 0; dayH <= dayH + 1; end if (incMonthL) begin dayL <= 1; dayH <= 0; monthL <= monthL + 1; end if (incMonthH) begin monthL <= 0; monthH <= monthH + 1; end if (incYearN0) begin monthL <= 1; monthH <= 0; end if (incYearN1) begin yearN0 <= 0; yearN1 <= yearN1 + 1; end if (incYearN2) begin yearN1 <= 0; yearN2 <= yearN2 + 1; end if (incYearN3) begin yearN2 <= 0; yearN3 <= yearN3 + 1; end end end // Take snapshot of date / time always @(posedge clk) if (rst) begin jiffyLo <= 0; jiffyHo <= 0; secLo <= 0; secHo <= 0; minLo <= 0; minHo <= 0; hourLo <= 0; hourHo <= 0; dayLo <= 0; dayHo <= 0; monthLo <= 0; monthHo <= 0; yearN0o <= 0; yearN1o <= 0; yearN2o <= 0; yearN3o <= 0; 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 endmodule