Mimic 8 bit wide data-bus using dedicated gpio
Mimic 8 bit wide data-bus using dedicated gpio
I have to achieve data bus behavior on ESP32 S3 using dedicated gpio. The goal is to make the same 8 pins with bi-directional capabilities on request. However, making an 8 pins bundle (for example with input enabled) returns handle on that bundle which is fine for reading. Is it possible to reconfigure the same bundle for output purpose (when needed) without destroying such a handle or I have to destroy (reading bundle) and recreate (writing bundle on the same pins) every time I have to read/write on data bus? Keep in mind that any reading or writing to those 8 pads "simulating" the data bus must be performed within a same clock cycle.
(For documentation: The VSCode examples for dedicated gpio are all with pads of fixed behavior. Some pads are output and some are inputs but they do not change their direction behavior at runtime. So those examples are not the best representative which effectively shows those usual things related to data bus like, making data bus for reading, reading byte, making it for writing and writing byte. I understand that it could be done by creating/destroying dedicated gpio bundle every time data bus has to be read/write but it doesn't look as the most efficient way. It is simply too many operations for such a basic thing and any bi-directional communication in between ESP32 and other peripheral chips, using data bus would suffer if switching from in/out direction would cost such a huge amount of clock cycles)
What is the best approach to solve that problem? Is it possible to have, at the same time, two different bundles (using the same pads) where one created handle is for reading and another handle for writing and leaving them permanent all the time and using one when reading from data bus is required and another for writing to data bus?
(For documentation: The VSCode examples for dedicated gpio are all with pads of fixed behavior. Some pads are output and some are inputs but they do not change their direction behavior at runtime. So those examples are not the best representative which effectively shows those usual things related to data bus like, making data bus for reading, reading byte, making it for writing and writing byte. I understand that it could be done by creating/destroying dedicated gpio bundle every time data bus has to be read/write but it doesn't look as the most efficient way. It is simply too many operations for such a basic thing and any bi-directional communication in between ESP32 and other peripheral chips, using data bus would suffer if switching from in/out direction would cost such a huge amount of clock cycles)
What is the best approach to solve that problem? Is it possible to have, at the same time, two different bundles (using the same pads) where one created handle is for reading and another handle for writing and leaving them permanent all the time and using one when reading from data bus is required and another for writing to data bus?
-
- Posts: 1708
- Joined: Mon Oct 17, 2022 7:38 pm
- Location: Europe, Germany
Re: Mimic 8 bit wide data-bus using dedicated gpio
AFAICT this is not possible. There are only 8 dedicated GPIO 'slots' available, and each of them is exclusively allocated by one bundle. This alone makes it impossible to have 8 output + 8 input dedicated GPIOs allocated at the same time.
Do you actually need dedicated GPIO? Note that you can read or write up to 32 GPIO pins at the same time via a single GPIO register access. Maybe the extra latency of a few clock cycles is still acceptable for your use case.
Do you actually need dedicated GPIO? Note that you can read or write up to 32 GPIO pins at the same time via a single GPIO register access. Maybe the extra latency of a few clock cycles is still acceptable for your use case.
-
- Posts: 9730
- Joined: Thu Nov 26, 2015 4:08 am
Re: Mimic 8 bit wide data-bus using dedicated gpio
At least conceptually (as in ignoring what restrictions the driver applies), you could configure both the inputs and outputs to the same GPIOs, and use the OE invert bit in the GPIO matrix to switch between inputs and outputs. That would mean bus turnaround would be pretty slow, however.
Re: Mimic 8 bit wide data-bus using dedicated gpio
Thank for the answers to both of you guys. I hope that some of you experts, at least now see, the needs for internal function (let's say in the matrix or IO or completely independent) which would provide the same functionality as the regular data bus (which is so common on many other microcontrollers) allowing other users to redesign their old hardware designs and allowing them to use ESP32 as replacement for old microcontrollers. There are still many chips out there, where high speed data bus is still MUST have for their working.
@microcontroler, @ESP-Sprite can you provide some link or some examples with usage of function 0x100 in the matrix? Or any other universal (not related to S3 family but workable on raw ESP32) examples related to reading/writing the 8 bunch of pins within the same cycle.
Is it possible for ULP processor to execute instruction(s) which would read/write 8 bits on sequential pads? Some examples on that?
@microcontroler, @ESP-Sprite can you provide some link or some examples with usage of function 0x100 in the matrix? Or any other universal (not related to S3 family but workable on raw ESP32) examples related to reading/writing the 8 bunch of pins within the same cycle.
Is it possible for ULP processor to execute instruction(s) which would read/write 8 bits on sequential pads? Some examples on that?
-
- Posts: 1708
- Joined: Mon Oct 17, 2022 7:38 pm
- Location: Europe, Germany
Re: Mimic 8 bit wide data-bus using dedicated gpio
Could you please narrow down what "high speed" means in your case?There are still many chips out there, where high speed data bus is still MUST have for their working.
In the meantime, check out the S3's LCD and octal-SPI peripherals.
Re: Mimic 8 bit wide data-bus using dedicated gpio
Hi
If you want a really high speed bidirectional bus - see octal spi
if you only need gpio on dedicated gpio, configure input and output gpio to the same pins.
Set GPIO_FUNCx_OEN_SEL -> 1 (Force the output enable signal to be sourced from GPIO_ENABLE_REG[x]).
OEN switching will be done with one write to the GPIO_ENABLE_REG(W1TS/W1TC) register, reading will be available at any time. This is the maximum you can get in gpio exchange mode via CPU.
If you want a really high speed bidirectional bus - see octal spi
if you only need gpio on dedicated gpio, configure input and output gpio to the same pins.
Set GPIO_FUNCx_OEN_SEL -> 1 (Force the output enable signal to be sourced from GPIO_ENABLE_REG[x]).
OEN switching will be done with one write to the GPIO_ENABLE_REG(W1TS/W1TC) register, reading will be available at any time. This is the maximum you can get in gpio exchange mode via CPU.
Re: Mimic 8 bit wide data-bus using dedicated gpio
@MicroController: It would be nice to have 60 MB/s in both directions. @240 MHz it would mean one read (or write) per 4 clock cycles. But anyway, what speeds are achievable by using IO matrix function 0x100? Is there any example of proper usage of that function?
@Ok-Home: I'm not sure I understand where did you perform switching when 8 bits are required as inputs from pads and where is switching when those same GPIOs are required to output 8 bit value. Which register determines in/out direction? What are necessary steps to execute for a list of pads to be configured as an input? And what are necessary steps after that to reconfigure those same pads as outputs? Suppose I want GPIO6 up to GPIO13 (that is 8 bits wide) to set up like you proposed. Can you please write a part for initialization those bits for inputs (and read some value from those pads) then reconfigure them for outputs (and write some byte out to those pads)?
@Ok-Home: I'm not sure I understand where did you perform switching when 8 bits are required as inputs from pads and where is switching when those same GPIOs are required to output 8 bit value. Which register determines in/out direction? What are necessary steps to execute for a list of pads to be configured as an input? And what are necessary steps after that to reconfigure those same pads as outputs? Suppose I want GPIO6 up to GPIO13 (that is 8 bits wide) to set up like you proposed. Can you please write a part for initialization those bits for inputs (and read some value from those pads) then reconfigure them for outputs (and write some byte out to those pads)?
Re: Mimic 8 bit wide data-bus using dedicated gpio
hi
this will help you a little to achieve 60 MB/s
but switching in/out with one entry to the register is below
reading is always on
this will help you a little to achieve 60 MB/s
but switching in/out with one entry to the register is below
reading is always on
Code: Select all
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/dedic_gpio.h"
#include "logic_analyzer_ws_server.h"
#include "soc/gpio_reg.h"
#include "soc/soc.h"
void dedic_task(void *p)
{
const int bundleA_gpios[] = {6, 7,8,9,10,11,12,13};
gpio_config_t io_conf = {
.mode = GPIO_MODE_INPUT_OUTPUT,
.pull_down_en = true,
};
uint64_t oemask = 0; // gpio mask -> enable/disable output
for (int i = 0; i < sizeof(bundleA_gpios) / sizeof(bundleA_gpios[0]); i++)
{
io_conf.pin_bit_mask = 1ULL << bundleA_gpios[i];
gpio_config(&io_conf);
oemask |= io_conf.pin_bit_mask; // gpio mask -> enable/disable output
}
// Create bundleA, output only
dedic_gpio_bundle_handle_t bundleA = NULL;
dedic_gpio_bundle_config_t bundleA_config = {
.gpio_array = bundleA_gpios,
.array_size = sizeof(bundleA_gpios) / sizeof(bundleA_gpios[0]),
.flags = {
.out_en = 1,
.in_en = 1 // input mode already connected
},
};
ESP_ERROR_CHECK(dedic_gpio_new_bundle(&bundleA_config, &bundleA));
// switch OEN control to register GPIO_ENABLE_W1TS_REG/REG_WRITE(GPIO_ENABLE_W1TC_REG
for (int i = 0; i < sizeof(bundleA_gpios) / sizeof(bundleA_gpios[0]); i++)
{
uint32_t tmp = REG_READ(GPIO_FUNC0_OUT_SEL_CFG_REG+bundleA_gpios[i]*4);
REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG+bundleA_gpios[i]*4,tmp | 1<<10);
}
while (1)
{
REG_WRITE(GPIO_ENABLE_W1TS_REG,oemask); // enable output
for (int i = 0; i < 255; i++)
{
dedic_gpio_bundle_write(bundleA, 0xff, i);
}
REG_WRITE(GPIO_ENABLE_W1TC_REG,oemask); // disable output - out data always pulldown
for (int i = 0; i < 255; i++)
{
dedic_gpio_bundle_write(bundleA, 0xff, i);
}
REG_WRITE(GPIO_ENABLE_W1TS_REG,oemask); // enable output
for (int i = 0; i < 255; i++)
{
dedic_gpio_bundle_write(bundleA, 0xff, i);
}
vTaskDelay(1);
}
}
void app_main(void)
{
//logic_analyzer_ws_server();
xTaskCreatePinnedToCore(dedic_task, "dedic", 4096, NULL, 5, NULL, 1);
while (1)
{
vTaskDelay(10);
}
}
Re: Mimic 8 bit wide data-bus using dedicated gpio
Thanks a lot @ok-home.
Such an example (or similar) should be part of TRM because usage of GPIO_ENABLE_W1TS_REG and GPIO_ENABLE_W1TC_REG registers become obvious when 10th bit in GPIO_FUNCn_OUT_SEL_CFG_REG is set and such an example "tells" MORE much faster than jumping through pages in TRM, where description of an operation is explained and pages where involved registers are described. Maybe, if some decides to put it in TRM, part where outputs are disabled, should be followed by routine of READING data bus. It is more logical to read data-bus when it is configured for reading (assuming some external device put some data on it), instead of writing data on it to show that data will not appear on external pads.
Now I am wondering, how close to that approach can be achieved on a simple ESP32 with LX6 core which does not have dedicated IO module implemented (ESP32S3 with LX7 core has such a module)?
I tried to use the same approach (with an assumption that all 8 data lines on external pads are each one after another, to avoid complex masking over scattered indices for the sake of speed). So I come to this working solution
The most critical part is write_db() function, because it needs to read actual GPIO_OUT_REG (you may have other pads beside those used for data bus also configured as GP outputs and you must not overwrite them by zeroes), masks it (to preserve those other pinouts) and then OR with new value. It is critical operation in multi-tasking environment because in the final compiled code it involves several assembler instructions which can not guaranty the integrity of whole operation.
So it would be much better approach (in the case if all 8 bits of data-bus are BYTE aligned within GPIO_OUT_REG) to use just a byte writing instruction into proper byte positioned memory location inside GPIO_OUT_REG. But unfortunately there is no C macro of assembler instruction S8I capable to do that thing. I am not sure how would C compiler compile the following
uint8_t value = 0xAA;
(*(volatile uint8_t *)(GPIO_OUT_REG + 1)) = value;
Would it be reading of 32 bit value, plus shifting, plus masking, plus rewriting or it would be just BYTE writing using S8I assembler instruction? How can I see assembly code generated by C compiler?
Such an example (or similar) should be part of TRM because usage of GPIO_ENABLE_W1TS_REG and GPIO_ENABLE_W1TC_REG registers become obvious when 10th bit in GPIO_FUNCn_OUT_SEL_CFG_REG is set and such an example "tells" MORE much faster than jumping through pages in TRM, where description of an operation is explained and pages where involved registers are described. Maybe, if some decides to put it in TRM, part where outputs are disabled, should be followed by routine of READING data bus. It is more logical to read data-bus when it is configured for reading (assuming some external device put some data on it), instead of writing data on it to show that data will not appear on external pads.
Now I am wondering, how close to that approach can be achieved on a simple ESP32 with LX6 core which does not have dedicated IO module implemented (ESP32S3 with LX7 core has such a module)?
I tried to use the same approach (with an assumption that all 8 data lines on external pads are each one after another, to avoid complex masking over scattered indices for the sake of speed). So I come to this working solution
Code: Select all
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "soc/gpio_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/dport_access.h"
// 8 lines of data bus are assumed to be consecutive pads starting from DATA_BUS_LOWEST_PAD
// in this example lines: GPIO8 to GPIO15
// if you want to change that, change just starting pad number for DATA_BUS_LOWEST_PAD
// also take care not to overlap data-bus with pads required for external flash, PSRAM etc.
#define DATA_BUS_LOWEST_PAD 8
#define SET_MASK (0x000000FF << DATA_BUS_LOWEST_PAD)
#define CLR_MASK (SET_MASK ^ 0xFFFFFFFF)
static __always_inline void make_data_bus_in(void) { DPORT_REG_WRITE(GPIO_ENABLE_W1TC_REG, SET_MASK);}
static __always_inline void make_data_bus_out(void) { DPORT_REG_WRITE(GPIO_ENABLE_W1TS_REG, SET_MASK);}
static __always_inline void write_db(uint8_t val) { DPORT_REG_WRITE(GPIO_OUT_REG, (_DPORT_REG_READ(GPIO_OUT_REG) & CLR_MASK) | (val << DATA_BUS_LOWEST_PAD));}
static __always_inline uint8_t read_db(void){ return (_DPORT_REG_READ(GPIO_IN_REG) >> DATA_BUS_LOWEST_PAD);}
void app_main(void)
{
//CONFIGURATION PART
// unfortunately IO_MUX_GPIOx_REG are not positioned in memory map in ascending order
// so there is no easy way to calculate their address, based on GPIO index (we would needed some remapping array)
// so user have to rename them manually if he changes DATA_BUS_LOWEST_PAD to some other value
// we set: MCU_SEL to 2 (it provides GPIO pad functionality),
// FUN_IE (this bit allows input from a pad to IO mux,
// FUN_WPD (pull down resistors to define data-bus state in the case no external peripheral write to it)
DPORT_REG_WRITE(IO_MUX_GPIO8_REG, 0x00002280);
DPORT_REG_WRITE(IO_MUX_GPIO9_REG, 0x00002280);
DPORT_REG_WRITE(IO_MUX_GPIO10_REG, 0x00002280);
DPORT_REG_WRITE(IO_MUX_GPIO11_REG, 0x00002280);
DPORT_REG_WRITE(IO_MUX_GPIO12_REG, 0x00002280);
DPORT_REG_WRITE(IO_MUX_GPIO13_REG, 0x00002280);
DPORT_REG_WRITE(IO_MUX_GPIO14_REG, 0x00002280);
DPORT_REG_WRITE(IO_MUX_GPIO15_REG, 0x00002280);
// GPIO_FUNCx_OUT_SEL_CFG_REG are consecutive in memory so we can use math to calc proper
// register offset using GPIO index
// we set forced OE (bit 10) and also set matrix function 0x100 (in bits 0 to 8)
DPORT_REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (DATA_BUS_LOWEST_PAD ) * 4 , 0x00000500);
DPORT_REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (DATA_BUS_LOWEST_PAD + 1) * 4 , 0x00000500);
DPORT_REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (DATA_BUS_LOWEST_PAD + 2) * 4 , 0x00000500);
DPORT_REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (DATA_BUS_LOWEST_PAD + 3) * 4 , 0x00000500);
DPORT_REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (DATA_BUS_LOWEST_PAD + 4) * 4 , 0x00000500);
DPORT_REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (DATA_BUS_LOWEST_PAD + 5) * 4 , 0x00000500);
DPORT_REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (DATA_BUS_LOWEST_PAD + 6) * 4 , 0x00000500);
DPORT_REG_WRITE(GPIO_FUNC0_OUT_SEL_CFG_REG + (DATA_BUS_LOWEST_PAD + 7) * 4 , 0x00000500);
// HERE IS THE USAGE PART:
// when you need to put byte(s)
make_data_bus_out();
write_db(0x00);
write_db(0x01);
...
// when you need to read from some external peripheral
uint8_t value;
make_data_bus_in();
// trigger OE on those external peripheral (for example some 8-bit latch or other MCU) to switch its output from tri-state and
// allows it on bus
value = read_db();
// use value
// trigger another device
value = read_db();
...
}
So it would be much better approach (in the case if all 8 bits of data-bus are BYTE aligned within GPIO_OUT_REG) to use just a byte writing instruction into proper byte positioned memory location inside GPIO_OUT_REG. But unfortunately there is no C macro of assembler instruction S8I capable to do that thing. I am not sure how would C compiler compile the following
uint8_t value = 0xAA;
(*(volatile uint8_t *)(GPIO_OUT_REG + 1)) = value;
Would it be reading of 32 bit value, plus shifting, plus masking, plus rewriting or it would be just BYTE writing using S8I assembler instruction? How can I see assembly code generated by C compiler?
-
- Posts: 9730
- Joined: Thu Nov 26, 2015 4:08 am
Re: Mimic 8 bit wide data-bus using dedicated gpio
Your code should result in a S8I instruction. Note that I'm not sure if on all our chips all IO ranges have the byte strobes implemented... in other words, GPIO registers may not respond correctly to anything but 32-bit reads or writes.
Who is online
Users browsing this forum: Baidu [Spider] and 79 guests