Closed sy2002 closed 5 months ago
My understanding is the following:
The GEOS communicates on the cassette port and expects an F83 RTC device.
In other words, GEOS expects to be communicating with an I2C device with a specific address and a specific memory map.
Therefore, the M2M framework would need to emulate a "virtual" F83 RTC device, while at the same time communicating with the "real" RTC on the board. Essentially, the M2M framework would be the "man-in-the-middle".
However, much simpler is to re-use MiSTer's emulation rtcF83.sv
. This takes care of the I2C interface to GEOS.
The MiSTer emulation file expects a 65-bit signal containing the current date/time, with the following format (see file user_io.cpp
in Main_MiSTer
repo):
-- Bits 7 - 0 : Seconds (BCD format, 0x00-0x60)
-- Bits 15 - 8 : Minutes (BCD format, 0x00-0x59)
-- Bits 23 - 16 : Hours (BCD format, 0x00-0x23)
-- Bits 31 - 24 : DayOfMonth (BCD format, 0x01-0x31)
-- Bits 39 - 32 : Month (BCD format, 0x01-0x12)
-- Bits 47 - 40 : Year (BCD format, 0x00-0x99)
-- Bits 55 - 48 : DayOfWeek (0x00-0x06)
-- Bits 63 - 56 : 0x40
-- Bit 64 : Toggle flag. Flips anytime there is a change in the other bits
The M2M framework would then continuously make sure this signal has the correct value, by appropriate communication with whatever RTC is present on the MEGA65 board.
After several attempts I have come to the conclusion that the following "hybrid" approach is best:
Firstly, the M2M framework (VHDL part) will provide a generic QNICE-memory-mapped interface to the I2C bus. In other words, via QNICE reads/writes to framework device ID C_DEV_I2C it is possible to initiate reads and writes to an arbitrary I2C device.
Secondly, the Core makes use of the same interface as on MiSTer, i.e. a 65-bit generic value describing the current date/time in BCD format. This value only needs to be presented once after reset, the core will maintain (i.e. keep up-to-date) its own local copy of the current date/time.
What is left (i.e. still not done), is a purely QNICE firmware task:
control_d_o
signal.The benefit of this approach is that the same QNICE register map can be used from the monitor to debug / verify / communicate with (any) I2C devices on the board.
Since the next step is a firmware task, I'm reassigning to @sy2002
There should also be some menu options.
E.g. some users might actually have a real RTC connected via the tape port (just like on a "real" C64), while others would use the onboard RTC (i.e. emulate the GEOS-compatible RTC).
There is also the "confusion" about the "original" RTC on the R3 board, and a "modified" RTC. I don't know if it's possible to auto-detect which is the case. Probably, but if not, the user would have to make the selection.
Other TODOs for the VHDL part:
On the R4 board, the following sequence of operations will read out the date/time from the RTC:
Similar sequence (with few alterations) should work for both R3 variants.
One idea to auto-detect the RTC, is to monitor bit 2 of value 0x70F1 (Status.2 = NACK). This bit is set when there is no device responding.
Regarding detecting the board revision, I propose to use the SysInfo record. Specifically to introduce a new record: C_SYS_BOARD with value 0x0040. Then address 0 could be one of:
R3 : 0x0003 R4 : 0x0004 R5 : 0x0005
The VHDL part would then be responsible for populating the correct value (based on the generic G_BOARD).
The following lists the various I2C busses on the various board. For each bus there is a separate pair of SCL/SDA pins on the FPGA. The naming of the busses is from the schematic.
In order to support RTC on the R3 board, we must be able to communicate with both the "FPGA" bus (the internal RTC), and the "Grove" bus (the external RTC connected via PMOD).
@MJoergen : Makes sense for me - let's make it so. We already have a poll-loop in the firmware, so the firmware can rather easily ensure that the output signal stays updated. I am not sure if we should use control_d_o
or if it makes sense to introduce a new, dedicated signal that is meant to be used for RTCs-only.
Since we agree so far, I've now merged the branch "mfj_rtc_support" into the "develop" branch.
Since this whole RTC feature is very much core-dependent, I was thinking about having the necessary QNICE firmware be placed in the core-specific file CORE/m2m-rom/m2m-rom.asm
.
We could do that as a first step, and if we see a recurring pattern for other cores, then we can refactor as needed.
I am not fully getting why the firmware is core specific? If we had a new dedicated VHDL signal that is provided in main.vhd and that is for example formatted like the MiSTer format: Couldn't the CPP take it from there? And then the firmware would be non-core-specific? "Formatting" or "converting" this 64 bit std_logic_vector would be something that happened inside main.vhd (or mega65.vhd) and the firmware just make sure that it is updated at least 10x per second or so.
My bad. I agree with you. The process of reading the date/time from the (board-specific) RTC and converting to the "generic" date/time format is completely core-independent. It is only the subsequent processing of this generic date/time information from the framework, which is core-specific.
So the question remains, whether this should be presented as part of the control_d_o
signal, or whether we should introduce a new dedicated signal. I guess the latter makes most sense.
Since this is our "first" use of I2C, there are some architectural choices to be made. Specifically, should we support e.g. the Audio DAC? The Audio DAC works differently on the three boards, and it's connected differently too (i.e. different I2C bus and different I2C address). If we want to support the Audio DAC, should the "knowledge" of connectivity of the Audio DAC (i.e. which I2C bus and I2C address to use) be placed in VHDL and abstracted away, or should the QNICE firmware be in full control?
Since we're currently only using the I2C interface for the RTC, this decision is conveniently postponed. But the current implementation provides no abstraction, and expects the QNICE firmware to be in full control / knowledge of which devices (e.g. Audio DAC) are connected to which I2C busses and with which I2C addresses.
If technically possible at all going forward (and I admittedly did not think it through) I would prefer a completely board-revision agnostic firmware: Everything should be abstracted away on the VHDL part of the house. Now... ...this is my wishful thinking. You @MJoergen decide what is feasible at all. And since I will not be able to work at this issue any time soon: Feel free to refactor everything you've done so far and yes, we should add a dedicated (64-bit?) signal for RTC to the framework.
I understand your point of view. This will require some thought, because there are many ways to do this. But I understand that the firmware just "wants" the date/time somehow, without having to worry about the implementation (i.e. board specific) details. This is really the same requirement the core has. If this is done completely, then the firmware need not be involved at all.
Thinking out loud: The VHDL code could implement a small state machine that (upon request, e.g. after reset) goes through a pre-defined (but board-specific) set of reads/writes. This will be interfacing with the RTC module over the I2C bus using the new QNICE-to-I2C wrapper (implemented in i2c_controller.vhd
). In other words, we'll keep the QNICE register interface intact, because it is really useful for debugging.
So I will have three (or so) sets of QNICE accesses (read/writes), written in a small table, and fed into a small state machine. This entity would be called rtc_reader.vhd
and connect to the QNICE device interface (i.e. acting as a second "master" on the QNICE device interface). The output from this file would be the 65-bit date/time signal that can be fed directly to the core.
The only thing missing here is how to arbitrate between this new rtc_reader
and the existing QNICE code. I will need to write a QNICE-arbiter, that will delay one (or the other). For maximum robustness, the rtc_reader
should not be interrupted while speaking with the I2C device, in case the QNICE firmware starts writing to e.g. M2M$RAMROM_DEV
.
However, if I connect the arbiter only to the QNICE-2-I2C wrapper (instead of the entire general QNICE memory mapped I/O), then the QNICE firmware will not be impacted (assuming it is not itself speaking with the I2C devices). Then the memory location M2M$RAMROM_DEV
will not be in use either.
In conclusion, that is entirely doable, and the work so far is not in vain. I'm re-assigning to myself again.
👍🏻 Sounds promising :-)
@MJoergen ✅ WORKS IN GEOS, see screenshot:
One needs to install the RTC driver by copying the driver file on the local GEOS boot disk: https://github.com/mister-devel/C64_MiSTer/blob/master/releases/CP-ClockF83_1.3.D64
@MJoergen Please decide if you consider this as a long hanging fruit for 5.1. If not, then remove the 5.1 surge label.
This issue needs the following M2M hardware abstraction first, so that the three different RTCs on the MEGA65 are abstracted away from the C64 core (and so that other cores such as the ZX Spectrum can leverage the RTC, too):
https://github.com/sy2002/MiSTer2MEGA65/issues/29
Information on how to support an RTC on the C64