1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-30 08:32:52 +08:00

Add i2c init code for si570 reference oscillator

This commit is contained in:
Alex Forencich 2016-08-03 14:44:10 -04:00
parent 833d1dac81
commit 36af29db77
5 changed files with 1484 additions and 5 deletions

View File

@ -106,7 +106,7 @@ set_property -dict {LOC AL21 IOSTANDARD LVCMOS18} [get_ports qsfp_intl]
set_property -dict {LOC AM21 IOSTANDARD LVCMOS18} [get_ports qsfp_lpmode]
# I2C interface
#set_property -dict {LOC AN21 IOSTANDARD LVCMOS18} [get_ports i2c_scl]
#set_property -dict {LOC AP21 IOSTANDARD LVCMOS18} [get_ports i2c_sda]
set_property -dict {LOC AN21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_scl]
set_property -dict {LOC AP21 IOSTANDARD LVCMOS18 SLEW SLOW DRIVE 8} [get_ports i2c_sda]

View File

@ -10,6 +10,8 @@ SYN_FILES += rtl/fpga_core.v
SYN_FILES += rtl/debounce_switch.v
SYN_FILES += rtl/sync_reset.v
SYN_FILES += rtl/sync_signal.v
SYN_FILES += rtl/i2c_master.v
SYN_FILES += rtl/si570_i2c_init.v
SYN_FILES += lib/eth/rtl/eth_mac_1g_fifo.v
SYN_FILES += lib/eth/rtl/eth_mac_1g.v
SYN_FILES += lib/eth/rtl/eth_mac_1g_rx.v

View File

