SpinalHDL / SaxonSoc

SoC based on VexRiscv and ICE40 UP5K
MIT License
150 stars 40 forks source link

Standard SPI available? #77

Closed surabibio closed 1 year ago

surabibio commented 1 year ago

Hi, How do I set up a standard SPI with data-width as 1 (i.e 1 MOSI and 1 MISO signal) BmbSpiGenerator() seems to work only when SpiXdrParameter.dataWidth is 2 or more...

Dolu1990 commented 1 year ago

Hi,

1 MOSI + 1 MISO => datawidth = 2 with full duplex :)

Do all good i would say.

surabibio commented 1 year ago

Hi Charles, The xdr which is new to me, I couldn't get it to work. On the other hand good old SPI works great. Your comments on this piece of code, please. Am I doing something redundant that can be easily accomplished with xdr?

class BmbSpiGenerator2(apbOffset : Handle[BigInt] = Unset)      // Good Old MoSi, MiSo wihtout xdr
(implicit interconnect: BmbInterconnectGenerator, decoder : BmbImplicitPeripheralDecoder = null) extends Area {
  val parameter = Handle[SpiMasterCtrlMemoryMappedConfig]
  val spi = Handle(logic.io.spi.toIo)  
  val ctrl : Handle[Bmb] = Handle(logic.io.ctrl)
  val interrupt = Handle(logic.io.interrupt)

  val accessSource = Handle[BmbAccessCapabilities]
  val accessRequirements = Handle[BmbAccessParameter]

  val logic = Handle(BmbSpiMasterCtrl(parameter, accessRequirements.toBmbParameter()))

  val bmbRequirements = Handle[BmbParameter]
  val bmb = Handle[Bmb]

  @dontName var interruptCtrl : InterruptCtrlGeneratorI = null
  var interruptId = 0
  def connectInterrupt(ctrl : InterruptCtrlGeneratorI, id : Int): Unit = {
    ctrl.addInterrupt(interrupt, id)
    interruptCtrl = ctrl
    interruptId = id
  }

  interconnect.addSlave(
    accessSource = accessSource,
    accessCapabilities = accessSource.derivate(BmbGpio2.getBmbCapabilities),
    accessRequirements = accessRequirements,
    bus = ctrl,
    mapping = apbOffset.derivate(SizeMapping(_, 1 << Gpio.addressWidth))
  )

  if(decoder != null) interconnect.addConnection(decoder.bus, ctrl)
}

--

case class BmbSpiMasterCtrl(p : SpiMasterCtrlMemoryMappedConfig, ctrlParameter : BmbParameter) extends Component{
  val io = new Bundle {
    val ctrl = slave(Bmb(ctrlParameter))
    val spi = master(SpiMaster(p.ctrlGenerics.ssWidth))
    val interrupt = out Bool()
  }

  val spiCtrl = SpiMasterCtrl(p.ctrlGenerics)
  io.spi <> spiCtrl.io.spi

  val busCtrl = BmbSlaveFactory(io.ctrl)
  val bridge = spiCtrl.io.driveFrom(busCtrl)(p)
  io.interrupt <> bridge.interruptCtrl.interrupt
}

And the driver...

#ifndef SPI_H_
#define SPI_H_

#include "type.h"
#include "io.h"

#define SPI_DATA        0x00
#define SPI_STATUS      0x04
#define SPI_CONFIG      0x08
#define SPI_CLK_DIVIDER 0x0C
#define SPI_SS_SETUP    0x10
#define SPI_SS_HOLD     0x14
#define SPI_SS_DISABLE  0x18

typedef struct {
    u32 cpol;
    u32 cpha;
    u32 mode;
    u32 clkDivider;
    u32 ssSetup;
    u32 ssHold;
    u32 ssDisable;
} Spi_Config;

#define SPI_MISO_READ   0x01000000
#define SPI_RX_VALID    0x80000000

#define SPI_CMD_SS_EN   0x11000000
#define SPI_CMD_SS_DIS  0x10000000

#define SPI_RSP_VALID (1 << 31)

#define SPI_STATUS_CMD_INT_ENABLE   (1 << 0)
#define SPI_STATUS_RSP_INT_ENABLE   (1 << 1)
#define SPI_STATUS_CMD_INT_FLAG     (1 << 8)
#define SPI_STATUS_RSP_INT_FLAG     (1 << 9)

