sy2002 / MiSTer2MEGA65

Framework to simplify porting MiSTer (and other) cores to the MEGA65
GNU General Public License v3.0
35 stars 8 forks source link

Add support for RTC in M2M: Hardware #29

Closed sy2002 closed 8 months ago

sy2002 commented 11 months ago

@MJoergen Please decide if you want to do this in 5.1 (then the existing label "R4/R5 Surge" is correct - or not, then delete that label).

Adding support for RTC in M2M makes sense because:

  1. Multiple cores can make use of an RTC: C64 core (GEOS), ZX Uno (currently outputs an error due to missing RTC when booting), the PC 486 core from MiSTer...

  2. The firmware can make use of the RTC and make sure that files on the SD card (FAT32) that are written to/changed (for example disk images) are showing the updated date/time: https://github.com/sy2002/MiSTer2MEGA65/issues/30 (The firmware part is not a long hanging fruit for the R4/R5 surge, but maybe the hardware part is.)

  3. We need to abstract away the details of the different RTCs in R3/R3A vs. R4/R5 and even the fixed RTC for R3/R3A which uses a PMOD connector. So there are three RTCs as of now that we need to support.

MJoergen commented 11 months ago

Here is the datasheet for the RTC in R4/R5: https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-3032-C7_App-Manual.pdf .

Notes from the datasheet:

For more details, see Chapter 6 pages 121-126

MJoergen commented 11 months ago

The interface to the core is via the C64 cassette port.

MJoergen commented 11 months ago

Question: Should we poll the RTC regularly, or should we just read it once after power-on? Surely the crystal oscillator on the MEGA65 is accurate enough for time keeping while the MEGA65 is turned on.

MJoergen commented 9 months ago

The following is a description of what I intend to implement. In other words, this describes the interface to the QNICE CPU (firmware). This is all part of the QNICE device number 0x0006 (C_DEV_RTC defined in M2M/vhdl/qnice_wrapper.vhd).

This entity contains a free-running internal timer, running independently of the                                                   
external RTC. The internal timer can be stopped and started, and can be set (when
stopped). Furthermore, when internal timer is stopped, the date/time can be copied
between the internal timer and the external RTC.

Address | Data
0x7000  | Hundredths of a second (BCD format, 0x00-0x99)
0x7001  | Seconds                (BCD format, 0x00-0x60)
0x7002  | Minutes                (BCD format, 0x00-0x59)
0x7003  | Hours                  (BCD format, 0x00-0x23)
0x7004  | DayOfMonth             (BCD format, 0x01-0x31)
0x7005  | Month                  (BCD format, 0x01-0x12)
0x7006  | Year since 2000        (BCD format, 0x00-0x99)
0x7007  | DayOfWeek              (0x00 = Monday)
0x7008  | Command                (bit 0 : RO : I2C Busy)
                                 (bit 1 : RW : Copy from RTC to internal)
                                 (bit 2 : RW : Copy from internal to RTC)
                                 (bit 3 : RW : Internal Timer Running)

Addresses 0x7000 to 0x7007 provide R/W access to the internal timer.

The Command byte (address 0x08) is used to start or stop the internal timer, and to
synchronize with the external RTC.  Any changes to the internal timer are only allowed
when the internal timer is stopped. So addresses 0x00 to 0x07 are read-only, when the
internal timer is running.
The protocol for synchronizing with the RTC is as follows:
1. Stop the internal timer by writing 0x00 to Command.
2. Read from Command and make sure value read is zero (otherwise wait).
3. Write either 0x02 (read from RTC) or 0x04 (write to RTC) to the command byte.
4. Read from Command and wait until value read is zero. (Note: The I2C transaction
   takes approximately 1 millisecond to complete).
5. Start the internal timer by writing 0x08 to Command.
Optionally, you may use auto-start in step 3 above by writing 0x0A or 0x0C. This
will automatically re-start the internal timer right after the I2C transaction is
complete, so that step 5 can be skipped.
Note: The Command byte automatically clears, when the command is completed. Reading
from the Command byte gives the status of the current command.
sy2002 commented 9 months ago

