Initial commit

This commit is contained in:
Nikolay Puzanov 2022-06-14 12:50:47 +03:00
commit cb2d44f4b6
9 changed files with 433 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.vvp
*.fst

89
i2c_io_output.sv Normal file
View 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

View File

@ -0,0 +1 @@
((verilog-mode . ((flycheck-verilator-include-path . ("../")))))

View 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

View 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
View 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

View File

@ -0,0 +1 @@
((verilog-mode . ((flycheck-verilator-include-path . ("../")))))

7
i2c_receiver_tb/Makefile Normal file
View 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

View 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