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:
parent
833d1dac81
commit
36af29db77
@ -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]
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
||||
|
895
example/VCU108/fpga_10g/rtl/i2c_master.v
Normal file
895
example/VCU108/fpga_10g/rtl/i2c_master.v
Normal 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
|
486
example/VCU108/fpga_10g/rtl/si570_i2c_init.v
Normal file
486
example/VCU108/fpga_10g/rtl/si570_i2c_init.v
Normal 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
|
Loading…
x
Reference in New Issue
Block a user