@ -52,8 +52,8 @@ module fpga (
/*
* I2C for board management
*/
// inout wire i2c_scl,
// inout wire i2c_sda,
inout wire i2c_scl,
inout wire i2c_sda,
/*
* Ethernet: QSFP28
@ -250,6 +250,102 @@ sync_signal_inst (
.out({uart_rxd_int, uart_cts_int})
);
// SI570 I2C
wire i2c_scl_i;
wire i2c_scl_o;
wire i2c_scl_t;
wire i2c_sda_i;
wire i2c_sda_o;
wire i2c_sda_t;
assign i2c_scl_i = i2c_scl;
assign i2c_scl = i2c_scl_t ? 1'bz : i2c_scl_o;
assign i2c_sda_i = i2c_sda;
assign i2c_sda = i2c_sda_t ? 1'bz : i2c_sda_o;
wire [6:0] si570_i2c_cmd_address;
wire si570_i2c_cmd_start;
wire si570_i2c_cmd_read;
wire si570_i2c_cmd_write;
wire si570_i2c_cmd_write_multiple;
wire si570_i2c_cmd_stop;
wire si570_i2c_cmd_valid;
wire si570_i2c_cmd_ready;
wire [7:0] si570_i2c_data;
wire si570_i2c_data_valid;
wire si570_i2c_data_ready;
wire si570_i2c_data_last;
wire si570_i2c_init_busy;
// delay start by ~10 ms
reg [20:0] si570_i2c_init_start_delay = 21'd0;
always @(posedge clk_125mhz_int) begin
if (rst_125mhz_int) begin
si570_i2c_init_start_delay <= 21'd0;
end else begin
if (!si570_i2c_init_start_delay[20]) begin
si570_i2c_init_start_delay <= si570_i2c_init_start_delay + 21'd1;
end
end
end
si570_i2c_init
si570_i2c_init_inst (
.clk(clk_125mhz_int),
.rst(rst_125mhz_int),
.cmd_address(si570_i2c_cmd_address),
.cmd_start(si570_i2c_cmd_start),
.cmd_read(si570_i2c_cmd_read),
.cmd_write(si570_i2c_cmd_write),
.cmd_write_multiple(si570_i2c_cmd_write_multiple),
.cmd_stop(si570_i2c_cmd_stop),
.cmd_valid(si570_i2c_cmd_valid),
.cmd_ready(si570_i2c_cmd_ready),
.data_out(si570_i2c_data),
.data_out_valid(si570_i2c_data_valid),
.data_out_ready(si570_i2c_data_ready),
.data_out_last(si570_i2c_data_last),
.busy(si570_i2c_init_busy),
.start(si570_i2c_init_start_delay[20])
);
i2c_master
si570_i2c_master (
.clk(clk_125mhz_int),
.rst(rst_125mhz_int),
.cmd_address(si570_i2c_cmd_address),
.cmd_start(si570_i2c_cmd_start),
.cmd_read(si570_i2c_cmd_read),
.cmd_write(si570_i2c_cmd_write),
.cmd_write_multiple(si570_i2c_cmd_write_multiple),
.cmd_stop(si570_i2c_cmd_stop),
.cmd_valid(si570_i2c_cmd_valid),
.cmd_ready(si570_i2c_cmd_ready),
.data_in(si570_i2c_data),
.data_in_valid(si570_i2c_data_valid),
.data_in_ready(si570_i2c_data_ready),
.data_in_last(si570_i2c_data_last),
.data_out(),
.data_out_valid(),
.data_out_ready(1),
.data_out_last(),
.scl_i(i2c_scl_i),
.scl_o(i2c_scl_o),
.scl_t(i2c_scl_t),
.sda_i(i2c_sda_i),
.sda_o(i2c_sda_o),
.sda_t(i2c_sda_t),
.busy(),
.bus_control(),
.bus_active(),
.missed_ack(),
.prescale(800),
.stop_on_idle(1)
);
// XGMII 10G PHY
assign qsfp_modesell = 1'b1;
assign qsfp_resetl = 1'b1;
@ -322,7 +418,7 @@ ten_gig_eth_pcs_pma_inst (
.coreclk_out(),
.reset(rst_125mhz_int),
.reset(rst_125mhz_int | si570_i2c_init_busy),
.sim_speedup_control(1'b0),

View File

@ -0,0 +1,895 @@
/*
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

View File

@ -0,0 +1,486 @@
/*
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
/*
* si570_i2c_init
*/
module si570_i2c_init (
input wire clk,
input wire rst,
/*
* I2C master interface
*/
output wire [6:0] cmd_address,
output wire cmd_start,
output wire cmd_read,
output wire cmd_write,
output wire cmd_write_multiple,
output wire cmd_stop,
output wire cmd_valid,
input wire cmd_ready,
output wire [7:0] data_out,
output wire data_out_valid,
input wire data_out_ready,
output wire data_out_last,
/*
* Status
*/
output wire busy,
/*
* Configuration
*/
input wire start
);
/*
Generic module for I2C bus initialization. Good for use when multiple devices
on an I2C bus must be initialized on system start without intervention of a
general-purpose processor.
Copy this file and change init_data and INIT_DATA_LEN as needed.
This module can be used in two modes: simple device initalization, or multiple
device initialization. In multiple device mode, the same initialization sequence
can be performed on multiple different device addresses.
To use single device mode, only use the start write to address and write data commands.
The module will generate the I2C commands in sequential order. Terminate the list
with a 0 entry.
To use the multiple device mode, use the start data and start address block commands
to set up lists of initialization data and device addresses. The module enters
multiple device mode upon seeing a start data block command. The module stores the
offset of the start of the data block and then skips ahead until it reaches a start
address block command. The module will store the offset to the address block and
read the first address in the block. Then it will jump back to the data block
and execute it, substituting the stored address for each current address write
command. Upon reaching the start address block command, the module will read out the
next address and start again at the top of the data block. If the module encounters
a start data block command while looking for an address, then it will store a new data
offset and then look for a start address block command. Terminate the list with a 0
entry. Normal address commands will operate normally inside a data block.
Commands:
00 0000000 : stop
00 0000001 : exit multiple device mode
00 0000011 : start write to current address
00 0001000 : start address block
00 0001001 : start data block
00 1000001 : send I2C stop
01 aaaaaaa : start write to address
1 dddddddd : write 8-bit data
Examples
write 0x11223344 to register 0x0004 on device at 0x50
01 1010000 start write to 0x50
1 00000000 write address 0x0004
1 00000100
1 00010001 write data 0x11223344
1 00100010
1 00110011
1 01000100
0 00000000 stop
write 0x11223344 to register 0x0004 on devices at 0x50, 0x51, 0x52, and 0x53
00 0001001 start data block
00 0000011 start write to current address
1 00000100
1 00010001 write data 0x11223344
1 00100010
1 00110011
1 01000100
00 0001000 start address block
01 1010000 address 0x50
01 1010000 address 0x51
01 1010000 address 0x52
01 1010000 address 0x53
00 0000000 stop
*/
// init_data ROM
localparam INIT_DATA_LEN = 24;
reg [8:0] init_data [INIT_DATA_LEN-1:0];
initial begin
// set up I2C muxes to select U32
init_data[0] = {2'b01, 7'b1110101}; // select U80 (0x75)
init_data[1] = {1'b1, 8'b00000000}; // disconnect all outputs
init_data[2] = {2'b00, 7'b1000001}; // I2C stop
init_data[3] = {2'b01, 7'b1110100}; // select U28 (0x74)
init_data[4] = {1'b1, 8'b00000001}; // connect only output 0 to SI570
init_data[5] = {2'b00, 7'b1000001}; // I2C stop
// set SI570 output frequency (U32)
// freeze DCO
init_data[6] = {2'b01, 7'b1011101};
init_data[7] = {1'b1, 8'd137};
init_data[8] = {1'b1, 8'h10}; // 137: 0x10
// set output to 156.25 MHz (HS_DIV=4 N1=8 DCO=5000.0 RFREQ=0x02BC035168)
init_data[9] = {2'b01, 7'b1011101};
init_data[10] = {1'b1, 8'd7};
init_data[11] = {1'b1, 8'h01}; // 7: HS_DIV[2:0], N1[6:2]
init_data[12] = {1'b1, 8'hC2}; // 8: N1[1:0], RFREQ[37:32]
init_data[13] = {1'b1, 8'hBC}; // 9: RFREQ[31:24]
init_data[14] = {1'b1, 8'h03}; // 10: RFREQ[23:16]
init_data[15] = {1'b1, 8'h51}; // 11: RFREQ[15:8]
init_data[16] = {1'b1, 8'h68}; // 12: RFREQ[7:0]
// un-freeze DCO
init_data[17] = {2'b01, 7'b1011101};
init_data[18] = {1'b1, 8'd137};
init_data[19] = {1'b1, 8'h00}; // 137: 0x00
// new frequency
init_data[20] = {2'b01, 7'b1011101};
init_data[21] = {1'b1, 8'd135};
init_data[22] = {1'b1, 8'h40}; // 135: 0x40
init_data[23] = 9'd0; // stop
end
localparam [3:0]
STATE_IDLE = 3'd0,
STATE_RUN = 3'd1,
STATE_TABLE_1 = 3'd2,
STATE_TABLE_2 = 3'd3,
STATE_TABLE_3 = 3'd4;
reg [4:0] state_reg = STATE_IDLE, state_next;
parameter AW = $clog2(INIT_DATA_LEN);
reg [8:0] init_data_reg = 9'd0;
reg [AW-1:0] address_reg = {AW{1'b0}}, address_next;
reg [AW-1:0] address_ptr_reg = {AW{1'b0}}, address_ptr_next;
reg [AW-1:0] data_ptr_reg = {AW{1'b0}}, data_ptr_next;
reg [6:0] cur_address_reg = 7'd0, cur_address_next;
reg [6:0] cmd_address_reg = 7'd0, cmd_address_next;
reg cmd_start_reg = 1'b0, cmd_start_next;
reg cmd_write_reg = 1'b0, cmd_write_next;
reg cmd_stop_reg = 1'b0, cmd_stop_next;
reg cmd_valid_reg = 1'b0, cmd_valid_next;
reg [7:0] data_out_reg = 8'd0, data_out_next;
reg data_out_valid_reg = 1'b0, data_out_valid_next;
reg start_flag_reg = 1'b0, start_flag_next;
reg busy_reg = 1'b0;
assign cmd_address = cmd_address_reg;
assign cmd_start = cmd_start_reg;
assign cmd_read = 1'b0;
assign cmd_write = cmd_write_reg;
assign cmd_write_multiple = 1'b0;
assign cmd_stop = cmd_stop_reg;
assign cmd_valid = cmd_valid_reg;
assign data_out = data_out_reg;
assign data_out_valid = data_out_valid_reg;
assign data_out_last = 1'b1;
assign busy = busy_reg;
always @* begin
state_next = STATE_IDLE;
address_next = address_reg;
address_ptr_next = address_ptr_reg;
data_ptr_next = data_ptr_reg;
cur_address_next = cur_address_reg;
cmd_address_next = cmd_address_reg;
cmd_start_next = cmd_start_reg & ~(cmd_valid & cmd_ready);
cmd_write_next = cmd_write_reg & ~(cmd_valid & cmd_ready);
cmd_stop_next = cmd_stop_reg & ~(cmd_valid & cmd_ready);
cmd_valid_next = cmd_valid_reg & ~cmd_ready;
data_out_next = data_out_reg;
data_out_valid_next = data_out_valid_reg & ~data_out_ready;
start_flag_next = start_flag_reg;
if (cmd_valid | data_out_valid) begin
// wait for output registers to clear
state_next = state_reg;
end else begin
case (state_reg)
STATE_IDLE: begin
// wait for start signal
if (~start_flag_reg & start) begin
address_next = {AW{1'b0}};
start_flag_next = 1'b1;
state_next = STATE_RUN;
end else begin
state_next = STATE_IDLE;
end
end
STATE_RUN: begin
// process commands
if (init_data_reg[8] == 1'b1) begin
// write data
cmd_write_next = 1'b1;
cmd_stop_next = 1'b0;
cmd_valid_next = 1'b1;
data_out_next = init_data_reg[7:0];
data_out_valid_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg[8:7] == 2'b01) begin
// write address
cmd_address_next = init_data_reg[6:0];
cmd_start_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg == 9'b001000001) begin
// send stop
cmd_write_next = 1'b0;
cmd_start_next = 1'b0;
cmd_stop_next = 1'b1;
cmd_valid_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg == 9'b000001001) begin
// data table start
data_ptr_next = address_reg + 1;
address_next = address_reg + 1;
state_next = STATE_TABLE_1;
end else if (init_data_reg == 9'd0) begin
// stop
cmd_start_next = 1'b0;
cmd_write_next = 1'b0;
cmd_stop_next = 1'b1;
cmd_valid_next = 1'b1;
state_next = STATE_IDLE;
end else begin
// invalid command, skip
address_next = address_reg + 1;
state_next = STATE_RUN;
end
end
STATE_TABLE_1: begin
// find address table start
if (init_data_reg == 9'b000001000) begin
// address table start
address_ptr_next = address_reg + 1;
address_next = address_reg + 1;
state_next = STATE_TABLE_2;
end else if (init_data_reg == 9'b000001001) begin
// data table start
data_ptr_next = address_reg + 1;
address_next = address_reg + 1;
state_next = STATE_TABLE_1;
end else if (init_data_reg == 1) begin
// exit mode
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg == 9'd0) begin
// stop
cmd_start_next = 1'b0;
cmd_write_next = 1'b0;
cmd_stop_next = 1'b1;
cmd_valid_next = 1'b1;
state_next = STATE_IDLE;
end else begin
// invalid command, skip
address_next = address_reg + 1;
state_next = STATE_TABLE_1;
end
end
STATE_TABLE_2: begin
// find next address
if (init_data_reg[8:7] == 2'b01) begin
// write address command
// store address and move to data table
cur_address_next = init_data_reg[6:0];
address_ptr_next = address_reg + 1;
address_next = data_ptr_reg;
state_next = STATE_TABLE_3;
end else if (init_data_reg == 9'b000001001) begin
// data table start
data_ptr_next = address_reg + 1;
address_next = address_reg + 1;
state_next = STATE_TABLE_1;
end else if (init_data_reg == 9'd1) begin
// exit mode
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg == 9'd0) begin
// stop
cmd_start_next = 1'b0;
cmd_write_next = 1'b0;
cmd_stop_next = 1'b1;
cmd_valid_next = 1'b1;
state_next = STATE_IDLE;
end else begin
// invalid command, skip
address_next = address_reg + 1;
state_next = STATE_TABLE_2;
end
end
STATE_TABLE_3: begin
// process data table with selected address
if (init_data_reg[8] == 1'b1) begin
// write data
cmd_write_next = 1'b1;
cmd_stop_next = 1'b0;
cmd_valid_next = 1'b1;
data_out_next = init_data_reg[7:0];
data_out_valid_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_TABLE_3;
end else if (init_data_reg[8:7] == 2'b01) begin
// write address
cmd_address_next = init_data_reg[6:0];
cmd_start_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_TABLE_3;
end else if (init_data_reg == 9'b000000011) begin
// write current address
cmd_address_next = cur_address_reg;
cmd_start_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_TABLE_3;
end else if (init_data_reg == 9'b001000001) begin
// send stop
cmd_write_next = 1'b0;
cmd_start_next = 1'b0;
cmd_stop_next = 1'b1;
cmd_valid_next = 1'b1;
address_next = address_reg + 1;
state_next = STATE_TABLE_3;
end else if (init_data_reg == 9'b000001001) begin
// data table start
data_ptr_next = address_reg + 1;
address_next = address_reg + 1;
state_next = STATE_TABLE_1;
end else if (init_data_reg == 9'b000001000) begin
// address table start
address_next = address_ptr_reg;
state_next = STATE_TABLE_2;
end else if (init_data_reg == 9'd1) begin
// exit mode
address_next = address_reg + 1;
state_next = STATE_RUN;
end else if (init_data_reg == 9'd0) begin
// stop
cmd_start_next = 1'b0;
cmd_write_next = 1'b0;
cmd_stop_next = 1'b1;
cmd_valid_next = 1'b1;
state_next = STATE_IDLE;
end else begin
// invalid command, skip
address_next = address_reg + 1;
state_next = STATE_TABLE_3;
end
end
endcase
end
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
init_data_reg <= 9'd0;
address_reg <= {AW{1'b0}};
address_ptr_reg <= {AW{1'b0}};
data_ptr_reg <= {AW{1'b0}};
cur_address_reg <= 7'd0;
cmd_valid_reg <= 1'b0;
data_out_valid_reg <= 1'b0;
start_flag_reg <= 1'b0;
busy_reg <= 1'b0;
end else begin
state_reg <= state_next;
// read init_data ROM
init_data_reg <= init_data[address_next];
address_reg <= address_next;
address_ptr_reg <= address_ptr_next;
data_ptr_reg <= data_ptr_next;
cur_address_reg <= cur_address_next;
cmd_valid_reg <= cmd_valid_next;
data_out_valid_reg <= data_out_valid_next;
start_flag_reg <= start & start_flag_next;
busy_reg <= (state_reg != STATE_IDLE);
end
cmd_address_reg <= cmd_address_next;
cmd_start_reg <= cmd_start_next;
cmd_write_reg <= cmd_write_next;
cmd_stop_reg <= cmd_stop_next;
data_out_reg <= data_out_next;
end
endmodule