A mc68k-based computer motherboard, some expansion cards, and a very basic multitasking OS, all built from scratch.
The following is a rant. If you're not in the mood for reading a rant, please don't bother reading on.
So: the LM3671 dc-dc converter. According to its data sheet, "...only three tiny surface-mount external components required (one inductor, two ceramic capacitors)". Astonishing! Compared with an LDO, the cost of an extra inductor buys you a switching converter with much better efficiency. Unless, of course, it doesn't.
Some background: I'm building a video card for the MC68000 system. I'm using the design of the video card as an opportunity to validate some tech that I'm going to use in an MC68030-based system that's coming along soon. The video card will attach to one of the expansion sockets on the MC68000 motherboard; these provide +5V power. The video card uses a LM3671MF-3.3 and a LM3671MF-1.2 to develop the 3.3V and 1.2V supplies required by the card. Easy! So I thought, anyway...
The LM3671 features an enable pin, EN, which enables or disables the regulator. On all the regulators I've ever worked with, such a pin can be strapped to the input voltage rail in order to force the regulator into a permanently-on state - this is the required behaviour in the majority of cases, after all.
There's an interesting throwaway sentence on page 18 of the LM3671 data sheet: "... It is recommended to set EN pin low to turn off the LM3671 during system power up and undervoltage conditions when the supply is less than 2.7V...." This appears to be the only concrete recommendation in the datasheet regarding the EN pin.
It turns out that this "recommendation" is actually rather more of a requirement. In my design, the EN pin is connected directly to the input voltage supply - a regulated +5V - because I want the LM3671 to be operational all the time the +5V supply is present.
It appears that this arrangement is not suitable for the LM3671-1.2 and the LM3671-3.3. What I see, at the output of both regulators, is a rough triangle wave, sawing between 0V and about 2V. The regulators draw an effectively unlimited amount of current in this state, and present an output that is not only unregulated, but swings far above the rated output voltage. I have reproduced this result on multiple LM3671 devices, using a pcb layout which is nearly identical to the layout suggested in the device's data sheet. I am not the only person to have encountered this problem.
So: my conclusion is that I will definitely not be using this device in future, and I will be suspicious in the extreme of any device making similar claims regarding the number of external components required to make an effective regulator. It's a shame, as the device promises so much; I guess the rule is, as ever: if it seems too good to be true, it probably is. Caveat emptor.
This is the shortest log ever, written mainly because a) I'm feeling guilty about not having written a log for so long, and b) to prove that I'm still making progress on the MC68000 computer project.
I've already got an ATA (IDE) interface and serial ports on the MC68000 motherboard; other project logs describe my adventures creating Ethernet and PS/2 controller expansion cards for the system. The obvious next step is therefore to add video support. This is what I've been up to for the last month or two.
Initially I designed a framebuffer card based on an MC6845. I managed to get a viable-looking design together using some fast SRAMs, a couple of high-speed shift registers, a triple video DAC and a *lot* of bus transceivers. The prototype looked a bit like this while it was under construction.
I became a bit demotivated when the time came to route the (gigantic) PCB. I have already routed one fairly gigantic PCB (the motherboard), and it can be a real time-sink. So: I ended up breaking a few of my own rules.
So far I've kept the MC68000 project fairly "vintage". Except for the Ethernet controller, the system uses technology consistent with the 68k's era: no surface-mount, all DIL packages, no fancy stuff. The point of the project, however, has always been to learn as much as possible. Readers of my other logs will know that I've tried to avoid using too much pre-packaged stuff; there are plenty of good, easy-to-use 80s/90s-era video controller ICs out there, and it would be quite easy to throw together a graphics card based on one of those, but where would be the fun in that? So, I thought - stick with me on this - what better way to learn about video hardware than to design my own video controller IC, and build a graphics card around it?
I started a design based on a Lattice iCE40HX1K FPGA. I had already played around with a Lattice iCEstick and found the FPGA (and, crucially, the tools) quite easy to use. I learnt a bit of VHDL, and implemented a VGA pattern generator:
Having done this, it wasn't much of a leap to start work on a graphics card for the MC68000 system. I settled on a minimal specification, to keep things straightforward.
|On-board video RAM||1MB SRAM (512k x 16), 12ns access time|
|Supported video modes||640x480, 800x600, 1024x768 (all at 60Hz)|
|Colour depth||8 bit (3:3:2 R:G:B) via ladder DAC|
|Interface||15-pin high-density D connector (analogue VGA)|
|Host interface||16-bit data bus, direct-mapped RAM, zero wait state, zero contention|
|Host interrupt support||VSYNC / HSYNC / both|
Anyway, the design is done and the PCB is ready to go to manufacture. I'm still working on the logic implementation in VHDL, but I'm reasonably confident that it can all be made to work.
Here's a poorly-rendered 3D image of the completed board.
The design is almost entirely surface-mount. The two ICs in the top-right corner are the SRAMs; the FPGA is in the centre. The ladder DAC is the collection of resistors to the right of the 15-pin D connector. Most of the remaining ICs implement the host interface; the host connector (a DIN41612 plug) is missing from the image, as I don't have a 3D model for it. The 10-pin header connector below the VGA socket is a programming header for the SPI Flash ROM which contains the FPGA configuration bitstream.
I hope to be able to upload a photograph of the real thing very soon!
I spent a little while reworking the network stack in the m68k OS code. The idea is to end up with a reasonably generic stack: ideally, like Linux and friends, the OS code shouldn't really care about what kind of network adaptors are plugged in, and should support a variety of higher-level network protocols.
Having done a fairly substantial refactor, I was able to get basic protocol handling and routing working. The network stack knows how to enumerate attached network adaptors and to instantiate one kernel process per adaptor to deal with incoming network traffic. Each network protocol is written as a separate module, conforming to a common API; these are "discovered" at boot time, after the system hardware discovery phase, and started independently.
I made a 10/100Mbps Ethernet adaptor using an ENC624J600; I've written about this network card in an earlier log. On boot, the OS detects the network card and loads its driver; the network stack creates a virtual device, eth0, to represent the Ethernet interface. Multiple network adaptors are supported - a separate virtual device is created for each card.
The screenshot below shows the OS booting, performing hardware discovery and other startup tasks, starting the various network protocol drivers, and finally dropping into the interactive shell (lines beginning with the "$" prompt). The output looks quite Linux-ey, but it's not Linux - all of the software running on the system is my own work.
Having reached the shell, I then enter some "route" commands to set up the kernel IPv4 routing table (I have implemented basic IPv4 routing), and use the "netif" command to add an IPv4 address (172.16.0.12) to interface eth0. Finally, the "schedule" command starts the task scheduler. The system is now ready to deal with network traffic!
So, starting from the bottom of the network stack: after writing an Ethernet protocol driver, I added support for ARP. This enables the kernel to maintain a mapping between hardware addresses (e.g. MAC addresses) and protocol addresses (e.g. IPv4 addresses). All ARP traffic is handled transparently within the kernel; once the system has been up for a while, and has gathered a bit of ARP traffic, it's possible to dump the contents of the ARP table and see details of various network neighbours. All address-handling is fully generic; the output below just happens to show Ethernet and IPv4 addresses.
Next up, after ARP, I wrote a very basic ICMP driver. This enabled me to reach an important and deeply satisfying point: pinging the board! The screen-grab below shows the result of pinging the MC68000 system from a Linux host elsewhere on the LAN. The latency isn't great; this is mainly a consequence of the rather primitive task-scheduling logic. I hope to improve this in future.
I need to do a little more work on the low-level protocols before I can make a serious attempt at implementing TCP and friends. A half-decent TCP implementation is going to take a lot of work, but it should be an interesting challenge.
It's been a long time since I wrote an update on the project. My excuse is that I have been working on a new hardware platform based on a Motorola 68030 processor. This system will run the same OS code as the MC68000 system; I'm going to add virtual memory support to the code. The main focus at the moment is on designing a workable memory subsystem using 72-pin SIMMs. This has been a lot of fun, and I'll probably set up a separate project page soon to describe my work.
This is a short log[*] describing the 16-bit expansion card interface in the 68000 system. I've deliberately kept the design of this interface as simple as possible, in order to make it easy and quick to build expansion cards. I know I could have implemented a standard interface, like VME, S100, or (shudder) ISA, but I didn't. I felt like I'd learn more by implementing my own interface, mainly so that I could discover all of its shortcomings!
The motherboard has four expansion sockets, all with an identical pinout. Here's the setup:
The motherboard sockets are actually DIN41612 "Q" connectors - i.e. the motherboard has a male (pins) connector, and the peripheral card uses a right-angled female (sockets) connector.
Most of the pins are directly mapped to the 68000 bus interface; some are a little different. This table describes the pinout. In the table, "input" means "signal generated by the host", and "output" means "signal generated by the peripheral". "Host pullup" indicates that there's a 10K resistor pulling the pin to VCC on the motherboard.
|VCC||Power||Regulated +5V rail|
|D0-D15||Bidirectional, 3-state||Buffered CPU data bus; only valid when nCS is asserted.|
|A1-A23||Input, 3-state||Buffered CPU address bus; only valid when nCS is asserted.|
|nID||Input||When asserted with nCS, indicates that the host processor is running an "identify peripheral" cycle. The peripheral should place its ID on D15-D8 and assert nACK.|
|nIACK||Input||Asserted when the host processor is acknowledging an interrupt raised by the peripheral.|
|nRESET||Bidirectional, open-drain, host pullup||System reset input. Can be pulled low by a peripheral to reset the system.|
|CLK||Input||Host processor clock.|
|nIRQ||Output, host pullup||Peripheral interrupt request.|
|nBERR||Output, host pullup||Peripheral bus error output.|
|E||Input||6800 peripheral interface: "E" clock signal (10% of f(CLK), 60:40 duty cycle).|
|nACK||Output, host pullup||Peripheral bus cycle transfer acknowledge: asserted by the peripheral to complete a bus cycle.|
|nCS||Input||Peripheral chip select: asserted by the host during a bus cycle addressed to the peripheral.|
|nUR||Input||"Upper byte read": asserted when the host wishes to read bits D15-D8. Only valid when nCS is asserted.|
|nLR||Input||"Lower byte read": same as nUR, but applies to bits D7-D0.|
|nUW||Input||"Upper byte write": asserted when the host has placed valid data on bits D15-D8 during a write cycle. Only valid when nCS is asserted.|
|nLW||Input||"Lower byte write": same as nUW, but applies to bits D7-D0.|
|nVMA||Input||6800 peripheral interface: "valid memory address" strobe.|
|nVPA||Output, host pullup||6800 peripheral interface: "valid peripheral address" strobe.|
|nPD||Output, host pullup||"Presence detect": should be tied to GND by the peripheral. Enables the host to detect that a peripheral is plugged in to an expansion slot.|
Each slot is mapped to a fixed 1MB region of the MC68000 address space. The expansion interface exposes all 23 of the MC68000's address lines. This might seem pointless, given that only the lower 19 lines are needed to address a 1MB space; however, exposing the entire address creates the possibility of plugging a bus analyser into a slot in order to help with debugging.
The nPD (presence detect) lines on each of the four expansion slots are connected to general-purpose inputs on an MC68681 DUART. At boot, the operating system code uses the status of these pins to determine whether any peripheral cards are attached. If a card is found, the OS runs an "identify" cycle to work out what's plugged in. To do this, the host asserts the nID line and runs an eight-bit read cycle on each occupied expansion slot. The peripheral must respond by placing an ID number on the upper eight bits of the data bus and terminating the cycle with nACK. This enables the OS to initialise a driver for the attached peripheral.
Here's a screenshot of the output generated by the system during the boot process, showing the OS identifying some peripheral cards (lines highlighted in red). Having found two Ethernet cards,...Read more »
As usual, it has been a while since my last update. I've been busy working on some new expansion cards for the MC68000 motherboard. It's a bit boring interacting with the system via an RS232 serial port, so I thought I'd build an expansion card enabling me to connect the motherboard to a keyboard and mouse.
The first step in the design was to pick a communications protocol. I wanted to implement a standardised protocol so that I could use an ordinary keyboard and mouse without modification. Having ruled out ancient XT keyboards, I was left with USB and PS/2. USB felt too modern for this project - after all, the idea is to build a system which has a level of technology roughly in line with the sort of stuff that was available in the 80s and early 90s. PS/2 it is, then! To the breadboards!
Here's the prototype dual-channel PS/2 controller plugged into the MC68000 motherboard.
The PS/2 protocol is pretty straightforward. It's an 8-bit synchronous bidirectional serial interface in which the "device" (the keyboard or mouse) generates a clock signal at around 10kHz and exchanges data with the host in a format quite similar to RS232. In PCs, the interface was implemented using an Intel 8042 microcontroller. When a key is pressed on the keyboard, a scan code is sent by the keyboard to the host. This code uniquely identifies the key, but is not related to e.g. the ASCII code representing the symbol on the key-cap. When the key is released, the keyboard sends a "key released" code (0xF0), followed by the scan code of the key. The PS/2 mouse protocol works a bit differently: whenever the mouse is moved, or a button is pressed or released, the mouse sends a small packet of data (three or four bytes, depending on the type of mouse) containing information about the mouse's relative motion and the state of the buttons.
I considered building the controller around an Intel 8042, but eventually decided against it. It's a decent enough chip, but I felt that I wouldn't learn the protocol in sufficient depth by using somebody else's implementation. Instead, I decided to build an interface around a microcontroller: the (admittedly fairly modern) Atmel ATmega8. This is what I came up with.
The ATmega8 has 28 pins, which is just about enough to implement two separate PS/2 ports and an 8-bit interface to the host system. The firmware is quite simple: it's mainly an interrupt-driven state machine which manages communication between the microcontroller and the PS/2 devices. The non-interrupt-driven part of the code is pretty much just a loop which deals with the interface between the controller and the MC68000.
The controller exposes a bunch of registers to the host processor; these include a configuration register which can be programmed to enable interrupts to be raised when various different conditions are encountered on each port: byte received, transmission completed, timeout, parity error, etc. The controller also contains a 256-byte FIFO for each PS/2 channel (unlike the Intel 8042!); this means that the host doesn't need to respond immediately whenever data is received from the peripherals.
There are horror stories about the damage caused by unplugging PS/2 peripherals from early PCs. Apparently the electrical interface wasn't very well-protected, or whatever; if the rumours are to be believed, a motherboard could be destroyed by connecting or disconnecting a PS/2 keyboard at the wrong moment. It's difficult to know how much of this is true, but given that my controller connects to the outside world, it needs to be reasonably tolerant of abuse. With this in mind I have added short-circuit protection, protection against shorts to out-of-range voltages (within reason), and some transient/noise suppression. In order to make the interface somewhat hot-pluggable, I've wired things up so that the ATmega8 can switch each PS/2 port's power on and off independently.
Another slightly annoying thing about early PCs was the need to connect the keyboard and mouse to...Read more »
I'm about to start assembling the first revision of the Motorola 68000 motherboard. This revision, rev1 (I start my revisions at rev0), includes a few fixes for minor problems in the prototype. None of these problems were show-stoppers, and I was able to craft fixes where necessary in order to get the rev0 (prototype) motherboard working. More importantly, rev1 adds some minor new features to the board. Here it is.
Here's an example of a rev0 bug I needed to fix. This is my hacky fix for a wiring error in the capacitor network which the MAX238 RS232 driver/receiver IC uses to drive its internal voltage converters...
The second revision fixes these bugs in the prototype:
A few other, more cosmetic, problems are also fixed in rev1:
New features in rev1 include
So: I'm about to start populating the board. The last one took about 8 hours to solder; this one should be quicker, as I will be doing a lot less testing as I go along. I also won't be socketing all the ICs this time around; I'm only going to socket the expensive ones.
Wish me luck...
It has been quite a while since my last update, and the project has moved forward a long way! I’ve made a lot of progress with the operating system software (I’ll be writing a log about this soon) and the hardware has advanced a bit too.
I designed a 10/100Mbps Ethernet peripheral card, based on a Microchip ENC624J600, for the system. This enabled me to validate the motherboard’s peripheral interface design, and - more importantly - to start writing networking code in the operating system. Here’s a picture of the bare pcb next to a populated board.
The bare pcb is actually rev1 (i.e. the second version of the board - I start my revisions at 0). It contains two minor fixes to the rev0 design.
I mentioned in an earlier post that I'm trying to keep the whole design as close as possible to Motorola 68000-era (i.e. 1980s) technology. As time goes by, that's proving to be more of an aspiration than a rule set in stone. When considering designs for a network card, I looked at a few 80s/90s-era Ethernet controllers but - unsurprisingly - none were as capable or as easy to use as the ENC624J600. Also, magnetics (i.e. Ethernet transformers) suitable for these old parts are now really hard to find. I'm keen to avoid using parts that must be scavenged from ancient PCs, or are only obtainable on eBay, so I decided to use a modern part in the design. The ENC624J600, and a few of its decoupling capacitors, are (so far!) the only surface-mount parts I've used in the system. I admit that my desire to get the motherboard attached to a network trumped my desire for that pure '80s look.
The ENC624J600 is a really nice chip. It provides an integrated Ethernet MAC and PHY, has a built-in encryption engine (albeit a slightly buggy one…), has 5V-tolerant inputs, provides 24KB of buffer RAM and is extremely configurable. What's not to like? There is even a 16-bit data bus interface - ideal for the MC68000/68010 - and the chip glues to the system bus very easily. Most importantly of all, Microchip’s data sheet for the part is excellent. It's a 3.3V part, and it's fairly thirsty, hence the enormous TO-220 LDO at the right-hand side of the card.
I've byte-swapped the data bus in this design, meaning that the upper half of the ENC624J600's data bus (D15...D8) is connected to the lower half of the MC68000's data bus (D7...D0), and vice versa. The ENC624J600 is an inherently little-endian device, unlike the big-endian MC68000. Sadly the world is now overwhelmingly little-endian (boo...), so - while Ethernet header and footer data is big-endian (yay!) - the contents inside packets is typically not. Byte-swapping the data bus in this design saves me the effort of swapping received bytes around in memory, thereby increasing the overall performance of the interface. I've used the same byte-swapping trick in the ATA interface on the rev1 motherboard - more on this in a future log.
I have written most of a driver for the ENC624J600; the operating system is able to configure the part, establish an Ethernet link, and transmit and receive packets. The motherboard peripheral interface design incorporates a way for the operating system code to automatically detect and identify plugged-in peripheral cards, so the OS can locate the card and start its driver during the boot process. There's no reason why I couldn't plug additional Ethernet cards into the motherboard, if - for example - I wanted to create an extremely low-performance network router.
Here’s a picture of the Ethernet controller card installed in the motherboard.
Read more »
I’ve also started work on a network stack in the OS code. So far it’s quite minimal - it knows how to handle incoming Ethernet packets and route them to appropriate protocol handlers (IPv4, ARP, etc.) Eventually I hope to have a working TCP implementation, but that’s going to take some time. For now, I’m happy to have reached a point where the operating system can, using a background process which listens for incoming packets, respond to ARP requests sent by...
Here's another picture of the board - this time with RAM modules, ATA CompactFlash card, RS232 cable and power supply plugged in.
The board requires a regulated 12V input. It generates a 5V logic supply using an LM2678 (the package mounted to the large heatsink at the rear of the board). A four-pin Molex socket provides 12V and 5V for an external hard drive. At the moment my mass storage is a CompactFlash card on a CF-to-ATA adaptor board.
The serial cable connects to a USB-RS232 converter, which is plugged in to my development PC. The PC runs an Ubuntu virtual machine that hosts an m68k-elf-gcc cross-compiler and toolchain. I use PuTTY for interactive sessions with the board.
I'm currently working on file system support in the OS. So far I've written a minimal virtual file system (VFS) abstraction, and the beginnings of a FAT file system driver. I'm working on the FAT file system first because it's extremely easy to implement. I am also in the process of resurrecting some old ext2 driver code that I wrote a few years ago. My goal is to prove the viability of the VFS design by supporting two different file system types in a way that's transparent to the code accessing the code accessing the files.
To test the system, I partitioned a CompactFlash card, created a few ext2 and FAT file systems on it, and copied a bunch of files and directories into each file system. The 68010 motherboard is able to read the card's MBR and partition table, and can expose individual partitions as block devices. It's a bit like the Linux block device abstraction.
I'm now at the point where I can traverse a FAT file system and list files in a directory, which is nice :)
The picture below is a screenshot of a serial terminal session between my dev PC and the 68010 system. It shows the system booting, and the output of an "ls" command.
(The references to powerpc are irrelevant - I just happen to have a copy of glibc on this file system)
1/2/4MB RAM module (this one is 4MB), before and after assembly