Wednesday, July 29, 2015

Programmed MCU CPLD

So I programmed the CPLD (Lattice M4A5 63/32) to hold my MCU code. Here is a screen shot of the ispVM program from Lattice and the programmer connected to my CPLD through the JTAG connector.

Now I'm ready for a bunch of wire wrapping and test prep!





More Soldering, nRESET

I soldered in the power to the CPLD. I also added the sockets for the SRAM and the Z-80. I haven't connected them to power yet, but do have the decoupling caps soldered in.

Here are some pictures:



So, the next step is to make sure it powers up, then program another CPLD with the dedicated CPLD programmer, then start testing. To test, I've decided to use the flash programmer. I'll bring over some extra pins from the Arduino to control, temporarily, CLK, nIOREQ, nMREQ, nM1. I can use DIP switches temporarily for nHALT, nRESET, and nBUSACK. If I run out of pins. I can also use A16 and A17  if I don't have enough Arduino pins.

By the way, I just checked to see if I can use pins 0 and 1 on the Arduino UNO. There is a little text on the connector that indicates that they are RX and TX. And yes, they are the same RX and TX that go the the UART. So, if I want to use the Arduino Serial object, I have to forego pins 0 and 1. Pin 13, of course, is also the LED. So I have 2 through 12. Eleven pins. Should be enough. I'm only using 5 (Enable, Clock, Control, Din, Dout). So (clk, nIOREQ, nMREQ, nM1, nHALT). I'll use A16 and A17 for nRESET and nBUSACK.

Now that I think about it, I need to ensure that the flash chip doesn't drive the data lines. That means I need to ensure that either RD or CS never reach it. Simple enough--don't use CS. I'll need a slightly different Arduino program.

