Keyboard controller construction and a DRAM controller

Receiving deliveries of brand new electronic components is exciting!

The order from Mouser included a part for the keyboard controller board and multiple parts for MAXI000. On the same day I also received the previously described PCBs from allpcb.com. Here’s the front of a keyboard controller board:

As usual they look great. A quick test of the updated RJ10 socket footprint resulted in a good fit; the mounting holes are just big enough to accept the posts. No drilling required this time.

All but one of the parts used on this board was sourced from ebay. The part in the Mouser order for it was the 8MHz oscillator can. Straight away I noticed a problem: the footprint was for the relatively large 7mm by 5mm version, but I had ordered a 5mm by 3mm part. The problem was with the order as I tried to order the bigger part but clearly misread the specifications. Luckily the AVR MCUs can also run on an inbuilt RC oscillator, though in my experience it isn’t accurate enough to run the UART at any rate higher then 9600 baud. But it will be alright to omit an oscillator for now. MAXI09 only ever ran the keyboard controller link at 9600 baud anyway.

Because it is so short here is the full BOM (Bill of Materials) for the board:

5 x 1K 1206 resistor
2 x 100nF 1206 capacitor
1 x 10uF SMT electrolytic capacitor
1 x 8MHz 7mm x 5mm oscillator can
1 x ATMega8515 in 44pin 0.8mm TQFP
1 x 3528 (metric) RGB LED
1 x 3528 (metric) red LED
1 x 4mm x 4mm SMT push button
1 x THT 6 pin 100mil IDC vertical male header
1 x THT RJ10 right angle PCB socket
1 x THT 30 pin flex connector 5597-NCPB

The parts were attached roughly in order of smallest to biggest. The method used, for the Surface Mount parts, was reflowed, with a hand held rework station, solder paste.

I must confess to being a complete SMT convert. I wish I’d seriously looked at SMT construction years ago.

One nice illustration of the relative, yet I think surprising, ease of the construction techniques involved is in replacing a relatively intricate part like a 44 pin TQFP IC with pins on a 0.8mm pitch. After being quite chuffed that I’d attached the part nicely, it dawned on me that I’d attached it rotated 180 degrees relative to its correct orientation. Here is a rough list of the steps needed to remove and reattach this part:

  1. Remove the part with the hot air stick. I use a temperature of 320C with the airflow set to 2 (out of 10). Hold the body of the part gently with tweezers. About 10-20 seconds of heat will be needed.
  2. When the part pops off most of the solder will be attached to the pads but some may still be attached to the IC leads. Do not try to remove it from the IC.
  3. The pads, however, must be flat. Use the flat of the iron, with a bevel or chisel tip, and solder wick to remove the old solder. Flux may help the wick to suck up the solder.
  4. Clean the area of the board. My current preferred cleaning method is lint-free pads soaked with white spirit, but IPA also works.
  5. Dry the board with some hot air.
  6. Place the replacement IC so it is properly aligned. If the IC is big enough it can be held in place with a finger; otherwise tweezers will be needed. This is perhaps the hardest step. I have had good success putting the board under the microscope and nudging the part with the tweezers but it can be done with a naked eye, at least down to 0.8mm pitch pins.
  7. Tack a bunch of pins down with a little solder on the tip of the iron. It does not matter about solder bridges, just make sure the leads stay perfectly aligned with the pads. The size of the tip does not matter, though I’m happy with a fairly wide (2mm or so) tip.
  8. Apply a thin bead of paste along the leads. I’m now using a 16 gauge (1.8 mm) cannula¬† which works well. The width of the bead is not critical but slightly more is better than slightly less.
  9. The best bit: reflow the solder with the heat stick. 320C for leaded solder paste. I tend to move the end of the nozzle around to cover the part evenly instead of focusing on one area at a time, but it probably does not really matter
  10. At this point you will probably have a few bridges, especially where you tacked the IC down.
  11. Small bridges can be removed by gently scraping down the length of the lead with the flat of the iron tip.
  12. Bigger bridges can be removed with wick. Lay it along the leads and run the iron along. I tend to do this (gently) along all leads just to tidy things up.
  13. Clean and dry the IC area as described above.
  14. Review the IC leads under a microscope and, if necessary check for continuity with the multi-meter.

There are many excellent videos showing the construction of an SMT board with solder paste. This one is perhaps my favorite as it goes into a great level of detail.

Here is a closeup – picture made with the microscope – of the ATMega8515 attached to the board:

It’s not perfect but is pretty good considering I’ve not been doing this very long. It looks like more scrubbing could be employed to remove some more flux though.

Attaching the other parts was as I’ve previously described. Perhaps the trickiest bit was attaching the LEDs and, not being familiar with the markings and shape of them, figuring out the correct orientation.

And attaching the electrolytic capacitor looked difficult; they have a plastic base and I was worried it would melt, but it attached easily.

After all the SMT parts were attached the next part to attach was the 6 pin ISP header. And then, before attaching the rest of the connectors, would the controller program?

Of course not.

After reviewing the schematic and beeping out each connection between the 6 pin ISP header and the controller I spotted a problem.

The ISP header is essentially a bolt on to the old MAXI09 keyboard controller circuit. That board had no ISP header for the keyboard MCU and when adding one I made the mistake of connecting it to a pin at the far end of a 1K resistor, the resistor used by the caps lock LED. The LED and SCK for programming share the same pin. It’s not immediately clear if this resistor would prevent programming, but it’s not right and no example circuits I’ve located have a resistor on any ISP pins.

The current solution to this problem is to replace the 1K resistor with a zero ohm “resistor”. Swapping the 1K resistor for a zero ohm one took all of about 20 seconds, much faster then pulling out and replacing a THT part. A problem to solve later will be making sure the caps lock LED, embedded in the caps lock key on the keyboard, would not burn out due to over current when it’s turned on. Even after doing this, the AVR still would not program.

I next decided to solder a second ATMega8515  to an adapter board which had 100mil spaced pins. It was easy to hook it up to a 10 pin IDC header and check that it could be programmed:

For completeness, every IC lead was checked for bridges with the multimeter. Again, no luck programming the part. Out of paranoia I checked the programmer was good by programming a PLCC44 ATMega8515, as used by the MAXI09 board.

At this point I started to suspect that the MCUs I was using were faulty. Or fake.

So I duly ordered three more, this time from a UK supplier.

After doing the replacement dance, success! The MCU took the controller program and ran it. Because the MINI000 board is currently being used for other experiments – described later in this post – I cannot, yet, fully verify the functionality of the keyboard controller board. But for fun I’ve tried flashing patterns of colour on the attached RGB LED and everything looks to be operating. One minor code change, relative to the code running on the controller in the MAXI09 board, was required because the RGB LED is common anode instead of common cathode.

The last thing I had to do was attach the remaining components: the keyboard flex connector and the RJ10 socket.

Here’s a picture of the assembled board with working controller:

A closeup of the top of the AVR IC:

Looking at the picture, you can see that the pads near capacitor C2 could probably do with a bit more solder, but otherwise it looks to be a good job. And a close up of a non working one attached to the adapter board:

It’s difficult to conclude that these parts are definitely fakes, but it is a reasonable conclusion. The alternative suggestion would be that the parts have miss-programmed fuses which is preventing them from being programmed, but as they were packaged like new this should not be the case.

I think the lesson is simple: buy currently in production parts, especially ICs, from proper suppliers. Interestingly Mouser sells these MCUs for half the price that I paid for the suspected fakes.

Here is why I cannot yet test the keyboard controller board with MINI000 and its IO board:

The 72pin SIMM, is a Micron MT16D232-6 (PDF) 8MBytes 60ns part.

The schematic for this breadboard portion is approximately the following:

There is one subtle difference to what I currently have wired on the breadboard: the output enable inputs on the mux are attached to the /SIMM CPLD line – which is asserted by the address decoder – on the breadboard setup instead of being grounded.

This difference was only done so the SIMM address lines could be attached to the oscilloscope with a signal only passing through when the SIMM address is selected by the MPU. Ordinarily passing through the row address continually is perfectly reasonable. This change was made only so I could check the signal integrity on the oscilloscope using it’s triggering feature.

A less subtle difference is that the above schematic includes inline resistors to smooth the transitions on the bus lines. These may or may not be needed – they will initially be loaded with zero ohm resistor arrays – on the MAXI000 board, which is where the above schematic comes from.

DRAMs sure are an order of magnitude more complex then the SRAM I’ve been using for years. A brief description of the principles involved is best read from the Wikipedia DRAM page; I wont attempt to go into much detail here, mostly because I’m still learning.

I’m using CAS-before-RAS refresh mode. The SIMM datasheet lists a maximum time of 16 ms across all 1024 rows or 15.6 us per row, assuming refreshes are spread out and not clustered.

The datasheet explains how to use the SIMM with a 16bit wide databus, which I have to do as the 68000 has a 16bit and not a 32bit databus. This is why D0 through D15 is connected to D16 through D31. In code /CAS0 is linked to /CAS2 and /CAS1 to /CAS3, as per the datasheet. However because all CAS pins are wired in, ultimately it is down to the VHDL to determine how they are used.

Talking of code, here is the VHDL process for my 72pin SIMM (adaptable to any simple DRAM) controller:

process (CLOCK)
    variable REFRESHCOUNT : INTEGER range 0 to 120 := 0;
    variable STATE : SIMM_STATE := IDLE;
    variable NEEDSREFRESH : STD_LOGIC := '0';
