Interfacing with a classic SRAM chip

Hi. Sorry if this is the wrong forum category - I have been puzzling where to post this question for the past 10 mins.

I am trying to interface with two AS6C4008 4Mbit SRAM chips. Datasheet Here

I have 2 of these chips with the same address lines attached to each so that I can retrieve 2 lots of 8 bits per "row address".

I am doing this:

  1. writing to address 0,
  2. reading address 0 (data is correct)
  3. reading address 2 (don't care about data returned)
  4. reading address 0 again (data is different)

Can somebody please help me with this? I've been staring at the timing diagrams and cannot see what I'm doing wrong. I have a similar setup with an FPGA but I had a few problems there and thought it would be easier to test out operation using Ardunio Mega instead.

Below is my sketch.

//                     A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  A10  A11  A12  A13  A14  A15  A16  A17  A18
const char ADDR[]  = { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,  33,  34,  35,  36,  37,  38,  39,  40};
const char DATAH[] = { 41, 42, 43, 44, 45, 46, 47, 48 };

#define WE1_NOT 6
#define OE1_NOT 7
#define CE1_NOT 8

#define WE2_NOT 9
#define OE2_NOT 10
#define CE2_NOT 11


const char DATAL[] = { 49, 50, 51, 52, 53,  2,  3,  4 };


void setup() {
  // put your setup code here, to run once:
  for( int n = 0; n < 16; n ++ ) {
    pinMode( ADDR[n], OUTPUT );
  }

  pinMode( CE1_NOT, OUTPUT );
  pinMode( CE2_NOT, OUTPUT );
  pinMode( OE1_NOT, OUTPUT );
  pinMode( OE2_NOT, OUTPUT );
  pinMode( WE1_NOT, OUTPUT );
  pinMode( WE2_NOT, OUTPUT );

  setDataMode(INPUT);
  
  //attachInterrupt(digitalPinToInterrupt(CLOCK), onClock, RISING);
  Serial.begin(115200);
}

void setDataMode( int mode ) {
  for( int n = 0; n < 8; n ++ ) {
    pinMode( DATAH[n], mode );
    pinMode( DATAL[n], mode );
  }
}

int count = 0;

enum CMD_STATE {
  CMD_INIT,
  CMD_IDLE,
  CMD_SET_ADDRESS,
  CMD_POST_SET_ADDRESS,
  CMD_WRITE,
  CMD_POST_WRITE,
  CMD_POST_READ,

  CMD_READ,
  CMD_RETURN_TO_IDLE,
};

CMD_STATE rsmState = CMD_INIT;
CMD_STATE request  = CMD_IDLE;
unsigned int  requestWriteData  = 0;
unsigned int  requestReadData   = 0;
unsigned long requestDataLen    = 0;
unsigned long requestAddress    = 0;
unsigned long requestRowAddress = 0;

unsigned long wordsLeft         = 0;
char output[100];
int  requestCount = 0;
char done = 0;

//--------------------------------------------------------------------------------
// getBit
//--------------------------------------------------------------------------------
char getBit( int bit, unsigned long value ) {
  switch( bit ) {
    case  0: return ( value & 0x0001 ) ? HIGH : LOW;
    case  1: return ( value & 0x0002 ) ? HIGH : LOW;
    case  2: return ( value & 0x0004 ) ? HIGH : LOW;
    case  3: return ( value & 0x0008 ) ? HIGH : LOW;
    case  4: return ( value & 0x0010 ) ? HIGH : LOW;
    case  5: return ( value & 0x0020 ) ? HIGH : LOW;
    case  6: return ( value & 0x0040 ) ? HIGH : LOW;
    case  7: return ( value & 0x0080 ) ? HIGH : LOW;
    case  8: return ( value & 0x0100 ) ? HIGH : LOW;
    case  9: return ( value & 0x0200 ) ? HIGH : LOW;
    case 10: return ( value & 0x0400 ) ? HIGH : LOW;
    case 11: return ( value & 0x0800 ) ? HIGH : LOW;
    case 12: return ( value & 0x1000 ) ? HIGH : LOW;
    case 13: return ( value & 0x2000 ) ? HIGH : LOW;
    case 14: return ( value & 0x4000 ) ? HIGH : LOW;    
    case 15: return ( value & 0x8000 ) ? HIGH : LOW;
  }
  return LOW;
}

//--------------------------------------------------------------------------------
// setAddressPins
//--------------------------------------------------------------------------------
void setAddressPins( unsigned int address ) {
  // A0 thru A18
  for( int n = 0; n < 19; n ++ ) {
    digitalWrite(ADDR[n], getBit( n, address ) );
  }
}

//--------------------------------------------------------------------------------
// setDataPins
//--------------------------------------------------------------------------------
void setDataPins( unsigned int data ) {
  // D0 thru D7
  for( int n = 0; n < 8; n ++ ) {
    digitalWrite(DATAL[n], getBit( n, data & 0xFF ) );
  }

  unsigned int dataH = (data & 0xFF00) >> 8;
  
  // D8 thru D15
  for( int n = 0; n < 8; n ++ ) {
    digitalWrite(DATAH[n], getBit( n, dataH ) );
  }
}


//--------------------------------------------------------------------------------
// handleRSM
//--------------------------------------------------------------------------------
void handleRSM(){
  switch(rsmState){
    case CMD_INIT:
      rsmState = CMD_RETURN_TO_IDLE;    
      break;
      
    case CMD_IDLE:
      // Convert actual memory address to rowAddress (rowAddress=0, two bytes)
      requestRowAddress = requestAddress >> 1;
    
      if( request == CMD_WRITE ) {
        sprintf(output, "WRITE ROW: %05lx  Addr: %05lx  Data: %04x  Repeat: %ld", requestRowAddress, requestAddress, requestWriteData, requestDataLen);
        Serial.println(output);
        if( requestDataLen > 1 )
          wordsLeft = requestDataLen;
        else 
          wordsLeft = 1;
        rsmState = CMD_SET_ADDRESS;
      }
      
      if( request == CMD_READ ) {
        sprintf(output, "READ  ROW: %05lx  Addr: %05lx  Repeat: %ld", requestRowAddress, requestAddress, requestDataLen);
        Serial.println(output);
        wordsLeft = requestDataLen;
        rsmState = CMD_SET_ADDRESS;
      }
      break;

    case CMD_SET_ADDRESS:
      Serial.println("-> CMD_SET_ADDRESS");

      //digitalWrite(CE1_NOT, LOW);
      //digitalWrite(CE2_NOT, LOW);
      digitalWrite(WE1_NOT, HIGH);
      digitalWrite(WE2_NOT, HIGH);

      // Does OE need to be off when address asserted?
      //digitalWrite(OE1_NOT, HIGH);
      //digitalWrite(OE2_NOT, HIGH);

      setAddressPins( requestRowAddress );

      rsmState = CMD_POST_SET_ADDRESS;
      break;

    case CMD_POST_SET_ADDRESS:
      Serial.println("-> CMD_POST_SET_ADDRESS");
      digitalWrite(CE1_NOT, LOW);
      digitalWrite(CE2_NOT, LOW);
      
      // Choose read or write..
      rsmState = request;
      break;
      
    case CMD_WRITE:
      Serial.println("-> CMD_WRITE");
    
      setDataMode(OUTPUT);
      setDataPins( requestWriteData );
      digitalWrite(WE1_NOT, LOW);
      digitalWrite(WE2_NOT, LOW);
      
      rsmState = CMD_POST_WRITE;
      break;
    
    case CMD_POST_WRITE:
      Serial.println("-> CMD_POST_WRITE");

      digitalWrite(WE1_NOT, HIGH);
      digitalWrite(WE2_NOT, HIGH);

      wordsLeft --;
      
      if( wordsLeft > 0 ) {
        rsmState = CMD_SET_ADDRESS;
      } else {
        rsmState = CMD_RETURN_TO_IDLE;
      }
      break;
    
    case CMD_READ:
      Serial.println("-> CMD_READ");
      digitalWrite(OE1_NOT, LOW);
      digitalWrite(OE2_NOT, LOW);

      setDataMode(INPUT);    
    
      rsmState = CMD_POST_READ;    
      break;    
      
    case CMD_POST_READ:
    {
      unsigned int dataH = 0;
      unsigned int dataL = 0;
      int bit;

      Serial.println("-> CMD_POST_READ");
      
      for( int n = 7; n >= 0; n -- ) {
        
        bit = digitalRead(DATAH[n]);
        dataH = (dataH << 1 ) + bit;
    
        bit = digitalRead(DATAL[n]);
        dataL = (dataL << 1 ) + bit;
      }
      
      sprintf(output, "<- @%05lx  Data %02x %02x", requestRowAddress, dataH, dataL);
      Serial.println(output);

      wordsLeft --;

      // Next word
      requestRowAddress ++;
      
      if( wordsLeft > 0 ) {
        rsmState = CMD_SET_ADDRESS;
      } else {
        rsmState = CMD_RETURN_TO_IDLE;
      }
      break;
    }

    case CMD_RETURN_TO_IDLE:
      Serial.println("-> CMD_RETURN_TO_IDLE");
      request = CMD_IDLE;
      requestReadData  = 0;
      requestWriteData = 0;  
      requestAddress   = 0;  

      digitalWrite(CE1_NOT, HIGH);
      digitalWrite(CE2_NOT, HIGH);
      digitalWrite(WE1_NOT, HIGH);
      digitalWrite(WE2_NOT, HIGH);
      digitalWrite(OE1_NOT, HIGH);
      digitalWrite(OE2_NOT, HIGH);
      
      rsmState         = CMD_IDLE;
      break;
  }
}

//--------------------------------------------------------------------------------
// loop
//--------------------------------------------------------------------------------
void loop() {
  if( rsmState == CMD_IDLE ) {
    switch( requestCount ) {
      case 0: 
        requestAddress   = 0;
        requestWriteData = 0x1234;
        //requestDataLen   = 307200;  // bytes
        //requestDataLen   = 153600;  // WORDs
        requestDataLen   = 1;  // WORDs
        request = CMD_WRITE;
        break;

      case 1: 
        requestAddress   = 0;
        requestDataLen   = 1;
        request = CMD_READ;
        break;

      case 2:
        requestAddress   = 0;
        request = CMD_READ;
        break;

      case 3:
        requestAddress   = 2;
        request = CMD_READ;
        break;

      case 4:
        requestAddress   = 0;
        request = CMD_READ;
        break;

      case 5:
        done = 1;
        requestCount ++;
        Serial.println("DONE");  
        break;

      default:  
        break;
    }

    if( ! done )
      requestCount ++;
  }

  if( ! done )
    handleRSM();
  
  //digitalWrite(CE1_NOT, LOW);

  //delay( 1000 );
  delay( 100 );
  count ++;
}

You know that the Mega has a memory bus interface?

See the r/w diagrams for proper timing. Writing can be controlled by CE or WE and the controlling signal has to be activated last and reverted first.

@ DrDiettrich has a great tip.
Else I would ask You for a wiring diagram, controller - memories.

Thanks guys. I ultimately want to get this idea working with my FPGA - so this is why I'm wiring up the Arduino in the same manner (@DrDiettrich thanks for the suggestion).

I'll draw a diagram by hand and upload - pretty simple really.

Here's a diagram - I hope this helps.

With a 16 bit data bus the chips can share all but the data lines.
With an 8 bit data bus only the CE has to be separate for each chip.

Thanks @DrDiettrich . In my FPGA project, I tried sharing the WE, OE and CE lines. When I began to have problems, I thought I had better keep the control lines separate to try and figure out what's going on.

My FPGA project uses the 2 chips for a VGA framebuffer. It was seeing data being duplicated at address 0x0140 and 0x4140 (suggesting a problem with address line A14). Anyway, I couldn't figure out what was going wrong with the FPGA project and Quartus takes about 4-5mins per compile.. so I took 2 more identical chips and wired up with the Arduino to try and troubleshoot my memory access in general.. However, I didn't think I would have problems getting the Arduino to access the memory.

Why do you use a state machine? It obfuscates the sequencing to some degree. I'd try direct sequential access to the control lines first, until the sequences are found correct.

It was just to try and follow what I'd done in the FPGA design which is typically state machine driven. I realise Arduino is way slower than the 50 Mhz FPGA.

I could get rid of the state machine for now and create another project. I thought it was working last week. I wanted to try writing/clearing blocks of sequential memory this week and that's when I noticed things were not working as expected.

That's correct but how do you define the states? I'd fix the sequences and then break them down into sections (states) where timing constraints exist. In general memory transfers include Preset, Execute and Hold states with related timing.

@DrDiettrich I tried the simpler sketch below. This is working OK. I don't understand why each line on the serial monitor takes about 1 second to appear though.

Now the question is: why didn't my state machine work?

//                     A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  A10  A11  A12  A13  A14  A15  A16  A17  A18
const char ADDR[]  = { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,  33,  34,  35,  36,  37,  38,  39,  40};
const char DATAH[] = { 41, 42, 43, 44, 45, 46, 47, 48 };

#define WE1_NOT 6
#define OE1_NOT 7
#define CE1_NOT 8

#define WE2_NOT 9
#define OE2_NOT 10
#define CE2_NOT 11


const char DATAL[] = { 49, 50, 51, 52, 53,  2,  3,  4 };

void setDataMode( int mode ) {
  for( int n = 0; n < 8; n ++ ) {
    pinMode( DATAH[n], mode );
    pinMode( DATAL[n], mode );
  }
}

unsigned long requestAddress    = 0;

char output[100];

//--------------------------------------------------------------------------------
// getBit
//--------------------------------------------------------------------------------
char getBit( int bit, unsigned long value ) {
  switch( bit ) {
    case  0: return ( value & 0x0001 ) ? HIGH : LOW;
    case  1: return ( value & 0x0002 ) ? HIGH : LOW;
    case  2: return ( value & 0x0004 ) ? HIGH : LOW;
    case  3: return ( value & 0x0008 ) ? HIGH : LOW;
    case  4: return ( value & 0x0010 ) ? HIGH : LOW;
    case  5: return ( value & 0x0020 ) ? HIGH : LOW;
    case  6: return ( value & 0x0040 ) ? HIGH : LOW;
    case  7: return ( value & 0x0080 ) ? HIGH : LOW;
    case  8: return ( value & 0x0100 ) ? HIGH : LOW;
    case  9: return ( value & 0x0200 ) ? HIGH : LOW;
    case 10: return ( value & 0x0400 ) ? HIGH : LOW;
    case 11: return ( value & 0x0800 ) ? HIGH : LOW;
    case 12: return ( value & 0x1000 ) ? HIGH : LOW;
    case 13: return ( value & 0x2000 ) ? HIGH : LOW;
    case 14: return ( value & 0x4000 ) ? HIGH : LOW;    
    case 15: return ( value & 0x8000 ) ? HIGH : LOW;
  }
  return LOW;
}

//--------------------------------------------------------------------------------
// setAddressPins
//--------------------------------------------------------------------------------
void setAddressPins( unsigned long address ) {
  requestAddress = address;

  // Page 6 "WE# and CE# must be high during all address transitions"
  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);
  digitalWrite(WE1_NOT, HIGH);
  digitalWrite(WE2_NOT, HIGH);

  delay( 50 );
  
  // A0 thru A18
  for( int n = 0; n < 19; n ++ ) {
    digitalWrite(ADDR[n], getBit( n, address ) );
    
  delay( 50 );    
  
  digitalWrite(CE1_NOT, LOW);
  digitalWrite(CE2_NOT, LOW);
}
}

