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