begin
    if (CLOCK'Event and CLOCK = '1') then
        REFRESHCOUNT := REFRESHCOUNT + 1;
        if (REFRESHCOUNT = 120) then
            REFRESHCOUNT := 0;
            NEEDSREFRESH := '1';
        end if;
        
        case STATE is
            when IDLE =>
                if (nSIMM = '0') then
                    if (A1 = '0') then
                        nSRAS <= "1010";
                    else
                        nSRAS <= "0101";
                    end if;
                    STATE := MEMRW1;
                end if;         
                if (NEEDSREFRESH = '1') then
                    NEEDSREFRESH := '0';
                    nSWRITE <= '1';
                    STATE := REFRESH1;
                end if;

            when MEMRW1 =>
                nSMUXSEL <= '0';
                nSWRITE <= RnW;
                STATE := MEMRW2;
            when MEMRW2 =>
                nSCAS <= nLDS & nUDS & nLDS & nUDS;
                STATE := MEMRW3;
            when MEMRW3 =>
                nSDTACK <= '0';
                if (nAS = '1') then
                    nSWRITE <= '1';
                    nSMUXSEL <= '1';
                    nSRAS <= "1111";
                    nSCAS <= "1111";
                    nSDTACK <= '1';
                    STATE := IDLE;
               end if;

           when REFRESH1 =>
                nSCAS <= "0000";
                STATE := REFRESH2;
           when REFRESH2 =>
                nSRAS <= "0000";
                STATE := REFRESH3;
           when REFRESH3 =>
                nSRAS <= "1111";
                nSCAS <= "1111";
                STATE := IDLE;                    
        end case;
    end if;
end process;

I have been lazy and skipped the simulation step, something I intend to rectify.

The core of the controller is a FSM. There are 3 “modes” the system can be in: idle, reading or writing, and refreshing. The reading/writing and refreshing modes have multiple states. In all states, the CBR timer is counted up. When it reaches the point when a refresh should be generated, ie 15 us has passed, a flag is set. This flag is checked in the idle state and if it is set the refresh states are ran in sequence.

The refresh actions are:

  1. Lower all CAS lines
  2. Lower all RAS lines
  3. Raise both sets high again

A memory read or write looks like the following:

  1. Lower the relevant RAS lines, as determined by A1. This is how the 16 bit MPU databus is divided out onto the 32bit SIMM databus.
  2. Switch the mux from row to column mode by setting the mux’s select input low. At the same time set the SIMMs write line according to the MPU’s R/W line.
  3. Copy the processors /UDS and /LDS to the /CAS lines, per the datasheet. 0 is the same as 2 and 1 is the same as 3.
  4. Lower /DTACK. After which the MPU will end its wait-state loop and raise /AS, upon which the /RAS, /CAS, SIMMs write and local /DTACK are all raised. The mux is also reset to the row inputs by setting it’s select input high, in preparation for the next operation. If the 68K takes time to raise /AS the state machine will sit in this final state until it does.

In summary, the SIMM attached to the MINI000 via breadboard “mostly works”. It will hold data written to it for a number of reads, but eventually bit errors creep in. Its not immediately obvious what the problem is, but I have spent enough time trying to get access to be flawless by checking and double checking the wiring and VHDL, as well as looking at other SIMM interface’s schematics to 68000 systems, to conclude that the problem I’m seeing is almost certainly down to the breadboard wiring and not a flaw in my design.

Here is a screenshot from the logic analyzer showing the operation of the controller:

The left hand portion, up to 5 us into the plot, shows a CAS before RAS refresh cycle. Right of that shows a word write.

This plot shows one currently unexplained problem: /RAS0 is being lowered two machine cycles after /AS. This introduces more wait-states then there needs to be. It’s unclear why it is doing this and not lowering /RAS0 on the same cycle as I’d expect looking at the code. Hopefully simulation will show why this is happening.

On the subject of wait-states, /DTACK is being asserted for 4 cycles, which is not good at all. A big reason for this is that the SIMM memory controller logic is running only at the speed of the MPU. Until I crank up the MPU to fast (greater then 20MHz) speeds, it should be possible to clock the the SIMM controller at twice the rate of the CPU. The MAXI000 board will include this option, in the hope that wait-states for SIMM access can be minimised or perhaps even eliminated.

In the next post I’ll go over the following things, which I plan to tackle next:

  • A test of the keyboard controller board, which can be done when the IO board is plugged back into the MINI000.
  • Simulation of the SIMM controller.
  • The above mentioned scheme of clocking the SIMM controller at twice the MPU clock can be prototyped with a minor hack (trace cut, addition of a hook up wire, and swap of the (socketted) 16MHz oscillator for a 32MHz one) to the MINI000. The only downside is I must temporarily disconnect the SIMM breadboard from the MINI000 in order to make the changes.

Also, there will be a description of the MAXI000 schematic, which is perhaps 80% complete…

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.