Arduino UNO の フラッシュメモリ を増設

#はじめに

　以前、FPGAを試すためにCycloneIIの試験ボードを購入しましたが、その際に、コンフィグ用のデバイスが必要という記事をみたので、併せてEPCS4チップを購入しました。
しかし、到着した試験ボードには、既にEPCS4が装着してあり、購入したEPCS4は不要となってしまいました。
　ただせっかく、購入したので、なにか使い道はないかと思案し、Arduinoの増設フラッシュメモリとして使えないかと思い立ち試してみました。

#具体的に

　実際に、試した内容について記述します。なお、EPCS4の詳細については、ドキュメントを参照してください。
　
　参考：https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/hb/cfg/cyc_c51014.pdf

##接続

　EPCS4の動作電圧は、3.3Vなので Arduino UNO と接続する際には、レベルコンバータが必要になります。

```
(Arduino UNO)  (Level Converter)   (EPCS4)
       2 ------------> | | -------> 1.nCS
       3 <------------ | | <------- 2.DATA
       4 ------------> | | -------> 5.ASDI
       5  -----------> | | -------> 6.DCLK
```

##ソースコード

　EPCS4の制御は、ドキュメントにしたがって、それぞれのピンをHIGH、LOWにするだけです。
　このスケッチでは、シリアルモニタから指定されたコマンドに従って、EPCSのオペレーションコードと必要に応じた値を送っています。
　関数 initPin で、デジタルピンを初期化し、関数 rwEPCS の中で digitalWrite および diditalRead で、 EPCS とのインターフェースをとっています。
なお、データの入出力については、16バイトの固定データ（0x30 ～ 0x53）をアドレス 0x0 から読み書きしています。

```
//========================================
//        EPCS4 / read and write
//----------------------------------------

//----------------------------------------
//            EPCS operations
//----------------------------------------
#define   OPE_WRITE_ENABLE      B00000110
#define   OPE_WRITE_DISABLE     B00000100
#define   OPE_READ_STATUS       B00000101
#define   OPE_READ_BYTES        B00000011
#define   OPE_READ_SILICON_ID   B10101011
#define   OPE_FAST_READ         B00001011
#define   OPE_WRITE_STATUS      B00000001
#define   OPE_WRITE_BYTES       B00000010
#define   OPE_ERASE_BULK        B11000111
#define   OPE_ERASE_SECTOR      B11011000
#define   OPE_READ_DEVICE_ID    B10011111

#define   NUM_SECTOR            8         // for EPCS4

//----------------------------------------
//      PIN assign for Arduino UNO
//----------------------------------------
#define   PIN_nCS     2
#define   PIN_DATA    3
#define   PIN_ASDI    4
#define   PIN_DCLK    5

//----------------------------------------
//          constants
//----------------------------------------
#define   LEN_BUFF    36
#define   LEN_DATA    36
#define   LEN_TEMP    40

#define   ADDR_DATA   0                   // to read & write
#define   STT_LOCK    B00011100
#define   STT_UNLOCK  B00000000

//----------------------------------------
//            field
//----------------------------------------
byte    buff[LEN_BUFF];                   // serial read
byte    data[LEN_DATA];                   // read & write
byte    temp[LEN_TEMP];                   // EPCS

//----------------------------------------
//            initialize
//----------------------------------------
void  initPin() {

    pinMode     (PIN_DATA, INPUT);
    pinMode     (PIN_nCS,  OUTPUT);
    pinMode     (PIN_ASDI, OUTPUT);
    pinMode     (PIN_DCLK, OUTPUT);
    digitalWrite(PIN_nCS,  HIGH);
    digitalWrite(PIN_ASDI, HIGH);    
    digitalWrite(PIN_DCLK, HIGH);
}

//****************************************
//            EPCS functions
//----------------------------------------
//            read silicon ID
//----------------------------------------
byte  readSiliconID() {

    // set operation
    temp[0] = OPE_READ_SILICON_ID;
    setAddr(0);
    
    // read & write EPCS
    rwEPCS(4, 1);

    return  temp[0];
}

//----------------------------------------
//            write enable
//----------------------------------------
void  writeEnable() {

    // set operation
    temp[0] = OPE_WRITE_ENABLE;
    
    // read & write EPCS
    rwEPCS(1, 0);
}

//----------------------------------------
//            write disable
//----------------------------------------
void  writeDisable() {

    // set operation
    temp[0] = OPE_WRITE_DISABLE;
    
    // read & write EPCS
    rwEPCS(1, 0);
}

//----------------------------------------
//            read status
//----------------------------------------
byte  readStatus() {

    // set operation
    temp[0] = OPE_READ_STATUS;
    
    // read & write EPCS
    rwEPCS(4, 1);

    return  temp[0];
}

//----------------------------------------
//            write status
//----------------------------------------
void  writeStatus(byte stat) {

    // set operation & status
    temp[0] = OPE_WRITE_STATUS;
    temp[1] = stat;
    
    // read & write EPCS
    rwEPCS(2, 0);
}

//----------------------------------------
//          read data from EPCS
//----------------------------------------
void  readDataBytes(unsigned long addr) {

    // set operation
    temp[0] = OPE_READ_BYTES;
    setAddr(addr);
    
    // read & write EPCS
    rwEPCS(4, LEN_TEMP);
    
    // copy data
    for (int i = 0; i < LEN_DATA; i++) {
        data[i] = temp[i];
    }

    return data;
}

//----------------------------------------
//          write data to EPCS
//----------------------------------------
void  writeData(unsigned long addr) {
    int   n, i;

    // set operation
    temp[0] = OPE_WRITE_BYTES;
    setAddr(addr);

    // copy data
    n = 4;
    for (i = 0; i < LEN_DATA; i++)
        temp[n++] = data[i];
    
    // read & write EPCS
    rwEPCS(LEN_TEMP, 0);
}

//----------------------------------------
//            erase bulk
//----------------------------------------
void  eraseBulk() {

    // set operation
    temp[0] = OPE_ERASE_BULK;

    // read & write EPCS
    rwEPCS(1, 0);
}

//----------------------------------------
//            erase sector
//----------------------------------------
void  eraseSector(unsigned long sec) {

    // set operation
    temp[0] = OPE_ERASE_SECTOR;
    setAddr(sec);

    // read & write EPCS
    rwEPCS(4, 0);
}

//----------------------------------------
//        wait for end of process
//----------------------------------------
void  waitEOP() {
    byte    s;

    Serial.print("EOP:");
    // wait for eop
    do {
        s = readStatus();
        Serial.print(s, HEX);
        Serial.print(" ");
    } while (s & 1);
    Serial.println();
}

//----------------------------------------
//            read & write EPCS
//----------------------------------------
void  rwEPCS(uint8_t numSnd, uint8_t numRcv) {
    int     n, i;
    byte    d;

    digitalWrite(PIN_nCS, LOW);

    //++++++++++++++++++++++++++++++++++++++++
    //            send byte
    //----------------------------------------
    if (numSnd > 0) {
        for (n = 0; n < numSnd; n++) {
            d = temp[n];
            for (i = 0; i < 8; i++) {
                digitalWrite(PIN_DCLK, LOW);
                if (d & B10000000)
                    digitalWrite(PIN_ASDI, HIGH);
                else
                    digitalWrite(PIN_ASDI, LOW);
                d <<= 1;
                digitalWrite(PIN_DCLK, HIGH);
            }
        }
    }
    
    //++++++++++++++++++++++++++++++++++++++++
    //            receive byte
    //----------------------------------------
    if (numRcv > 0) {
        for (n = 0; n < numRcv; n++) {
            d = 0;
            for (i = 0; i < 8; i++) {
                digitalWrite(PIN_DCLK, LOW);
                d <<= 1;
                if (digitalRead(PIN_DATA))
                    d |= 1;
                digitalWrite(PIN_DCLK, HIGH);
            }
            temp[n] = d;
        }
    }
    digitalWrite(PIN_nCS, HIGH);
}

//****************************************
//              functions
//----------------------------------------
//      set address to asdi buffer
//----------------------------------------
void  setAddr(unsigned long adr) {
    temp[3] = adr & 0xff;
    adr >>= 8; 
    temp[2] = adr & 0xff;
    adr >>= 8;
    temp[1] = adr & 0xff;
}

```

