Showing posts with label Verilog. Show all posts
Showing posts with label Verilog. Show all posts

Thursday, September 17, 2015

Ticks

Last night, I decided to wire in the 1ms ticker. Of course, as soon as it wired in, the whole board stopped working. This morning I figured out that I had the INT and SCK lines swapped. So I unswapped them. I have already compiled in the tick driver so it should be grabbing the 3 bits of tick from the ticker. I verified that it is doing that when the UART interrupts occur.

I also verified that the INT pulls a pull-up low. But... I forgot that I had swapped out the 2MHz oscillator for a 4MHz oscillator. So my 1ms tick was actually 500μs. That was easy to fix. I ran home and grabbed my JTAG program and reburned the CPLD.

Now I had a 1ms tick. It wasn't counting correctly, but my code to handle the count wasn't right. Once that was corrected, it counted correctly. I can now make clocks.

However, there was an issue when I tried to program an intel hex file into flash--the tick makes the CPU too busy to keep up with communication. So, I rerouted the timer CPLD INT through a DIP switch. For now, I'll just turn off the timer when I want to program the flash. Until I can figure out a better way. I also have the 8MHz clock timer ready to burn. I may also toy with different tick amounts such as 2ms, or 5ms, or even 10ms.

Just for reference, here is the CPLD verilog:

// This CPLD is a timer circuit that generates a 500Hz tick. It takes a 2MHz
// clock in. Scales that down to 125MHz with a 4 bit counter.
// Then it counts 125 of those in a 7 bit counter that resets on 7c.
// When the reset occurs, the pin_nINT line goes from Hi-Z to low.
//
// The falling edge of pin_nCS pin brings pin_nINT back to Hi-Z.
//
// SCK and Dout are used to read the 3 bit tick counter. This is used
// to ensure that the count is kept synched. Dout is updated on the
// falling edge of SCK and the MSB is shifted first. 
//