//--------------------------------------------------------------------------------
// writeData
//--------------------------------------------------------------------------------
void writeData( unsigned long address, unsigned int data ) {

  setAddressPins( address );

  unsigned int dataL = (data & 0xFF);

  // D0 thru D7
  for( int n = 0; n < 8; n ++ ) {
    digitalWrite(DATAL[n], getBit( n, dataL ) );
  }

  unsigned int dataH = (data & 0xFF00) >> 8;

  setDataMode(OUTPUT);  
  
  // D8 thru D15
  for( int n = 0; n < 8; n ++ ) {
    digitalWrite(DATAH[n], getBit( n, dataH ) );
  }
  
  sprintf(output, "WRITE @%05lx -> Data %02x %02x", requestAddress, dataH, dataL);
  Serial.println(output);

  digitalWrite(WE1_NOT, LOW);
  digitalWrite(WE2_NOT, LOW);

  //delay( 50 );
  
  digitalWrite(WE1_NOT, HIGH);
  digitalWrite(WE2_NOT, HIGH);
}

//--------------------------------------------------------------------------------
// readData
//--------------------------------------------------------------------------------
void readData( unsigned long address ) {
  
    setAddressPins( address );
    
    unsigned int dataH = 0;
    unsigned int dataL = 0;
    int bit;

    setDataMode(INPUT);    

    for( int n = 7; n >= 0; n -- ) {
      
      bit = digitalRead(DATAH[n]);
      dataH = (dataH << 1 ) + bit;
  
      bit = digitalRead(DATAL[n]);
      dataL = (dataL << 1 ) + bit;
    }
    
    sprintf(output, "READ  @%05lx -> Data %02x %02x", requestAddress, dataH, dataL);
    Serial.println(output);

    //delay( 50 );
}

