mirror of
https://github.com/alexforencich/verilog-ethernet.git
synced 2025-01-21 06:53:10 +08:00
896 lines
29 KiB
Coq
896 lines
29 KiB
Coq
|
/*
|
||
|
|
||
|
Copyright (c) 2015-2016 Alex Forencich
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
of this software and associated documentation files (the "Software"), to deal
|
||
|
in the Software without restriction, including without limitation the rights
|
||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
copies of the Software, and to permit persons to whom the Software is
|
||
|
furnished to do so, subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in
|
||
|
all copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
THE SOFTWARE.
|
||
|
|
||
|
*/
|
||
|
|
||
|
// Language: Verilog 2001
|
||
|
|
||
|
`timescale 1ns / 1ps
|
||
|
|
||
|
/*
|
||
|
* I2C master
|
||
|
*/
|
||
|
module i2c_master (
|
||
|
input wire clk,
|
||
|
input wire rst,
|
||
|
|
||
|
/*
|
||
|
* Host interface
|
||
|
*/
|
||
|
input wire [6:0] cmd_address,
|
||
|
input wire cmd_start,
|
||
|
input wire cmd_read,
|
||
|
input wire cmd_write,
|
||
|
input wire cmd_write_multiple,
|
||
|
input wire cmd_stop,
|
||
|
input wire cmd_valid,
|
||
|
output wire cmd_ready,
|
||
|
|
||
|
input wire [7:0] data_in,
|
||
|
input wire data_in_valid,
|
||
|
output wire data_in_ready,
|
||
|
input wire data_in_last,
|
||
|
|
||
|
output wire [7:0] data_out,
|
||
|
output wire data_out_valid,
|
||
|
input wire data_out_ready,
|
||
|
output wire data_out_last,
|
||
|
|
||
|
/*
|
||
|
* I2C interface
|
||
|
*/
|
||
|
input wire scl_i,
|
||
|
output wire scl_o,
|
||
|
output wire scl_t,
|
||
|
input wire sda_i,
|
||
|
output wire sda_o,
|
||
|
output wire sda_t,
|
||
|
|
||
|
/*
|
||
|
* Status
|
||
|
*/
|
||
|
output wire busy,
|
||
|
output wire bus_control,
|
||
|
output wire bus_active,
|
||
|
output wire missed_ack,
|
||
|
|
||
|
/*
|
||
|
* Configuration
|
||
|
*/
|
||
|
input wire [15:0] prescale,
|
||
|
input wire stop_on_idle
|
||
|
);
|
||
|
|
||
|
/*
|
||
|
|
||
|
I2C
|
||
|
|
||
|
Read
|
||
|
__ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __
|
||
|
sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_\_R___A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A____/
|
||
|
____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____
|
||
|
scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP
|
||
|
|
||
|
Write
|
||
|
__ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ __
|
||
|
sda \__/_6_X_5_X_4_X_3_X_2_X_1_X_0_/_W_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_\_A_/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_/_N_\__/
|
||
|
____ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ____
|
||
|
scl ST \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ SP
|
||
|
|
||
|
Commands:
|
||
|
|
||
|
read
|
||
|
read data byte
|
||
|
set start to force generation of a start condition
|
||
|
start is implied when bus is inactive or active with write or different address
|
||
|
set stop to issue a stop condition after reading current byte
|
||
|
if stop is set with read command, then data_out_last will be set
|
||
|
|
||
|
write
|
||
|
write data byte
|
||
|
set start to force generation of a start condition
|
||
|
start is implied when bus is inactive or active with read or different address
|
||
|
set stop to issue a stop condition after writing current byte
|
||
|
|
||
|
write multiple
|
||
|
write multiple data bytes (until data_in_last)
|
||
|
set start to force generation of a start condition
|
||
|
start is implied when bus is inactive or active with read or different address
|
||
|
set stop to issue a stop condition after writing block
|
||
|
|
||
|
stop
|
||
|
issue stop condition if bus is active
|
||
|
|
||
|
Status:
|
||
|
|
||
|
busy
|
||
|
module is communicating over the bus
|
||
|
|
||
|
bus_control
|
||
|
module has control of bus in active state
|
||
|
|
||
|
bus_active
|
||
|
bus is active, not necessarily controlled by this module
|
||
|
|
||
|
missed_ack
|
||
|
strobed when a slave ack is missed
|
||
|
|
||
|
Parameters:
|
||
|
|
||
|
prescale
|
||
|
set prescale to 1/4 of the minimum clock period in units
|
||
|
of input clk cycles (prescale = Fclk / (FI2Cclk * 4))
|
||
|
|
||
|
stop_on_idle
|
||
|
automatically issue stop when command input is not valid
|
||
|
|
||
|
Example of interfacing with tristate pins:
|
||
|
(this will work for any tristate bus)
|
||
|
|
||
|
assign scl_i = scl_pin;
|
||
|
assign scl_pin = scl_t ? 1'bz : scl_o;
|
||
|
assign sda_i = sda_pin;
|
||
|
assign sda_pin = sda_t ? 1'bz : sda_o;
|
||
|
|
||
|
Equivalent code that does not use *_t connections:
|
||
|
(we can get away with this because I2C is open-drain)
|
||
|
|
||
|
assign scl_i = scl_pin;
|
||
|
assign scl_pin = scl_o ? 1'bz : 1'b0;
|
||
|
assign sda_i = sda_pin;
|
||
|
assign sda_pin = sda_o ? 1'bz : 1'b0;
|
||
|
|
||
|
Example of two interconnected I2C devices:
|
||
|
|
||
|
assign scl_1_i = scl_1_o & scl_2_o;
|
||
|
assign scl_2_i = scl_1_o & scl_2_o;
|
||
|
assign sda_1_i = sda_1_o & sda_2_o;
|
||
|
assign sda_2_i = sda_1_o & sda_2_o;
|
||
|
|
||
|
Example of two I2C devices sharing the same pins:
|
||
|
|
||
|
assign scl_1_i = scl_pin;
|
||
|
assign scl_2_i = scl_pin;
|
||
|
assign scl_pin = (scl_1_o & scl_2_o) ? 1'bz : 1'b0;
|
||
|
assign sda_1_i = sda_pin;
|
||
|
assign sda_2_i = sda_pin;
|
||
|
assign sda_pin = (sda_1_o & sda_2_o) ? 1'bz : 1'b0;
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
scl_o should not be connected directly to scl_i, only via AND logic or a tristate
|
||
|
I/O pin. This would prevent devices from stretching the clock period.
|
||
|
|
||
|
*/
|
||
|
|
||
|
localparam [4:0]
|
||
|
STATE_IDLE = 4'd0,
|
||
|
STATE_ACTIVE_WRITE = 4'd1,
|
||
|
STATE_ACTIVE_READ = 4'd2,
|
||
|
STATE_START_WAIT = 4'd3,
|
||
|
STATE_START = 4'd4,
|
||
|
STATE_ADDRESS_1 = 4'd5,
|
||
|
STATE_ADDRESS_2 = 4'd6,
|
||
|
STATE_WRITE_1 = 4'd7,
|
||
|
STATE_WRITE_2 = 4'd8,
|
||
|
STATE_WRITE_3 = 4'd9,
|
||
|
STATE_READ = 4'd10,
|
||
|
STATE_STOP = 4'd11;
|
||
|
|
||
|
reg [4:0] state_reg = STATE_IDLE, state_next;
|
||
|
|
||
|
localparam [4:0]
|
||
|
PHY_STATE_IDLE = 5'd0,
|
||
|
PHY_STATE_ACTIVE = 5'd1,
|
||
|
PHY_STATE_REPEATED_START_1 = 5'd2,
|
||
|
PHY_STATE_REPEATED_START_2 = 5'd3,
|
||
|
PHY_STATE_START_1 = 5'd4,
|
||
|
PHY_STATE_START_2 = 5'd5,
|
||
|
PHY_STATE_WRITE_BIT_1 = 5'd6,
|
||
|
PHY_STATE_WRITE_BIT_2 = 5'd7,
|
||
|
PHY_STATE_WRITE_BIT_3 = 5'd8,
|
||
|
PHY_STATE_READ_BIT_1 = 5'd9,
|
||
|
PHY_STATE_READ_BIT_2 = 5'd10,
|
||
|
PHY_STATE_READ_BIT_3 = 5'd11,
|
||
|
PHY_STATE_READ_BIT_4 = 5'd12,
|
||
|
PHY_STATE_STOP_1 = 5'd13,
|
||
|
PHY_STATE_STOP_2 = 5'd14,
|
||
|
PHY_STATE_STOP_3 = 5'd15;
|
||
|
|
||
|
reg [4:0] phy_state_reg = STATE_IDLE, phy_state_next;
|
||
|
|
||
|
reg phy_start_bit;
|
||
|
reg phy_stop_bit;
|
||
|
reg phy_write_bit;
|
||
|
reg phy_read_bit;
|
||
|
reg phy_release_bus;
|
||
|
|
||
|
reg phy_tx_data;
|
||
|
|
||
|
reg phy_rx_data_reg = 1'b0, phy_rx_data_next;
|
||
|
|
||
|
reg [6:0] addr_reg = 7'd0, addr_next;
|
||
|
reg [7:0] data_reg = 8'd0, data_next;
|
||
|
reg last_reg = 1'b0, last_next;
|
||
|
|
||
|
reg mode_read_reg = 1'b0, mode_read_next;
|
||
|
reg mode_write_multiple_reg = 1'b0, mode_write_multiple_next;
|
||
|
reg mode_stop_reg = 1'b0, mode_stop_next;
|
||
|
|
||
|
reg [16:0] delay_reg = 16'd0, delay_next;
|
||
|
reg delay_scl_reg = 1'b0, delay_scl_next;
|
||
|
reg delay_sda_reg = 1'b0, delay_sda_next;
|
||
|
|
||
|
reg [3:0] bit_count_reg = 4'd0, bit_count_next;
|
||
|
|
||
|
reg cmd_ready_reg = 1'b0, cmd_ready_next;
|
||
|
|
||
|
reg data_in_ready_reg = 1'b0, data_in_ready_next;
|
||
|
|
||
|
reg [7:0] data_out_reg = 8'd0, data_out_next;
|
||
|
reg data_out_valid_reg = 1'b0, data_out_valid_next;
|
||
|
reg data_out_last_reg = 1'b0, data_out_last_next;
|
||
|
|
||
|
reg scl_i_reg = 1'b1;
|
||
|
reg sda_i_reg = 1'b1;
|
||
|
|
||
|
reg scl_o_reg = 1'b1, scl_o_next;
|
||
|
reg sda_o_reg = 1'b1, sda_o_next;
|
||
|
|
||
|
reg last_scl_i_reg = 1'b1;
|
||
|
reg last_sda_i_reg = 1'b1;
|
||
|
|
||
|
reg busy_reg = 1'b0;
|
||
|
reg bus_active_reg = 1'b0;
|
||
|
reg bus_control_reg = 1'b0, bus_control_next;
|
||
|
reg missed_ack_reg = 1'b0, missed_ack_next;
|
||
|
|
||
|
assign cmd_ready = cmd_ready_reg;
|
||
|
|
||
|
assign data_in_ready = data_in_ready_reg;
|
||
|
|
||
|
assign data_out = data_out_reg;
|
||
|
assign data_out_valid = data_out_valid_reg;
|
||
|
assign data_out_last = data_out_last_reg;
|
||
|
|
||
|
assign scl_o = scl_o_reg;
|
||
|
assign scl_t = scl_o_reg;
|
||
|
assign sda_o = sda_o_reg;
|
||
|
assign sda_t = sda_o_reg;
|
||
|
|
||
|
assign busy = busy_reg;
|
||
|
assign bus_active = bus_active_reg;
|
||
|
assign bus_control = bus_control_reg;
|
||
|
assign missed_ack = missed_ack_reg;
|
||
|
|
||
|
wire scl_posedge = scl_i_reg & ~last_scl_i_reg;
|
||
|
wire scl_negedge = ~scl_i_reg & last_scl_i_reg;
|
||
|
wire sda_posedge = sda_i_reg & ~last_sda_i_reg;
|
||
|
wire sda_negedge = ~sda_i_reg & last_sda_i_reg;
|
||
|
|
||
|
wire start_bit = sda_negedge & scl_i_reg;
|
||
|
wire stop_bit = sda_posedge & scl_i_reg;
|
||
|
|
||
|
always @* begin
|
||
|
state_next = STATE_IDLE;
|
||
|
|
||
|
phy_start_bit = 1'b0;
|
||
|
phy_stop_bit = 1'b0;
|
||
|
phy_write_bit = 1'b0;
|
||
|
phy_read_bit = 1'b0;
|
||
|
phy_tx_data = 1'b0;
|
||
|
phy_release_bus = 1'b0;
|
||
|
|
||
|
addr_next = addr_reg;
|
||
|
data_next = data_reg;
|
||
|
last_next = last_reg;
|
||
|
|
||
|
mode_read_next = mode_read_reg;
|
||
|
mode_write_multiple_next = mode_write_multiple_reg;
|
||
|
mode_stop_next = mode_stop_reg;
|
||
|
|
||
|
bit_count_next = bit_count_reg;
|
||
|
|
||
|
cmd_ready_next = 1'b0;
|
||
|
|
||
|
data_in_ready_next = 1'b0;
|
||
|
|
||
|
data_out_next = data_out_reg;
|
||
|
data_out_valid_next = data_out_valid_reg & ~data_out_ready;
|
||
|
data_out_last_next = data_out_last_reg;
|
||
|
|
||
|
missed_ack_next = 1'b0;
|
||
|
|
||
|
// generate delays
|
||
|
if (phy_state_reg != PHY_STATE_IDLE && phy_state_reg != PHY_STATE_ACTIVE) begin
|
||
|
// wait for phy operation
|
||
|
state_next = state_reg;
|
||
|
end else begin
|
||
|
// process states
|
||
|
case (state_reg)
|
||
|
STATE_IDLE: begin
|
||
|
// line idle
|
||
|
cmd_ready_next = 1'b1;
|
||
|
|
||
|
if (cmd_ready & cmd_valid) begin
|
||
|
// command valid
|
||
|
if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin
|
||
|
// read or write command
|
||
|
addr_next = cmd_address;
|
||
|
mode_read_next = cmd_read;
|
||
|
mode_write_multiple_next = cmd_write_multiple;
|
||
|
mode_stop_next = cmd_stop;
|
||
|
|
||
|
cmd_ready_next = 1'b0;
|
||
|
|
||
|
// start bit
|
||
|
if (bus_active) begin
|
||
|
state_next = STATE_START_WAIT;
|
||
|
end else begin
|
||
|
phy_start_bit = 1'b1;
|
||
|
bit_count_next = 4'd8;
|
||
|
state_next = STATE_ADDRESS_1;
|
||
|
end
|
||
|
end else begin
|
||
|
// invalid or unspecified - ignore
|
||
|
state_next = STATE_IDLE;
|
||
|
end
|
||
|
end else begin
|
||
|
state_next = STATE_IDLE;
|
||
|
end
|
||
|
end
|
||
|
STATE_ACTIVE_WRITE: begin
|
||
|
// line active with current address and read/write mode
|
||
|
cmd_ready_next = 1'b1;
|
||
|
|
||
|
if (cmd_ready & cmd_valid) begin
|
||
|
// command valid
|
||
|
if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin
|
||
|
// read or write command
|
||
|
addr_next = cmd_address;
|
||
|
mode_read_next = cmd_read;
|
||
|
mode_write_multiple_next = cmd_write_multiple;
|
||
|
mode_stop_next = cmd_stop;
|
||
|
|
||
|
cmd_ready_next = 1'b0;
|
||
|
|
||
|
if (cmd_start || cmd_address != addr_reg || cmd_read) begin
|
||
|
// address or mode mismatch or forced start - repeated start
|
||
|
|
||
|
// repeated start bit
|
||
|
phy_start_bit = 1'b1;
|
||
|
bit_count_next = 4'd8;
|
||
|
state_next = STATE_ADDRESS_1;
|
||
|
end else begin
|
||
|
// address and mode match
|
||
|
|
||
|
// start write
|
||
|
data_in_ready_next = 1'b1;
|
||
|
state_next = STATE_WRITE_1;
|
||
|
end
|
||
|
end else if (cmd_stop && !(cmd_read || cmd_write || cmd_write_multiple)) begin
|
||
|
// stop command
|
||
|
phy_stop_bit = 1'b1;
|
||
|
state_next = STATE_IDLE;
|
||
|
end else begin
|
||
|
// invalid or unspecified - ignore
|
||
|
state_next = STATE_ACTIVE_WRITE;
|
||
|
end
|
||
|
end else begin
|
||
|
if (stop_on_idle & cmd_ready & ~cmd_valid) begin
|
||
|
// no waiting command and stop_on_idle selected, issue stop condition
|
||
|
phy_stop_bit = 1'b1;
|
||
|
state_next = STATE_IDLE;
|
||
|
end else begin
|
||
|
state_next = STATE_ACTIVE_WRITE;
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
STATE_ACTIVE_READ: begin
|
||
|
// line active to current address
|
||
|
cmd_ready_next = ~data_out_valid;
|
||
|
|
||
|
if (cmd_ready & cmd_valid) begin
|
||
|
// command valid
|
||
|
if (cmd_read ^ (cmd_write | cmd_write_multiple)) begin
|
||
|
// read or write command
|
||
|
addr_next = cmd_address;
|
||
|
mode_read_next = cmd_read;
|
||
|
mode_write_multiple_next = cmd_write_multiple;
|
||
|
mode_stop_next = cmd_stop;
|
||
|
|
||
|
cmd_ready_next = 1'b0;
|
||
|
|
||
|
if (cmd_start || cmd_address != addr_reg || cmd_write) begin
|
||
|
// address or mode mismatch or forced start - repeated start
|
||
|
|
||
|
// write nack for previous read
|
||
|
phy_write_bit = 1'b1;
|
||
|
phy_tx_data = 1'b1;
|
||
|
// repeated start bit
|
||
|
state_next = STATE_START;
|
||
|
end else begin
|
||
|
// address and mode match
|
||
|
|
||
|
// write ack for previous read
|
||
|
phy_write_bit = 1'b1;
|
||
|
phy_tx_data = 1'b0;
|
||
|
// start next read
|
||
|
bit_count_next = 4'd8;
|
||
|
data_next = 8'd0;
|
||
|
state_next = STATE_READ;
|
||
|
end
|
||
|
end else if (cmd_stop && !(cmd_read || cmd_write || cmd_write_multiple)) begin
|
||
|
// stop command
|
||
|
// write nack for previous read
|
||
|
phy_write_bit = 1'b1;
|
||
|
phy_tx_data = 1'b1;
|
||
|
// send stop bit
|
||
|
state_next = STATE_STOP;
|
||
|
end else begin
|
||
|
// invalid or unspecified - ignore
|
||
|
state_next = STATE_ACTIVE_READ;
|
||
|
end
|
||
|
end else begin
|
||
|
if (stop_on_idle & cmd_ready & ~cmd_valid) begin
|
||
|
// no waiting command and stop_on_idle selected, issue stop condition
|
||
|
// write ack for previous read
|
||
|
phy_write_bit = 1'b1;
|
||
|
phy_tx_data = 1'b1;
|
||
|
// send stop bit
|
||
|
state_next = STATE_STOP;
|
||
|
end else begin
|
||
|
state_next = STATE_ACTIVE_READ;
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
STATE_START_WAIT: begin
|
||
|
// wait for bus idle
|
||
|
|
||
|
if (bus_active) begin
|
||
|
state_next = STATE_START_WAIT;
|
||
|
end else begin
|
||
|
// bus is idle, take control
|
||
|
phy_start_bit = 1'b1;
|
||
|
bit_count_next = 4'd8;
|
||
|
state_next = STATE_ADDRESS_1;
|
||
|
end
|
||
|
end
|
||
|
STATE_START: begin
|
||
|
// send start bit
|
||
|
|
||
|
phy_start_bit = 1'b1;
|
||
|
bit_count_next = 4'd8;
|
||
|
state_next = STATE_ADDRESS_1;
|
||
|
end
|
||
|
STATE_ADDRESS_1: begin
|
||
|
// send address
|
||
|
bit_count_next = bit_count_reg - 1;
|
||
|
if (bit_count_reg > 1) begin
|
||
|
// send address
|
||
|
phy_write_bit = 1'b1;
|
||
|
phy_tx_data = addr_reg[bit_count_reg-2];
|
||
|
state_next = STATE_ADDRESS_1;
|
||
|
end else if (bit_count_reg > 0) begin
|
||
|
// send read/write bit
|
||
|
phy_write_bit = 1'b1;
|
||
|
phy_tx_data = mode_read_reg;
|
||
|
state_next = STATE_ADDRESS_1;
|
||
|
end else begin
|
||
|
// read ack bit
|
||
|
phy_read_bit = 1'b1;
|
||
|
state_next = STATE_ADDRESS_2;
|
||
|
end
|
||
|
end
|
||
|
STATE_ADDRESS_2: begin
|
||
|
// read ack bit
|
||
|
missed_ack_next = phy_rx_data_reg;
|
||
|
|
||
|
if (mode_read_reg) begin
|
||
|
// start read
|
||
|
bit_count_next = 4'd8;
|
||
|
data_next = 1'b0;
|
||
|
state_next = STATE_READ;
|
||
|
end else begin
|
||
|
// start write
|
||
|
data_in_ready_next = 1'b1;
|
||
|
state_next = STATE_WRITE_1;
|
||
|
end
|
||
|
end
|
||
|
STATE_WRITE_1: begin
|
||
|
data_in_ready_next = 1'b1;
|
||
|
|
||
|
if (data_in_ready & data_in_valid) begin
|
||
|
// got data, start write
|
||
|
data_next = data_in;
|
||
|
last_next = data_in_last;
|
||
|
bit_count_next = 4'd8;
|
||
|
data_in_ready_next = 1'b0;
|
||
|
state_next = STATE_WRITE_2;
|
||
|
end else begin
|
||
|
// wait for data
|
||
|
state_next = STATE_WRITE_1;
|
||
|
end
|
||
|
end
|
||
|
STATE_WRITE_2: begin
|
||
|
// send data
|
||
|
bit_count_next = bit_count_reg - 1;
|
||
|
if (bit_count_reg > 0) begin
|
||
|
// write data bit
|
||
|
phy_write_bit = 1'b1;
|
||
|
phy_tx_data = data_reg[bit_count_reg-1];
|
||
|
state_next = STATE_WRITE_2;
|
||
|
end else begin
|
||
|
// read ack bit
|
||
|
phy_read_bit = 1'b1;
|
||
|
state_next = STATE_WRITE_3;
|
||
|
end
|
||
|
end
|
||
|
STATE_WRITE_3: begin
|
||
|
// read ack bit
|
||
|
missed_ack_next = phy_rx_data_reg;
|
||
|
|
||
|
if (mode_write_multiple_reg && !last_reg) begin
|
||
|
// more to write
|
||
|
state_next = STATE_WRITE_1;
|
||
|
end else if (mode_stop_reg) begin
|
||
|
// last cycle and stop selected
|
||
|
phy_stop_bit = 1'b1;
|
||
|
state_next = STATE_IDLE;
|
||
|
end else begin
|
||
|
// otherwise, return to bus active state
|
||
|
state_next = STATE_ACTIVE_WRITE;
|
||
|
end
|
||
|
end
|
||
|
STATE_READ: begin
|
||
|
// read data
|
||
|
|
||
|
bit_count_next = bit_count_reg - 1;
|
||
|
data_next = {data_reg[6:0], phy_rx_data_reg};
|
||
|
if (bit_count_reg > 0) begin
|
||
|
// read next bit
|
||
|
phy_read_bit = 1'b1;
|
||
|
state_next = STATE_READ;
|
||
|
end else begin
|
||
|
// output data word
|
||
|
data_out_next = data_next;
|
||
|
data_out_valid_next = 1'b1;
|
||
|
data_out_last_next = 1'b0;
|
||
|
if (mode_stop_reg) begin
|
||
|
// send nack and stop
|
||
|
data_out_last_next = 1'b1;
|
||
|
phy_write_bit = 1'b1;
|
||
|
phy_tx_data = 1'b1;
|
||
|
state_next = STATE_STOP;
|
||
|
end else begin
|
||
|
// return to bus active state
|
||
|
state_next = STATE_ACTIVE_READ;
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
STATE_STOP: begin
|
||
|
// send stop bit
|
||
|
phy_stop_bit = 1'b1;
|
||
|
state_next = STATE_IDLE;
|
||
|
end
|
||
|
endcase
|
||
|
end
|
||
|
end
|
||
|
|
||
|
always @* begin
|
||
|
phy_state_next = PHY_STATE_IDLE;
|
||
|
|
||
|
phy_rx_data_next = phy_rx_data_reg;
|
||
|
|
||
|
delay_next = delay_reg;
|
||
|
delay_scl_next = delay_scl_reg;
|
||
|
delay_sda_next = delay_sda_reg;
|
||
|
|
||
|
scl_o_next = scl_o_reg;
|
||
|
sda_o_next = sda_o_reg;
|
||
|
|
||
|
bus_control_next = bus_control_reg;
|
||
|
|
||
|
if (phy_release_bus) begin
|
||
|
// release bus and return to idle state
|
||
|
sda_o_next = 1'b1;
|
||
|
scl_o_next = 1'b1;
|
||
|
delay_scl_next = 1'b0;
|
||
|
delay_sda_next = 1'b0;
|
||
|
delay_next = 1'b0;
|
||
|
phy_state_next = PHY_STATE_IDLE;
|
||
|
end else if (delay_scl_reg) begin
|
||
|
// wait for SCL to match command
|
||
|
delay_scl_next = scl_o_reg & ~scl_i_reg;
|
||
|
phy_state_next = phy_state_reg;
|
||
|
end else if (delay_sda_reg) begin
|
||
|
// wait for SDA to match command
|
||
|
delay_sda_next = sda_o_reg & ~sda_i_reg;
|
||
|
phy_state_next = phy_state_reg;
|
||
|
end else if (delay_reg > 0) begin
|
||
|
// time delay
|
||
|
delay_next = delay_reg - 1;
|
||
|
phy_state_next = phy_state_reg;
|
||
|
end else begin
|
||
|
case (phy_state_reg)
|
||
|
PHY_STATE_IDLE: begin
|
||
|
// bus idle - wait for start command
|
||
|
sda_o_next = 1'b1;
|
||
|
scl_o_next = 1'b1;
|
||
|
if (phy_start_bit) begin
|
||
|
sda_o_next = 1'b0;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_START_1;
|
||
|
end else begin
|
||
|
phy_state_next = PHY_STATE_IDLE;
|
||
|
end
|
||
|
end
|
||
|
PHY_STATE_ACTIVE: begin
|
||
|
// bus active
|
||
|
if (phy_start_bit) begin
|
||
|
sda_o_next = 1'b1;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_REPEATED_START_1;
|
||
|
end else if (phy_write_bit) begin
|
||
|
sda_o_next = phy_tx_data;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_WRITE_BIT_1;
|
||
|
end else if (phy_read_bit) begin
|
||
|
sda_o_next = 1'b1;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_READ_BIT_1;
|
||
|
end else if (phy_stop_bit) begin
|
||
|
sda_o_next = 1'b0;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_STOP_1;
|
||
|
end else begin
|
||
|
phy_state_next = PHY_STATE_ACTIVE;
|
||
|
end
|
||
|
end
|
||
|
PHY_STATE_REPEATED_START_1: begin
|
||
|
// generate repeated start bit
|
||
|
// ______
|
||
|
// sda XXX/ \_______
|
||
|
// _______
|
||
|
// scl ______/ \___
|
||
|
//
|
||
|
|
||
|
scl_o_next = 1'b1;
|
||
|
delay_scl_next = 1'b1;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_REPEATED_START_2;
|
||
|
end
|
||
|
PHY_STATE_REPEATED_START_2: begin
|
||
|
// generate repeated start bit
|
||
|
// ______
|
||
|
// sda XXX/ \_______
|
||
|
// _______
|
||
|
// scl ______/ \___
|
||
|
//
|
||
|
|
||
|
sda_o_next = 1'b0;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_START_1;
|
||
|
end
|
||
|
PHY_STATE_START_1: begin
|
||
|
// generate start bit
|
||
|
// ___
|
||
|
// sda \_______
|
||
|
// _______
|
||
|
// scl \___
|
||
|
//
|
||
|
|
||
|
scl_o_next = 1'b0;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_START_2;
|
||
|
end
|
||
|
PHY_STATE_START_2: begin
|
||
|
// generate start bit
|
||
|
// ___
|
||
|
// sda \_______
|
||
|
// _______
|
||
|
// scl \___
|
||
|
//
|
||
|
|
||
|
bus_control_next = 1'b1;
|
||
|
phy_state_next = PHY_STATE_ACTIVE;
|
||
|
end
|
||
|
PHY_STATE_WRITE_BIT_1: begin
|
||
|
// write bit
|
||
|
// ________
|
||
|
// sda X________X
|
||
|
// ____
|
||
|
// scl __/ \__
|
||
|
|
||
|
scl_o_next = 1'b1;
|
||
|
delay_scl_next = 1'b1;
|
||
|
delay_next = prescale << 1;
|
||
|
phy_state_next = PHY_STATE_WRITE_BIT_2;
|
||
|
end
|
||
|
PHY_STATE_WRITE_BIT_2: begin
|
||
|
// write bit
|
||
|
// ________
|
||
|
// sda X________X
|
||
|
// ____
|
||
|
// scl __/ \__
|
||
|
|
||
|
scl_o_next = 1'b0;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_WRITE_BIT_3;
|
||
|
end
|
||
|
PHY_STATE_WRITE_BIT_3: begin
|
||
|
// write bit
|
||
|
// ________
|
||
|
// sda X________X
|
||
|
// ____
|
||
|
// scl __/ \__
|
||
|
|
||
|
phy_state_next = PHY_STATE_ACTIVE;
|
||
|
end
|
||
|
PHY_STATE_READ_BIT_1: begin
|
||
|
// read bit
|
||
|
// ________
|
||
|
// sda X________X
|
||
|
// ____
|
||
|
// scl __/ \__
|
||
|
|
||
|
scl_o_next = 1'b1;
|
||
|
delay_scl_next = 1'b1;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_READ_BIT_2;
|
||
|
end
|
||
|
PHY_STATE_READ_BIT_2: begin
|
||
|
// read bit
|
||
|
// ________
|
||
|
// sda X________X
|
||
|
// ____
|
||
|
// scl __/ \__
|
||
|
|
||
|
phy_rx_data_next = sda_i_reg;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_READ_BIT_3;
|
||
|
end
|
||
|
PHY_STATE_READ_BIT_3: begin
|
||
|
// read bit
|
||
|
// ________
|
||
|
// sda X________X
|
||
|
// ____
|
||
|
// scl __/ \__
|
||
|
|
||
|
scl_o_next = 1'b0;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_READ_BIT_4;
|
||
|
end
|
||
|
PHY_STATE_READ_BIT_4: begin
|
||
|
// read bit
|
||
|
// ________
|
||
|
// sda X________X
|
||
|
// ____
|
||
|
// scl __/ \__
|
||
|
|
||
|
phy_state_next = PHY_STATE_ACTIVE;
|
||
|
end
|
||
|
PHY_STATE_STOP_1: begin
|
||
|
// stop bit
|
||
|
// ___
|
||
|
// sda XXX\_______/
|
||
|
// _______
|
||
|
// scl _______/
|
||
|
|
||
|
scl_o_next = 1'b1;
|
||
|
delay_scl_next = 1'b1;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_STOP_2;
|
||
|
end
|
||
|
PHY_STATE_STOP_2: begin
|
||
|
// stop bit
|
||
|
// ___
|
||
|
// sda XXX\_______/
|
||
|
// _______
|
||
|
// scl _______/
|
||
|
|
||
|
sda_o_next = 1'b1;
|
||
|
delay_next = prescale;
|
||
|
phy_state_next = PHY_STATE_STOP_3;
|
||
|
end
|
||
|
PHY_STATE_STOP_3: begin
|
||
|
// stop bit
|
||
|
// ___
|
||
|
// sda XXX\_______/
|
||
|
// _______
|
||
|
// scl _______/
|
||
|
|
||
|
bus_control_next = 1'b0;
|
||
|
phy_state_next = PHY_STATE_IDLE;
|
||
|
end
|
||
|
endcase
|
||
|
end
|
||
|
end
|
||
|
|
||
|
always @(posedge clk) begin
|
||
|
if (rst) begin
|
||
|
state_reg <= STATE_IDLE;
|
||
|
phy_state_reg <= PHY_STATE_IDLE;
|
||
|
delay_reg <= 16'd0;
|
||
|
delay_scl_reg <= 1'b0;
|
||
|
delay_sda_reg <= 1'b0;
|
||
|
cmd_ready_reg <= 1'b0;
|
||
|
data_in_ready_reg <= 1'b0;
|
||
|
data_out_valid_reg <= 1'b0;
|
||
|
scl_o_reg <= 1'b1;
|
||
|
sda_o_reg <= 1'b1;
|
||
|
busy_reg <= 1'b0;
|
||
|
bus_active_reg <= 1'b0;
|
||
|
bus_control_reg <= 1'b0;
|
||
|
missed_ack_reg <= 1'b0;
|
||
|
end else begin
|
||
|
state_reg <= state_next;
|
||
|
phy_state_reg <= phy_state_next;
|
||
|
|
||
|
delay_reg <= delay_next;
|
||
|
delay_scl_reg <= delay_scl_next;
|
||
|
delay_sda_reg <= delay_sda_next;
|
||
|
|
||
|
cmd_ready_reg <= cmd_ready_next;
|
||
|
data_in_ready_reg <= data_in_ready_next;
|
||
|
data_out_valid_reg <= data_out_valid_next;
|
||
|
|
||
|
scl_o_reg <= scl_o_next;
|
||
|
sda_o_reg <= sda_o_next;
|
||
|
|
||
|
busy_reg <= !(state_reg == STATE_IDLE || state_reg == STATE_ACTIVE_WRITE || state_reg == STATE_ACTIVE_READ);
|
||
|
|
||
|
if (start_bit) begin
|
||
|
bus_active_reg <= 1'b1;
|
||
|
end else if (stop_bit) begin
|
||
|
bus_active_reg <= 1'b0;
|
||
|
end else begin
|
||
|
bus_active_reg <= bus_active_reg;
|
||
|
end
|
||
|
|
||
|
bus_control_reg <= bus_control_next;
|
||
|
missed_ack_reg <= missed_ack_next;
|
||
|
end
|
||
|
|
||
|
phy_rx_data_reg <= phy_rx_data_next;
|
||
|
|
||
|
addr_reg <= addr_next;
|
||
|
data_reg <= data_next;
|
||
|
last_reg <= last_next;
|
||
|
|
||
|
mode_read_reg <= mode_read_next;
|
||
|
mode_write_multiple_reg <= mode_write_multiple_next;
|
||
|
mode_stop_reg <= mode_stop_next;
|
||
|
|
||
|
bit_count_reg <= bit_count_next;
|
||
|
|
||
|
data_out_reg <= data_out_next;
|
||
|
data_out_last_reg <= data_out_last_next;
|
||
|
|
||
|
scl_i_reg <= scl_i;
|
||
|
sda_i_reg <= sda_i;
|
||
|
last_scl_i_reg <= scl_i_reg;
|
||
|
last_sda_i_reg <= sda_i_reg;
|
||
|
end
|
||
|
|
||
|
endmodule
|