diff options
Diffstat (limited to 'source/i2c.v')
| -rw-r--r-- | source/i2c.v | 391 |
1 files changed, 0 insertions, 391 deletions
diff --git a/source/i2c.v b/source/i2c.v deleted file mode 100644 index aca6452..0000000 --- a/source/i2c.v +++ /dev/null @@ -1,391 +0,0 @@ -/* - * i2c.v - * - * Copyright (C) 2018, 2019 Mind Chasers Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * function: I2C slave controller for communicating with internal controller - * - */ - - -`timescale 1ns /10ps - - -module i2c( - input rstn, - input clk, - - // external I2C I/O - input scl_i, // serial clock - output scl_o, // serial clock out - output scl_oe, // serial clock output enable - - input sda_i, // serial data in - output reg sda_o, // serial data out - output reg sda_oe, // serial data output enable - - // shared memory and controller data output - output [7:0] mem_do, - - // dedicated memory interface - output [10:0] mem_ad, - output mem_ce, - output reg mem_we, - input [7:0] mem_di, - - // dedicated controller write interface - output reg cont_we, - output cont_done, - - // Controller->FIFO input interface - input tx_fifo_empty, // use for control and pass as msbit of data - input [6:0] fifo_di, // data from FIFO to transmit on I2C - output reg fifo_re - -); - -localparam CONT_SEL = 7'h10, - DPRAM_SEL = 7'h20, - SCI_SEL = 7'h30; - -// slave-related signals - -reg scl_high, scl_low; -reg [4:0] bit_cnt; // cnt the bits received -reg start; // falling SDA while SCL is high -reg stop; // rising SDA while SCL is high -reg run; // assert while running and counting bits -reg rwn; // follows address - -reg [6:0] dev_ad; // 7-bit device address -reg [7:0] addr; // address -reg [7:0] d; // data received from external i2c controller during an I2C write -wire target_sel, dpram_sel, cont_sel; -reg scl_i_m1, scl_i_m2, sda_i_m1; // delayed versions of the inputs -reg ack; - -wire [7:0] i_di; // internal data in muxed from external sources (e.g., DPRAM and controller) - -assign i_di = dpram_sel ? mem_di : { tx_fifo_empty, fifo_di }; - -// master-related signals -//reg halt; // assert if we lose arbitration, detect a zero in place of a one - -assign scl_oe = 1'b0; - -// since it's open drain, never drive the outputs high -assign scl_o = 1'b0; - -// slave interface - -/* - debounce and capture the asynch inputs - delayed signals, by default, they're 1 since they're open drain -*/ -always @(posedge clk or negedge rstn) - begin - if (!rstn) - begin - scl_i_m1 <= 1'b1; - scl_i_m2 <= 1'b1; - sda_i_m1 <= 1'b1; - end - else - begin - scl_i_m1 <= scl_i; - scl_i_m2 <= scl_i_m1; - sda_i_m1 <= sda_i; - end - end - -/* - create a one shot when scl is first stable high - use this to register inputs -*/ -always@(posedge clk or negedge rstn) - begin - if (!rstn) - scl_high <= 1'b0; - else if (scl_i && scl_i_m1 && ~scl_i_m2) - scl_high <= 1'b1; - else - scl_high <= 1'b0; - end - -/* - create a one shot when scl is first stable low - use this to register outputs -*/ -always@(posedge clk or negedge rstn) - begin - if (!rstn) - scl_low <= 1'b0; - else if ( !scl_i && !scl_i_m1 && scl_i_m2) - scl_low <= 1'b1; - else - scl_low <= 1'b0; - end - -/* - one shot start/restart bit, sda 1 to 0 anytime while scl is high -*/ -always @(posedge clk or negedge rstn) - begin - if (!rstn) - start <= 1'b0; - else if ( scl_i == 1'b1 && scl_i_m1 == 1'b1 && sda_i == 1'b0 && sda_i_m1 == 1'b1 ) - start <= 1'b1; - else - start <= 1'b0; - end - -/* - one shot stop bit, sda 0 to 1 anytime while scl is high -*/ -assign cont_done = stop & ~rwn; - -always @(posedge clk or negedge rstn) - begin - if (!rstn ) - stop <= 1'b0; - else if ( scl_i == 1'b1 && scl_i_m1 == 1'b1 && sda_i == 1'b1 && sda_i_m1 == 1'b0 ) - stop <= 1'b1; - else - stop <= 1'b0; - end - -/* - This I2C block runs between start and stop while run == 1 -*/ -always @(posedge clk or negedge rstn) - begin - if (!rstn) - run <= 1'b0; - else if ( start ) - run <= 1'b1; - else if ( stop ) - run <= 1'b0; - end - - -/* - bit_cnt indicates the state of the i2c transfer: - start/stop act as synchronous resets - otherwise, bit_cnt can change when scl is low - - 31 (1F): reserved for first cycle after start / idle state - 0:6: dev_ad - 7: rwn, ACK is clocked out - 8: - 9:16: if rwn == 0 addr, else d, ACK is clocked out on 16 - 17: ( restart to 9 if read ) - 18:25: write data, ack write data is clocked out on 25 - 26: ( restart to 18 for write data ) -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - bit_cnt <= 5'h1f; - else if ( start || stop ) - bit_cnt <= 5'h1f; - else if ( run && scl_low ) - begin - if ( rwn && bit_cnt == 5'd17 ) - bit_cnt <= 5'd9; - else if ( bit_cnt == 5'd26 ) - bit_cnt <= 5'd18; - else - bit_cnt <= bit_cnt + 1; - end - end - -/* - shift device address (dev_ad) into the module from the i2c bus -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - dev_ad <= 7'h0; - else if ( stop ) - dev_ad <= 7'h0; - else if ( run && scl_high && bit_cnt <= 5'h6 ) - dev_ad <= { dev_ad[5:0], sda_i }; - end - - -/* - shift I2C memory addr into the module from the i2c bus during first cycle - auto increment on subsequent byte reads during same I2C cycle -*/ -always @(posedge clk or negedge rstn) - begin - if (!rstn) - addr <= 8'h00; - else if ( run && scl_high) - begin - if ( !rwn && bit_cnt >= 5'd9 && bit_cnt <= 5'd16 ) - addr <= { addr[6:0], sda_i }; - else if ( (rwn && bit_cnt == 5'd17) || (!rwn && bit_cnt == 5'd26) ) - addr <= addr + 1; - end - end - - -/* -* shift write data (d) into the module from the i2c bus. -*/ -always @(posedge clk or negedge rstn) - begin - if (!rstn) - d <= 8'ha5; - else if ( run && scl_high && !rwn && !start && bit_cnt >= 5'd18 && bit_cnt < 5'd26 ) - d <= { d[6:0], sda_i }; - end - - - -assign dpram_sel = ( dev_ad == DPRAM_SEL ) ? 1 : 0; -assign cont_sel = ( dev_ad == CONT_SEL ) ? 1 : 0; -assign target_sel = cont_sel | dpram_sel; - -/* - register ack during I2C reads when bit_cnt == 17 -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - ack <= 1'b0; - else if ( run && scl_high && rwn && bit_cnt == 5'd17 ) - ack <= ~sda_i; - end - -/* - register rwn bit -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - rwn <= 1'b1; - else if ( stop ) - rwn <= 1'b1; - else if ( run && scl_high && bit_cnt == 5'd7 ) - rwn <= sda_i; - end - -/* - sda_oe logic, note that the bit_cnt will be changing simultaneously, so it's one behind -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - begin - sda_oe <= 1'b0; - sda_o <= 1'b1; - end - else if ( stop ) - begin - sda_oe <= 1'b0; - sda_o <= 1'b1; - end - else if ( scl_low ) - begin - // ack the first byte written by master if it's addressed to our I2C slave - if ( target_sel && bit_cnt == 5'd7 ) - begin - sda_oe <= 1'b1; - sda_o <= 1'b0; - end - // ack write address - else if ( target_sel && rwn == 1'b0 && bit_cnt == 5'd16 ) - begin - sda_oe <= 1'b1; - sda_o <= 1'b0; - end - // ack write data - else if ( target_sel && rwn == 1'b0 && bit_cnt == 5'd25 ) - begin - sda_oe <= 1'b1; - sda_o <= 1'b0; - end - // drive read data - else if ( target_sel && rwn == 1'b1 && bit_cnt >= 5'd8 && bit_cnt <=5'd15 ) // xmt data for read cycle - begin - sda_oe <= 1'b1; - sda_o <= i_di[7-bit_cnt[2:0]]; - end - // drive data for first bit of burst read cycle, multi-byte if we get an ack - // if no ack, then the burst read or single read is done - else if ( target_sel && rwn == 1'b1 && ack && bit_cnt == 5'd17 ) - begin - sda_oe <= 1'b1; - sda_o <= i_di[7]; - end - else - begin - sda_oe <= 1'b0; - sda_o <= 1'b1; // don't care - end - end - end - -/* DPRAM Control */ -assign mem_ad = {3'b0, addr}; -assign mem_ce = 1'b1; - -/* - driver: mem_do - shared between both cont and dpram interfaces - drive addr for first byte since this is the controller cmd -*/ -assign mem_do = ( bit_cnt <= 5'd17 ) ? addr : d; - -// mem_we bit -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - mem_we <= 1'b0; - else if ( run && scl_high && dpram_sel && !rwn && bit_cnt == 5'd26 ) - mem_we <= 1'b1; - else - mem_we <= 1'b0; - end - -// cont_we bit is asserted at the end of both command and data bytes -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - cont_we <= 1'b0; - else if ( run && scl_high && cont_sel && !rwn && (bit_cnt == 5'd26 || bit_cnt == 5'd17) ) - cont_we <= 1'b1; - else - cont_we <= 1'b0; - end - -/* -* drive: fifo_re -* Stop asserting during a burst if we don't get an ACK (use sda_i for real-time ACK) -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - fifo_re <= 1'b0; - else if ( run && scl_high && cont_sel && rwn && !tx_fifo_empty && ( bit_cnt == 5'd8 || ( bit_cnt == 5'd17 && !sda_i ) ) ) - fifo_re <= 1'b1; - else - fifo_re <= 1'b0; - end - - -endmodule |