//--------------------------------------------------------------------------------
// setup
//--------------------------------------------------------------------------------
void setup() {
  Serial.begin(115200);
  Serial.println("================= STARTED =================");
  
  for( int n = 0; n < 16; n ++ ) {
    pinMode( ADDR[n], OUTPUT );
  }
}

int runOnce = 1;

//--------------------------------------------------------------------------------
// loop
//--------------------------------------------------------------------------------
void loop(){

  if( runOnce ) {
    pinMode( CE1_NOT, OUTPUT );
    pinMode( CE2_NOT, OUTPUT );
    pinMode( OE1_NOT, OUTPUT );
    pinMode( OE2_NOT, OUTPUT );
    pinMode( WE1_NOT, OUTPUT );
    pinMode( WE2_NOT, OUTPUT );
  
    // Truth Table in data sheet suggests these don't matter for read/write..
    digitalWrite(CE1_NOT, LOW);
    digitalWrite(CE2_NOT, LOW);
    digitalWrite(OE1_NOT, LOW);
    digitalWrite(OE2_NOT, LOW);
    digitalWrite(WE1_NOT, HIGH);
    digitalWrite(WE2_NOT, HIGH);
  
    writeData( 0, 0x1234 );
  
    writeData( 1, 0x5678 );
  
    readData( 0 );
  
    readData( 1 );
    
    readData( 0 );
    
  }
  runOnce = 0;
}

