Initial commit
This commit is contained in:
commit
cb2d44f4b6
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.vvp
|
||||||
|
*.fst
|
||||||
89
i2c_io_output.sv
Normal file
89
i2c_io_output.sv
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
`timescale 1ps/1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module i2c_io_output #(parameter [6:0] I2C_ADDR = 7'h5a,
|
||||||
|
parameter IO_BYTES_COUNT = 2,
|
||||||
|
localparam DATA_WIDTH = IO_BYTES_COUNT * 8)
|
||||||
|
(input wire clock,
|
||||||
|
input wire reset,
|
||||||
|
|
||||||
|
input wire i_scl,
|
||||||
|
input wire i_sdi,
|
||||||
|
output wire o_sdo,
|
||||||
|
|
||||||
|
output reg [DATA_WIDTH-1:0] o_data);
|
||||||
|
|
||||||
|
/* -------- I2C Receiver -------- */
|
||||||
|
logic [7:0] i2c_data;
|
||||||
|
logic i2c_strobe;
|
||||||
|
logic i2c_start;
|
||||||
|
logic i2c_stop;
|
||||||
|
logic i2c_ack;
|
||||||
|
|
||||||
|
i2c_receiver i2c_receiver_impl
|
||||||
|
(.clock(clock), .reset(reset),
|
||||||
|
.i_scl(i_scl), .i_sdi(i_sdi), .o_sdo(o_sdo),
|
||||||
|
.o_data(i2c_data),
|
||||||
|
.o_strobe(i2c_strobe),
|
||||||
|
.o_start(i2c_start),
|
||||||
|
.o_stop(i2c_stop),
|
||||||
|
.i_ack(i2c_ack));
|
||||||
|
|
||||||
|
/* -------- FSM -------- */
|
||||||
|
enum int unsigned {
|
||||||
|
ST_IDLE = 0,
|
||||||
|
ST_RECEIVE_ADDR,
|
||||||
|
ST_RECEIVE_BYTE,
|
||||||
|
ST_COMPLETE
|
||||||
|
} state;
|
||||||
|
|
||||||
|
localparam BYTE_CNTR_W = $clog2(IO_BYTES_COUNT);
|
||||||
|
|
||||||
|
logic [BYTE_CNTR_W-1:0] byte_cntr;
|
||||||
|
logic [DATA_WIDTH-1:0] data;
|
||||||
|
|
||||||
|
always_ff @(posedge clock)
|
||||||
|
if (reset) begin
|
||||||
|
state <= ST_IDLE;
|
||||||
|
byte_cntr <= '0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
if (i2c_stop)
|
||||||
|
state <= ST_IDLE;
|
||||||
|
case (state)
|
||||||
|
ST_IDLE: begin
|
||||||
|
if (i2c_start)
|
||||||
|
state <= ST_RECEIVE_ADDR;
|
||||||
|
end
|
||||||
|
|
||||||
|
ST_RECEIVE_ADDR: begin
|
||||||
|
if (i2c_strobe)
|
||||||
|
if (i2c_data == {I2C_ADDR, 1'b0}) begin
|
||||||
|
i2c_ack <= 1'b1;
|
||||||
|
byte_cntr <= '0;
|
||||||
|
state <= ST_RECEIVE_BYTE;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
i2c_ack <= 1'b0;
|
||||||
|
state <= ST_IDLE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ST_RECEIVE_BYTE: begin
|
||||||
|
if (i2c_strobe) begin
|
||||||
|
data <= {data[DATA_WIDTH-8-1:0], i2c_data}; // MSB first
|
||||||
|
byte_cntr <= byte_cntr + 1'b1;
|
||||||
|
|
||||||
|
if (byte_cntr == BYTE_CNTR_W'(IO_BYTES_COUNT-1))
|
||||||
|
state <= ST_COMPLETE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ST_COMPLETE: begin
|
||||||
|
o_data <= data;
|
||||||
|
state <= ST_IDLE;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule // i2c_io_output
|
||||||
1
i2c_io_output_tb/.dir-locals.el
Normal file
1
i2c_io_output_tb/.dir-locals.el
Normal file
@ -0,0 +1 @@
|
|||||||
|
((verilog-mode . ((flycheck-verilator-include-path . ("../")))))
|
||||||
7
i2c_io_output_tb/Makefile
Normal file
7
i2c_io_output_tb/Makefile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
SOURCES = ../i2c_io_output.sv ../i2c_receiver.sv ./i2c_io_output_tb.sv
|
||||||
|
|
||||||
|
all: run
|
||||||
|
|
||||||
|
run: $(SOURCES)
|
||||||
|
iverilog -g2012 -DDUMP -o out.vvp $(SOURCES)
|
||||||
|
vvp ./out.vvp -fst
|
||||||
111
i2c_io_output_tb/i2c_io_output_tb.sv
Normal file
111
i2c_io_output_tb/i2c_io_output_tb.sv
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
`timescale 1ps/1ps
|
||||||
|
|
||||||
|
/* verilator lint_off DECLFILENAME */
|
||||||
|
/* verilator lint_off MULTITOP */
|
||||||
|
/* verilator lint_off STMTDLY */
|
||||||
|
/* verilator lint_off INFINITELOOP */
|
||||||
|
/* verilator lint_off INITIALDLY */
|
||||||
|
|
||||||
|
module i2c_io_output_tb;
|
||||||
|
logic clock = 1'b0;
|
||||||
|
logic reset = 1'b1;
|
||||||
|
|
||||||
|
/* Master clock 100MHz (10ns period) */
|
||||||
|
always #(10ns/2) clock = ~clock;
|
||||||
|
|
||||||
|
parameter SCL_PERIOD = 364793; // ~300ns
|
||||||
|
logic scl_async;
|
||||||
|
logic sda_async;
|
||||||
|
|
||||||
|
event scl_low_ev;
|
||||||
|
event scl_high_ev;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
scl_async = 1'b1;
|
||||||
|
|
||||||
|
forever begin
|
||||||
|
#(SCL_PERIOD/4) scl_async = 1'b1;
|
||||||
|
#(SCL_PERIOD/4) -> scl_high_ev;
|
||||||
|
#(SCL_PERIOD/4) scl_async = 1'b0;
|
||||||
|
#(SCL_PERIOD/4) -> scl_low_ev;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
parameter IO_BYTES_COUNT = 2;
|
||||||
|
parameter [6:0] I2C_ADDRESS = 7'h5a;
|
||||||
|
localparam DATA_WIDTH = IO_BYTES_COUNT * 8;
|
||||||
|
|
||||||
|
logic i_scl;
|
||||||
|
logic i_sdi;
|
||||||
|
logic o_sdo;
|
||||||
|
logic [DATA_WIDTH-1:0] o_data;
|
||||||
|
|
||||||
|
i2c_io_output #(.I2C_ADDR(I2C_ADDRESS),
|
||||||
|
.IO_BYTES_COUNT(IO_BYTES_COUNT))
|
||||||
|
DUT (.*);
|
||||||
|
|
||||||
|
logic [1:0] scl_sync;
|
||||||
|
logic [1:0] sda_sync;
|
||||||
|
|
||||||
|
always_ff @(posedge clock)
|
||||||
|
if (reset) begin
|
||||||
|
scl_sync <= '1;
|
||||||
|
sda_sync <= '1;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
scl_sync <= {scl_sync[0], scl_async};
|
||||||
|
sda_sync <= {sda_sync[0], sda_async};
|
||||||
|
end
|
||||||
|
|
||||||
|
assign i_scl = scl_sync[1];
|
||||||
|
assign i_sdi = sda_sync[1];
|
||||||
|
|
||||||
|
task send_start;
|
||||||
|
wait(scl_async == 1'b0);
|
||||||
|
sda_async = 1'b1;
|
||||||
|
@(scl_high_ev);
|
||||||
|
sda_async = 1'b0;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task send_stop;
|
||||||
|
wait(scl_async == 1'b0);
|
||||||
|
sda_async = 1'b0;
|
||||||
|
@(scl_high_ev);
|
||||||
|
sda_async = 1'b1;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task send_byte(logic [7:0] b);
|
||||||
|
for (int n = 7; n >= 0; n -= 1) begin
|
||||||
|
@(scl_low_ev);
|
||||||
|
sda_async = b[n];
|
||||||
|
end
|
||||||
|
|
||||||
|
@(scl_low_ev);
|
||||||
|
wait(scl_async == 1'b1);
|
||||||
|
endtask
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
reset = 1'b1;
|
||||||
|
repeat(2) @(posedge clock) #1;
|
||||||
|
reset = 1'b0;
|
||||||
|
repeat(2) @(posedge clock) #1;
|
||||||
|
|
||||||
|
send_start;
|
||||||
|
send_byte(8'hb4);
|
||||||
|
send_byte(8'ha5);
|
||||||
|
send_byte(8'h38);
|
||||||
|
send_byte(8'h42);
|
||||||
|
send_stop;
|
||||||
|
|
||||||
|
repeat(1000) @(posedge clock) #1;
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifdef DUMP
|
||||||
|
initial begin
|
||||||
|
$dumpfile("i2c_io_output_tb.fst");
|
||||||
|
$dumpvars(0, i2c_io_output_tb);
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
|
||||||
|
endmodule // i2c_io_output_tb
|
||||||
106
i2c_receiver.sv
Normal file
106
i2c_receiver.sv
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
`timescale 1ps/1ps
|
||||||
|
`default_nettype none
|
||||||
|
|
||||||
|
module i2c_receiver
|
||||||
|
(input wire clock,
|
||||||
|
input wire reset,
|
||||||
|
|
||||||
|
input wire i_scl,
|
||||||
|
input wire i_sdi,
|
||||||
|
output wire o_sdo,
|
||||||
|
|
||||||
|
output wire [7:0] o_data,
|
||||||
|
output wire o_strobe,
|
||||||
|
output wire o_start,
|
||||||
|
output wire o_stop,
|
||||||
|
input wire i_ack); // Active ONE
|
||||||
|
|
||||||
|
logic sdi_prev;
|
||||||
|
logic scl_prev;
|
||||||
|
logic start_condition;
|
||||||
|
logic stop_condition;
|
||||||
|
logic scl_rise;
|
||||||
|
logic scl_fall;
|
||||||
|
|
||||||
|
always_ff @(posedge clock)
|
||||||
|
if (reset) begin
|
||||||
|
sdi_prev <= 1'b1;
|
||||||
|
scl_prev <= 1'b1;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
sdi_prev <= i_sdi;
|
||||||
|
scl_prev <= i_scl;
|
||||||
|
end
|
||||||
|
|
||||||
|
assign start_condition = (sdi_prev == 1'b1) && (i_sdi == 1'b0) && (scl_prev == 1'b1) && (i_scl == 1'b1);
|
||||||
|
assign stop_condition = (sdi_prev == 1'b0) && (i_sdi == 1'b1) && (scl_prev == 1'b1) && (i_scl == 1'b1);
|
||||||
|
assign scl_rise = (scl_prev == 1'b0) && (i_scl == 1'b1);
|
||||||
|
assign scl_fall = (scl_prev == 1'b1) && (i_scl == 1'b0);
|
||||||
|
|
||||||
|
enum int unsigned {
|
||||||
|
ST_WAIT_FOR_START = 0,
|
||||||
|
ST_RECEIVE_BITS,
|
||||||
|
ST_WAIT_FOR_SCL_LOW,
|
||||||
|
ST_SEND_ACK
|
||||||
|
} state;
|
||||||
|
|
||||||
|
logic [2:0] bit_cntr;
|
||||||
|
logic [7:0] data_sr;
|
||||||
|
logic byte_strobe;
|
||||||
|
logic sdo;
|
||||||
|
|
||||||
|
always_ff @(posedge clock)
|
||||||
|
if (reset) begin
|
||||||
|
state <= ST_WAIT_FOR_START;
|
||||||
|
bit_cntr <= '0;
|
||||||
|
byte_strobe <= 1'b0;
|
||||||
|
sdo <= 1'b1;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
byte_strobe <= 1'b0;
|
||||||
|
|
||||||
|
if (stop_condition)
|
||||||
|
state <= ST_WAIT_FOR_START;
|
||||||
|
else
|
||||||
|
case (state)
|
||||||
|
ST_WAIT_FOR_START: begin
|
||||||
|
sdo <= 1'b1;
|
||||||
|
|
||||||
|
if (start_condition) begin
|
||||||
|
bit_cntr <= '0;
|
||||||
|
state <= ST_RECEIVE_BITS;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ST_RECEIVE_BITS:
|
||||||
|
if (scl_rise) begin
|
||||||
|
data_sr <= {data_sr[6:0], i_sdi};
|
||||||
|
bit_cntr <= bit_cntr + 1'b1;
|
||||||
|
|
||||||
|
if (bit_cntr == 3'd7) begin
|
||||||
|
state <= ST_WAIT_FOR_SCL_LOW;
|
||||||
|
byte_strobe <= 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ST_WAIT_FOR_SCL_LOW:
|
||||||
|
if (scl_fall) begin
|
||||||
|
sdo <= i_ack ? 1'b0 : 1'b1;
|
||||||
|
state <= ST_SEND_ACK;
|
||||||
|
end
|
||||||
|
|
||||||
|
ST_SEND_ACK:
|
||||||
|
if (scl_fall) begin
|
||||||
|
sdo <= 1'b1;
|
||||||
|
state <= ST_RECEIVE_BITS;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
assign o_data = data_sr;
|
||||||
|
assign o_strobe = byte_strobe;
|
||||||
|
assign o_start = start_condition;
|
||||||
|
assign o_stop = stop_condition;
|
||||||
|
assign o_sdo = sdo;
|
||||||
|
|
||||||
|
endmodule // i2c_receiver
|
||||||
1
i2c_receiver_tb/.dir-locals.el
Normal file
1
i2c_receiver_tb/.dir-locals.el
Normal file
@ -0,0 +1 @@
|
|||||||
|
((verilog-mode . ((flycheck-verilator-include-path . ("../")))))
|
||||||
7
i2c_receiver_tb/Makefile
Normal file
7
i2c_receiver_tb/Makefile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
SOURCES = ../i2c_receiver.sv ./i2c_receiver_tb.sv
|
||||||
|
|
||||||
|
all: run
|
||||||
|
|
||||||
|
run: $(SOURCES)
|
||||||
|
iverilog -g2012 -DDUMP -o out.vvp $(SOURCES)
|
||||||
|
vvp ./out.vvp -fst
|
||||||
109
i2c_receiver_tb/i2c_receiver_tb.sv
Normal file
109
i2c_receiver_tb/i2c_receiver_tb.sv
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
`timescale 1ps/1ps
|
||||||
|
|
||||||
|
/* verilator lint_off DECLFILENAME */
|
||||||
|
/* verilator lint_off MULTITOP */
|
||||||
|
/* verilator lint_off STMTDLY */
|
||||||
|
/* verilator lint_off INFINITELOOP */
|
||||||
|
/* verilator lint_off INITIALDLY */
|
||||||
|
|
||||||
|
module i2c_receiver_tb;
|
||||||
|
logic clock = 1'b0;
|
||||||
|
logic reset = 1'b1;
|
||||||
|
|
||||||
|
/* Master clock 100MHz (10ns period) */
|
||||||
|
always #(10ns/2) clock = ~clock;
|
||||||
|
|
||||||
|
parameter SCL_PERIOD = 364793; // ~300ns
|
||||||
|
logic scl_async;
|
||||||
|
logic sda_async;
|
||||||
|
|
||||||
|
event scl_low_ev;
|
||||||
|
event scl_high_ev;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
scl_async = 1'b1;
|
||||||
|
|
||||||
|
forever begin
|
||||||
|
#(SCL_PERIOD/4) scl_async = 1'b1;
|
||||||
|
#(SCL_PERIOD/4) -> scl_high_ev;
|
||||||
|
#(SCL_PERIOD/4) scl_async = 1'b0;
|
||||||
|
#(SCL_PERIOD/4) -> scl_low_ev;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
logic i_scl;
|
||||||
|
logic i_sdi;
|
||||||
|
logic o_sdo;
|
||||||
|
logic [7:0] o_data;
|
||||||
|
logic o_strobe;
|
||||||
|
logic o_start;
|
||||||
|
logic o_stop;
|
||||||
|
logic i_ack;
|
||||||
|
|
||||||
|
i2c_receiver DUT (.*);
|
||||||
|
|
||||||
|
logic [1:0] scl_sync;
|
||||||
|
logic [1:0] sda_sync;
|
||||||
|
|
||||||
|
always_ff @(posedge clock)
|
||||||
|
if (reset) begin
|
||||||
|
scl_sync <= '1;
|
||||||
|
sda_sync <= '1;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
scl_sync <= {scl_sync[0], scl_async};
|
||||||
|
sda_sync <= {sda_sync[0], sda_async};
|
||||||
|
end
|
||||||
|
|
||||||
|
assign i_scl = scl_sync[1];
|
||||||
|
assign i_sdi = sda_sync[1];
|
||||||
|
|
||||||
|
task send_start;
|
||||||
|
wait(scl_async == 1'b0);
|
||||||
|
sda_async = 1'b1;
|
||||||
|
@(scl_high_ev);
|
||||||
|
sda_async = 1'b0;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task send_stop;
|
||||||
|
wait(scl_async == 1'b0);
|
||||||
|
sda_async = 1'b0;
|
||||||
|
@(scl_high_ev);
|
||||||
|
sda_async = 1'b1;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
task send_byte(logic [7:0] b);
|
||||||
|
for (int n = 7; n >= 0; n -= 1) begin
|
||||||
|
@(scl_low_ev);
|
||||||
|
sda_async = b[n];
|
||||||
|
end
|
||||||
|
|
||||||
|
@(scl_low_ev);
|
||||||
|
wait(scl_async == 1'b1);
|
||||||
|
endtask
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
reset = 1'b1;
|
||||||
|
repeat(2) @(posedge clock) #1;
|
||||||
|
reset = 1'b0;
|
||||||
|
repeat(2) @(posedge clock) #1;
|
||||||
|
|
||||||
|
i_ack = 1'b1;
|
||||||
|
|
||||||
|
send_start;
|
||||||
|
send_byte(8'h6a);
|
||||||
|
send_byte(8'ha5);
|
||||||
|
send_stop;
|
||||||
|
|
||||||
|
repeat(1000) @(posedge clock) #1;
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifdef DUMP
|
||||||
|
initial begin
|
||||||
|
$dumpfile("i2c_receiver_tb.fst");
|
||||||
|
$dumpvars(0, i2c_receiver_tb);
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
|
||||||
|
endmodule // i2c_receiver_tb
|
||||||
Loading…
x
Reference in New Issue
Block a user