Decode my I2C Stream!

5

0

From the sandbox!

I2c or TWI, is a 2 way stream that uses 2 signal lines: SDA and SCK. This is used in sensors to communicate between a master and a slave. So, how does this work? Well, here's the boilerplate:

Input:

1 I2c command represented by 2 strings where - is a high signal, and _ is low. SDA is always the first string.

Output:

The debug info for that command, including weather
it's a READ or WRITE,
which address it's to (in Hex), and
what memory the signal's addressing(in Hex).
if the transmission is invalid, print "Invalid"

How does I2c work, then?

enter image description here Image from Sparkfun!
An i2c request has 2 9-bit parts. the address and the payload. The address comes first.

A bit is only READ when the clock signal goes from LOW to HIGH. Going from HIGH to LOW is not a valid reading event.

The address is 7 bits, which come after both SDA and SCL are LOW. This is sent by the master.

After the 7th bit, the next is sent by the master, which is the READ Bit. If this bit is HIGH, then it's a READ operation, if the bit is LOW, then it's a WRITE operation.

After the 8 bits, the slave responds with an ACK bit, if the ACK bit is HIGH, then the entire transmission is invalid.

Then, the second transmission is sent.

The first 8 bits represent the data address that the master is READing or WRITEing.

and the final bit is the 2nd ACK bit. If this bit is HIGH, then the entire transmission is invalid.

Examples:

Raw IN: ["--____--__----__----__----__--__--__","___-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-"]
SDA: --____--__----__--__--__----__--__--__ 
SCK: ___-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
Result: 0 0 1 0 1 1 0 1 0 1 0 1 1 0 1 0 1 0
Parse:  addr[22] op:[R] data:[181] success[yes]  
out: address:0x16 op:Read data:0xB5

Raw IN: ["--____--__----__------__----__--__--__","___-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-"]
SDA: --____--__----__------__----__--__--__ 
SCK: ___-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
Result: 0 0 1 0 1 1 0 1 1 1 0 1 1 0 1 0 1 0
Parse:  addr[22] op:[R] data:[181] success[No]  
out: invalid
Note: (the ACK bit flipped high);

Raw IN: ["--_-_--__--_-__--_-_-__-_-_-_--__--__-","___-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-"]
SDA: --_-_--__--_-__--___-__-_-_-_--__--__- 
SCK: ___-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
Result: 0 0 1 0 1 1 0 1 0 1 0 1 1 0 1 0 1 0
Parse:  addr[22] op:[R] data:[181] success[yes]
out: address:0x16 op:Read data:0xB5 
Note: (the bit before the change is read.);