OK, so I just spotted a silly bug in the above sketch. setAddressPin() was wasting a lot of time and I was getting expected read/write results.

Now that I have fixed it as below:

void setAddressPins( unsigned long address ) {
  requestAddress = address;

  // Page 6 "WE# and CE# must be high during all address transitions"
  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);
  digitalWrite(WE1_NOT, HIGH);
  digitalWrite(WE2_NOT, HIGH);

  delay( 50 );
  
  // A0 thru A18
  for( int n = 0; n < 19; n ++ ) {
    digitalWrite(ADDR[n], getBit( n, address ) );
  }   
  delay( 50 );    
  
  digitalWrite(CE1_NOT, LOW);
  digitalWrite(CE2_NOT, LOW);
}

..I get problems reading and writing. In particular, read of address 0 returns 50 18 instead of 12 34 which is exactly what my state machine version is doing - so I must be doing something too fast.

================= STARTED =================
WRITE @00000 -> Data 12 34
WRITE @00001 -> Data 56 78
READ  @00000 -> Data 50 18
READ  @00001 -> Data 56 78
READ  @00000 -> Data 50 18

So I wonder what happens if I increase the delay after setting the address pins..

Sure enough, if I add a 1 second delay as shown below, it works:

  // Page 6 "WE# and CE# must be high during all address transitions"
  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);
  digitalWrite(WE1_NOT, HIGH);
  digitalWrite(WE2_NOT, HIGH);

  delay( 50 );
  
  // A0 thru A18
  for( int n = 0; n < 19; n ++ ) {
    digitalWrite(ADDR[n], getBit( n, address ) );
  }   
  delay( 1000 );    // 1 second delay
  
  digitalWrite(CE1_NOT, LOW);
  digitalWrite(CE2_NOT, LOW);

After a bit more fiddling around, it seems I need a delay between setting the address and doing the read:

void readData( unsigned long address ) {
  
    setAddressPins( address );
    delay( 1000 );                   // NEED THIS??
    unsigned int dataH = 0;
    unsigned int dataL = 0;
    int bit;

    setDataMode(INPUT);    

    for( int n = 7; n >= 0; n -- ) {
      
      bit = digitalRead(DATAH[n]);
      dataH = (dataH << 1 ) + bit;
  
      bit = digitalRead(DATAL[n]);
      dataL = (dataL << 1 ) + bit;
    }
    
    sprintf(output, "READ  @%05lx -> Data %02x %02x", requestAddress, dataH, dataL);
    Serial.println(output);

I don't get any of this at all. The AC Electrical Characteristics list all of the minimum times in term of nanoseconds. I cannot see what's going wrong - the Arduino timings should be massive compared with what the chips need as minimum.

I also don't get why there is a "Read Cycle 1" , "Read Cycle 2", "Write Cycle 1" and "Write Cycle 2" in the datasheet. Does this mean that every second read or write must follow the sequence in Cycle 2? I thought it was listing options of whether it is WE# or CE# controlled (for write) as a choice.

Does this mean multiple writes are supposed to be done like this:

Write Cycle 1
Write Cycle 2
Write Cycle 2
...

and multiple reads like this:

Read Cycle 1
Read Cycle 2
Read Cycle 2
...

There is always missing information in these datasheets with a lot of assumptions being made. Goodness knows how I managed to get an SDRAM controller working. I was expecting SRAM to be easier :slight_smile:

I wonder where you activate OE before reading data?
Oops, Read Cycle 1 (continuous reads) is misleading! Don't ignore notes (1,2)!

Microprocessors typically apply:

  • set address
  • set data if Write
  • set CE (from external memory bank decoder)
  • wait for address decoded (tAS)
  • OE if Read, WE if Write
  • wait for r/w cycle complete
  • read data if Read
  • reset OE/WE+data
  • reset CE

On Read the data is valid after max. of

  • address valid + tAA
  • CE active + tACE
  • OE active + tOE

On Write the data must be valid and stable at least

  • tDW before reset WE or CE
  • tDH after reset WE or CE
  • address remains stable for tWR

@DrDiettrich thanks. I probably won't get a chance to try this until 3-4 days time but I'll take a closer look with what you have said.

@DrDiettrich tAS is defined as 0 for my chip and tAA=55ns.

Well.. there was a really stupid bug in the last sketch that I uploaded. I was printing the values of dataH and dataL before actually reading them.

Tiredness results in stupid mistakes such as this.

So, the below sketch is now working as expected. I'll now be interested to see why the state machine version didn't work.

//                     A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  A10  A11  A12  A13  A14  A15  A16  A17  A18
const char ADDR[]  = { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,  33,  34,  35,  36,  37,  38,  39,  40};
const char DATAH[] = { 41, 42, 43, 44, 45, 46, 47, 48 };

#define WE1_NOT 6
#define OE1_NOT 7
#define CE1_NOT 8

#define WE2_NOT 9
#define OE2_NOT 10
#define CE2_NOT 11


const char DATAL[] = { 49, 50, 51, 52, 53,  2,  3,  4 };

void setDataMode( int mode ) {
  for( int n = 0; n < 8; n ++ ) {
    pinMode( DATAH[n], mode );
    pinMode( DATAL[n], mode );
  }
}

unsigned long requestAddress    = 0;

char output[100];

//--------------------------------------------------------------------------------
// getBit
//--------------------------------------------------------------------------------
char getBit( int bit, unsigned long value ) {
  switch( bit ) {
    case  0: return ( value & 0x0001 ) ? HIGH : LOW;
    case  1: return ( value & 0x0002 ) ? HIGH : LOW;
    case  2: return ( value & 0x0004 ) ? HIGH : LOW;
    case  3: return ( value & 0x0008 ) ? HIGH : LOW;
    case  4: return ( value & 0x0010 ) ? HIGH : LOW;
    case  5: return ( value & 0x0020 ) ? HIGH : LOW;
    case  6: return ( value & 0x0040 ) ? HIGH : LOW;
    case  7: return ( value & 0x0080 ) ? HIGH : LOW;
    case  8: return ( value & 0x0100 ) ? HIGH : LOW;
    case  9: return ( value & 0x0200 ) ? HIGH : LOW;
    case 10: return ( value & 0x0400 ) ? HIGH : LOW;
    case 11: return ( value & 0x0800 ) ? HIGH : LOW;
    case 12: return ( value & 0x1000 ) ? HIGH : LOW;
    case 13: return ( value & 0x2000 ) ? HIGH : LOW;
    case 14: return ( value & 0x4000 ) ? HIGH : LOW;    
    case 15: return ( value & 0x8000 ) ? HIGH : LOW;
  }
  return LOW;
}

//--------------------------------------------------------------------------------
// setAddressPins
//--------------------------------------------------------------------------------
void setAddressPins( unsigned long address ) {
  requestAddress = address;

  // A0 thru A18
  for( int n = 0; n < 19; n ++ ) {
    digitalWrite(ADDR[n], getBit( n, address ) );
  }   
  //delay( 1000 );    
  
 //delay( 1000 );    
}

//--------------------------------------------------------------------------------
// writeData
//--------------------------------------------------------------------------------
void writeData( unsigned long address, unsigned int data ) {

  digitalWrite(OE1_NOT, HIGH);
  digitalWrite(OE2_NOT, HIGH);

  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);

  digitalWrite(WE1_NOT, HIGH);
  digitalWrite(WE2_NOT, HIGH);

  setAddressPins( address );

  unsigned int dataL = (data & 0xFF);

  setDataMode(OUTPUT);

  // D0 thru D7
  for( int n = 0; n < 8; n ++ ) {
    digitalWrite(DATAL[n], getBit( n, dataL ) );
  }

  unsigned int dataH = (data & 0xFF00) >> 8;

  // D8 thru D15
  for( int n = 0; n < 8; n ++ ) {
    digitalWrite(DATAH[n], getBit( n, dataH ) );
  }

  digitalWrite(CE1_NOT, LOW);
  digitalWrite(CE2_NOT, LOW);

  delay( 10 );

  digitalWrite(WE1_NOT, LOW);
  digitalWrite(WE2_NOT, LOW);

  delay( 10 );
  
  sprintf(output, "WRITE @%05lx -> Data %02x %02x", requestAddress, dataH, dataL);
  Serial.println(output);

  digitalWrite(WE1_NOT, HIGH);
  digitalWrite(WE2_NOT, HIGH);

  delay( 10 );
  
  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);

  setDataMode(INPUT);

}