All the prep and testing will take a little while. And since I'm about to do some camping in Wisconsin and I want to travel light from Seattle, I'm at a good stage where I won't need too much of my laboratory once the CPLD is programmed. I'll bring my wirewrap tools: wire, wrapper, cutter, stripper (at 7.25" it's right at the 7" TSA limit for tools I can carry on. I hope it won't be an issue (depends on how they round and if they are ridiculous). I have neither the desire to hijack the flight nor the ability to do it with a pair of 7.25" wire strippers.)

MCU Verilog

Since I'm starting to get ready to mount the MCU CPLD, I thought I'd post the Verilog. It's pretty straightforward.

I made an error in my assumption of the flash programmer, though and I see I have to possibly change something. I was assuming that the flash device was using the rising edge of the WR line to write and starting to drive the data lines at the same as WR went low. This led to errors because it was actually using the falling edge of WR and so the data wasn't properly set up.

According to the Z-80 data sheet, the data is set up and held for ~15 ns before and after the WR line low is low. So, I should be able to use the rising edge of WR without a problem.

       
// This CPLD meant for the Lattice M4A5 64/32 is a simple bank switching
// memory control unit. It has 8 registers:
//  00 - bank 0
//  01 - bank 1
//  02 - bank 2
//  03 - bank 3
//  04 - update 0
//  05 - update 1
//  06 - update 2
//  07 - update 3
//
//  The banks are meant to substitute starting address line 14.
//  The update registers can only be written to from bank 00 i.e. the lowest
//  16kb.
//
//  The banks are updated from the update registers when HALT is called
//  from bank 0. NMI immediately follows HALT in this circumstance
//

`define BANKTOP 4
`define IO_RANGE 7:3
`define IO_VALUE 5'b00000
`define ACTIVELOW 1'b0

module mcu (
    input pin_CLK,
    input [15:0] pins_A,
    input pin_nRESET, pin_nWR, pin_nRD, pin_nMREQ, pin_nIORQ, pin_nM1, pin_nHALT,
    output reg pin_nNMI,
    output [`BANKTOP-1:0] pins_Aout,
    output pin_nCS0, pin_nCS1,
    inout [7:0] pins_D
);

    reg [`BANKTOP:0] bankReg0;
    reg [`BANKTOP:0] bankReg1;
    reg [`BANKTOP:0] bankReg2;
    reg [`BANKTOP:0] bankReg3;
    reg [`BANKTOP:0] updateReg0;
    reg [`BANKTOP:0] updateReg1;
    reg [`BANKTOP:0] updateReg2;
    reg [`BANKTOP:0] updateReg3;
    reg nKernel;
    wire nInRange;
    reg [7:0] Dint;
    wire [`BANKTOP:0] selBank;

    // Determine if the address bus is talking to us
    assign nInRange = ((pins_A[`IO_RANGE] == `IO_VALUE) ? 1'b0 : 1'b1) | pin_nIORQ;

    // Get selected output
    assign selBank = pins_A[15] ? (pins_A[14] ? bankReg3 : bankReg2) : (pins_A[14] ? bankReg1 : bankReg0);

    // assign Aout -- Hi-Z on reset
    assign pins_Aout = pin_nRESET ? selBank[`BANKTOP-1:0] : 8'bzzzzzzzz;

    // CS's for top bit of bank
    assign pin_nCS0 = pin_nRESET ? (selBank[`BANKTOP] | pin_nMREQ) : 1'bz;
    assign pin_nCS1 = pin_nRESET ? ((~selBank[`BANKTOP]) | pin_nMREQ) : 1'bz;

    assign pins_D = ((pin_nRD | nInRange) == 1'b0) ? Dint : 8'bzzzzzzzz;

    always @(*) begin
        case (pins_A[2:0])
            3'b000:
                Dint = bankReg0;
            3'b001:
                Dint = bankReg1;
            3'b010:
                Dint = bankReg2;
            3'b011:
                Dint = bankReg3;
            3'b100:
                Dint = updateReg0;
            3'b101:
                Dint = updateReg1;
            3'b110:
                Dint = updateReg2;
            3'b111:
                Dint = updateReg3;
        endcase
    end

    always @(posedge pin_CLK) begin
        if (pin_nRESET == `ACTIVELOW) begin
            bankReg0 <= 0;
            bankReg1 <= 1;
            bankReg2 <= (1 << `BANKTOP);
            bankReg3 <= (1 << `BANKTOP) | 1;
            pin_nNMI <= 1'b1;
        end
        else begin
            if ((pin_nHALT | nKernel) == `ACTIVELOW) begin
                bankReg0 <= updateReg0;
                bankReg1 <= updateReg1;
                bankReg2 <= updateReg2;
                bankReg3 <= updateReg3;
            end

            pin_nNMI <= pin_nHALT;
           
            if ((pin_nM1 | pin_nMREQ) == `ACTIVELOW) begin
                nKernel <= pins_A[15] | pins_A[14];
            end
        end
    end
   
    always @(posedge pin_nWR) begin
        if (((pin_nIORQ | nInRange) == `ACTIVELOW) && (pins_A[2] == 1'b1)) begin
            case (pins_A[1:0])
                2'b00: begin
                    updateReg0 <= pins_D;
                end
                2'b01: begin
                    updateReg1 <= pins_D;
                end
                2'b10: begin
                    updateReg2 <= pins_D;
                end
                2'b11: begin
                    updateReg3 <= pins_D;
                end
            endcase
        end
    end

endmodule

So, this is already old. I added a nBUSQACK pin for better cooperation with the in circuit flash programmer. Also added it to the testbench. It seems to work in the simulation.

Connections Soldered In. Power Wires Layed

A got the conections all soldered in. I shortened the lead length to 5 holes (0.5") and realized that I didn't have to strip both ends, just one end and slide the insulation. Still it takes about 2 hours to solder one 44 PLCC with its connections. Then another close to an hour to bend and lay in the power which I haven't soldered in yet.

I also did a continuity check between the socket interior and the pins of the header. As well as tested for shorts. Everything is OK.

Here is a bit of a blurry picture of the board (I'm at a cafe and don't have my usual lenses).


Loupe closeups on the four sides:





This will hold the memory bank switcher. Once this is in, it will be safe to put the SRAM and CPU in without fear of bus contention. I also have to make sure that the in-circuit flash programmer is properly connected so that it doesn't drive the bus while the CPU is. I'm thinking I'll run the enable line of the Arduino flash programmer to BUSREQ on the Z-80 and then BUSACK to the CPLD ENABLE line. Then the flash programmer can only ever be enabled when the CPU is not driving the bus.


Tuesday, July 28, 2015

Experiments in CPLD Connection

I'm trying to figure out a way of soldering in the CPLD socket without all those little loops. So I'm at my desk with my wire tools. Here is what I have so far.


Here is some wiring. pre solder through the jeweler's loupe over my camera lens. This wiring is flat. Each wire is cut to 6 of those clad holes. Then stripped by eye. Then a bend down at one end and a bend to the right immediately followed by a bend down at the other end. Then I slip the ends in with tweezers. I do 38 for each of the remaining CPLD, and each one takes about a minute. So it's a project for a morning. But it's working. I will experiment with soldering all at once vs one wire at a time tomorrow. Having the conical tip for the soldering iron is certainly good.


The rest of the way was spent working on the flash programmer and also starting to look at the sdcc compiler package and what I'll need to use that.

Monday, July 27, 2015

Solution: Problem Programming After Erase

I had 2 problems with the programming of the flash.
  1. The top two bits weren't getting set when they were different from the previous address bit.
  2. The first few writes after a sector erase were failing

I found the solution to each

The top two bits, i.e. bits 16 and 17 weren't getting written. It turns out it's because the Arduino Atmel part is a 16 bit address space and I had forgotten that sizeof(void*) == sizeof(int). I was using void* to show that the addresses were addresses. But, now they are proper 32 bit integers.

The other problem was thanks to my having forgotten which edge the write happens on write enable. I was bringing WR and CS down at the same time I started driving the data bus. Then I would be careful to ensure that the data was driven before and after the rising edge of WR. I actually have to ensure that the data bus is driven before the falling edge.

On top of that, I revamped the slow but simple serial protocol into something a little more complex and feature filled. The Arduino can now send back human readable progress strings while it's doing its thing, so I don't have to debug with the one LED. There is also support for sending bulk packets in 256 byte chunks for reading, writing, and verifying.

I did have a problem getting the serial port to work at 19200 baud. Set the baud rate to 19200 at both ends, and communication was not possible. So I'm back at 9600 for now. I suspect it's a limitation of the virtual COM3 port that goes through the cable to the Arduino.

Problem Programming After Erase

I can program bytes. But when I try to program bytes after a sector erase operation, the first few program attempts usually, but do not always, fail

I did find a big bug in my toggle routine--it was reading the data bus without touching the CS and RD lines, so it was just reading 0xff the whole time. Of course, there was no toggling if that happens unless there is just noise on the floating D6. But the max byte write time is 20 microseconds, the max sector erase time is 25 ms, and the max chip erase is 100ms. So it shouldn't be in those modes.

I hypothesized that may there could be an address issue and that the bytes were written but to the wrong location. So I did a full sweep of the flash (which took a long time) and discovered that it was not the case, but also that A17 and A16 weren't changing. It's related to the comparison to the previous address.

So, I have 2 problems to work on. A17 and A16 not changing because ((addr^(lastAddr) & 0xfff00L) doesn't seem to be working.

I also want to speed things up and make block transfers easier. So that's my agenda for today.

Saturday, July 25, 2015

A Byte Is Programmed!!!

I had to do a little debugging to get the read just right. I was reading Dout on the wrong side of the clock pulse. Once I got that fixed, it was time to try to program a byte. So I wrote 0x3c to 0x0000. The Arduino and CPLD did their thing and returned.

I read the location 0x0000. It was 0x3c. I read 0x0001. It was 0xff. Then I read 0x0000 again, and again it was 0x3c.

So, I can program the flash. The flash is working. Another success. Now I'll hunker down and get the C# code tip top. I need to be able to upload and verify files.

Which reminds me, I need to start coming up with some code to write to the flash. This afternoon, I downloaded so stuff from http://sdcc.sourceforge.net/. I'll try this to write code for the Z-80 processor.

But, that's in a little bit. For now, getting this whole flash thing rock solid is my goal.

Success is good!

All Connections OK--Time to Plug In the Flash Part

I tested all the connection on the socket side of the the flash socket. Everything is connected and the levels change as they should under the control of the Arduino.

I forgot to solder the flash power connection to by little power distribution strip, so I have to do that. I also forgot to pull up the ENABLE pin on the CPLD. 

Next step is to start programming the flash.

For those who don't know how to write and erase flash, here is a quick rundown. Reading from flash is easy. You just put the address on the address bus, pull CE (chip enable) and OE (output enable aka RD for read) down, and read the data on the databus.

Writing is not so simple. With RAM, you can just set the address and data, and while CE is pulled low, pull WE (aka WR) low and it will write on the rising edge. But flash doesn't work that way. Instead, you enter a mode and while in the mode, tell the flash which byte to write to which address. Then you keep reading until databit 6 stops toggling.

The way you enter the mode is to write a sequence of bytes that would be unlikely to just be written at random.

To just write a byte, write 0xaa to 0x5555. Then write 0x55 to 0x2aaaa. Then write 0xa0 to 0x5555. Finally, write the byte to the address you want to program. Now read and when two consecutive reads have the same bit #6, it is over.

The thing about writing to flash is that you can write a 0 bit to a location where there is a 1 bit, but you cannot turn a 0 back into a 1 by writing a new byte. It will always be an &= operation, not an = operation.

But that doesn't mean you can never turn a 0 back into a 1. You have to do it a sector at a time. The 39SF020A I chose has 4kB pages that can be erased. The entire chip can also be erased.

To erase a sector, write 0xaa to 0x5555. Then write 0x55 to 0x2aaaa. Next write 0x80 to 0x5555, then 0xaa to 0x5555. Next, write 0x55 to 0x2aaaa. Finally, write 0x30 to any byte in the sector to erase.

The chip erase is the same except if the last byte is 0x10 instead of 0x30, the whole chip is erased.

Again, as with a write, read the chip and watch the toggle bit until it stops toggling.



Wire Wrapping CPLD to Flash Socket Done

It took a little time, but I finally got the CPLD wire wrapped to the socket that will hold the 39SF020A flash part.



I'll do some testing to make sure that each pin of the flash does what it's supposed to do before plugging in the part.

Friday, July 24, 2015

Flash Socket and 10K Resistor Net Soldered In

I got the socket that will hold the 39SF020A in with power and a ceramic decoupling cap:



And here is a set of 8 10K resistors for pullups/pulldowns:


There is no power for these. It will all be wire-wrapped.

Finally, it is worth pointing out that it is 8:30 on a Friday night. All work and play makes Jack a dull boy. So a little Alaskan Amber is always a way to make wire-wrapping more fun:


Now to actually start wire-wrapping. 18 address lines, 8 data lines, and 3 bus control lines. As well as the pull-up on the enable going into the CPLD. That's 31 tiny wires and 62 stripped ends!

Next post will have pictures....

flashLow.cpp/h

This is the low level flash driver. It has some stuff at the top of the cpp file that lets me see the pins states. I originally planned to use the Arduino LED blinking to know what was going on. The use of the serial port is much better.

Right now the diagnostic code is made of functions living in the file aren't part of the class. I'll change that going forward, or I'll remove it.

But, for now, the code seems to work.

       
#ifndef INCLUDE_FLASHLOW
#define INCLUDE_FLASHLOW
#include <Arduino.h>

class FlashLow
{
public:
    FlashLow();

    // Setsw the input/output pin mode
    void EnablePins(bool enable);

    // Resets the CPLD
    bool Reset();

    // Write data to *ptr.
    // NOTE: This is a NOT toggle bit operation, just sends data to *ptr
    // returns true if successful
    void Write(long ptr, byte data);

    // Read data at *ptr into *pData
    // returns true if successful
    void Read(long, byte* pData);

    // Force update of all address bits
    void UpdateAddress(long addr);

    // Update address bits only on parts that are different
    void UpdateAddressDiff(long addr);

    // Update address bits 3:0
    void UpdateAddress_3_0(long addr);

    // Update address bits 7:4
    void UpdateAddress_7_4(long addr);

    // Update address bits 17:8
    void UpdateAddress_17_8(long addr);

    // Update chip control where bits 3-0 are drive, CS*, WR*, and RD*
    void UpdateChip(int chip);

    // Update the CTRL register that tells which register to pulse data in and out
    void UpdateCtrl(int ctrl);

    // Swaps a data byte on Din/Dout
    byte ReadWrite(byte data);

    // Pulse CLK high and low
    void PulseClk();

    // Pulse nCTRL low and high
    void PulseCtrl();

    // Control pins directly
    void DirectWrite(bool enable, bool clk, bool din, bool ctrl);
    void DirectRead(bool* enable, bool* clk, bool* din, bool* dout, bool* ctrl);

    // Reads until the toggle bits stop toggling
    // Returns true if the read data matches expected
    // Use expected of 0xff for erase operations
    bool Toggle(byte expected);

    // Send CPLD reset sequence
    void ResetSequence();

private:
    // Update an arbitrary part of the address bits
    void UpdateAddress_start_end(long addr, int startBit, int endBit);

    int m_lastAddr;

public:
    static const int PIN_nCTRL = 
    static const int PIN_Dout = 3;
    static const int PIN_Din = 4;
    static const int PIN_CLK = 5;
    static const int PIN_ENABLE = 6;
};

#endif

Here is the cpp part:
       
#include "flashLow.h"

//#define SUPER_SLO_MO() delay(1000)
#define SUPER_SLO_MO() SendOutPins()

void SendOutPins()
{
    byte progress[10] = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    progress[1] =
        (digitalRead(FlashLow::PIN_ENABLE) ? 0x01 : 0) |
        (digitalRead(FlashLow::PIN_nCTRL) ? 0x02 : 0) |
        (digitalRead(FlashLow::PIN_CLK) ? 0x04 : 0) |
        (digitalRead(FlashLow::PIN_Din) ? 0x08 : 0) |
        (digitalRead(FlashLow::PIN_Dout) ? 0x10 : 0);
    Serial.write(progress, 10);

    delay(250);
}

void BlinkOutPins()
{
    digitalWrite(13, HIGH);
    delay(1000);
    digitalWrite(13, LOW);
    delay(1000);

    if (digitalRead(FlashLow::PIN_ENABLE))
    {
        digitalWrite(13, HIGH);
        delay(500);
        digitalWrite(13, LOW);
        delay(500);
    }
    else
    {
        digitalWrite(13, HIGH);
        delay(250);
        digitalWrite(13, LOW);
        delay(250);
        digitalWrite(13, HIGH);
        delay(250);
        digitalWrite(13, LOW);
        delay(250);
    }

    delay(1000);
    if (digitalRead(FlashLow::PIN_nCTRL))
    {
        digitalWrite(13, HIGH);
        delay(500);
        digitalWrite(13, LOW);
        delay(500);
    }
    else
    {
        digitalWrite(13, HIGH);
        delay(250);
        digitalWrite(13, LOW);
        delay(250);
        digitalWrite(13, HIGH);
        delay(250);
        digitalWrite(13, LOW);
        delay(250);
    }

    delay(1000);
    if (digitalRead(FlashLow::PIN_CLK))
    {
        digitalWrite(13, HIGH);
        delay(500);
        digitalWrite(13, LOW);
        delay(500);
    }
    else
    {
        digitalWrite(13, HIGH);
        delay(250);
        digitalWrite(13, LOW);
        delay(250);
        digitalWrite(13, HIGH);
        delay(250);
        digitalWrite(13, LOW);
        delay(250);
    }

    delay(1000);
    if (digitalRead(FlashLow::PIN_Din))
    {
        digitalWrite(13, HIGH);
        delay(500);
        digitalWrite(13, LOW);
        delay(500);
    }
    else
    {
        digitalWrite(13, HIGH);
        delay(250);
        digitalWrite(13, LOW);
        delay(250);
        digitalWrite(13, HIGH);
        delay(250);
        digitalWrite(13, LOW);
        delay(250);
    }

    delay(1000);
    if (digitalRead(FlashLow::PIN_Dout))
    {
        digitalWrite(13, HIGH);
        delay(500);
        digitalWrite(13, LOW);
        delay(500);
    }
    else
    {
        digitalWrite(13, HIGH);
        delay(250);
        digitalWrite(13, LOW);
        delay(250);
        digitalWrite(13, HIGH);
        delay(250);
        digitalWrite(13, LOW);
        delay(250);
    }

    delay(1000);
}

FlashLow::FlashLow()
{
    m_lastAddr = -1;
}

void FlashLow::EnablePins(bool enable)
{
    if (enable)
    {
        pinMode(PIN_ENABLE, OUTPUT);
        pinMode(PIN_nCTRL, OUTPUT);
        pinMode(PIN_Dout, INPUT);
        pinMode(PIN_Din, OUTPUT);
        pinMode(PIN_CLK, OUTPUT);
    }
    else
    {
        pinMode(PIN_ENABLE, INPUT_PULLUP);
        pinMode(PIN_nCTRL, INPUT_PULLUP);
        pinMode(PIN_Dout, INPUT);
        pinMode(PIN_Din, INPUT_PULLUP);
        pinMode(PIN_CLK, INPUT_PULLUP);
    }

}

void FlashLow::Write(long addr, byte data)
{
    UpdateAddressDiff(addr);
    ReadWrite(data);

    UpdateChip(0x1); // drive, CS, WR
    UpdateChip(0x3); // drive, CS
    UpdateChip(0xf); // done
}

void FlashLow::Read(long addr, byte* pData)
{
    UpdateAddressDiff(addr);
    *pData = ReadWrite(0);
}

void FlashLow::UpdateAddressDiff(long addr)
{
    if (m_lastAddr == -1)
    {
        UpdateAddress(addr);
    }
    else
    {
        long diffs = (addr ^ m_lastAddr);
        if ((diffs & 0x0000f) != 0)
        {
            UpdateAddress_3_0(addr);
        }
        if ((diffs & 0x000f0) != 0)
        {
            UpdateAddress_7_4(addr);
        }
        if ((diffs & 0xfff00) != 0)
        {
            UpdateAddress_17_8(addr);
        }

        m_lastAddr = addr;
    }
}

void FlashLow::UpdateAddress(long addr)
{
    UpdateAddress_3_0(addr);
    UpdateAddress_7_4(addr);
    UpdateAddress_17_8(addr);
    m_lastAddr = addr;
}

void FlashLow::UpdateAddress_start_end(long addr, int endBit, int startBit)
{
    int i;
    for (i = startBit; i <= endBit; ++i)
    {
        long mask = 1L << i;
        m_lastAddr &= ~mask;
        m_lastAddr |= (mask & addr);
        digitalWrite(PIN_Din, (addr & (1L << i)) ? HIGH : LOW);
        SUPER_SLO_MO();
        PulseClk();
    }
}

void FlashLow::UpdateAddress_3_0(long addr)
{
    UpdateCtrl(0x01);
    UpdateAddress_start_end(addr, 3, 0);
}

void FlashLow::UpdateAddress_7_4(long addr)
{
    UpdateCtrl(0x02);
    UpdateAddress_start_end(addr, 7, 4);
}

void FlashLow::UpdateAddress_17_8(long addr)
{
    UpdateCtrl(0x03);
    UpdateAddress_start_end(addr, 17, 8);
}

void FlashLow::UpdateChip(int chip)
{
    int i;
    UpdateCtrl(0x07);
    for (i = 0; i < 4; ++i)
    {
        digitalWrite(PIN_Din, (chip & 1) ? HIGH : LOW);
        SUPER_SLO_MO();
        PulseClk();
        chip >>= 1;
    }
    PulseCtrl();
}

void FlashLow::UpdateCtrl(int ctrl)
{
    int i;
    digitalWrite(PIN_nCTRL, LOW);
    SUPER_SLO_MO();
    for (i = 0; i < 3; ++i)
    {
        int bit = ctrl & 1;
        ctrl = ctrl >> 1;
        digitalWrite(PIN_Din, bit ? HIGH : LOW);
        SUPER_SLO_MO();
        PulseClk();
    }
    digitalWrite(PIN_nCTRL, HIGH);
    SUPER_SLO_MO();
}

byte FlashLow::ReadWrite(byte data)
{
    int i;
    byte outData = 0;
    UpdateCtrl(0x00);
    for (i = 0; i < 8; ++i)
    {
        if (digitalRead(PIN_Dout) == HIGH)
        {
            outData |= 0x80;
        }
        digitalWrite(PIN_Din, (data & 1) ? HIGH : LOW);
        SUPER_SLO_MO();
        PulseClk();
        outData >>= 1;
        data >>= 1;
    }
    return outData;
}

bool FlashLow::Toggle(byte expected)
{
    bool toggling = true;
    byte data0;
    byte data1;
    Read(m_lastAddr, &data0);
    while (toggling)
    {
        Read(m_lastAddr, &data1);
        if ((data1 ^ data0) & 0x40)
        {
            // toggle bit toggling
            data0 = data1; // set up for next toggle check
        }
        else
        {
            toggling = false;
        }
    }
    return data1 == expected;
}

void FlashLow::PulseClk()
{
    digitalWrite(PIN_CLK, HIGH);
    SUPER_SLO_MO();
    digitalWrite(PIN_CLK, LOW);
    SUPER_SLO_MO();
}

void FlashLow::PulseCtrl()
{
    digitalWrite(PIN_nCTRL, LOW);
    SUPER_SLO_MO();
    digitalWrite(PIN_nCTRL, HIGH);
    SUPER_SLO_MO();
}

void FlashLow::ResetSequence()
{
    m_lastAddr = -1;

    // initialize pins before setting output mode
    digitalWrite(PIN_ENABLE, HIGH);
    digitalWrite(PIN_CLK, HIGH);
    digitalWrite(PIN_nCTRL, HIGH);
    digitalWrite(PIN_Din, HIGH);

    // set pin modes
    pinMode(PIN_ENABLE, OUTPUT);
    pinMode(PIN_nCTRL, OUTPUT);
    pinMode(PIN_Dout, INPUT);
    pinMode(PIN_Din, OUTPUT);
    pinMode(PIN_CLK, OUTPUT);

    // Execute reset sequence
    PulseClk();
    PulseClk();
    digitalWrite(PIN_nCTRL, LOW);
    PulseClk();
    PulseClk();
    PulseClk();
    digitalWrite(PIN_nCTRL, HIGH);
    SUPER_SLO_MO();
    digitalWrite(PIN_ENABLE, LOW);
    SUPER_SLO_MO();
}

void FlashLow::DirectWrite(bool enable, bool clk, bool din, bool ctrl)
{
    digitalWrite(PIN_ENABLE, enable ? (HIGH) : (LOW));
    digitalWrite(PIN_CLK, clk ? (HIGH) : (LOW));
    digitalWrite(PIN_Din, din ? (HIGH) : (LOW));
    digitalWrite(PIN_nCTRL, ctrl ? (HIGH) : (LOW));
}

void FlashLow::DirectRead(bool* enable, bool* clk, bool* din, bool* dout, bool* ctrl)
{
    *enable = digitalRead(PIN_ENABLE) == HIGH;
    *clk = digitalRead(PIN_CLK) == HIGH;
    *din = digitalRead(PIN_Din) == HIGH;
    *dout = digitalRead(PIN_Dout) == HIGH;
    *ctrl = digitalRead(PIN_nCTRL) == HIGH;
}


And just to refresh, here is the code part of the Verilog for the CPLD:

       

`define ACTIVELOW 1'b0
`define ACTIVEHIGH 1'b1

module flashProgrammer(
    input pin_ENABLE,
    input pin_CLK,
    input pin_Din,
    input pin_nCTRL,
    output reg pin_Dout,

    output [17:0] pins_A,
    output pin_nWR, 
    output pin_nRD,
    output pin_nCS,
    inout [7:0] pins_D
    );

    reg [2:0] ctrlReg;
    reg [7:0] dataReg;
    reg [17:0] addrReg;
    reg [3:0] chipReg;
    wire nOE;

    reg nRDint;
    reg nWRint;
    reg nCSint;
    reg nOEint;

    assign pin_nRD = !pin_ENABLE ? nRDint : 1'bz;
    assign pin_nWR = !pin_ENABLE ? nWRint : 1'bz;
    assign pin_nCS = !pin_ENABLE ? nCSint : 1'bz;
    assign nOE = nOEint | pin_ENABLE;

    assign pins_D = (nOE==`ACTIVELOW && pin_ENABLE==`ACTIVELOW && pin_nRD==1'b1) ? dataReg : 8'bzzzzzzzz;
    assign pins_A = (pin_ENABLE == `ACTIVELOW) ? addrReg : 18'bzzzzzzzzzzzzzzzzzz;

    always @(posedge pin_nCTRL) begin
        // Strobe out of the chip
        nRDint <= chipReg[0];
        nWRint <= chipReg[1];
        nCSint <= chipReg[2];
        nOEint <= chipReg[3];
    end

    always @(posedge pin_CLK) begin

        if (pin_nCTRL == `ACTIVELOW) begin

            // clocking in control
            pin_Dout <= ctrlReg[0];
            ctrlReg <= {pin_Din, ctrlReg[2:1]}; 

            if (pin_ENABLE == 1'b1) begin
                chipReg <= 4'b1111;
            end
        end
        else begin
            case (ctrlReg)
                3'b000 : begin
                    pin_Dout <= dataReg[0];
                    dataReg <= { pin_Din, dataReg[7:1]};
                end
                3'b001 : begin
                    pin_Dout <= addrReg[0];
                    addrReg[3:0] <= { pin_Din, addrReg[3:1]};
                end
                3'b010 : begin
                    pin_Dout <= addrReg[4];
                    addrReg[7:4] <= { pin_Din, addrReg[7:5]};
                end
                3'b011 : begin
                    pin_Dout <= addrReg[8];
                    addrReg[17:8] <= { pin_Din, addrReg[17:9]};
                end
                3'b100 : begin
                end
                3'b101 : begin
                end
                3'b110 : begin
                end
                3'b111 : begin
                    pin_Dout <= chipReg[0];
                    chipReg <= { pin_Din, chipReg[3:1]};
                    // If reading, read now
                    if ((pin_nRD | nOE) == `ACTIVELOW) begin
                        dataReg <= pins_D;
                    end
                end
            endcase
        end
    end

endmodule

BTW, this has the bug fix described in the previous post. The actual CPLD I have doesn't update Dout properly while shifting ctrlReg.

Testing and Troubleshooting Arduino Connection to CPLD

I spent much of last night and this morning getting the Arduino communicating properly with the CPLD. I was able to see the bus control in action, but I wasn't able to get the address lines working last night. All the CPLD really is is a set of shift registers. How hard can it be?

Finally, I wrote some code that sends back the 5 pin statuses to the C# program in progress responses which can are display while it's waiting for the final response. Thus I was able to see what the pins are actually doing.

The first thing I did was look at Dout to see if I was shifting out what I was shifting in.

I found a bug in the Verilog with the control register and realized I can't use it on that particular register:
       

    always @(posedge pin_CLK) begin

        if (pin_nCTRL == `ACTIVELOW) begin

            // clocking in control
//            pin_Dout <= ctrlReg[2]; <-----------------BUG
            pin_Dout <= ctrlReg[0];
            ctrlReg <= {pin_Din, ctrlReg[2:1]}; 

            if (pin_ENABLE == 1'b1) begin
                chipReg <= 4'b1111;
            end
        end


I'll keep the bugged CPLD for now since I don't use the Dout from the control register. I don't have the same bug in the other shift sections.

Then, I noticed that my address bits weren't even getting shifted out. Turns out I had my for loop start and end variables reversed. Once that was solved, I was able to see the bits shifting out. And then I saw them on the address lines! Yay!

Next, I looked at the data lines. The first and last seemed to work. I wasn't sure that the driving was ending properly. But wirewrapping makes it easy to pop a 10K resistor on the board and connect to ground and a data line and Vcc and a data line and write 1 and 0 and make sure that that pulldown/pullup works as expected. And it did!

Here is an example of the flashProg.exe output (address was already programmed):

       
C:\Users\Mark\Documents\EEProjects\Z80Computer\Tools\Arduino\flashProg\flashProg
\bin\Debug>flashProg.exe wb 5555 80
0: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=0
1: nENABLE=0 nCTRL=0 CLK=0 Din=0 Dout=0
2: nENABLE=0 nCTRL=0 CLK=1 Din=0 Dout=1
3: nENABLE=0 nCTRL=0 CLK=0 Din=0 Dout=1
4: nENABLE=0 nCTRL=0 CLK=0 Din=0 Dout=1
5: nENABLE=0 nCTRL=0 CLK=1 Din=0 Dout=0
6: nENABLE=0 nCTRL=0 CLK=0 Din=0 Dout=0
7: nENABLE=0 nCTRL=0 CLK=0 Din=0 Dout=0
8: nENABLE=0 nCTRL=0 CLK=1 Din=0 Dout=0
9: nENABLE=0 nCTRL=0 CLK=0 Din=0 Dout=0
10: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
11: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
12: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=0
13: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
14: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
15: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=0
16: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
17: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
18: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=0
19: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
20: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
21: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=0
22: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
23: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
24: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=0
25: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
26: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
27: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=0
28: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
29: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
30: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=0
31: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
32: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=0
33: nENABLE=0 nCTRL=1 CLK=1 Din=1 Dout=1
34: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
35: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
36: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
37: nENABLE=0 nCTRL=0 CLK=1 Din=1 Dout=0
38: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=0
39: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=0
40: nENABLE=0 nCTRL=0 CLK=1 Din=1 Dout=1
41: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
42: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
43: nENABLE=0 nCTRL=0 CLK=1 Din=1 Dout=1
44: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
45: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
46: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
47: nENABLE=0 nCTRL=1 CLK=1 Din=1 Dout=1
48: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
49: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=1
50: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=1
51: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=1
52: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=1
53: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=1
54: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=1
55: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=1
56: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=1
57: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=1
58: nENABLE=0 nCTRL=0 CLK=0 Din=0 Dout=1
59: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=1
60: nENABLE=0 nCTRL=0 CLK=0 Din=0 Dout=1
61: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
62: nENABLE=0 nCTRL=0 CLK=1 Din=1 Dout=1
63: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
64: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
65: nENABLE=0 nCTRL=0 CLK=1 Din=1 Dout=1
66: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
67: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
68: nENABLE=0 nCTRL=0 CLK=1 Din=1 Dout=1
69: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
70: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
71: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
72: nENABLE=0 nCTRL=1 CLK=1 Din=1 Dout=1
73: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
74: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
75: nENABLE=0 nCTRL=1 CLK=1 Din=1 Dout=0
76: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=0
77: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
78: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=0
79: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
80: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
81: nENABLE=0 nCTRL=1 CLK=1 Din=0 Dout=0
82: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
83: nENABLE=0 nCTRL=0 CLK=0 Din=0 Dout=0
84: nENABLE=0 nCTRL=1 CLK=0 Din=0 Dout=0
85: nENABLE=0 nCTRL=0 CLK=0 Din=0 Dout=0
86: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=0
87: nENABLE=0 nCTRL=0 CLK=1 Din=1 Dout=1
88: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
89: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
90: nENABLE=0 nCTRL=0 CLK=1 Din=1 Dout=1
91: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
92: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
93: nENABLE=0 nCTRL=0 CLK=1 Din=1 Dout=1
94: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=1
95: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
96: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
97: nENABLE=0 nCTRL=1 CLK=1 Din=1 Dout=1
98: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
99: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
100: nENABLE=0 nCTRL=1 CLK=1 Din=1 Dout=1
101: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
102: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=1
103: nENABLE=0 nCTRL=1 CLK=1 Din=1 Dout=0
104: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=0
105: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=0
106: nENABLE=0 nCTRL=1 CLK=1 Din=1 Dout=0
107: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=0
108: nENABLE=0 nCTRL=0 CLK=0 Din=1 Dout=0
109: nENABLE=0 nCTRL=1 CLK=0 Din=1 Dout=0
Wrote 80 to 5555
>

So, at this point, it looks like the CPLD is working as expected, except for the minor bug.

I'm ready to start wiring up the 39SF020A. I need to write the bulk reading and writing in flashProg.exe.

I should also add a pullup for the ENABLE signal. It has to be high when the Arduino is not connected. That means I need to mount one of the 10K resistor nets with the 8 10K resistors.

Thursday, July 23, 2015

Wired All Arduino Signals To CPLD

And here is the part of the CPLD interface that talks to the Arduino:


The next step is to see if I can get the address, and bus control working. Then try to see if I can read in some random data bits by holding maybe D0 and D7 on the bus high and low.

Initial Power With Flash Programmer CPLD

I plugged inn the CPLD and powered up.

I ensured that I had 0V on the ground lines and 5V on the VCC lines.

Then I attached the Arduino ENABLE pin to pin 38 of the CPLD. And aimed a command line terminal to my directory with the C# flashProg.exe program.

I enabled the pins with:

flashProg.exe pins 1

and then set them all low with:

flashProg.exe direct 0

This makes 2 of the address lines I tested solidly 0. Meaning, I hooked up a multimeter and saw the voltage go to 0. If I tap the lead, it stays at 0. Then I entered:

flashProg.exe direct f

Now these lines float the address lines I tested. That is, if I tap the lead, the voltage on the multimeter bounces around. A characteristic of floating lines.

So, ENABLE does drive and float the address lines as it should. At least the 2 I tested. I now have confidence that I'm on the right track. I'll do a bunch of testing this afternoon and hopefully, start to use some of the more complex features of flashProg.exe.

BTW, the help (so far) for the flashProg.exe is this:

       
    flashProg: a PC interface to an Arduino that talks to a CPLD that talks to flash

    Usage:
        Note that in the following, [addr] and [data] must be hex
        flashProg nop
            Just checks the connection
        flashProg reset
            Resets the CPLD
        flashProg pins [0|1]
            Enables pins for output (1) or makes them all inputs
        flashProg wb [addr] [data]
            Writes a byte data to addr in flash
        flashProg rb [addr]
            Reads a byte data from addr in flash
        flashProg direct [0-f]
            directly write pins. msb to lsb: ctrl, din, clk, enable
        flashProg write [addr] [len] [binaryFileName]
            Writes a binary file addr in flash up to len bytes
        flashProg read [addr] [len] [binaryFileName]
            Reads len bytes from addr in flash to a binary file
        flashProg erase [addr]
            Erases the sector in flash containing addr
        flashProg eraseall
            Erases the entire flash

So you can see some of the features I plan for this.

My plans now are to ensure that the address and data lines work properly by sending them different values. Then that the CS, RD, and WR work. Once they all seem to work as expected, I'll out the flash part in and wire-wrap. Then, try to read and hopefully get 0xff. Then try to program and hopefully get a toggling DQ6 or at least a written byte if it's too slow.

I'll post the Arduino and flashProg code later today.

Wednesday, July 22, 2015

Getting Ready To Interface Arduino With CPLD

So this afternoon, I soldered in the connector wires to the Arduino.




The ground connection to Arduino goes through my first wire wrapped connection:



I also have to think about power delivery. So I soldered in two pieces of wire  Here are the front and back. The two rails of copper wire in the upper picture run along 5 holes which will take other wires so they can be connected. The lower picture shows the other side with a tantalum capacitor that will keep the 5V close to 5V under sudden loads.



The IC sockets are not soldered in. They are just there for visualization. The one closest to the CPLD socket that's soldered in will be where the 39SF020A flash part will be mounted once I've established the CPLD is behaving the way I want it to.

Tomorrow, I'll make sure that the CPLD powers up and that the inputs look like they're floating. Then I'll wire wrap the Arduino wires to the CPLD and see if I can control the CPLD outputs as I expect. A scope or logic analyzer would help, but I'll just get creative since I have neither. Probably, I'll add a mode to my Arduino code that will let me single step through the operations. Or maybe just slow them down so my multimeter can see signals changing.

Controlling Pins On Arduino

I had some Arduino code, but all I did was check to see if it executes and sends a response on the serial port. Today, I ensured that pins at least get controlled in the sense of can I turn them on and off and measure the voltage on the pins. I added some code directly to set the pin outputs. After a while of fighting against a missing 'break' in my switch statement, I got it to work. Now to heighten my warnings....

Tuesday, July 21, 2015

Open and Short Testing

I checked the headers to the PLCC-44 CPLD socket for opens and shorts. None. Everything that should be connected was and everything that shouldn't be connected wasn't.

I guess my soldering skills are picking up.

Visual Micro (for Arduino) Plugin for Visual Studio

As I mentioned in a previous blog post, the Arduino IDE drives me batty. So tonight I decided to do something about it. I considered makefiles, but every time I thought I was onto something, it just didn't look it was going to be a good investment in times. Especially since I really dislike working with makefiles.

Then I decided to see what's out there for IDEs. Some stuff, but not much. Fortunately, one of the things out there was something that had promise. Visual Micro.

But not yet. I had been using Visual Studio Express 2010 C# for a long time now and never had the need to upgrade. The Express versions of Visual Studio don't allow plugins and this is a plugin. So I decided to move from 2010 to Visual Studio Community 2013. Visual Micro said it doesn't work with 2015 yet, otherwise I would have gone with that.

I had to add a directory to my Tools>Options>Environment>Security Add-Ins as described in the FAQ and figure out where to aim the directory for the IDE. C:\Program Files (x86)\Arduino\ as I recall. Or something like that. Then, once I told it which board I'm using, Intellisense went through and indexed all the symbols.

Sooooo much nicer than the Arduino IDE. Once again, I failed to upload to my UNO R3 the first time and discovered it was I didn't tell Visual Micro to use COM3. There was a blank dropdown box that had the com port. Apparently, it doesn't default to the only available port when there is only one. But that was easy enough to solve.

Debugging will be a little bit more difficult that a target over which you have total control. And I think there is a Pro version of Visual Micro that is reasonably priced and has stronger debugging capability.

But, I'm happy with Visual Micro. A free plugin on top of a great free IDE...can't beat that. I highly recommend anyone to quickly ditch the Arduino IDE and install this.

Since I'm getting close to having my flash programmer ready to go, I'll be needing to make sure the Arduino outputs the right signals before I connect it the flash programmer CPLD. So I got this installed just in time.

CPLD Socket #2 Soldered In

Here is the solder side of the socket and the 4 headers for wire-wrap.



And here is the wire-wrap side.


Now to check continuity and for adjacent shorts....

Soldering CPLD Socket #2 Half Done

The CPLD socket that I soldered on was specifically for CPLD programming. I will have 3 different sockets for the CPLDs in the circuit. I have started the first of those sockets. It will be a socket and 4 12 pin headers on the wire-wrap side of the board.

Here is what the socket and 2 of the headers look like so far.


Here is the back side.


And some closer ups.



I'll get the other half done next and them ensure that the contuity checks are all good.

Also, this a whoooooole lot easier with my new conical soldering iron tip. It would basically have been impossible with the tip the iron shipped with.


Monday, July 20, 2015

Operation Program CPLD

I finally got enough of my stuff from Amazon late last night that I was able to do some soldering this morning. I am getting better at soldering. I am learning tricks.

First, I redid the whole 5V supply a second time and checked for LED and 5v.

I got some pictures by hold lenses in front of Surface Pro 3 camera which otherwise sucks for close-ups.

As you can see, this is a mess--but it works:


Here is the front:


Next, I went to solder in the JTAG connector and a PLCC-44 socket.

First I did some small tacking solders to keep them in place. Then I soldered a bunch of wires to the JTAG connector. In retrospect, it's better to do it wire by wire rather than connect a bunch at one end and then connect them at the at other end.

Once I was all done, I took the 10 connector ribbon cable from the Lattice ispDownloadCable, my DMM, my loop and glasses, and my design paper and headed over to Liberty for a schooner of beer and to check the continuity of the power, ground, and JTAG connections. I had misplaced the TMI in pin 21 instead of pin 32. So I fixed that when I got home.



Once it was ready, I powered up. LED came on and no smoke. 5V everywhere.

Next I plugged in my ispDownload cable and fired up ispVM. My chain was already there with the part number and the .jed file to download. ispVM has a some issues doing more than one thing. I can do one thing then I have to unplug and replug otherwise I get an error telling me the driver isn't loaded. I can deal with that.

Finally, the moment of truth... I popped an M4A5-64/32 into the socket. Then I clicked Download which does erase, program, verify. ispVM showed a little progress dialog that counted to about 5 seconds, and then indicated that the process passed.

Wow! It passed. I just programmed a CPLD with verilog I wrote. This is a big early milestone.



BTW, there are 3 pins on the JTAG connector that don't need to be connected. Two are outputs. One is an input that is ignored be default, at least with the part I'm using. I have 3 wires soldered at one end, but I'll pop those off.

Next? Get ready to program the flash. That will require a second PLCC-44 socket and the 32 pin flash socket. It will be wire wrapped, but I need to solder the pins. I'm also thinking about better ways to get power to it. I'll update progress as I make it. But this might take a little time. I also have a conical tip for the soldering iron coming from Amazon, so that should help.

Saturday, July 18, 2015

Power Redo and Amazon

I built the power part of the circuit without really thinking about how to route the power to the rest of my board. Since the power part is built, I was faced with the issue of getting the power elsewhere.

I realized that I should have planned this out before I started soldering. So, tomorrow, I'll disassemble the circuit and start over. This time, I'll use two long wires--1 red and one black. Red will be Vcc and black will be ground. I'll strip off insulation in the middle and make little loops. I'll bring the resistor and LED into the board. I'll set up the entire wiring scheme before I solder and leave long wires that can connect to other posts that I need to power. Once all that bending is done, then I'll resolder.

I ordered a few tools I need to so all this from Amazon and it was supposed to arrive by 8pm today. It didn't so I contacted them via chat and got a $10 credit to use at Amazon. I told them I need the shipment in the AM. If it doesn't arrive in the AM, I'll contact them again and try to pry another $10 from them. If it does arrive, I'll just be happy.

If you're wondering how bad I suck at solder, here is the solder side of the board taken through a 4x lens held up to my tablet camera:


And here is the component side:


Here are the pins of the regulator with the jeweler's loop held up to the camera:

Thursday, July 16, 2015

Reading Glasses

So I suck at soldering. But part of that is that I can't see what I'm soldering. So I went to Walgreens and got some reading glasses. Now I see the PCB board and the parts really well. Even without the jeweler's loop while I'll still use for bending of wire in tight places.

So reading glasses...who'd have guessed? I can't see anything else while they're on, though. Just those things at soldering distance from my eyes.

Arduino Development Environment Annoyances

So, I've used the Arduino development environment a little, and it drives me nuts. It keeps adding automatic closing brackets and I don't see a setting anywhere to turn that off. It doesn't auto-indent the way I want it to.

The "tabs" feature is highly unintuitive. Or rather, highly unlike any other IDE I've used. You can't access the tabs menu from menu bar. Instead there is a tiny little unlabeled dropdown button with the tab commands. And on my Surface Pro3, it's a tiny box. When you add a tab, a bar under the code area turns into the place where the name of the tab is entered. Also, the color contrast in the tabs is horrible. My eyes hurt. Apparently, if you don't give the tab an extension, it just concatenates it to the main file.

The error pane also drives me nuts. The top is filled with blank lines. The pane is too small. The slider to change the size is way too narrow. And you can't double click a line to take you to the error.

Also the title bar stays on top even when in other application. Ugh!

I have to figure out something else. I can't use this.

Wednesday, July 15, 2015

Pullups in Verilog

So I have written in VHDL a long time ago and read verilog a few years ago. But I don't know the ins and outs of verilog programming. Today, I went to check my waveforms on the flash programmer part to ensure that the whole bus and buscontrol goes Hi-Z when RESET is asserted. And I saw that the data bus was actually going from Hi-Z to unknown while RESET was asserted. Odd, I thought.

I quickly decided that the most likely scenario was that my memory simulator was mishandling the Hi-Z RD signal. And indeed. I routed the RD signal straight from the CPLD simulation to the memory simulator. So the memory simulator was getting Z as an input. Well, in verilog, if you have OR/NOR gates and everything that's not L is either Z or X, the output is X. Similarly, with AND/NAND gates, if everything that's not H is Z or X, the output is X. So, my bus enabler was getting confused.

The hardware solution is pullup resistors between the output and Vcc (or pull downs to ground as the case may be). But verilog has no resistors that I know of. What to do?

I added three new 'wire' declarations.

wire nCS_pu;
wire nRD_pu;
wire nWR_pu;

Then took the Hi-Z-able pins pin_nCS, pin_nRD, and pin_nWR and ran them through these:

assign nCS_pu = (pin_nCS === 1'bz) ? 1'b1 : pin_nCS;
assign nRD_pu = (pin_nRD === 1'bz) ? 1'b1 : pin_nRD;
assign nWR_pu = (pin_nWR === 1'bz) ? 1'b1 : pin_nWR;

And voila! Pull-ups. The === is needed to do a comparison to Z or X. With just ==, verilog rightly cannot determine the output and just assigns X if the the output is not 100% clear from the inputs. In other words a==b in verilog where a and b are both 1'bz is neither true nor false because the interpreter says Z can be either H or L--not sure which. So a==b might me H==L or H==H or L==H or L==L. But with ===, if both a and b are 1'bz, the result is true (false for !==) and if a and b are both 1'bx, then the results is true (again, false for !==). I think that 1'bz !== 1'bx. Obviously this behavior can't be synthesized, but it is useful for testing.

I connected my memory to these and everything worked as expected.

Here is the testbench code with the relevant parts in bold:

// TOOL:     vlog2tf
// DATE:     07/11/15  10:39:08 
// TITLE:    Lattice Semiconductor Corporation
// MODULE:   flashProgrammer
// DESIGN:   flashProgrammer
// FILENAME: flashProgrammer.tft
// PROJECT:  flashprog
// VERSION:  1.0
// This file is auto generated by the ispLEVER


`timescale 1 ns / 1 ns

// Define Module for Test Fixture
module flashProgrammer_tf();

// Inputs
    reg pin_ENABLE;
    reg pin_CLK;
    reg pin_Din;
    reg pin_nCTRL;


// Outputs
    wire pin_Dout;
    wire [17:0] pins_A;
    wire pin_nWR;
    wire pin_nRD;
    wire pin_nCS;


// Bidirs
    wire [7:0] pins_D;
 
// pull-up lines
    wire nWR_pu;
    wire nRD_pu;
    wire nCS_pu;


// Instantiate the UUT
    flashProgrammer UUT (
        .pin_ENABLE(pin_ENABLE), 
        .pin_CLK(pin_CLK), 
        .pin_Din(pin_Din), 
        .pin_nCTRL(pin_nCTRL), 
        .pin_Dout(pin_Dout), 
        .pins_A(pins_A), 
        .pin_nWR(pin_nWR), 
        .pin_nRD(pin_nRD), 
        .pin_nCS(pin_nCS), 
        .pins_D(pins_D)
        );

    flash simFlash (
        .A(pins_A),
        .D(pins_D),
        .nWR(nWR_pu),
        .nRD(nRD_pu),
        .nCS(nCS_pu)
        );

    reg [2:0] ctrlToSend;
    reg [17:0] aToSend;
    reg [7:0] dToSend;
    reg [3:0] cswrrdToSend;
    reg [7:0] dRcvd;
    reg [7:0] dShift;


    // Pullups
    assign nCS_pu = (pin_nCS===1'bz) ? 1'b1 : pin_nCS;
    assign nRD_pu = (pin_nRD===1'bz) ? 1'b1 : pin_nRD;
    assign nWR_pu = (pin_nWR===1'bz) ? 1'b1 : pin_nWR;

// Initialize Inputs
// You can add your stimulus here
    initial begin

        resetSequence();

        write(17'h12345, 8'haa);

        simFlash.testRdD = 8'hC3;
        $display("simFlash.testRdD = %h", simFlash.testRdD);

        read(17'h0abcd, dRcvd);

        resetSequence();

        write(17'h14567, 8'h55);
        write(17'h14568, 8'h77);

        simFlash.testRdD = 8'h3C;
        $display("simFlash.testRdD = %h", simFlash.testRdD);

        read(17'h19876, dRcvd);
        simFlash.testRdD = 8'h55;
        read(17'h19877, dRcvd);

        #10

        $finish;
    end

    task resetSequence;
        begin
            aToSend = 0;
            dToSend = 0;

            pin_ENABLE = 1;
            pin_CLK = 0;
            pin_Din = 0;
            pin_nCTRL = 1;

            pulseClk();

            #5 
            pin_nCTRL = 0; // negedge resets outputs:w

        
            pulseClk(); // CLK pulse resets flash_control
            pulseClk(); // CLK pulse resets flash_control
            pulseClk(); // CLK pulse resets flash_control

            #5 
            pin_nCTRL = 1; // stobes chip control out

            #5 
            pin_ENABLE = 0;

        end
    endtask

    task write;
        input [17:0] A;
        input [7:0] D;
        begin
            $display("Writing %h to %h.", D, A);

            setFullAddress(A);

            setCtrl(3'b000);
            sendData(D);
            setCtrl(3'b111);
            setDriveCsWrRd(0, 0, 1, 1); // drive, CS
            pulseCtrl();
            setDriveCsWrRd(0, 0, 0, 1); // drive CS WR
            pulseCtrl();
            setDriveCsWrRd(0, 0, 1, 1); // drive CS
            pulseCtrl(); 
            setDriveCsWrRd(1, 1, 1, 1); // done
            pulseCtrl();
            $display("SimFlash saw %h<-%h", simFlash.testWrA, simFlash.testWrD);

            if (A != simFlash.testWrA) begin
                $display("ERROR!");
                $finish;
            end
            if (D != simFlash.testWrD) begin
                $display("ERROR!");
                $finish;
            end
        end
    endtask

    task read;
        input [17:0] A;
        output [7:0] D;
        begin
            $display("Reading %h.", A);

            setFullAddress(A);

            setCtrl(3'b111);
            setDriveCsWrRd(0, 0, 1, 0); // drive CS RD
            pulseCtrl();
            setDriveCsWrRd(1, 0, 1, 0); // CS RD
            pulseCtrl();
            setDriveCsWrRd(1, 1, 1, 1); // done
            pulseCtrl();
            setCtrl(3'b000);
            recvData();
            D = dShift;
            dShift = 8'bxxxxxxxx;
            $display("Read %h", D);
            $display("SimFlash saw %h", simFlash.testRdA);

            if (D != simFlash.testRdD) begin
                $display("ERROR!");
                $finish;
            end
        end
    endtask

    task setFullAddress;
        input [17:0] A;
        begin
            if (A[3:0] != aToSend[3:0]) begin
                //$display("Writing A[3:0]");
                setCtrl(3'b001);
                setAddr3_0(A[3:0]);
            end
            if (A[7:4] != aToSend[7:4]) begin
                //$display("Writing A[7:4]");
                setCtrl(3'b010);
                setAddr7_4(A[7:4]);
            end
            if (A[17:8] != aToSend[17:8]) begin
                //$display("Writing A[17:8]");
                setCtrl(3'b011);
                setAddr17_8(A[17:8]);
            end
        end
    endtask

    task setCtrl;
        input [2:0] regNo;
        begin
            ctrlToSend = regNo;
            #5
            pin_nCTRL = 0;
            pin_Din = ctrlToSend[0];
            pulseClk();
            pin_Din = ctrlToSend[1];
            pulseClk();
            pin_Din = ctrlToSend[2];
            pulseClk();
            #5
            pin_nCTRL = 1;
        end
    endtask
    
    task pulseCtrl;
        begin
            #5
            pin_nCTRL = 0;
            #5
            pin_nCTRL = 1;
        end
    endtask
   
    task pulseClk;
        begin
            #2
            pin_CLK = 0;
            #5
            pin_CLK = 1;
            #2
            pin_CLK = 0;
        end
    endtask
   
    task setAddr3_0;
        input [3:0] addrPart;
        begin
            aToSend[3:0] = addrPart;
            pin_Din = aToSend[0];
            pulseClk();
            pin_Din = aToSend[1];
            pulseClk();
            pin_Din = aToSend[2];
            pulseClk();
            pin_Din = aToSend[3];
            pulseClk();
        end
    endtask

    task setAddr7_4;
        input [3:0] addrPart;
        begin
            aToSend[7:4] = addrPart;
            pin_Din = aToSend[4];
            pulseClk();
            pin_Din = aToSend[5];
            pulseClk();
            pin_Din = aToSend[6];
            pulseClk();
            pin_Din = aToSend[7];
            pulseClk();
        end
    endtask

    task setAddr17_8;
        input [9:0] addrPart;
        begin
            aToSend[17:8] = addrPart;
            pin_Din = aToSend[8];
            pulseClk();
            pin_Din = aToSend[9];
            pulseClk();
            pin_Din = aToSend[10];
            pulseClk();
            pin_Din = aToSend[11];
            pulseClk();
            pin_Din = aToSend[12];
            pulseClk();
            pin_Din = aToSend[13];
            pulseClk();
            pin_Din = aToSend[14];
            pulseClk();
            pin_Din = aToSend[15];
            pulseClk();
            pin_Din = aToSend[16];
            pulseClk();
            pin_Din = aToSend[17];
            pulseClk();
        end
    endtask

    task setDriveCsWrRd;
        input rqDrive;
        input rqCs;
        input rqWr;
        input rqRd;
        begin
            cswrrdToSend = { rqDrive, rqCs, rqWr, rqRd };
            pin_Din = cswrrdToSend[0];
            pulseClk();
            pin_Din = cswrrdToSend[1];
            pulseClk();
            pin_Din = cswrrdToSend[2];
            pulseClk();
            pin_Din = cswrrdToSend[3];
            pulseClk();
        end
    endtask

    task sendData;
        input [7:0] data;
        begin
            dToSend = data;
            pin_Din = dToSend[0];
            pulseClk();
            pin_Din = dToSend[1];
            pulseClk();
            pin_Din = dToSend[2];
            pulseClk();
            pin_Din = dToSend[3];
            pulseClk();
            pin_Din = dToSend[4];
            pulseClk();
            pin_Din = dToSend[5];
            pulseClk();
            pin_Din = dToSend[6];
            pulseClk();
            pin_Din = dToSend[7];
            pulseClk();
        end
    endtask

    task recvData;
        begin
            pulseClk();
            dShift[0] = pin_Dout;
            pulseClk();
            dShift[1] = pin_Dout;
            pulseClk();
            dShift[2] = pin_Dout;
            pulseClk();
            dShift[3] = pin_Dout;
            pulseClk();
            dShift[4] = pin_Dout;
            pulseClk();
            dShift[5] = pin_Dout;
            pulseClk();
            dShift[6] = pin_Dout;
            pulseClk();
            dShift[7] = pin_Dout;
        end
    endtask

endmodule // flashProgrammer_tf

module flash(
    input [78:0] A,
    inout [7:0] D,

    input nWR,
    input nRD,
    input nCS,
);

    reg [7:0] testWrD;
    reg [17:0] testWrA;
    reg [7:0] testRdD;
    reg [17:0] testRdA;

    assign D = ((nRD | nCS) == 1'b0) ? testRdD : 8'bzzzzzzzz;

    always @(posedge nRD) begin
        if (nCS == 1'b0) begin
            testRdA <= A;
        end

    end
    always @(posedge nWR) begin
        if (nCS == 1'b0) begin
            testWrA <= A;
            testWrD <= D;
        end
    end

endmodule

Some Design, Some Work, Some...Success

I made some 8.5" x 14" sheets of my notebook page to plan my routing. Here are two sheets that show some of what I plan to do tonight.


So, let me explain what this is. The top sheet shows the address lines according to location on the PCB. (Yes, I know that it's not really a "printed" circuit board, but I still call it a PCB in my head.) The bottom sheet shows the design for the dedicated JTAG programmer. The lower left of the PCB shows that dedicated programmer PLCC-44 socket and the 2x5 pin header for the JTAG connecter. The ispDownload Cable in the upper right of the picture is my Lattice JTAG programmer.

I'll also place the 5V voltage regulator in the space just above the dedicated JTAG programmer. Which is important because that's the first thing I'll do. Make sure I can generate a 5V power supply.

I'll use the dedicated JTAG programmer to create the flash programmer CPLD. Then I'll pop it out and place it in a socket that will underneath the PCB. On all 4 sides of the socket I'll have 12 pin headers that will be wirewrappable connections to the PLCC socket. To connect them together, I'll solder little wires from the socket pins to the header pins. The little wires are shown in the lower sheet just to the upper left of the dedicated JTAG socket.

So my nest steps are:
solder in the voltage regulator, and an LED/220 ohm resistor
plug into a $4 power supply I got from Value Village.
ensure the LED lights up.

So---I've just done 2/3 of these. I do have a 5V supply, but the diode isn't lighting up. Also, I suck at soldering. Thank goodness that I have a 10x jeweler's loop. I think I need magnifying glasses.

I replaced the diode--no small feat for someone who sucks at soldering. And it lights up now!

Yay!!! My first triumph. 5V and a lit up LED!!! I'll see if my kitchen thermometer can see how hot the voltage regulator is getting....not hot at all.

Here's proof of the light.


CPLD Pin Assignment

After the easy-peasy pin assignment on the flash programmer CPLD, I just assigned pins on the MCU CPLD. But that didn't work. Not enough partition crossovers or something.

So I sorted out a few things in within the 4 GLB blocks. And that worked. So my CPLD is not super ordered.

MCU Design Refinements

I changed the MCU CPLD verilog.

The MCU is just a simple bank switcher. I had 4 5 bit banks. The top two bits of the 16 bit address bus: 15 and 14, select a map. Then this map result is used for address bits 17-14.

Originally, I was thinking I would the top bit (essentially A[18]) as the chip select for the SRAM or the flash. But Duh!  Ab address line does not a chip select make. So I removed the top address line and added two pin_nCS lines. These will select. And they tristate in reset. Also, the mapping tristates in reset also. This will allow me to use my in-circuit flash programmer. The Arduino will assert RESET to....   I need that needle scratching across the record sound effect... I just realized a problem.

I went to see if the bus control signals tristate on the Z-80 in reset. They don't. They go high. I DO need to use BUSREQ because they do tri-state in that mode. And I can't see what happens if I just tie BUSREQ to RESET. Which takes priority? Most likely RESET.

Well, crap! Back to the CPLD design...

[UPDATE: OK, I've decided to use 2 switches on the DIP switch instead. So the CPLD pin_nRESET line will connect to the board RESET or CPU BUSREQ line. So all is well with the CPLD...for now.]

Here is the latest MCU code before I realized that I need to use BUSREQ or BUSACK to tristate the bus lines:
       
// This CPLD meant for the Lattice M4A5 64/32 is a simple bank switching
// memory control unit. It has 8 registers:
//  00 - bank 0
//  01 - bank 1
//  02 - bank 2
//  03 - bank 3
//  04 - update 0
//  05 - update 1
//  06 - update 2
//  07 - update 3
//
//  The banks are meant to substitute starting address line 14.
//  The update registers can only be written to from bank 00 i.e. the lowest
//  16kb.
//
//  The banks are updated from the update registers when HALT is called
//  from bank 0. NMI immediately follows HALT in this circumstance
//

`define BANKTOP 4
`define IO_RANGE 7:3
`define IO_VALUE 5'b00000
`define ACTIVELOW 1'b0

module mcu (
    input pin_CLK,
    input [15:0] pins_A,
    input pin_nRESET, pin_nWR, pin_nRD, pin_nMREQ, pin_nIORQ, pin_nM1, pin_nHALT,
    output reg pin_nNMI,
    output [`BANKTOP-1:0] pins_Aout,
    output pin_nCS0, pin_nCS1,
    inout [7:0] pins_D
);

    reg [`BANKTOP:0] bankReg0;
    reg [`BANKTOP:0] bankReg1;
    reg [`BANKTOP:0] bankReg2;
    reg [`BANKTOP:0] bankReg3;
    reg [`BANKTOP:0] updateReg0;
    reg [`BANKTOP:0] updateReg1;
    reg [`BANKTOP:0] updateReg2;
    reg [`BANKTOP:0] updateReg3;
    reg nKernel;
    wire nInRange;
    reg [7:0] Dint;
    wire [`BANKTOP:0] selBank;

    // Determine if the address bus is talking to us
    assign nInRange = ((pins_A[`IO_RANGE] == `IO_VALUE) ? 1'b0 : 1'b1) | pin_nIORQ;

    // Get selected output
    assign selBank = pins_A[15] ? (pins_A[14] ? bankReg3 : bankReg2) : (pins_A[14] ? bankReg1 : bankReg0);

    // assign Aout -- Hi-Z on reset
    assign pins_Aout = pin_nRESET ? selBank[`BANKTOP-1:0] : 8'bzzzzzzzz;

    // CS's for top bit of bank
    assign pin_nCS0 = pin_nRESET ? (selBank[`BANKTOP] | pin_nMREQ) : 1'bz;
    assign pin_nCS1 = pin_nRESET ? ((~selBank[`BANKTOP]) | pin_nMREQ) : 1'bz;

    assign pins_D = ((pin_nRD | nInRange) == 1'b0) ? Dint : 8'bzzzzzzzz;

    always @(*) begin
        case (pins_A[2:0])
            3'b000:
                Dint = bankReg0;
            3'b001:
                Dint = bankReg1;
            3'b010:
                Dint = bankReg2;
            3'b011:
                Dint = bankReg3;
            3'b100:
                Dint = updateReg0;
            3'b101:
                Dint = updateReg1;
            3'b110:
                Dint = updateReg2;
            3'b111:
                Dint = updateReg3;
        endcase
    end

    always @(posedge pin_CLK) begin
        if (pin_nRESET == `ACTIVELOW) begin
            bankReg0 <= 0;
            bankReg1 <= 1;
            bankReg2 <= (1 << `BANKTOP);
            bankReg3 <= (1 << `BANKTOP) | 1;
            pin_nNMI <= 1'b1;
        end
        else begin
            if ((pin_nHALT | nKernel) == `ACTIVELOW) begin
                bankReg0 <= updateReg0;
                bankReg1 <= updateReg1;
                bankReg2 <= updateReg2;
                bankReg3 <= updateReg3;
            end

            pin_nNMI <= pin_nHALT;
            
            if ((pin_nM1 | pin_nMREQ) == `ACTIVELOW) begin
                nKernel <= pins_A[15] | pins_A[14];
            end
        end
    end
    
    always @(posedge pin_nWR) begin
        if (((pin_nIORQ | nInRange) == `ACTIVELOW) && (pins_A[2] == 1'b1)) begin
            case (pins_A[1:0])
                2'b00: begin
                    updateReg0 <= pins_D;
                end
                2'b01: begin
                    updateReg1 <= pins_D;
                end
                2'b10: begin
                    updateReg2 <= pins_D;
                end
                2'b11: begin
                    updateReg3 <= pins_D;
                end
            endcase
        end
    end

endmodule

Tuesday, July 14, 2015

It Occurred to Me...

If I want to be able program the flash in circuit, I need to be able to control the upper bits of the flash addressable range which means, I should disable mapping in the map CPLD on BUSACK low. Or--I need to be be able to control the MCU CPLD  which means I need to control nIORQ, nMREQ, and nM1 as well as nHALT. Well, that's all way to complicated. So I'll see if I can add BUSACK to the MCU CPLD.....  Running out of pins. I can't have too much stuff occurring to me....

Then I'll make the flash bank come from the Arduino. I plan to use the top bit of the address range choose the flash.

Dammit! I just realized, I need to invert it for CS on the SRAM.....    And dammit again, I need to only enable CS on flash and SRAM when MREQ is low.....

So, I have 2 pins left over I can use. reduce the top bit from map---, that makes 3. Then add in nBUSACK, nCS0, and nCS1. These are essentially the top bit of the mapping but only go low on nMREQ low. I hope it fits... I hope I can constrain the pins....

This will require a little tweaking to the design. Such is engineering....


Update: I'm overcomplicating the BUSACK part. RESET is good enough. I need to control RESET from the Arduino.