# Problems about using SPI

**URL:** <https://forums.sifive.com/t/problems-about-using-spi/442>\
**Category:** HiFive1 Rev B\
**Created:** [April 1, 2017, 8:01am UTC](https://forums.sifive.com/t/problems-about-using-spi/442 "2017-04-01T08:01:52Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![nonerkao](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.sifive.com/nonerkao/32/194_2.png) [@nonerkao](https://forums.sifive.com/u/nonerkao)\
**Post date:** [April 1, 2017, 8:01am UTC](https://forums.sifive.com/t/problems-about-using-spi/442/1 "2017-04-01T08:01:52Z")

</div>

Hi,

I am struggling making one hifive1 be the SPI master (MHF below), and the other one be the slave (SHF below).

After connecting both of them from pin 10 to 13 (SS, MOSI, MISO, SCK), I tried [this SPI example](http://www.gammon.com.au/forum/?id=10892). In the MHF part, no modification is required. The linking is automatically done by arduino IDE and the hifive1 package. In the SHF part, however, the scenario is very different from arduino family boards.

In practice, a SPI slave often works in a interrupt-driven model. This cause two difficulties when applying the example code to the SHF:

- SPI registers  
Arduino uses things like `SPDR` (data reg.) and `SPCR` (control reg.). I wonder if there was similar design in hifive1, so I dig into the related files but found no trivial mappings for this. Even if I try to replace `SPDR` by `SPI_REG_TXFIFO/SPI_REG_RXFIFO`, I have no idea how to reassemble `SPCR` and `SPSR` from all the `SPI_REG_XXX`s.
- ISR design  
The author use `ISR (SPI_STC_vect)` to declare the interrupt handler, and they don’t even need to do `attachInterrupt()` in `setup()`. But the `ISR` here is a AVR-specific macro. I did try to design a ISR for SHF, but the problem became: what interrupt number to attach? `INT_SPI1_BASE`? but it did not seem to work either.

Therefore for SHF, I currently have no idea how to utilize the hardware support part, so I implemented software-emulated SPI slave. (But of course I want to know the native way to do SPI on hifive1 later 😅 )

There are two ISRs,

- on the `CHANGING` of `SS`  
After the `FALLING` of `SS`, set an `ENABLE` flag, and disable it once `RISING` is observed.
- on the `RISING` of `SCK`  
The `SPI.begin()` shows that hifive1 boards do SPI under mode 0. So when `SCK` rises and `SS` is low (`ENABLE` flag is set), one bit of the data should be transmitted in `MOSI`. In theory I can read the bit and do the shift-and-accumulate on SHF.

I don’t know if I understood anything wrong, but surprisingly at least to me, this emulation did not work either. I always imagine the SPI communication process as the upper part of figure 76 in [this website](http://avrbeginners.net/architecture/spi/spi.html), so I expected when the MHF send a byte of data, there should be 8 rises on `SCK` pin. However, the ISR on `SCK` does not respond like that; rather, it activate only once as MHF do a `SPI.transfer()`.

So I don’t know where to go from here now. 😢

**Quick summary** : I am working on using hifive1 as an SPI slave. I try to find an easy mapping to work for an arduino example code but in vain. I then try to do it in a software-emulated way, but nothing works out either.

Sorry for the messy description above. If any of you need more information, please just ask me. Any help would be appreciated. Thanks!

---

<div class="post-metadata">

**Author:** ![aou](https://avatars.discourse-cdn.com/v4/letter/a/e99b99/32.png) [@aou](https://forums.sifive.com/u/aou)\
**Post date:** [April 3, 2017, 7:02pm UTC](https://forums.sifive.com/t/problems-about-using-spi/442/2 "2017-04-03T19:02:25Z")

</div>

The SPI controller on the HiFive1 FE310 supports master mode only. Chapter 13 of the [Freedom E300 Platform Reference Manual](https://dev.sifive.com/documentation/freedom-e300-platform-reference-manual/) describes the SPI registers.

> [@nonerkao](#):
>
> However, the ISR on SCK does not respond like that; rather, it activate only once as MHF do a SPI.transfer().

Could you post your code for emulating the slave functionality?

---

<div class="post-metadata">

**Author:** ![nonerkao](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.sifive.com/nonerkao/32/194_2.png) [@nonerkao](https://forums.sifive.com/u/nonerkao)\
**Post date:** [April 4, 2017, 6:49pm UTC](https://forums.sifive.com/t/problems-about-using-spi/442/3 "2017-04-04T18:49:07Z")

</div>

Thanks for the information!

I’ve read the manual and tried some modification but still not worked.

With the **master** code:

```auto
#include <SPI.h>
#include "encoding.h"

void setup (void)
{
  Serial.begin (115200);
  Serial.println ("[MASTER0]");

  digitalWrite(SS, HIGH);
  digitalWrite(SCK, LOW);
  
  pinMode(MISO, INPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(SS, OUTPUT);
  pinMode(SCK, OUTPUT);
  SPI.begin ();

  // Just try to add more delay here, but seems no effect
  SPI_REG(SPI_REG_SCKDIV) = 1023;
  SPI_REG(SPI_REG_DSCKCS) = 255;
  SPI_REG(SPI_REG_DCSSCK) = 255;
  SPI_REG(SPI_REG_DINTERCS) = 255;
  SPI_REG(SPI_REG_DINTERXFR) = 255;

} // end of setup

byte transferAndWait (const byte what)
{
  byte a = SPI.transfer (what);
  delayMicroseconds (20);
  return a;
} // end of transferAndWait

void loop (void)
{
  static int index = 0;
  digitalWrite(SS, LOW);
  delay (250);

  //send 1, 2, 4, 8, ... 128, 1, ... sequence, 
  //one number roughly per second
  byte ret = transferAndWait ((byte)(1 << index));  

  delay (250);
  digitalWrite(SS, HIGH);

  Serial.print ("Send: ");
  Serial.print (1<< index);
  Serial.print (", Recv: ");
  Serial.println (ret);

  index++;
  index %= 8;

  delay (500); // totally 1 second delay
} // end of loop

```

and the **slave** :

```auto
#include <SPI.h>
#include "encoding.h"

int pinmask[2];
bool state;
void setup (void)
{
  Serial.begin (115200);
  Serial.println ("[SLAVE6]");

  SPI.begin();
  //some tweaks here:  
  // SS and SCK become input pins, and
  // turn of the CSMODE setting
  pinMode(SS, INPUT);
  pinMode(SCK, INPUT);
  SPI_REG(SPI_REG_CSMODE) = SPI_CSMODE_OFF;

  SPI_REG(SPI_REG_SCKDIV) = 1023;
  SPI_REG(SPI_REG_DSCKCS) = 255;
  SPI_REG(SPI_REG_DCSSCK) = 255;
  SPI_REG(SPI_REG_DINTERCS) = 255;
  SPI_REG(SPI_REG_DINTERXFR) = 255;

  pinmask[0] = digitalPinToBitMask(SCK);
  attachInterrupt(digitalPinToInterrupt(SCK), ISR0, RISING);
  pinmask[1] = digitalPinToBitMask(SS);
  attachInterrupt(digitalPinToInterrupt(SS), ISR1, CHANGE);

  set_csr(mstatus, MSTATUS_MIE);
} // end of setup

// SPI interrupt routine
bool newData = false;
bool enable = false;

volatile byte x;
void ISR0 (void)
{
  if (enable == false)
    return;
  newData = true;
  x = SPI.transfer (x);
  SPI_REG(SPI_REG_CSMODE) = SPI_CSMODE_OFF;

  GPIO_REG(GPIO_RISE_IP) = pinmask[0];
}
void ISR1 (void)
{
  enable = (enable == true) ? false : true;
  GPIO_REG(GPIO_RISE_IP) = pinmask[1];
  GPIO_REG(GPIO_FALL_IP) = pinmask[1];
}

void loop (void)
{
  clear_csr(mstatus, MSTATUS_MIE);
  if (newData == true ) {
    Serial.print(x);
    Serial.println(". ");
    newData = false;
  }
  set_csr(mstatus, MSTATUS_MIE);
}

```

where the slave’s MISO connects to master’s MOSI and vice versa.

The output of master is something like:

```auto
...
Send: 1, Recv: 0
Send: 2, Recv: 0
Send: 4, Recv: 255
Send: 8, Recv: 0
Send: 16, Recv: 0
Send: 32, Recv: 0
Send: 64, Recv: 0
Send: 128, Recv: 0
Send: 1, Recv: 0
Send: 2, Recv: 0
Send: 4, Recv: 255
Send: 8, Recv: 0
Send: 16, Recv: 0
Send: 32, Recv: 0
Send: 64, Recv: 0
Send: 128, Recv: 0
Send: 1, Recv: 0
Send: 2, Recv: 0
Send: 4, Recv: 255
...

```

and the output of slave be:

```auto
...
0.
0.
0.
0.
255.
0.
0.
0.
0.
0.
0.
0.
255.
0.
0.
0.
0.
0.
0.
...

```

I guess the result indicates that after the master sends `0x0000 0001`, the slave cannot read the result synchronously so just seeing the last bit 8 times, which is 255.

Any comments would be appreciated.

---

<div class="post-metadata">

**Author:** ![aou](https://avatars.discourse-cdn.com/v4/letter/a/e99b99/32.png) [@aou](https://forums.sifive.com/u/aou)\
**Post date:** [April 4, 2017, 7:55pm UTC](https://forums.sifive.com/t/problems-about-using-spi/442/4 "2017-04-04T19:55:48Z")

</div>

Regarding the slave code:

- Don’t call `SPI.begin()`. `SPI.begin()` enables the hardware I/O function (IOF) for the SPI pins, meaning that the pins are directly controlled by the SPI1 peripheral and not by the software GPIO registers.
- Replace `SPI.transfer()` with a bit-banged implementation. The SPI library cannot be used here since it only supports master functionality, so you would need to detect the `SCK` transitions and perform the sampling/shifting manually.
- Remove all `SPI_REG()` assignments. These don’t do anything since the SPI1 hardware is not involved.

Regarding the master code:

- The `digitalWrite(SS, ...)` calls are not needed. The SPI controller automatically asserts/deasserts the chip select before/after each SPI transaction.
- `DSCKCS`, `DCSSCK`, `DINTERCS`, and `DINTERXFR` are 16-bit registers, while the `SPI_REG` macro assumes 32-bit memory-mapped registers. Interacting with them as individual fields requires some slightly different macros:

```auto
#define _REG16(p, i) (*(volatile uint16_t *) ((p) + (i)))
#define SPI_REG16(offset) _REG16(SPI1_BASE_ADDR, offset)

SPI_REG16(SPI_REG_DSCKCS) = 255;
SPI_REG16(SPI_REG_DCSSCK) = 255;
SPI_REG16(SPI_REG_DINTERCS) = 255;
SPI_REG16(SPI_REG_DINTERXFR) = 255;

```

---

<div class="post-metadata">

**Author:** ![nonerkao](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.sifive.com/nonerkao/32/194_2.png) [@nonerkao](https://forums.sifive.com/u/nonerkao)\
**Post date:** [April 4, 2017, 8:49pm UTC](https://forums.sifive.com/t/problems-about-using-spi/442/5 "2017-04-04T20:49:45Z")

</div>

Thanks for the quick response and tips!

The slave is re-writed to the following:

```auto
#include <SPI.h>
#include "encoding.h"

int pinmask[2];
bool isHigh = false;
byte newData;
void setup (void)
{
  Serial.begin (115200);
  Serial.println ("[SLAVE6]");

  pinMode(MISO, INPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(SS, INPUT);
  pinMode(SCK, INPUT);
  isHigh = (digitalRead(SCK) == HIGH) ? true : false;

  pinmask[0] = digitalPinToBitMask(SCK);
  attachInterrupt(digitalPinToInterrupt(SCK), ISR0, CHANGE);
  pinmask[1] = digitalPinToBitMask(SS);
  attachInterrupt(digitalPinToInterrupt(SS), ISR1, CHANGE);

  set_csr(mstatus, MSTATUS_MIE);
  newData = 0;
} // end of setup

// SPI interrupt routine

bool dorw = false;
bool enable = false;

byte rxfifo;
byte txfifo = '0';
volatile byte x;
void ISR0 (void)
{
  if (enable == false)
    return;
  if (isHigh) {
    GPIO_REG(GPIO_RISE_IP) = pinmask[0];
    dorw = true;
  }
  else {
    GPIO_REG(GPIO_FALL_IP) = pinmask[0];
    rxfifo = (rxfifo << 1) | (x == HIGH);
    txfifo <<= 1;
  }
  isHigh = (isHigh == true) ? false : true;
}

void ISR1 (void)
{
  enable = (enable == true) ? false : true;
  GPIO_REG(GPIO_RISE_IP) = pinmask[1];
  GPIO_REG(GPIO_FALL_IP) = pinmask[1];
}

void loop (void)
{
  if (dorw) {
    clear_csr(mstatus, MSTATUS_MIE);
    x = digitalRead(MISO);
    digitalWrite(MOSI, txfifo & 0x80);
    dorw = false;
    newData++;
    set_csr(mstatus, MSTATUS_MIE);
  }

  if (newData < 8)
    return ;

  txfifo = rxfifo;
  Serial.print(rxfifo);
  Serial.println(". ");
  newData = 0;
}

```

but now (with the same master) the output is like:

```auto
...
0.
1.
130.
4.
8.
16.
32.
64.
0.
1.
130.
4.
8.
16.
32.
64.
0.
1.
130.
4.
...

```

The 0 and 130 are somehow weird. Any ideas?

---

<div class="post-metadata">

**Author:** ![aou](https://avatars.discourse-cdn.com/v4/letter/a/e99b99/32.png) [@aou](https://forums.sifive.com/u/aou)\
**Post date:** [April 4, 2017, 11:26pm UTC](https://forums.sifive.com/t/problems-about-using-spi/442/6 "2017-04-04T23:26:35Z")

</div>

130 is `8'b1000_0010`, which might indicate that the MSB is being sampled too early when MOSI still represents the LSB of the previous transfer. The SPI controller continues to drive MOSI with its last value even after SS is deasserted. This would also explain `8'b0100_0000` being followed by `8'b0000_0000` when it should be `8'b1000_0000`. The other cases are unaffected since LSB = MSB = 0, so the MSB would be the correct value by coincidence.

This doesn’t quite explain why the intermediate bits seem to be unaffected. However, it is straightforward to test this hypothesis by transmitting different values from the master, not just powers of 2.

It is not clear to me exactly how the code would manifest such behavior, but I noticed that there are a couple potential race conditions. For example, `enable` being initialized to `false` assumes that the first execution of ISR1 is after a falling edge of SS, which would not be the case if the interrupt is enabled while a transfer is ongoing. The delays on the master side make this less likely, but it is still a possibility.

Also, suppose that the master is a quiescent state (`SCK` is low) when `setup()` is run, so `isHigh = false`. When ISR0 is first run, it would be on a rising edge of `SCK`, but the `else` clause would be executed first instead. The code just happens to work since the handler fails to clear the `GPIO_RISE_IP` flag, so the processor takes another interrupt immediately and executes the `if` clause as expected the second time.

To ensure that the slave is properly synchronized with the master, the ISRs should check whether or not it was a rising or falling edge that triggered the interrupt, either by examining `GPIO_RISE_IP` / `GPIO_FALL_IP` or the `SS` / `SCK` pins directly. You could also try moving the `if (dorw) { ... }` logic from `loop()` into ISR0 to reduce latency. (If you do so, the `clear_csr` / `set_csr` become unnecessary since interrupts are already disabled within the handlers.)

---

<div class="post-metadata">

**Author:** ![nonerkao](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.sifive.com/nonerkao/32/194_2.png) [@nonerkao](https://forums.sifive.com/u/nonerkao)\
**Post date:** [April 5, 2017, 10:47am UTC](https://forums.sifive.com/t/problems-about-using-spi/442/7 "2017-04-05T10:47:44Z")

</div>

I change both the master and the slave according to your suggestion. The master now sends from 48(‘0’) to 122(‘z’). And the slave becomes:

```auto
#include <SPI.h>
#include "encoding.h"

int pinmask[2];
byte newData;
void setup (void)
{
  Serial.begin (115200);
  Serial.println ("[SLAVE6]");

  pinMode(MISO, INPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(SS, INPUT);
  pinMode(SCK, INPUT);

  pinmask[0] = digitalPinToBitMask(SCK);
  attachInterrupt(digitalPinToInterrupt(SCK), ISR0, CHANGE);
  pinmask[1] = digitalPinToBitMask(SS);
  attachInterrupt(digitalPinToInterrupt(SS), ISR1, FALLING);

  set_csr(mstatus, MSTATUS_MIE);
  newData = 0;
} // end of setup

byte data[2]; // 0 for rx, 1 for tx;
bool boot = false;
volatile byte rxfifo;
volatile byte txfifo = '0';
volatile byte x;
void ISR0 (void)
{
  if (GPIO_REG(GPIO_RISE_IP) & pinmask[0]) {
    GPIO_REG(GPIO_RISE_IP) = pinmask[0];
    digitalWrite(MOSI, txfifo & 0x80);
    x = digitalRead(MISO);

  }
  else if (GPIO_REG(GPIO_FALL_IP) & pinmask[0]) {
    GPIO_REG(GPIO_FALL_IP) = pinmask[0];
    rxfifo = (rxfifo << 1) | (x == HIGH);
    txfifo <<= 1;
    if (boot == true) {
      newData++;
      if (newData >= 8) {
        txfifo = rxfifo;
        data[0] = rxfifo;
        data[1] = txfifo;
        rxfifo = 0;
        newData = 0;
      }
    }
  }
}

void ISR1 (void)
{
  GPIO_REG(GPIO_FALL_IP) = pinmask[1];
  boot = true;
}

void loop (void)
{
  if (boot != true)
    return;
  if (newData != 0)
    return;  
  Serial.print ("Recv: ");
  Serial.println (data[0]);
}

```

Various output patterns are observed. For instance, from the master’s point of view, it maybe something like (it is a real case!):

```auto
...
Send: 119, Recv: 187
Send: 120, Recv: 59
Send: 121, Recv: 188
Send: 122, Recv: 60
Send: 48, Recv: 189
Send: 49, Recv: 24
Send: 50, Recv: 24
Send: 51, Recv: 153
Send: 52, Recv: 25
Send: 53, Recv: 154
Send: 54, Recv: 26
Send: 55, Recv: 155
Send: 56, Recv: 27
Send: 57, Recv: 156
Send: 58, Recv: 28
Send: 59, Recv: 157
...

```

It is worth noting that, the values master receives may not be the values the slave sends.

However, consider three different sequences,

- The sequence that the master sends: 48, 49, 50, …
- The sequence that the slave receives/sends
- The sequence that the master receives

they are actually identical bit strings. I think they have different interpretation of the whole bit string because of timing skew of value sampling. Is there any way to have more delay or larger period of the `SCK` signal?

BTW, once I remove the `SS` control commands from the master code, the SS becomes no signal at all. So I keep it for now.

---

<div class="post-metadata">

**Author:** ![nonerkao](https://sea2.discourse-cdn.com/flex020/user_avatar/forums.sifive.com/nonerkao/32/194_2.png) [@nonerkao](https://forums.sifive.com/u/nonerkao)\
**Post date:** [April 5, 2017, 3:05pm UTC](https://forums.sifive.com/t/problems-about-using-spi/442/8 "2017-04-05T15:05:52Z")

</div>

Good news! I got the software-emulated SPI slave work on my hifive1 board.🎉

The key modification is the logic inside the ISR handling `SCK`. The problem is on the slave side. It is just too late to let the master sample the right bit, if the slave write the bit right after it notice the rising of `SCK`. So I move tx-related codes to falling edge part to ensure that when the next rising edge comes, the right bit should have already been there.

The slave code now becomes:

```auto
#include <SPI.h>
#include "encoding.h"

int pinmask[2];
byte newData;
volatile byte txfifo = 128;
void setup (void)
{
  Serial.begin (115200);
  Serial.println ("[SLAVE6]");

  pinMode(MISO, INPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(SS, INPUT);
  pinMode(SCK, INPUT);

  pinmask[0] = digitalPinToBitMask(SCK);
  attachInterrupt(digitalPinToInterrupt(SCK), ISR0, CHANGE);
  pinmask[1] = digitalPinToBitMask(SS);
  attachInterrupt(digitalPinToInterrupt(SS), ISR1, FALLING);

  set_csr(mstatus, MSTATUS_MIE);
  newData = 0;
  digitalWrite(MOSI, txfifo & 0x80);
} // end of setup

byte data[2]; // 0 for rx, 1 for tx;
bool boot = false;
volatile byte rxfifo;
volatile byte x;
void ISR0 (void)
{
  if (GPIO_REG(GPIO_RISE_IP) & pinmask[0]) {
    GPIO_REG(GPIO_RISE_IP) = pinmask[0];
    x = digitalRead(MISO);
    rxfifo = (rxfifo << 1) | (x == HIGH);
  }
  else if (GPIO_REG(GPIO_FALL_IP) & pinmask[0]) {
    txfifo <<= 1;
    digitalWrite(MOSI, txfifo & 0x80);
    if (boot == true) {
      newData++;
      if (newData >= 8) {
        txfifo = rxfifo;
        digitalWrite(MOSI, txfifo & 0x80);
        data[0] = rxfifo;
        data[1] = txfifo;
        rxfifo = 0;
        newData = 0;
      }
    }
    GPIO_REG(GPIO_FALL_IP) = pinmask[0];
  }
}

void ISR1 (void)
{
  GPIO_REG(GPIO_FALL_IP) = pinmask[1];
  boot = true;
}

void loop (void)
{
  if (boot != true)
    return;
  if (newData != 0)
    return;

  Serial.print ("Recv: ");
  Serial.println (data[0]);
}

```

But there are some glitches sometimes, for example:

```auto
...
Send: 55, Recv: 54
Send: 56, Recv: 119 
Send: 57, Recv: 24
Send: 58, Recv: 57
Send: 59, Recv: 58
Send: 60, Recv: 59
Send: 61, Recv: 60
Send: 62, Recv: 61
Send: 63, Recv: 126 
Send: 64, Recv: 31
Send: 65, Recv: 64
Send: 66, Recv: 65
Send: 67, Recv: 66
Send: 68, Recv: 67
Send: 69, Recv: 68
Send: 70, Recv: 69
Send: 71, Recv: 70
Send: 72, Recv: 7
Send: 73, Recv: 72
...

```

This implementation does not seem to be stable enough for serious usage, but is by far the best one. As a little exercise for understanding SPI, I think this is an interesting experience. My next step should be playing with some real SPI modules, can’t wait!

Anyway, thanks for all the kind helps!

---

<div class="post-metadata">

**Author:** ![aou](https://avatars.discourse-cdn.com/v4/letter/a/e99b99/32.png) [@aou](https://forums.sifive.com/u/aou)\
**Post date:** [April 5, 2017, 6:50pm UTC](https://forums.sifive.com/t/problems-about-using-spi/442/9 "2017-04-05T18:50:17Z")

</div>

You’re welcome! Glad to be of help.

> [@nonerkao](#):
>
> But there are some glitches sometimes, for example:

It always seems to be single-bit corruption, so at least the slave is shifting the correct number of times. Perhaps there is some noise on MOSI, if the glitches are happening on the slave side? I wonder if implementing a low-pass filter would help, i.e., sampling three times and taking the majority.

You could also try increasing `SPI_REG_SCKDIV`, but a clock divisor of 2048 is already rather generous.
