> From the Opencores code though, I can see that it only checks for clock stretching when the internal counter(cnt) reaches 0
Could you go through the details of that please? From my reading, the only time it WON'T n recognise clock-stretch is when "ctr" is zero:
This bit generates dscl_oen one master (undivided) clock behind iscl_oen, which is driven when the state machine tries to drive SCL high (lets it go to pull high):
elsif (clk'event and clk = '1') then
dscl_oen <= iscl_oen;
"slave_wait" is generated when "iscl_oen" goes high here:
elsif (clk'event and clk = '1') then
slave_wait <= (iscl_oen and not dscl_oen and not sSCL) or (slave_wait and not sSCL);
From me reading, that means that slave_wait should be set on every assertion of "iscl_oen" and should latch on until "sSCL" gets back through the syncronizers and filters and then clear.
This code is clocked by the master clock and generates the 5-times-bit-rate clock:
gen_clken: process(clk, nReset)
...
elsif (clk'event and clk = '1') then
if ((rst = '1') or (cnt = 0) or (ena = '0') or (scl_sync = '1')) then
cnt <= clk_cnt;
clk_en <= '1';
elsif (slave_wait = '1') then
cnt <= cnt;
clk_en <= 0;
else
cnt <= cnt - 1;
clk_en <= 0;
end if;
That counter is meant to be STALLED (and stop counting) when "slave_wait" is true. So that should be checked on every clock EXCEPT when "cnt" is zero.
That's probably a bug. The test against slave_wait should take precedence over the test for (cnt = 0).
Does my analysis match up with what you're seeing with SignalTap?
> As for the AL logic, as you correctly pointed out, the code does not check to see that SCL actually went high before issuing the arbitration loss.
I think it does. The AL logic only checks for the problem when "sda_chk" is high and that only happens during the third part of a write cycle.
Eight Write cycles are run for the data byte being written, then a Read cycle runs to read the Ack. Then it runs another Write cycle which is where the Arb Failure is happening. So it does this, and note each state transition is caused by the above code generating "clk_en" at the decimated rate:
wr_a SCL Low Write cycle writing last bit or a byte
wr_b SCL High
wr_c SCL High sda_chk=1
wr_d SCL Low
idle SCL Low
rd_a SCL Low Read Cycle reading the ACK
rd_b SCL High
rd_c SCL High
rd_d SCL Low
idle SCL Low
wr_a SCL Low Write Cycle for next byte
wr_b SCL High
wr_c SCL High sda_chk=1
wr_d SCL Low
At the start of the last "wr_b" SCL is driven high, and "slave_wait" should be set. HOWEVER this is where the MCF stretches the clock, so it should be in more detail:
wr_a SCL Low Write Cycle for next byte
"cnt" has to go 10, 9, 8, ... 4, 3, 2, 1, 0
wr_b SCL High
"cnt" goes10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, ... 4, 3, 2, 1, 0, stuck at "9" while waiting for "slave_wait" to go away
wr_c SCL High sda_chk=1 **NOW** the code check for Arbitration Lost.
"cnt" has to go 10, 9, 8, ... 4, 3, 2, 1, 0
wr_d SCL Low
So it should work. From your CodeTap view, why isn't the above working like that?
> I would have thought though that this fix would have been implemented a long time ago.
It has been reported at least once and then not followed up. I would guess that most people use the I2C core to connect small memory, temperature or time devices to FPGAs that never stretch the clock. So most people don't run into this, and anybody that has had the problem hasn't put in the time to understand it and fix it.
The whole point of Open Projects is that the users have an obligation to find, report, and sometimes even fix the bugs... don't you?
I think you have enough information to file a detailed bug report on this on OpenCore.org including a pointer back to this discussion and your oscilloscope traces.
(Edit) My regisration on OpenCores has come through so I've already put in a short description and pointer to here:
Arb Lost due to Clock Stretch detection failure :: Bugtracker :: OpenCores
> I'm also still looking at the Freescale side to see if this behavior is typical. I have some Freescale folks looking at it now.
Why? The OpenCore is clearly broken. It doesn't handle Clock Stretching. The Freescale part you're using has to clock stretch, and is doing it legally. Fix the I2C Core or use something else.
Tom