Fixing famicom with buggy RP2A03 revisionless CPU

Discuss hardware-related topics, such as development cartridges, CopyNES, PowerPak, EPROMs, or whatever.
User avatar
krzysiobal
Posts: 1221
Joined: Sun Jun 12, 2011 12:06 pm
Location: Poland

Fixing famicom with buggy RP2A03 revisionless CPU

Post by krzysiobal »

I got famicom with broken joystick cable that was needed to be fixed.
The famicom needed much more interest, its bottom is smooth (right), not like most (left) of the famicoms I met before.
//shell-bottom.jpg
Image

The PCB also has traces on both sides, not obscured by the soldermask like I always spotted.
//pcb-top.jpg
Image

But to my surprise, it has problems in playing many pirate famiclone cartridges. LIke this Doki MMC3 Doki Yuuenchi cartridge that shakes or does IRQ split scroll not when it is needed. It turns out the software is not a culprit as thie same game played in this console on my KrzysioCart flashcart works fine. It passes CPU-RAM and PPU-RAM tests, also games like BattleToads works just fine.
//doki_doki_bad.jpg
Image

//doki_doki_bad.gif
doki_doki_bad.gif
Looks like the RP2A03 revisionless CPU is causing some troubles. Its M2 duty cycle is 76.34% (19/24) and that probably fools the cartridge scanline detector.
//clk.png
Image

It has also troubles running Everdrive
//ed-bad.jpg
Image

I would replace the CPU with another NTSC model but I dont have any of them so I figured out why not to replace it with UA6527P (dendy CPU) multiply the crystal frequency that is fed to CPU with 16/12 = 4/3 = 1.33.

I already did similar approach using 100MHz crystal and CPLD to create edges of 33ns clock cycles that are in sync with 21.7M .
viewtopic.php?p=299076&hilit=ua6527p#p299076

Disadvantages are:
- requirement of two crystals (100MHz + 21.7MHz)
- Clock is generated using 74HCU04 that requires a few resistors/capacitors
- output clock have jitter

One of the users (borishim) suggested using ICS501 as a cheap frequency multiplier so I bought few of them and now it was good moment to test them.
Advantages are:
+ only single 21.7MHz crystal is required
+ no extra resistors/capacitors
+ output clock does not have any jitter

So I designed a small PCB with that chip + EPM3064 that divides that frequency by two different factors, one is fed to CPU and the other to PPU:
//proj-sch.png
//proj-pcb.png
//proj-pcb-soldered.jpg
Image Image Image

In fact, this CPLD can be selected by one of its pin to be used with either old UA6527P (16div) or the other with new UA6527P (15div).

Code: Select all

                              +-------------------+  
----------------------------->|mode               | +----+--+--|---------------+------------+-------------------+
          +---------------+   |                   | |mode|S1|S0| cpu_clk_out   | ppu_clk_in | note              |
          |             S1|<-<|                   | +----+--+--+---------------+------------+-------------------+
          |             S0|<-<|        cpu_clk_out| |0   | 0| 1|clk_in * 5 / 4 | clk_in / 5 |new-UA6527P (15div)|
-21.477M->|clk_in  clk_out|>->|clk_in  ppu_clk_Out| |1   | 0| 0|clk_in * 4 / 3 | clk_in / 4 |old-UA6527P (16div)|
          +---------------+   +-------------------+ +----+--+--+---------------+------------+-------------------|
           ICS502               EPM3064
I spend some time inventing how to divide the clock by 3 or 5 and finally got the idea:

Code: Select all


+-------------------------------------------------------+-------------------------------------------------------+
|                  CLOCK DIVISION BY 3                  |               CLOCK DIVISION BY 5                     |
+-------------------------------------------------------+-------------------------------------------------------+
| clk_in  ___---___---___---___---___---___---___---___ | clk_in  ___---___---___---___---___---___---___---___ |
| pos_cnt 000111111222222000000111111222222000000111111 | pos_cnt 000111111222222333333444444000000111111222222 |
| neg_cnt 000000111111222222000000111111222222000000111 | neg_cnt 000000111111222222333333444444000000111111222 |
| clk_out ---------_________---------_________--------- | clk_out ---------_______________---------------______ |
|                                                       |                                                       |
| if rising_edge(clk_in) then                           | if rising_edge(clk_in) then                           |
|     if pos_cnt /= 2 then                              |     if pos_cnt /= 4 then                              |
|         pos_cnt <= pos_cnt + 1;                       |         pos_cnt <= pos_cnt + 1;                       |
|     else                                              |     else                                              |
|         pos_cnt <= "00";                              |         pos_cnt <= "000";                             |
|     end if;                                           |     end if;                                           |
| end if;                                               | end if;                                               |
|                                                       |                                                       |
| if falling_edge(clk_in) then                          | if falling_edge(clk_in) then                          |
|     if neg_cnt /= 2 then                              |     if neg_cnt /= 4 then                              |
|         neg_cnt <= neg_cnt + 1;                       |         neg_cnt <= neg_cnt + 1;                       |
|     else                                              |     else                                              |
|         neg_cnt <= "00";                              |         neg_cnt <= "000";                             |
|     end if;                                           |     end if;                                           |
| end if;                                               | end if;                                               |
|                                                       |                                                       |
| clk_out <=                                            | clk_out <=                                            |
|     '0' when pos_cnt = "10" or neg_cnt = "10" else    |     '0' when pos_cnt = "010" or pos_cnt = "011" or    |
|     '1';                                              |              neg_cnt = "010" or neg_cnt = "011" else  |
|                                                       |     '1';                                              |
+-------------------------------------------------------+-------------------------------------------------------+
Similar solutions can be used for div3 and div4 though for even factors it's easier just to change output clock every single (div2) or every second (div4) rising edge of input clock.