//--------------------------------------------------------------------------------
// readData
//--------------------------------------------------------------------------------
void readData( unsigned long address ) {

  setDataMode(INPUT);    

  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);

  digitalWrite(WE1_NOT, HIGH);
  digitalWrite(WE2_NOT, HIGH);

  digitalWrite(OE1_NOT, HIGH);
  digitalWrite(OE2_NOT, HIGH);
  
  setAddressPins( address );
  //delay( 100 );                   // NEED THIS??
  delay( 1000 );                   // NEED THIS??
  unsigned int dataH = 0;
  unsigned int dataL = 0;
  int bit;

  digitalWrite(CE1_NOT, LOW);
  digitalWrite(CE2_NOT, LOW);

  digitalWrite(OE1_NOT, LOW);
  digitalWrite(OE2_NOT, LOW);
 
  delay( 50 );

  for( int n = 7; n >= 0; n -- ) {
    
    bit = digitalRead(DATAH[n]);
    dataH = (dataH << 1 ) + bit;

    bit = digitalRead(DATAL[n]);
    dataL = (dataL << 1 ) + bit;
  }

  sprintf(output, "READ  @%05lx -> Data %02x %02x", requestAddress, dataH, dataL);
  Serial.println(output);

  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);
}

//--------------------------------------------------------------------------------
// setup
//--------------------------------------------------------------------------------
void setup() {
  Serial.begin(115200);
  Serial.println("================= STARTED =================");
  
  for( int n = 0; n < 16; n ++ ) {
    pinMode( ADDR[n], OUTPUT );
  }
}

int runOnce = 1;

//--------------------------------------------------------------------------------
// loop
//--------------------------------------------------------------------------------
void loop(){

  if( runOnce ) {
    pinMode( CE1_NOT, OUTPUT );
    pinMode( CE2_NOT, OUTPUT );
    pinMode( OE1_NOT, OUTPUT );
    pinMode( OE2_NOT, OUTPUT );
    pinMode( WE1_NOT, OUTPUT );
    pinMode( WE2_NOT, OUTPUT );
  
    // Truth Table in data sheet suggests these don't matter for read/write..
    digitalWrite(CE1_NOT, LOW);
    digitalWrite(CE2_NOT, LOW);
    digitalWrite(OE1_NOT, LOW);
    digitalWrite(OE2_NOT, LOW);
    digitalWrite(WE1_NOT, HIGH);
    digitalWrite(WE2_NOT, HIGH);
  
    writeData( 0, 0x1234 );
  
    writeData( 1, 0x5678 );
  
    readData( 0 );
  
    readData( 1 );
    
    readData( 0 );
    
  }
  runOnce = 0;
}

So yes, the above sketch works BUT only if it has 1 second delay after setting the address pins!

void readData( unsigned long address ) {

  setDataMode(INPUT);    

  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);

  digitalWrite(WE1_NOT, HIGH);
  digitalWrite(WE2_NOT, HIGH);

  digitalWrite(OE1_NOT, HIGH);
  digitalWrite(OE2_NOT, HIGH);
  
  setAddressPins( address );

  delay( 1000 );                   // WHY DO I NEED THIS??
  unsigned int dataH = 0;
  unsigned int dataL = 0;
  int bit;

  digitalWrite(CE1_NOT, LOW);
  digitalWrite(CE2_NOT, LOW);

  digitalWrite(OE1_NOT, LOW);
  digitalWrite(OE2_NOT, LOW);
 
  delay( 50 );

  for( int n = 7; n >= 0; n -- ) {
    
    bit = digitalRead(DATAH[n]);
    dataH = (dataH << 1 ) + bit;

    bit = digitalRead(DATAL[n]);
    dataL = (dataL << 1 ) + bit;
  }

  sprintf(output, "READ  @%05lx -> Data %02x %02x", requestAddress, dataH, dataL);
  Serial.println(output);

  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);
}

If I drop the delay() down to delay( 10 ), it doesn't work. Why on earth do I need a massive 1 second delay??

One second delay output:

================= STARTED =================
WRITE @00000 -> Data 12 34
WRITE @00001 -> Data 56 78
READ  @00000 -> Data 12 34
READ  @00001 -> Data 56 78
READ  @00000 -> Data 12 34

10ms delay output:

================= STARTED =================
WRITE @00000 -> Data 12 34
WRITE @00001 -> Data 56 78
READ  @00000 -> Data 00 00
READ  @00001 -> Data 56 78
READ  @00000 -> Data 00 00

1 second is an eternity between setting the address pins and then change the /CE and /OE signals for the read.

Your getBit() is defined only for 16 bit! You also set only 16 address pins for output.

1 Like

@DrDiettrich Thank you! Tired brain here not checking the obvious and looking for the harder problems!

Yay! The state machine version is working now. Thank you so much! :slight_smile:

Now I can try and investigate more why my FPGA solution (doesn't) work as it should by doing something similar on the Arduino.

Here's the working sketch if it helps anybody in the future:

//                     A0  A1  A2  A3  A4  A5  A6  A7  A8  A9  A10  A11  A12  A13  A14  A15  A16  A17  A18
const char ADDR[]  = { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,  33,  34,  35,  36,  37,  38,  39,  40};
const char DATAH[] = { 41, 42, 43, 44, 45, 46, 47, 48 };

#define WE1_NOT 6
#define OE1_NOT 7
#define CE1_NOT 8

#define WE2_NOT 9
#define OE2_NOT 10
#define CE2_NOT 11


const char DATAL[] = { 49, 50, 51, 52, 53,  2,  3,  4 };


void setup() {
  // put your setup code here, to run once:
  for( int n = 0; n < 19; n ++ ) {
    pinMode( ADDR[n], OUTPUT );
  }

  pinMode( CE1_NOT, OUTPUT );
  pinMode( CE2_NOT, OUTPUT );
  pinMode( OE1_NOT, OUTPUT );
  pinMode( OE2_NOT, OUTPUT );
  pinMode( WE1_NOT, OUTPUT );
  pinMode( WE2_NOT, OUTPUT );

  setDataMode(INPUT);
  
  //attachInterrupt(digitalPinToInterrupt(CLOCK), onClock, RISING);
  Serial.begin(115200);
}

void setDataMode( int mode ) {
  for( int n = 0; n < 8; n ++ ) {
    pinMode( DATAH[n], mode );
    pinMode( DATAL[n], mode );
  }
}

int count = 0;

enum CMD_STATE {
  CMD_INIT,
  CMD_IDLE,
  CMD_SET_ADDRESS,
  CMD_POST_SET_ADDRESS,
  CMD_WRITE,
  CMD_POST_WRITE,
  CMD_POST_READ,

  CMD_READ,
  CMD_RETURN_TO_IDLE,
};

CMD_STATE rsmState = CMD_INIT;
CMD_STATE request  = CMD_IDLE;
unsigned int  requestWriteData  = 0;
unsigned int  requestReadData   = 0;
unsigned long requestDataLen    = 0;
unsigned long requestAddress    = 0;
unsigned long requestRowAddress = 0;

unsigned long wordsLeft         = 0;
char output[100];
int  requestCount = 0;
char done = 0;

//--------------------------------------------------------------------------------
// getBit
//--------------------------------------------------------------------------------
char getBit( int bit, unsigned long value ) {
  switch( bit ) {
    case  0: return ( value & 0x00001 ) ? HIGH : LOW;
    case  1: return ( value & 0x00002 ) ? HIGH : LOW;
    case  2: return ( value & 0x00004 ) ? HIGH : LOW;
    case  3: return ( value & 0x00008 ) ? HIGH : LOW;
    case  4: return ( value & 0x00010 ) ? HIGH : LOW;
    case  5: return ( value & 0x00020 ) ? HIGH : LOW;
    case  6: return ( value & 0x00040 ) ? HIGH : LOW;
    case  7: return ( value & 0x00080 ) ? HIGH : LOW;
    case  8: return ( value & 0x00100 ) ? HIGH : LOW;
    case  9: return ( value & 0x00200 ) ? HIGH : LOW;
    case 10: return ( value & 0x00400 ) ? HIGH : LOW;
    case 11: return ( value & 0x00800 ) ? HIGH : LOW;
    case 12: return ( value & 0x01000 ) ? HIGH : LOW;
    case 13: return ( value & 0x02000 ) ? HIGH : LOW;
    case 14: return ( value & 0x04000 ) ? HIGH : LOW;    
    case 15: return ( value & 0x08000 ) ? HIGH : LOW;
    case 16: return ( value & 0x10000 ) ? HIGH : LOW;
    case 17: return ( value & 0x20000 ) ? HIGH : LOW;
    case 18: return ( value & 0x40000 ) ? HIGH : LOW;
  }
  return LOW;
}

//--------------------------------------------------------------------------------
// setAddressPins
//--------------------------------------------------------------------------------
void setAddressPins( unsigned long address ) {

  // A0 thru A18
  for( int n = 0; n < 19; n ++ ) {
    digitalWrite(ADDR[n], getBit( n, address ) );
  }
}

//--------------------------------------------------------------------------------
// writeData
//--------------------------------------------------------------------------------
void writeData( unsigned int data ) {

  unsigned int dataL = (data & 0xFF);
  
  setDataMode(OUTPUT);  

  // D0 thru D7
  for( int n = 0; n < 8; n ++ ) {
    digitalWrite(DATAL[n], getBit( n, dataL ) );
  }

  unsigned int dataH = (data & 0xFF00) >> 8;
  
  // D8 thru D15
  for( int n = 0; n < 8; n ++ ) {
    digitalWrite(DATAH[n], getBit( n, dataH ) );
  }

  digitalWrite(CE1_NOT, LOW);
  digitalWrite(CE2_NOT, LOW);

  digitalWrite(WE1_NOT, LOW);
  digitalWrite(WE2_NOT, LOW);

/*
  digitalWrite(WE1_NOT, HIGH);
  digitalWrite(WE2_NOT, HIGH);

  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);

  setDataMode(INPUT);  
*/
  sprintf(output, "WRITE @%05lx -> Data %02x %02x", requestAddress, dataH, dataL);
  Serial.println(output);
  
}

//--------------------------------------------------------------------------------
// readData
//--------------------------------------------------------------------------------
void readData( unsigned long address ) {

  setDataMode(INPUT);    

  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);

  digitalWrite(WE1_NOT, HIGH);
  digitalWrite(WE2_NOT, HIGH);

  digitalWrite(OE1_NOT, HIGH);
  digitalWrite(OE2_NOT, HIGH);
  
  setAddressPins( address );

  unsigned int dataH = 0;
  unsigned int dataL = 0;
  int bit;

  digitalWrite(CE1_NOT, LOW);
  digitalWrite(CE2_NOT, LOW);

  digitalWrite(OE1_NOT, LOW);
  digitalWrite(OE2_NOT, LOW);
 
  for( int n = 7; n >= 0; n -- ) {
    
    bit = digitalRead(DATAH[n]);
    dataH = (dataH << 1 ) + bit;

    bit = digitalRead(DATAL[n]);
    dataL = (dataL << 1 ) + bit;
  }

  sprintf(output, "READ  @%05lx -> Data %02x %02x", requestAddress, dataH, dataL);
  Serial.println(output);

  digitalWrite(CE1_NOT, HIGH);
  digitalWrite(CE2_NOT, HIGH);
}

