News:

I've successfully built LOS from source!: https://lisalist2.com/index.php/topic,644.0.html

Main Menu

A Lisa Inside An FPGA

Started by AlexTheCat123, September 04, 2025, 05:20:35 PM

Previous topic - Next topic

AlexTheCat123

Quote from: stepleton on December 07, 2025, 12:32:19 AM@Alex You mention switching over to the dedicated RAM chip but needing a place for the parity bits. I know this is wasteful, but the RAM chip is 2 MiB, so what about an option for emulating a Lisa with the common configuration of 1 MiB of RAM and squeezing the 128 KiB of parity bits somewhere in the space that remains? It would ensure a working system if software ever turns up that uses parity more than the boot ROM does.

Or even 1.5 MiB / 192 KiB to be more space-efficient, though there you will need more than one bit per byte in your parity pool, which could slow things down. This would be like a Lisa with one each of a 1 MiB and a 512 KiB RAM card.

Hmm, interesting idea! I had actually considered doing this a while back, but I really wanted the full 2MB to be available, so I decided not too. But maybe it wouldn't be such a bad idea after all. I'd still like to have  2MB of RAM though, so maybe I should just upgrade to a larger RAM chip on the next board revision. Like 4MB maybe. That would give plenty of room for parity! And spoiler: the design meets timing now by a pretty hefty margin, so maybe I would be able to add some parity stuff back into the internal block RAM without destroying timing closure again...

Quote from: stepleton on December 07, 2025, 07:52:03 AMIn general, would it be a good idea to allow the amount of RAM to be configurable? I seem to dimly remember hearing here about some OS (maybe one of the UNIXes) or other piece of bootable software that required exactly 1 MiB of RAM.

Don't worry, that's already part of the plan! I've got 2 jumpers on the board that you'll be able to use to select between 512K, 1M, 1.5M, and 2M of RAM. They don't do anything yet (it's currently hard-coded to 512K), but they will once I get the RAM working reliably.

Speaking of RAM, I've got another update to give! I was surprisingly able to fix all 2000-something of the timing violations yesterday, and now we're down to zero; everything meets timing! Which meant that I was able to proceed to the next step of migrating to the external RAM chip. It went better than I expected given that the Lisa actually tried to boot on the very first attempt, but there are clearly still some problems. It fails the RAM test with error 70 (so actual RAM problems, not just it thinking that parity is wrong) and the entire screen has these weird ghosting artifacts all over it. Anything that's shown on the display will have repeated ghosts of itself off to its right, and there's a bit of noise on certain parts of the screen too. I've also noticed that it now takes the Lisa quite a long time to hunt for a valid chunk of memory to stick the video page in when it first turns on, so I think we've got some sort of RAM timing issue where either certain writes aren't fully sticking or certain reads are being done before the data is ready. Figuring all that out is my job for today!

sigma7

Quotemaybe I would be able to add some parity stuff back into the internal block RAM

If it is true that nothing important uses/tests the parity circuitry aside from the ROM's self-test, then perhaps modifying the ROM to ignore that part of the self-test would be the best ROI option.

Perhaps even that's not strictly necessary... can one just "Continue" after getting error 71?
Warning: Memory errors found. ECC non-functional. Verify comments if accuracy is important to you.

AlexTheCat123

Quote from: sigma7 on December 07, 2025, 06:18:25 PMIf it is true that nothing important uses/tests the parity circuitry aside from the ROM's self-test, then perhaps modifying the ROM to ignore that part of the self-test would be the best ROI option.

Perhaps even that's not strictly necessary... can one just "Continue" after getting error 71?

I've already patched the parity test out of the ROM for the sake of testing, so it's certainly an option to just keep that patch in there!

You can indeed Continue after the parity error, so leaving it alone is another option if people are okay with that. Although I'd prefer something more elegant if possible. The solutions, from most to least elegant, are:

1. Get a bigger SRAM and implement parity in there or do parity internally using block RAM.
2. Put some logic on the CPU board that detects the ROM's "write wrong parity" test and asserts HPIR at the appropriate time to simulate the detection of the parity error.
3. Patch the test out of the ROM entirely.
4. Don't do anything, and have the user hit Continue whenever the parity error pops up.

I'll probably try the block RAM strategy, but that'll be a job for later on once much more of the system is fully-functional!

TorZidan

On a real Lisa memory board, the "HDER" (Hard Memory Error) signal (pin 49 on each memory slot) "tells" the Lisa that there was a parity error (during read). If you disconnect that signal, the Lisa will never see such errors and should run happy. Perhaps the same can be done in the FPGA? Then you don't need any parity circuitry.

