#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <generated/csr.h>
define_command(memtest, memtest, "Run a memory test", LITEDRAM_CMDS);
#endif
+/**
+ * Command "i2creset"
+ *
+ * Reset I2C line state in case a slave locks the line.
+ *
+ */
+#ifdef CSR_I2C_BASE
+define_command(i2creset, i2c_reset, "Reset I2C line state", LITEDRAM_CMDS);
+#endif
+
+/**
+ * Command "i2cwr"
+ *
+ * Write I2C slave memory using 7-bit slave address and 8-bit memory address.
+ *
+ */
+#ifdef CSR_I2C_BASE
+static void i2cwr_handler(int nb_params, char **params)
+{
+ int i;
+ char *c;
+ unsigned char write_params[32]; // also indirectly limited by CMD_LINE_BUFFER_SIZE
+
+ if (nb_params < 2) {
+ printf("i2cwr <slaveaddr7bit> <addr> [<data>, ...]");
+ return;
+ }
+
+ if (nb_params - 1 > sizeof(write_params)) {
+ printf("Max data length is %d", sizeof(write_params));
+ return;
+ }
+
+ for (i = 0; i < nb_params; ++i) {
+ write_params[i] = strtoul(params[i], &c, 0);
+ if (*c != 0) {
+ printf("Incorrect value of parameter %d", i);
+ return;
+ }
+ }
+
+ if (!i2c_write(write_params[0], write_params[1], &write_params[2], nb_params - 2)) {
+ printf("Error during I2C write");
+ return;
+ }
+}
+define_command(i2cwr, i2cwr_handler, "Write over I2C", LITEDRAM_CMDS);
+#endif
+
+/**
+ * Command "i2crd"
+ *
+ * Read I2C slave memory using 7-bit slave address and 8-bit memory address.
+ *
+ */
+#ifdef CSR_I2C_BASE
+static void i2crd_handler(int nb_params, char **params)
+{
+ char *c;
+ int len;
+ unsigned char slave_addr, addr;
+ unsigned char buf[256];
+ bool send_stop = true;
+
+ if (nb_params < 3) {
+ printf("i2crd <slaveaddr7bit> <addr> <len> [<send_stop>]");
+ return;
+ }
+
+ slave_addr = strtoul(params[0], &c, 0);
+ if (*c != 0) {
+ printf("Incorrect slave address");
+ return;
+ }
+
+ addr = strtoul(params[1], &c, 0);
+ if (*c != 0) {
+ printf("Incorrect memory address");
+ return;
+ }
+
+ len = strtoul(params[2], &c, 0);
+ if (*c != 0) {
+ printf("Incorrect data length");
+ return;
+ }
+ if (len > sizeof(buf)) {
+ printf("Max data count is %d", sizeof(buf));
+ return;
+ }
+
+ if (nb_params > 3) {
+ send_stop = strtoul(params[3], &c, 0) != 0;
+ if (*c != 0) {
+ printf("Incorrect send_stop value");
+ return;
+ }
+ }
+
+ if (!i2c_read(slave_addr, addr, buf, len, send_stop)) {
+ printf("Error during I2C read");
+ return;
+ }
+
+ dump_bytes((unsigned int *) buf, len, addr);
+}
+define_command(i2crd, i2crd_handler, "Read over I2C", LITEDRAM_CMDS);
+#endif
/**
* Command "spdread"
*
* Read contents of SPD EEPROM memory.
- * SPD address is defined by the pins A0, A1, A2.
+ * SPD address is a 3-bit address defined by the pins A0, A1, A2.
*
*/
#ifdef CSR_I2C_BASE
+#define SPD_RW_PREAMBLE 0b1010
+#define SPD_RW_ADDR(a210) ((SPD_RW_PREAMBLE << 3) | ((a210) & 0b111))
+
static void spdread_handler(int nb_params, char **params)
{
- unsigned char buf[256];
- unsigned int spdaddr;
- int length = sizeof(buf);
char *c;
+ unsigned char spdaddr;
+ unsigned char buf[256];
+ int len = sizeof(buf);
+ bool send_stop = true;
if (nb_params < 1) {
- printf("spdread <spdaddr> [<length>]");
+ printf("spdread <spdaddr> [<send_stop>]");
return;
}
}
if (nb_params > 1) {
- length = strtoul(params[1], &c, 0);
+ send_stop = strtoul(params[1], &c, 0) != 0;
if (*c != 0) {
- printf("Incorrect address");
- return;
- }
- if (length > sizeof(buf)) {
- printf("Max length is %d", sizeof(buf));
+ printf("Incorrect send_stop value");
return;
}
}
- if (!spdread(spdaddr, 0, buf, length)) {
+ if (!i2c_read(SPD_RW_ADDR(spdaddr), 0, buf, len, send_stop)) {
printf("Error when reading SPD EEPROM");
return;
}
- dump_bytes((unsigned int *) buf, length, 0);
+ dump_bytes((unsigned int *) buf, len, 0);
}
define_command(spdread, spdread_handler, "Read SPD EEPROM", LITEDRAM_CMDS);
#endif
include ../include/generated/variables.mak
include $(SOC_DIRECTORY)/software/common.mak
-OBJECTS = sdram.o spd.o
+OBJECTS = sdram.o i2c.o
all: liblitedram.a
--- /dev/null
+// This file is Copyright (c) 2020 Antmicro <www.antmicro.com>
+
+#include <stdio.h>
+#include "i2c.h"
+
+#ifdef CSR_I2C_BASE
+
+#define I2C_PERIOD_CYCLES (CONFIG_CLOCK_FREQUENCY / I2C_FREQ_HZ)
+#define I2C_DELAY(n) cdelay((n)*I2C_PERIOD_CYCLES/4)
+
+static inline void cdelay(int i)
+{
+ while(i > 0) {
+ __asm__ volatile(CONFIG_CPU_NOP);
+ i--;
+ }
+}
+
+static inline void i2c_oe_scl_sda(bool oe, bool scl, bool sda)
+{
+ i2c_w_write(
+ ((oe & 1) << CSR_I2C_W_OE_OFFSET) |
+ ((scl & 1) << CSR_I2C_W_SCL_OFFSET) |
+ ((sda & 1) << CSR_I2C_W_SDA_OFFSET)
+ );
+}
+
+// START condition: 1-to-0 transition of SDA when SCL is 1
+static void i2c_start(void)
+{
+ i2c_oe_scl_sda(1, 1, 1);
+ I2C_DELAY(1);
+ i2c_oe_scl_sda(1, 1, 0);
+ I2C_DELAY(1);
+ i2c_oe_scl_sda(1, 0, 0);
+ I2C_DELAY(1);
+}
+
+// STOP condition: 0-to-1 transition of SDA when SCL is 1
+static void i2c_stop(void)
+{
+ i2c_oe_scl_sda(1, 0, 0);
+ I2C_DELAY(1);
+ i2c_oe_scl_sda(1, 1, 0);
+ I2C_DELAY(1);
+ i2c_oe_scl_sda(1, 1, 1);
+ I2C_DELAY(1);
+ i2c_oe_scl_sda(0, 1, 1);
+}
+
+// Call when in the middle of SCL low, advances one clk period
+static void i2c_transmit_bit(int value)
+{
+ i2c_oe_scl_sda(1, 0, value);
+ I2C_DELAY(1);
+ i2c_oe_scl_sda(1, 1, value);
+ I2C_DELAY(2);
+ i2c_oe_scl_sda(1, 0, value);
+ I2C_DELAY(1);
+ i2c_oe_scl_sda(0, 0, 0); // release line
+}
+
+// Call when in the middle of SCL low, advances one clk period
+static int i2c_receive_bit(void)
+{
+ int value;
+ i2c_oe_scl_sda(0, 0, 0);
+ I2C_DELAY(1);
+ i2c_oe_scl_sda(0, 1, 0);
+ I2C_DELAY(1);
+ // read in the middle of SCL high
+ value = i2c_r_read() & 1;
+ I2C_DELAY(1);
+ i2c_oe_scl_sda(0, 0, 0);
+ I2C_DELAY(1);
+ return value;
+}
+
+// Send data byte and return 1 if slave sends ACK
+static bool i2c_transmit_byte(unsigned char data)
+{
+ int i;
+ int ack;
+
+ // SCL should have already been low for 1/4 cycle
+ i2c_oe_scl_sda(0, 0, 0);
+ for (i = 0; i < 8; ++i) {
+ // MSB first
+ i2c_transmit_bit((data & (1 << 7)) != 0);
+ data <<= 1;
+ }
+ ack = i2c_receive_bit();
+
+ // 0 from slave means ack
+ return ack == 0;
+}
+
+// Read data byte and send ACK if ack=1
+static unsigned char i2c_receive_byte(bool ack)
+{
+ int i;
+ unsigned char data = 0;
+
+ for (i = 0; i < 8; ++i) {
+ data <<= 1;
+ data |= i2c_receive_bit();
+ }
+ i2c_transmit_bit(!ack);
+
+ return data;
+}
+
+// Reset line state
+void i2c_reset(void)
+{
+ int i;
+ i2c_oe_scl_sda(1, 1, 1);
+ I2C_DELAY(8);
+ for (i = 0; i < 9; ++i) {
+ i2c_oe_scl_sda(1, 0, 1);
+ I2C_DELAY(2);
+ i2c_oe_scl_sda(1, 1, 1);
+ I2C_DELAY(2);
+ }
+ i2c_oe_scl_sda(0, 0, 1);
+ I2C_DELAY(1);
+ i2c_stop();
+ i2c_oe_scl_sda(0, 1, 1);
+ I2C_DELAY(8);
+}
+
+/*
+ * Read slave memory over I2C starting at given address
+ *
+ * First writes the memory starting address, then reads the data:
+ * START WR(slaveaddr) WR(addr) STOP START WR(slaveaddr) RD(data) RD(data) ... STOP
+ * Some chips require that after transmiting the address, there will be no STOP in between:
+ * START WR(slaveaddr) WR(addr) START WR(slaveaddr) RD(data) RD(data) ... STOP
+ */
+bool i2c_read(unsigned char slave_addr, unsigned char addr, unsigned char *data, unsigned int len, bool send_stop)
+{
+ int i;
+
+ i2c_start();
+
+ if(!i2c_transmit_byte(I2C_ADDR_WR(slave_addr))) {
+ i2c_stop();
+ return false;
+ }
+ if(!i2c_transmit_byte(addr)) {
+ i2c_stop();
+ return false;
+ }
+
+ if (send_stop) {
+ i2c_stop();
+ }
+ i2c_start();
+
+ if(!i2c_transmit_byte(I2C_ADDR_RD(slave_addr))) {
+ i2c_stop();
+ return false;
+ }
+ for (i = 0; i < len; ++i) {
+ data[i] = i2c_receive_byte(i != len - 1);
+ }
+
+ i2c_stop();
+
+ return true;
+}
+
+/*
+ * Write slave memory over I2C starting at given address
+ *
+ * First writes the memory starting address, then writes the data:
+ * START WR(slaveaddr) WR(addr) WR(data) WR(data) ... STOP
+ */
+bool i2c_write(unsigned char slave_addr, unsigned char addr, const unsigned char *data, unsigned int len)
+{
+ int i;
+
+ i2c_start();
+
+ if(!i2c_transmit_byte(I2C_ADDR_WR(slave_addr))) {
+ i2c_stop();
+ return false;
+ }
+ if(!i2c_transmit_byte(addr)) {
+ i2c_stop();
+ return false;
+ }
+ for (i = 0; i < len; ++i) {
+ if(!i2c_transmit_byte(data[i])) {
+ i2c_stop();
+ return false;
+ }
+ }
+
+ i2c_stop();
+
+ return true;
+}
+
+#endif /* CSR_I2C_BASE */
--- /dev/null
+#ifndef __I2C_H
+#define __I2C_H
+
+#include <stdbool.h>
+#include <generated/csr.h>
+
+/* I2C frequency defaults to a safe value in range 10-100 kHz to be compatible with SMBus */
+#ifndef I2C_FREQ_HZ
+#define I2C_FREQ_HZ 50000
+#endif
+
+#define I2C_ADDR_WR(addr) ((addr) << 1)
+#define I2C_ADDR_RD(addr) (((addr) << 1) | 1u)
+
+void i2c_reset(void);
+bool i2c_write(unsigned char slave_addr, unsigned char addr, const unsigned char *data, unsigned int len);
+bool i2c_read(unsigned char slave_addr, unsigned char addr, unsigned char *data, unsigned int len, bool send_stop);
+
+#endif /* __I2C_H */
#define __SDRAM_H
#include <generated/csr.h>
-#include "spd.h"
+#include "i2c.h"
void sdrsw(void);
void sdrhw(void);
+++ /dev/null
-// This file is Copyright (c) 2020 Antmicro <www.antmicro.com>
-
-#include <stdio.h>
-#include "spd.h"
-
-#ifdef CSR_I2C_BASE
-
-// SMBus uses frequency 10-100 kHz
-#define I2C_FREQ_HZ 50000
-#define I2C_PERIOD_CYCLES (CONFIG_CLOCK_FREQUENCY / I2C_FREQ_HZ)
-#define I2C_DELAY(n) cdelay((n)*I2C_PERIOD_CYCLES/4)
-
-static void cdelay(int i)
-{
- while(i > 0) {
- __asm__ volatile(CONFIG_CPU_NOP);
- i--;
- }
-}
-
-static void i2c_oe_scl_sda(int oe, int scl, int sda)
-{
- i2c_w_write(
- ((oe & 1) << CSR_I2C_W_OE_OFFSET) |
- ((scl & 1) << CSR_I2C_W_SCL_OFFSET) |
- ((sda & 1) << CSR_I2C_W_SDA_OFFSET)
- );
-}
-
-
-// START condition: 1-to-0 transition of SDA when SCL is 1
-static void i2c_start(void)
-{
- i2c_oe_scl_sda(1, 1, 1);
- I2C_DELAY(1);
- i2c_oe_scl_sda(1, 1, 0);
- I2C_DELAY(1);
- i2c_oe_scl_sda(1, 0, 0);
- I2C_DELAY(1);
-}
-
-// STOP condition: 0-to-1 transition of SDA when SCL is 1
-static void i2c_stop(void)
-{
- i2c_oe_scl_sda(1, 0, 0);
- I2C_DELAY(1);
- i2c_oe_scl_sda(1, 1, 0);
- I2C_DELAY(1);
- i2c_oe_scl_sda(1, 1, 1);
- I2C_DELAY(1);
- i2c_oe_scl_sda(0, 1, 1);
-}
-
-// Reset line state
-static void i2c_reset(void)
-{
- int i;
- i2c_oe_scl_sda(1, 1, 1);
- I2C_DELAY(8);
- for (i = 0; i < 9; ++i) {
- i2c_oe_scl_sda(1, 0, 1);
- I2C_DELAY(2);
- i2c_oe_scl_sda(1, 1, 1);
- I2C_DELAY(2);
- }
- i2c_oe_scl_sda(0, 0, 1);
- I2C_DELAY(1);
- i2c_stop();
- i2c_oe_scl_sda(0, 1, 1);
- I2C_DELAY(8);
-}
-
-// Call when in the middle of SCL low, advances one clk period
-static void i2c_transmit_bit(int value)
-{
- i2c_oe_scl_sda(1, 0, value);
- I2C_DELAY(1);
- i2c_oe_scl_sda(1, 1, value);
- I2C_DELAY(2);
- i2c_oe_scl_sda(1, 0, value);
- I2C_DELAY(1);
- i2c_oe_scl_sda(0, 0, 0); // release line
-}
-
-// Call when in the middle of SCL low, advances one clk period
-static int i2c_receive_bit(void)
-{
- int value;
- i2c_oe_scl_sda(0, 0, 0);
- I2C_DELAY(1);
- i2c_oe_scl_sda(0, 1, 0);
- I2C_DELAY(1);
- // read in the middle of SCL high
- value = i2c_r_read() & 1;
- I2C_DELAY(1);
- i2c_oe_scl_sda(0, 0, 0);
- I2C_DELAY(1);
- return value;
-}
-
-// Send data byte and return 1 if slave sends ACK
-static int i2c_transmit(unsigned char data)
-{
- int ack;
- int i;
-
- // SCL should have already been low for 1/4 cycle
- i2c_oe_scl_sda(0, 0, 0);
- for (i = 0; i < 8; ++i) {
- // MSB first
- i2c_transmit_bit((data & (1 << 7)) != 0);
- data <<= 1;
- }
- ack = i2c_receive_bit();
-
- // 0 from slave means ack
- return ack == 0;
-}
-
-// Read data byte and send ACK if ack=1
-static unsigned char i2c_receive(int ack)
-{
- unsigned char data = 0;
- int i;
-
- i2c_oe_scl_sda(0, 0, 0);
- I2C_DELAY(1);
- for (i = 0; i < 8; ++i) {
- data <<= 1;
- data |= i2c_receive_bit();
- }
- i2c_transmit_bit(!ack);
-
- return data;
-}
-
-
-#define ADDR_PREAMBLE_RW 0b1010
-#define ADDR_7BIT(addr) ((ADDR_PREAMBLE_RW << 3) | ((addr) & 0b111))
-#define ADDR_WRITE(addr) ((ADDR_7BIT(addr) << 1) & (~1u))
-#define ADDR_READ(addr) ((ADDR_7BIT(addr) << 1) | 1u)
-
-/*
- * Read SPD memory content
- *
- * spdaddr: address of SPD EEPROM defined by pins A0, A1, A2
- * addr: memory starting address
- */
-int spdread(unsigned int spdaddr, unsigned int addr, unsigned char *buf, unsigned int len) {
- int i;
-
- i2c_reset();
-
- // To read from random address, we have to first send a "data-less" WRITE,
- // followed by START condition with a READ (no STOP condition)
- i2c_start();
-
- if(!i2c_transmit(ADDR_WRITE(spdaddr))) {
- i2c_reset();
- return 0;
- }
- if(!i2c_transmit(addr)) {
- i2c_reset();
- return 0;
- }
-
- I2C_DELAY(1);
- i2c_start();
- if(!i2c_transmit(ADDR_READ(spdaddr))) {
- i2c_reset();
- return 0;
- }
- for (i = 0; i < len; ++i) {
- buf[i] = i2c_receive(i != len - 1);
- }
- i2c_stop();
-
- return 1;
-}
-#endif /* CSR_I2C_BASE */
+++ /dev/null
-#ifndef __SPD_H
-#define __SPD_H
-
-#include <generated/csr.h>
-
-int spdread(unsigned int spdaddr, unsigned int addr, unsigned char *buf, unsigned int len);
-
-#endif /* __SPD_H */