How the Convers+ firmware accesses the external EEPROM

Disassemble Convers+ firmware 7M2T-14C026-AG using IDA Pro
Locked
Go4IT
Pro
Posts: 967
Joined: 08 Feb 2019, 12:25

How the Convers+ firmware accesses the external EEPROM

Post by Go4IT »

This topic is used to get familar with the way the Convers+ firmware reads, processes and writes the content of the external EEPROM. It contains some important informations like the type of car, engine and of course the total and trip mileages.

Here is a picture of it on the board which shows the part an it's pins:
convers_vfl_eeprom.jpg
convers_eeprom_24c16.png
It is a "Atmel 24C16A" and before start looking into the firmware, we need to get familar of how it's work and how to access or write data into it.

EEPROM data transfer logic (I2C)

The chip uses an I2C interface to communicate (here i describe the basics of I2C communication: viewtopic.php?f=40&t=106) I2C is primarily made of two signals, SCL for a clock and SDA for data, the other pins are used to preset a special mode of the chip:
pinout_desc_block_pack.png
PB0, PB1, PRE are used to hardware-protect some regions of the data. Both pins are fixed to GND on the mainboard, so they are both "0" what means "no protection at all".
The MODE/WC pin is used to decide on write-operations if a full page of data is written (MODE=1), or just a bunch of bytes as multibyte (MODE=0) in the initial START-sequence. The pin follows the SCL signal level by a 4,7k resistor. This way MODE is HIGH if clock is HIGH and vice versa.

To start communication the uC which acts as a master must initiate a START condition. The 8 bits sent after a START condition are made up of 4 bits device ID (static "1010"), followed by 3 block select bits and one bit for a READ (RW = 1) or WRITE (RW = 0) operation.

In the "Byte Write mode" the master sends one data byte, then terminates the transfer by generating a STOP condition. This mode is independant of the state of the MODE pin which could be left floating if only this mode was to be used.

For the "Multibyte Write mode", the MODE pin must be HIGH. The Multibyte Write mode can be started from any address in the memory. The master sends from one up to 8 bytes of data.

For the "Page Write mode", the MODE pin must be at LOW. The Page Write mode allows up to 16 bytes to be written in a single write cycle, provided that they are all located in the same ’row’ in the memory: that is the same Block Address bits (b3, b2, b1 of Device Select code in Table 3) and the same 4 MSBs in the Byte Address. After each byte is transfered, the internal byte address counter (4 Least Significant Bits only) is incremented.

All modes need a lower address byte to be sent first, then the corresponding data bytes follows (1 up to 16, depending pn choosen mode).

I2C Bus controlled by Microcontroller

The firmware uses the integrated I2C bus module of the MAC7116 to communicate with this part:
314DFDDD-CA40-4F99-95BF-8C1009E71099.jpeg
eeprom_connected_mac7116.png
The register address range of the I2C module is 0xFC0A C000 - 0xFC0A FFFF. But only the first 8 bytes are used. The memory map from the datasheet:
mac7116_eeprom_memorymap.png
mac7116_i2c_register_addresses.png
mac7116_i2c_register_overview.png
Meaning of IO registers

Code: Select all

I2C Offset Register Description Access
0xFC0A C000 I2C Bus Address Register (IBAD) R/W
0xFC0A C001 I2C Bus Frequency Divider Register (IBFD) R/W
0xFC0A C002 I2C Bus Control Register (IBCR) R/W
0xFC0A C003 I2C Bus Status Register (IBSR) R/W
0xFC0A C004 I2C Bus Data I/O Register (IBDR) R/W
The IBAD register is only used if the uC is setup as I2C slave, which in the Convers+ is not the case.

IBFD registers setup the bus signal timings.

IBCR register keeps track of some settings:
1CAE828E-B026-4EA2-836E-895DEE11E221.jpeg
The IBSR is used to check for bus status and ACK and other signals:
91984B8D-F45B-470A-AF0D-88776C0F3B0B.jpeg
A write to IBDR issues the byte to be sent via I2C to the EEPROM. Received data can also be read from this register:
FAC8116A-D22C-44FC-AB23-9E01F2BE7F60.jpeg
For more informations read the datasheet of the MAC7116 from chapter 24 onwards.
You do not have the required permissions to view the files attached to this post.
Go4IT
Pro
Posts: 967
Joined: 08 Feb 2019, 12:25

How the Convers+ firmware accesses the external EEPROM

Post by Go4IT »

Lookup I2C subs in firmware

To find functions handling EEPROM data i first search for code using one of the above register addresses. A simple search for immediate value of the I2C base address 0xFC0A C000 brings up a lot of references:

Code: Select all

Address	Function	Instruction
Address	Function	Instruction
PBL:000039B0	sub_39B0	LDR             R0, =0xFC0AC000
PBL:000039C0	sub_39C0	LDR             R2, =0xFC0AC000
PBL:000039CC	sub_39CC	LDR             R1, =0xFC0AC000
PBL:00003A02	sub_3A02	LDR             R2, =0xFC0AC000
PBL:00003A2C	sub_3A2C	LDR             R1, =0xFC0AC000
PBL:00003A74	sub_3A74	LDR             R1, =0xFC0AC000
PBL:00003AA2	sub_3AA2	LDR             R0, =0xFC0AC000
PBL:00003AD8	sub_3AD8	LDR             R3, =0xFC0AC000
PBL:00003B36	sub_3B36	LDR             R2, =0xFC0AC000
PBL:00003B78	sub_3B78	LDR             R2, =0xFC0AC000
PBL:00003BBE	sub_3BBE	LDR             R2, =0xFC0AC000
It is common for Assembler code to point to the base register and use index-functions of adding offsets to access the individual registers.

Then i gave the registers names and also prefix the functions with something lime "i2c_..." to get readable names in the code. Until i know the real functionality of them, i leave the address. So for example i rename the "sub_39B0" to "i2c_39B0" and so on.

Now that we found some, let's analyze them to find out what they are doing in detail.
Go4IT
Pro
Posts: 967
Joined: 08 Feb 2019, 12:25

How the Convers+ firmware accesses the external EEPROM

Post by Go4IT »

i2c_39B0 => i2c_enable()

The first function takes no attributes so it's simply a setup function which is called from the MAC-Init subroutine after boot.

Disassembler code:

Code: Select all