Source: http://www.bitsavers.org/pdf/apple/lisa/hardware/Lisa_Hardware_Manual_Sep82.pdf

AlexTheCat123

Quote from: TorZidan on December 07, 2025, 11:38:59 PMOn a real Lisa memory board, the "HDER" (Hard Memory Error) signal (pin 49 on each memory slot) "tells" the Lisa that there was a parity error (during read). If you disconnect that signal, the Lisa will never see such errors and should run happy. Perhaps the same can be done in the FPGA? Then you don't need any parity circuitry.

That's absolutely right most of the time, but there's one extra detail to HDER that makes this fail under certain circumstances.

The CPU board has a "write wrong parity" circuit on it the forces the memory board to write incorrect parity info to any address that the CPU writes to while this circuit is enabled. As a test of the error detection circuitry, the boot ROM (and maybe LisaTest too, not sure) uses this feature to write invalid parity to an address, and then reads it back to make sure that the memory board detects the bad parity, that it pulls HDER low, and that the CPU receives the corresponding HPIR (high-priority interrupt). So if we tie HDER high all the time (which is what I'm doing temporarily right now), this test will fail. Parity will be fine all the time otherwise, but not during this test! That's the whole problem here.

stepleton

Back when I was considering the "tiniest 2 MiB RAM card" project (now shelved since multiple people seem to have this idea cooking away on a side burner), I wondered whether you might be able to accomplish something that passes write-wrong-parity checks using something like a lousy LRU cache. If write-wrong-parity is enabled, push the lower N bits of the address into the cache. Then when retrieving memory data, check to see if any address matches any of the cached address pieces and, if so, invert the parity bit. A bit more complexity and for what? Who knows. Maybe the idea can inspire something else.

I also meant to learn more about the "write wrong parity" feature to see if I could "unlock" 128 KB of extra (albeit inconvenient) RAM in my Lisa, but I haven't done that either.

AlexTheCat123

Quote from: stepleton on December 08, 2025, 07:20:03 PMBack when I was considering the "tiniest 2 MiB RAM card" project (now shelved since multiple people seem to have this idea cooking away on a side burner), I wondered whether you might be able to accomplish something that passes write-wrong-parity checks using something like a lousy LRU cache. If write-wrong-parity is enabled, push the lower N bits of the address into the cache. Then when retrieving memory data, check to see if any address matches any of the cached address pieces and, if so, invert the parity bit. A bit more complexity and for what? Who knows. Maybe the idea can inspire something else.

That was basically my exact idea! But time will tell if we actually need that level of complexity...

In the meantime, check this out; it actually boots MacWorks Plus now (albeit with barely-visible video)!

https://youtu.be/OBmNUpbnqVc

And it boots LOS as well, but it's nearly impossible to see the desktop given the horrendous-looking video! Ignore what I say about it only booting LOS 2 and not LOS 3; it actually boots 3 just fine and only failed because I tried to boot a corrupted image!

https://youtu.be/HIbJPjjW5ls

According to some logic analyzer traces, it seems like the video issues have something to do with the RAM not liking to be read from immediately after a write has finished. The CPU writes data into RAM during its half of the bus cycle, but then when the video circuitry goes to stick some stuff onto the screen during its half of the cycle, it reads completely wrong data and garbage ends up on the screen in that area. So I'm thinking that the RAM needs a little more turnaround/delay time after the end of a write before it's ready to do a read.

This would totally explain why the Lisa passes the RAM test just fine but still has the corrupted video; a CPU write is always followed by a video read, so the video read will get corrupted, but the next CPU read or write doesn't happen until a whole cycle after the first one, so by then the RAM is ready and reads and writes just fine again. If the video circuitry were somehow able to write to RAM too, then this would be a different story and we'd be getting RAM errors left and right.

Now I just need to figure out how to fix it. Right now I assert the RAM's CE only while RAS and CAS are both asserted, but keep WE asserted for a good bit longer (for as long as MREAD is low). I'm wondering if maybe it doesn't start "recovering" from the write until after WE gets deasserted, regardless of whether or not the chip is selected, so I'm going to try and shorten the WE pulse to be the same width as UDS and LDS instead. Hopefully that clears things up a bit! If not, I guess I can try shortening the CE pulse to not even be as long as the overlap of RAS and CAS, but I have to be careful there because it's already so short that I think we're pretty close to the minimum pulse width that the RAM will detect.

stepleton

Ha, wild about how the major issue is video when so much else works. It's interesting how it's so correlated with what the Lisa is doing. Is there a way to investigate the hypothesis more directly with software? In particular, what if you put the CPU into a tight loop where it wasn't writing to RAM at all, just constantly executing

.lp BRA.S .lp

which you might be able to run in Service Mode "in the blind" by putting 60FE in RAM somewhere and jumping to it. Would the display look OK then?

Meanwhile, I wonder if those bidirectional level shifters you've chosen are the same TXS0108Es I've regretted on Cameo/Aphid...

AlexTheCat123

Quote from: stepleton on December 10, 2025, 04:34:05 AMHa, wild about how the major issue is video when so much else works. It's interesting how it's so correlated with what the Lisa is doing. Is there a way to investigate the hypothesis more directly with software? In particular, what if you put the CPU into a tight loop where it wasn't writing to RAM at all, just constantly executing

.lp BRA.S .lp

which you might be able to run in Service Mode "in the blind" by putting 60FE in RAM somewhere and jumping to it. Would the display look OK then?

Yeah, it's insane that the problem is so horrendous while everything else is fine. You'd think that there would be no way that the RAM would be functional with that kind of corruption.

Trying to write some code to get some more info on what was going on was going to be my next step, but I actually was able to fix it without needing to do that! It turned out to be a problem with the /LDS and /UDS data strobes for the lower and upper bytes of RAM. The stock RAM board uses them for writes (obviously) to determine which byte(s) to write to, but completely disregards them for reads. During a read op, both bytes are always returned no matter their state. During the CPU half of a bus cycle, the CPU always puts the strobes in the "proper" states for reads even though the memory disregards them, but when the video circuity goes to read from memory, it doesn't set the strobes at all. And my RAM chip, unlike the original board, requires the strobes for BOTH reads AND writes; without them, garbage data will be returned. So this explains why CPU cycles were fine, but video ones weren't; the CPU always set the strobes right, but the video didn't. The only reason that video sometimes worked was because a CPU strobe would occasionally overlap into a video cycle just enough to trigger the RAM, but this happened less when the CPU was accessing RAM vs ROM, hence the worse video performance when there was heavy RAM activity. So the solution was to force my RAM's strobes to be asserted all the time when MREAD is high (read mode), and to only follow the strobes from the CPU when MREAD is low (write mode). Now the picture is perfectly-crisp, as you can see here!

Perfect over RGBtoHDMI, that is. Oddly enough, it's a little weird over the native HDMI output. Not sure why; it was fine before. I've attached a pic of the HDMI output too; you can clearly tell the difference!

Quote from: stepleton on December 10, 2025, 04:34:05 AMMeanwhile, I wonder if those bidirectional level shifters you've chosen are the same TXS0108Es I've regretted on Cameo/Aphid...

Yep, that's the exact level shifter I'm using! I'm really regretting them here too. They're probably/hopefully fine on the bidirectional lines, but utterly destroy the unidirectional ones. I just get random 5-20MHz oscillations that come and go. Not the end of the world for the ProFile because I've got the internal ESProFile that works fine, but this might prevent me from getting the floppy drive working on this board. I've got the onboard ESFloppy emulator, but I doubt my code for that is functional, and also it can't do writes yet. I really need to test with a real floppy drive first, but if I can't plug one in without oscillations, then I'm not sure what I'm supposed to do. I think the FPGA can actually tolerate 5V even though it's out of spec, so maybe I just remove the shifters and bridge straight across them?

You know the floppy controller error 57 I was getting? Well, it was really confusing me because the floppy controller worked great on the previous version of the project that ran on the PYNQ board, and I finally figured out why it's broken. It turns out it's not broken; it was getting confused because of the crazy multi-MHz oscillations on its inputs! I checked the status byte at FCC017 and it said the reason why it was failing the test is that couldn't step away from Track 0, but I didn't even have a drive connected, so I figured the oscillations were screwing things up. And sure enough, flipping the "Floppy Drive Source" switch from External to Internal (which isn't even running any code yet) made the error go away entirely. So we now make it through the whole self-test without issue!

The next order of business is going to be to expand the RAM a bit. Right now we're stuck with 512K, which makes LOS painfully-slow. So expanding to the full 2MB and getting the memory size jumpers going is next up.

I also tried booting the Workshop, and it boots just fine, but it's insanely slow. Anytime you type a key from the main menu, it takes about 10 seconds for it to process what you typed, and it comes back with a filesystem error whenever you try to launch a program. But clearly the FS is fine because it can list the directory just fine and the Preferences program can launch from an LOS instance on the same disk just fine. So I'm hoping that maybe the Workshop just hates living in 512K of RAM and that this will fix it.

After that, I'll probably get the H/3A and A8/40 ROM selection switches working. The selection part should be easy, but this also means that I'll need to add extra scaling logic to the HDMI subsystem for when the 3A ROMs are selected. So that'll be a good time to fix the HDMI issue that I'm having too.

Then I'll probably do the speed selection switches, which will control the DOTCK frequency. I'm not sure how high I can go before things break, but the SRAM will probably be the limiting factor. I'm hoping for at least 40MHz (2x the normal DOTCK speed), but maybe we can go even higher. I've already confirmed that I can lower the DOTCK (all the way down to 5MHz) without breaking anything, so hopefully raising it will be the same story.

Ignore the weird artifacts on the "good" picture; that's just thanks to image compression and nothing wrong with the Lisa!

AlexTheCat123

Just got the full 2MB of RAM going. It's crazy how much faster LOS is with that extra RAM. Nearly unusable with 512K, but quite pleasant with 2MB. And those weird issues with the Workshop where programs wouldn't open and everything was insanely slow are all gone now, so it looks like the RAM upgrade fixed that too!

The size selection jumpers don't work because I implemented all the logic for size selection by inhibiting CAS and RAS when the CPU tries to access out-of-bounds memory, but then forgot to send those inhibited versions of CAS/RAS over to the RAM controller. So it's just stuck at 2MB all the time. But that's a super easy fix!

I've implemented that fix now, and I'm currently resynthesizing. I also added code to get the H and 3A CPU board ROM selector switch and the A8 and 40 I/O ROM selector switch working, so we'll see if those go as planned...

At the same time, I also added functionality to the speed selection switches, so that they can alter the speed of the Lisa's DOTCK. I picked 20MHz (stock), 40MHz, 60MHz, and 80MHz, which correspond to CPU clock speeds of 5MHz, 10MHz, 15MHz, and 20MHz, respectively. Given the external SRAM and its 55ns speed limitation, I'm not optimistic about being able to increase the DOTCK very far. I'm hoping to at least get the 40MHz DOTCK to work, but I really only give that about a 50/50 chance of success, and I highly doubt I'll be able to push it past there without having RAM issues.

But I'll find out about all of that in 40-ish minutes when the design is done synthesizing!

stepleton

#70
Awesome to see good video.

The HDMI issue looks a bit similar to situations I've encountered when making hacky adapters that go from TTL monochrome video to VGA. (Basically you buffer and level-shift the video signal into VGA RGB and then, if needed, massage the sync pulse timing as best you can to match the spec.) Modern LCD monitors do the best they can to cope with your adapted signal, but it's all too common to be off by subpixels, which does a number on single-pixel vertical lines. You find yourself making minute adjustments via on-screen menus to stretch the screen geometry just right, to change the phase of the video signal, and so on.

In the image itself it almost looks to me like the horizontal screen resolution is set to be just a little bit too narrow.

But I can't account for the "ghosts" of vertical contrast edges (e.g. the one just to the right of the window), which seems to me almost like a signal integrity issue!


As for the parallel port issues --- I'm inferring from your remarks that you're running I/O ROM 88, meaning a 2/10 I/O board, meaning a faster parallel port (reflecting earlier discussions). Maybe a 2/5 I/O board would achieve better behaviour? Or maybe the oscillations are more fundamental. For Cameo/Aphid, I achieved some stability improvements by putting some series resistance on the signal lines (on the +5V side).

For future designs, unidirectional ICs are likely the way to go, but if you want a bidirectional approach, the fistful-of-MOSFETs method seems to work pretty well, as James D. can attest.

Eager to see what's next, in any case...

AlexTheCat123

Quote from: stepleton on December 10, 2025, 11:43:03 PMAwesome to see good video.

The HDMI issue looks a bit similar to situations I've encountered when making hacky adapters that go from TTL monochrome video to VGA. (Basically you buffer and level-shift the video signal into VGA RGB and then, if needed, massage the sync pulse timing as best you can to match the spec.) Modern LCD monitors do the best they can to cope with your adapted signal, but it's all too common to be off by subpixels, which does a number on single-pixel vertical lines. You find yourself making minute adjustments via on-screen menus to stretch the screen geometry just right, to change the phase of the video signal, and so on.

In the image itself it almost looks to me like the horizontal screen resolution is set to be just a little bit too narrow.

But I can't account for the "ghosts" of vertical contrast edges (e.g. the one just to the right of the window), which seems to me almost like a signal integrity issue!

Yeah, not completely sure what's going on just yet. I put the image up on my big monitor instead of the HDMI capture device to get a better look at it, and it looks like every 8th column of pixels is a duplicate of the one 8 columns before. So I guess something to do with switching between bytes as we either read Lisa pixels into the framebuffer or read HDMI pixels out of the framebuffer. I'm going to try to change the pipelining logic for the scaling a bit to see if that fixes it, but I'm not sure.

The weird thing is that it was working perfectly up until a few days ago, so I'm not sure what changed!

Quote from: stepleton on December 10, 2025, 11:43:03 PMAs for the parallel port issues --- I'm inferring from your remarks that you're running I/O ROM 88, meaning a 2/10 I/O board, meaning a faster parallel port (reflecting earlier discussions). Maybe a 2/5 I/O board would achieve better behaviour? Or maybe the oscillations are more fundamental. For Cameo/Aphid, I achieved some stability improvements by putting some series resistance on the signal lines (on the +5V side).

I'm actually doing the 2/5 I/O board, mainly because I want Twiggy compatibility for the lucky few people (yourself included!) who happen to have some and might want to plug them in. I'm pretty sure the oscillations are more fundamental though, like where the level shifter can't figure out which side is driving it because it's got a signal from the FPGA on one end and a signal from a pullup on the other, and so it oscillates back and forth between the two, leading to a really high-frequency square wave.

For the next board revision, I'll almost certainly switch to unidirectional ICs for everything other than the few things that need to be bidirectional, and I'll probably go with the MOSFET strategy for those unless I come up with anything better. But I've heard that it's pretty reliable like you're saying, and James' board seems to prove it!

Synthesis finished, and I programmed the board with the new bitstream that should enable some of those additional switches. I ended up having to disable the speed switches thanks to some weird design rules Xilinx has when it comes to routing clock networks, so I'll have to come back to that later. But luckily it looks like the RAM sizing jumpers work now, and so does the CPU ROM H/3A selection switch. I obviously need to update the HDMI logic to detect which VSROM is installed and push out the pixels accordingly, but at least I can see that it's booting even if the square pixels make the screen garbled.

The I/O ROM A8/40 switch unfortunately doesn't seem to work though. The A8 position works fine, but the 40 position causes the I/O ROM revision to display as 52 and it hangs on the I/O board test for a while. Which means that the floppy controller never asserted DISK_DIAG and probably isn't making it through its self-test. I don't see anything it could be other than a corrupted ROM, so I'm rebuilding right now with a new ROM image and some tweaks to hopefully fix the HDMI weirdness to see if I have any luck there.

AlexTheCat123

HDMI issues are (mostly) fixed! The weird repeated 8th line issue was thanks to a mistake in my video pipeline where I was always accidentally reusing the byte index from the previous pixel for the first pixel of the next byte, so the first pixel of the next byte was actually showing the first pixel of the previous byte. That's all fixed now!

The image is still slightly shifted to the right, but hopefully that'll easier to track down.

AlexTheCat123

I think all the video issues are fixed at this point!

Now, when you flip the H/3A toggle switch, it not only switches the ROM, but it also switches the HDMI decoding to account for the different screen resolution and square pixels. It's pretty cool to be able to flip the switch and see the screen change while the Lisa is running, although of course it results in corrupted video until you flip the switch back or reboot!

While messing around in LOS, I discovered a really subtle bug with the I/O board. I was trying to change the contrast and noticed that the HDMI signal brightness wasn't changing, whereas it works fine in MacWorks.

After some digging through the LOS source code (LIBHW/MACHINE.TEXT and LIBHW/DRIVERS.TEXT), I discovered that LOS does a check at boot to see whether your I/O board is the "Sept81" or "Feb82" model. I don't think any Sept81 I/O boards are known to exist, so all 2/5 boards are the Feb82 revision. And interestingly enough, the two revisions handle setting the contrast differently.

But LOS is detecting my board as a Sept81 for some reason. And I think I know why. The way that it determines which one you have is by checking that the ProFile parity generator that hooks to PB5 on the parallel port VIA is functioning properly. Apparently the Sept81 board didn't have this parity generator, so if the parity doesn't work, then it knows it's a Sept81 board. So clearly my parity isn't working for some reason! So now I need to figure out why and fix it so it can set the contrast correctly...

It's just really funny that the parity on the ProFile bus being broken causes contrast not to work! And according to the source code, the contrast is the only thing that cares about whether your board is Sept81 or Feb82, so it makes sense that it's the only thing broken!