libbase/i2c: use busy_wait_us instead of cdelay
`cdelay` function is not a proper thing to count time. It wouldn't count SoC clocks, but CPU clocks. But even then, there are multiple instructions in `cdelay`: - NOP - decrement - branch with compare Assuming each instruction takes exactly 1 CPU cycle it is still wrong, as we wait 3 time longer than requested. But they don't take exactly 1 CPU cycle. CPUs have caches, branch predictors, are out-of-order and so on. So a much better way to count this time would be `busy_wait_us`. I performed some test using vexriscv and Saleae Logic Analyzer: vexriscv variant | requested I2C speed | actual (cdelay) | actual (busy_wait_us) -----------------+---------------------+-----------------+---------------------- minimal | 50 kHz | 4 kHz | 38 kHz minimal | 200 kHz | 15 kHz | 96 kHz minimal | 400 kHz | 28 kHz | 137 kHz -----------------+---------------------+-----------------+---------------------- lite | 50 kHz | 12 kHz | 40 kHz lite | 200 kHz | 43 kHz | 115 kHz lite | 400 kHz | 74 kHz | 180 kHz -----------------+---------------------+-----------------+---------------------- standard | 50 kHz | 12 kHz | 45 kHz standard | 200 kHz | 48 kHz | 159 kHz standard | 400 kHz | 84 kHz | 311 kHz Signed-off-by: Michal Sieron <msieron@antmicro.com>
This commit is contained in:
parent
b122178876
commit
7f829f9e44
|
@ -7,11 +7,14 @@
|
|||
#include <generated/soc.h>
|
||||
#include <generated/csr.h>
|
||||
|
||||
#include <system.h>
|
||||
|
||||
#ifdef CONFIG_HAS_I2C
|
||||
#include <generated/i2c.h>
|
||||
|
||||
#define I2C_PERIOD_CYCLES (CONFIG_CLOCK_FREQUENCY / I2C_FREQ_HZ)
|
||||
#define I2C_DELAY(n) cdelay((n)*I2C_PERIOD_CYCLES/4)
|
||||
#define U_SECOND (1000000)
|
||||
#define I2C_PERIOD (U_SECOND / I2C_FREQ_HZ)
|
||||
#define I2C_DELAY(n) busy_wait_us(n * I2C_PERIOD / 4)
|
||||
|
||||
int current_i2c_dev = DEFAULT_I2C_DEV;
|
||||
|
||||
|
@ -20,14 +23,6 @@ int get_i2c_devs_count(void) { return I2C_DEVS_COUNT; }
|
|||
void set_i2c_active_dev(int dev) { current_i2c_dev = dev; }
|
||||
int get_i2c_active_dev(void) { return current_i2c_dev; }
|
||||
|
||||
static inline void cdelay(int i)
|
||||
{
|
||||
while(i > 0) {
|
||||
__asm__ volatile(CONFIG_CPU_NOP);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
int i2c_send_init_cmds(void)
|
||||
{
|
||||
#ifdef I2C_INIT
|
||||
|
|
Loading…
Reference in New Issue