Tuesday, May 5, 2020

A bug in my i4004 ALU implementation

Almost eight years ago I observed in Coding the ALU in Verilog that the i4004 arithmetic logic unit used positive logic for bits 0 and 2, and negative logic for bits 1 and 3. I thought this was odd but didn't ponder the matter for very long.

Last night I discovered a bug in my implementation of the ALU: if the accumulator contained 0x0 (4'b0000), executing the CMA (Complement Accumulator) instruction set the accumulator to 0x5 (4'b0101) rather than 0xF (4'b1111). Obviously this was wrong, and it bore a suspicious resemblance to the positive/negative pattern I'd noted before.

After poking around in the ALU schematics I figured out what was happening. Like most of the bugs I've introduced, this was an error in translating the schematic representation of the i4004 circuitry into Verilog HDL. Here's the problem circuit:


This circuit's 8 FETs form a 2:1 multiplexer and 4-bit latch, and it sits between the accumulator and the ALU. Its purpose is to allow either the true or inverted bits in the accumulator to be used by the ALU in its next operation. Bit 3 is muxed by the two FETs on the far left and bit 0 by the two FETs on the far right. The upper horizontal selector (red) is called ACC-ADAC, while the lower selector (blue) is ACC-ADA. I'd coded this mux in Verilog this way:
// Feedback from Accumulator
reg n0873, n0872, n0871, n0870;
always @(*) begin
    if (m12)
        {n0873, n0872, n0871, n0870} <= 4'b1010;
    if (acc_ada)
        {n0873, n0872, n0871, n0870} <= acc;
    if (acc_adac)
        {n0873, n0872, n0871, n0870} <= ~acc;
end
If you look closely, however, you'll note that ACC-ADA doesn't always select the true outputs from the accumulator, and ACC-ADAC doesn't always select the inverted outputs. Instead, ACC-ADA selects the true outputs for bits 0 and 2 and the inverted outputs for bits 1 and 3, while ACC-ADAC does the reverse. This makes sense when you remember that the ALU bits 0 and 2 use true logic while bits 1 and 3 use inverted logic.

With this in mind I re-coded this multiplexer this way:
// Feedback from Accumulator
reg n0873, n0872, n0871, n0870;
always @(*) begin
    if (acc_ada)
        {n0873, n0872, n0871, n0870} <= acc ^ 4'b1010;
    if (acc_adac)
        {n0873, n0872, n0871, n0870} <= acc ^ 4'b0101;
    if (m12)
        {n0873, n0872, n0871, n0870} <= 4'b1010;
end
This change appears to have fixed the problem with the CMA instruction, and the DAC (Decrement Accumulator) and IAC (Increment Accumulator) now also behave properly.

1 comment:

  1. Found another bug in the ALU last night. The symptom was that the IAC (Increment Accumulator) instruction worked as long as bit 3 was zero, but if bit 3 was a 1 it incremented by two. So the sequence was 5 -> 6 -> 7 -> 9 -> b -> d -> f -> 0 -> 1 -> 2 -> 3 -> 4 -> 5.

    It took all evening, but eventually I found a one character typo: I'd typed "n0877" when I should have typed "n0887", which resulted in the output of bit 3 being fed back into the bit 0 adder.

    ReplyDelete