I want to implement an MMC3 clone based on a CPLD. I'm planning to use a Xilinx CoolRunner 2 CPLD (with transceivers for 5V <--> 3.3V translation). So far I have implemented (but have not yet tested) the bankswitching logic. I was going to implement the scanline counter and IRQ logic, and 2 doubts arose (more will come for sure as I continue implementing this, and start testing it with a proper test bench):
1.- IRQ (for the normal MMC3) is generated when counter is 0. But how long does the interrupt remain active? Does it stay low all the time count is 0 (a full scanline), until disabled by writing to $E000? Or does it stay low for several CPU cycles and then it goes high automatically? Or is there any other way to acknowledge the interrupt without disabling it (maybe a write to $E001?)?
2.- If I enable interrupts when the count is 0 (e.g. suppose an interrupt happens, then I disable interrupts with a write to $E000 and re-enable writing to $E001 before counter is changed), should an interrupt be immediately generated?
The IRQ line stays low until the software acknowledges it by writing to E000, and writing to E000 is the only known way to acknowledge the IRQ.
Interrupts are only generated when the scanline counter is clocked by PPU A12, so writing to E000 and E001 in quick succession shouldn't generate an IRQ.
Thanks for the replies!
Drag wrote:
Interrupts are only generated when the scanline counter is clocked by PPU A12, so writing to E000 and E001 in quick succession shouldn't generate an IRQ.
That makes sense, but reading from the wiki I had the impression that it stayed low (for chips with "normal" behavior):
Quote:
- All MMC3A's and non-Sharp MMC3B's will generate only a single IRQ when $C000 is $00. This is because this version of the MMC3 generates IRQs when the scanline counter is decremented to 0. In addition, writing to $C001 with $C000 still at $00 will result in another single IRQ being generated. In the community, this is known as the "alternate" or "old" behavior.
- All MMC3C's and Sharp MMC3B's will generate an IRQ on each scanline while $C000 is $00. This is because this version of the MMC3 generates IRQs when the scanline counter is equal to 0. In the community, this is known as the "normal" or "new" behavior.
Could it be an error in the wiki?
You can refer to FPGANES sourcecode (mmu.v)
here:
https://github.com/strigeus/fpganes/tree/master/srcAnd here is my MMC3 card with a xc95144xl
The wiki's correct as far as we know, but it could be worded a little better.
The counter being zero does not continuously assert the IRQ line, it's
just when the counter tries to decrement but is already zero, which can only happen once per scanline. Once E000 is poked, the mapper releases the IRQ line and clears any interrupt status in the counter. When E001 is poked, that only tells the mapper that it's allowed to assert the IRQ line again, but it won't do that until the counter underflows again.
Thanks a lot, I think everything is clear now!
@byemu: Wow, nice cart! Is your implementation open? It would be nice to browse the HDL sources! About the FPGANES sources you pointed out, I don't know verilog, but I think it's readable enough to understand how it works.
BTW, the bankswitching I implemented in VHDL is 100% asynchronous (it uses evil latches) to minimize resource usage (as CPLDs are small). But for the scanline counter I'll have to go synchronous. So the question is: Should I use the 21.48 MHz signal available on the cart connector as CPLD clock? Is it stable? Does it run all the time? Or should I use the CIC CLK (I think I read somewhere it's 4 MHz, so I think it should be fast enough for sampling the NES buses)?
You should be able to generate the clock signal for the scanline counter using just PPU/RD and PPU A12; see the
last comment in this section on the wiki's talk page.
As far as clock sources ... on the one hand, the 21.5MHz clock source ought to be reliable? But neither the 21.5MHz clock nor the 4MHz clock is available on the famicom (and probably most famiclones) and the 4MHz clock isn't available on top-loading NESes.
doragasu wrote:
Thanks a lot, I think everything is clear now!
@byemu: Wow, nice cart! Is your implementation open? It would be nice to browse the HDL sources! About the FPGANES sources you pointed out, I don't know verilog, but I think it's readable enough to understand how it works.
BTW, the bankswitching I implemented in VHDL is 100% asynchronous (it uses evil latches) to minimize resource usage (as CPLDs are small). But for the scanline counter I'll have to go synchronous. So the question is: Should I use the 21.48 MHz signal available on the cart connector as CPLD clock? Is it stable? Does it run all the time? Or should I use the CIC CLK (I think I read somewhere it's 4 MHz, so I think it should be fast enough for sampling the NES buses)?
Do not need a external clock, m2 is enough.
You can google "NES FunkyFlashCart" to find another mmc3 code(not verilog)
I already saw the FunkyFlashCart, but AFAIK sources are in "schematic" format, not HDL (or at least I could only find schematics).
About the clocks, I gave it another thought and you might be right, M2 should be enough since it's frequency is much higher than "PPU A12 frequency".
Again, thanks!
Finished implementation and simulation. If I understood properly MMC3 documentation, I think everything should run fine. Here are the simulation results:
Just in case anyone is wondering, here is the port mapping:
There is a small problem with the transceiver output enable (n_toe line), but other than that, I think it it behaves correctly. Corrections and/or suggestions are welcome.
Your IRQ handler isn't quite right.
For MMC3 (not RAMBO-1, not MC-ACC), /IRQ should be asserted as PPU A12 rises (and other conditions; the point is: not synchronized to M2). It should remain asserted until acknowledged by the CPU.
Thanks for taking the effort of interpreting the waves and making corrections!
Although it doesn't look like on the simulation, IRQ is asserted when PPU A12 rises. It doesn't look like because it's delayed one M2 clock cycle. This causes it to fire when PPU A12 is lowered on the simulation, even though the falling edge of A12 is not what it's causing the IRQ. On a real system (or on a simulation in which PPU A12 is not changed that fast) you should see the IRQ on the rising edge of PPU A12 (with a small delay).
If you are wondering why I have delayed it, the reason is simple: because as I think PPU A12 can change asynchronously to M2, I decided to sample it to avoid metastability. As PPU A12 frequency should be way lower than M2, M2 clock should be enough for sampling this signal. But sampling it causes a 1 cycle delay.
Also as you state, IRQ is kept low until acknowledged by the CPU (see the write to IRQ disable, $E000, caddr = "110", followed by a write to IRQ enable, $E001, caddr = "111"). If I run the simulation a bit more time, I can also see it remains low at the end of the simulation.
So, if I understood correctly, the problem is I'm synchronizing to M2 and I shouldn't. But my question is: is it really that critical? I'm only delaying the interrupt routine one CPU clock cycle. Will a single clock cycle delay cause problems with games?
Given that the NTSC CPU and PPU can power up in any one of 4 different alignments relative to each other, I wouldn't be confident that synchronizing to M2 would have no effect.
I mean, it might. It might not. I don't remember exactly how the 6502 checks for its IRQ input; it probably checks continuously during all of φ1 or φ2 and the state machine changes its trajectory at that point. (And M2 isn't identical to φ2 either...)
There have been discussions around the forum about being off by a little causing visual artifacts.
I understand. I suppose the only way of making sure is testing. So I'll continue designing the PCB and test it when it's finished. I hope I need not to change it, because doing so would complicate things a bit.
Again, thanks for the tips!