`define COUNTER_TOP 11

module timer (
    input pin_CLK,
    input pin_nCS,
    input pin_SCK,
    output pin_Dout,
    output pin_nINT
    );

    reg [`COUNTER_TOP:0] counter;
    reg [2:0] ticks;
    reg [2:0] tickShift;
    wire nTick;
    reg nInt;
    reg nCS0;
    reg nCS1;
    reg SCK0;

    // Not synthesized. Just for testing
    initial begin
        counter = 0;
        ticks = 0;
    end

    // @ 2MHz 1ms, 124
    // @ 4MHz 1ms, 249
    // @ 8MHz 1ms, 499
    //assign nTick = ~(counter ==   11'b11111001111); // 124'15 causes 125'0->0'0
    assign nTick = ~(counter ==  12'b111110011111); // 124'31 causes 250'0->0'0
    //assign nTick = ~(counter == 13'b1111100111111); // 124'63 causes 500'0->0'0
    assign pin_nINT = (nInt == 1'b1) ? 1'bz : 1'b0;
    assign pin_Dout = pin_nCS ? 1'bz : tickShift[2];

    always @(posedge pin_CLK) begin
        if (nTick == 1'b0) begin
            counter <= 0;
            ticks <= ticks+1;
        end
        else begin
            counter <= counter+1;
        end

        nCS0 <= pin_nCS;
        nCS1 <= nCS0;

        // Set interrupt on the CLK. Reset it on the falling edge of
        // pin_nCS+2 clocks
        if (nTick == 1'b0) begin
            nInt <= 1'b0; 
        end
        else if ((nCS1 & (~nCS0)) == 1'b1) begin
            nInt <= 1'b1; 
        end

        // search for falling edge of SCK in CLK
        SCK0 <= pin_SCK;
        if (~pin_nCS) begin
            if (SCK0 & ~pin_SCK) begin
                tickShift[2:0] <= { tickShift[1:0], 1'b0 };
            end
        end
        else begin
            tickShift[2:0] <= ticks[2:0];
        end

    end

endmodule

Here is tick.c:

#include "tick.h"
#include "spi.h"
#include "gpio.h"
#include "idle.h"
#include "uart.h"
#include "timer.h"
#include "interrupt.h"

Z80_IO_PORT(GPIO_OUT, 0x80);
Z80_IO_PORT(SPI_REG, 0x83);
Z80_IO_PORT(SPI_CTRL, 0x84);

uint8_t lastTick = 0;
uint32_t totalTicks = 0;

void tickInit()
{
    // GPIO-0 is UART CS#
    gpioWriteLevel(0x02, 0x02);
    gpioWriteDirection(0x02, 0x02);
    lastTick = 0;
    totalTicks = 0;
}

uint32_t getTicks()
{
    uint32_t ticks;
    di();
    ticks = totalTicks;
    ei();
    return ticks;
}

// called from the actual ISR, so this one doesn't end in reti
void tickISR()
{
    uint8_t tick;
    uint8_t out;

    //tick = spiExchange(0, SPI_SHIFT_TO_MSB, 3);
    out = GPIO_OUT;
    GPIO_OUT = out & ~(1 << 1);
    SPI_REG = 0;
    SPI_CTRL = SPI_SHIFT_TO_MSB | 3;
    __asm__("nop");
    __asm__("nop");
    tick = SPI_REG;
    tick &= 0x07;
    GPIO_OUT = out;

    if (lastTick != tick)
    {
        totalTicks += (uint8_t)((tick - lastTick) & 0x07);
        lastTick = tick;
        timerTick(totalTicks);
        releaseIdle();
    }
}

So, next I have to start working with the timers and idle. One of my short term goals was to make a clock...now that's possible. Also, timeouts for communication.

I haven't used the timer code or idle yet. Here they are--probably buggy as hell since they are totally not tested.

timer.c:

#include "timer.h"
#include "tick.h"
#include <string.h>
#include <assert.h>

#define TIMER_COUNT 2

struct TimerInfo
{
    uint32_t interval;
    uint32_t trigger;
    bool claimed;
    bool repeat;
    bool running;
    void(*callback)(int timer);
};

struct TimerInfo timers[TIMER_COUNT];



void timerInit()
{
    memset(timers, sizeof(timers), 0);
}

// called from inside interrupt
void timerTick(uint32_t totalTicks)
{
    int timerId;
    for (timerId = 0; timerId < TIMER_COUNT; timerId++)
    {
        struct TimerInfo* timer = &timers[timerId];
        if (timer->claimed)
        {
            if (timer->running)
            {
                if ((int)(totalTicks - timer->trigger) >= 0)
                {
                    // tick has reached the trigger
                    if (timer->repeat)
                    {
                        timer->trigger += timer->interval;
                    }
                    if (timer->callback)
                    {
                        timer->callback(totalTicks);
                    }
                }
            }
        }
    }
}

int timerGetAvailableTimerId()
{
    int timerId = -1;
    int i;
    for (i = 0; i < TIMER_COUNT; i++)
    {
        struct TimerInfo* timer = &timers[timerId];
        if (!timer->claimed)
        {
            timer->claimed = 1;
            timerId = i;
            break;
        }
    }
    return timerId;
}

void timerFree(int timerId)
{
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timers[timerId].claimed = 0;
}

void timerSet(int timerId, uint32_t interval, bool repeat, bool start, void(*cb)(int timer))
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    assert(timer->claimed);
    timer->interval = interval;
    timer->repeat = repeat;
    timer->running = start;
    timer->callback = cb;
    timer->trigger = getTicks() + interval;
}

void timerGet(int timerId, uint32_t* pTrigger, uint32_t* pInterval, bool* pRepeat, bool* pRunning, void(**pCb)(int timer))
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    if (pTrigger)
    {
        *pTrigger = timer->trigger;
    }
    if (pInterval)
    {
        *pInterval = timer->interval;
    }
    if (pRepeat)
    {
        *pRepeat = timer->repeat;
    }
    if (pRunning)
    {
        *pRunning = timer->running;
    }
    if (pCb)
    {
        *pCb = timer->callback;
    }
}

void timerStop(int timerId)
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    timer->running = 0;
}

void timerRun(int timerId)
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    timer->running = 1;
}

void timerResetInterval(int timerId)
{
    struct TimerInfo* timer;
    assert(timerId > 0 && timerId < TIMER_COUNT);
    timer = &timers[timerId];
    timer->trigger = getTicks() + timer->interval;
}


idle.c:

#include "idle.h"
#include "interrupt.h"

volatile bool idling;

// DO NOT CALL FROM CRITICAL SECTION
void idle(void(*pIdleFn)(void* p), void* p)
{
    idling = true;
    // assert(interrupts enabled);

    while (idling)
    {
        // spinwait for tick to set idling to false
        if (pIdleFn)
        {
            pIdleFn(p);
        }
    }
}

void releaseIdle()
{
    idling = false;
}



Thursday, August 20, 2015

Simplified Timer

I realized that the timer CPLD verilog was overly complicated. So I simplified it:

// This CPLD is a timer circuit that generates a 500Hz tick. It takes a 2MHz
// clock in. Scales that down to 125MHz with a 4 bit counter.
// Then it counts 125 of those in a 7 bit counter that resets on 7c.
// When the reset occurs, the pin_nINT line goes from Hi-Z to low.
//
// The falling edge of pin_nCS pin brings pin_nINT back to Hi-Z.
//
// SCK and Dout are used to read the 3 bit tick counter. This is used
// to ensure that the count is kept synched. Dout is updated on the
// falling edge of SCK and the MSB is shifted first. 
//


module timer (
    input pin_CLK,
    input pin_nCS,
    input pin_SCK,
    output pin_Dout,
    output pin_nINT
    );

    reg [10:0] counter;
    reg [2:0] ticks;
    reg [2:0] tickShift;
    wire nTick;
    reg nInt;
    reg nCS0;
    reg nCS1;
    reg SCK0;

    // Not synthesized. Just for testing
    initial begin
        counter = 0;
        ticks = 0;
    end

    assign nTick = ~(counter == 11'b11111001111); // 124'15 causes 125'0->0'0
    assign pin_nINT = (nInt == 1'b1) ? 1'bz : 1'b0;
    assign pin_Dout = pin_nCS ? 1'bz : tickShift[2];

    always @(posedge pin_CLK) begin
        if (nTick == 1'b0) begin
            counter <= 0;
            ticks <= ticks+1;
        end
        else begin
            counter <= counter+1;
        end

        nCS0 <= pin_nCS;
        nCS1 <= nCS0;

        // Set interrupt on the CLK. Reset it on the falling edge of
        // pin_nCS+2 clocks
        if (nTick == 1'b0) begin
            nInt <= 1'b0; 
        end
        else if ((nCS1 & (~nCS0)) == 1'b1) begin
            nInt <= 1'b1; 
        end

        // search for falling edge of SCK in CLK
        SCK0 <= pin_SCK;
        if (~pin_nCS) begin
            if (SCK0 & ~pin_SCK) begin
                tickShift[2:0] <= { tickShift[1:0], 1'b0 };
            end
        end
        else begin
            tickShift[2:0] <= ticks[2:0];
        end

    end

endmodule

Wednesday, August 19, 2015

Timer CPLD

Here is the verilog for the 4th CPLD that I figured out I need. It's a 1ms timer. It will pull the interrupt pin low and return to Hi-Z mode when the interrupt is cleared by reading the tick counter. The tick counter is 3 bits and can be used to ensure some degree of consistency when late servicing the tick interrupt or when using shared interrupts (which I will do). It has a very small pin footprint. Just clock, read-only SPI, chip select, and the interrupt line. This one fits into the smaller M4A5-32/32 so I'll use that.

// This CPLD is a timer circuit that generates a 500Hz tick. It takes a 2MHz
// clock in. Scales that down to 125MHz with a 4 bit counter.
// Then it counts 125 of those in a 7 bit counter that resets on 7c.
// When the reset occurs, the pin_nINT line goes from Hi-Z to low.
//
// The falling edge of pin_nCS pin brings pin_nINT back to Hi-Z.
//
// SCK and Dout are used to read the 3 bit tick counter. This is used
// to ensure that the count is kept synched. Dout is updated on the
// falling edge of SCK and the MSB is shifted first. 
//


module timer (
    input pin_CLK,
    input pin_nCS,
    input pin_SCK,
    output pin_Dout,
    output pin_nINT
    /*
    output [3:0] test_prescaler,
    output [6:0] test_counter,
    output [2:0] test_ticks,
    output [2:0] test_tickShift,
    output test_nCount,
    output test_nTick,
    output test_nInt,
    output test_nCS0,
    output test_nCS1
    */
    );

    /*
    assign test_prescaler = prescaler;
    assign test_counter = counter;
    assign test_ticks = ticks;
    assign test_tickShift = tickShift;
    assign test_nCount = nCount;
    assign test_nTick = nTick;
    assign test_nInt = nInt;
    assign test_nCS0 = nCS0;
    assign test_nCS1 = nCS1;
    */

    reg [3:0] prescaler;
    reg [6:0] counter;
    reg [2:0] ticks;
    reg [2:0] tickShift;
    reg nCount;
    reg nTick;
    reg nInt;
    reg nCS0;
    reg nCS1;
    reg SCK0;
    reg SCK1;

    initial begin
        prescaler = 0;
        counter = 0;
        ticks = 0;
    end

    assign pin_nINT = (nInt == 1'b1) ? 1'bz : 1'b0;

    always @(posedge pin_CLK) begin
        prescaler <= prescaler + 1;
        nCount <= ~(&prescaler); // 0 once every 16 pin_CLK
        nTick <= ~(counter == 7'b1111100); // 124 caused 125->0
        if (nCount == 1'b0) begin
            if (nTick == 1'b0) begin
                counter <= 0;
                ticks <= ticks+1;
            end
            else begin
                counter <= counter+1;
            end
        end

        nCS0 <= pin_nCS;
        nCS1 <= nCS0;

        // Set interrupt on the CLK. Reset it on the falling edge of
        // pin_nCS+2 clocks
        if ((nTick | nCount) == 1'b0) begin
            nInt <= 1'b0; 
        end
        else if ((nCS1 & (~nCS0)) == 1'b1) begin
            nInt <= 1'b1; 
        end

        SCK0 <= pin_SCK;
        SCK1 <= SCK0;
        if (~pin_nCS) begin
            //if (SCK1 & ~SCK0) begin
            if (SCK0 & ~pin_SCK) begin
                tickShift[2:0] <= { tickShift[1:0], 1'b0 };
            end
        end
        else begin
            tickShift[2:0] <= ticks[2:0];
        end

    end
    assign pin_Dout = pin_nCS ? 1'bz : tickShift[2];

endmodule

Saturday, August 15, 2015

GPIO SPI CPLD

In order to connect the UART (MAX3110E) part to the Z-80, I'll use a CPLD that does SPI. My first CPLD plan was fairly specific to the SPI of the UART part. But, it makes more sense to make something a little more general purpose. So, I came up with the following verilog design.

This design should allow me to do not just SPI but also I2C. And it has 8 GPIO.

The MAX3110E requires that its CS goes low while SCLK is low. Then a 16 bit data exchange takes place. Then while SCLK is low, CS goes high. It updates Dout and expects Din to be updated on SCLK falling edge and clocks in and expects the user to clock in on the rising edge. Sixteen times.

So this will be able to do it by using a GPIO as CE.

Here some example Z-80 assembly that would use the CPLD to communicate with the MAX3110E:


; Assume HL is the data to write out to MAX3110E and HL will have the
; exchanged MAX3110E data upon return
; CS# low
    IN A, (0x80)         ; get current GPIOs
    AND A, ~0x01         ; set bit 0 to 0
    OUT (0x80), A        ; write GPIO back out

; exchange H
    OUT (0x83), H        ; load shift register
    OUT (0x84), 0x48     ; shift 8 toward MSB
    NOP                  ; give it time to work
    NOP
    IN H, (0x83)         ; read in first 8 bits from MAX3110E

; exchange L
    OUT (0x83), L        ; load shift register
    OUT (0x84), 0x48     ; shift 8 toward MSB
    NOP                  ; give it time to work
    NOP
    IN L, (0x83)         ; read in last 8 bits from MAX3110E

; CS# high
    IN A, (0x80)         ; get current GPIOs
    OR A, 0x01           ; set bit 0 to 0
    OUT (0x80), A        ; write GPIO back out

    RET


Here is the verilog:

// SPI module
//
//  This CPLD will provide 8 GPIO and limited serial IO for SPI and/or I2C
//      The registers are:
//      0x80: GPIO outvalues
//          A read/write register holding values to be written on GPIO lines
//          that are in output mode
//      0x81: GPIO direction (1=out, 0=in)
//          A read/write register to control the input/output mode of the GPIO
//          lines
//      0x82: GPIO levels
//          A read only register with the current GPIO levels.
//      0x83: shift register (shifted based on control register)
//          A read/write register with data to be shifted in/out
//      0x84: shift control
//          bits 3-0: count
//              0: no shifting
//              1-8: shift by the amount
//              9-15: not supported
//          bit 4: 1=HiZ Dout and use it for input, 0=use Din for input
//              Use 1 for I2C type buses where the clock master controls
//              a bidirectional data line
//              Use 0 for full duplex buses with separate Din and Dout
//          bit 5: 1=shift toward MSB 0=SCK shift toward LSB
//              Use 1 to shift up. If the count is less than 8, the value
//              written to the shift register must be shifted by the CPU
//              Data is read into the LSB
//              Use 0 to shift down. Data is read into the MSB
//          bit 6: 1=SCK goes write high read low 0=SCK goes write low read high
//              Internally, the counter is decremented every 2 pin_CLK. The
//              first beat outputs Dout. The second shifts Din in.
//              Use 0 if the first beat is 0 and the second is 1. i.e. read on
//              rising edge
//              Use 1 if the first beat is 1 and the second is 0. i.e. read on
//              falling edge
//          bit 7: 1=SCK default high, 0=SCK default low
//              Use this bit to set the SCK before and after the actual
//              shift operation
//
//  Usage:
//      0x80, 0x81, and 0x82 control 8 GPIO pins
//      read and write to 0x83 for data shifted in/out
//      Use 0x84 to write a write only count. This will immediately start
//      to shift the shift register in/out. Use a NOP or two to ensure it
//      is complete before reading or writing
//      Use bit5 =1 for 2 wire serial and =0 for 3 wire serial.
//
`define IO_VALUE 5'b10000
`define IO_RANGE 7:3
`define ACTIVELOW 1'b0

module GPIO_SPI(
    // 2MHz clock for CPU
    input pin_CLK,

    // RESET
    input pin_nRESET,

    // CPU ddress bus
    input [7:0] pins_A,

    // CPU data bus
    inout [7:0] pins_D,

    // CPU pins
    input pin_nIORQ,
    input pin_nRD,
    input pin_nWR,
    
    // Pins to UART
    input pin_Din,
    inout pin_Dout,
    output reg pin_SCK,

    // GPIO
    inout [7:0]pins_GPIO
);

    // registers
    reg [7:0] regGpioOutLevel;
    reg [7:0] regGpioDirection;
    reg [7:0] regShift;
    reg [3:0] regCounter;
    reg regDoutIsDin;
    reg regShiftTowardMSB;
    reg regSckHigh;
    reg regDefaultSCK;

    // shift
    reg sck;
    reg Dout;
    wire Din;
    
    // Internal databus for output
    reg [7:0] Dint;

    // IO request in the addressable range of this module
    wire nInRange;

    assign Din = regDoutIsDin ? pin_Dout : pin_Din;
    assign pin_Dout = regDoutIsDin ? 1'bz : Dout;

    // An IO read in the addressable range of this module
    assign nInRange = ((pins_A[`IO_RANGE] == `IO_VALUE)? 1'b0 : 1'b1) | pin_nIORQ;

    // Bidirectional databus control
    assign pins_D = ((pin_nRD | nInRange) == `ACTIVELOW) ? Dint : 8'bzzzzzzzz;

    assign pins_GPIO[0] = regGpioDirection[0] ? regGpioOutLevel[0] : 1'bz;
    assign pins_GPIO[1] = regGpioDirection[1] ? regGpioOutLevel[1] : 1'bz;
    assign pins_GPIO[2] = regGpioDirection[2] ? regGpioOutLevel[2] : 1'bz;
    assign pins_GPIO[3] = regGpioDirection[3] ? regGpioOutLevel[3] : 1'bz;
    assign pins_GPIO[4] = regGpioDirection[4] ? regGpioOutLevel[4] : 1'bz;
    assign pins_GPIO[5] = regGpioDirection[5] ? regGpioOutLevel[5] : 1'bz;
    assign pins_GPIO[6] = regGpioDirection[6] ? regGpioOutLevel[6] : 1'bz;
    assign pins_GPIO[7] = regGpioDirection[7] ? regGpioOutLevel[7] : 1'bz;


    // Keep internal databus updated
    always @(*) begin
        case (pins_A[2:0])
            3'b000: begin
                Dint <= regGpioOutLevel[7:0];
            end
            3'b001: begin
                Dint <= regGpioDirection[7:0];
            end
            3'b010: begin
                Dint <= pins_GPIO[7:0];
            end
            3'b011: begin
                Dint <= regShift[7:0];
            end
            default: begin
                Dint <= 8'bxxxxxxxx;
            end
        endcase
    end

    always @(posedge pin_CLK) begin

        if (pin_nRESET == `ACTIVELOW) begin
            regGpioDirection <= 0;
            regCounter <= 0;
            regDoutIsDin <= 0;
            regShiftTowardMSB <= 0;
            regSckHigh <= 0;
            sck <= 0;
        end
        else begin
            if ((nInRange | pin_nWR) == `ACTIVELOW) begin
                case (pins_A[2:0])
                3'b000: begin
                    regGpioOutLevel[7:0] <= pins_D[7:0];
                end
                3'b001: begin
                    regGpioDirection[7:0] <= pins_D[7:0];
                end
                3'b011: begin
                    regShift[7:0] <= pins_D[7:0];
                end
                3'b100: begin
                    regCounter[3:0] <= pins_D[3:0];
                    regDoutIsDin <= pins_D[4];
                    regShiftTowardMSB <= pins_D[5];
                    regSckHigh <= pins_D[6];
                    regDefaultSCK = pins_D[7];
                    sck <= 1'b0;
                end
                endcase
                pin_SCK <= regDefaultSCK;
            end
            else if ((|regCounter) == 1'b1) begin
                if (sck == 1'b0) begin
                    Dout <= regShiftTowardMSB ? regShift[7] : regShift[0];
                end
                else begin
                    regCounter <= regCounter - 1;
                    if (regShiftTowardMSB) begin
                        regShift <= { regShift[6:0], Din };
                    end
                    else begin
                        regShift <= { Din, regShift[7:1] };
                    end
                end
                sck <= ~sck;
                pin_SCK <= sck ^ regSckHigh;
            end
            else begin
                pin_SCK <= regDefaultSCK;
            end
        end
    end

endmodule

Wednesday, July 29, 2015

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.

Friday, July 24, 2015

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.

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

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

Monday, July 13, 2015

Finalizing Flash Programmer CPLD

OK, getting the flash programmer CPLD verilog finalized. This one seems to work as I want it to. It lets me have fine control over low address bits, coarse control over high ones, and control over the RD, WR, and CS going to the flash chip as well as when to drive the data lines and when to read in the read data from the flash.

       
// This module has a set of serial registers that can be used to
// interface to an 8 bit wide 2MB chip.
// drive/strobe rd works by driving data when RD is high or pos edge
// of CLK reads in while nRD is low
//
// Toggle nCtrl high low high to strobe chip control register to pins
// Usage requires a software driver that does the following:
//
// Reset:
//      bring pin_ENABLE high,
//      bring pin_nCTRL high
//      toggle pin_CLK low high low
//      bring pin_nCTRL low
//      toggle pin_CLK low high low
//      toggle pin_CLK low high low
//      toggle pin_CLK low high low
//      bring pin_nCTRL high
//      bring pin_ENABLE low
//
// Write A[3:0] bits of address bus;
//      bring pin_nCTRL low
//      put 0 on pin_Din
//      toggle pin_CLK high low
//      put 0 on pin_Din
//      toggle pin_CLK high low
//      put 1 on pin_Din
//      toggle pin_CLK high low
//      bring pin_nCTRL high
//      put A[0] in pin_Din
//      toggle pin_CLK high low
//      put A[1] in pin_Din
//      toggle pin_CLK high low
//      put A[2] in pin_Din
//      toggle pin_CLK high low
//      put A[3] in pin_Din
//      toggle pin_CLK high low
//
// Write A[7:4] bits of address bus;
//      bring pin_nCTRL low
//      put 0 on pin_Din
//      toggle pin_CLK high low
//      put 1 on pin_Din
//      toggle pin_CLK high low
//      put 0 on pin_Din
//      toggle pin_CLK high low
//      bring pin_nCTRL high
//      put A[4] in pin_Din
//      toggle pin_CLK high low
//      put A[5] in pin_Din
//      toggle pin_CLK high low
//      put A[6] in pin_Din
//      toggle pin_CLK high low
//      put A[7] in pin_Din
//      toggle pin_CLK high low
//
// Write A[17:8] bits of address bus;
//      bring pin_nCTRL low
//      put 1 on pin_Din
//      toggle pin_CLK high low
//      put 1 on pin_Din
//      toggle pin_CLK high low
//      put 0 on pin_Din
//      toggle pin_CLK high low
//      bring pin_nCTRL high
//      put A[8] in pin_Din
//      toggle pin_CLK high low
//      put A[9] in pin_Din
//      toggle pin_CLK high low
//      put A[10] in pin_Din
//      toggle pin_CLK high low
//      put A[11] in pin_Din
//      toggle pin_CLK high low
//      put A[12] in pin_Din
//      toggle pin_CLK high low
//      put A[13] in pin_Din
//      toggle pin_CLK high low
//      put A[14] in pin_Din
//      toggle pin_CLK high low
//      put A[15] in pin_Din
//      toggle pin_CLK high low
//      put A[16] in pin_Din
//      toggle pin_CLK high low
//      put A[17] in pin_Din
//      toggle pin_CLK high low
//
// Write drive/rdstrobe RD* WR* CS*
//      bring pin_nCTRL low
//      put 1 on pin_Din
//      toggle pin_CLK high low
//      put 1 on pin_Din
//      toggle pin_CLK high low
//      put 1 on pin_Din
//      toggle pin_CLK high low
//      bring pin_nCTRL high
//      put RD* in pin_Din
//      toggle pin_CLK high low
//      put WR* in pin_Din
//      toggle pin_CLK high low
//      put CS* in pin_Din
//      toggle pin_CLK high low
//      put drive/rdstrobe in pin_Din
//      toggle pin_CLK high low
//
// Write data bus to be asserted while WR* low;
//      bring pin_nCTRL low
//      put 0 on pin_Din
//      toggle pin_CLK high low
//      put 0 on pin_Din
//      toggle pin_CLK high low
//      put 0 on pin_Din
//      toggle pin_CLK high low
//      bring pin_nCTRL high
//      put D[0] in pin_Din
//      toggle pin_CLK high low
//      put D[1] in pin_Din
//      toggle pin_CLK high low
//      put D[2] in pin_Din
//      toggle pin_CLK high low
//      put D[3] in pin_Din
//      toggle pin_CLK high low
//      put D[4] in pin_Din
//      toggle pin_CLK high low
//      put D[5] in pin_Din
//      toggle pin_CLK high low
//      put D[6] in pin_Din
//      toggle pin_CLK high low
//      put D[7] in pin_Din
//      toggle pin_CLK high low
//
// Load data bus read on RD* rising edge;
//      bring pin_nCTRL low
//      put 0 on pin_Din
//      toggle pin_CLK high low
//      put 0 on pin_Din
//      toggle pin_CLK high low
//      put 0 on pin_Din
//      toggle pin_CLK high low
//      bring pin_nCTRL high
//      toggle pin_CLK high low
//      data = (data >> 1) | (pin_Dout << 7)
//      toggle pin_CLK high low
//      data = (data >> 1) | (pin_Dout << 7)
//      toggle pin_CLK high low
//      data = (data >> 1) | (pin_Dout << 7)
//      toggle pin_CLK high low
//      data = (data >> 1) | (pin_Dout << 7)
//      toggle pin_CLK high low
//      data = (data >> 1) | (pin_Dout << 7)
//      toggle pin_CLK high low
//      data = (data >> 1) | (pin_Dout << 7)
//      toggle pin_CLK high low
//      data = (data >> 1) | (pin_Dout << 7)
//      toggle pin_CLK high low
//      data = (data >> 1) | (pin_Dout << 7)
//   
//


`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 = nRDint | pin_ENABLE;
    assign pin_nWR = nWRint | pin_ENABLE;
    assign pin_nCS = nCSint | pin_ENABLE;
    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[2];
            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

And here is the test bench:
       




// 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;




// 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(pin_nWR),
        .nRD(pin_nRD),
        .nCS(pin_nCS)
        );


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


// 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