Monday, October 22, 2012

A Non-Update

After devoting a lot of personal time over several months to this project, my attention has necessarily turned elsewhere. Activities such as client projects, where an internal release is pending at the end of this month, have priority. They are paying the bills, after all.

That said, I have not abandoned this project. I still plan to release the Verilog source for my 4004 CPU implementation later this month. Also on my "to do" list for later this year is characterization of the DRAM array, and the layout of the remaining four PCBs to complete the CPU.

Sunday, September 30, 2012

Like a Phoenix

Like the mythical Phoenix that rises from its own ashes, this project has taken wing.

While writing the last post I started to say, "The possible causes are too numerous to itemize." Once posted, I realized that the problem was so repeatable that the problem was likely in the soft implementation of the 4001 ROM, which I'd hacked together rather quickly. I really intended to leave it like that, but my brain wouldn't stop thinking about it.

One of the hacks in the soft 4001 code was to only assert the external data bus during the portions of the M12 and M22 phases while CLK2 was asserted, when I knew the soft 4004 was sampling the internal data bus. That's a far narrower period than a real 4001 would drive. After some research I concluded that I could safely expand it to the entire M12 and M22 phases, which is what I believe a real 4001 would do. That worked OK in simulation (i.e. no invalid states with multiple drivers), so I had to try it with the real IP board.

And it worked! My logic analyzer has a limited buffer and I was only able to capture the first 260us after POC de-assertion. That's a bit more than 16 machine cycles, sampled on the rising edge of the 20ns sysclk. During that period it executed two NOPs, a JUN, three consecutive JMS followed by three consecutive BBL (nested subroutine calls and returns, which test the EA counter and all four rows of the IP array), a LDM (load accumulator), and a JCN (jump condition). That tests most of the functions of the board, and I think that's pretty good!

Of course, this really should work, as the underlying design was in production for 15 years (1971-1986). But this also validates the work I put in selecting components, laying out the board, and placing and soldering the 500-some components on the board. Not to mention over a thousand lines of Verilog HDL. Not too shabby for a software engineer playing at electronics engineering.

Note that I haven't added any of the extra charge-storage capacitors I laid out on the board; this is purely using the MOSFET Gate capacitances and whatever small capacitance the PCB traces add for value storage. Apparently that's sufficient with a 1.36us cycle time. At some point I'll do some margin testing with the clock rate and see just how fast and slow this will run.

It starts... and crashes

Just for a laugh I chopped out the Verilog version of the Instructor Pointer board and hooked up the real IP board. With a logic analyzer connected to the interface between the two I triggered the recording on the falling edge of the Power-On Clear output. At first I got garbage, then realized that the soft reset was far too short to clear the real IP DRAM array. Holding the POC input button I reset the analyzer's trigger, held my breath, and released the reset.

During the first machine cycle the hardware IP board reports an address of 000 (hex) at the appropriate time, which is correct. The soft 4001 ROM responds with 00 (hex) — a NOP instruction — which is also correct. So far, so good. During the second machine cycle the IP board reports an address of 001h, which suggests that the incrementer circuit is working. The soft ROM responds with another NOP, again correct.

The third cycle produces address 002h, more evidence of a successful increment. The instruction fetched is 40h — a JUN, or unconditional jump, a two-cycle instruction — again correct, and the JUN+JMS and SC signals from the soft instruction decoder switch states at the proper time. The fourth cycle produces address 003h and the byte fetched is 0Bh, the proper destination address for the jump. What should happen here is that, instead of incrementing the current IP value, the IP board should store the destination address of 0Bh. This apparently is not happening properly, as the next address to come out is F0Fh. From there it goes F10h, which is wrong but makes sense, 101h, which doesn't, and downhill from there.

The signals being fed into the IP board look roughly correct, based on the both simulators' outputs. The problem appears to be fairly repeatable, which will make it easier to debug. In fact, it's so repeatable I suspect the problem is that the soft 4001 ROM isn't outputting the data long enough to meet timing with the real IP board. If this isn't it I may have to probe some internal signals, which will necessitate soldering some wire loops to the board to form test points I can grab with a micro-clip.

Unfortunately I don't have time to dig into the problem further tonight.

A cheaper FPGA reference board

One of the things I found time to look for was a cheaper reference board. The Digilent Spartan-3E Starter board I'm using is packed full of features, and as reference boards go is quite reasonable at around $200 (USD). However, it's gross overkill for this little application. I'm using 3% of the FPGA's slices and LUTs, and none of the other on-board hardware.

I came across some really hobbyist-unfriendly products in my search. One product came with a free toolchain license, but it automatically synthesized logic to monitor how long the FPGA had been running and shut it down after 24 hours. The cost of the lowest tier license that didn't produce self-terminating loads was prohibitively high, and required expensive, yearly renewals. I don't care how good their products are or how cheap a single chip is, the lack of a usable toolchain makes them not suitable for hobbyist use.

