Wow, that's awesome - congratulations! I bet a lot of people will find this really useful. As you said, old Localtalk to Ethernet bridges are getting hard to find. For the IIci I guess you'd also have the option of a Nubus ethernet card, but those aren't so common either, and expensive when you find one on eBay.
What Linux-based program are you referring to? My understanding of the whole technology stack here is foggy, but I thought it was basically just a microcontroller running custom C code. Then talking to netatalk on you Mac Mini.
Thanks! Yeah, I actually already have an ethernet card in my IIci (inactive for these tests, of course!). It's mainly just for a fun project, and I guess it could help people with some computers that can't have Ethernet added. It would be pretty neat to use it to do something on the internet with my Mac Plus, for example.
All my microcontroller program does is talk to to the SCC. It reads out frames and forwards them at 921,600 baud over a UART to something else (which right now is my Linux VM, but in the future could be a Raspberry Pi or BeagleBone Black). It also listens on the UART for frames to send over the SCC and sends them over the SCC. It also meets some quick timings required by LocalTalk for CTS/RTS packets. So basically it exists to meet timing requirements, and then it passes the less time-sensitive communication onto something else for doing all the Ethernet and protocol translation (a Linux C program I wrote). I could actually do Ethernet directly on the LPC1769 microcontroller, but for now it's easier, quicker, and more flexible to get things up and running with a Linux program. I don't have to worry about how much RAM I use, which is a bonus while I'm initially figuring out all the protocol stuff.
I still haven't figured out exactly what I'm doing wrong that causes the LocalTalk side to get all screwed up, but it's not the fault of my UART stuff as far as I can tell after some live debugging. It appears to be something with the SCC or communication with it. I'll have to do some debugging to dump the SCC registers and make sure it's not in a bad state. I'm really tempted to try bit-banging the SDLC stuff myself. The LPC1769 is probably fast enough (120 MHz), and with some input capture and output compare stuff with timers, it might not even be too CPU-intensive. Sure, the SCC is interesting and supposedly easy to get up and running, but it also has a lot of really annoying quirks. It's pretty complicated to work with, and the LocalTalk traffic really isn't all that complicated. I know I'm not the only one who feels that way about the SCC -- here's a recent post on comp.arch.embedded where someone else complains about the SCC being annoying
What happens when the LocalTalk gets screwed up - what's the symptom? If you're confident your LPC1769 stuff is working correctly, then what could leave the SCC in a bad state? It's just basically a fancy UART itself, isn't it? So whatever you're doing to it externally shouldn't be able to get the chip into a bad state, I'd hope.
Have you looked into the possibility that the LocalTalk / AppleTalk code in Mac OS is partially at fault? From my experiments with floppy stuff, I found that the floppy driver code is not very robust as far as error checking goes, and some mistakes in the Floppy Emu firmware could cause a Mac Plus to lock up during a file copy. Maybe the LocalTalk code has the same level of non-robustness. The code probably covers the error conditions you'd see in a real LocalTalk environment, but you're probably creating exciting and new error conditions. So if some of the data that the driver software is getting back from you contains stuff it didn't expect, or the timing is wrong, it might choke on it in unexpected ways.
What does bit-banging the SDLC imply - doing the ethernet part directly on the LPC1769? Your comment makes it sound like you wouldn't be using the SCC at all in that case, but I'm not sure how that would work.
Thanks for trying to get my mind rolling! I'm actually not exactly sure what happens. I think the problem is that I no longer receive LocalTalk packets from the IIci, but I'm not sure if I'm supposed to be receiving more LocalTalk packets or not after the IIci hangs in the middle of communication. A few times it seemed like rebooting my microcontroller program fixed it, but rebooting my microcontroller program doesn't always fix it either. I tried some Wireshark sniffing last night and it looked like Wireshark was missing out on a lot of EtherTalk frames FROM my netatalk server (like only getting 5 of 8 pieces that are supposed to be transmitted), so I'm not sure if that is involved in the problem at all. I should probably make sure that Linux isn't dropping incoming Ethernet packets.
The SCC is essentially a fancy UART like you said, so it definitely shouldn't be getting into any weird states. However, it really is a weird chip with a weird programming interface. I have noticed that it has a lot of weird behavior with its interrupt flags that can cause strange problems. In one instance earlier in this thread, I wasn't initializing the value of some interrupt flag the way that Apple was, and it was the difference between working and not working -- even though I'm not using interrupts at all. At this point I'm pretty sure I'm initializing it the same way the Mac OS does.
Good thoughts on the non-robustness of Apple's drivers. One difference I noticed earlier is that I start transmitting while the IIci's still busy transmitting its trailing sequence of bits to end the packet. It didn't seem to cause any problems in my small testing, but maybe it occasionally causes an issue. I should fix that.
Yeah, by bit-banging the SDLC I meant doing what the SCC does, but just with the microcontroller pins. Manually doing the waveform that the SCC generates. It's a really simple waveform. Basically a "1" bit toggles the line once at the beginning of the bit and a "0" bit toggles the line twice per bit (once at the beginning of the bit, once halfway through). Oh, and a "0" is inserted if you get 5 "1"s in a row. I'd still do Ethernet externally at least until I have it working reliably. I think it's possible to bit-bang it, but I will definitely spend some more time troubleshooting the SCC first. An advantage of bit-banging the SDLC is that it would lower the final cost of making this adapter. I'm still considering the trade-offs between dealing with the SCC's quirks and dealing with my own quirks involved in bit-banging.
"I wasn't initializing the value of some SCC interrupt flag the way that Apple was."
I thought you're exchanging data between your microcontroller and the SCC chip on your IIci, which is initialized by the LocalTalk driver built into Mac OS. But this sounds like you have some custom software on the Mac that initializes the SCC directly.
Here's my understanding of what you've built so far, which I think must be flawed:
Mac IIci running standard Mac OS
SCC chip in the IIci controls the Mac's printer port
Mac OS controls the SCC, for use with LocalTalk over the printer port
A microcontroller is hooked to the Mac's printer port, using a custom cable
The microcontroller is also hooked to a PC running Linux
The microcontroller program essentially copies/converts data frames between the Mac side and the PC/Linux side
Linux program does more complex stuff, and sends/receives ethernet packets
I think this must be wrong, because I don't see how the SCC could be bypassed or eliminated with this setup, as you're talking about trying to do.
Yeah, exactly--I have a second SCC chip. Sorry I didn't make it clearer! To correct your earlier understanding:
Mac IIci running standard Mac OS
SCC chip in the IIci controls the Mac's printer port
Mac OS controls the SCC, for use with LocalTalk over the printer port.
A microcontroller is hooked up to another Z85C30 SCC through a bunch of GPIO pins.
This other SCC is hooked up to the Mac's printer port, so thus, the Mac's SCC and my SCC are hooked together through the printer port.
Microcontroller configures my SCC to talk LocalTalk
Microcontroller listens for LocalTalk packets through my SCC
Microcontroller listens for incoming "Ethernet" packets through UART and sends them over LocalTalk through my SCC
So the problem I saw in the past was when I was configuring my SCC, it was behaving really weirdly. There are a lot of different options in the SCC, and I was sure I had everything set up perfectly. I finally got it to work by looking at what Apple does with their SCC, and copying their setup algorithm and using it on my SCC. The only difference was Apple was setting something up on an interrupt register and I wasn't doing the same thing on my SCC.
When I'm talking about bypassing the SCC, what I mean is that I think I could bit-bang the raw SDLC FM0-encoded communication rather than use another SCC to do it--the 120 MHz LPC1769 should be plenty fast enough to do it, especially with some timers doing input capture and output compare stuff with pins.
OK, so I should be a little embarrassed. I did find one issue, and it turns out it was my UART driver.
My circular buffer for reading incoming bytes from the UART (in preparation for sending over LocalTalk) was only 1 KB, and it was overflowing. I turned on an LED if the buffer was full when trying to add a received character in the interrupt handler, and...yeah. The LED was turning on as soon as I did a big file transfer. 1 KB is not very big. I raised it to 8 KB and it's much more reliable now.
That solves one of the issues with the SCC, and it wasn't even the SCC's fault. The other issue I'm seeing now is that sometimes the SCC just completely quits notifying me that new data bytes are coming in from the IIci. I'll have to start dumping the SCC's registers to see if I can find something that's acting up. (Or maybe it's another bug in my UART driver )
Ah, now I get it. I had assumed you were already bit-banging data from your micro to the Mac's SCC, as you're now considering doing. If it's not too difficult, that simplification certainly sounds good to me!
I'm sure you're well past the point of considering other microcontrollers, but for the future, I think the Cypress PSoC could be excellent for these kinds of projects. It's an ARM core combined with a bunch of CPLD-type logic. I've just started playing with one of their dev kits, and it's impressive what the combination can do. Things like parity checking or "insert a 0 after every five 1s" can easily be done in programmable logic, leaving the microcontroller core to handle the higher-level stuff.
Yeah...I think it should be possible with my microcontroller running at 120 MHz
I've actually played with those Cypress PSoC chips. They're pretty nifty! The SCSI2SD project uses them. The programmable logic built-in is a really cool feature -- I need to learn more about how to do stuff in VHDL/Verilog. I really wish they had a Linux toolchain, that's my only real complaint about them.
I wrote some code to dump all the SCC registers and ran it after the SCC was hung up--the last problem I've been seeing. I discovered that the RX overrun bit was set, meaning the small 3-byte FIFO in the SCC had filled up before I had read it. Sure enough, I turned on an LED as soon as that error condition occurs -- it turned on at the same point where the SCC was hanging on me! So I guess I'm occasionally not reading from the SCC fast enough (which is somewhat surprising, but whatever). The newer ESCC has an 8-byte FIFO, which would be nicer. Anyway, it turns out that when this error bit is set, it stops the receive FIFO from working. So I added code to reset the error conditions after I try to read a frame. It's important because other error conditions like the CRC being bad would probably have done the same thing.
Apple's sample code had a function called resetRx() that I had to implement. They weren't clear about what it was supposed to do -- I used it to flush the receive FIFO like they told me to do. It appears this function is also supposed to clear the error status. After I did that, everything's working just peachy!
I also have an idea about how to stop myself from transmitting while the Mac is still sending the trailing 1 bits (abort sequence) to end the frame. It doesn't seem to be causing any problems, but it bothers me and would cause problems on a shared LocalTalk bus. It looks like bit 7 of RR0 (break/abort status) should stay at 1 until the abort sequence is complete. I can probably wait until that bit is 0 before transmitting a frame. That test will be for another night...
Doesn't it cause a problem when the SCC's FIFO overflows, even if you're now resetting the error flags? I assume you're using an interrupt to signal when a byte should be read from the SCC… maybe there's a section in your code where interrupts are temporarily disabled, and if a few bytes arrive during that interval, overflow occurs. Otherwise it seems surprising that a 100+ MHz microcontroller couldn't service a serial connection fast enough to avoid overflow.
Once you've got things working, you should make a YouTube video of your Plus on the internet, using your hardware…
Hehe, thanks! I definitely will make a YouTube video once it's up and running with MacIP.
I'm actually not using interrupts when I read from the SCC. Instead, I implemented a polling algorithm that was provided with a few pages of sample Pascal code in Inside AppleTalk. While an LLAP packet is coming in, the processor is dedicating itself solely to polling the SCC (except for servicing occasional UART interrupts). This might not work well in the future if I also have to handle Ethernet at the same time in the microcontroller, but we'll see. But with that polling, it really shouldn't be losing any characters. Hmm, maybe a separate part of the program gets stuck waiting if I'm trying to send more data than what my 1024-byte UART TX ring buffer can contain at a time...but other than that, yeah.
It might also be that the FIFO overflow gets set sometimes if both sides try to transmit at the same time or something like that--the transmission might get corrupted and the polling algorithm might not try to read from the SCC during that time because it thinks it's transmitting. It might be bad that the FIFO overflow bit is being set, but I'm not too concerned. It's honestly not a huge deal to miss out on a single LLAP packet every once in a while, because all the protocols on top of it are built on the understanding that there will be collisions from time to time and a packet might get lost.
I know that interrupts would probably be a better way to talk to the SCC and write a program that doesn't have to poll in a packet, but the sample code was sitting there for me. Interrupts on the SCC are also kind of annoying because it requires a separate type of interrupt acknowledge cycle, similar to a read or write cycle but with a different pin. When I was getting everything going, the SCC manual was getting incredibly boring to read, so I decided to skip interrupts and get LocalTalk actually working . I'm bit-banging the read and write cycles to the SCC, so I'd also have to take care of what happens if an SCC interrupt occurs in the middle of a register access. I guess I could turn off the GPIO interrupt on the SCC's interrupt pin during every register access or something to ensure that interrupts happened between SCC accesses by the main loop. For now though, the plan is to not bother with interrupts, and if I focus any more effort on the raw LLAP communication, it will probably be on switching to bit-banging the raw LLAP frames and taking the SCC out of the picture entirely.
Last SCC hurdle was overcome (how I was starting to transmit before the Mac was finished with its abort sequence). It turns out the bit I talked about looking for in my last post didn't work. It gets set when an abort sequence of 1s begins, but it doesn't clear itself until a new zero bit arrives, which won't happen until the start of the next message. I was hoping it would clear itself if 1 bits stopped arriving, but that's not the case.
The Mac has to do a similar thing when it replies to my messages, so I looked at it from the Mac side. With my scope, I experimentally determined that my IIci is actually timing it. I shortened my abort sequence and the IIci was still waiting the same time. So all I had to do was add an appropriate delay after I receive a message. Now everything works just fine.
At this point, the SCC is working reliably and so is the LocalTalk forwarding. I think it's about time to turn this thing into a preliminary PCB. I can think about bitbanging the SDLC traffic later. The fun is just starting!
Update: I just tested connecting to my Mac Mini netatalk server through this forwarder with a Mac Plus running System 3.3 (thanks, BMOW, for making the floppy emu which made this process painless and dare I say fun) and my IIci with various system versions from 6 to 8.1. They all worked perfectly. Yay