Wednesday 19 July 2023

Last minute changes to the R4 board

Well, we thought we had the R4 board all settled, and then the ghost of supply chains past came back to visit, requiring some changes to the R4 board to accommodate the lack of availability of one of the voltage regulators.

This is giving us the opportunity to make some further minor refinements based on feedback from our community, to make the cartridge port compatible with more cartridges.  This is because in the previous PCB revisions, I didn't realise that we needed to have the /RESET, /GAME, /EXROM, /IRQ, /NMI and potentially also /ROML and /ROMH lines should be bi-directional.

This requires one additional FPGA pin for each of these lines, but there are currently no spare pins, so we need a way to save 7 pins.

We can do this by replacing the 4 FPGA pins that are used to identify board revisions (but was only added on the R4 board), and the 4 FPGA pins used to connect to the DIP switches with an I2C IO expander. This would require only 2 pins (and allow 8 DIP switches instead of 4, which would be handy), thus saving 6 pins. We can then re-use DBG10 for the 7th one, thus meeting our needs.

To make this change we would need to:

0. The revised board shall be called R5, because it will not be bitstream compatible with R4, and is not feature identical, because of the cartridge port enhancements that will result from these changes.
1. Remove the CHS-04TA dipswitch, freeing FPGA pins N18, P19, T16 and U16.
2. Remove the REV_BIT0 - REV_BIT3 assignments, freeing FPGA pins L15, M16, F20 and T21.
3. Add a PCA9555 or similar 16-bit I2C IO expander with internal pull-up resistors (or alternatively, a PCA9535 or similar that lacks pull-up resistors, and then add external pull-up resistors as required to the signals described in (4) - (6).
4. Assign REV_BIT0 -- REV_BIT3 to 4 IO pins on the IO expander, to indicate REV5, i.e., with BIT1,3 tied to GND, and BIT0,2 unconnected to float to VCC.
5. Create new SUBREV_BIT0 -- SUBREV_BIT3 to indicate the sub-revision of the board, to indicate sub-revision 0 = "R5" without suffix letter, i.e., BIT0,1,2,3 tied to GND.
6. Add an 8-position dipswitch (or 2x8 pin 0.1" male header such as this to save cost), connected to the other 8 IO pins of the I2C IO expander.
7. Connect the I2C interface of IO expander to FPGA pins N18 and P19.
8. Disconnect F_C64_ROMH, F_C64_ROML, C64_ROMH and C64_ROML from U8.
9. Add an NC7SZ126P5X IC (similar to U30) to input and level shift C64_ROMH to F_C64_ROMH, but with pin 1 (OE, active high) connected to new signal F_C64_ROMH_DIR.
10. Add an NC7SZ126P5X IC (similar to U30) to input and level shift C64_ROML to F_C64_ROML, but with pin 1 (OE, active high) connected to new signal F_C64_ROML_DIR.
11. Add a 74AHCT1G125DBV gate to allow driving C64_ROMH low when new signal F_C64_ROMH_DIR is low, with pin 2 of the gate tied to existing signal F_C64_ROMH, to create a tri-stateable output driver, pulled high by the existing C64_ROMH pull-up resistor R99.
12. Assign the new signal F_C64_ROMH_DIR to FPGA pin T16.
13. Add a 74AHCT1G125DBV gate to allow driving C64_ROML low controlled by the new signal F_C64_ROML_DIR and existing signal F_C64_ROML, similar to (11).
14. Assign the new signal F_C64_ROML_DIR to FPGA pin U16.
15. Disconnect F_C64_RESET and C64_RESET from U9.
16. Add an NC7SZ126P5X IC (similar to U30) to level input and level shift C64_RESET to F_C64_RESET, but with pin 1 (OE, active high) connected to new signal F_C64_RESET_EN.
17. Add a 74AHCT1G125DBV gate to allow driving C64_RESET low controlled by the new signal F_C64_RESET_EN, similar to U30.
18. Assign the new signal F_C64_RESET_EN to FPGA pin T21.
19. Add a 74AHCT1G125DBV gate to allow driving C64_GAME low controlled by the new signal F_C64_GAME_EN and existing signal F_C64_GAME, similar to U30.
20. Assign the new signal F_C64_GAME_EN to FPGA pin L15.
21. Add a 74AHCT1G125DBV gate to allow driving C64_EXROM low controlled by the new signal F_C64_EXROM_EN and existing signal F_C64_EXROM, similar to U30.
22. Assign the new signal F_C64_EXROM_EN to FPGA pin M16.
23. Add a 74AHCT1G125DBV gate to allow driving C64_NMI low independently, controlled by the new signal F_C64_NMI_EN and existing signal F_C64_NMI, similar to U30.
24. Assign the new signal F_C64_NMI_EN signal to FPGA pin F20.
25. Add a 74AHCT1G125DBV gate to allow driving C64_IRQ low independently, controlled by the signal DBG10 on pin 1 of the gate, and existing signal F_C64_IRQ on pin 2 of the gate, similar to U30.
26. Replace R41 and R42 with 3.3K resistors instead of the current 4.7K resistors. This will then exactly match the value of those on the C64.

That list looks long, but all it really does is fix those seven signals to be independently bidirectionally controllable at high-speed. When I say high-speed, I mean at full cartridge speed of ~2MHz.  The routing on the PCB can, however, be as byzantine as is expedient, because they are still very low speed in the grand scheme of things.

The next step is to submit this to Trenz Electronic, so that they can sanity check it for us, and confirm if the changes are feasible.

Sunday 9 July 2023

Enabling the Super-Cap on the MEGA65 R4 PCB

The MEGA65 R4 PCB uses a different RTC chip than on the old R3A board.  The R4 uses a newer chip, the RTC-RV-3032-C7. This chip uses less power, so a battery will last longer.  Also, it supports the use of a "super capacitor", if you don't have a battery, and the MEGA65 R4 board is designed to use such an arrangement (it also has a socket for a CR2032 battery as well).


We already have the new RTC chip working on the R4, and you can set the time and date on it, without problem. But what we have not yet implemented is enabling the super capacitor.  This requires setting an EEPROM register that enables the charge pump to the super capacitor, and selecting the voltage and current for this.

This is all done using the Power Management Unit (PMU), which is configured in register $C0.  We have this accessible via $FFD71D0 on the R4 board.  The only problem is, writing to it doesn't seem to have any effect. So I need to investigate this.

The datasheet can be found here.

Page 72 of the datasheet explains the correct process for changing this register:

Edit the Configuration settings (example, when write protection is enabled (EEPWE = 255)):
1. Enter the correct password PW (PW = EEPW) to unlock write protection
2. Disable automatic refresh by setting EERD = 1
3. Edit Configuration settings in registers C0h to C5h (RAM)
4. Update EEPROM (all Configuration RAM EEPROM) by setting EECMD = 11h
5. Enable automatic refresh by setting EERD = 0
6. Enter an incorrect password PW (PW ≠ EEPW) to lock the device

We don't have write protection enabled, so far as I am aware. So we need to set the EERD bit, which is in bit 2 of register $10, which is mapped at $FFD7120, update the value, then write $11 into the EECMD register, which is register $3F, which is mapped at $FFD714F.

So let's try that.

Hmm... Setting $FFD7120 doesn't seem to work.  I've just double-checked the VHDL, and it looks like all the registers should be writeable. But now that I am testing it, I am finding that none of the registers are writeable. But I'm pretty sure I had them writeable in the past -- at least the ones for the time and date.  I did have to adjust the register writing code when I added support for writing to higher-numbered registers, so it's possible I have messed something up.

Now I need to go back in time and find where this problem came in, so that I can confirm this theory. This requires doing some POKE and PEEKs using about 100 different bitstream versions.  So I really want to automate this.  So here's the crazy shell script I wrote to do this for me:

#!/bin/tcsh -fx

foreach bit ( `ls -1t bin/mega65r4*.bit` )
  echo $bit
  m65 -b $bit
  sleep 4
  m65 -r rtctest.prg
  sleep 2
  monitor_save -a 0800:0802 foop
  hexdump -C foop | cut -c11-18 > foop2
  set len=`gzip < foop2 | wc -c`
  if ( $len != 27 ) then
    echo "Bingo! $bit"
    set userinput=$<

This basically loads each bitstream, then loads a little BASIC program that tries to set and read back the seconds value of the RTC in $FFD7110.  It also copies the initial value from $FFD7110 to $0800, and then the PEEK of $FFD7110 after writing to different values to it to $0801 and $0802.  If the RTC register writing is working, then at least two of those results will be different.  I couldn't be bothered writing a program to compare those three numbers, so I just used a fun hack I know: Check if the compressed size is different to if it contains 3 identical numbers. 

So with this ugly little script, I have it automatically testing a bunch of bitstreams while I write this blog.  Hopefully it will pick one up soon. If it doesn't, then I have to assume that writing to the RTC has never worked -- or that it is write-protected or something like that.

Hmm.. it's tried bitstreams all the way back to mid-March (its late-June now), with no luck. So I am beginning to suspect that I might have enabled some write-protect kind of thing.

The process of unlocking the write-protect is listed here on page 114 of the datasheet:

If the write protection function is enabled by writing 255 in register EEPWE (EEPROM CAh), it remains possible to
read all the registers except the EEPROM registers. The EEPROM registers cannot be read because it cannot be
written to the EE Address and EE Command registers. If the function is not enabled, read and write are possible for
all corresponding registers.
If the write protection function is enabled, it is necessary to first write the correct 32-Bit Password PW (PW = EEPW)
(Unlock), before any attempt to write in the RAM registers and to read and write in the EEPROM registers.
Once the user is finished with the write access and subsequently the write protection is still enabled or enabled again
(by writing 255 in EEPROM register EEPWE), it is necessary to write an incorrect password (PW ≠ EEPW) into the
Password PW registers in order to write-protect (Lock) the registers. See program sequences below and
Enable write protection:
1. Initial state (POR): WP-Registers are Not write-protected (EEPWE ≠ 255)
Reference password is stored in the RAM mirror of EEPW (addrs C6h to C9h)
2. Disable automatic refresh by setting EERD = 1
3. Enable password function by entering EEPWE = 255 (RAM mirror address CAh)
4. Enter the correct password PW (PW = EEPW) to unlock write protection (RAM addresses 39h to 3Ch)
5. Update EEPROM (all Configuration RAM  EEPROM) by writing 11h to EECMD
6. Enable automatic refresh by setting EERD = 0
7. Enter an incorrect password PW (PW ≠ EEPW) to lock the device (RAM addresses 39h to 3Ch)
8. Final state: WP-Registers are Write-protected by password (EEPWE = 255)

Disable write protection:
1. Initial state (POR): WP-Registers are Write-protected by password (EEPWE = 255)
Reference password is stored in the RAM mirror of EEPW (addrs C6h to C9h)
2. Enter the correct password PW (PW = EEPW) to unlock write protection (RAM addresses 39h to 3Ch)
3. Disable automatic refresh by setting EERD = 1
4. Disable password function by entering EEPWE ≠ 255) (RAM mirror address CAh)
5. Update EEPROM (all Configuration RAM  EEPROM) by writing 11h to EECMD
6. Enable automatic refresh by setting EERD = 0
7. Final state: WP-Registers are Not write-protected (EEPWE ≠ 255)

This sounds quite feasible to be the issue here.

After rebuilding a bitstream that fixed an unrelated bug preventing writing to registers on the RTC, I have been able to confirm that this procedure for un-write-protecting the RTC registers works:

10 A=$FFD7110
20 POKE A + $39, ASC("E")
30 POKE A + $3A, ASC("E")
40 POKE A + $3B, ASC("P")
50 POKE A + $3C, ASC("W")

Does indeed disable write-protection of the registers temporarily, allowing me to set the RTC time and date registers. (I believe I probably accidentally enabled write protection at some point.)

So next step is to update the EEPROM configuration registers. After some fiddling about, the following program seems to be able to set the PMU register for me:

   5 W=0.01
   10 A=$FFD7110
   15 SLEEP W
   20 POKEA+$39,ASC("E")
   25 SLEEP W
   30 POKEA+$3A,ASC("E")
   35 SLEEP W
   40 POKEA+$3B,ASC("P")
   45 SLEEP W
   50 POKEA+$3C,ASC("W")
   55 SLEEP W
   65 SLEEP W
   75 SLEEP W
   85 SLEEP W
   95 SLEEP W
  115 SLEEP W

The SLEEP statements are to ensure the I2C memory mapping system of the MEGA65 has time to commit each write, before attempting the next. So if you ignore those, we see that I enter the default RTC password of EEPW, then stop the EEPROM shadow ram reading, so that I can modify the shadow RAM contents for the EEPROM registers, then I set the trickle-charge settings in the PMU register ($13 into register $C0), write it to the EEPROM, and then tidy up after ourselves.  Finally, I read from the EEPROM shadow RAM to confirm that our setting has taken effect.

Running it takes only a second or so, and then we see something like this:

Great -- so assuming I have correctly understood the settings to charge the super-capacitor, and that it all actually works, my super capacitor should now be being charged. This means, I should be able to turn the power off to the MEGA65 R4 board for a while (I purposely have no battery installed), and it should still retain the time and date. 

What I don't know, is how long it will take for the super-cap to charge up enough to work. Also, I don't know if I have really put the right settings into the PMU register to use the super-cap.  So while I give the super-cap some time to charge up, let's take a look at the PMU register bits and related things.

First, you can tell if the RTC lost power on boot, by checking bit 1 of $FFD711D. If set, then the RTC didn't have power. This can be cleared by writing to it, e.g., POKE $FFD711D,0.  

Okay, so I have done that. Let's power cycle, and see if it gets asserted still. But first, I think I'll flash this fixed bitstream for the MEGA65 R4 board, so that I don't have to keep loading it on power on.

Done. I've also read through the datasheet for the RTC, and it looks like putting either $13 or $23 into the PMU register should work. The difference between the two values affects only the way that it detects when to switch to backup power (level switching or direct switching modes). So far, neither seems to be effective, which makes me think that something is not allowing the capacitor to charge. I might need to pull the board out of the case, so that I can get to the pins of the super capacitor, and see what the voltage there is doing. 

Looking at the schematic, there are no components between the super capacitor and the RTC, so there doesn't seem to be any possibility for something outside of the RTC or super capacitor to be the problem. The RTC is ticking, so I am assuming either the super capacitor is dead (unlikely) or that I am somehow not programming it correctly (more likely).  So let's see if it is charging, then...

I've pulled the board out, and see 0.39V on the super capacitor. After a minute or so, it climbed to 0.40V. It's climbing by about 0.02V per minute. So this means that I probably do have it all setup correctly, and that it will just take a lot longer than I had originally anticipated for the super cap to get to a usable voltage (about 2V).  It should take about another 1 - 2 hours to climb to that level, so I'll leave it charging for a while and do some other stuff, and then come back to it and see if it has charged enough to keep time.

Yes, that was all it needed -- enough time to charge the capacitor enough to reach the voltage required by the RTC chip.  Based on this, I'd recommend leaving a MEGA65 R4 on all day or over-night to put enough initial charge into the super capacitor to allow the RTC to keep time.  

The next logical question is how long it can hold the time for when powered off.  To get an idea of this, I've just turned the machine off, and recorded 2.45V on the super capacitor at 14:15.  I'll check the voltage again in a couple of hours, and see how much it as dropped. From that, we should be able to come up with a fairly decent estimate of the RTC retention time when using just the super capacitor. After 121 minutes, it is down to 2.32V. This means ~(2.54 - 2.32 / 2) V / hour, i.e., about 0.11V / hour. Assuming fully charged will be around 4.5V, and the minimum viable voltage is 2V, 2.5V / 0.11V/hour = ~20 hours -- assuming that the charge consumption is constant regardless of voltage, which may not be the case.

Anyway, I'll set it charging overnight, so that I can see what the actual maximum voltage is, and then see how long that voltage takes to decay. The datasheet suggests "days to weeks" when using a super capacitor -- but without indicating the capacity of super capacitor that this range corresponds to.  My gut feeling is that the super capacitor will provide at most a few days, based on the data so far -- but we will see when I have fully charged it ...

So overnight it charged up to 4.40V. I am now going to let it discharge for a while, and see if it still drops at 0.11V / hour from full, or if its a bit slower.  Power turned off to the MEGA65 R4 board at 12:41 on Sunday. I'll take some measurments whenever I come by my office and remember:

Sunday 13:21 - 4.39V

Sunday 14:31 - 4.38V

Sunday 15:36 - 4.37V

Sunday 16:54 - 4.35V

Sunday 05:48 - 4.27V

Monday 17:23 - 4.22V

Tuesday 17:21 - 4.14V

At this rage, 0.08V/day, and a critical voltage of 2V, this suggests it might last 30 days.  Will keep an eye on it over coming days...

Friday 21:02 -- 3.94V

Well, its still continuing at 0.08V per day, which is pretty good.  It's now close to a week since I turned it off, and it has a lot more time to go before it goes flat.

Tuesday 20:29 -- 3.74V

Yup, continuing at about that rate. After 9.5 days, it's gone from 4.40V to 3.74V = ~0.07V/day average over the full time.  I'll leave it running a few more days yet...

Sunday 19:20 - 3.55V

So that's just over 15 days, with a drop of 4.40V - 3.35V = 0.06V / day on average. Given the critival voltage of ~2V, this is pointing to a maximum retension without power of 4.4V - 2V = 2.4V / 0.06V / day = 40 days. I'd allow some safety margin on that, and say that 3 to 4 weeks should be a reasonable bet.  

Anyway, that's more than enough to confirm that the supercap works.