Poking around a bit further I found a demo board with a Lattice HX1K part that sells for $20 (USD). Yes, that's 10% of the S3E board's cost. There's no extra hardware on this board, just the FPGA, an oscillator, a SPI Flash ROM, and an ATMEL microcontroller that implements the USB programming interface. The board is in a form factor that supports both PMOD and ChipKit interfaces, for those who value such features.

Running my current 4004 CPU sources through their toolchain produces a load that uses about 40% of the logic cells and one of the 16 block RAMs (it put the 64-bit scratchpad array in a 4K-bit block RAM, but not the 48-bit instruction pointer array). The remaining block RAMs are sufficient to support a full set (16 each) of 4001 ROMs and 4002 RAMs, and there should be enough logic cells to interface them. Power consumption at full speed is spec'd as a small fraction of the quiescent consumption of the S3E, though it's also a small fraction of the capacity too, and requires only two voltage sources rather than three. For more ambitious designs there is another chip in the same family that has 4x the logic cells, though it's in a larger package (TQ144 vs VQ100) so you can't just swap chips on the same board.

There is a free development environment license (Windoze-only *grumble*), though the license is only valid for one year. I'm hoping they'll allow hobbyists to request a new license. After all, hobbyists sometimes turn into consultants who recommend products in their professional capacity.

My plan is to continue to use the Xilinx S3E as a target, as I'm much more familiar with the Xilinx ISE development environment, but with an eye toward making sure it'll port easily to the Lattice HX series.

No code posts this weekend

Firstly, work is sucking up my time, and I literally haven't touched the code since early last week. Secondly, my lawyer friend is totally saturated with end-of-quarter work and hasn't been able to review my proposed license disclosure verbiage.

So I won't be posting the Verilog source code for my 4004 CPU implementation this weekend. Rest assured, though, it will come, and will be under the same Creative Commons non-commercial license as the original Intel materials.

Wednesday, September 26, 2012

OpenCores project created

A couple of days ago I created a project on the OpenCores.org website for the Verilog version of the MCS-4 components. There's a direct link to the project here, or you can look under the "Processor" projects.

The project is currently empty, other than a general description and a couple of links. Since the Verilog is derived directly from the Intel schematics, I want to make sure all of the Verilog source files have appropriate copyright and license notices before posting them. This may happen this weekend.

I've started to notice an up-tick in the number of hits on this blog from that project. For those of you looking for more background on the Verilog aspect of this blog, here are a few links to some of the early entries in this blog you may find most helpful:
That should be enough to get you started. I've also begun tagging the postings based on their content.

Sunday, September 23, 2012

It's a RACE!

I've been switching back and forth between the hardware and Verilog versions of the 4004 CPU, based mostly on mood. The Verilog version had been moving ahead so quickly I was thinking I'd have it working before I got the Instruction Pointer board fully populated and tested, but then I ran into the inevitable bugs in my translation from schematic to Verilog.

Suddenly I have an Instruction Pointer board which needs testing, with nothing to properly test it. When my plans for the evening were rescheduled, I got some time to focus on the Verilog version.

Most of the problems I've run into have either been from misreading the schematic, or from dropping a negation in the constant polarity reversals. Let me explain the latter: the type of circuits used in the 4004 are either NOR or NAND gates. This means that the output is the negation (i.e. "NOT") of the basic logic. When they want to OR two signals the output comes out inverted (if either A OR B is 1, the output is 0, else it's 1). The same thing happens with a NAND (if A AND B are 1, the output is a 0, else 1).  Every time I trace a signal through series of logic circuits I have to account for this reversal. If I do a verbatim translation to Verilog I usually get it right, but if I need to do a bit of creative interpretation the chance of getting it wrong goes up.

Finding such mistakes can be a challenge, but this is a geek's version of a good detective mystery. Every possible clue must be considered; some clues are misleading, but others lead you closer and closer to catching the culprit. It's something I'm pretty good at, and it's fun when there isn't a deadline hanging over my head.

A little while ago I was very excited, thinking I had the system working. The simulator had successfully fetched and executed the first two instructions, but then the simulator screen full of nice, green signal traces (valid states, either 0 or 1) turned to a cascade of red (invalid or unknown states, neither 0 or 1, represented as 'X'). The first clue was that the first two instructions are both NOPs (No-Operation), which are coded as '0000'. The third instruction, which was being fetched when things blew up, was a JUN (Jump Unconditional), coded as '0100'. For 20 nanoseconds, one internal clock cycle, the data bus showed '0100', but then it changed to '0X00'. That started a cascade of red 'X' states as one signal after another became unknown. It took about an hour (including time spent petting the cat while pondering the problem) to track it back to a missing inversion in the generation of the OPR-IB signal; this caused a second source to attempt to drive the data bus to '0000', and the conflict caused the failure.

I'm not claiming I've fixed all the bugs, but the simulator just stepped through a sequence of JMS and BBL (call and return) instructions, which tests a whole lot of logic in the Instruction Decode and Instruction Pointer groups. This is enough to be able to test the Instruction Pointer board pretty thoroughly.

So the race is on!