Serial Protocol

Introduction
This page is dedicated to understanding the synchronous serial protocol used between the Toshiba chip (aka TX19) running A firmware and the Expeed chip (aka FR80) running B firmware.

Message structure
The protocol is based on messages of the form

 =  

The header itself is built as :

 =  

,  and are one byte each, and is  bytes long

Synchronous serial dialog
As the dialog is synchronous, one of the chips (A) is the master and generates the clock signal. If no communication is going on, no clock signal is generated. When the master starts emitting a clock signal, bytes have to be exchanged both ways at the same time: at each clock pulse, one bit is sent and one bit is sampled by the master. So it's not a simple question/answer dialog where each speaks in turn: as soon as the A chip starts sending bytes, it also "receives" (samples) the signal available on its Data-In pin.

Consequently, for each message transmitted from A to B, a message has to be transmitted from B to A. If one of the chips has nothing to transmit, it will have to send a "null message" instead: function_id = 0, no payload (length=0) and corresponding checksum (0xFF). So a "null message" is "00 00 FF"

Another consequence is that the same number of bytes has to be sent in each direction. If messages don't have the same length, dummy data (zeroes) have to be sent as "stuffing" after the message by the chip sending the shortest message.

Acknowledge
Once two messages have been exchanged as explained above, each chip validates the received message and sends an byte, which is either the received message's id if successful (ACK) or 0xFF if there was an error (NACK).

Example
At startup, FR80 wants to send a message with function id 01 (firmware version) while TX19 does not have any message yet, so it will send a null message (the request for a communication to happen involves a protocol using 2 external lines, see end of page). Let's follow that step by step:

1. TX19 writes the header of its message (00 00) at the same time that FR80 sends the header of its own message (01 03):
 * TX19 sends 00 00
 * FR80 sends 01 03

2. Then come the payload and checksum: For FR80, the payload of message 1 is the firmware version: ascii "101" is encoded as 0x31 0x30 0x31, and the checksum is computed as 0x69. For TX19, null message has no payload and checksum is 0xFF.

Thanks to the headers, the TX19 knows that the FR80 still has 4 bytes (3+checksum) to send while it has only 1 byte to send (0+checksum). So 4 more bytes will have to be exchanged for the messages to be transmitted, and the TX19 will generate the clock for 4 more bytes, and will use dummy bytes to complete its own message :
 * TX19 sends 00 00 FF 00 00 00
 * FR80 sends 01 03 31 30 31 69

3. Then both chips check that the message they received is valid (thanks to the checksum), and as it's OK, they acknowledge it by replying with each other's message id:
 * TX19 sends 00 00 FF 00 00 00 01
 * FR80 sends 01 03 31 30 31 69 00

The longer session below is left to decode as an exercise for the reader :-) :
 * TX19 sends 00 00 FF 00 00 00 01 - 02 06 01 01 00 00 00 00 F5 00 00 00 00 00 00 00 0B - 04 1A 00 01 00 00 00 00 00 03 00 00 00 80 00 C0 00 00 80 00 00 00 02 00 00 C0 00 00 5B 00
 * FR80 sends 01 03 31 30 31 69 00 - 0B 0D 00 00 08 00 00 00 00 00 00 00 00 00 00 DF 02 - 00 00 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04

FR Function table
A constant table is stored in the B firmware at 0x00267CF4-0x00268203 and contains 0x51 records of 0x10 bytes. I call it "table10". Here it is : The fields are: Note : if param_len is 0 (no param), then no param address is needed (e.g. ids 00, 1D, 3F, 4D)
 * func_addr : the address of the function
 * param_addr : the address in RAM where function parameters will be stored
 * param_len : length of the parameters (in bytes)
 * flags : holds 2 booleans as bit1 and bit0

Structures
The queue of messages to be sent are stored in a number of structures and variables

dlists as fifos
Many aspects of the firmware rely on double linked lists, or dlists, which are composed of individual elements of the form



Each element holds two pointers, one pointing to the previous element (ptr_prev), one to the next element (ptr_next), and a value, or payload, which is the actual data. dlists can be used as queues (FIFO), stacks (LIFO) or plain lists.

The implementation uses a "sentinel" element to indicate an empty list, which is an element that has both ptr_prev and ptr_next pointing to that element's own address.

In the case of CSIO, dlists are used as stacks, each new element being "pushed" in the first position, and each element to be processed being "popped" from that position. To avoid confusion of such stacks with the program stack, I'll use here the name lifo.

The serial protocol uses two lists containing elements to be processed, and the process works with one or the other, according to a value set by one of the functions that is called via messages (function_id=0x0E).

Elements array
The elements that are inserted in the fifos are built as an array of 0x51 elements of 0xC bytes, stored in RAM at 0x8F89965C-0x8F899A1C. I call it "arrayC"

Each element, to be part of a dlist, has the structure . In this case, the consists of 4 bytes : , where:
 * is a bitmap. Bits 7 and 6 are used
 *  is initialized with the index of the record in arrayC (0x00-0x50)
 * is an unused byte

Variables
The following variables are used:

EventFlags
Event flag 12:

bit 0 : not relevant

bit 1 : set to 0 upon init when enabling int_16, and waiting for it to become 1 meaning data has been received and is ready. Set by int_16 mode 1/2/3

bit 2 : set by int_16 mode 2

bit 3 : set to 0 before reading head of lifo, waiting for it to become 1 before trying again if empty. Set to 1 when an item is pushed into a lifo

bit 4 : int_1B occurred, success

bit 5 : int_1B occurred, failure

bit 6 : set before starting timer, and waiting the timer interrupt to be triggered

bit 7 : set when fifo and array init is done

Communication Request
Two special addresses are used in the FR code controlling the exchanges, that are really registers corresponding to I/O ports. They are:

0x50000100, bit 5:

This is pin 5 of FR Port 0 and is physically connected to pin 3 of port 5 of the TX CPU (INTF).

It is set by FR when serial interrupt occurs in modes 1 or 2, or timer interrupt occurs in mode 2, to request serial transmission. Reset after transmitting param part 1 or 2, or (N)ACK.

0x50000107, bit 6:

This is pin 6 of FR port 7 and is physically connected to pin 3 of port C of the TX CPU (INT16).

It is tested by FR during init of external interrupt 6 to see if a transmission is requested by the TX CPU.