diff options
author | Raptor Engineering Development Team <support@raptorengineering.com> | 2017-12-29 00:34:27 -0600 |
---|---|---|
committer | Raptor Engineering Development Team <support@raptorengineering.com> | 2017-12-29 00:42:10 -0600 |
commit | 7cf24ce07484a6d86cd97a9af9c240da7e1a4043 (patch) | |
tree | 5dc9d7c253c8ace9e000ae35a7f5a1fcde2d59c4 /i2c_slave.v | |
parent | 7ecc8a1787abda92cb066865ac46f77c62113bcd (diff) | |
download | talos-system-fpga-7cf24ce07484a6d86cd97a9af9c240da7e1a4043.tar.gz talos-system-fpga-7cf24ce07484a6d86cd97a9af9c240da7e1a4043.zip |
Initial conversion pass VHDL to Verilog
Diffstat (limited to 'i2c_slave.v')
-rw-r--r-- | i2c_slave.v | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/i2c_slave.v b/i2c_slave.v new file mode 100644 index 0000000..2701ef4 --- /dev/null +++ b/i2c_slave.v @@ -0,0 +1,252 @@ +// Copyright © 2017 Raptor Engineering, LLC +// Copyright © 2014-2016 Peter Samarin +// All Rights Reserved +// +// See I2C_SLAVE_LICENSE file for licensing details + +module I2C_slave( + inout wire scl, + inout wire sda, + input wire clk, + input wire rst, + + // User interface + output wire read_req, + input wire [7:0] data_to_master, + output wire data_valid, + output wire [7:0] data_from_master + ); + + parameter [6:0] SLAVE_ADDR; + + + //---------------------------------------------------------- + parameter [2:0] + i2c_idle = 0, + i2c_get_address_and_cmd = 1, + i2c_answer_ack_start = 2, + i2c_write = 3, + i2c_read = 4, + i2c_read_ack_start = 5, + i2c_read_ack_got_rising = 6, + i2c_read_stop = 7; + // I2C state management + reg [2:0] state_reg = i2c_idle; + reg cmd_reg = 1'b0; + reg [31:0] bits_processed_reg = 0; + reg continue_reg = 1'b0; // Helpers to figure out next state + reg start_reg = 1'b0; + reg stop_reg = 1'b0; + reg scl_rising_reg = 1'b0; + reg scl_falling_reg = 1'b0; // Address and data received from master + reg [6:0] addr_reg = 1'b0; + reg [7:0] data_reg = 1'b0; // Delayed SCL (by 1 clock cycle, and by 2 clock cycles) + reg scl_reg = 1'b1; + reg scl_prev_reg = 1'b1; // Slave writes on scl + wire scl_wen_reg = 1'b0; + wire scl_o_reg = 1'b0; // Delayed SDA (1 clock cycle, and 2 clock cycles) + reg sda_reg = 1'b1; + reg sda_prev_reg = 1'b1; // Slave writes on sda + reg sda_wen_reg = 1'b0; + reg sda_o_reg = 1'b0; // User interface + reg data_valid_reg = 1'b0; + reg read_req_reg = 1'b0; + reg [7:0] data_to_master_reg = 1'b0; + + always @(posedge clk) begin + // Delay SCL by 1 and 2 clock cycles + scl_reg <= scl; + scl_prev_reg <= scl_reg; + + // Delay SDA by 1 and 2 clock cycles + sda_reg <= sda; + sda_prev_reg <= sda_reg; + + // Detect rising and falling SCL + scl_rising_reg <= 1'b0; + if (scl_prev_reg == 1'b0 && scl_reg == 1'b1) begin + scl_rising_reg <= 1'b1; + end + scl_falling_reg <= 1'b0; + if (scl_prev_reg == 1'b1 && scl_reg == 1'b0) begin + scl_falling_reg <= 1'b1; + end + + // Detect I2C START condition + start_reg <= 1'b0; + stop_reg <= 1'b0; + if (scl_reg == 1'b1 && scl_prev_reg == 1'b1 && sda_prev_reg == 1'b1 && sda_reg == 1'b0) begin + start_reg <= 1'b1; + stop_reg <= 1'b0; + end + + // Detect I2C STOP condition + if (scl_prev_reg == 1'b1 && scl_reg == 1'b1 && sda_prev_reg == 1'b0 && sda_reg == 1'b1) begin + start_reg <= 1'b0; + stop_reg <= 1'b1; + end + end + + //-------------------------------------------------------- + // I2C state machine + //-------------------------------------------------------- + always @(posedge clk) begin + // Default assignments + sda_o_reg <= 1'b0; + sda_wen_reg <= 1'b0; + + // User interface + data_valid_reg <= 1'b0; + read_req_reg <= 1'b0; + + case(state_reg) + i2c_idle : begin + if (start_reg == 1'b1) begin + state_reg <= i2c_get_address_and_cmd; + bits_processed_reg <= 0; + end + end + i2c_get_address_and_cmd : begin + if (scl_rising_reg == 1'b1) begin + if (bits_processed_reg < 7) begin + bits_processed_reg <= bits_processed_reg + 1; + addr_reg[6 - bits_processed_reg] <= sda_reg; + end + else if (bits_processed_reg == 7) begin + bits_processed_reg <= bits_processed_reg + 1; + cmd_reg <= sda_reg; + end + end + if (bits_processed_reg == 8 && scl_falling_reg == 1'b1) begin + bits_processed_reg <= 0; + if (addr_reg == SLAVE_ADDR) begin + // check req address + state_reg <= i2c_answer_ack_start; + if (cmd_reg == 1'b1) begin + // issue read request + read_req_reg <= 1'b1; + data_to_master_reg <= data_to_master; + end + end else begin + state_reg <= i2c_idle; + end + end + end + i2c_answer_ack_start : begin + //-------------------------------------------------- + // I2C acknowledge to master + //-------------------------------------------------- + sda_wen_reg <= 1'b1; + sda_o_reg <= 1'b0; + if (scl_falling_reg == 1'b1) begin + if (cmd_reg == 1'b0) begin + state_reg <= i2c_write; + end else begin + state_reg <= i2c_read; + end + end + end + i2c_write : begin + //-------------------------------------------------- + // WRITE + //-------------------------------------------------- + if (scl_rising_reg == 1'b1) begin + if (bits_processed_reg <= 7) begin + data_reg[7 - bits_processed_reg] <= sda_reg; + bits_processed_reg <= bits_processed_reg + 1; + end + if (bits_processed_reg == 7) begin + data_valid_reg <= 1'b1; + end + end + if (scl_falling_reg == 1'b1 && bits_processed_reg == 8) begin + state_reg <= i2c_answer_ack_start; + bits_processed_reg <= 0; + end + end + i2c_read : begin + //-------------------------------------------------- + // READ: send data to master + //-------------------------------------------------- + sda_wen_reg <= 1'b1; + sda_o_reg <= data_to_master_reg[7 - bits_processed_reg]; + if (scl_falling_reg == 1'b1) begin + if (bits_processed_reg < 7) begin + bits_processed_reg <= bits_processed_reg + 1; + end + else if (bits_processed_reg == 7) begin + state_reg <= i2c_read_ack_start; + bits_processed_reg <= 0; + end + end + end + i2c_read_ack_start : begin + //-------------------------------------------------- + // I2C read master acknowledge + //-------------------------------------------------- + if (scl_rising_reg == 1'b1) begin + state_reg <= i2c_read_ack_got_rising; + if (sda_reg == 1'b1) begin + // nack = stop read + continue_reg <= 1'b0; + end else begin + // ack = continue read + continue_reg <= 1'b1; + read_req_reg <= 1'b1; + + // request reg byte + data_to_master_reg <= data_to_master; + end + end + end + i2c_read_ack_got_rising : begin + // Wait for START or STOP to get out of this state + if (scl_falling_reg == 1'b1) begin + if (continue_reg == 1'b1) begin + if (cmd_reg == 1'b0) begin + state_reg <= i2c_write; + end else begin + state_reg <= i2c_read; + end + end else begin + state_reg <= i2c_read_stop; + end + end + end + i2c_read_stop : begin + // Wait for START or STOP to get out of this state + end + default : begin + end + endcase + //------------------------------------------------------ + // Reset counter and state on start/stop + //------------------------------------------------------ + if (start_reg == 1'b1) begin + state_reg <= i2c_get_address_and_cmd; + bits_processed_reg <= 0; + end + if (stop_reg == 1'b1) begin + state_reg <= i2c_idle; + bits_processed_reg <= 0; + end + if (rst == 1'b1) begin + state_reg <= i2c_idle; + end + end + + //-------------------------------------------------------- + // I2C interface + //-------------------------------------------------------- + assign sda = sda_wen_reg == 1'b1 ? sda_o_reg : 1'bZ; + assign scl = scl_wen_reg == 1'b1 ? scl_o_reg : 1'bZ; + //-------------------------------------------------------- + // User interface + //-------------------------------------------------------- + // Master writes + assign data_valid = data_valid_reg; + assign data_from_master = data_reg; + // Master reads + assign read_req = read_req_reg; + +endmodule
\ No newline at end of file |