ULP RISCV SPI Simulation Using Bit-banging

Honzik321
Posts: 52
Joined: Mon Apr 06, 2020 11:17 pm
Location: Czech Republic

ULP RISCV SPI Simulation Using Bit-banging

Postby Honzik321 » Thu Jan 30, 2025 11:13 pm

Dear Espressif programmers,
I would like to use ULP RISCV for SPI Simulation Using Bit-banging with ESP32-S3.
Is it possible to increase the CLK frequency for reading data from an SPI slave device? The ULP runs at 17.5 MHz, but the maximum achieved CLK frequency using the GPIO Bit-banging using my code is 235 kHz (see the attached code).

Optimization of the code is already tested without impact (-Os, -O2, O0). Could implementing it in assembler help? Or is this the maximum speed that can be achieved? Alternatively, can ULP I2C be used for this purpose in any way?


Thank you,
Jan
Attachments
ulp_riscv_example_main.c
(2.31 KiB) Downloaded 70 times
main.c
(5.29 KiB) Downloaded 68 times

MicroController
Posts: 2045
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: ULP RISCV SPI Simulation Using Bit-banging

Postby MicroController » Fri Jan 31, 2025 8:13 am

REG_WRITE(RTC_GPIO_OUT_W1TS_REG, ...) should be faster than REG_SET_FIELD(RTC_GPIO_OUT_W1TS_REG,...).

Note that most ULP instructions take several clock cycles to execute, so the 17.5MHz will hardly result in more than 3-5 MIPS of throughput.

Honzik321
Posts: 52
Joined: Mon Apr 06, 2020 11:17 pm
Location: Czech Republic

Re: ULP RISCV SPI Simulation Using Bit-banging

Postby Honzik321 » Fri Jan 31, 2025 4:29 pm

What is the correct setting by the REG_WRITE ? Commented code below doesn't work (it does probably nothing). The REG_SET_FIELD or ulp_riscv_gpio_output_level(PINx,level) works both with the same clock speed. I would like to improve to about 1 us (1 MHz) GPIO CLK.

// REG_WRITE(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS+0x02);
// REG_WRITE(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TS+0x02);
// WRITE_PERI_REG(RTC_GPIO_OUT_W1TS_REG,0x02);
// WRITE_PERI_REG(RTC_GPIO_OUT_W1TC_REG,0x02);

REG_SET_FIELD(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS, 0x02);
REG_SET_FIELD(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TS, 0x02);

MicroController
Posts: 2045
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: ULP RISCV SPI Simulation Using Bit-banging

Postby MicroController » Fri Jan 31, 2025 4:54 pm

What is the correct setting by the REG_WRITE ?

Code: Select all

REG_WRITE(RTC_GPIO_OUT_W1TS_REG, (BIT(CLK_PIN) << RTC_GPIO_OUT_DATA_W1TS_S));
REG_WRITE(RTC_GPIO_OUT_W1TC_REG, (BIT(CLK_PIN) << RTC_GPIO_OUT_DATA_W1TC_S));
should do the trick. 1MHz will still be challenging since you also have to extract each data bit (~3-4 instructions per bit (load+shift+and+or)).
(See also https://github.com/espressif/esp-idf/is ... 1439418487 - assuming an average of 7 clocks per instruction, i.e. ~2.5MIPS, there's no way you could get anywhere close to 1MHz.)

Honzik321
Posts: 52
Joined: Mon Apr 06, 2020 11:17 pm
Location: Czech Republic

Re: ULP RISCV SPI Simulation Using Bit-banging

Postby Honzik321 » Sat Feb 01, 2025 5:56 pm

Ladies and gentlemen, this way you can generate 1.1 MHz!

Code: Select all

REG_WRITE(RTC_GPIO_OUT_W1TS_REG, (BIT(CLK_PIN) << RTC_GPIO_OUT_DATA_W1TS_S));
REG_WRITE(RTC_GPIO_OUT_W1TC_REG, (BIT(CLK_PIN) << RTC_GPIO_OUT_DATA_W1TC_S));
350 kHz:

Code: Select all

REG_SET_FIELD(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS, 0x02);
REG_SET_FIELD(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TS, 0x02);
or:

Code: Select all

ulp_riscv_gpio_output_level(CLK_PIN, 1);
ulp_riscv_gpio_output_level(CLK_PIN, 0);	
How to get GPIO level by this fastest procedure? Something like REG_READ(...) ?
Attachments
NewFile0.png
NewFile0.png (6.96 KiB) Viewed 5096 times

MicroController
Posts: 2045
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: ULP RISCV SPI Simulation Using Bit-banging

Postby MicroController » Sat Feb 01, 2025 7:30 pm

Honzik321 wrote:
Sat Feb 01, 2025 5:56 pm
How to get GPIO level by this fastest procedure? Something like REG_READ(...) ?
You may save 1 instruction by replacing

Code: Select all

REG_GET_FIELD(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT)
with

Code: Select all

(REG_READ(RTC_GPIO_IN_REG) >> RTC_GPIO_IN_NEXT_S)
As I wrote above, you'll end up with about 2+4=6 instructions per bit (= 1 SPI clock cycle), which, at an estimated average of 2.5MIPS, would result in an SPI clk of around 417kHz.

Honzik321
Posts: 52
Joined: Mon Apr 06, 2020 11:17 pm
Location: Czech Republic

Re: ULP RISCV SPI Simulation Using Bit-banging

Postby Honzik321 » Sat Feb 01, 2025 9:36 pm

How can an array or a block of data be shared between the ULP and the main program? I am unable to share an array for the SPI buffer. All variables are automatically created as uint32_t. I cannot read the array in the main program either via a pointer or by directly accessing its elements. Thank you for any possible solution!

MicroController
Posts: 2045
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: ULP RISCV SPI Simulation Using Bit-banging

Postby MicroController » Sat Feb 01, 2025 9:56 pm

Honzik321 wrote:
Sat Feb 01, 2025 9:36 pm
I cannot read the array in the main program either via a pointer ...
Why not?

Honzik321
Posts: 52
Joined: Mon Apr 06, 2020 11:17 pm
Location: Czech Republic

Re: ULP RISCV SPI Simulation Using Bit-banging

Postby Honzik321 » Sat Feb 01, 2025 11:02 pm

This is the only way how to get the read_buffer elements ? It seems to be correct, it returns the right values. I tried other options to access the read_buffer, but it always returned an error except for this approach...

Code: Select all

            for(int i = 0; i < 27; i++) {
				printf("%lu ", *(uint32_t *) (&ulp_read_buffer + i) );
			}

MicroController
Posts: 2045
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: ULP RISCV SPI Simulation Using Bit-banging

Postby MicroController » Sun Feb 02, 2025 9:35 am

Yep, that's about the only way.
If you want you can try variations like

Code: Select all

volatile uint32_t* const buffer_array = &ulp_read_buffer;
uint32_t x = buffer_array[i];
or

Code: Select all

volatile uint8_t* const buffer_array_u8 = (volatile uint8_t*)&ulp_read_buffer;
uint8_t x = buffer_array_u8[i];
or

Code: Select all

typedef union {
    volatile uint32_t first;
    volatile uint32_t array_u32[27];
    volatile uint8_t array_u8[27*4];
} ulp_buffer_t;

const ulp_buffer_t* const buffer = (ulp_buffer_t*)&ulp_read_buffer;
...

Who is online

Users browsing this forum: No registered users and 119 guests