#define SPI_MODE_CPOL (1 << 0)
#define SPI_MODE_CPHA (1 << 1)

static u32 spi_cmdAvailability(u32 reg){
    return read_u32(reg + SPI_STATUS) >> 16;
}
static u32 spi_rspOccupancy(u32 reg){
    return read_u32(reg + SPI_STATUS) & SPI_STATUS_RSP_INT_FLAG;
}

static void spi_write(u32 reg, u8 data){
    while(spi_cmdAvailability(reg) == 0);                           // wait for Tx Buffer to be available
    write_u32((u32)data, reg + SPI_DATA);                           // just zero extend and write
}

static u8 spi_writeRead(u32 reg, u8 data){
    u32 u;
    while(spi_cmdAvailability(reg) == 0);                           // wait for Tx Buffer to be available
    write_u32(SPI_MISO_READ | data, reg + SPI_DATA);                // que transfer on SPI bus

    while (1) {
        u = read_u32(reg + SPI_DATA);
        if (u & SPI_RX_VALID)
            return u;
    }
}

static u8 spi_read(u32 reg){
    u32 u;
    while(spi_cmdAvailability(reg) == 0);                           // wait for Tx Buffer to be available
    write_u32(SPI_MISO_READ, reg + SPI_DATA);                       // que transfer on SPI bus

    while (1) {
        u = read_u32(reg + SPI_DATA);
        if (u & SPI_RX_VALID)
            return u;
    }
}

static void spi_select(u32 reg, u32 slaveId){
    while(spi_cmdAvailability(reg) == 0);
    write_u32(SPI_CMD_SS_EN | slaveId, reg + SPI_DATA);
}

static void spi_deselect(u32 reg, u32 slaveId){
    while(spi_cmdAvailability(reg) == 0);
    write_u32(SPI_CMD_SS_DIS | slaveId, reg + SPI_DATA);
}

static void spi_applyConfig(u32 reg, Spi_Config *config){
    write_u32((config->cpol << 0) | (config->cpha << 1) | (config->mode << 4), reg + SPI_CONFIG);
    write_u32(config->clkDivider,   reg + SPI_CLK_DIVIDER);
    write_u32(config->ssSetup,      reg + SPI_SS_SETUP);
    write_u32(config->ssHold,       reg + SPI_SS_HOLD);
    write_u32(config->ssDisable,    reg + SPI_SS_DISABLE);
}

#endif /* SPI_H_ */
Dolu1990 commented 1 year ago

As long as it works for you ^^

So, the idea of the XDR is to support DDR data rate, faster SPI phy, multiple data width, half / full duplex all at once.

How did you parametrized / instancied the ctrl / phy for the Xdr controller ?

surabibio commented 1 year ago

This way

` val spiA = new BmbSpiGenerator2(0x100) (interconnect,periphDecoder) spiA.parameter load SpiMasterCtrlMemoryMappedConfig ( ctrlGenerics = SpiMasterCtrlGenerics( ssWidth = 1, dataWidth = 8, timerWidth = 12 ), cmdFifoDepth = 16, rspFifoDepth = 16 )

`

surabibio commented 1 year ago

Oh, for the XDR this is the one..

`
val spiB = new BmbSpiGenerator(0x200) (interconnect, periphDecoder) { val decoder = SpiPhyDecoderGenerator(phy) val wifiSpi = decoder.spiMasterNone() }

spiB.parameter load SpiXdrMasterCtrl.MemoryMappingParameters(
  SpiXdrMasterCtrl.Parameters(
    dataWidth = 8,
    timerWidth = 12,
    spi = SpiXdrParameter(
      dataWidth = 1, // 2
      ioRate = 1,
      ssWidth = 1
    )
  //) .addFullDuplex(id = 0).addHalfDuplex(id = 1, rate = 1, ddr = false, spiWidth = 1, lateSampling = false),
  ) .addHalfDuplex(id = 1, rate = 1, ddr = false, spiWidth = 1, lateSampling = false),
  cmdFifoDepth = 16,
  rspFifoDepth = 16
)

`

Dolu1990 commented 1 year ago

Ahhh, so dataWidth need to be 2, and addFullDuplex(id = 0) need to remain i would say.

surabibio commented 1 year ago

Hi Charles, I decided to go with the BmbSpiGenerator2 mentioned above, because it was within my comprehension.