I am starting this thread so I can ask for some feedback on my designs for an adapter for the IOU and MMU. As I mentionned in this thread: https://www.applefritter.com/comment/110444#comment-110444, ReActiveMicro originally offered to design the hardware for the replacement adapters. However, Henry told me recently that he wouldn't be able to work on this any time soon given his lack of free time. So, I decided to revisit an attempt I did some time ago.
The VHDL code for my project (https://github.com/frozen-signal/Apple_IIe_MMU_IOU) was developped using an ALTERA EPM7128STC. But, this CPLD is not produced anymore and the only way to buy them is to find them on places like eBay and AliExpress. The CPLDs that comes from these places are unlikely to be new ones; they are probably pulls or recycled ones in some way. So, each chip must be tested indivually to make sure it works. A tedious task. Besides, the irony of using obsolete CPLDs to replace obsolete ASICs don't escape me.
So, designing the replacement adapters with modern and active compnents makes sense. This is what is needed:
- A FPGA with at least 38 GPIOs and small enough to fit on PCB the size of a 40-pin DIP.
- Ideally, the FPGA should have an internal oscillator to generate some hold delays that the Apple IIe requires.
- All the components must be active, inexpensive and easy to source.
- The PCB must only require basic PCB manufacturing and be relatively easy to assemble for a hobbyist.
The Lattice iCE40 FPGA (ICE5LP1K-SG48ITR) fits that bill; a 7x7 mm, QFN-48 package that is very popular with the hobbyists and has an internal 48 MHz oscillator.
On the downside it's a 1.2V device with 3.3V banks, so every inputs and bidirectional signals need to be voltage level shifted down to 3.3V. The main challenge is to fit all the level translation onto a PCB the size the official IOU and MMU.
Also, although it has enough GPIOs, 4 of them are shared with the configuration pins. For those unfamiliar, this means that when the iCE40 is powered on, it uses 4 special pins SPI to read its configuration data from a Flash Memory IC. Once the configuration is done, these 4 pins becomes available as GPIO.
Note the KSTRB and AKD signals that are shared with FPGA_SCK and FPGA_SI respectively, more on this later.
For the bidirectional signals ORA6-0, a LSF0108 is used for level-translation. This device don't need a direction pin which is really good, but need a bias resistor (R9) and a pull-up for each channel on the high-voltage side (see table 9-3 of https://www.ti.com/lit/ds/symlink/lsf0108.pdf to understand where the value of 581 Ohms comes from). /PRAS is an input-only signal but is routed through this component because a channel was free. This is why R7 is DNP; the pull-up is optional for an input signal, but the footprint will be there if needed.
The LVC245 level-shift input-only signals and would be pretty straightforward if it was not for KSTRB and AKD. These two signals are shared with the FPGA SPI pins and we must make sure that external KSTRB or AKD signals don't mess with the SPI signals during the FPGA's configuration. The way to do it is to disable the LVC245 with /OE during configuration time.
I use CDONE, this signal is LOW during configuration and driven HIGH in user mode. Unfortunately, that's the inverse of what we need, we'll have to invert CDONE:
Here, when CDONE is LOW, the NMOS will be in a non-conducting state, and /OE_245 will be pulled-up by R14. When the configuration has finished CDONE will be driven HIGH and the NMOS will turn on driving /OE_245 LOW.
I could also do it with a GPIO:
- Move an input pin to FPGA_SO (FPGA_SS can't be used because the Flash Mem need to remain unselected in user mode)
- Pull-up that freed GPIO. During configuration, all GPIOs are placed in a high-impedance state.
- Configure that GPIO as open-drain and drive it a constant LOW in user mode.
I just preferred to use the FPGA's CDONE signal with an NMOS.
I use the DMG1012TQ-7 because it's small, it's logic-level (VGS(th) = ~0.75V) and has low RDS(on). Plus, it's inexpensive and places like Mouser have these in large quantities.
The /RESET signal also need special handling:
In the official IOU, this signal is an open-drain pin with an internal pull-up. To level-shift these with a LSF010x, a pull-up is required on both sides. And since GPIOs are in a high-impedance state during configuration, that means /RESET will be pulled HIGH during this time, and this is not what we want. The fix is simple, fortunately: CDONE is itself an open-drain with weak pull-up, and shorting CDONE with /RESET will give us the desired behaviour.
The remainder of the schematic is standard stuff:
- A Winbond W25X20CLUXIG Flash Memory: a SPI Flash module that is small, inexpensive and in ample supply. Note that CLK and DO are tied to KSTRB and AKD respectively. During configuration, KSTRB and AKD won't mess with the SPI signals because as seen earlier, the LVC245 will stop outside's KSTRB and AKD from messing the data. And once the configuration is done, /CS will be held HIGH and CLK and DO signals from outside will be ignored.
- MIC5504-3.3YM5 and MIC5365-1.2YC5 LDOs are used to convert 5V to 3.3V and 1.2V. I used these because, like the other components, they are small, inexpensive, easy to solder and easy to source.
There are two problems with the current design: CASSO and SPKR are tied directly to the adapter's pin and are 3.3V. Since these two aren't used as logic signals, I may have to level-shift them to 5V. SPKR is amplified and may be okay, but CASSO probably won't be.
Assembling:
I moved components to the front side as much as I could, and only resistors and capacitors remains on the back. So, the easiest way to assemble the adapter would be reflow the back first. Then, only light 0402 components will be on the underside during the second reflow cycle, minimizing solder joint sagging. With a hot plate & hot air, I think the best way will be to reflow the front, and then solder the back with hot air.
Finally, just like the VHDL part of this project, I plan to upload the Kicad schematics on github once I know they works. I will use the same license (Creative Commons Zero) so that anyone can use them in any way they want. And as a sort-of disclaimer, I have no intention of making money from this project, and I have no problem if other people makes money from this project.
As always, any thoughts are very welcome.
Hi,
SPKR sould not be a problem as the amplifier is class C. For the CASSO there is a voltage divider and the output is app. 41mV; with 3.3 it will be 27mV... Might still work as the s/n ratio would be quite good.
Lesson learned ;) http:// https://www.applefritter.com/comment/110457#comment-110457
Oh yes, I see this now about the switching mode, nice! :)
Hello Frozen signal,Thank you for your IOU/MMU project.
I'm trying to create my own replica of Apple IIE (mentionned in this thread: https://www.applefritter.com/comment/110461#comment-110461 ) using your VHDL code. I am using Quartus II 13.0.1 and EPM7128SLC84-15 as CPLD for MMU and IOU. I wanted to ask if it's normal that I get multiple warnings about inferring latch(es) during compilation like this:
Also of concern are warnings such as:
I would like to understand, is this how it should be, or am I doing something completely wrong? I have already received the PCB and partially assembled it, but something is not going as I would like, especially in terms of image output and working with DRAM memory...
"Inferring a latch" is what the silicon compiler is forced to do when the HDL code assigns a value to a signal in some branches of a process but not in others. If the signal's value is determined only by the inputs at the current clock cycle, it can be generated entirely by gates (implemented by LUTs in the FPGA). If its value might be "held over" from the previous clock cycle without change, it's necessary for there to be someplace to store the old value, which must be a latch.
Where the designer knows that a value must be stored across cycles, the proper way is to create a register in the HDL. No warning about inferring latches will be emitted in that case.
A "combinational loop" is another type of problem where there is a mutual interaction between signals. If you define A = B, and B = not A outside of a clocked process, you have created a free-running oscillator. This can make the circuit behavior nondeterministic.
Both types of warnings are indications that there is a bug in the HDL code. Signals should only be updated in clocked processes and defined within every branch of the process.
I partially disagree with robespierre; these warnings is just the compiler telling you to check if that's what you want, and not necessarily latent bugs. "Inferring a latch" is indeed a signal that is mapped to a latch, but in the case of the MMU and IOU, that's what we want. I agree though that ideally all processes should be clocked, but most of the MMU and IOU is async and we have to do the same.
For your problem with DRAM and image output, if you have an oscilloscope or a logic analyzer, the fix will be easy. The hold times must be adjusted.
To fix, hook your scope on PHI_0, /RAS, Q3 and RA0. Check the time between the falling edge of /RAS (during PHI_0 in the case of the MMU and PHI_1 in the case of the IOU) and when RA0 change value (t_RAH). Do the same thing for Q3 (t_CAH). In your DRAM_HOLD_TIME.vhdl (assuming you're using the one with LCELLs), adjust NUM_LCELLS_RAS and NUM_LCELLS_Q3 so that t_RAH is between 32ns and 80ns, and t_CAH is at least 20ns (with a max of ~275ns). This file is used in both the MMU and the IOU, so fix the MMU and the IOU should be fixed at the same time.
See: https://github.com/frozen-signal/Apple_IIe_MMU_IOU/tree/master/CUSTOM/DRAM_HOLD_TIME
The hold delays in MMU_HOLD_TIME.vhdl are for /EN80, /ROMEN1, and /ROMEN2. The hold delay for /ROMEN1 and /ROMEN2 is not necessary; it's just added to be similar to the real MMU. So, do the same thing as before, but for /EN80 making sure the delay between PHI_0 changes and /EN80 changes is about 45ns.
See https://github.com/frozen-signal/Apple_IIe_MMU_IOU/tree/master/CUSTOM/MMU_HOLD_TIME
What exactly are your image output and DRAM problems?
And congrats on your project!
Thanks for answer, i have carefully studied the documentation on the MMU/IOU project website and read about NUM_LCELLS_RAS and NUM_LCELLS_Q3. Yes, i am using Altera's LCELLs, so first of all I try to use your default values (LCELLS_RAS=14, LCELLS_Q3=10). I connected the logic analyzer to the main signals and saw the following picture:
ras.PNG
So, t_RAH with default settings is 18ns
q3.PNG
and t_CAH is 33ns. I tried increasing the values NUM_LCELLS but haven't achieved any success yet. I'm also confused by the appearance of some glitches on RA0....
I don't know -) I am starting with appleII deadtest ROM ( https://github.com/misterblack1/appleII_deadtest ) in this case the behavior is approximately as follows: short video . I also tried to run the PCB with a A2VGA video card, and it sometimes showed some signs of life:
photo_2025-01-15_19-32-24.jpg
photo_2025-01-15_19-44-20.jpg
photo_2025-01-15_19-44-57.jpg
I am concerned about glitches on RA0 - I can't figure out what is causing them yet. Maybe a bus conflicts on the ORA between MMU and IOU.
May be I'm wrong, but it seems your 14Mhz clock is actually 66Mhz regarding timings that you show.
Good catch rumburak!
Tronix, this looks more like signal noise than contention.
If I were you, I'd make sure the MMU and the IOU works on an actual Apple IIe first.
Make an adapter and hook your MMU on the Apple IIe and make sure it work. Then do the same with only the IOU. Then both together, and finally with a 80-col card. Only then I'd try to make them work in your motherboard.
Here is my hw, based on EPM7160STC100:
20250117_103349.jpg
20250117_105008.jpg
I have problem with some CPU from different manufacturers.
When use both emulated MMU and IOU and test with internal ROM test (Apple IIe ROM and enhanced IIe ROM) it fail for MMU.
Test pass with: SY6502, R65C02P2, some newer CM630 (Bulgarian clone).
Fail with: UM6502, G65SC02P-2, some older CM630.
Thanks rumburak! I'll try to find some of those CPUs that fail and update my repo.