Code: Select all

+-------------------------------------------------------+-------------------------------------------------------+
|                  CLOCK DIVISION BY 2                  |                  CLOCK DIVISION BY 4                  |
+-------------------------------------------------------+-------------------------------------------------------+
| clk_in  ___---___---___---___---___---___---___---___ | clk_in  ___---___---___---___---___---___---___---___ |
| pos_cnt 000111111000000111111000000111111000000111111 | pos_cnt 000111111222222333333000000111111222222333333 |
| neg_cnt 000000111111000000111111000000111111000000111 | neg_cnt 000000111111222222333333000000111111222222333 |
| clk_out ___------______------______------______------ | clk_out ___------------____________------------______ |
|                                                       |                                                       |
| if rising_edge(clk_in) then                           | if rising_edge(clk_in) then                           |
|     if pos_cnt /= 1 then                              |     if pos_cnt /= 3 then                              |
|         pos_cnt <= pos_cnt + 1;                       |         pos_cnt <= pos_cnt + 1;                       |
|     else                                              |     else                                              |
|         pos_cnt <= "0";                               |         pos_cnt <= "00";                              |
|     end if;                                           |     end if;                                           |
| end if;                                               | end if;                                               |
|                                                       |                                                       |
| if falling_edge(clk_in) then                          | if falling_edge(clk_in) then                          |
|     if neg_cnt /= 1 then                              |     if neg_cnt /= 3 then                              |
|         neg_cnt <= neg_cnt + 1;                       |         neg_cnt <= neg_cnt + 1;                       |
|     else                                              |     else                                              |
|         neg_cnt <= "0";                               |         neg_cnt <= "00";                              |
|     end if;                                           |     end if;                                           |
| end if;                                               | end if;                                               |
|                                                       |                                                       |
| clk_out <=                                            | clk_out <=                                            |
|     '0' when pos_cnt = "0" else                       |     '0' when pos_cnt = "00" or neg_cnt = "11" else    |
|     '1';                                              |     '1';                                              |
|                                                       |                                                       |
+-------------------------------------------------------+-------------------------------------------------------+
After replacing "odd" RP2A03 CPU with UA6527P (old) installing clock divider mod, eveyrhing started to work fine!
//final.jpg

Image
You do not have the required permissions to view the files attached to this post.
Image My website: http://krzysiobal.com | Image My NES/FC flashcart: http://krzysiocart.com
lidnariq
Site Admin
Posts: 11803
Joined: Sun Apr 13, 2008 11:12 am

Re: Fixing famicom with buggy RP2A03 revisionless CPU

Post by lidnariq »

Some early motherboards have a resistor-capacitor-diode pulse shaper to fix up the too-early M2. Might be worth seeing if that's practical...
User avatar
Quietust
Posts: 2028
Joined: Sun Sep 19, 2004 10:59 pm

Re: Fixing famicom with buggy RP2A03 revisionless CPU

Post by Quietust »

Way back when I traced and simulated the letterless RP2A03's clock divider circuit, I came up with a 17/24 duty cycle, though I don't recall what was actually measured from the real hardware.

The fact that you got a 19/24 duty cycle suggests one of several possibilities:
  • There were multiple revisions of the letterless RP2A03 with different clock dividers
  • The CPU's phi2 output just happens to be delayed by 47ns (i.e. two XTAL edges) compared to its phi0 input, which would suggest a similar error in the 2A03G's M2 duty cycle; underclocking the chip would probably make this delay disappear, though it can't be too slow otherwise some of the dynamic logic will fail
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.