Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 27 additions & 25 deletions rtl/i2c_master.sv
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
// Company: www.circuitden.com
// Engineer: Artin Isagholian
// artinisagholian@gmail.com
//
//
// Create Date: 01/20/2021 05:47:22 PM
// Design Name:
// Design Name:
// Module Name: i2c_master
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies: cycle_timer.sv
//
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
//////////////////////////////////////////////////////////////////////////////////
module i2c_master#(
parameter NUMBER_OF_DATA_BYTES = 1,
Expand Down Expand Up @@ -199,7 +199,6 @@ always_comb 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}; // write
Expand All @@ -210,7 +209,6 @@ always_comb begin
if (enable) begin
_state = S_START;
_post_state = S_WRITE_ADDR_W;
_busy = 1;
end
end
S_START: begin
Expand All @@ -224,14 +222,16 @@ always_comb begin
_process_counter = 2;
end
2: begin
_serial_clock = 0;
_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];
_serial_clock = 0;
_process_counter = 0;
_state = post_state;
_serial_data = saved_device_address[ADDRESS_WIDTH];
_saved_device_address = {saved_device_address[ADDRESS_WIDTH-1:0], saved_device_address[ADDRESS_WIDTH]};
end
endcase
end
Expand Down Expand Up @@ -275,8 +275,8 @@ always_comb begin
_byte_counter = NUMBER_OF_REGISTER_BYTES - 1;
end
else begin
_serial_data = saved_device_address[ADDRESS_WIDTH-1];
_saved_device_address = {saved_device_address[ADDRESS_WIDTH-2:0], saved_device_address[ADDRESS_WIDTH-1]};
_serial_data = saved_device_address[ADDRESS_WIDTH];
_saved_device_address = {saved_device_address[ADDRESS_WIDTH-1:0], saved_device_address[ADDRESS_WIDTH]};
end
_process_counter = 0;
end
Expand Down Expand Up @@ -364,7 +364,7 @@ always_comb begin
_state = S_CHECK_ACK;