//--------------------------------------------------------------------------------
// handleRSM
//--------------------------------------------------------------------------------
void handleRSM(){
  switch(rsmState){
    case CMD_INIT:

      // Truth Table in data sheet suggests these don't matter for read/write..
      digitalWrite(CE1_NOT, LOW);
      digitalWrite(CE2_NOT, LOW);
      digitalWrite(OE1_NOT, LOW);
      digitalWrite(OE2_NOT, LOW);
      digitalWrite(WE1_NOT, HIGH);
      digitalWrite(WE2_NOT, HIGH);
    
      rsmState = CMD_RETURN_TO_IDLE;    
      break;
      
    case CMD_IDLE:
      // Convert actual memory address to rowAddress (rowAddress=0, two bytes)
      requestRowAddress = requestAddress >> 1;
    
      if( request == CMD_WRITE ) {
        sprintf(output, "WRITE ROW: %05lx  Addr: %05lx  Data: %04x  Repeat: %ld", requestRowAddress, requestAddress, requestWriteData, requestDataLen);
        Serial.println(output);
        if( requestDataLen > 1 )
          wordsLeft = requestDataLen;
        else 
          wordsLeft = 1;

        rsmState = CMD_SET_ADDRESS;
      }
      else      
      if( request == CMD_READ ) {
        sprintf(output, "READ  ROW: %05lx  Addr: %05lx  Repeat: %ld", requestRowAddress, requestAddress, requestDataLen);
        Serial.println(output);

        setDataMode(INPUT);

        wordsLeft = requestDataLen;
        rsmState = CMD_SET_ADDRESS;
      }
      break;

    case CMD_SET_ADDRESS:
      setAddressPins( requestRowAddress );

      // Choose read or write..
      rsmState = request;
      break;

      
    case CMD_WRITE:
      writeData( requestWriteData );
     
      wordsLeft --;
      
      if( wordsLeft > 0 ) {
        rsmState = CMD_SET_ADDRESS;
      } else {
        rsmState = CMD_RETURN_TO_IDLE;
      }
      break;
    
    
    case CMD_READ:
    {
      unsigned int dataH = 0;
      unsigned int dataL = 0;
      int bit;

      digitalWrite(CE1_NOT, LOW);
      digitalWrite(CE2_NOT, LOW);

      digitalWrite(OE1_NOT, LOW);
      digitalWrite(OE2_NOT, LOW);
       
      for( int n = 7; n >= 0; n -- ) {
        
        bit = digitalRead(DATAH[n]);
        dataH = (dataH << 1 ) + bit;
    
        bit = digitalRead(DATAL[n]);
        dataL = (dataL << 1 ) + bit;
      }

      digitalWrite(CE1_NOT, HIGH);
      digitalWrite(CE2_NOT, HIGH);
      
      sprintf(output, "<- @%05lx  Data %02x %02x", requestRowAddress, dataH, dataL);
      Serial.println(output);

      wordsLeft --;

      // Next word
      requestRowAddress ++;
      
      if( wordsLeft > 0 ) {
        rsmState = CMD_SET_ADDRESS;
      } else {
        rsmState = CMD_RETURN_TO_IDLE;
      }
      break;
    }

    case CMD_RETURN_TO_IDLE:
      request = CMD_IDLE;
      requestReadData  = 0;
      requestWriteData = 0;  
      requestAddress   = 0;  

      digitalWrite(WE1_NOT, HIGH);
      digitalWrite(WE2_NOT, HIGH);
    
      digitalWrite(CE1_NOT, HIGH);
      digitalWrite(CE2_NOT, HIGH);

      digitalWrite(OE1_NOT, HIGH);
      digitalWrite(OE2_NOT, HIGH);
    
      setDataMode(INPUT);       
      
      rsmState         = CMD_IDLE;
      break;
  }
}

//--------------------------------------------------------------------------------
// loop
//--------------------------------------------------------------------------------
void loop() {
  if( rsmState == CMD_IDLE ) {
    switch( requestCount ) {
      case 0: 
        requestAddress   = 0;
        requestWriteData = 0x1234;
        requestDataLen   = 1;  // WORDs
        request = CMD_WRITE;
        break;

      case 1: 
        requestAddress   = 2;
        requestWriteData = 0x5678;
        requestDataLen   = 1;
        request = CMD_WRITE;
        break;

      case 2:
        requestAddress   = 0;
        request = CMD_READ;
        break;

      case 3:
        requestAddress   = 2;
        request = CMD_READ;
        break;

      case 4:
        requestAddress   = 0;
        request = CMD_READ;
        break;

      case 5:
        requestAddress   = 2;
        request = CMD_READ;
        break;

      case 6:
        done = 1;
        requestCount ++;
        Serial.println("DONE");  
        break;

      default:  
        break;
    }

    if( ! done )
      requestCount ++;
  }

  if( ! done )
    handleRSM();

  //delay( 100 );
  count ++;
}