Thursday 20 February 2020

Making the Real-Time Clock Tick

The Real-Time Clock (RTC) chip that Trenz chose for the MEGA65 main board is different to the one we have on the MEGAphone.  While the one on the MEGAphone proved easy to set, the one in the MEGA65 is resisting my efforts to be able to set the time.

The datasheet explains that you have to set the WRTC (Write Real-Time Clock) bit, before the time can be set.  The only problem is, is that this doesn't actually work. My best guess, is that the RTC registers have to be all set in a single write, for the write to take effect, whereas my SPI controller writes only one byte per transaction.

Although, it might not be required after all.  This Linux driver for the RTC chip writes one byte at a time, and seems to indicate that writing to any of the RTC clock registers should start it running.  However, nothing I have tried actually works to trigger this to occur.

Reading through the data sheet again, I was reminded that the RTC chip is very picky about writing to the clock registers:  It requires that an I2C STOP signal occurs immediately after writing to the clock registers, so that partial writes will be ignored to help protect the integrity of the clock.

This led me through quite an adventure of tracking down and fixing a bunch of I2C management errors in mega65_i2c.vhdl, which collectively caused that problem, along with having the potential to cause some other I2C glitches.   What it boiled down to, was that I was not allowing the I2C bus to go idle between the loop that reads all register values for displaying in the memory map, and when it writes a new value when requested by the CPU -- and vice versa.

With that fixed, I was finally seeing the STOP condition, which consists of the SDA line going high, while SCL stays high, as indicated by the vertical red-ish line in this simulation trace:


With that fixed and synthesised, I was then finally able to write to the RTC clock, and set it ticking (it doesn't start ticking until it has been initially set).

To make it easier for people to use, I have added getrtc() and setrtc() functions to the MEGA65 libc that I am writing. I have also added some initial documentation of the registers to the MEGA65 Book.  I also updated the i2cstatus test program to show the current RTC (and other target specific information).  It also allows editing of the RTC value:






 Thanks to the libc functions that I wrote, the code to read and display the current time and date is quite simple:

void show_rtc(void)
{
    getrtc(&tm);
    

    printf("Real-time clock: %02d:%02d.%02d",
           tm.tm_hour,tm.tm_min,tm.tm_sec);
    printf("\n");

    printf("Date:            %02d-",tm.tm_mday+1);
    switch(tm.tm_mon) {
    case 1: printf("jan"); break;
    case 2: printf("feb"); break;
    case 3: printf("mar"); break;
    case 4: printf("apr"); break;
    case 5: printf("may"); break;
    case 6: printf("jun"); break;
    case 7: printf("jul"); break;
    case 8: printf("aug"); break;
    case 9: printf("sep"); break;
    case 10: printf("oct"); break;
    case 11: printf("nov"); break;
    case 12: printf("dec"); break;
    default: printf("invalid month"); break;
    }
    printf("-%04d\n",tm.tm_year+1900);

}


The main trick there, is that we will need to use a MEGA65 Enhanced DMA operation to fetch the RTC registers, because the RTC registers sit above the 1MB barrier, which is the limit of the C65's normal DMA operations.  The easiest way to do this is to construct a little DMA list in memory somewhere, and make an assembly language routine that uses it.  Something like this (using BASIC 10 in C65 mode):

10 RESTORE 110:FORI=0TO43:READA$:POKE1024+I,DEC(A$):NEXT:BANK 128:SYS1042
20 S=PEEK(1024):M=PEEK(1025):H=PEEK(1026)
30 D=PEEK(1027):MM=PEEK(1028):Y=PEEK(1029)+DEC("2000")
40 IF H AND 128 GOTO 80
50 PRINT "THE TIME IS ";RIGHT$(HEX$(H AND 63),1);":";RIGHT$(HEX$(M),2);".";RIGHT$(HEX$(S),2)
60 IF H AND 32 THEN PRINT "PM": ELSE PRINT "AM"
70 GOTO 90
80 PRINT "THE TIME IS ";RIGHT$(HEX$(H AND 63),1);":";RIGHT$(HEX$(M),2);".";RIGHT$(HEX$(S),2)
90 PRINT "THE DATE IS";RIGHT$(HEX$(D),2);".";RIGHT$(HEX$(MM),2);".";HEX$(Y)
100 END
100 DATA 0B,80,FF,81,00,00,00,08,00,10,71,0D,20,04,00,00,00,00
110 DATA A9,47,8D,2F,D0,A9,53,8D,2F,D0,A9,00,8D,02,D7,A9
120 DATA 04,8D,01,D7,A9,00,8D,05,D7,60

This program works by setting up a DMA list in memory at $0400 (unused normally on the C65), followed by a routine at $1012 ( = 1,042 in decimal) which ensures we have MEGA65 registers unhidden, and then sets the DMA controller registers appropriately to trigger the DMA job, and then returns.  The rest of the BASIC code PEEKs out the RTC registers that the DMA job copied to $0400 -- $0407, and interprets them appropriately to print the time.
The curious can use the MONITOR command, and then D1012 to see the routine.

If you want a running clock, you could replace line 100 with GOTO 10.  Doing that, you will get a result something like the following:



If you first POKE0,65 to set the CPU to full speed, the whole program can run many times per second. There is an occasional glitch, if the RTC registers are read while being updated by the machine, so we really should de-bounce the values by reading the time a couple of times in succession, and if the values aren't the same both times, then repeat the process until they are. This is left as an exercise for the reader.

Finally, I updated the auto-documentation in the VHDL source, so that the new registers will be automatically documented in the MEGA65 Book, which is already getting close to 500 pages.  I'm expecting that the MEGA65 Book will be close to 1,000 pages when complete -- not that this should be scary, but rather reflect the depth of documentation that we want to provide potential users and developers of the machine.

Sunday 16 February 2020

Getting the external microSD card slot working

The internal SD card slot has been working for ages, but we never managed to get the external one working when we had the sprint to bring the MEGA65 R2 board up last year.  So I am trying to finally fix this.

It's been an interesting problem, with a few unexpected things.

First, I had to understand how the microSD card interface and MAX10 JTAG interface share the microSD connector.  Trenz designed it this way, so that if you hold the reset button in on the side of the MEGA65, you can connect a JTAG breakout into the microSD slot in order to programme the MAX10 FPGA.

Initially I thought that I had to manually direct the pins, or control the JTAG enable pin.  However, it turned out a bit simpler: The MAX10 JTAG pins and the main Xilinx FPGA microSD card pins are just both connected in parallel on the microSD connector. This means if one device tri-states the lines, the other can control it.

However, it wasn't exactly that simple, as it still didn't work.  Looking closer, a couple of the microSD slot lines are connected not to the MAX10's JTAG interface, but to some other IO lines.  This meant that the MAX10 was trying to drive those lines, in particular the Card Select line, high, regardless of what the Xilinx FPGA was doing.  I realised that this had to be the case when I found that increasing the drive strength of the Xilinx FPGA on this pin to 24mA enabled it to be controlled. But this is not a great solution, as it means that almost 100mW of extra power is being disipated through the cross-driving, and it could eventually damage something.

So this meant adjusting the program for the MAX10 FPGA to tri-state the pin in question.  The change was only one line, to set the line to input.  However, it has been ages since I have programmed the MAX10 FPGA, and it took me a few hours of trying things to remember that I had to run jtagd as root before running the program.sh or flash.sh scripts in https://github.com/MEGA65/mega65-r2-max10.  I've now added those commands into the scripts, so that I don't have to remember this in future.

I also got tripped up by the fact that the Xilinx FPGA tells the MAX10 to reconfigure during its startup process, which meant that my updated bitstream was getting thrown away without me noticing.  This was fine, once I realised it, as all I had to do was flash the new bitstream into place.

At this point, I could again control the Card Select line without having to overdrive the line. However, the SD card interface was still not working.  Probing with the oscilloscope revealed that the clock pin was not being driven.  Digging through, it looks like I made the SD card interface switching logic to assume a common clock for both SD card busses, but never actually tied them together.  So I fixed this by making the clock on the correct bus toggle, instead of having them both toggle all the time.  Again the changes were relatively small.

So now its the waiting game again, while I resynthesise the design, in the hope that it will all work.  But even if it doesn't work immediately, I am feeling much more confident, as I have a good understanding of all of the relevant parts.

Two more problems remained: The Chip Select line wasn't being driven, and there were some related "signal plumbing" problems, that prevented things from working.

Now, finally, it is possible to run a MEGA65 from the external microSD card slot! :)

Obligatory photos showing the MEGA65 booting from the external microSD card: