Closed sy2002 closed 8 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
The interface to the core is via the C64 cassette port.
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.
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.
@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 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.
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.
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
This is verified in the C64 repo in commit 111feea.
Self assigned to test on my R3 machine:
@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.
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?
Note: in 12h mode 0x31 would be 11am!
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: So I need to set this bit before updating the RTC, and clear it again afterwards.
Please retest
@MJoergen ❌ Test failed:
develop
branch) to test.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.
Well, that didn't help. I still sometimes get a "NACK" on the I2C transaction to the RTC chip.
@sy2002 : Fixed in commit e8bfed6. Please try again.
✅ SUCCESS
Here is what I did:
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.
@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:
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...
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.)
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.