enjoy-digital / litex

Build your hardware, easily!
Other
2.99k stars 568 forks source link

SPI master with DMA implementation #571

Closed kamejoko80 closed 4 years ago

kamejoko80 commented 4 years ago

Hello,

I'd like to connect FPGA with the Silabs WF200 Wifi controller. From the IC spec, it basically requires SPI or SDIO bus interface. First of all, I want to try with SPI because it is simple, but I don't know how configure SPI with DMA to increase the performance. Is there any reference design including gateware and firmware to build a Vexrisc-V system with SPI and DMA?

Thank you.

enjoy-digital commented 4 years ago

Hi,

here is some code i prototyped some time ago (but haven't tested it on hardware).

This is designed to be used with SPIMaster (https://github.com/enjoy-digital/litex/blob/master/litex/soc/cores/spi.py) and the idea is read data from Wishbone, write it to SPI and be able to control this DMA from the CPU:

from migen import *

from litex.soc.interconnect.csr import *
from litex.soc.interconnect import wishbone

SPI_START  = (1<<0)
SPI_LENGTH = (1<<8)

SPI_DONE   = (1<<0)

class Wishbone2SPIDMA(Module, AutoCSR):
    def __init__(self):
        # Wishbone
        self.bus = bus = wishbone.Interface()

        # Control
        self.start = CSR()
        self.done  = CSRStatus()

        # Read parameters: base and length of DMA
        self.read_base   = CSRStorage(32)
        self.read_length = CSRStorage(32)

        # SPI parameters: address of control/status/mosi registers
        self.spi_control_reg_address = CSRStorage(32)
        self.spi_status_reg_address  = CSRStorage(32)
        self.spi_mosi_reg_address    = CSRStorage(32)

        # # #

        # Shorten CSR's names
        start = self.start.re
        done  = self.done.status

        read_base   = self.read_base.storage[2:]
        read_length = self.read_length.storage[2:]

        spi_mosi_reg_address    = self.spi_mosi_reg_address.storage
        spi_control_reg_address = self.spi_control_reg_address.storage
        spi_status_reg_address  = self.spi_status_reg_address.storage

        # internals
        offset = Signal(32)
        data   = Signal(32)

        # fsm
        self.submodules.fsm = fsm = FSM()
        fsm.act("IDLE",
            If(start,
                NextValue(offset, 0),
                NextState("WISHBONE-READ-DATA")
            ).Else(
                done.eq(1),
            )
        )

        fsm.act("WISHBONE-READ-DATA",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.adr.eq(read_base + offset),
            If(bus.ack,
                NextValue(data, bus.dat_r),
                NextState("SPI-WRITE-DATA")
            )
        )
        fsm.act("SPI-WRITE-DATA",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(spi_mosi_reg_address),
            bus.dat_w.eq(data),
            If(bus.ack,
                NextState("SPI-WRITE-START")
            )
        )
        fsm.act("SPI-WRITE-START",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(spi_control_reg_address),
            bus.dat_w.eq(SPI_START),
            If(bus.ack,
                NextState("SPI-WAIT-DONE")
            )
        )
        fsm.act("SPI-WAIT-DONE",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.adr.eq(spi_status_reg_address),
            If(bus.ack,
                If(bus.dat_r & SPI_DONE,
                    NextState("INC-OFFSET")
                )
            )
        )
        fsm.act("INC-OFFSET",
            If(offset >= read_length,
                NextState("IDLE")
            ).Else(
                NextValue(offset, offset + 1),
                NextState("WISHBONE-READ-DATA")
            )
        )

if __name__ == '__main__':
    dut = Wishbone2SPIDMA()
    def dut_tb(dut):
        yield dut.bus.ack.eq(1)   # always ack
        yield dut.bus.dat_r.eq(1) # always return 1
        # configure DMA
        yield dut.read_base.storage.eq(0x10000000)
        yield dut.read_length.storage.eq(0x100)
        yield dut.spi_mosi_reg_address.storage.eq(0x20000000)
        yield dut.spi_control_reg_address.storage.eq(0x20000004)
        yield dut.spi_status_reg_address.storage.eq(0x20000004)
        yield
        # start DMA
        yield dut.start.re.eq(1)
        yield
        yield dut.start.re.eq(0)
        yield
        # wait DMA
        while (yield dut.done.status) == 0:
            yield

    run_simulation(dut, dut_tb(dut), vcd_name="wb2spi_dma.vcd")

To integrate it in your design, in your SoC:

# SPI Master
self.submodules.spi = SPIMaster(pads, 32, sys_clk_freq, spi_clk_freq)
self.add_csr("spi")

#SPI DMA
self.submodules.spi_dma = Wishbone2SPIDMA()
self.add_csr("spi_dma")
self.bus.add_master(self.spi_dma.bus).

You will then need to write the CPU software to configure the address of the SPI Master registers of your design and program the DMA transfer.

kamejoko80 commented 4 years ago

Hi enjoy-digital,

Thanks for the reply.

I defined a function "add_spidma" in soc_linux.py

        # SPI --------------------------------------------------------------------------------------
        def add_spi(self, data_width, clk_freq):
            spi_pads = self.platform.request("spi")
            self.submodules.spi = SPIMaster(spi_pads, data_width, self.clk_freq, clk_freq)
            self.add_csr("spi")

        # SPI DMA ----------------------------------------------------------------------------------
        def add_spidma(self):
            self.submodules.spi_dma = Wishbone2SPIDMA()
            self.add_csr("spi_dma")
            self.bus.add_master(self.spi_dma.bus)

Then called it from make.py

        if "spidma" in board.soc_capabilities:
            soc.add_spidma()

Finally, I got the error:

INFO:SoCCSRHandler:spi CSR allocated at Location 7.
INFO:SoCCSRHandler:spi_dma CSR allocated at Location 8.
Traceback (most recent call last):
  File "./make.py", line 463, in <module>
    main()
  File "./make.py", line 416, in main
    soc.add_spidma()
  File "/home/phuong/Workspace/FPGA/LITEX/linux-on-litex-vexriscv/soc_linux.py", line 133, in add_spidma
    self.bus.add_master(self.spi_dma.bus)
  File "/home/phuong/Workspace/FPGA/LITEX/linux-on-litex-vexriscv/litex/litex/soc/integration/soc.py", line 307, in add_master
    master = self.add_adapter(name, master)
  File "/home/phuong/Workspace/FPGA/LITEX/linux-on-litex-vexriscv/litex/litex/soc/integration/soc.py", line 286, in add_adapter
    if interface.data_width != self.data_width:
AttributeError: 'NoneType' object has no attribute 'data_width'

Please correct me if I'm wrong. Thanks

enjoy-digital commented 4 years ago

Sorry, there was a typo in the integration code, it's: self.bus.add_master(master=self.spi_dma.bus) and not self.bus.add_master(self.spi_dma.bus).

kamejoko80 commented 4 years ago

Hi enjoy-digital,

After correcting the typo, I could compile the gateware successfully, but the DMA doesn't work. To check the SPI, I've tried first with normal mode:

void spidma_test(void)
{
    unsigned int ctrl;

    /* SPI loopback config */
    spi_loopback_write(1);

    /* data to write */
    spi_mosi_write(0xA5);

    /* CS assert */
    spi_cs_write(1);

    /* SPI start, length = 32bits */
    ctrl = (8 << 8) | (1 << 0);

    spi_control_write(ctrl);

    while(!spi_status_read());

    /* CS de-assert */
    spi_cs_write(0);

    printf("MISO read %X\r\n", spi_miso_read());
}

Just added spidma_test into the bios command line for quick testing, then it worked properly, I could capture the waveform from the logic analyzer.

        __   _ __      _  __
       / /  (_) /____ | |/_/
      / /__/ / __/ -_)>  <
     /____/_/\__/\__/_/|_|
   Build your hardware, easily!

 (c) Copyright 2012-2020 Enjoy-Digital
 (c) Copyright 2007-2015 M-Labs

 BIOS built on Jun 22 2020 22:34:16
 BIOS CRC passed (789a0fec)

 Migen git sha1: 19d5eae
 LiteX git sha1: 56e1528

--=============== SoC ==================--
CPU:       VexRiscv @ 100MHz
ROM:       32KB
SRAM:      4KB
L2:        8KB
MAIN-RAM:  262144KB

--========== Initialization ============--
Initializing SDRAM...
SDRAM now under software control
Read leveling:
m0, b0: |00000000000000000000000000000000| delays: -
m0, b1: |00000000000000000000000000000000| delays: -
m0, b2: |00000000000000000000000000000000| delays: -
m0, b3: |00000000000000000000000000000000| delays: -
m0, b4: |00000000000000000000000000000000| delays: -
m0, b5: |00000000000000000000000000000000| delays: -
m0, b6: |00111111111111110000000000000000| delays: 09+-06
m0, b7: |00000000000000000000000000000000| delays: -
best: m0, b6 delays: 09+-06
m1, b0: |00000000000000000000000000000000| delays: -
m1, b1: |00000000000000000000000000000000| delays: -
m1, b2: |00000000000000000000000000000000| delays: -
m1, b3: |00000000000000000000000000000000| delays: -
m1, b4: |00000000000000000000000000000000| delays: -
m1, b5: |00000000000000000000000000000000| delays: -
m1, b6: |00011111111111100000000000000000| delays: 09+-06
m1, b7: |00000000000000000000000000000000| delays: -
best: m1, b6 delays: 09+-06
SDRAM now under hardware control
Memtest OK
Memspeed Writes: 262Mbps Reads: 327Mbps

--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
             Timeout
No boot medium found

--============= Console ================--
litex> spidma
MISO read A5

For DMA mode, I've tried with the following code:

void spidma_test(void)
{
    unsigned int *buffer = (unsigned int *)MAIN_RAM_BASE;

    /* initialize buffer */
    buffer[0] = 0xA5A5A5A5;
    buffer[1] = 0x12345678;
    buffer[2] = 0x87654321;
    buffer[3] = 0x11223344;

    /* SPI loopback config */
    spi_loopback_write(1);

    /* config frame length = 1 byte */
    spi_control_write(8 << 8);

    /* initialize spi dma */
    spi_dma_spi_control_reg_address_write(CSR_SPI_CONTROL_ADDR);
    spi_dma_spi_status_reg_address_write(CSR_SPI_STATUS_ADDR);
    spi_dma_spi_mosi_reg_address_write(CSR_SPI_MOSI_ADDR);

    /* setup dma transfer */
    spi_dma_read_base_write(MAIN_RAM_BASE);
    spi_dma_read_length_write(16);

    /* CS assert */
    spi_cs_write(1);

    /* start dma transfer */
    spi_dma_start_write(1);
    spi_dma_start_write(0);

    /* wait for dma done */
    while(!spi_dma_done_read());

    /* CS de-assert */
    spi_cs_write(0);

    printf("DMA done! MISO read %X\r\n", spi_miso_read());
}

I got the result:

--============= Console ================--
litex> spidma
DMA done! MISO read 0
litex> mr 0x40000000 16
Memory dump:
0x40000000  a5 a5 a5 a5 78 56 34 12 21 43 65 87 44 33 22 11  ....xV4.!Ce.D3".
litex> 

There is no pulse on the analyzer display, csn pin didn't change. There are somethings wrong here.

enjoy-digital commented 4 years ago

The way you are controlling the module seems fine, there are others typos in the Wishbone2SPIDMA code:

spi_mosi_reg_address    = self.spi_mosi_reg_address.storage
spi_control_reg_address = self.spi_control_reg_address.storage
spi_status_reg_address  = self.spi_status_reg_address.storage

should be:

spi_mosi_reg_address    = self.spi_mosi_reg_address.storage[2:]
spi_control_reg_address = self.spi_control_reg_address.storage[2:]
spi_status_reg_address  = self.spi_status_reg_address.storage[2:]
kamejoko80 commented 4 years ago

Hi enjoy-digital,

Thanks for your help.

After adding [2:] the result was not changed.

I noticed there is a function in the csr.h like:

static inline void spi_control_write(uint16_t v) {
    csr_write_simple(v >> 8, 0xf0003800L);
    csr_write_simple(v, 0xf0003804L);
}

It seems that my system is big-endian, then I've tried to modify the control register accessing in the dma.py:

        fsm.act("SPI-WRITE-START",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(spi_control_reg_address),
            bus.dat_w.eq(SPI_START),
            If(bus.ack,
                NextState("SPI-WAIT-DONE")
            )
        )

Changed to:

        fsm.act("SPI-WRITE-START",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(spi_control_reg_address + 4),
            bus.dat_w.eq(SPI_START),
            If(bus.ack,
                NextState("SPI-WAIT-DONE")
            )
        )

However, it still doesn't work. I have a question, does the code statement above can access the register by word, half-word, or byte? Does it allow unaligned access?

Thanks

enjoy-digital commented 4 years ago

@kamejoko80: sorry i just realized for this work you will need to set csr_data_width to 32 in your SoC, otherwise you won't be able write the SPI control register with a single access as you noticed. (so change csr_data_width to 32 and use the [2:] change i was suggesting).

kamejoko80 commented 4 years ago

Hi enjoy-digital,

Thanks for pointing out my mistake,

To make the DMA copy whole bytes in the buffer I've modified a little bit:

from migen import *

from litex.soc.interconnect.csr import *
from litex.soc.interconnect import wishbone

SPI_START  = ((8<<8) | (1<<0))
SPI_LENGTH = (1<<8)
SPI_DONE   = (1<<0)

class Wishbone2SPIDMA(Module, AutoCSR):
    def __init__(self):
        # Wishbone
        self.bus = bus = wishbone.Interface()

        # Control
        self.start = CSR()
        self.done  = CSRStatus()

        # Read parameters: base and length of DMA
        self.read_base   = CSRStorage(32)
        self.read_length = CSRStorage(32)

        # SPI parameters: address of control/status/mosi registers
        self.spi_control_reg_address = CSRStorage(32)
        self.spi_status_reg_address  = CSRStorage(32)
        self.spi_mosi_reg_address    = CSRStorage(32)

        # # #

        # Shorten CSR's names
        start = self.start.re
        done  = self.done.status

        read_base   = self.read_base.storage[2:]
        read_length = self.read_length.storage

        spi_mosi_reg_address    = self.spi_mosi_reg_address.storage[2:]
        spi_control_reg_address = self.spi_control_reg_address.storage[2:]
        spi_status_reg_address  = self.spi_status_reg_address.storage[2:]

        # internals
        word_offset = Signal(32)
        byte_offset = Signal(3)
        byte_count  = Signal(32)
        data        = Signal(32)

        # fsm
        self.submodules.fsm = fsm = FSM()
        fsm.act("IDLE",
            If(start,
                NextValue(word_offset, 0),
                NextValue(byte_offset, 0),
                NextValue(byte_count, 0),
                NextState("WISHBONE-READ-DATA")
            ).Else(
                done.eq(1),
            )
        )
        fsm.act("WISHBONE-READ-DATA",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.adr.eq(read_base + word_offset),
            If(bus.ack,
                NextValue(data, bus.dat_r),
                NextState("SPI-WRITE-DATA")
            )
        )
        fsm.act("SPI-WRITE-DATA",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(spi_mosi_reg_address),
            bus.dat_w.eq(data),
            If(bus.ack,
                NextState("SPI-WRITE-START")
            )
        )
        fsm.act("SPI-WRITE-START",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(spi_control_reg_address),
            bus.dat_w.eq(SPI_START),
            If(bus.ack,
                NextState("SPI-WAIT-DONE")
            )
        )
        fsm.act("SPI-WAIT-DONE",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.adr.eq(spi_status_reg_address),
            If(bus.ack,
                If(bus.dat_r & SPI_DONE,
                    NextValue(byte_count, byte_count + 1),
                    NextValue(byte_offset, byte_offset + 1),
                    NextState("SHIFT-BYTE")
                )
            )
        )
        fsm.act("SHIFT-BYTE",
            If(byte_count >= read_length,
                NextState("IDLE")
            ).Elif(byte_offset >= 4,
                NextValue(byte_offset, 0),
                NextState("INC-WORD-OFFSET")
            ).Else(
                NextValue(data, data >> 8),
                NextState("SPI-WRITE-DATA")
            )
        )
        fsm.act("INC-WORD-OFFSET",
            NextValue(word_offset, word_offset + 1),
            NextState("WISHBONE-READ-DATA")
        )

Also changed the firmware:

    unsigned int *buffer = (unsigned int *)MAIN_RAM_BASE;

    /* initialize buffer */
    buffer[0] = 0xA5A5A5A5;
    buffer[1] = 0x12345678;
    buffer[2] = 0x87654321;
    buffer[3] = 0x11223344;

    /* SPI loopback config */
    spi_loopback_write(1);

    /* config frame length = 1 byte */
    //spi_control_write(8 << 8);

    /* initialize spi dma */
    spi_dma_spi_control_reg_address_write(CSR_SPI_CONTROL_ADDR);
    spi_dma_spi_status_reg_address_write(CSR_SPI_STATUS_ADDR);
    spi_dma_spi_mosi_reg_address_write(CSR_SPI_MOSI_ADDR);

    /* setup dma transfer */
    spi_dma_read_base_write(MAIN_RAM_BASE);
    spi_dma_read_length_write(16);

    /* CS assert */
    spi_cs_write(1);

    /* start dma transfer */
    spi_dma_start_write(1);
    spi_dma_start_write(0);

    /* wait for dma done */
    while(!spi_dma_done_read());

    /* CS de-assert */
    spi_cs_write(0);

    printf("DMA done! MISO read %X\r\n", spi_miso_read());

By checking on the logic analyzer's display I saw the bytes are shifted out perfectly. Next step I'll try to modify the dma.py to support the DMA RX part.

Thank you,

enjoy-digital commented 4 years ago

Hi @kamejoko80,

great you got it working! In case you release your project, feel free to share it with us, it would make a good example and we could add a link to it to the wiki.

kamejoko80 commented 4 years ago

Hi enjoy-digital,

Yes, I will do.

Thank you very much.

kamejoko80 commented 4 years ago

Hi enjoy-digital,

I've added DMA RX part and consolidated the source code, but I could not write the MISO register's contain to the DMA RX RAM buffer:


#!/usr/bin/env python3

from migen import *

from litex.soc.interconnect.csr import *
from litex.soc.interconnect import wishbone

SPI_START  = ((8<<8) | (1<<0))
SPI_DONE   = (1<<0)

class Wishbone2SPIDMA(Module, AutoCSR):
    def __init__(self):
        # Wishbone
        self.bus = bus = wishbone.Interface()

        # Control
        self.start = CSR()
        self.done  = CSRStatus()

        # Read parameters: tx source address and length of DMA
        self.tx_src_addr = CSRStorage(32) # DMA TX source address
        self.rx_dst_addr = CSRStorage(32) # DMA RX destination address
        self.tx_len      = CSRStorage(32) # DMA TX size (bytes)
        self.rx_ena      = CSRStorage(32) # DMA RX enable

        # SPI parameters: address of control/status/mosi registers
        self.spi_control_reg_address = CSRStorage(32)
        self.spi_status_reg_address  = CSRStorage(32)
        self.spi_mosi_reg_address    = CSRStorage(32)
        self.spi_miso_reg_address    = CSRStorage(32)

        # DMA debug
        self.debug0 = CSRStorage(32)
        self.debug1 = CSRStorage(32)
        self.debug2 = CSRStorage(32)
        self.debug3 = CSRStorage(32)

        # # #

        # Shorten CSR's names
        start = self.start.re
        done  = self.done.status

        tx_src_addr = self.tx_src_addr.storage[2:]
        rx_dst_addr = self.rx_dst_addr.storage[2:]
        tx_len      = self.tx_len.storage
        rx_ena      = self.rx_ena

        spi_mosi_reg_address    = self.spi_mosi_reg_address.storage[2:]
        spi_miso_reg_address    = self.spi_miso_reg_address.storage[2:]
        spi_control_reg_address = self.spi_control_reg_address.storage[2:]
        spi_status_reg_address  = self.spi_status_reg_address.storage[2:]

        # internals
        word_offset = Signal(32)
        byte_offset = Signal(3)
        byte_count  = Signal(32)
        tx_data     = Signal(32)
        rx_data     = Signal(32)
        miso_data   = Signal(32)

        # fsm
        self.submodules.fsm = fsm = FSM()
        fsm.act("IDLE",
            If(start,
                NextValue(word_offset, 0),
                NextValue(byte_offset, 0),
                NextValue(byte_count, 0),
                NextValue(rx_data, 0),
                NextState("WISHBONE-READ-TX-DMA-BUFF")
            ).Else(
                done.eq(1),
            )
        )
        fsm.act("WISHBONE-READ-TX-DMA-BUFF",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.adr.eq(tx_src_addr + word_offset),
            If(bus.ack,
                NextValue(tx_data, bus.dat_r),
                NextState("WISHBONE-WRITE-MOSI-REG")
            )
        )
        fsm.act("WISHBONE-WRITE-MOSI-REG",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(spi_mosi_reg_address),
            bus.dat_w.eq(tx_data),
            If(bus.ack,
                NextState("WISHBONE-WRITE-CONTROL-START")
            )
        )
        fsm.act("WISHBONE-WRITE-CONTROL-START",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(spi_control_reg_address),
            bus.dat_w.eq(SPI_START),
            If(bus.ack,
                NextState("SPI-WAIT-DONE")
            )
        )
        fsm.act("SPI-WAIT-DONE",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.adr.eq(spi_status_reg_address),
            If(bus.ack,
                If(bus.dat_r & SPI_DONE,
                    If(rx_ena == 0,
                        NextValue(byte_count, byte_count + 1),
                        NextValue(byte_offset, byte_offset + 1),
                        NextState("SHIFT-BYTE")
                    ).Else(
                        NextValue(byte_count, byte_count + 1),
                        NextValue(byte_offset, byte_offset + 1),
                        NextState("WISHBONE-READ-MISO-REG")
                    )
                )
            )
        )
        fsm.act("WISHBONE-READ-MISO-REG",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.adr.eq(spi_miso_reg_address),
            If(bus.ack,
                NextValue(miso_data, bus.dat_r),
                NextState("SHIFT-BYTE")
            )
        )
        fsm.act("WISHBONE-WRITE-RX-DMA-BUFF-LAST",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(rx_dst_addr + word_offset),
            bus.dat_w.eq(rx_data),
            If(bus.ack,
                NextState("IDLE")
            )
        )
        fsm.act("WISHBONE-WRITE-RX-DMA-BUFF",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(rx_dst_addr + word_offset),
            bus.dat_w.eq(rx_data),
            If(bus.ack,
                NextValue(rx_data, 0),
                NextState("INC-WORD-OFFSET")
            )
        )
        fsm.act("SHIFT-BYTE",
            If(byte_count >= tx_len,
                If(rx_ena == 0,
                    NextState("IDLE")
                ).Else(
                    NextValue(rx_data, (rx_data << 8) | miso_data[0:8]),
                    NextState("WISHBONE-WRITE-RX-DMA-BUFF-LAST")
                )
            ).Elif(byte_offset >= 4,
                If(rx_ena == 0,
                    NextValue(byte_offset, 0),
                    NextState("INC-WORD-OFFSET")
                ).Else(
                    NextValue(byte_offset, 0),
                    NextValue(rx_data, (rx_data << 8) | miso_data[0:8]),
                    NextState("WISHBONE-WRITE-RX-DMA-BUFF")
                )
            ).Else(
                NextValue(rx_data, (rx_data << 8) | miso_data[0:8]),
                NextValue(tx_data, tx_data >> 8),
                NextState("WISHBONE-WRITE-MOSI-REG")
            )
        )
        fsm.act("INC-WORD-OFFSET",
            NextValue(word_offset, word_offset + 1),
            NextState("WISHBONE-READ-TX-DMA-BUFF")
        )

I'm stuck at the following code statement that attempts to copy a word from rx_data to the destination RAM buffer @ rx_dst_addr + word_offset.

       fsm.act("WISHBONE-WRITE-RX-DMA-BUFF-LAST",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(rx_dst_addr + word_offset),
            bus.dat_w.eq(rx_data),
            If(bus.ack,
                NextState("IDLE")
            )
        )
        fsm.act("WISHBONE-WRITE-RX-DMA-BUFF",
            bus.stb.eq(1),
            bus.cyc.eq(1),
            bus.we.eq(1),
            bus.adr.eq(rx_dst_addr + word_offset),
            bus.dat_w.eq(rx_data),
            If(bus.ack,
                NextValue(rx_data, 0),
                NextState("INC-WORD-OFFSET")
            )
        )

Instead of copy to RAM, I've temporarily declared 4 CSRStorage and passed the address to the rx_dst_addr register then I saw the results are correct.

        # DMA debug
        self.debug0 = CSRStorage(32)
        self.debug1 = CSRStorage(32)
        self.debug2 = CSRStorage(32)
        self.debug3 = CSRStorage(32)
    /* setup dma transfer */
    spi_dma_tx_src_addr_write(MAIN_RAM_BASE);
    //spi_dma_rx_dst_addr_write(MAIN_RAM_BASE + 16); /* DMA RX buffer's address  */
    spi_dma_rx_dst_addr_write(CSR_SPI_DMA_DEBUG0_ADDR); /* Write the results to the CSRs  */
    spi_dma_tx_len_write(16);
    spi_dma_rx_ena_write(1);

        ...

#if 0
        printf("rx_buff[0]          %X\r\n", rx_buff[0]);
        printf("rx_buff[1]          %X\r\n", rx_buff[1]);
        printf("rx_buff[2]          %X\r\n", rx_buff[2]);
        printf("rx_buff[3]          %X\r\n", rx_buff[3]);
#else
    printf("rx_buff[0]          %X\r\n", spi_dma_debug0_read());
    printf("rx_buff[1]          %X\r\n", spi_dma_debug1_read());
    printf("rx_buff[2]          %X\r\n", spi_dma_debug2_read());
    printf("rx_buff[3]          %X\r\n", spi_dma_debug3_read());
#endif

I don't know why the above code statement could copy the data to the CSRStorages but it couldn't in cases of RAM buffer?

Thanks for your help.

kamejoko80 commented 4 years ago

Hello,

Maybe I know how to resolve the problem, just adding bus.sel.eq(0b1111) in case of memory writing and it works properly now.

francis2tm commented 2 years ago

Hey @kamejoko80, have you managed to make this work but with SDIO?