Raw IN: ["--_____--___-----___--___--___-----__---____--____","___--_-_--_--_-_--_-_--_-_--_-_--_-_--_-___-_--__-"]
SDA: --_____--___-----___--___--___-----__---____--____
SCK: ___--_-_--_--_-_--_-_--_-_--_-_--_-_--_-___-_--__-
Result: 0  0 1  0  1 1  0 1  0 1  0 1  1 0  1   0 1   0
Parse:  addr[22] op:[R] data:[181] success[yes] 
out: address:0x16 op:Read data:0xB5
Note: (Clock signals Don't have to be perfect.);    

Raw IN: ["--_____--___-----___--___--___-----__---____--____","___---_-_--_-___-_--__-"]
SDA: --_____--___-----___--___--___-----__---____--____
SCK: ___---_-_--_-___-_--__-
Result: 0   0 1  0   1 0   1
Parse:  Invalid
out: invalid
Note: (They do however, have to match the length of the data dignal.);

Extra rules:

The 0x prefix must be appended to all hex values in the output, as a formality. The input is strictly - for HIGH and _ for LOW, and will always be 2 separate strings with a separator character. If both SDA and SCL change at the same time, then the previous bit is read.

This is a code golf, so whoever creates a running program in the least number of bytes wins! Good luck

tuskiomi

Posted 2017-06-15T13:35:48.333

Reputation: 3 113

Is the third example incorrect..? It seems that the first result bit should be set. – Tyler MacDonell – 2017-06-15T19:59:06.760

@TylerMacDonell i looked it over, and it is correct. For this, imagine that sda is pushed forward (right) half a character – tuskiomi – 2017-06-15T20:42:51.053

Oops, misunderstood this part: If both SDA and SCL change at the same time, then the previous bit is read. – Tyler MacDonell – 2017-06-15T20:47:00.790

"The 0x prefix must be appended to all hex values in the output" Do you mean "prepended"? – Jordan – 2017-06-15T21:44:41.143

I'm a bit disappointed that you didn't take most of my Sandbox feedback into account. (For example, the output format is still underspecified; your Java "model answer" doesn't match the format given in the test cases, and most of the details of the format are left entirely unspecified in the prose.) This makes it hard to compete fairly, because it's unclear how many corners can be cut in the format to save bytes. – None – 2017-06-16T02:28:02.650

@ais what do you mean? I added the extra rules section for that – tuskiomi – 2017-06-16T02:29:57.150

As a simple example, you have address: in the test cases, and Addr: in your model answer. Those are different numbers of bytes. Just how much can someone abbreviate those whilst still complying with the question? As this is [tag:code-golf], any abbreviation that could be used there, must be, in order to save bytes. Additionally, there are some cases that are relevant, but don't come up in your testcases (such as leading zeroes on the addresses), so it's unclear how they should work. These aren't the only problems either. – None – 2017-06-16T02:32:15.547

@ais leading zeroes are like any other number, i don't think i understand. You mean instead of writing 0010110, writing 10110, right? – tuskiomi – 2017-06-16T02:34:18.093

If the address is 12 (decimal), is that written as 0x0C or 0xC, or does it not matter? (If you'd followed my advice in the Sandbox to allow a more flexible output format, this sort of thing wouldn't be an issue.) – None – 2017-06-16T02:36:22.323

@ais output is pretty flexible. As long as you use the 0x notation and have a designator for each of the three data types, I'd say you're valid. The output shouldn't be as much of a problem as the input. – tuskiomi – 2017-06-16T02:39:54.460

Concretely: what's the shortest possible designator for each of the three values? This needs to be stated in the question! – None – 2017-06-16T02:41:10.723

@ais523 it's flexible, it could be any one character token that you like, so long as it's documented in your answer. – tuskiomi – 2017-06-16T13:28:33.383

1The SDA and SCK in the first example's raw input does not match the signals on the following two lines – PunPun1000 – 2017-06-19T18:51:35.923

1I did this once in college, that was enough lol. – Magic Octopus Urn – 2017-07-24T19:22:55.760

Answers

2

GNU sed, 427 378 364 + 1 = 428 379 365 bytes

+1 byte for -r flag. Takes colon-separated input. Output format is e.g. a0x16oRd0xB5, where a precedes the address, o precedes the operation (R or W), and d precedes the data.

Why do I do this to myself?

s/^/:/
:Q
s/:.+:$|::./E/
s/:(.).(.*):_-/\1:x\2:x/
s/:./:/g
tQ
s/(.{7})(.)(.)(.*)(.):/#\3\1%\2#\5\4:/
/#-|E/{z;iinvalid
q}
s/#./#/g
:R
s/[:%]/!u0;%0123456789ABCDEF,/
:A
s/((-)|_)!/!\2/
s/!-(u+)(.+);/!\1\2;\1/
:
/u%/!bZ
s/F;/;0/
t
s/(.);(.*%.*\1)(.)/\3\2\3/
s/u;/u1/
s/u(u*)%/;\1%/
b
:Z
s/!(u+)/!\1\1/
/#!/!bA
s/#!u+(.+);.+,/\1/
/:/bR
s/(..)(.)/a0x\1o\2d0x/
y/-_/RW/

Try it online!

Explanation

The code works in two phases. In phase 1 we turn the two signals into a single binary string. This is the easy part.

# Prepend a `:`
s/^/:/
:Q
  # Check for length mismatch (one string exhausted before the other)
  s/:.+:$|::./E/

  # If the first two characters of SCK are `_-`, record first character of SDA
  # and discard second; replace each pair with `x` so we can unconditionally...
  s/:(.).(.*):_-/\1:x\2:x/

  # ...consume a character from each signal
  s/:./:/g

  # If we did a substitution, branch to :Q
  tQ

If this was our input:

--_____--___-----___--___--___-----__---____--____:___--_-_--_--_-_--_-_--_-_--_-_--_-_--_-___-_--__-

...we now have this:

__-_--_-_-_--_-_-_:

Before phase 2, we reformat this to make it a little easier to work with:

# Reformat `.......ma........b:` to `#a.......%m#b........:`
s/(.{7})(.)(.)(.*)(.):/#\3\1%\2#\5\4:/

# Check if ACK high or length mismatch; if either, print error and quit
/#-|E/{z;iinvalid
q}

# Delete ACK bits
s/#./#/g

Now we have:

#__-_--_%-#-_--_-_-:

In phase 2, we convert each run of _ and - (0 and 1) to a hexadecimal number. Because sed doesn't do math, this is almost half of the code. It actually takes each binary digit, converts it to unary, and then increments the hexadecimal number once for each unary digit.

:R
# Initialize: u = magnitude (unary 1); 0 = running sum (decimal); lookup table
s/[:%]/!u0;%0123456789ABCDEF,/

:A
  # Take the least-significant bit; if it's 0 drop it
  s/((-)|_)!/!\2/

  # If 1 (-), remove it and copy current magnitude (`u+`) after `;`
  s/!-(u+)(.+);/!\1\2;\1/

  # Increment number for each `u` after `;`
  # e.g. 12;uu -> 13uu -> 13;u -> 14u -> 14;
  :
    # No more `u`s, branch to :Z
    /u%/!bZ

    # Carry: 1F;u -> 1;0u -> 20u -> 20;
    s/F;/;0/
    t

    s/(.);(.*%.*\1)(.)/\3\2\3/
    s/u;/u1/

    # Drop a `u`; branch to `:`
    s/u(u*)%/;\1%/
    b

  :Z
    # Double magnitude: !uu3 -> !uuuu3
    s/!(u+)/!\1\1/
    # If there are binary digits left, branch to :A
    /#!/!bA

# Clean up
s/#!u+(.+);.+,/\1/

# Branch to :R for the second number
/:/bR

# Format output
s/(..)(.)/a0x\1o\2d0x/
y/-_/RW/

Before formatting, we have:

16-B5

All that remains is to add a prefix to each hexadecimal number change the operation (- or _) to read (R) or write (W), giving us:

a0x16oRd0xB5

Jordan

Posted 2017-06-15T13:35:48.333

Reputation: 5 001