if (byte_counter == 0) begin
if (read_write == 0) begin
if (saved_read_write == 0) begin
_post_state = S_WRITE_REG_DATA;
_post_serial_data = saved_mosi_data[DATA_WIDTH-1];
_saved_mosi_data = {saved_mosi_data[DATA_WIDTH-2:0], saved_mosi_data[DATA_WIDTH-1]};
Expand All @@ -382,7 +382,7 @@ always_comb begin
end
else begin
_serial_data = saved_register_address[REGISTER_WIDTH-1];
_saved_register_address = {saved_register_address[REGISTER_WIDTH-2:0], saved_register_address[REGISTER_WIDTH-1]};
_saved_register_address = {saved_register_address[REGISTER_WIDTH-2:0], saved_register_address[REGISTER_WIDTH-1]};
end
_process_counter = 0;
end
Expand Down Expand Up @@ -462,7 +462,7 @@ always_comb begin
3: begin
_state = S_START;
_post_state = S_WRITE_ADDR_R;
_saved_device_address = {device_address,1'b1}; // read
_saved_device_address = {saved_device_address[ADDRESS_WIDTH:1],1'b1}; // read
_process_counter = 0;
end
endcase
Expand All @@ -472,7 +472,7 @@ always_comb begin
if (process_counter == 3 && bit_counter == 0) begin
serial_data_output_enable = 0;
end

if (divider_tick) begin
case (process_counter)
0: begin
Expand Down Expand Up @@ -506,16 +506,16 @@ always_comb begin
_byte_counter = NUMBER_OF_DATA_BYTES - 1;
end
else begin
_serial_data = saved_device_address[ADDRESS_WIDTH-1];
_saved_device_address = {saved_device_address[ADDRESS_WIDTH-2:0], saved_device_address[ADDRESS_WIDTH-1]};
_serial_data = saved_device_address[ADDRESS_WIDTH];
_saved_device_address = {saved_device_address[ADDRESS_WIDTH-1:0], saved_device_address[ADDRESS_WIDTH]};
end
_process_counter = 0;
end
endcase
end
end
S_READ_REG: begin
if (process_counter != 3) begin
if (process_counter != 3 || bit_counter != 0) begin
serial_data_output_enable = 0;
end

Expand Down Expand Up @@ -662,6 +662,8 @@ always_comb begin
end
end
endcase

_busy = (_state != S_IDLE);
end

always_ff @(posedge clock) begin
Expand Down Expand Up @@ -702,5 +704,5 @@ always_ff @(posedge clock) begin
byte_counter <= _byte_counter;
end
end

endmodule
12 changes: 6 additions & 6 deletions test/case_000/case_000.svh
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@
`ifndef _case_000_svh_
`define _case_000_svh_

task case_000();
task case_000(logic [6:0] device_addr);
$display("Running case 000");
$display(" Writing value 8'hAC to address 0");
$display("Configuring master");
@(posedge testbench.clock);
testbench.rw = 0; //write operation
testbench.reg_addr = 8'h00; //writing to slave register 0
testbench.data_to_write = 8'hAC;
testbench.device_addr = 7'b001_0001; //slave address
testbench.device_addr = device_addr; //slave address
testbench.divider = 16'hFFFF; //divider value for i2c serial clock
@(posedge testbench.clock);
$display("Enabling master");
Expand All @@ -38,26 +38,26 @@ task case_000();
$display("Master has started writing");
testbench.enable = 0;
@(negedge testbench.i2c_master_busy);
$display("Master has finsihed writing");
$display("Master has finished writing");

$display("Reading from address 0");
$display("Configuring master");
@(posedge testbench.clock);
testbench.rw = 1; //read operation
testbench.reg_addr = '0; //reading from slave register 0
testbench.data_to_write = '0;
testbench.device_addr = 7'b001_0001; //slave address
testbench.device_addr = device_addr; //slave address
@(posedge testbench.clock);
$display("Enabling master");
testbench.enable = 1;
@(posedge testbench.i2c_master_busy)
$display("Master has started reading");
testbench.enable = 0;
@(negedge testbench.i2c_master_busy);
$display("Master has finsihed reading");
$display("Master has finished reading");
assert (testbench.i2c_master_miso_data == 8'hAC) $display ("Read correct data from address 0");
else $error("Read back incorrect data from address 0. Expected %h but got %h", 8'hAC, testbench.i2c_master_miso_data);

endtask: case_000

`endif
`endif
12 changes: 6 additions & 6 deletions test/case_001/case_001.svh
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@
`ifndef _case_001_svh_
`define _case_001_svh_

task case_001();
task case_001(logic [6:0] device_addr);
$display("Running case 001");
$display(" Writing value 8'hDC to address 1");
$display("Configuring master");
@(posedge testbench.clock);
testbench.rw = 0; //write operation
testbench.reg_addr = 8'h01; //writing to slave register 1
testbench.data_to_write = 8'hDC;
testbench.device_addr = 7'b001_0001; //slave address
testbench.device_addr = device_addr; //slave address
testbench.divider = 16'hFFFF; //divider value for i2c serial clock
@(posedge testbench.clock);
$display("Enabling master");
Expand All @@ -38,26 +38,26 @@ task case_001();
$display("Master has started writing");
testbench.enable = 0;
@(negedge testbench.i2c_master_busy);
$display("Master has finsihed writing");
$display("Master has finished writing");

$display("Reading from address 0");
$display("Configuring master");
@(posedge testbench.clock);
testbench.rw = 1; //read operation
testbench.reg_addr = 8'h01; //reading from slave register 1
testbench.data_to_write = '0;
testbench.device_addr = 7'b001_0001; //slave address
testbench.device_addr = device_addr; //slave address
@(posedge testbench.clock);
$display("Enabling master");
testbench.enable = 1;
@(posedge testbench.i2c_master_busy)
$display("Master has started reading");
testbench.enable = 0;
@(negedge testbench.i2c_master_busy);
$display("Master has finsihed reading");
$display("Master has finished reading");
assert (testbench.i2c_master_miso_data == 8'hDC) $display ("Read correct data from address 1");
else $error("Read back incorrect data from address 1. Expected %h but got %h", 8'hDC, testbench.i2c_master_miso_data);

endtask: case_001

`endif
`endif
18 changes: 9 additions & 9 deletions test/i2c_slave.v
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ module i2c_slave (scl, sda);
sto <= #1 1'b0;

if(debug)
$display("DEBUG i2c_slave; start condition detected at %t", $time);
$display("DEBUG i2c_slave %x; start condition detected at %t", I2C_ADR, $time);
end
else
sta <= #1 1'b0;
Expand All @@ -186,7 +186,7 @@ module i2c_slave (scl, sda);
sto <= #1 1'b1;

if(debug)
$display("DEBUG i2c_slave; stop condition detected at %t", $time);
$display("DEBUG i2c_slave %x; stop condition detected at %t", I2C_ADR, $time);
end
else
sto <= #1 1'b0;
Expand Down Expand Up @@ -219,18 +219,18 @@ module i2c_slave (scl, sda);

#2;
if(debug && rw)
$display("DEBUG i2c_slave; command byte received (read) at %t", $time);
$display("DEBUG i2c_slave %x; command byte received (read) at %t", I2C_ADR, $time);
if(debug && !rw)
$display("DEBUG i2c_slave; command byte received (write) at %t", $time);
$display("DEBUG i2c_slave %x; command byte received (write) at %t", I2C_ADR, $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]);
#2 $display("DEBUG i2c_slave %x; data block read %x from address %x (1)", I2C_ADR, mem_do, mem_adr);
#2 $display("DEBUG i2c_slave %x; memcheck [0]=%x, [1]=%x, [2]=%x", I2C_ADR, mem[4'h0], mem[4'h1], mem[4'h2]);
end
end
end
Expand All @@ -256,7 +256,7 @@ module i2c_slave (scl, sda);
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);
#1 $display("DEBUG i2c_slave %x; address received. adr=%x, ack=%b", I2C_ADR, sr, sda_o);
end

gma_ack:
Expand All @@ -281,15 +281,15 @@ module i2c_slave (scl, sda);
#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);
#5 $display("DEBUG i2c_slave %x; data block read %x from address %x (2)", I2C_ADR, 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);
#2 $display("DEBUG i2c_slave %x; data block write %x to address %x", I2C_ADR, sr, mem_adr);
end
end
end
Expand Down
44 changes: 28 additions & 16 deletions test/testbench.sv
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
// Company: www.circuitden.com
// Engineer: Artin Isagholian
// artinisagholian@gmail.com
//
// Create Date: 15:43:35 10/22/2020
// Design Name:
//
// Create Date: 15:43:35 10/22/2020
// Design Name:
// Module Name: testbench
// Project Name:
// Target Devices:
// Tool versions:
// Description:
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
// Dependencies:
//
// Revision:
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
`include "./case_000/case_000.svh"
Expand Down Expand Up @@ -88,7 +88,17 @@ pullup pullup_scl(scl); // pullup scl line
pullup pullup_sda(sda); // pullup sda line


i2c_slave i2c_slave(
i2c_slave #(
.I2C_ADR(7'h11)
) i2c_slave_11 (
.scl (scl),
.sda (sda)
);


i2c_slave #(
.I2C_ADR(7'h41)
) i2c_slave_41 (
.scl (scl),
.sda (sda)
);
Expand All @@ -97,7 +107,7 @@ i2c_slave i2c_slave(
//clock generation
initial begin
clock = 0;

forever begin
#(CLOCK_PERIOD/2);
clock = ~clock;
Expand All @@ -114,9 +124,11 @@ initial begin
reset_n = 1;
@(posedge clock)

case_000();
case_001();
$display("Tests have finsihed");
case_000(7'h11);
case_001(7'h11);
case_000(7'h41);
case_001(7'h41);
$display("Tests have finished");
$stop();
end

Expand All @@ -130,4 +142,4 @@ assign i2c_master_device_address = device_addr;
assign i2c_master_register_address = reg_addr;
assign i2c_master_divider = divider;

endmodule
endmodule