We've got a great problem on the MEGA65 right now -- there are more cores coming out than you can fit in the 8 slots that the MEGA65 motherboard's flash can fit. But we have a JTAG port, so we have the means to push bitstreams in just a few seconds. A lot of us use a JTAG adaptor connected to a PC to do this and other things. But sometimes you just want to be able to do it without a PC or extra external cabling. Thus this little project.
The core of the idea is to put a microcontroller on a board that connects to the JTAG port that can push bitstreams. Now, this can't be from the existing SD cards on the MEGA65, because they're connected via the FPGA that we're trying to re-configure. So it will need an SD card slot of it's own -- which is fine, it means you can have potentially thousands of cores on there, and have the MEGA65 be able to request that it load any of them. The request mechanism will be via a UART.
So we need JTAG, UART and microSD card. A cheap PicoPi should be able to talk to all those. JTAG and UART are simple, and it has SPI that can talk to a microSD card. I have a pile of microSD card breakout boards around here that will help me to prototype it.
There's even a WiFi enabled version of the PicoPi, so I might even roll this into the expansion board bridge board and replace the ESP32 on there. (I might be able to make the ESP32 do the microSD card and JTAG, too, but I've not got good enough handle on the toolchain to know if that's possible -- but the PicoPi should be a doddle.)
As this is a little utility thing, I'm vibe-coding the firmware for the Pi. It _claims_ to have implemented the UART control interface and pulled in some FAT file system library etc to talk to the SD card, and adapted the m65 utility's JTAG bitstream pushing. So let's see if it can push a bitstream via UART to JTAG.
To do that, I need to work out the pinout from the Pico to the TE0790 JTAG header on the MEGA65's mainboard.
Pico pin 8 white -> TE0790 pin 2 GND
Pico pin 9 grey -> TE0790 pin 4 TCK
Pico pin 10 green -> TE0790 pin 12 TMS
Pico pin 11 blue -> TE0790 pin 10 TDI
Pico pin 12 purple -> TE0790 pin 8 TDO
With that, I can totally push a bitstream over the USB UART of the PicoPi to the MEGA65! Great news!
The not so great news is it takes 107 seconds!
Now, the UART at 2Mbps means ~5 sec per MB, so about 35 seconds for a 6.6MB bitstream, just for UART transport. But that still leaves a full minute for the JTAG bit-bashing.
Here's the pretty picture ChatGPT drew me for the pin connections from an SD card to microSD adapter to the Pico Pi, so that I can quickly hack up a test cable:
Textually, that's this:
SD pin 1 CS -> Pico GP5, physical pin 7
SD pin 2 MOSI -> Pico GP3, physical pin 5
SD pin 3 GND -> Pico GND (e.g. pin 13)
SD pin 4 3.3V -> Pico 3V3 OUT, physical pin 36
SD pin 5 SCK -> Pico GP2, physical pin 4
SD pin 6 GND -> Pico GND (e.g. pin 18)
SD pin 7 MISO -> Pico GP4, physical pin 6
Here's our little octopus (ok, heptopus) assembled, ready for testing:
Now I just have to find a spare microSD card. I'm sure I have numerous laying around, but can't find any. So I'll go to the supermarket and grab one this morning while running errands, format it for FAT, and then see if the firmware ChatGPT built me can read from it.
Meanwhile, this looks like a great switch for the JTAG bus to allow TE0790 to operate normally, except when we want to push a bitstream: SN74CBTLV3257-Q1.
Meanwhile, I went and got a cheap 32GB SD card from the supermarket, copied some bitstreams and core files on... and it works! I can push a MEGA65 core, and it loads. Okay, it takes 18 seconds, which is annoying, but it loads. The AExp core is refusing to load, though, which I'll have to investigate the reason behind. The problem was in the JTAG closing after pushing the core.
Now it works! I can load the AExp core, for example, and see the workbench hand and load disks and things. Nice.
And with a bit of optimising I've cut the bitstream load time from ~18 sec to ~6 seconds, which feels totally reasonable -- that's about how long it takes via TE0790 with m65 -b. I could get it down closer to 4 or so seconds by trying to speed up the JTAG, but that's not our top priority right now.
Top priority is thinking through security and physical arrangement.
Security matters here, because the UART that controls the Pico will be accessible via the MEGA65 user mode as one of the buffered UARTs (it will take the place of the ESP32 on the previous expansion bridge board design). That means rogue software could install and launch a rogue core that then does whatever it wants -- including fry your MEGA65 if it was nasty enough.
The simplest solution is leave the SD card read-only in this module. Then you have to open the case and take the SD card out to add cores. Doable, but not super nice.
So my two key refinements are:
1. Have a jumper accessible via the user-port cut-out that if fitted, allows writing to the SD card on the module.
2. Position the SD card on the module so that it can be accessed via the user port cut out (even if a bit fiddly so), so that no-case-opening adding of cores is possible.
So I think I'll try to design up the changes to the PCB to support all this, and then we can look to proof-of-concept native M65 software that can talk to this. The MEGA-FLASH program might want to be updated to allow querying of the SD card for additional cores to keep a single unified core launching experience. But that's a question for after we have working hardware.
Okay, so in the meantime I've finished up an initial PCB design, then made corrections to the SPI pinout just hours after PCBWay started fabrication, so added a 2nd order with the fixed version, and then after all that improved the power circuit so that the Pi can't back-power the MEGA65 (PCBs for that not yet ordered -- I might hand-modify the revH PCBs when they arrive to do this).
Here's the current (revJ) state of affairs:
And how the PCB looks:
So that leaves the firmware. I've been shamelessly vibe-coding this using ChatGPT, and have something that works well enough for now: It support AT-style commands on the USB serial or direct UART pins, as well as running a little web server that lets you interact wirelessly to push bitstreams:
And it works! It can load a new core in 5 - 8 seconds, depending on size of core and phase of moon.
The key remaining thing that I want to implement is allowing the Pico to download core files from a specially prepared mirror website, so that you can get all the latest cores on demand.
I'm busy bashing my head against this at the moment. First, WiFi doesn't always power up cleanly. Then when it does, sometimes it fails to join the AP. And then there's firmware bugs with the fetch logic on top of that. Layers of failure that all have to not happen to progress. Quite annoying.
I'm making good progress on this, getting it to download a core file, and have it in a .partial file while downloading, and cleaning up the .partial files when starting a fetch. But the little TCP stack keeps failing in fascinating ways. I'll keep working on that in the background while I think about other bits.
The other main bit I'd like is to add support for hijacking the MEGA65 Serial Monitor Interface to allow loading of programs remotely etc, and wireless debugging. The question is whether this merely _sounds_ like a good idea, or whether it actually _is_ a good idea.
Okay, we'll go back to the TCP stuff -- and it's finally working now, downloading core files at 500KB/sec from a mirror on my lan. Will likely be slower over the open internet, but it's all nice an automatic. It now has a "I'm busy updating" web page.
Okay, lots of progress to report! The PCBs arrived, and I've assembled one:
_And_ the SD card _and_ the JTAG bitstream loading on the MEGA65 worked first time! :)
That all makes me very happy. What makes me less happy, is that the JTAG-pass through from the TE0790 to the MEGA65 mainboard doesn't seem to be working. It doesn't even recognise the FPGA on the JTAG chain:
$ m65 --autodiscover --verbose
2026-07-26T07:32:04.864Z NOTE MEGA65 Cross-Development Tool 20260124.13-develo-355b567
2026-07-26T07:32:04.864Z INFO using default FPGA ID ffffffff
2026-07-26T07:32:04.884Z INFO found 0 candidate USB devices.
2026-07-26T07:32:04.884Z INFO requested id ffffffff does not match any usb interface
2026-07-26T07:32:04.884Z CRIT no matching jtag port found, aborting
There's a few possible things that could be wrong here:
1. I have the pinout messed up.
2. The JTAG bus switch is doing something dumb
3. The extra electrical load of the JTAG bus switch is slowing things.
(3) I can check by dropping the JTAG frequency.
I think I might just have spotted part of the problem. Can you spot it in the following schematic snapshot?
Let's just say it would be nice if Kicad had a spell checker ;)
Well, except that I had it wrong everywhere which means that it was fine.
So why aren't I seeing 3.3V on the JTAG header power pins? Okay, forget that. I just had the MEGA65 turned off :/
Time to trace from the TE0790 back towards the MEGA65.
The TCK line is at 3.3V on pin 3 of the TE0790 header. But it's like 0.5V at the input TE0790 relay header on the expansion connector board. This is odd, because those two pins should be directly connected.
Okay, I've definitely screwed things up: On the old connector PCB I apparently flipped the order of the pins of the TE0790 relay to make routing it out on the expansion board easier. But on the new one I haven't done that. Which means that pins are totally wrong, fortunately not in a let-the-smoke-out kind of way. But certainly in a no-hope-of-the-jtag-working kind of way.
I've fixed that on the schematic. It would be nice to be able to test the electrical path without having to wait for a new PCB spin, but it's not really practical. The main thing is that I've found a highly probable cause, and can test it out easily once I get the new PCBs spun.
If I'm going to get a batch of those PCBs, I should also get a batch of the M1565 external drive boards made, since I can't find the ones that I'm sure I had made previously. It's a 100x100 2-layer board, so it is only like US$5 to get another 5 made. The question is whether I have the parts for assembling one lurking around, too, and if so, where on earth I've stashed them.
I do hate having to prepare BoMs from schematics. So the sooner it's started the sooner its done. And if I order everything today, it should show up by next weekend, all going well.
Digikey parts:
5mm LED - 732-5016-ND
1565 mini-din - MD-90SM
A pile of 3.3K Ohm resistors -- those I almost certainly have on hand.
And 1K Ohm resistors.
8-way DIP switch - CT2088-ND
74LS125 - 296-1638-5-ND
74LS85 - 296-14900-5-ND
74LS595 - Okay, those are like $30 each in LS series! Fortunately HC are cheap still: 296-1600-5-ND
74LS165 x2 - 296-14885-5-ND
74LS04 - 296-1629-5-ND
Okay, I'll order enough parts to make 3 to get free shipping.
Then I just need a mini-din cable that suits the MD-90SM.
For the cable, I also hate soldering mini-DINs, so I'm just going to buy a random one off ebay. This one claims Australian stock, so I'll go with them:
https://www.ebay.com.au/itm/336344275528
Well, cable, PCBs and components all ordered. So that's all I can do on this project until the bits arrive.
It's the following weekend, and the revJ PCBs have arrived, along with a batch of M1565 external drive PCBs... which of course means that I found the ones I already had out in the shed this morning when I went to assemble the revJ board.
I also realised I still hadn't updated the outline of the revJ PCB for the microUSB cable access to the Pi Pico. Nor moved the SD card slot back a bit to make insertion/removal easier. So RevK here we come.
But the good news: The revJ works, and the TE0790 via the relay port now also works! So we have integrated JTAG with the web interface (and it came up on WiFi on first boot :) and a connected TE0790 works again, too, now :) The JTAG switch is indeed doing its thing -- if the Pico is JTAGing, then the TE0790 sees an empty JTAG chain.
The next step is to see whether we can talk to the Pico via UART from on the MEGA65 itself. It should work, provided I haven't got the UART pins backwards.
My plan is to test the UART using megacom, the little minicom-like terminal program I made and used in this blog post: https://c65gs.blogspot.com/2026/01/megaphone-cellular-modem-and-power.html which currently lives here https://github.com/MEGA65/megaphone-modular/tree/main/src/telephony.
The UART pins themselves are on P1HI(2) (RX for MEGA65) and P1HI1(1) (TX for MEGA65). But either I'm not using the right bitstream for the MEGA65 core, or megacom doesn't work, because I don't see any UART signalling on either pin when I use MEGAcom to talk to buffered uart 0, which is what (I believe) it's mapped to. And it's not mapped to any of the higher numbered buffered UARTs (just tried them all in MEGAcom).
I can't see what the problem is, but suspect I might be using an old bitstream. So I'm rebuilding a 932-* bitstream, and we'll see if that helps.
Okay, I'm back investigating this again after too long a break.
First up, it looks like the PMOD pins were declared high-impedance above where they get assigned to the UART plumbing. That matches what I'm seeing, in that the pins are floating, rather than driven. So I'll cook a bitstream to test that and see if that gets those pins behaving: Specifically TX pin should be driven high, and the RX pin should still be floating.
First attempt didn't fix it. I'm suspicious that Vivado might be caching some build thing, so I'm re-running it now with make clean first.
Nope, the pins are still floating.
And I'm not getting anything useful out of the keyboard-connector 2nd UART, either. So I'll put a super-simple loopback on the PMOD pins. If that doesn't work, then there's something really really wrong going on.
Well, connecting clocks to the pins I've at least allayed my fear that I had fried those FPGA pins. So now to work my way slowly back up from this foundation, first by looping back the pins in VHDL, and then after that connecting them to the buffered UARTs.
I think part of the issue is that I'd assumed the p1hi pins were the top row on the PMOD. They're not. They're the bottom physical row which is the high-numbered pins. That probably explains a lot of my headless-chicken chasing.
And that was indeed the root cause. So I have buffered UART 0 talking again on the PMOD, and buffered UART 1 on the keyboard connector.
So the next step is to finally see whether we can talk to the Pi Pico via the PMOD relay to the expansion board. And it looks like I have the UART pins on the PMOD backwards. So I'll fix that, and then check if I can finally talk to the Pi Pico.
Ok -- confirmed that the pins have switched. Now to see whether I can get the Pi to talk to me via the UART.
Hmm.. I got something at 2Mbps, but it was quite garbled.
That said, I can get the same garbled output by typing ATI + CRLF into it repeatedly.
Yup, looks like it's receiving commands from the UART just fine, but the output coming back is all messed up.
I've just fixed a bunch of bugs in the Pi Pico firmware, and now AT+JTAGLOAD= works to load a core from the MEGA65 :)
I should probably make a native utility that reads the list and lets the user pick one to start. I'll probably try getting an LLM to make the proof-of-concept code for that, based on the megacom stuff. It won't be particularly elegant, but it will be a basis on which a real human can build something better. Ideally it would eventually be integrated with the NO SCROLL on boot core loader. And maybe some little program you can put on a floppy, and have it autoboot using the C65/MEGA65 ROM's autoboot.c65 file thing. That way you could have a "kickstart" disk that when inserted and you reset the machine really boots it into an amiga. And another for C64 mode etc.
Anyway, here's the little thing the LLM hacked up for me from the megacom source (that had the necessary UART code in it). The source is in https://github.com/MEGA65/megaphone-modular/blob/main/src/telephony/jcore.c.
So at this point, we have all the bits we need.And that feels like a good place to end this post. The next one will probably be tackling some of the MEGAphone stuff that all this UART work has setup.






No comments:
Post a Comment