added initial files for spd
This commit is contained in:
parent
3b2ef2afa8
commit
fbc4b5ff9a
|
|
@ -0,0 +1,39 @@
|
||||||
|
`timescale 1ps/1ps
|
||||||
|
|
||||||
|
module clk_wiz
|
||||||
|
(
|
||||||
|
input clk_in1,
|
||||||
|
output clk_out1,
|
||||||
|
input reset,
|
||||||
|
output locked
|
||||||
|
);
|
||||||
|
wire clk_out1_clk_wiz_0;
|
||||||
|
|
||||||
|
|
||||||
|
wire clkfbout;
|
||||||
|
|
||||||
|
PLLE2_ADV
|
||||||
|
#(.BANDWIDTH ("OPTIMIZED"),
|
||||||
|
.COMPENSATION ("INTERNAL"),
|
||||||
|
.STARTUP_WAIT ("FALSE"),
|
||||||
|
.DIVCLK_DIVIDE (1),
|
||||||
|
.CLKFBOUT_MULT (5), // 200 MHz * 5 = 1000 MHz
|
||||||
|
.CLKFBOUT_PHASE (0.000),
|
||||||
|
.CLKOUT0_DIVIDE (10), // 1000 MHz / 10 = 100 MHz
|
||||||
|
.CLKOUT0_PHASE (0.000),
|
||||||
|
.CLKOUT0_DUTY_CYCLE (0.500),
|
||||||
|
.CLKIN1_PERIOD (5.000) // 200 MHz input
|
||||||
|
)
|
||||||
|
plle2_adv_inst
|
||||||
|
(
|
||||||
|
.CLKFBOUT (clkfbout),
|
||||||
|
.CLKOUT0 (clk_out1_clk_wiz_0),
|
||||||
|
.CLKFBIN (clkfbout),
|
||||||
|
.CLKIN1 (clk_in1),
|
||||||
|
.LOCKED (locked),
|
||||||
|
.RST (reset)
|
||||||
|
);
|
||||||
|
BUFG clkout1_buf
|
||||||
|
(.O (clk_out1),
|
||||||
|
.I (clk_out1_clk_wiz_0));
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,694 @@
|
||||||
|
`timescale 1ns / 1ps
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Company: www.circuitden.com
|
||||||
|
// Engineer: Artin Isagholian
|
||||||
|
// artinisagholian@gmail.com
|
||||||
|
//
|
||||||
|
// Create Date: 01/20/2021 05:47:22 PM
|
||||||
|
// Design Name:
|
||||||
|
// Module Name: i2c_master
|
||||||
|
// Project Name:
|
||||||
|
// Target Devices:
|
||||||
|
// Tool Versions:
|
||||||
|
// Description:
|
||||||
|
//
|
||||||
|
// Dependencies:
|
||||||
|
//
|
||||||
|
// Revision:
|
||||||
|
// Revision 0.01 - File Created
|
||||||
|
// Additional Comments:
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
module i2c_master#(
|
||||||
|
parameter DATA_WIDTH = 8,
|
||||||
|
parameter REGISTER_WIDTH = 8,
|
||||||
|
parameter ADDRESS_WIDTH = 7
|
||||||
|
)(
|
||||||
|
input wire clock,
|
||||||
|
input wire reset_n,
|
||||||
|
input wire enable,
|
||||||
|
input wire read_write,
|
||||||
|
input wire [DATA_WIDTH-1:0] mosi_data,
|
||||||
|
input wire [REGISTER_WIDTH-1:0] register_address,
|
||||||
|
input wire [ADDRESS_WIDTH-1:0] device_address,
|
||||||
|
input wire [15:0] divider,
|
||||||
|
|
||||||
|
output reg [DATA_WIDTH-1:0] miso_data,
|
||||||
|
output reg busy,
|
||||||
|
|
||||||
|
inout external_serial_data,
|
||||||
|
inout external_serial_clock,
|
||||||
|
// debug if slave NACKs
|
||||||
|
output reg slave_nack // high at same time as busy deasserts if slave nacks at previous transaction. otherwise low
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/*INSTANTATION TEMPLATE
|
||||||
|
i2c_master #(.DATA_WIDTH(8),.REGISTER_WIDTH(8),.ADDRESS_WIDTH(7))
|
||||||
|
i2c_master_inst(
|
||||||
|
.clock (),
|
||||||
|
.reset_n (),
|
||||||
|
.enable (),
|
||||||
|
.read_write (),
|
||||||
|
.mosi_data (),
|
||||||
|
.register_address (),
|
||||||
|
.device_address (),
|
||||||
|
.divider (),
|
||||||
|
|
||||||
|
.miso_data (),
|
||||||
|
.busy (),
|
||||||
|
|
||||||
|
.external_serial_data (),
|
||||||
|
.external_serial_clock (),
|
||||||
|
.slave_nack ()
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
S_IDLE = 0,
|
||||||
|
S_START = 1,
|
||||||
|
S_WRITE_ADDR_W = 2,
|
||||||
|
S_CHECK_ACK = 3,
|
||||||
|
S_WRITE_REG_ADDR = 4,
|
||||||
|
S_RESTART = 5,
|
||||||
|
S_WRITE_ADDR_R = 6,
|
||||||
|
S_READ_REG = 7,
|
||||||
|
S_SEND_NACK = 8,
|
||||||
|
S_SEND_STOP = 9,
|
||||||
|
S_WRITE_REG_DATA = 10,
|
||||||
|
S_WRITE_REG_ADDR_MSB = 11,
|
||||||
|
S_WRITE_REG_DATA_MSB = 12,
|
||||||
|
S_READ_REG_MSB = 13,
|
||||||
|
S_SEND_ACK = 14
|
||||||
|
} state_type;
|
||||||
|
|
||||||
|
state_type state;
|
||||||
|
state_type _state;
|
||||||
|
state_type post_state;
|
||||||
|
state_type _post_state;
|
||||||
|
reg serial_clock;
|
||||||
|
logic _serial_clock;
|
||||||
|
reg [ADDRESS_WIDTH:0] saved_device_address;
|
||||||
|
logic [ADDRESS_WIDTH:0] _saved_device_address;
|
||||||
|
reg [REGISTER_WIDTH-1:0] saved_register_address;
|
||||||
|
logic [REGISTER_WIDTH-1:0] _saved_register_address;
|
||||||
|
reg [DATA_WIDTH-1:0] saved_mosi_data;
|
||||||
|
logic [DATA_WIDTH-1:0] _saved_mosi_data;
|
||||||
|
reg [1:0] process_counter;
|
||||||
|
logic [1:0] _process_counter;
|
||||||
|
reg [7:0] bit_counter;
|
||||||
|
logic [7:0] _bit_counter;
|
||||||
|
reg serial_data;
|
||||||
|
logic _serial_data;
|
||||||
|
reg post_serial_data;
|
||||||
|
logic _post_serial_data;
|
||||||
|
reg last_acknowledge;
|
||||||
|
logic _last_acknowledge;
|
||||||
|
logic _saved_read_write;
|
||||||
|
reg saved_read_write;
|
||||||
|
reg [15:0] divider_counter;
|
||||||
|
logic [15:0] _divider_counter;
|
||||||
|
reg divider_tick;
|
||||||
|
logic [DATA_WIDTH-1:0] _miso_data;
|
||||||
|
logic _busy;
|
||||||
|
logic serial_data_output_enable;
|
||||||
|
logic serial_clock_output_enable;
|
||||||
|
|
||||||
|
assign external_serial_clock = (serial_clock_output_enable) ? serial_clock : 1'bz;
|
||||||
|
assign external_serial_data = (serial_data_output_enable) ? serial_data : 1'bz;
|
||||||
|
|
||||||
|
// determine if slave NACK
|
||||||
|
reg last_acknowledge_q;
|
||||||
|
always_ff @(posedge clock) begin
|
||||||
|
if (!reset_n) begin
|
||||||
|
last_acknowledge_q <= 1'b0;
|
||||||
|
slave_nack <= 1'b0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
last_acknowledge_q <= last_acknowledge? 1'b1 : last_acknowledge_q; // if last_ack becomes high, store that high
|
||||||
|
if(!_busy && busy) begin // if busy is about to deassert next clk
|
||||||
|
last_acknowledge_q <= 1'b0; // reset to zero
|
||||||
|
slave_nack <= !last_acknowledge_q;// if last_acknowledge_q never becomes 1 then the slave NACKs
|
||||||
|
end
|
||||||
|
else if(_busy) begin
|
||||||
|
slave_nack <= 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always_comb begin
|
||||||
|
_state = state;
|
||||||
|
_post_state = post_state;
|
||||||
|
_process_counter = process_counter;
|
||||||
|
_bit_counter = bit_counter;
|
||||||
|
_last_acknowledge = last_acknowledge;
|
||||||
|
_miso_data = miso_data;
|
||||||
|
_saved_read_write = saved_read_write;
|
||||||
|
_busy = busy;
|
||||||
|
_divider_counter = divider_counter;
|
||||||
|
_saved_register_address = saved_register_address;
|
||||||
|
_saved_device_address = saved_device_address;
|
||||||
|
_saved_mosi_data = saved_mosi_data;
|
||||||
|
_serial_data = serial_data;
|
||||||
|
_serial_clock = serial_clock;
|
||||||
|
_post_serial_data = post_serial_data;
|
||||||
|
|
||||||
|
if (divider_counter == divider) begin
|
||||||
|
_divider_counter = 0;
|
||||||
|
divider_tick = 1;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_divider_counter = divider_counter + 1;
|
||||||
|
divider_tick = 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (state!=S_IDLE && state!=S_CHECK_ACK && state!=S_READ_REG && state!=S_READ_REG_MSB) begin
|
||||||
|
serial_data_output_enable = 1;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
serial_data_output_enable = 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (state!=S_IDLE && process_counter!=1 && process_counter!=2) begin
|
||||||
|
serial_clock_output_enable = 1;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
serial_clock_output_enable = 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
case (state)
|
||||||
|
S_IDLE: begin
|
||||||
|
_process_counter = 0;
|
||||||
|
_bit_counter = 0;
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_busy = 0;
|
||||||
|
_saved_read_write = read_write;
|
||||||
|
_saved_register_address = register_address;
|
||||||
|
_saved_device_address = {device_address,1'b0};
|
||||||
|
_saved_mosi_data = mosi_data;
|
||||||
|
_serial_data = 1;
|
||||||
|
_serial_clock = 1;
|
||||||
|
|
||||||
|
if (enable) begin
|
||||||
|
_state = S_START;
|
||||||
|
_post_state = S_WRITE_ADDR_W;
|
||||||
|
_busy = 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_START: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
_serial_data = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_bit_counter = 8;
|
||||||
|
_process_counter = 3;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
_serial_clock = 0;
|
||||||
|
_process_counter = 0;
|
||||||
|
_state = post_state;
|
||||||
|
_serial_data = saved_device_address[ADDRESS_WIDTH];
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_WRITE_ADDR_W: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_serial_clock = 0;
|
||||||
|
_bit_counter = bit_counter - 1;
|
||||||
|
_process_counter = 3;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
if (bit_counter == 0) begin
|
||||||
|
_post_serial_data = saved_register_address[REGISTER_WIDTH-1];
|
||||||
|
|
||||||
|
if (REGISTER_WIDTH == 16) begin
|
||||||
|
_post_state = S_WRITE_REG_ADDR_MSB;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_post_state = S_WRITE_REG_ADDR;
|
||||||
|
end
|
||||||
|
|
||||||
|
_state = S_CHECK_ACK;
|
||||||
|
_bit_counter = 8;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_serial_data = saved_device_address[bit_counter-1];
|
||||||
|
end
|
||||||
|
_process_counter = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_CHECK_ACK: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_serial_clock = 0;
|
||||||
|
|
||||||
|
if (external_serial_data == 0) begin
|
||||||
|
_last_acknowledge = 1;
|
||||||
|
end
|
||||||
|
_process_counter = 3;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
if (last_acknowledge == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_serial_data = post_serial_data;
|
||||||
|
_state = post_state;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_state = S_SEND_STOP;
|
||||||
|
end
|
||||||
|
_process_counter = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_WRITE_REG_ADDR_MSB: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_serial_clock = 0;
|
||||||
|
_bit_counter = bit_counter - 1;
|
||||||
|
_process_counter = 3;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
if (bit_counter == 0) begin
|
||||||
|
_post_state = S_WRITE_REG_ADDR;
|
||||||
|
_post_serial_data = saved_register_address[7];
|
||||||
|
_bit_counter = 8;
|
||||||
|
_serial_data = 0;
|
||||||
|
_state = S_CHECK_ACK;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_serial_data = saved_register_address[bit_counter+7];
|
||||||
|
end
|
||||||
|
_process_counter = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_WRITE_REG_ADDR: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_serial_clock = 0;
|
||||||
|
_bit_counter = bit_counter - 1;
|
||||||
|
_process_counter = 3;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
if (bit_counter == 0) begin
|
||||||
|
if (read_write == 0) begin
|
||||||
|
if (DATA_WIDTH == 16) begin
|
||||||
|
_post_state = S_WRITE_REG_DATA_MSB;
|
||||||
|
_post_serial_data = saved_mosi_data[DATA_WIDTH == 16? 15 : 7];
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_post_state = S_WRITE_REG_DATA;
|
||||||
|
_post_serial_data = saved_mosi_data[7];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_post_state = S_RESTART;
|
||||||
|
_post_serial_data = 1;
|
||||||
|
end
|
||||||
|
_bit_counter = 8;
|
||||||
|
_serial_data = 0;
|
||||||
|
_state = S_CHECK_ACK;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_serial_data = saved_register_address[bit_counter-1];
|
||||||
|
end
|
||||||
|
_process_counter = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_WRITE_REG_DATA_MSB: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_serial_clock = 0;
|
||||||
|
_bit_counter = bit_counter - 1;
|
||||||
|
_process_counter = 3;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
if (bit_counter == 0) begin
|
||||||
|
_state = S_CHECK_ACK;
|
||||||
|
_post_state = S_WRITE_REG_DATA;
|
||||||
|
_post_serial_data = saved_mosi_data[7];
|
||||||
|
_bit_counter = 8;
|
||||||
|
_serial_data = 0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_serial_data = saved_mosi_data[bit_counter+7];
|
||||||
|
end
|
||||||
|
_process_counter = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_WRITE_REG_DATA: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_serial_clock = 0;
|
||||||
|
_bit_counter = bit_counter - 1;
|
||||||
|
_process_counter = 3;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
if (bit_counter == 0) begin
|
||||||
|
_state = S_CHECK_ACK;
|
||||||
|
_post_state = S_SEND_STOP;
|
||||||
|
_post_serial_data = 0;
|
||||||
|
_bit_counter = 8;
|
||||||
|
_serial_data = 0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_serial_data = saved_mosi_data[bit_counter-1];
|
||||||
|
end
|
||||||
|
_process_counter = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_RESTART: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
_process_counter = 2;
|
||||||
|
_serial_clock = 1;
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_process_counter = 3;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
_state = S_START;
|
||||||
|
_post_state = S_WRITE_ADDR_R;
|
||||||
|
_saved_device_address[0] = 1;
|
||||||
|
_process_counter = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_WRITE_ADDR_R: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_serial_clock = 0;
|
||||||
|
_bit_counter = bit_counter - 1;
|
||||||
|
_process_counter = 3;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
if (bit_counter == 0) begin
|
||||||
|
if (DATA_WIDTH == 16) begin
|
||||||
|
_post_state = S_READ_REG_MSB;
|
||||||
|
_post_serial_data = 0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_post_state = S_READ_REG;
|
||||||
|
_post_serial_data = 0;
|
||||||
|
end
|
||||||
|
_state = S_CHECK_ACK;
|
||||||
|
_bit_counter = 8;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
_serial_data = saved_device_address[bit_counter-1];
|
||||||
|
end
|
||||||
|
_process_counter = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_READ_REG_MSB: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_serial_clock = 0;
|
||||||
|
//sample data on this rising edge of scl
|
||||||
|
_miso_data[bit_counter+7] = external_serial_data;
|
||||||
|
_bit_counter = bit_counter - 1;
|
||||||
|
_process_counter = 3;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
if (bit_counter == 0) begin
|
||||||
|
_post_state = S_READ_REG;
|
||||||
|
_state = S_SEND_ACK;
|
||||||
|
_bit_counter = 8;
|
||||||
|
_serial_data = 0;
|
||||||
|
end
|
||||||
|
_process_counter = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_READ_REG: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_serial_clock = 0;
|
||||||
|
//sample data on this rising edge of scl
|
||||||
|
_miso_data[bit_counter-1] = external_serial_data;
|
||||||
|
_bit_counter = bit_counter - 1;
|
||||||
|
_process_counter = 3;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
if (bit_counter == 0) begin
|
||||||
|
_state = S_SEND_NACK;
|
||||||
|
_serial_data = 0;
|
||||||
|
end
|
||||||
|
_process_counter = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_SEND_NACK: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_serial_data = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_process_counter = 3;
|
||||||
|
_serial_clock = 0;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
_state = S_SEND_STOP;
|
||||||
|
_process_counter = 0;
|
||||||
|
_serial_data = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_SEND_ACK: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
_serial_data = 0;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_process_counter = 3;
|
||||||
|
_serial_clock = 0;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
_state = post_state;
|
||||||
|
_process_counter = 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S_SEND_STOP: begin
|
||||||
|
if (divider_tick) begin
|
||||||
|
case (process_counter)
|
||||||
|
0: begin
|
||||||
|
_serial_clock = 1;
|
||||||
|
_process_counter = 1;
|
||||||
|
end
|
||||||
|
1: begin
|
||||||
|
//check for clock stretching
|
||||||
|
if (external_serial_clock == 1) begin
|
||||||
|
_last_acknowledge = 0;
|
||||||
|
_process_counter = 2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
2: begin
|
||||||
|
_process_counter = 3;
|
||||||
|
_serial_data = 1;
|
||||||
|
end
|
||||||
|
3: begin
|
||||||
|
_state = S_IDLE;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
always_ff @(posedge clock) begin
|
||||||
|
if (!reset_n) begin
|
||||||
|
state <= S_IDLE;
|
||||||
|
post_state <= S_IDLE;
|
||||||
|
process_counter <= 0;
|
||||||
|
bit_counter <= 0;
|
||||||
|
last_acknowledge <= 0;
|
||||||
|
miso_data <= 0;
|
||||||
|
saved_read_write <= 0;
|
||||||
|
divider_counter <= 0;
|
||||||
|
saved_device_address <= 0;
|
||||||
|
saved_register_address <= 0;
|
||||||
|
saved_mosi_data <= 0;
|
||||||
|
serial_clock <= 0;
|
||||||
|
serial_data <= 0;
|
||||||
|
saved_mosi_data <= 0;
|
||||||
|
post_serial_data <= 0;
|
||||||
|
busy <= 0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
state <= _state;
|
||||||
|
post_state <= _post_state;
|
||||||
|
process_counter <= _process_counter;
|
||||||
|
bit_counter <= _bit_counter;
|
||||||
|
last_acknowledge <= _last_acknowledge;
|
||||||
|
miso_data <= _miso_data;
|
||||||
|
saved_read_write <= _saved_read_write;
|
||||||
|
divider_counter <= _divider_counter;
|
||||||
|
saved_device_address <= _saved_device_address;
|
||||||
|
saved_register_address <= _saved_register_address;
|
||||||
|
saved_mosi_data <= _saved_mosi_data;
|
||||||
|
serial_clock <= _serial_clock;
|
||||||
|
serial_data <= _serial_data;
|
||||||
|
post_serial_data <= _post_serial_data;
|
||||||
|
busy <= _busy;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
`default_nettype none
|
||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
module spd_reader (
|
||||||
|
// clock and reset
|
||||||
|
input wire i_clk,
|
||||||
|
input wire i_rst_n,
|
||||||
|
// i2c interface
|
||||||
|
inout wire i2c_scl,
|
||||||
|
inout wire i2c_sda
|
||||||
|
// uart interface
|
||||||
|
// input uart_rx,
|
||||||
|
// output uart_tx
|
||||||
|
);
|
||||||
|
|
||||||
|
// byte 2: DRAM Device Type (DDR3 SDRAM = 0x0B)
|
||||||
|
// byte 3: Module Type (SO-DIMM = 0x03)
|
||||||
|
// byte 4: SDRAM Density and Banks ([6:4] = BA_BITS, [3:0] = SDRAM capacity)
|
||||||
|
// byte 5: SDRAM Addressing ([5:3] = Row Addr , [2:0] = Column Addr)
|
||||||
|
// byte 7: Module Organization ([5:3] = Ranks , Device Width = [2:0])
|
||||||
|
// byte 8: Module Memory Bus Width ([2:0] = Bus Width)
|
||||||
|
// byte 10,11: Medium Timebase (MTB) Dividend (0x01), Medium Timebase (MTB) Divisor (0x08 = 0.125ns , 0x10 = 0.0625ns) (tXX = tXX(MTB) * MTB)
|
||||||
|
// byte 12: SDRAM Minimum Cycle Time tCK
|
||||||
|
|
||||||
|
localparam I2C_ADDRESS = 7'h30;
|
||||||
|
localparam IDLE = 0,
|
||||||
|
READ_ADDRESS = 1,
|
||||||
|
WAIT_ACK = 2;
|
||||||
|
(* mark_debug = "true" *) reg[1:0] state_find_i2c_address;
|
||||||
|
(* mark_debug = "true" *) reg find_i2c_address_done;
|
||||||
|
(* mark_debug = "true" *) reg[6:0] i2c_address;
|
||||||
|
|
||||||
|
// i2c master interface
|
||||||
|
reg enable;
|
||||||
|
reg read_write;
|
||||||
|
reg[7:0] register_address;
|
||||||
|
reg[6:0] device_address;
|
||||||
|
wire[7:0] miso_data;
|
||||||
|
wire busy;
|
||||||
|
wire slave_nack;
|
||||||
|
|
||||||
|
always @(posedge i_clk, negedge i_rst_n) begin
|
||||||
|
if(!i_rst_n) begin
|
||||||
|
state_find_i2c_address <= IDLE;
|
||||||
|
find_i2c_address_done <= 0;
|
||||||
|
enable <= 1'b0;
|
||||||
|
read_write <= 1'b0;
|
||||||
|
register_address <= 8'd0;
|
||||||
|
i2c_address <= 7'd0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
// Find I2C Address of SPD
|
||||||
|
case(state_find_i2c_address)
|
||||||
|
IDLE: if(!find_i2c_address_done) begin
|
||||||
|
state_find_i2c_address <= READ_ADDRESS;
|
||||||
|
i2c_address <= 7'd0;
|
||||||
|
end
|
||||||
|
READ_ADDRESS: if(!busy) begin
|
||||||
|
enable <= 1'b1;
|
||||||
|
read_write <= 1'b1; // read i2c
|
||||||
|
register_address <= 8'h00; // just always read byte 0
|
||||||
|
device_address <= i2c_address;
|
||||||
|
state_find_i2c_address <= WAIT_ACK;
|
||||||
|
end
|
||||||
|
WAIT_ACK: if(!busy && !enable) begin
|
||||||
|
if(slave_nack) begin // if wrong i2c_address
|
||||||
|
i2c_address <= i2c_address + 1; // increment i2c address
|
||||||
|
state_find_i2c_address <= READ_ADDRESS;
|
||||||
|
end
|
||||||
|
else begin // I2C acks so i2c_address is correct!
|
||||||
|
state_find_i2c_address <= IDLE;
|
||||||
|
find_i2c_address_done <= 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
enable <= 1'b0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// module instantiations
|
||||||
|
i2c_master #(.DATA_WIDTH(8),.REGISTER_WIDTH(8),.ADDRESS_WIDTH(7))
|
||||||
|
i2c_master_inst(
|
||||||
|
.clock (i_clk),
|
||||||
|
.reset_n (i_rst_n),
|
||||||
|
.enable (enable),
|
||||||
|
.read_write (read_write),
|
||||||
|
.mosi_data (8'd0),
|
||||||
|
.register_address (register_address),
|
||||||
|
.device_address (device_address),
|
||||||
|
.divider (249), // 100MHz/(4*(249+1)) = 100KHz
|
||||||
|
|
||||||
|
.miso_data (miso_data),
|
||||||
|
.busy (busy),
|
||||||
|
|
||||||
|
.external_serial_data (i2c_sda),
|
||||||
|
.external_serial_clock (i2c_scl),
|
||||||
|
.slave_nack (slave_nack)
|
||||||
|
);
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
`default_nettype none
|
||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
module spd_reader_top (
|
||||||
|
// clock and reset
|
||||||
|
input wire sys_clk_p,
|
||||||
|
input wire sys_clk_n,
|
||||||
|
input wire i_rst_n,
|
||||||
|
// i2c interface
|
||||||
|
inout wire i2c_scl,
|
||||||
|
inout wire i2c_sda,
|
||||||
|
output wire i2c_lsb,
|
||||||
|
// fan
|
||||||
|
output wire fan_pwm
|
||||||
|
);
|
||||||
|
assign fan_pwm = 1'b0; // turn on fan
|
||||||
|
assign i2c_lsb = 1'b0;
|
||||||
|
wire clk_locked;
|
||||||
|
wire main_clk_100;
|
||||||
|
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//Differentia system clock to single end clock
|
||||||
|
//===========================================================================
|
||||||
|
wire sys_clk; // 200MHz
|
||||||
|
IBUFGDS u_ibufg_sys_clk
|
||||||
|
(
|
||||||
|
.I (sys_clk_p),
|
||||||
|
.IB (sys_clk_n),
|
||||||
|
.O (sys_clk)
|
||||||
|
);
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// Generate 100MHz
|
||||||
|
//===========================================================================
|
||||||
|
clk_wiz clk_wiz_inst
|
||||||
|
(
|
||||||
|
// Clock out ports
|
||||||
|
.clk_out1(main_clk_100),
|
||||||
|
// Status and control signals
|
||||||
|
.reset(!i_rst_n),
|
||||||
|
.locked(clk_locked),
|
||||||
|
// Clock in ports
|
||||||
|
.clk_in1(sys_clk)
|
||||||
|
);
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// Instantiate SPD reader
|
||||||
|
//===========================================================================
|
||||||
|
spd_reader spd_reader_inst (
|
||||||
|
.i_clk(main_clk_100),
|
||||||
|
.i_rst_n(i_rst_n && clk_locked),
|
||||||
|
.i2c_scl(i2c_scl),
|
||||||
|
.i2c_sda(i2c_sda)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,363 @@
|
||||||
|
`timescale 1ns / 1ps
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
//// ////
|
||||||
|
//// WISHBONE rev.B2 compliant synthesizable I2C Slave model ////
|
||||||
|
//// ////
|
||||||
|
//// ////
|
||||||
|
//// Authors: Richard Herveille (richard@asics.ws) www.asics.ws ////
|
||||||
|
//// John Sheahan (jrsheahan@optushome.com.au) ////
|
||||||
|
//// ////
|
||||||
|
//// Downloaded from: http://www.opencores.org/projects/i2c/ ////
|
||||||
|
//// ////
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
//// ////
|
||||||
|
//// Copyright (C) 2001,2002 Richard Herveille ////
|
||||||
|
//// richard@asics.ws ////
|
||||||
|
//// ////
|
||||||
|
//// This source file may be used and distributed without ////
|
||||||
|
//// restriction provided that this copyright statement is not ////
|
||||||
|
//// removed from the file and that any derivative work contains ////
|
||||||
|
//// the original copyright notice and the associated disclaimer.////
|
||||||
|
//// ////
|
||||||
|
//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY ////
|
||||||
|
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ////
|
||||||
|
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ////
|
||||||
|
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR ////
|
||||||
|
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ////
|
||||||
|
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ////
|
||||||
|
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ////
|
||||||
|
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ////
|
||||||
|
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ////
|
||||||
|
//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ////
|
||||||
|
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ////
|
||||||
|
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ////
|
||||||
|
//// POSSIBILITY OF SUCH DAMAGE. ////
|
||||||
|
//// ////
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// CVS Log
|
||||||
|
//
|
||||||
|
// $Id: i2c_slave_model.v,v 1.7 2006-09-04 09:08:51 rherveille Exp $
|
||||||
|
//
|
||||||
|
// $Date: 2006-09-04 09:08:51 $
|
||||||
|
// $Revision: 1.7 $
|
||||||
|
// $Author: rherveille $
|
||||||
|
// $Locker: $
|
||||||
|
// $State: Exp $
|
||||||
|
//
|
||||||
|
// Change History:
|
||||||
|
// $Log: not supported by cvs2svn $
|
||||||
|
// Revision 1.6 2005/02/28 11:33:48 rherveille
|
||||||
|
// Fixed Tsu:sta timing check.
|
||||||
|
// Added Thd:sta timing check.
|
||||||
|
//
|
||||||
|
// Revision 1.5 2003/12/05 11:05:19 rherveille
|
||||||
|
// Fixed slave address MSB='1' bug
|
||||||
|
//
|
||||||
|
// Revision 1.4 2003/09/11 08:25:37 rherveille
|
||||||
|
// Fixed a bug in the timing section. Changed 'tst_scl' into 'tst_sto'.
|
||||||
|
//
|
||||||
|
// Revision 1.3 2002/10/30 18:11:06 rherveille
|
||||||
|
// Added timing tests to i2c_model.
|
||||||
|
// Updated testbench.
|
||||||
|
//
|
||||||
|
// Revision 1.2 2002/03/17 10:26:38 rherveille
|
||||||
|
// Fixed some race conditions in the i2c-slave model.
|
||||||
|
// Added debug information.
|
||||||
|
// Added headers.
|
||||||
|
//
|
||||||
|
|
||||||
|
module i2c_slave (scl, sda);
|
||||||
|
|
||||||
|
//
|
||||||
|
// parameters
|
||||||
|
//
|
||||||
|
parameter I2C_ADR = 7'b001_0000;
|
||||||
|
|
||||||
|
//
|
||||||
|
// input && outpus
|
||||||
|
//
|
||||||
|
input scl;
|
||||||
|
inout sda;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Variable declaration
|
||||||
|
//
|
||||||
|
wire debug = 1'b1;
|
||||||
|
genvar i;
|
||||||
|
|
||||||
|
reg [7:0] mem [3:0]; // initiate memory
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
mem[0] = 8'd00;
|
||||||
|
mem[1] = 8'd01;
|
||||||
|
mem[2] = 8'd02;
|
||||||
|
mem[3] = 8'd03;
|
||||||
|
end
|
||||||
|
|
||||||
|
reg [7:0] mem_adr; // memory address
|
||||||
|
reg [7:0] mem_do; // memory data output
|
||||||
|
|
||||||
|
reg sta, d_sta;
|
||||||
|
reg sto, d_sto;
|
||||||
|
|
||||||
|
reg [7:0] sr; // 8bit shift register
|
||||||
|
reg rw; // read/write direction
|
||||||
|
|
||||||
|
wire my_adr; // my address called ??
|
||||||
|
wire i2c_reset; // i2c-statemachine reset
|
||||||
|
reg [2:0] bit_cnt; // 3bit downcounter
|
||||||
|
wire acc_done; // 8bits transfered
|
||||||
|
reg ld; // load downcounter
|
||||||
|
|
||||||
|
reg sda_o; // sda-drive level
|
||||||
|
wire sda_dly; // delayed version of sda
|
||||||
|
|
||||||
|
// statemachine declaration
|
||||||
|
parameter idle = 3'b000;
|
||||||
|
parameter slave_ack = 3'b001;
|
||||||
|
parameter get_mem_adr = 3'b010;
|
||||||
|
parameter gma_ack = 3'b011;
|
||||||
|
parameter data = 3'b100;
|
||||||
|
parameter data_ack = 3'b101;
|
||||||
|
|
||||||
|
reg [2:0] state; // synopsys enum_state
|
||||||
|
|
||||||
|
//
|
||||||
|
// module body
|
||||||
|
//
|
||||||
|
|
||||||
|
initial
|
||||||
|
begin
|
||||||
|
sda_o = 1'b1;
|
||||||
|
state = idle;
|
||||||
|
end
|
||||||
|
|
||||||
|
// generate shift register
|
||||||
|
always @(posedge scl)
|
||||||
|
sr <= #1 {sr[6:0],sda};
|
||||||
|
|
||||||
|
//detect my_address
|
||||||
|
assign my_adr = (sr[7:1] == I2C_ADR);
|
||||||
|
// FIXME: This should not be a generic assign, but rather
|
||||||
|
// qualified on address transfer phase and probably reset by stop
|
||||||
|
|
||||||
|
//generate bit-counter
|
||||||
|
always @(posedge scl)
|
||||||
|
if(ld)
|
||||||
|
bit_cnt <= #1 3'b111;
|
||||||
|
else
|
||||||
|
bit_cnt <= #1 bit_cnt - 3'h1;
|
||||||
|
|
||||||
|
//generate access done signal
|
||||||
|
assign acc_done = !(|bit_cnt);
|
||||||
|
|
||||||
|
// generate delayed version of sda
|
||||||
|
// this model assumes a hold time for sda after the falling edge of scl.
|
||||||
|
// According to the Phillips i2c spec, there s/b a 0 ns hold time for sda
|
||||||
|
// with regards to scl. If the data changes coincident with the clock, the
|
||||||
|
// acknowledge is missed
|
||||||
|
// Fix by Michael Sosnoski
|
||||||
|
assign #1 sda_dly = sda;
|
||||||
|
|
||||||
|
|
||||||
|
//detect start condition
|
||||||
|
always @(negedge sda)
|
||||||
|
if(scl)
|
||||||
|
begin
|
||||||
|
sta <= #1 1'b1;
|
||||||
|
d_sta <= #1 1'b0;
|
||||||
|
sto <= #1 1'b0;
|
||||||
|
|
||||||
|
if(debug)
|
||||||
|
$display("DEBUG i2c_slave; start condition detected at %t", $time);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
sta <= #1 1'b0;
|
||||||
|
|
||||||
|
always @(posedge scl)
|
||||||
|
d_sta <= #1 sta;
|
||||||
|
|
||||||
|
// detect stop condition
|
||||||
|
always @(posedge sda)
|
||||||
|
if(scl)
|
||||||
|
begin
|
||||||
|
sta <= #1 1'b0;
|
||||||
|
sto <= #1 1'b1;
|
||||||
|
|
||||||
|
if(debug)
|
||||||
|
$display("DEBUG i2c_slave; stop condition detected at %t", $time);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
sto <= #1 1'b0;
|
||||||
|
|
||||||
|
//generate i2c_reset signal
|
||||||
|
assign i2c_reset = sta || sto;
|
||||||
|
|
||||||
|
// generate statemachine
|
||||||
|
always @(negedge scl or posedge sto)
|
||||||
|
if (sto || (sta && !d_sta) )
|
||||||
|
begin
|
||||||
|
state <= #1 idle; // reset statemachine
|
||||||
|
|
||||||
|
sda_o <= #1 1'b1;
|
||||||
|
ld <= #1 1'b1;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
// initial settings
|
||||||
|
sda_o <= #1 1'b1;
|
||||||
|
ld <= #1 1'b0;
|
||||||
|
|
||||||
|
case(state) // synopsys full_case parallel_case
|
||||||
|
idle: // idle state
|
||||||
|
if (acc_done && my_adr)
|
||||||
|
begin
|
||||||
|
state <= #1 slave_ack;
|
||||||
|
rw <= #1 sr[0];
|
||||||
|
sda_o <= #1 1'b0; // generate i2c_ack
|
||||||
|
|
||||||
|
#2;
|
||||||
|
if(debug && rw)
|
||||||
|
$display("DEBUG i2c_slave; command byte received (read) at %t", $time);
|
||||||
|
if(debug && !rw)
|
||||||
|
$display("DEBUG i2c_slave; command byte received (write) at %t", $time);
|
||||||
|
|
||||||
|
if(rw)
|
||||||
|
begin
|
||||||
|
mem_do <= #1 mem[mem_adr];
|
||||||
|
|
||||||
|
if(debug)
|
||||||
|
begin
|
||||||
|
#2 $display("DEBUG i2c_slave; data block read %x from address %x (1)", mem_do, mem_adr);
|
||||||
|
#2 $display("DEBUG i2c_slave; memcheck [0]=%x, [1]=%x, [2]=%x", mem[4'h0], mem[4'h1], mem[4'h2]);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
slave_ack:
|
||||||
|
begin
|
||||||
|
if(rw)
|
||||||
|
begin
|
||||||
|
state <= #1 data;
|
||||||
|
sda_o <= #1 mem_do[7];
|
||||||
|
end
|
||||||
|
else
|
||||||
|
state <= #1 get_mem_adr;
|
||||||
|
|
||||||
|
ld <= #1 1'b1;
|
||||||
|
end
|
||||||
|
|
||||||
|
get_mem_adr: // wait for memory address
|
||||||
|
if(acc_done)
|
||||||
|
begin
|
||||||
|
state <= #1 gma_ack;
|
||||||
|
mem_adr <= #1 sr; // store memory address
|
||||||
|
sda_o <= #1 !(sr <= 15); // generate i2c_ack, for valid address
|
||||||
|
|
||||||
|
if(debug)
|
||||||
|
#1 $display("DEBUG i2c_slave; address received. adr=%x, ack=%b", sr, sda_o);
|
||||||
|
end
|
||||||
|
|
||||||
|
gma_ack:
|
||||||
|
begin
|
||||||
|
state <= #1 data;
|
||||||
|
ld <= #1 1'b1;
|
||||||
|
end
|
||||||
|
|
||||||
|
data: // receive or drive data
|
||||||
|
begin
|
||||||
|
if(rw)
|
||||||
|
sda_o <= #1 mem_do[7];
|
||||||
|
|
||||||
|
if(acc_done)
|
||||||
|
begin
|
||||||
|
state <= #1 data_ack;
|
||||||
|
mem_adr <= #2 mem_adr + 8'h1;
|
||||||
|
sda_o <= #1 (rw && (mem_adr <= 15) ); // send ack on write, receive ack on read
|
||||||
|
|
||||||
|
if(rw)
|
||||||
|
begin
|
||||||
|
#3 mem_do <= mem[mem_adr];
|
||||||
|
|
||||||
|
if(debug)
|
||||||
|
#5 $display("DEBUG i2c_slave; data block read %x from address %x (2)", mem_do, mem_adr);
|
||||||
|
end
|
||||||
|
|
||||||
|
if(!rw)
|
||||||
|
begin
|
||||||
|
mem[ mem_adr[3:0] ] <= #1 sr; // store data in memory
|
||||||
|
|
||||||
|
if(debug)
|
||||||
|
#2 $display("DEBUG i2c_slave; data block write %x to address %x", sr, mem_adr);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
data_ack:
|
||||||
|
begin
|
||||||
|
ld <= #1 1'b1;
|
||||||
|
|
||||||
|
if(rw)
|
||||||
|
if(sr[0]) // read operation && master send NACK
|
||||||
|
begin
|
||||||
|
state <= #1 idle;
|
||||||
|
sda_o <= #1 1'b1;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
state <= #1 data;
|
||||||
|
sda_o <= #1 mem_do[7];
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
state <= #1 data;
|
||||||
|
sda_o <= #1 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
// read data from memory
|
||||||
|
always @(posedge scl)
|
||||||
|
if(!acc_done && rw)
|
||||||
|
mem_do <= #1 {mem_do[6:0], 1'b1}; // insert 1'b1 for host ack generation
|
||||||
|
|
||||||
|
// generate tri-states
|
||||||
|
assign sda = sda_o ? 1'bz : 1'b0;
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Timing checks
|
||||||
|
//
|
||||||
|
|
||||||
|
wire tst_sto = sto;
|
||||||
|
wire tst_sta = sta;
|
||||||
|
|
||||||
|
specify
|
||||||
|
specparam normal_scl_low = 4700,
|
||||||
|
normal_scl_high = 4000,
|
||||||
|
normal_tsu_sta = 4700,
|
||||||
|
normal_thd_sta = 4000,
|
||||||
|
normal_tsu_sto = 4000,
|
||||||
|
normal_tbuf = 4700,
|
||||||
|
|
||||||
|
fast_scl_low = 1300,
|
||||||
|
fast_scl_high = 600,
|
||||||
|
fast_tsu_sta = 1300,
|
||||||
|
fast_thd_sta = 600,
|
||||||
|
fast_tsu_sto = 600,
|
||||||
|
fast_tbuf = 1300;
|
||||||
|
|
||||||
|
$width(negedge scl, normal_scl_low); // scl low time
|
||||||
|
$width(posedge scl, normal_scl_high); // scl high time
|
||||||
|
|
||||||
|
$setup(posedge scl, negedge sda &&& scl, normal_tsu_sta); // setup start
|
||||||
|
$setup(negedge sda &&& scl, negedge scl, normal_thd_sta); // hold start
|
||||||
|
$setup(posedge scl, posedge sda &&& scl, normal_tsu_sto); // setup stop
|
||||||
|
|
||||||
|
$setup(posedge tst_sta, posedge tst_sto, normal_tbuf); // stop to start time
|
||||||
|
endspecify
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
module spd_reader_tb;
|
||||||
|
reg clk, rst_n;
|
||||||
|
wire scl, sda;
|
||||||
|
|
||||||
|
// spd_reader DUT (
|
||||||
|
// .i_clk(clk),
|
||||||
|
// .i_rst_n(rst_n),
|
||||||
|
// .i2c_scl(scl),
|
||||||
|
// .i2c_sda(sda)
|
||||||
|
// );
|
||||||
|
spd_reader_top DUT (
|
||||||
|
// clock and reset
|
||||||
|
.sys_clk_p(clk),
|
||||||
|
.sys_clk_n(!clk),
|
||||||
|
.i_rst_n(rst_n),
|
||||||
|
// i2c interface
|
||||||
|
.i2c_scl(scl),
|
||||||
|
.i2c_sda(sda),
|
||||||
|
.i2c_lsb(),
|
||||||
|
// fan
|
||||||
|
.fan_pwm()
|
||||||
|
);
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
clk = 0;
|
||||||
|
rst_n = 0;
|
||||||
|
#100;
|
||||||
|
rst_n = 1;
|
||||||
|
wait(DUT.spd_reader_inst.find_i2c_address_done);
|
||||||
|
#10_000;
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
always #2.5 clk = !clk; // 200MHz
|
||||||
|
|
||||||
|
pullup pullup_scl(scl); // pullup scl line
|
||||||
|
pullup pullup_sda(sda); // pullup sda line
|
||||||
|
|
||||||
|
i2c_slave i2c_slave(
|
||||||
|
.scl(scl),
|
||||||
|
.sda(sda)
|
||||||
|
);
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue