From a768072bbe3ea9762ee3325ffb5c42c3156e7859 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Wed, 3 Sep 2014 10:07:59 -0400 Subject: fix i2c read buffer overrun If a client requests data beyond the end of the coretest response, i2c_core could wait forever for txd_syn to be asserted, locking up the system. The current solution is to assume that data will be available by the time the client sends the READ_CMD and then issues the i2c read request; if no data is available, i2c_core returns 0x00. Given the relative speeds of the i2c bus and the FPGA, this seems justifiable, but it could use more scrutiny. --- src/rtl/i2c_core.v | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/rtl/i2c_core.v b/src/rtl/i2c_core.v index 3e2772a..a026545 100644 --- a/src/rtl/i2c_core.v +++ b/src/rtl/i2c_core.v @@ -175,14 +175,12 @@ module i2c_core ( I2C_nstate = ((SDA_cstate == SDA_FALL) && (SCL_cstate == SCL_HIGH)) ? I2C_RESTART : // repeated start ((I2C_bitcnt > 4'h7) && (SCL_cstate == SCL_FALL)) ? I2C_RXD_SYN : I2C_WR_DATA; end - // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv I2C_RXD_SYN: begin // put data on the coretest bus I2C_nstate = I2C_RXD_ACK; end I2C_RXD_ACK: begin // wait for coretest ack I2C_nstate = rxd_ack ? I2C_ACK_WR : I2C_RXD_ACK; end - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ I2C_ACK_WR: begin // trigger the ack response (pull SDA low until next falling edge) // and stay in this state until the next falling edge of SCL I2C_nstate = (SCL_cstate == SCL_FALL) ? I2C_END_WR : I2C_ACK_WR; @@ -192,14 +190,15 @@ module i2c_core ( end // read branch - // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - I2C_TXD_SYN: begin // get data from the coretest bus - I2C_nstate = txd_syn ? I2C_TXD_ACK : I2C_TXD_SYN; + I2C_TXD_SYN: begin // get data from the coretest bus + // if data isn't available (txd_syn isn't asserted) by the time we + // get to this state, it probably never will be, so skip it + I2C_nstate = txd_syn ? I2C_TXD_ACK : I2C_RD_DATA; end I2C_TXD_ACK: begin // send coretest ack + // hold ack high until syn is lowered I2C_nstate = txd_syn ? I2C_TXD_ACK : I2C_RD_DATA; end - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ I2C_RD_DATA: begin // 8 bits to get the read data I2C_nstate = ((SDA_cstate == SDA_FALL) && (SCL_cstate == SCL_HIGH)) ? I2C_RESTART : // repeated start ((I2C_bitcnt > 4'h7) && (SCL_cstate == SCL_FALL)) ? I2C_ACK_RD : I2C_RD_DATA; @@ -300,8 +299,7 @@ module i2c_core ( I2C_daddr <= I2C_daddr; I2C_rdata <= I2C_rdata; end // case: I2C_WR_DATA - // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - I2C_RXD_SYN: begin // put data on the coretest bus + I2C_RXD_SYN: begin // put data on the coretest bus and raise syn rxd_syn_reg <= 1; end I2C_RXD_ACK: begin // wait for coretest ack @@ -310,7 +308,6 @@ module i2c_core ( rxd_syn_reg <= 0; end end - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ I2C_ACK_WR: begin SDA_pd <= 1'b1; // active pull down ACK I2C_daddr <= I2C_daddr; @@ -327,7 +324,6 @@ module i2c_core ( end // read branch - // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv I2C_TXD_SYN: begin // get data from the coretest bus if (txd_syn) begin @@ -341,7 +337,6 @@ module i2c_core ( txd_ack_reg <= 0; end end - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ I2C_RD_DATA: begin // shift out data on falling edges of clock SDA_pd <= I2C_rdata[7] ? 1'b0 : 1'b1; if( SCL_cstate == SCL_RISE ) begin -- cgit v1.2.3