@MJoergen Sounds good. So the intend is to use this timer as the actual data source for all possible cores, independent of the actual RTC? Like this timer being a "cache" for us not needing to talk via I2C all the time? And then from time to time use command bit 1 to sync for example every 1 second or so? And maybe even offer a menu item in the OSM where the user can adjust the RTC via command bit 2 (without the need of using the MEGA65's setup menu?) And in the C64's case then the MiSTer F83 RTC implementation (https://github.com/MJoergen/C64MEGA65/issues/103) always uses this timer instead of directly accessing the RTC?

MJoergen commented 9 months ago

@MJoergen Sounds good. So the intend is to use this timer as the actual data source for all possible cores, independent of the actual RTC? Like this timer being a "cache" for us not needing to talk via I2C all the time? And then from time to time use command bit 1 to sync for example every 1 second or so? And maybe even offer a menu item in the OSM where the user can adjust the RTC via command bit 2 (without the need of using the MEGA65's setup menu?) And in the C64's case then the MiSTer F83 RTC implementation (MJoergen#103) always uses this timer instead of directly accessing the RTC?

Exactly! This internal timer is meant as an abstraction. Basically, hide all the various board specific variants, and instead provide a generic interface.

MJoergen commented 9 months ago

No need to periodically sync from external RTC to internal RTC. The accuracy is good enough to be less than 1 second within 24 h. I suggest a menu item where the user can manually choose a "sync from external RTC" if he/she so chooses.

MJoergen commented 9 months ago

Note: The description in https://github.com/sy2002/MiSTer2MEGA65/issues/29#issuecomment-1858735315 has been updated.

This can now been tested in hardware as follows:

MC FFF4 0006 -> Select RTC device
MD 7000 7008 -> Dump current time
MC FFF4 0006 -> Select RTC device
MC 7008 0000 -> Stop timer, enable writing
MC 7000 xxxx -> Set new timer value
...
MC 7007 xxxx -> etc.
MC 7008 000C -> Copy new timer value to RTC and start internal timer

Power cycle machine.

MC FFF4 0006 -> Select RTC device
MD 7000 7008 -> Dump current time
MJoergen commented 9 months ago

This is verified in the C64 repo in commit 111feea.

sy2002 commented 9 months ago

Self assigned to test on my R3 machine:

sy2002 commented 9 months ago

@MJoergen Not sure if I am doing everything correctly. The date / time that I have set on my MEGA65 is:

6th of January 2023 (sic!) 16:22 (or a few minutes/seconds later)

When doing this here:

MC FFF4 0006 -> Select RTC device
MD 7000 7007 -> Dump current time

I am receiving:

0027 0050 0024 0096 0006 0001 0023 0003 

If I decode correctly, then the DATE is correct: 6th of January 2023 Second 50 and Minute 24 seems reasonable, but hour 96 does not. Also the day of week is not correct as the 6th of January 2023 (sic!) was a Friday.

I am stopping here. We can do this test together during our next Skype chat.

lydon42 commented 9 months ago

For R3 RTC 0x96 is BCD 16 and highest bit 7 set == 24h mode (MIL as in military time).

The MEGA65 currently converts this in mega65-libc, the VHDL just transfers the data over I2C to the RTC. Perhaps it is a good idea to let the M2M framework handle this and only expose 24h time to the cores via the framework?

2024-01-05_155749_052190393

Note: in 12h mode 0x31 would be 11am!

MJoergen commented 9 months ago

Thanks @sy2002 for the test and @lydon42 for the hint. That behavior is indeed unintended. I will fix the VHDL to only expose pure 24-hour format.

It turns out reading the spec that there is another bug as well: image So I need to set this bit before updating the RTC, and clear it again afterwards.

MJoergen commented 9 months ago

Please retest

sy2002 commented 8 months ago

@MJoergen ❌ Test failed:

grafik
MJoergen commented 8 months ago

I believe the problem is that the RTC chip is accessed immediately after power-on, and that it sometimes won't respond. I can't find any information in the datasheet on when the RTC is ready after power-on, so now I'm trying with a 200 ms delay after power-on.

MJoergen commented 8 months ago

Well, that didn't help. I still sometimes get a "NACK" on the I2C transaction to the RTC chip.

MJoergen commented 8 months ago

@sy2002 : Fixed in commit e8bfed6. Please try again.

sy2002 commented 8 months ago

✅ SUCCESS

Here is what I did:

  1. Synthesized newest M2M framework for R3 using commit ae5c10943befa2121229f620ac3f9c767628905c
  2. Boot the MEGA65 core and get the "current time": 15.1.2023, 16:43:34 (which is obviously not correct, but intentional)
  3. Boot the M2M framework and dump the current date/time using MMIO: ✅ CORRECT
  4. Use the above-mentioned command to set the date/time to the current date time (14.1.2024, 15:54:00 and day of week 6)
  5. Power-Cycle and boot the MEGA65 core to see if the MEGA65's start screen shows the date/time that I set: ✅ CORRECT
  6. Now load the M2M framwork demo core again and use the QMON> debug console to dump $7000 to $7007 to see if date/time is correct here to: ✅ CORRECT

I did only test this cycle here once. Olivier's RTC is not working so we cannot ask him to do this test.

I tend to believe that your "retry" fix works and therefore I am closing this and will merge the fix into the C64, so that I can start testing this in GEOS.