```
//----------------------------------------
//      read data from serial
//----------------------------------------
void  readSerial() {
    int   i;
    byte  d;

    for (i = 0; i < LEN_BUFF - 1; ) {
        if (Serial.available()) {
            d = Serial.read();
            if (d == '\n')
                break;
            if (d > 0x1f)
                buff[i++] = d;
        }
    }
    buff[i] = '\0';

    i = 0;
    while (buff[i])
        Serial.println((char)buff[i++]);
}

//****************************************
//            procedure
//----------------------------------------
//            set up
//----------------------------------------
void  setup() {
    Serial.begin(9600);

    Serial.println("EPCS control test program");
    Serial.println("s:read silicon ID");
    Serial.println("d:write disable");
    Serial.println("e:write enable");
    Serial.println("t:read status");
    Serial.println("l:write lock status");
    Serial.println("u:write unlock status");
    Serial.println("r:read data");
    Serial.println("w:write data");
    Serial.println("b:erase bulk");
    Serial.println("c:erase sectors");
    Serial.println();
    
    initPin();
}

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

    // read command from serial
    readSerial();
    switch (buff[0]) {
        case 's':
        case 'S':
            procSiliconID();
            break;

        case 'd':
        case 'D':
            procWriteDisable();
            break;

        case 'e':
        case 'E':
            procWriteEnable();
            break;

        case 't':
        case 'T':
            procReadStatus();
            break;

        case 'l':
        case 'L':
            procLockStatus();
            break;
     
        case 'u':
        case 'U':
            procUnlockStatus();
            break;
     
        case 'r':
        case 'R':
            procReadData();
            break;

        case 'w':
        case 'W':
            procWriteData();
            break;

        case 'b':
        case 'B':
            procEraseBulk();
            break;
        
        case 'c':
        case 'C':
            procEraseSectors();
            break;
    }
    delay (10);
}

//----------------------------------------
//              silicon ID
//----------------------------------------
void  procSiliconID() {
    byte  d;
    
    // read silicon ID
    d = readSiliconID();

    // serial out
    Serial.println("silicon ID:");
    Serial.println(d, BIN);
}

//----------------------------------------
//              write enable
//----------------------------------------
void  procWriteEnable() {

    writeEnable();

    Serial.println("write enabled");
}

//----------------------------------------
//              write disable
//----------------------------------------
void  procWriteDisable() {

    writeDisable();

    Serial.println("write disabled");
}

//----------------------------------------
//              read status
//----------------------------------------
void  procReadStatus() {
    byte    s;

    // read data
    s = readStatus();

    // response to serial port
    Serial.print  ("read status:");
    Serial.println(s, HEX);
}

//----------------------------------------
//              lock status
//----------------------------------------
void  procLockStatus() {

    // write enable
    writeEnable();

    // write status
    writeStatus(STT_LOCK);

    // response to serial port
    Serial.println("writed status:");
    Serial.println(STT_LOCK, HEX);
}

//----------------------------------------
//              unlock status
//----------------------------------------
void  procUnlockStatus() {

    // write enable
    writeEnable();

    // write status
    writeStatus(STT_UNLOCK);

    // response to serial port
    Serial.println("writed status:");
    Serial.println(STT_UNLOCK, HEX);
}

//----------------------------------------
//              read data
//----------------------------------------
void  procReadData() {

    // read data
    readDataBytes(0x0);

    // response to serial port
    Serial.println("read data:");
    for (int i = 0; i < LEN_DATA; i++) {
        Serial.print  (data[i], HEX);
        Serial.print  (" "); 
    }
    Serial.println();
}

//----------------------------------------
//              write data
//----------------------------------------
void  procWriteData() {
    byte  s;
        
    // set data
    for (int i = 0; i < LEN_DATA; i++) {
        data[i] = 0x30 + i;
    }

    // write enable
    writeEnable();

    // write data
    writeData(0);

    // wait for eop
    waitEOP();

    Serial.println("writed data:");
    for (int i = 0; i < LEN_DATA; i++) {
        Serial.print  (data[i], HEX);
        Serial.print  (" "); 
    }
    Serial.println();
}

//----------------------------------------
//              erase bulk
//----------------------------------------
void  procEraseBulk() {

    // write enable
    writeEnable();

    // erase sector
    eraseBulk();
  
    // wait for eop
    waitEOP();

    Serial.println("erased bulk");
}

//----------------------------------------
//              erase sectors
//----------------------------------------
void  procEraseSectors() {
  
    // write enable
    writeEnable();

    // erase sector
    eraseSector(0);

    // wait for eop
    waitEOP();

    Serial.println("erased sector");
}
```

#終わりに

これにより、512Kバイトという膨大？なデータ領域を確保することができ、LEDのパターン領域の確保等に苦労することがなくなると思います。
（EPCS4しか試していないのですが、もっと容量の大きなEPCSを使えばさらに大容量のフラッシュ領域を確保することができると思います）
また、SDカードを利用することなく、計測したデータを保存する領域としても利用できます。