For now, I have only implemented a simple I2C protocol where only the master transmits the data. Also there is ack_bit
for the I2C Address only. For state 5, i.e., data_byte
its always acknowledged.
I am just starting with Verilog, and this is my first big project so, please review my code and suggest any changes.
main
source code:
module main(input reset); wire scl,clk; wire sda_line; wire sda_master_enable; master main_master(.reset(reset),.sda_line(sda_line),.scl(scl),.sda_master_enable(sda_master_enable)); slave main_slave(.sda_line(sda_line),.scl(scl),.sda_master_enable(sda_master_enable)); endmodule //////////////////////////////////////////////////////////////////////////////////// module master(input reset, inout sda_line, output reg scl,[2:0]state_out,wire sda_master_enable); wire clk,reset; reg sda,sda_enable = 1; reg [2:0]state = 3'b000; reg [2:0] state_out; reg sda_master_enable; reg [6:0]addr_reg = 7'h69; //69 = 1101001 reg [2:0]count = 3'd6; reg rw_reg = 0; //FOR NOW transmitting data from master; reg [7:0] data_reg = 8'b10001010; reg data_out; reg addr_ack_bit = 1; reg data_ack_bit; reg ack_flag = 0; reg [2:0]data_count = 3'd7; parameter IDLE_STATE = 3'b000, START_STATE = 3'b001, ADDR_STATE = 3'b010, RW_STATE = 3'b011, ADDR_ACK_STATE = 3'b100, DATA_STATE = 3'b101, DATA_ACK_STATE = 3'b110, STOP_STATE = 3'b111; clock_divider master_cd(.i2c_clk(clk)); assign sda_line = (sda_enable) ? sda : 1'bz; // Master drives SDA only when sda_enable = 1 always @(posedge clk) begin if(reset == 0) begin case(state) IDLE_STATE: begin sda<=1;scl<=1; state_out <= IDLE_STATE; state<=START_STATE; end START_STATE: begin sda<=0; state_out <= START_STATE; state<=ADDR_STATE; end ADDR_STATE: begin sda_enable = 1; if(count == 0) begin sda<=addr_reg[count]; state_out <= ADDR_STATE; state<=RW_STATE; count<=3'd6; end else begin sda<=addr_reg[count]; count = count-1; // DATA will work according to sysclk; // you have to configure scl accordingly to match i2c rule; end end RW_STATE: begin sda<=rw_reg; state_out <= RW_STATE; state<=ADDR_ACK_STATE; end ADDR_ACK_STATE: begin sda_enable = 0; state_out <= ADDR_ACK_STATE; end DATA_STATE: begin sda_enable = 1; if(data_count == 0) begin sda<=data_reg[data_count]; state_out <= DATA_STATE; state<=DATA_ACK_STATE; end else begin sda<=data_reg[data_count]; data_count = data_count-1; // DATA will work according to sysclk; // you have to configure scl accordingly to match i2c rule; end end DATA_ACK_STATE: begin sda_enable = 0; data_ack_bit = sda_line; state_out <= DATA_STATE; state <= (data_ack_bit) ? DATA_STATE : STOP_STATE; end STOP_STATE: begin sda_enable <= 1; sda<= 1; scl<=1; state_out <= STOP_STATE; end default: begin sda<=1;scl<=1; end endcase end else if(reset == 1) begin sda<=1; scl <= 1; end state_out <= state; end always @(posedge scl) begin if(state_out == ADDR_ACK_STATE) begin sda = sda_line; // Capture it properly addr_ack_bit = sda; $display("ACK Bit Read: %b", sda); if(addr_ack_bit == 1) begin state <= ADDR_STATE; // Retry address phase if no ACK end else if(addr_ack_bit == 0) begin state <= DATA_STATE; // Proceed to data transmission end end end always @(clk) begin if(state == ADDR_STATE || state == RW_STATE || state == ADDR_ACK_STATE || state == DATA_STATE ||state == DATA_ACK_STATE) begin //Starting of scl after completing starting state; scl <= ~clk; end if(state_out == DATA_ACK_STATE) begin scl <= 1; end end always @(sda_enable) begin sda_master_enable = sda_enable; end endmodule /////////////////////////////////////////////////////////////// module slave(input scl,sda_master_enable,inout sda_line,output addr_data_out,addr_count_out,flag); reg sda,sda_enable = 0; // Controls when slave drives SDA wire clk; reg flag_reg = 1'bz; wire flag; reg [7:0] addr_data = 8'b00000000; reg [7:0] addr_data_out; reg [3:0] addr_count = 4'b1010; //here from 9 to 0 10 states // we require 8 bits (7+1) //+1 bit for starting posedge of scl from Hi-im state; reg [3:0] addr_count_out; reg addr_flag = 0; parameter slave_addr = 8'hD1; assign sda_line = sda_enable ? sda : 1'bz; // To drive the inout net clock_divider master_cd(.i2c_clk(clk)); always @(negedge clk) begin if(flag_reg == 1) begin if(addr_flag == 0) begin if(addr_count <= 9 && addr_count >= 2) begin sda = sda_line; //no non-blocking for sda because we want it right now and here, nd not on next clock cycle addr_data = addr_data | (sda << addr_count-2) ; //same case with addr_data addr_data_out[7:0] <= addr_data[7:0]; addr_count <= addr_count -1; //Some fucked up shit combining bloacking and non blocking is not advisable but for now its giving me correct result so gud!! addr_count_out <= addr_count -1; end else begin addr_count <= addr_count -1; addr_count_out <= addr_count -1; //earlier it was addr_count_out <= addr_count ,,,,which was two step process first addt_count updates in one cycle and then addr_count_out; end end end end always @(negedge sda_line) begin #1; if(scl == 1) flag_reg <= 1; //Starting condition detected end always @(posedge sda_line) begin #1; if(scl == 1) flag_reg <= 0; //stopping condition detected; end always @(negedge sda_master_enable) begin if(addr_flag == 0) begin if(sda_master_enable == 0) begin sda_enable = 1; if (addr_data == slave_addr) begin sda = 1'b0; addr_data <= 8'b00000000; addr_data_out[7:0] <= 8'b00000000; addr_count <= 4'b1010; addr_count_out <= 4'b10; addr_flag<=1; end else begin sda = 1'b1; //NACK addr_data <= 8'b00000000; addr_data_out[7:0] <= 8'b00000000; addr_count <= 4'b1010; addr_count_out <= 4'b1010; addr_flag<=0; //If NACK then resend data from master and re store it in slave end end end else if(addr_flag == 1) begin sda_enable = 1; sda<=1'b0; end end always @(posedge sda_master_enable) begin if(sda_master_enable == 1) begin sda_enable = 0; end end assign flag = flag_reg; //Do notconfuse non blocking /blocking sign to assign sign here "=" simply means they are equal so when ever the reg changes the wire changes; //Above is the good choice to see reg output in testbench waveform since testbench only takes wires:) endmodule //////////////////////////////////////////////////////////// module clock_signal ( output reg val ); initial begin val = 0; forever #5 val = ~val; // 10 nanosecond main clock ;Frequency = 100MHz gud!! // BUT For standard i2c speed we want 100KHz or 10us clock end endmodule ///////////////////////////////////////////////////////////////////////////////// module clock_divider(output i2c_clk); wire ref_clk; wire reset; reg i2c_clk = 1; reg [10:0] counter = 0; clock_signal cd_cs(.val(ref_clk)); always @(posedge ref_clk) begin if(counter == (500)) begin i2c_clk = ~ i2c_clk; counter = 0; end else begin counter = counter + 1; // up counter is always efficient than down counter bcus ripple carry is gud!! end end endmodule /////////////////////////////////////////
testbench
code:
module testbench(); wire main_clk; wire i2c_clk; reg reset; wire sda_line, scl; wire [2:0]state_out; wire [3:0] addr_count_out; wire sda_master_enable; wire sda_master_enable; wire scl,sda_line; wire [7:0] addr_data_out; wire flag; // Instantiate the main module main dut1( .reset(reset) ); master dut2(.reset(reset),.sda_line(sda_line),.scl(scl),.state_out(state_out),.sda_master_enable(sda_master_enable)); slave dut5(.scl(scl),.sda_line(sda_line),.addr_data_out(addr_data_out),.addr_count_out(addr_count_out),.flag(flag),.sda_master_enable(sda_master_enable)); clock_signal dut3(.val(main_clk)); clock_divider dut4(.i2c_clk(i2c_clk)); // Test Stimulus initial begin reset = 1; #27000 reset = 0; // Trigger Start Condition #300000 $finish; end // Monitor Outputs initial begin $dumpfile("waveform.vcd"); $dumpvars(0, testbench); end endmodule