From 8fd3e74ec99e75e776548b7c94ae893e83c1f7fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Thu, 21 May 2020 14:07:42 +0200 Subject: [PATCH 1/5] bios/sdram: add firmware for reading SPD EEPROM --- litex/soc/software/liblitedram/Makefile | 2 +- litex/soc/software/liblitedram/sdram.h | 1 + litex/soc/software/liblitedram/spd.c | 180 ++++++++++++++++++++++++ litex/soc/software/liblitedram/spd.h | 8 ++ 4 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 litex/soc/software/liblitedram/spd.c create mode 100644 litex/soc/software/liblitedram/spd.h diff --git a/litex/soc/software/liblitedram/Makefile b/litex/soc/software/liblitedram/Makefile index 070263252..46d71339a 100644 --- a/litex/soc/software/liblitedram/Makefile +++ b/litex/soc/software/liblitedram/Makefile @@ -1,7 +1,7 @@ include ../include/generated/variables.mak include $(SOC_DIRECTORY)/software/common.mak -OBJECTS=sdram.o +OBJECTS = sdram.o spd.o all: liblitedram.a diff --git a/litex/soc/software/liblitedram/sdram.h b/litex/soc/software/liblitedram/sdram.h index 21433c03c..0c8cae9e6 100644 --- a/litex/soc/software/liblitedram/sdram.h +++ b/litex/soc/software/liblitedram/sdram.h @@ -2,6 +2,7 @@ #define __SDRAM_H #include +#include "spd.h" void sdrsw(void); void sdrhw(void); diff --git a/litex/soc/software/liblitedram/spd.c b/litex/soc/software/liblitedram/spd.c new file mode 100644 index 000000000..3f396e782 --- /dev/null +++ b/litex/soc/software/liblitedram/spd.c @@ -0,0 +1,180 @@ +// This file is Copyright (c) 2020 Antmicro + +#include +#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 */ diff --git a/litex/soc/software/liblitedram/spd.h b/litex/soc/software/liblitedram/spd.h new file mode 100644 index 000000000..54b04e349 --- /dev/null +++ b/litex/soc/software/liblitedram/spd.h @@ -0,0 +1,8 @@ +#ifndef __SPD_H +#define __SPD_H + +#include + +int spdread(unsigned int spdaddr, unsigned int addr, unsigned char *buf, unsigned int len); + +#endif /* __SPD_H */ From a42dc974010828ab5ae98d49b69a1d3f420321ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Thu, 21 May 2020 14:09:46 +0200 Subject: [PATCH 2/5] bios/sdram: add BIOS command for reading SPD --- litex/soc/software/bios/cmds/cmd_litedram.c | 53 +++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/litex/soc/software/bios/cmds/cmd_litedram.c b/litex/soc/software/bios/cmds/cmd_litedram.c index 0b9ea2280..1fcce272a 100644 --- a/litex/soc/software/bios/cmds/cmd_litedram.c +++ b/litex/soc/software/bios/cmds/cmd_litedram.c @@ -221,3 +221,56 @@ define_command(sdrlevel, sdrlevel, "Perform read/write leveling", LITEDRAM_CMDS) #ifdef CSR_SDRAM_BASE define_command(memtest, memtest, "Run a memory test", LITEDRAM_CMDS); #endif + + +/** + * Command "spdread" + * + * Read contents of SPD EEPROM memory. + * SPD address is defined by the pins A0, A1, A2. + * + */ +#ifdef CSR_I2C_BASE +static void spdread_handler(int nb_params, char **params) +{ + unsigned char buf[256]; + unsigned int spdaddr; + int length = sizeof(buf); + char *c; + + if (nb_params < 1) { + printf("spdread []"); + return; + } + + spdaddr = strtoul(params[0], &c, 0); + if (*c != 0) { + printf("Incorrect address"); + return; + } + if (spdaddr > 0b111) { + printf("SPD EEPROM max address is 0b111 (defined by A0, A1, A2 pins)"); + return; + } + + if (nb_params > 1) { + length = strtoul(params[1], &c, 0); + if (*c != 0) { + printf("Incorrect address"); + return; + } + if (length > sizeof(buf)) { + printf("Max length is %d", sizeof(buf)); + return; + } + } + + if (!spdread(spdaddr, 0, buf, length)) { + printf("Error when reading SPD EEPROM"); + return; + } + + dump_bytes((unsigned int *) buf, length, 0); +} +define_command(spdread, spdread_handler, "Read SPD EEPROM", LITEDRAM_CMDS); +#endif From bdc7eb5c48eae78b1e6bf30a09e0f726bbdd41a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Thu, 21 May 2020 16:19:28 +0200 Subject: [PATCH 3/5] litex_sim: load SPD data from files in hexdump format as printed in BIOS --- litex/tools/litex_sim.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/litex/tools/litex_sim.py b/litex/tools/litex_sim.py index 987681d33..e8fc25a98 100755 --- a/litex/tools/litex_sim.py +++ b/litex/tools/litex_sim.py @@ -19,6 +19,7 @@ from litex.soc.integration.builder import * from litex.soc.integration.soc import * from litedram import modules as litedram_modules +from litedram.modules import parse_spd_hexdump from litedram.common import * from litedram.phy.model import SDRAMPHYModel @@ -298,7 +299,7 @@ def main(): parser.add_argument("--sdram-module", default="MT48LC16M16", help="Select SDRAM chip") parser.add_argument("--sdram-data-width", default=32, help="Set SDRAM chip data width") parser.add_argument("--sdram-init", default=None, help="SDRAM init file") - parser.add_argument("--sdram-from-spd-data", default=None, help="Generate SDRAM module based on SPD data from file") + parser.add_argument("--sdram-from-spd-dump", default=None, help="Generate SDRAM module based on data from SPD EEPROM dump") parser.add_argument("--sdram-verbosity", default=0, help="Set SDRAM checker verbosity") parser.add_argument("--with-ethernet", action="store_true", help="Enable Ethernet support") parser.add_argument("--with-etherbone", action="store_true", help="Enable Etherbone support") @@ -337,9 +338,8 @@ def main(): soc_kwargs["sdram_module"] = args.sdram_module soc_kwargs["sdram_data_width"] = int(args.sdram_data_width) soc_kwargs["sdram_verbosity"] = int(args.sdram_verbosity) - if args.sdram_from_spd_data: - with open(args.sdram_from_spd_data, "rb") as f: - soc_kwargs["sdram_spd_data"] = [int(b) for b in f.read()] + if args.sdram_from_spd_dump: + soc_kwargs["sdram_spd_data"] = parse_spd_hexdump(args.sdram_from_spd_dump) if args.with_ethernet or args.with_etherbone: sim_config.add_module("ethernet", "eth", args={"interface": "tap0", "ip": args.remote_ip}) From 472bf9ac7196e77c405d99c28696f60f007d5dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Wed, 27 May 2020 08:52:24 +0200 Subject: [PATCH 4/5] bios/sdram: expose I2C functions --- litex/soc/software/bios/cmds/cmd_litedram.c | 135 +++++++++++++++-- litex/soc/software/liblitedram/Makefile | 2 +- .../soc/software/liblitedram/{spd.c => i2c.c} | 141 +++++++++++------- litex/soc/software/liblitedram/i2c.h | 19 +++ litex/soc/software/liblitedram/sdram.h | 2 +- litex/soc/software/liblitedram/spd.h | 8 - 6 files changed, 226 insertions(+), 81 deletions(-) rename litex/soc/software/liblitedram/{spd.c => i2c.c} (57%) create mode 100644 litex/soc/software/liblitedram/i2c.h delete mode 100644 litex/soc/software/liblitedram/spd.h diff --git a/litex/soc/software/bios/cmds/cmd_litedram.c b/litex/soc/software/bios/cmds/cmd_litedram.c index 1fcce272a..17d0bd17f 100644 --- a/litex/soc/software/bios/cmds/cmd_litedram.c +++ b/litex/soc/software/bios/cmds/cmd_litedram.c @@ -2,6 +2,7 @@ #include #include +#include #include @@ -222,24 +223,136 @@ define_command(sdrlevel, sdrlevel, "Perform read/write leveling", LITEDRAM_CMDS) 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 [, ...]"); + 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 []"); + 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 []"); + printf("spdread []"); return; } @@ -254,23 +367,19 @@ static void spdread_handler(int nb_params, char **params) } 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 diff --git a/litex/soc/software/liblitedram/Makefile b/litex/soc/software/liblitedram/Makefile index 46d71339a..c6113c0ab 100644 --- a/litex/soc/software/liblitedram/Makefile +++ b/litex/soc/software/liblitedram/Makefile @@ -1,7 +1,7 @@ include ../include/generated/variables.mak include $(SOC_DIRECTORY)/software/common.mak -OBJECTS = sdram.o spd.o +OBJECTS = sdram.o i2c.o all: liblitedram.a diff --git a/litex/soc/software/liblitedram/spd.c b/litex/soc/software/liblitedram/i2c.c similarity index 57% rename from litex/soc/software/liblitedram/spd.c rename to litex/soc/software/liblitedram/i2c.c index 3f396e782..af40b8f49 100644 --- a/litex/soc/software/liblitedram/spd.c +++ b/litex/soc/software/liblitedram/i2c.c @@ -1,16 +1,14 @@ // This file is Copyright (c) 2020 Antmicro #include -#include "spd.h" +#include "i2c.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) +#define I2C_DELAY(n) cdelay((n)*I2C_PERIOD_CYCLES/4) -static void cdelay(int i) +static inline void cdelay(int i) { while(i > 0) { __asm__ volatile(CONFIG_CPU_NOP); @@ -18,16 +16,15 @@ static void cdelay(int i) } } -static void i2c_oe_scl_sda(int oe, int scl, int sda) +static inline void i2c_oe_scl_sda(bool oe, bool scl, bool sda) { i2c_w_write( - ((oe & 1) << CSR_I2C_W_OE_OFFSET) | + ((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) { @@ -51,25 +48,6 @@ static void i2c_stop(void) 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) { @@ -99,10 +77,10 @@ static int i2c_receive_bit(void) } // Send data byte and return 1 if slave sends ACK -static int i2c_transmit(unsigned char data) +static bool i2c_transmit_byte(unsigned char data) { - int ack; int i; + int ack; // SCL should have already been low for 1/4 cycle i2c_oe_scl_sda(0, 0, 0); @@ -118,13 +96,11 @@ static int i2c_transmit(unsigned char data) } // Read data byte and send ACK if ack=1 -static unsigned char i2c_receive(int ack) +static unsigned char i2c_receive_byte(bool ack) { - unsigned char data = 0; int i; + unsigned char data = 0; - i2c_oe_scl_sda(0, 0, 0); - I2C_DELAY(1); for (i = 0; i < 8; ++i) { data <<= 1; data |= i2c_receive_bit(); @@ -134,47 +110,96 @@ static unsigned char i2c_receive(int 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) +// 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 SPD memory content + * Read slave memory over I2C starting at given address * - * spdaddr: address of SPD EEPROM defined by pins A0, A1, A2 - * addr: memory starting 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 */ -int spdread(unsigned int spdaddr, unsigned int addr, unsigned char *buf, unsigned int len) { +bool i2c_read(unsigned char slave_addr, unsigned char addr, unsigned char *data, unsigned int len, bool send_stop) +{ 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_byte(I2C_ADDR_WR(slave_addr))) { + i2c_stop(); + return false; } - if(!i2c_transmit(addr)) { - i2c_reset(); - return 0; + if(!i2c_transmit_byte(addr)) { + i2c_stop(); + return false; } - I2C_DELAY(1); + if (send_stop) { + i2c_stop(); + } i2c_start(); - if(!i2c_transmit(ADDR_READ(spdaddr))) { - i2c_reset(); - return 0; + + if(!i2c_transmit_byte(I2C_ADDR_RD(slave_addr))) { + i2c_stop(); + return false; } for (i = 0; i < len; ++i) { - buf[i] = i2c_receive(i != len - 1); + data[i] = i2c_receive_byte(i != len - 1); } + i2c_stop(); - return 1; + 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 */ diff --git a/litex/soc/software/liblitedram/i2c.h b/litex/soc/software/liblitedram/i2c.h new file mode 100644 index 000000000..c0cc2bfe6 --- /dev/null +++ b/litex/soc/software/liblitedram/i2c.h @@ -0,0 +1,19 @@ +#ifndef __I2C_H +#define __I2C_H + +#include +#include + +/* 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 */ diff --git a/litex/soc/software/liblitedram/sdram.h b/litex/soc/software/liblitedram/sdram.h index 0c8cae9e6..7deb82e39 100644 --- a/litex/soc/software/liblitedram/sdram.h +++ b/litex/soc/software/liblitedram/sdram.h @@ -2,7 +2,7 @@ #define __SDRAM_H #include -#include "spd.h" +#include "i2c.h" void sdrsw(void); void sdrhw(void); diff --git a/litex/soc/software/liblitedram/spd.h b/litex/soc/software/liblitedram/spd.h deleted file mode 100644 index 54b04e349..000000000 --- a/litex/soc/software/liblitedram/spd.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __SPD_H -#define __SPD_H - -#include - -int spdread(unsigned int spdaddr, unsigned int addr, unsigned char *buf, unsigned int len); - -#endif /* __SPD_H */ From 1172c10afb7955109890f844641c903d890d4ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Wed, 27 May 2020 15:13:16 +0200 Subject: [PATCH 5/5] bios: move I2C from liblitedram to libbase --- litex/soc/software/bios/Makefile | 1 + litex/soc/software/bios/cmds/cmd_i2c.c | 121 ++++++++++++++++++ litex/soc/software/bios/cmds/cmd_litedram.c | 110 +--------------- litex/soc/software/bios/command.h | 1 + .../{liblitedram => include/base}/i2c.h | 9 +- litex/soc/software/libbase/Makefile | 4 +- .../software/{liblitedram => libbase}/i2c.c | 5 +- litex/soc/software/liblitedram/Makefile | 2 +- litex/soc/software/liblitedram/sdram.h | 1 - 9 files changed, 137 insertions(+), 117 deletions(-) create mode 100644 litex/soc/software/bios/cmds/cmd_i2c.c rename litex/soc/software/{liblitedram => include/base}/i2c.h (88%) rename litex/soc/software/{liblitedram => libbase}/i2c.c (98%) diff --git a/litex/soc/software/bios/Makefile b/litex/soc/software/bios/Makefile index 0e288a43b..4ab4a7d56 100755 --- a/litex/soc/software/bios/Makefile +++ b/litex/soc/software/bios/Makefile @@ -17,6 +17,7 @@ OBJECTS = isr.o \ cmd_bios.o \ cmd_mem.o \ cmd_boot.o \ + cmd_i2c.o \ cmd_spiflash.o \ cmd_litedram.o \ cmd_liteeth.o \ diff --git a/litex/soc/software/bios/cmds/cmd_i2c.c b/litex/soc/software/bios/cmds/cmd_i2c.c new file mode 100644 index 000000000..6c53b76cc --- /dev/null +++ b/litex/soc/software/bios/cmds/cmd_i2c.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: BSD-Source-Code + +#include +#include +#include + +#include +#include + +#include "../command.h" +#include "../helpers.h" + + +/** + * 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", I2C_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 [, ...]"); + 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", I2C_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 []"); + 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", I2C_CMDS); +#endif diff --git a/litex/soc/software/bios/cmds/cmd_litedram.c b/litex/soc/software/bios/cmds/cmd_litedram.c index 17d0bd17f..026ab882e 100644 --- a/litex/soc/software/bios/cmds/cmd_litedram.c +++ b/litex/soc/software/bios/cmds/cmd_litedram.c @@ -5,6 +5,7 @@ #include #include +#include #include "sdram.h" @@ -223,115 +224,6 @@ define_command(sdrlevel, sdrlevel, "Perform read/write leveling", LITEDRAM_CMDS) 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 [, ...]"); - 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 []"); - 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" * diff --git a/litex/soc/software/bios/command.h b/litex/soc/software/bios/command.h index 1640231ff..05a93e805 100644 --- a/litex/soc/software/bios/command.h +++ b/litex/soc/software/bios/command.h @@ -15,6 +15,7 @@ #define MEM_CMDS 3 #define BOOT_CMDS 3 #define SPIFLASH_CMDS 4 +#define I2C_CMDS 4 #define LITEDRAM_CMDS 4 #define LITEETH_CMDS 5 #define LITESDCARD_CMDS 7 diff --git a/litex/soc/software/liblitedram/i2c.h b/litex/soc/software/include/base/i2c.h similarity index 88% rename from litex/soc/software/liblitedram/i2c.h rename to litex/soc/software/include/base/i2c.h index c0cc2bfe6..7021c9424 100644 --- a/litex/soc/software/liblitedram/i2c.h +++ b/litex/soc/software/include/base/i2c.h @@ -1,8 +1,11 @@ #ifndef __I2C_H #define __I2C_H +#ifdef __cplusplus +extern "C" { +#endif + #include -#include /* I2C frequency defaults to a safe value in range 10-100 kHz to be compatible with SMBus */ #ifndef I2C_FREQ_HZ @@ -16,4 +19,8 @@ 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); +#ifdef __cplusplus +} +#endif + #endif /* __I2C_H */ diff --git a/litex/soc/software/libbase/Makefile b/litex/soc/software/libbase/Makefile index b29bffb42..dff9f7188 100755 --- a/litex/soc/software/libbase/Makefile +++ b/litex/soc/software/libbase/Makefile @@ -1,8 +1,8 @@ include ../include/generated/variables.mak include $(SOC_DIRECTORY)/software/common.mak -OBJECTS=exception.o libc.o errno.o crc16.o crc32.o console.o \ - system.o id.o uart.o time.o qsort.o strtod.o spiflash.o strcasecmp.o +OBJECTS = exception.o libc.o errno.o crc16.o crc32.o console.o \ + system.o id.o uart.o time.o qsort.o strtod.o spiflash.o strcasecmp.o i2c.o all: crt0-ctr.o crt0-xip.o libbase.a libbase-nofloat.a diff --git a/litex/soc/software/liblitedram/i2c.c b/litex/soc/software/libbase/i2c.c similarity index 98% rename from litex/soc/software/liblitedram/i2c.c rename to litex/soc/software/libbase/i2c.c index af40b8f49..cd15abb53 100644 --- a/litex/soc/software/liblitedram/i2c.c +++ b/litex/soc/software/libbase/i2c.c @@ -1,7 +1,6 @@ // This file is Copyright (c) 2020 Antmicro - -#include -#include "i2c.h" +#include +#include #ifdef CSR_I2C_BASE diff --git a/litex/soc/software/liblitedram/Makefile b/litex/soc/software/liblitedram/Makefile index c6113c0ab..c541e3f65 100644 --- a/litex/soc/software/liblitedram/Makefile +++ b/litex/soc/software/liblitedram/Makefile @@ -1,7 +1,7 @@ include ../include/generated/variables.mak include $(SOC_DIRECTORY)/software/common.mak -OBJECTS = sdram.o i2c.o +OBJECTS = sdram.o all: liblitedram.a diff --git a/litex/soc/software/liblitedram/sdram.h b/litex/soc/software/liblitedram/sdram.h index 7deb82e39..21433c03c 100644 --- a/litex/soc/software/liblitedram/sdram.h +++ b/litex/soc/software/liblitedram/sdram.h @@ -2,7 +2,6 @@ #define __SDRAM_H #include -#include "i2c.h" void sdrsw(void); void sdrhw(void);