PBL:000039B0 ; Attributes:
PBL:000039B0
PBL:000039B0 i2c_39B0                  ; CODE XREF: mac_init+15E↑p
PBL:000039B0                                         ; uds_readDataByIdentifier+6↑p ...
PBL:000039B0                 LDR             R0, =0xFC0AC000 ; I2C Bus Address Register
PBL:000039B2                 MOVS            R1, #0x1D
PBL:000039B4                 STRB            R1, [R0,#1] ; Set Bus Frequence Divider Register (IBFD)
PBL:000039B4                                         ; 00 = MUL 01
PBL:000039B4                                         ; 011 = Prescaler divider
PBL:000039B4                                         ; 101 = Shift register tap point
PBL:000039B6                 LDRB            R1, [R0,#2] ; Read Bus Control Register (IBCR)
PBL:000039B8                 LSLS            R1, R1, #25 ; In fact set Bit 7 (Bus Disable/Enable) to 0 (=Enable)
PBL:000039BA                 LSRS            R1, R1, #25
PBL:000039BC                 STRB            R1, [R0,#2] ; Enable I2C Bus
PBL:000039BE                 BX              LR
IDA Pro decompiler code:

Code: Select all

void *i2c_39B0()
{
  void *result; // r0
  result = &I2C_IBAD;
  I2C_IBFD = 29;
  I2C_IBCR &= 0x7Fu;
  return result;
}
Analyze:

Code: Select all

LDR          R0, =0xFC0AC000; loads base address of I2C registers into R0. This is like a pointer in C to the given memory location)

Code: Select all

MOVS         R1, #0x1D; loads register R1 with given value
STRB          R1, [R0,#1]; write the value 0x1D from R1 to the memory location 0xFC0AC001 pointed by R0.
The target address 0xFC0AC001 is the "IBFD" register and the value 0x1D means in binary notation 0b00011101. These are the meanings of the bits in the register (found in datasheet of MAC7100: 24.5.1.2 I2C Bus Frequency Divider Register (IBFD))
Bit 7-6 = 00 = Set prescaler shift register: 01 MUL
Bit 5-3 = 011 = Set prescaler divider to: scl2start: 6 clocks, scl2stop: 9 clocks, scl2tap: 6, tap2tap: 8 clocks
Bit 2-0 = 101 = Set shift register tap point to: SCL Tap: 10 clocks, SDA Tap: 3 clocks

Code: Select all

LDRB            R1, [R0,#2] ; Read Bus Control Register (IBCR)
LSLS            R1, R1, #25 ;
LSRS            R1, R1, #25 ; both shifts equals to and logical and with 0x7F, which set's bit 7 to "0"
STRB            R1, [R0,#2] ;
Setting Bit 7 of IBCR to "0" means "Enable I2C Bus".

I would give this function the name i2c_enable

My handmade C code for it:

Code: Select all

void i2c_enable()
{
  char* I2C_BASE = 0xFC0AC000;
  I2C_BASE[1] = 0x1D;
  I2C_BASE[2] &= 0x7F;
}
Go4IT
Pro
Posts: 967
Joined: 08 Feb 2019, 12:25

How the Convers+ firmware accesses the external EEPROM

Post by Go4IT »

i2c_3C90 => i2c_disable()

Disassembler code:

Code: Select all

PBL:000039C0 i2c_3C90                      ; CODE XREF: PBL:00003C08↓p
PBL:000039C0                 LDR             R2, =I2C_IBAD ; I2C Bus Address Register
PBL:000039C2                 LDRB            R0, [R2,#2] ; I2C Bus Control Register (IBCR)
PBL:000039C4                 MOVS            R1, #0x80;
PBL:000039C6                 ORRS            R0, R1
PBL:000039C8                 STRB            R0, [R2,#2] ; I2C Bus Control Register (IBCR)
PBL:000039CA                 BX              LR
IDA decompile:

Code: Select all

int i2c_3C90()
{
  int result; // r0
  result = I2C_IBCR | 0x80;
  I2C_IBCR |= 0x80u;
  return result;
}
Analyze:
This code simply set's bit 7 of IBCR register ("IBDIS"), which disables the I2C module and hold it in reset-state.
So we can give it the name i2c_disable

My C-Code:

Code: Select all

void i2c_disable()
{
    char *I2C_BASE = 0xFC0AC000;
    I2C_BASE[2] |= 0x80; set IBDIS flag to 1
}
Go4IT
Pro
Posts: 967
Joined: 08 Feb 2019, 12:25

How the Convers+ firmware accesses the external EEPROM

Post by Go4IT »

i2c_39CC => i2c_initRandomAccessRead

Disassembler code:

Code: Select all

PBL:000039CC i2c_39CC                 ; CODE XREF: eeprom_read+C↓p
PBL:000039CC                                         ; eeprom_3AD0+4↓p ...
PBL:000039CC                 LDR             R1, =0xFC0AC000 ; I2C Bus Base Address Register
PBL:000039CE
PBL:000039CE loc_39CE                                ; CODE XREF: eeprom_i2c_send_address+6↓j
PBL:000039CE                 LDRB            R2, [R1,#3] ; IBSR Wait until bus is ready/free
PBL:000039D0                 LSLS            R2, R2, #26
PBL:000039D2                 BMI             loc_39CE ; Wait until bus is ready/free
PBL:000039D4                 LDRB            R2, [R1,#2] ; IBCR Disable I2C
PBL:000039D6                 MOVS            R3, #0b110000
PBL:000039D8                 ORRS            R2, R3
PBL:000039DA                 STRB            R2, [R1,#2] ; IBCR I2C Bus Control Register (IBCR)
PBL:000039DC                 LSRS            R0, R0, #7
PBL:000039DE                 MOVS            R2, #0b1110
PBL:000039E0                 ANDS            R0, R2
PBL:000039E2                 MOVS            R2, #0b10100000
PBL:000039E4                 ORRS            R0, R2
PBL:000039E6                 LDR             R2, =byte_4000180D
PBL:000039E8                 STRB            R0, [R2]
PBL:000039EA                 MOVS            R2, #0b11111110 ; set R/W bit 0 = Write to slave (EEPROM)
PBL:000039EC                 ANDS            R0, R2
PBL:000039EE                 STRB            R0, [R1,#4] ; IBDR Send address and write request to EEPROM
PBL:000039F0
PBL:000039F0 loc_39F0                                ; CODE XREF: eeprom_i2c_send_address+28↓j
PBL:000039F0                 LDRB            R0, [R1,#3] ; IBSR wait until address send
PBL:000039F2                 LSLS            R0, R0, #30
PBL:000039F4                 BPL             loc_39F0 ; wait until address send
PBL:000039F6                 LDRB            R0, [R1,#3] ; IBSR I2C Bus Status Register (IBSR)
PBL:000039F8                 MOVS            R2, R1
PBL:000039FA                 MOVS            R1, #2
PBL:000039FC                 ORRS            R0, R1
PBL:000039FE                 STRB            R0, [R2,#3] ; IBSR I2C Bus Status Register (IBSR)
PBL:00003A00                 BX              LR
IDA Pro decompile:

Code: Select all

int __fastcall i2c_39CC(unsigned int eepromAddress)
{
  int result; // r0

  while ( I2C_IBSR[0] & 0x20 )                  // Wait until bus is idle (Check Bit 5 IBB is 0)
    ;
  I2C_IBCR |= 0x30u;                            // Set Bit 5 to 1 => Select master mode
                                                // Set Bit 4 to 1 => Select transmit mode
  eeprom_i2c_send_address_byteVar = (eepromAddress >> 7) & 0xE | 0xA0;// The START condition is followed by a stream of 4 bits (identification code 1010), 3 block select bits (A10-A8), plus one read/write bit and terminated
                                                // by an acknowledge bit.
                                                // | 0xA0 will set "1010"
                                                // & 0x0E ensure Bit 0 is "0" which means write operation
  I2C_IBDR[0] = eeprom_i2c_send_address_byteVar & 0xFE;// Send page byte of address
  while ( !(I2C_IBSR[0] & 2) )                  // Wait until byte sent (IBIF Bit is set)
    ;
  result = I2C_IBSR[0] | 2;
  I2C_IBSR[0] |= 2u;                            // Reset I2C bus interrupt by byte send
  return result;                                // currenlty all functions calling this function don't care about the result
}
Analyze what it does:
Basically this function initiates a data transfer from the EEPROM to the microcontroller. It selects an EEPROM start address (random access mode) as the first function-parameter and read as much bytes as given in the second parameter (length). The read bytes are written to the RAM start address given as third parameter.

My resulting C code:

Code: Select all

void i2c_i2c_initRandomAccessRead(uint32_t startAddress)
{
  uint32_t *I2C_BASE[] = 0xFC0AC000;
  uint32_t *byte_4000180D = 0x4000180D;

  // check IBB-bit of IBSR register (Bit 5) is set, which means "transfer complete"
  while ( ! I2C_BASE[3] & 0x20);

  I2C_BASE[2] |= 0b00110000; // set Bits 4 (Tx/Rx) and 5 (MS/SL) of IBCR register, which means "set to Master/Transmit mode"

  // set base address (page)
  uint8_t eepromPage = ((startAddress >> 7) & 0b00001110) | 0b10100000;
  &byte_4000180D = eepromPage;
  I2C_BASE[4] = eepromPage & 0b11111110; // send address byte to EEPROM (bit 0 unset means "random access read")
  while ( ! I2C_BASE[3] & 0b00000010); // Wait until address sent (IBIF Bit is set because byte-transfer complete)
  I2C_BASE3] |= 0b00000010;
}
Go4IT
Pro
Posts: 967
Joined: 08 Feb 2019, 12:25

How the Convers+ firmware accesses the external EEPROM

Post by Go4IT »

i2c_3A02 => i2c_sendByte

IDA disassembler code:

Code: Select all

PBL:00003A02 ; Send byte given in R0
PBL:00003A02 ; Attributes:
PBL:00003A02
PBL:00003A02 i2c_3A02                    ; CODE XREF: eeprom_read+12↓p
PBL:00003A02                                         ; eeprom_3AFC+22↓p
PBL:00003A02                 LDR             R2, =I2C_IBAD ; I2C Bus Address Register
PBL:00003A04                 STRB            R0, [R2,#4] ; I2C Bus Data I/O Register (IBDR)
PBL:00003A06
PBL:00003A06 loc_3A06                                ; CODE XREF: eeprom_i2c_send_byte+8↓j
PBL:00003A06                 LDRB            R0, [R2,#3] ; wait until byte is send
PBL:00003A08                 LSLS            R0, R0, #0x1E
PBL:00003A0A                 BPL             loc_3A06 ; wait until byte is send
PBL:00003A0C                 LDRB            R0, [R2,#3] ; I2C Bus Status Register (IBSR)
PBL:00003A0E                 MOVS            R1, #2
PBL:00003A10                 ORRS            R0, R1
PBL:00003A12                 STRB            R0, [R2,#3] ; I2C Bus Status Register (IBSR)
PBL:00003A14                 BX              LR
IDA Decompile:

Code: Select all

// Send byte given in R0
int __fastcall i2c_3A02(char a1)
{
  int result; // r0

  I2C_IBDR[0] = a1;
  while ( !(I2C_IBSR[0] & 2) )
    ;
  result = I2C_IBSR[0] | 2;
  I2C_IBSR[0] |= 2u;
  return result;
}
Analysis:
The method sends a single byte to the I2C slave.

My C code:

Code: Select all

void i2c_sendByte(uint8_t data)
{
  uint32_t *I2B_BASE[] = 0xFC0AC000;
  I2C_BASE[4] = data;
  while ( ! I2B_BASE[3] & 0x02);
  I2B_BASE[3] |= 0x02;
}
Go4IT
Pro
Posts: 967
Joined: 08 Feb 2019, 12:25

How the Convers+ firmware accesses the external EEPROM

Post by Go4IT »

i2c_3A16 => i2c_eepromRead

IDA disassembler code:

Code: Select all

PBL:00003A16 i2c_3A16
PBL:00003A16 var_18          = -0x18
PBL:00003A16                 PUSH            {R6,R7,LR}
PBL:00003A18                 PUSH            {R3-R5}
PBL:00003A1A                 MOVS            R4, R0
PBL:00003A1C                 MOVS            R5, R1
PBL:00003A1E                 MOVS            R7, R2
PBL:00003A20                 MOVS            R0, R4
PBL:00003A22                 BL              i2c_initRandomAccessRead
PBL:00003A26                 MOVS            R0, R4
PBL:00003A28                 BL              i2c_sendByte ; Send byte given in R0
PBL:00003A2C                 LDR             R1, =0xFC0AC000 ; I2C Bus Address Register
PBL:00003A2E                 LDRB            R0, [R1,#2)] ; I2C Bus Control Register (IBCR)
PBL:00003A30                 MOVS            R2, #4
PBL:00003A32                 ORRS            R0, R2
PBL:00003A34                 STRB            R0, [R1,#2] ; I2C Bus Control Register (IBCR)
PBL:00003A36                 LDR             R0, =byte_4000180D
PBL:00003A38                 LDRB            R2, [R0]
PBL:00003A3A                 MOVS            R0, #1
PBL:00003A3C                 ORRS            R2, R0
PBL:00003A3E                 STRB            R2, [R1,#4)] ; I2C Bus Data I/O Register (IBDR)
PBL:00003A40
PBL:00003A40 loc_3A40                                ; CODE XREF: eeprom_read+2E↓j
PBL:00003A40                 LDRB            R2, [R1,#3] ; I2C Bus Status Register (IBSR)
PBL:00003A42                 LSLS            R2, R2, #0x1E
PBL:00003A44                 BPL             loc_3A40
PBL:00003A46                 LDRB            R3, [R1,#3] ; I2C Bus Status Register (IBSR)
PBL:00003A48                 MOVS            R2, #2
PBL:00003A4A                 ORRS            R3, R2
PBL:00003A4C                 STRB            R3, [R1,#3] ; I2C Bus Status Register (IBSR)
PBL:00003A4E                 LDRB            R3, [R1,#3] ; I2C Bus Status Register (IBSR)
PBL:00003A50                 ANDS            R0, R3
PBL:00003A52                 STRB            R0, [R1,#3] ; I2C Bus Status Register (IBSR)
PBL:00003A54                 BEQ             loc_3A5C
PBL:00003A56                 MOVS            R0, #1
PBL:00003A58                 STR             R0, [SP,#0x18+var_18]
PBL:00003A5A                 B               loc_3A60
PBL:00003A5C ; ---------------------------------------------------------------------------
PBL:00003A5C
PBL:00003A5C loc_3A5C                                ; CODE XREF: eeprom_read+3E↑j
PBL:00003A5C                                         ; DATA XREF: PBL:loc_10A0↑o
PBL:00003A5C                 MOVS            R0, #0
PBL:00003A5E                 STR             R0, [SP,#0x18+var_18]
PBL:00003A60
PBL:00003A60 loc_3A60                                ; CODE XREF: eeprom_read+44↑j
PBL:00003A60                 LDRB            R0, [R1,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003A62                 MOVS            R3, #0xEF
PBL:00003A64                 ANDS            R0, R3
PBL:00003A66                 STRB            R0, [R1,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003A68                 LDRB            R0, [R1,#(I2C_IBDR - 0xFC0AC000)] ; I2C Bus Data I/O Register (IBDR)
PBL:00003A6A                 MOVS            R4, #0
PBL:00003A6C                 SUBS            R6, R5, #2
PBL:00003A6E                 B               loc_3A9E
PBL:00003A70 ; ---------------------------------------------------------------------------
PBL:00003A70
PBL:00003A70 loc_3A70                                ; CODE XREF: eeprom_read+8A↓j
PBL:00003A70                 BL              sub_14D6
PBL:00003A74                 LDR             R1, =I2C_IBAD ; I2C Bus Address Register
PBL:00003A76
PBL:00003A76 loc_3A76                                ; CODE XREF: eeprom_read+64↓j
PBL:00003A76                 LDRB            R0, [R1,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003A78                 LSLS            R0, R0, #0x1E
PBL:00003A7A                 BPL             loc_3A76
PBL:00003A7C                 LDRB            R0, [R1,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003A7E                 MOVS            R2, #2
PBL:00003A80                 ORRS            R0, R2
PBL:00003A82                 STRB            R0, [R1,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003A84                 LDRB            R0, [R1,#(I2C_IBDR - 0xFC0AC000)] ; I2C Bus Data I/O Register (IBDR)
PBL:00003A86                 STRB            R0, [R7,R4]
PBL:00003A88                 CMP             R4, R6
PBL:00003A8A                 BEQ             loc_3A90
PBL:00003A8C                 CMP             R5, #1
PBL:00003A8E                 BNE             loc_3A98
PBL:00003A90
PBL:00003A90 loc_3A90                                ; CODE XREF: eeprom_read+74↑j
PBL:00003A90                 LDRB            R0, [R1,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003A92                 MOVS            R2, #8
PBL:00003A94
PBL:00003A94 loc_3A94                                ; DATA XREF: PBL:000010A4↑o
PBL:00003A94                 ORRS            R0, R2
PBL:00003A96                 STRB            R0, [R1,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003A98
PBL:00003A98 loc_3A98                                ; CODE XREF: eeprom_read+78↑j
PBL:00003A98                 ADDS            R4, #1
PBL:00003A9A                 LSLS            R4, R4, #0x10
PBL:00003A9C                 LSRS            R4, R4, #0x10
PBL:00003A9E
PBL:00003A9E loc_3A9E                                ; CODE XREF: eeprom_read+58↑j
PBL:00003A9E                 CMP             R4, R5
PBL:00003AA0                 BCC             loc_3A70
PBL:00003AA2                 LDR             R0, =I2C_IBAD ; I2C Bus Address Register
PBL:00003AA4
PBL:00003AA4 loc_3AA4                                ; CODE XREF: eeprom_read+92↓j
PBL:00003AA4                 LDRB            R1, [R0,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003AA6                 LSLS            R1, R1, #0x1E
PBL:00003AA8                 BPL             loc_3AA4
PBL:00003AAA                 LDRB            R1, [R0,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003AAC                 MOVS            R2, #2
PBL:00003AAE                 ORRS            R1, R2
PBL:00003AB0                 STRB            R1, [R0,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003AB2                 LDRB            R1, [R0,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003AB4                 MOVS            R2, #0xF7
PBL:00003AB6                 ANDS            R1, R2
PBL:00003AB8                 STRB            R1, [R0,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003ABA                 LDRB            R1, [R0,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003ABC                 MOVS            R2, #0xDF
PBL:00003ABE                 ANDS            R1, R2
PBL:00003AC0                 STRB            R1, [R0,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003AC2
PBL:00003AC2 loc_3AC2                                ; CODE XREF: eeprom_read+B0↓j
PBL:00003AC2                 LDRB            R1, [R0,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003AC4                 LSLS            R1, R1, #0x1A
PBL:00003AC6                 BMI             loc_3AC2
PBL:00003AC8                 LDR             R0, [SP,#0x18+var_18]
PBL:00003ACA                 POP             {R3-R7}
PBL:00003ACC                 POP             {R3}
PBL:00003ACE                 BX              R3
IDA Decompile:

Code: Select all

// Read (R1) bytes starting at (R0) in EEPROM to memory at (R2)
BOOL __fastcall i2c_3A16(int startAddress, unsigned int len, int targetPtr)
{
  char v3; // r4
  unsigned int v4; // r5
  int v5; // r7
  char v6; // r0
  unsigned int i; // r4
  BOOL v9; // [sp+0h] [bp-18h]

  v3 = startAddress;
  v4 = len;
  v5 = targetPtr;
  eeprom_i2c_send_address(startAddress);
  eeprom_i2c_send_byte(v3);
  v6 = I2C_IBCR;
  I2C_IBCR = v6 | 4;
  I2C_IBDR[0] = byte_4000180D | 1;
  while ( !(I2C_IBSR[0] & 2) )
    ;
  I2C_IBSR[0] |= 2u;
  I2C_IBSR[0] &= 1u;
  v9 = I2C_IBSR[0] != 0;
  I2C_IBCR &= 0xEFu;
  for ( i = 0; i < v4; i = (i + 1) & 0xFFFF )
  {
    sub_14D6();
    while ( !(I2C_IBSR[0] & 2) )
      ;
    I2C_IBSR[0] |= 2u;
    *(_BYTE *)(v5 + i) = I2C_IBDR[0];
    if ( i == v4 - 2 || v4 == 1 )
      I2C_IBCR |= 8u;
  }
  while ( !(I2C_IBSR[0] & 2) )
    ;
  I2C_IBSR[0] |= 2u;
  I2C_IBCR &= 0xF7u;
  I2C_IBCR &= 0xDFu;
  while ( I2C_IBSR[0] & 0x20 )
    ;
  return v9;
}
Analysis:
This sub can read random chunks of data from the EEPROM into SRAM. It get's an EEPROM memory location as start, a number of bytes to read and an pointer to the target address in RAM where to store that data.
A very strange thing about this sub is that it reads the first data byte sent from the EEPROM but not stores it anywhere, it just drops it! This would mean that the functions which calls it never get the first data byte in return, or need to provide an address one byte below the wanted, which is not the case.
When looking for usages of this sub i get only three backrefs. Two from uds_readDataByIdentifier() which returns the "ECUSerialNumberDataIdentifier" of PID 0xF18C or "vehicleManufacturerECUHardwareNumberDataIdentifier" of PID 0xF191 as a direct read from the EEPROM. The other call come from eeprom_readAll() which reads in the whole content of the EEPROM to 0x60000000. Currently i can't tell what this address represents, it is far above the SRAM, maybe an option for some other implementation. The routine itself is also never called by the firmware directly.

My C code:

Code: Select all

#include <stdint.h>

#define IBAD 0
#define IBFD 1
#define IBCR 2
#define IBCR_MSSL 0b00100000
#define IBCR_TXRX 0b00010000
#define IBCR_TXAK 0b00001000
#define IBCR_RSTA 0b00000100
#define IBSR 3
#define IBSR_IBIF 0b00000010
#define IBSR_RXAK 0b00000001
#define IBDR 4

uint8_t *I2C_BASE = 0xFC0AC000; // set the base register address of the I2C controller in MAC71xx
uint8_t *i2c_varDeviceSelectByte = 0x40000180D;  // global variable holding the I2C device-select byte

uint8_t i2c_eepromRead ( uint32_t eepromAddress, uint8_t len, uint32_t *ramAddress )
{
	uint8_t rc = 0;
	uint8_t data;

	/**
	  * These following operations follows the "RANDOM ACCESS READ" sequence
	  * (described in the datasheet of the 24C16 EEPROM page 13, figure 11)
     	  * It first sends a device-select code with the upper address bits (A10-A8)
	  * Then it sends the lower address bits (A7-A0) as a simple byte write operation
	  * Now another START condition is following
	  * Next send the device-select code again, with Bit 0 set
	  * Wait if EEPROM sends and ACK (RXAK Bit must be '0' for positive ACK)
	  * After that sequence the EEPROM will send the byte back to the master
	  */
	i2c_initRandomAccessRead(eepromAddress);  // send START, device-ID, upper address bits A10-A8 and read mode
                                                                    // will also set i2c_varDeviceSelectByte
	i2c_sendByte(eepromAddress);              // send lower address bits A7-A0
	I2C_BASE[IBCR] |= IBCR_RSTA;                // generate repeated start condition (set RSTA bit)
	I2C_BASE[IBDR] = (int)&i2c_varDeviceSelectByte | 1; // send device-select byte again but with Bit-0 set
	while ( ! (I2C_BASE[IBSR] & IBSR_IBIF));    // wait until byte transfer completed (IBIF set)
	I2C_BASE[IBSR] |= IBSR_IBIF;                // clear IBIF bit
	if ((I2C_BASE[IBSR] & IBSR_RXAK) != 0) {
		rc = 1;                                 // error, no read ACK received
	}
	I2C_BASE[IBSR] &= IBSR_RXAK;

	I2C_BASE[IBCR] &= ~IBCR_TXRX;               // set master to receive-mode (unset Bit 4 of IBCR ~IBCR_TXAK)
	data = I2C_BASE[IBDR];                      // read the requested byte from eeprom (and throw it away)
	for (uint16_t i = 0; i < len; i++)
	{
		//sub_14D6();                           // NOTE: this function is to update internal timers/CAN queues, has no meaning for I2C
		while ( ! (I2C_BASE[IBSR] & IBSR_IBIF)); // wait until ACK received
		I2C_BASE[IBSR] |= IBSR_IBIF;            // clear IRQ
		data = I2C_BASE[IBDR];
		ramAddress[i] = data;
		if (i == (ramAddress-2) || len == 1) {
			I2C_BASE[IBCR] |= IBCR_TXAK;
		}
    }
	while ( ! (I2C_BASE[IBSR] & IBSR_IBIF));    // wait until byte transfer completed (IBIF set)
	I2C_BASE[IBSR] |= IBSR_IBIF;                // clear IRQ
	I2C_BASE[IBCR] &= ~IBCR_TXAK;               // ACK read (unset TKAK, Bit 4)
	I2C_BASE[IBCR] &= ~IBCR_MSSL;               // generate STOP condition (MS/SL transition from 1 to 0)
	while ( ! (I2C_BASE[IBSR] & IBCR_MSSL));    // wait until slave ACK the STOP

	return rc;
}
Go4IT
Pro
Posts: 967
Joined: 08 Feb 2019, 12:25

Re: How the Convers+ firmware accesses the external EEPROM

Post by Go4IT »

i2c_3ad0 => ???

IDA Disassembler Code:

Code: Select all

PBL:00003AD0 eeprom_3AD0                             ; CODE XREF: eeprom_3AFC:loc_3BD4↓p
PBL:00003AD0                 PUSH            {LR}
PBL:00003AD2                 MOVS            R0, #0
PBL:00003AD4                 BL              i2c_initRandomAccessRead ; Send memory location address to EEPROM
PBL:00003AD8                 LDR             R3, =I2C_IBAD ; I2C Bus Address Register
PBL:00003ADA                 LDRB            R0, [R3,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003ADC                 MOVS            R1, #1
PBL:00003ADE                 ANDS            R0, R1
PBL:00003AE0                 STRB            R0, [R3,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003AE2                 BEQ             loc_3AE8
PBL:00003AE4                 MOVS            R0, #1
PBL:00003AE6                 B               loc_3AEA
PBL:00003AE8 ; ---------------------------------------------------------------------------
PBL:00003AE8
PBL:00003AE8 loc_3AE8                                ; CODE XREF: eeprom_3AD0+12↑j
PBL:00003AE8                 MOVS            R0, #0
PBL:00003AEA
PBL:00003AEA loc_3AEA                                ; CODE XREF: eeprom_3AD0+16↑j
PBL:00003AEA                 LDRB            R1, [R3,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003AEC                 MOVS            R2, #0xDF
PBL:00003AEE                 ANDS            R1, R2
PBL:00003AF0                 STRB            R1, [R3,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003AF2
PBL:00003AF2 loc_3AF2                                ; CODE XREF: eeprom_3AD0+26↓j
PBL:00003AF2                 LDRB            R1, [R3,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003AF4                 LSLS            R1, R1, #0x1A
PBL:00003AF6                 BMI             loc_3AF2
PBL:00003AF8                 POP             {R3}
PBL:00003AFA                 BX              R3
PBL:00003AFA ; End of function eeprom_3AD0
IDA Decompile:
BOOL eeprom_3AD0()
{
bool v0; // zf
BOOL result; // r0

i2c_initRandomAccessRead(0); // Reset EEPROM address to 0x000
v0 = (I2C_IBSR[0] & 1) == 0;
I2C_IBSR[0] &= 1u;
result = !v0;
I2C_IBCR &= 0xDFu;
while ( I2C_IBSR[0] & 0x20 )
;
return result;
}
Code analysis:

...

My hand-written C-code:

Code: Select all

#define IBCR 2
#define IBCR_MSSL 0b00100000
#define IBSR 3
#define IBSR_RXAK 0b00000001
#define IBSR_IBB    0b00100000
uint8_t function i2c_3ad0()
{
	uint8_t rc = 0;
	uint8_t *I2C_BASE = 0xFC0AC000; // set the base register address of the I2C controller in MAC71xx

	i2c_initRandomAccessRead(0);
	if ((I2C_BASE[IBSR] & IBSR_RXAK) != 0) {
	  rc = 1; // error, no read ACK received
	}
	I2C_BASE[IBSR] &= IBSR_RXAK; // ACK read
	I2C_BASE[IBCR] &= ~IBCR_MSSL; // generate STOP condition (MS/SL transition from 1 to 0)
	while ( I2C_BASE[IBSR] & 0x20 ); // wait until IBB is '0' (I2C bus is Idle)

	return rc;
}
Go4IT
Pro
Posts: 967
Joined: 08 Feb 2019, 12:25

Re: How the Convers+ firmware accesses the external EEPROM

Post by Go4IT »

i2c_3afc => ???

IDA Disassembler Code:

Code: Select all

BL:00003AFC ; R0 = Start address of EEPROM to access (0x0000-0x07FF)
PBL:00003AFC ; aLen = Number of bytes to read from EEPROM/write to EEPROM
PBL:00003AFC ; a3 = ?
PBL:00003AFC ; a4 = ?
PBL:00003AFC ; Attributes:
PBL:00003AFC
PBL:00003AFC ; signed int __fastcall eeprom_3AFC(int inR0_eepromStartAddress, unsigned int inR1_len, int inR2_targetBaseAddress, int inR3)
PBL:00003AFC eeprom_3AFC                             ; CODE XREF: eeprom_3C10+24↓p
PBL:00003AFC                                         ; PBL:00003C62↓p
PBL:00003AFC
PBL:00003AFC var_2C          = -0x2C
PBL:00003AFC var_28          = -0x28
PBL:00003AFC R0              = -0x24
PBL:00003AFC R2              = -0x1C
PBL:00003AFC
PBL:00003AFC                 PUSH            {R4-R7,LR}
PBL:00003AFE                 PUSH            {R0-R3}
PBL:00003B00                 MOVS            R7, R3
PBL:00003B02                 SUB             SP, SP, #0xC
PBL:00003B04                 MOVS            R4, #0
PBL:00003B06                 MOVS            R5, R1
PBL:00003B08                 MOVS            R6, #0
PBL:00003B0A                 B               loc_3BDC
PBL:00003B0C ; ---------------------------------------------------------------------------
PBL:00003B0C
PBL:00003B0C loc_3B0C                                ; CODE XREF: eeprom_3AFC+E6↓j
PBL:00003B0C                 LDR             R0, [SP,#0x30+R0]
PBL:00003B0E                 ADDS            R0, R0, R6
PBL:00003B10                 STR             R0, [SP,#0x30+var_28]
PBL:00003B12                 LSLS            R0, R0, #16
PBL:00003B14                 LSRS            R0, R0, #16
PBL:00003B16                 STR             R0, [SP,#0x30+var_2C]
PBL:00003B18                 BL              i2c_initRandomAccessRead ; Send memory location address to EEPROM
PBL:00003B1C                 LDR             R0, [SP,#0x30+var_2C]
PBL:00003B1E                 BL              i2c_sendByte ; Send byte given in R0
PBL:00003B22                 BL              sub_14D6
PBL:00003B26                 CMP             R5, #16
PBL:00003B28                 BCC             loc_3B74
PBL:00003B2A                 LDR             R1, [SP,#0x30+var_28]
PBL:00003B2C                 MOVS            R0, #0
PBL:00003B2E                 LSLS            R2, R1, #0b11100
PBL:00003B30                 LSRS            R2, R2, #0b11100
PBL:00003B32                 MOVS            R1, #0b10000
PBL:00003B34                 SUBS            R1, R1, R2
PBL:00003B36                 LDR             R2, =I2C_IBAD ; I2C Bus Address Register
PBL:00003B38                 B               loc_3B6E
PBL:00003B3A ; ---------------------------------------------------------------------------
PBL:00003B3A
PBL:00003B3A loc_3B3A                                ; CODE XREF: eeprom_3AFC+74↓j
PBL:00003B3A                 CMP             R7, #0
PBL:00003B3C                 BNE             loc_3B48
PBL:00003B3E                 LDR             R3, [SP,#0x30+R2]
PBL:00003B40                 ADDS            R3, R3, R6
PBL:00003B42                 LDRB            R3, [R3,R0]
PBL:00003B44                 STRB            R3, [R2,#(I2C_IBDR - 0xFC0AC000)] ; I2C Bus Data I/O Register (IBDR)
PBL:00003B46                 B               loc_3B4C
PBL:00003B48 ; ---------------------------------------------------------------------------
PBL:00003B48
PBL:00003B48 loc_3B48                                ; CODE XREF: eeprom_3AFC+40↑j
PBL:00003B48                 MOVS            R3, #0xFF
PBL:00003B4A                 STRB            R3, [R2,#(I2C_IBDR - 0xFC0AC000)] ; I2C Bus Data I/O Register (IBDR)
PBL:00003B4C
PBL:00003B4C loc_3B4C                                ; CODE XREF: eeprom_3AFC+4A↑j
PBL:00003B4C                                         ; eeprom_3AFC+54↓j
PBL:00003B4C                 LDRB            R3, [R2,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003B4E                 LSLS            R3, R3, #0x1E
PBL:00003B50                 BPL             loc_3B4C
PBL:00003B52                 LDRB            R3, [R2,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003B54                 MOVS            R4, #2
PBL:00003B56                 ORRS            R3, R4
PBL:00003B58                 STRB            R3, [R2,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003B5A                 LDRB            R3, [R2,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003B5C                 MOVS            R4, #1
PBL:00003B5E                 ANDS            R3, R4
PBL:00003B60                 STRB            R3, [R2,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003B62                 BEQ             loc_3B66
PBL:00003B64                 B               loc_3BB2
PBL:00003B66 ; ---------------------------------------------------------------------------
PBL:00003B66
PBL:00003B66 loc_3B66                                ; CODE XREF: eeprom_3AFC+66↑j
PBL:00003B66                 MOVS            R4, #0
PBL:00003B68                 ADDS            R0, #1
PBL:00003B6A                 LSLS            R0, R0, #0x10
PBL:00003B6C                 LSRS            R0, R0, #0x10
PBL:00003B6E
PBL:00003B6E loc_3B6E                                ; CODE XREF: eeprom_3AFC+3C↑j
PBL:00003B6E                 CMP             R1, R0
PBL:00003B70                 BHI             loc_3B3A
PBL:00003B72                 B               loc_3BB2
PBL:00003B74 ; ---------------------------------------------------------------------------
PBL:00003B74
PBL:00003B74 loc_3B74                                ; CODE XREF: eeprom_3AFC+2C↑j
PBL:00003B74                 LDR             R3, [SP,#0x30+R2]
PBL:00003B76                 MOVS            R0, #0
PBL:00003B78                 LDR             R2, =I2C_IBAD ; I2C Bus Address Register
PBL:00003B7A                 ADDS            R1, R3, R6
PBL:00003B7C                 B               loc_3BAE
PBL:00003B7E ; ---------------------------------------------------------------------------
PBL:00003B7E
PBL:00003B7E loc_3B7E                                ; CODE XREF: eeprom_3AFC+B4↓j
PBL:00003B7E                 CMP             R7, #0
PBL:00003B80                 BNE             loc_3B88
PBL:00003B82                 LDRB            R3, [R1,R0]
PBL:00003B84                 STRB            R3, [R2,#(I2C_IBDR - 0xFC0AC000)] ; I2C Bus Data I/O Register (IBDR)
PBL:00003B86                 B               loc_3B8C
PBL:00003B88 ; ---------------------------------------------------------------------------
PBL:00003B88
PBL:00003B88 loc_3B88                                ; CODE XREF: eeprom_3AFC+84↑j
PBL:00003B88                 MOVS            R3, #0xFF
PBL:00003B8A                 STRB            R3, [R2,#(I2C_IBDR - 0xFC0AC000)] ; I2C Bus Data I/O Register (IBDR)
PBL:00003B8C
PBL:00003B8C loc_3B8C                                ; CODE XREF: eeprom_3AFC+8A↑j
PBL:00003B8C                                         ; eeprom_3AFC+94↓j
PBL:00003B8C                 LDRB            R3, [R2,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003B8E                 LSLS            R3, R3, #0x1E
PBL:00003B90                 BPL             loc_3B8C
PBL:00003B92                 LDRB            R3, [R2,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003B94                 MOVS            R4, #2
PBL:00003B96                 ORRS            R3, R4
PBL:00003B98                 STRB            R3, [R2,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003B9A                 LDRB            R3, [R2,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003B9C                 MOVS            R4, #1
PBL:00003B9E                 ANDS            R3, R4
PBL:00003BA0                 STRB            R3, [R2,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003BA2                 BEQ             loc_3BA6
PBL:00003BA4                 B               loc_3BB2
PBL:00003BA6 ; ---------------------------------------------------------------------------
PBL:00003BA6
PBL:00003BA6 loc_3BA6                                ; CODE XREF: eeprom_3AFC+A6↑j
PBL:00003BA6                 MOVS            R4, #0
PBL:00003BA8                 ADDS            R0, #1
PBL:00003BAA                 LSLS            R0, R0, #0x10
PBL:00003BAC                 LSRS            R0, R0, #0x10
PBL:00003BAE
PBL:00003BAE loc_3BAE                                ; CODE XREF: eeprom_3AFC+80↑j
PBL:00003BAE                 CMP             R0, R5
PBL:00003BB0                 BCC             loc_3B7E
PBL:00003BB2
PBL:00003BB2 loc_3BB2                                ; CODE XREF: eeprom_3AFC+68↑j
PBL:00003BB2                                         ; eeprom_3AFC+76↑j ...
PBL:00003BB2                 ADDS            R1, R6, R0
PBL:00003BB4                 LSLS            R6, R1, #0x10
PBL:00003BB6                 LSRS            R6, R6, #0x10
PBL:00003BB8                 SUBS            R0, R5, R0
PBL:00003BBA                 LSLS            R5, R0, #0x10
PBL:00003BBC                 LSRS            R5, R5, #0x10
PBL:00003BBE                 LDR             R2, =I2C_IBAD ; I2C Bus Address Register
PBL:00003BC0                 LDRB            R0, [R2,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003BC2                 MOVS            R1, #0xDF
PBL:00003BC4                 ANDS            R0, R1
PBL:00003BC6                 STRB            R0, [R2,#(I2C_IBCR - 0xFC0AC000)] ; I2C Bus Control Register (IBCR)
PBL:00003BC8
PBL:00003BC8 loc_3BC8                                ; CODE XREF: eeprom_3AFC+D0↓j
PBL:00003BC8                 LDRB            R0, [R2,#(I2C_IBSR - 0xFC0AC000)] ; I2C Bus Status Register (IBSR)
PBL:00003BCA                 LSLS            R0, R0, #0x1A
PBL:00003BCC                 BMI             loc_3BC8
PBL:00003BCE                 B               loc_3BD4
PBL:00003BD0 ; ---------------------------------------------------------------------------
PBL:00003BD0
PBL:00003BD0 loc_3BD0                                ; CODE XREF: eeprom_3AFC+DE↓j
PBL:00003BD0                 BL              sub_14D6
PBL:00003BD4
PBL:00003BD4 loc_3BD4                                ; CODE XREF: eeprom_3AFC+D2↑j
PBL:00003BD4                 BL              eeprom_3AD0
PBL:00003BD8                 CMP             R0, #0
PBL:00003BDA                 BNE             loc_3BD0
PBL:00003BDC
PBL:00003BDC loc_3BDC                                ; CODE XREF: eeprom_3AFC+E↑j
PBL:00003BDC                 CMP             R5, #0
PBL:00003BDE                 BEQ             loc_3BE4
PBL:00003BE0                 CMP             R4, #0
PBL:00003BE2                 BEQ             loc_3B0C
PBL:00003BE4
PBL:00003BE4 loc_3BE4                                ; CODE XREF: eeprom_3AFC+E2↑j
PBL:00003BE4                 MOVS            R0, R4
PBL:00003BE6                 ADD             SP, SP, #0x1C
PBL:00003BE8                 POP             {R4-R7}
PBL:00003BEA                 POP             {R3}
PBL:00003BEC                 BX              R3
PBL:00003BEC ; End of function eeprom_3AFC
PBL:00003BEC
PBL:00003BEC ; ---------------------------------------------------------------------------
PBL:00003BEE                 ALIGN 0x10
PBL:00003BF0 off_3BF0        DCD I2C_IBAD            ; DATA XREF: i2c_enable↑r
PBL:00003BF0                                         ; i2c_disable↑r ...
PBL:00003BF4 off_3BF4        DCD i2c_varDeviceSelectByte
PBL:00003BF4                                         ; DATA XREF: i2c_initRandomAccessRead+1A↑r
PBL:00003BF4                                         ; i2c_eepromRead+20↑r
PBL:00003BF8 ; ---------------------------------------------------------------------------
PBL:00003BF8                 PUSH            {R4,LR}
PBL:00003BFA                 BL              i2c_enable
PBL:00003BFE                 MOVS            R0, #0
PBL:00003C00 ; START OF FUNCTION CHUNK FOR eeprom_readAll
PBL:00003C00 ;   ADDITIONAL PARENT FUNCTION eeprom_3C10
PBL:00003C00
PBL:00003C00 eeprom_3C00                             ; CODE XREF: PBL:00003C0E↓j
PBL:00003C00                                         ; eeprom_3C10:eeprom_3C38↓j ...
PBL:00003C00                 POP             {R4}
PBL:00003C02                 POP             {R3}
PBL:00003C04                 BX              R3
PBL:00003C04 ; END OF FUNCTION CHUNK FOR eeprom_readAll
PBL:00003C06 ; ---------------------------------------------------------------------------
PBL:00003C06                 PUSH            {R4,LR}
PBL:00003C08                 BL              i2c_disable
PBL:00003C0C                 MOVS            R0, #0
PBL:00003C0E                 B               eeprom_3C00
IDA Decompile:

Code: Select all

// R0 = Start address of EEPROM to access (0x0000-0x07FF)
// aLen = Number of bytes to read from EEPROM/write to EEPROM
// a3 = ?
// a4 = ?
signed int __fastcall eeprom_3AFC(int inR0_eepromStartAddress, unsigned int inR1_len, int inR2_targetBaseAddress, int inR3)
{
  int R3; // r7
  signed int R4_transferDone; // r4
  unsigned int R5_len; // r5
  int R6; // r6
  int v8; // r0
  unsigned int i; // r0
  bool v10; // zf
  int v11; // r0
  int R0; // [sp+Ch] [bp-24h]
  int R2; // [sp+14h] [bp-1Ch]

  R0 = inR0_eepromStartAddress;
  R2 = inR2_targetBaseAddress;
  R3 = inR3;
  R4_transferDone = 0;
  R5_len = inR1_len;
  R6 = 0;
  while ( R5_len && !R4_transferDone )          // loop until len == 0 or transferDone is set
  {
    i2c_initRandomAccessRead((R0 + R6) & 0xFFFF);// Send base address to write data into EEPROM (in fact selects an eeprom-page)
    v8 = i2c_sendByte(R0 + R6);                 // write address-offset to EEPROM
                                                // Even if R0+R6 is given, only LOBYTE is used, which contains offset in page
    sub_14D6(v8);
    if ( R5_len < 16 )                          // if less than 16 Bytes to write, can go in one call without change the page
    {
      for ( i = 0; i < R5_len; i = (i + 1) & 0xFFFF )
      {
        I2C_IBDR[0] = R3 ? -1 : *(_BYTE *)(R2 + R6 + i);
        while ( !(I2C_IBSR[0] & 2) )
          ;
        I2C_IBSR[0] |= 2u;
        R4_transferDone = 1;
        v10 = (I2C_IBSR[0] & 1) == 0;
        I2C_IBSR[0] &= 1u;
        if ( !v10 )
          break;
        R4_transferDone = 0;
      }
    }
    else                                        // more than 16 Bytes to write, use chunked transfer
    {
      for ( i = 0; 16 - ((R0 + R6) & 0xFu) > i; i = (i + 1) & 0xFFFF )
      {
        I2C_IBDR[0] = R3 ? -1 : *(_BYTE *)(R2 + R6 + i);
        while ( !(I2C_IBSR[0] & 2) )
          ;
        I2C_IBSR[0] |= 2u;
        R4_transferDone = 1;
        v10 = (I2C_IBSR[0] & 1) == 0;
        I2C_IBSR[0] &= 1u;
        if ( !v10 )
          break;
        R4_transferDone = 0;
      }
    }
    R6 = (R6 + i) & 0xFFFF;
    R5_len = (R5_len - i) & 0xFFFF;
    I2C_IBCR &= 0xDFu;
    while ( I2C_IBSR[0] & 0x20 )
      ;
    while ( 1 )
    {
      v11 = eeprom_3AD0();
      if ( !v11 )
        break;
      sub_14D6(v11);
    }
  }
  return R4_transferDone;
}
Code analysis:

My hand-written C-code:
Go4IT
Pro
Posts: 967
Joined: 08 Feb 2019, 12:25

Re: How the Convers+ firmware accesses the external EEPROM

Post by Go4IT »

i2c_3c10 => ???

IDA Disassembler Code:

Code: Select all

PBL:00003C10 ; =============== S U B R O U T I N E =======================================
PBL:00003C10
PBL:00003C10 ; Ensure address and length of EEPROM data is within valid range
PBL:00003C10 ; Return value store in R0
PBL:00003C10 ; Return 0x10 on error
PBL:00003C10
PBL:00003C10 ; signed int __fastcall eeprom_3C10(int inR0_eepromStartAddress, int inR1_len, unsigned int inR2_targetBaseAddress)
PBL:00003C10 eeprom_3C10
PBL:00003C10
PBL:00003C10 ; FUNCTION CHUNK AT PBL:00003C00 SIZE 00000006 BYTES
PBL:00003C10
PBL:00003C10                 PUSH            {R4,LR}
PBL:00003C12                 MOVS            R3, R0
PBL:00003C14                 MOVS            R0, #0x60000000
PBL:00003C18                 CMP             R2, R0
PBL:00003C1A                 BCC             eeprom_3C3A
PBL:00003C1C                 LDR             R4, =0x60000800
PBL:00003C1E                 ADDS            R0, R2, R1
PBL:00003C20                 CMP             R0, R4
PBL:00003C22                 BHI             eeprom_3C3A
PBL:00003C24                 MOVS            R0, #0
PBL:00003C26                 ADDS            R0, R2, R0
PBL:00003C28                 LSLS            R0, R0, #0x10
PBL:00003C2A                 LSLS            R1, R1, #0x10
PBL:00003C2C                 LSRS            R1, R1, #0x10
PBL:00003C2E                 LSRS            R0, R0, #0x10
PBL:00003C30                 MOVS            R2, R3  ; a3
PBL:00003C32                 MOVS            R3, #0  ; a4
PBL:00003C34                 BL              eeprom_3AFC ; R0 = Start address of EEPROM to access (0x0000-0x07FF)
PBL:00003C34                                         ; aLen = Number of bytes to read from EEPROM/write to EEPROM
PBL:00003C34                                         ; a3 = ?
PBL:00003C34                                         ; a4 = ?
PBL:00003C38
PBL:00003C38 eeprom_3C38                             ; CODE XREF: eeprom_3C10+2C↓j
PBL:00003C38                 B               eeprom_3C00
PBL:00003C3A ; ---------------------------------------------------------------------------
PBL:00003C3A
PBL:00003C3A eeprom_3C3A                             ; CODE XREF: eeprom_3C10+A↑j
PBL:00003C3A                                         ; eeprom_3C10+12↑j
PBL:00003C3A                 MOVS            R0, #0x10
PBL:00003C3C                 B               eeprom_3C38
PBL:00003C3C ; End of function eeprom_3C10
PBL:00003C3C
PBL:00003C3E ; ---------------------------------------------------------------------------
PBL:00003C3E                 PUSH            {R3,LR}
PBL:00003C40                 MOVS            R2, R1
PBL:00003C42                 MOVS            R1, #0x60000000
PBL:00003C46                 CMP             R2, R1
PBL:00003C48                 BCC             loc_3C6C
PBL:00003C4A                 LDR             R3, =0x60000800
PBL:00003C4C                 ADDS            R1, R2, R0
PBL:00003C4E                 CMP             R1, R3
PBL:00003C50                 BHI             loc_3C6C
PBL:00003C52                 LSLS            R1, R0, #0x10
PBL:00003C54                 MOVS            R0, #0
PBL:00003C56                 ADDS            R0, R2, R0
PBL:00003C58                 LSLS            R0, R0, #0x10
PBL:00003C5A                 LSRS            R1, R1, #0x10
PBL:00003C5C                 LSRS            R0, R0, #0x10
PBL:00003C5E                 MOVS            R3, #1
PBL:00003C60                 MOV             R2, SP
PBL:00003C62                 BL              eeprom_3AFC ; R0 = Start address of EEPROM to access (0x0000-0x07FF)
PBL:00003C62                                         ; aLen = Number of bytes to read from EEPROM/write to EEPROM
PBL:00003C62                                         ; a3 = ?
PBL:00003C62                                         ; a4 = ?
PBL:00003C66
PBL:00003C66 loc_3C66                                ; CODE XREF: PBL:00003C6E↓j
PBL:00003C66                 ADD             SP, SP, #4
PBL:00003C68                 POP             {R3}
PBL:00003C6A                 BX              R3
PBL:00003C6C ; ---------------------------------------------------------------------------
PBL:00003C6C
PBL:00003C6C loc_3C6C                                ; CODE XREF: PBL:00003C48↑j
PBL:00003C6C                                         ; PBL:00003C50↑j
PBL:00003C6C                 MOVS            R0, #0x10
PBL:00003C6E                 B               loc_3C66
IDA Decompile:

Code: Select all

// Ensure address and length of EEPROM data is within valid range
// Return value store in R0
// Return 0x10 on error
signed int __fastcall eeprom_3C10(int inR0_eepromStartAddress, int inR1_len, unsigned int inR2_targetBaseAddress)
{
  signed int result; // r0

  if ( inR2_targetBaseAddress < 0x60000000 || inR2_targetBaseAddress + inR1_len > 0x60000800 )
    result = 0x10;                              // 0x10 = Access Out-Of-Range
  else
    result = eeprom_3AFC(
               (unsigned __int16)inR2_targetBaseAddress,
               (unsigned __int16)inR1_len,
               inR0_eepromStartAddress,
               0);
  return result;
}
Code analysis:

My hand-written C-code:
Locked