From fbc4b5ff9a742f4b4c3d4e1d6e976d2807ef271a Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Sun, 29 Dec 2024 12:18:37 +0800 Subject: [PATCH 01/12] added initial files for spd --- rtl/spd/clk_wiz.v | 39 ++ rtl/spd/i2c_master.sv | 694 ++++++++++++++++++++++++++++++ rtl/spd/spd_reader.v | 103 +++++ rtl/spd/spd_reader_top.v | 60 +++ testbench/spd_tb/i2c_slave.v | 363 ++++++++++++++++ testbench/spd_tb/spd_reader_tb.sv | 44 ++ 6 files changed, 1303 insertions(+) create mode 100644 rtl/spd/clk_wiz.v create mode 100644 rtl/spd/i2c_master.sv create mode 100644 rtl/spd/spd_reader.v create mode 100644 rtl/spd/spd_reader_top.v create mode 100644 testbench/spd_tb/i2c_slave.v create mode 100644 testbench/spd_tb/spd_reader_tb.sv diff --git a/rtl/spd/clk_wiz.v b/rtl/spd/clk_wiz.v new file mode 100644 index 0000000..830dd21 --- /dev/null +++ b/rtl/spd/clk_wiz.v @@ -0,0 +1,39 @@ +`timescale 1ps/1ps + +module clk_wiz + ( + input clk_in1, + output clk_out1, + input reset, + output locked + ); + wire clk_out1_clk_wiz_0; + + + wire clkfbout; + + PLLE2_ADV + #(.BANDWIDTH ("OPTIMIZED"), + .COMPENSATION ("INTERNAL"), + .STARTUP_WAIT ("FALSE"), + .DIVCLK_DIVIDE (1), + .CLKFBOUT_MULT (5), // 200 MHz * 5 = 1000 MHz + .CLKFBOUT_PHASE (0.000), + .CLKOUT0_DIVIDE (10), // 1000 MHz / 10 = 100 MHz + .CLKOUT0_PHASE (0.000), + .CLKOUT0_DUTY_CYCLE (0.500), + .CLKIN1_PERIOD (5.000) // 200 MHz input + ) + plle2_adv_inst + ( + .CLKFBOUT (clkfbout), + .CLKOUT0 (clk_out1_clk_wiz_0), + .CLKFBIN (clkfbout), + .CLKIN1 (clk_in1), + .LOCKED (locked), + .RST (reset) + ); + BUFG clkout1_buf + (.O (clk_out1), + .I (clk_out1_clk_wiz_0)); +endmodule diff --git a/rtl/spd/i2c_master.sv b/rtl/spd/i2c_master.sv new file mode 100644 index 0000000..9cdf000 --- /dev/null +++ b/rtl/spd/i2c_master.sv @@ -0,0 +1,694 @@ +`timescale 1ns / 1ps +////////////////////////////////////////////////////////////////////////////////// +// Company: www.circuitden.com +// Engineer: Artin Isagholian +// artinisagholian@gmail.com +// +// Create Date: 01/20/2021 05:47:22 PM +// Design Name: +// Module Name: i2c_master +// Project Name: +// Target Devices: +// Tool Versions: +// Description: +// +// Dependencies: +// +// Revision: +// Revision 0.01 - File Created +// Additional Comments: +// +////////////////////////////////////////////////////////////////////////////////// +module i2c_master#( + parameter DATA_WIDTH = 8, + parameter REGISTER_WIDTH = 8, + parameter ADDRESS_WIDTH = 7 +)( + input wire clock, + input wire reset_n, + input wire enable, + input wire read_write, + input wire [DATA_WIDTH-1:0] mosi_data, + input wire [REGISTER_WIDTH-1:0] register_address, + input wire [ADDRESS_WIDTH-1:0] device_address, + input wire [15:0] divider, + + output reg [DATA_WIDTH-1:0] miso_data, + output reg busy, + + inout external_serial_data, + inout external_serial_clock, + // debug if slave NACKs + output reg slave_nack // high at same time as busy deasserts if slave nacks at previous transaction. otherwise low +); + + + /*INSTANTATION TEMPLATE +i2c_master #(.DATA_WIDTH(8),.REGISTER_WIDTH(8),.ADDRESS_WIDTH(7)) + i2c_master_inst( + .clock (), + .reset_n (), + .enable (), + .read_write (), + .mosi_data (), + .register_address (), + .device_address (), + .divider (), + + .miso_data (), + .busy (), + + .external_serial_data (), + .external_serial_clock (), + .slave_nack () + ); +*/ + +typedef enum +{ + S_IDLE = 0, + S_START = 1, + S_WRITE_ADDR_W = 2, + S_CHECK_ACK = 3, + S_WRITE_REG_ADDR = 4, + S_RESTART = 5, + S_WRITE_ADDR_R = 6, + S_READ_REG = 7, + S_SEND_NACK = 8, + S_SEND_STOP = 9, + S_WRITE_REG_DATA = 10, + S_WRITE_REG_ADDR_MSB = 11, + S_WRITE_REG_DATA_MSB = 12, + S_READ_REG_MSB = 13, + S_SEND_ACK = 14 +} state_type; + +state_type state; +state_type _state; +state_type post_state; +state_type _post_state; +reg serial_clock; +logic _serial_clock; +reg [ADDRESS_WIDTH:0] saved_device_address; +logic [ADDRESS_WIDTH:0] _saved_device_address; +reg [REGISTER_WIDTH-1:0] saved_register_address; +logic [REGISTER_WIDTH-1:0] _saved_register_address; +reg [DATA_WIDTH-1:0] saved_mosi_data; +logic [DATA_WIDTH-1:0] _saved_mosi_data; +reg [1:0] process_counter; +logic [1:0] _process_counter; +reg [7:0] bit_counter; +logic [7:0] _bit_counter; +reg serial_data; +logic _serial_data; +reg post_serial_data; +logic _post_serial_data; +reg last_acknowledge; +logic _last_acknowledge; +logic _saved_read_write; +reg saved_read_write; +reg [15:0] divider_counter; +logic [15:0] _divider_counter; +reg divider_tick; +logic [DATA_WIDTH-1:0] _miso_data; +logic _busy; +logic serial_data_output_enable; +logic serial_clock_output_enable; + +assign external_serial_clock = (serial_clock_output_enable) ? serial_clock : 1'bz; +assign external_serial_data = (serial_data_output_enable) ? serial_data : 1'bz; + +// determine if slave NACK +reg last_acknowledge_q; +always_ff @(posedge clock) begin + if (!reset_n) begin + last_acknowledge_q <= 1'b0; + slave_nack <= 1'b0; + end + else begin + last_acknowledge_q <= last_acknowledge? 1'b1 : last_acknowledge_q; // if last_ack becomes high, store that high + if(!_busy && busy) begin // if busy is about to deassert next clk + last_acknowledge_q <= 1'b0; // reset to zero + slave_nack <= !last_acknowledge_q;// if last_acknowledge_q never becomes 1 then the slave NACKs + end + else if(_busy) begin + slave_nack <= 1'b0; + end + end +end + +always_comb begin + _state = state; + _post_state = post_state; + _process_counter = process_counter; + _bit_counter = bit_counter; + _last_acknowledge = last_acknowledge; + _miso_data = miso_data; + _saved_read_write = saved_read_write; + _busy = busy; + _divider_counter = divider_counter; + _saved_register_address = saved_register_address; + _saved_device_address = saved_device_address; + _saved_mosi_data = saved_mosi_data; + _serial_data = serial_data; + _serial_clock = serial_clock; + _post_serial_data = post_serial_data; + + if (divider_counter == divider) begin + _divider_counter = 0; + divider_tick = 1; + end + else begin + _divider_counter = divider_counter + 1; + divider_tick = 0; + end + + if (state!=S_IDLE && state!=S_CHECK_ACK && state!=S_READ_REG && state!=S_READ_REG_MSB) begin + serial_data_output_enable = 1; + end + else begin + serial_data_output_enable = 0; + end + + if (state!=S_IDLE && process_counter!=1 && process_counter!=2) begin + serial_clock_output_enable = 1; + end + else begin + serial_clock_output_enable = 0; + end + + case (state) + S_IDLE: 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}; + _saved_mosi_data = mosi_data; + _serial_data = 1; + _serial_clock = 1; + + if (enable) begin + _state = S_START; + _post_state = S_WRITE_ADDR_W; + _busy = 1; + end + end + S_START: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _process_counter = 1; + end + 1: begin + _serial_data = 0; + _process_counter = 2; + end + 2: begin + _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]; + end + endcase + end + end + S_WRITE_ADDR_W: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _process_counter = 1; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _process_counter = 2; + end + end + 2: begin + _serial_clock = 0; + _bit_counter = bit_counter - 1; + _process_counter = 3; + end + 3: begin + if (bit_counter == 0) begin + _post_serial_data = saved_register_address[REGISTER_WIDTH-1]; + + if (REGISTER_WIDTH == 16) begin + _post_state = S_WRITE_REG_ADDR_MSB; + end + else begin + _post_state = S_WRITE_REG_ADDR; + end + + _state = S_CHECK_ACK; + _bit_counter = 8; + end + else begin + _serial_data = saved_device_address[bit_counter-1]; + end + _process_counter = 0; + end + endcase + end + end + S_CHECK_ACK: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _process_counter = 1; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _last_acknowledge = 0; + _process_counter = 2; + end + end + 2: begin + _serial_clock = 0; + + if (external_serial_data == 0) begin + _last_acknowledge = 1; + end + _process_counter = 3; + end + 3: begin + if (last_acknowledge == 1) begin + _last_acknowledge = 0; + _serial_data = post_serial_data; + _state = post_state; + end + else begin + _state = S_SEND_STOP; + end + _process_counter = 0; + end + endcase + end + end + S_WRITE_REG_ADDR_MSB: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _process_counter = 1; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _last_acknowledge = 0; + _process_counter = 2; + end + end + 2: begin + _serial_clock = 0; + _bit_counter = bit_counter - 1; + _process_counter = 3; + end + 3: begin + if (bit_counter == 0) begin + _post_state = S_WRITE_REG_ADDR; + _post_serial_data = saved_register_address[7]; + _bit_counter = 8; + _serial_data = 0; + _state = S_CHECK_ACK; + end + else begin + _serial_data = saved_register_address[bit_counter+7]; + end + _process_counter = 0; + end + endcase + end + end + S_WRITE_REG_ADDR: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _process_counter = 1; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _last_acknowledge = 0; + _process_counter = 2; + end + end + 2: begin + _serial_clock = 0; + _bit_counter = bit_counter - 1; + _process_counter = 3; + end + 3: begin + if (bit_counter == 0) begin + if (read_write == 0) begin + if (DATA_WIDTH == 16) begin + _post_state = S_WRITE_REG_DATA_MSB; + _post_serial_data = saved_mosi_data[DATA_WIDTH == 16? 15 : 7]; + end + else begin + _post_state = S_WRITE_REG_DATA; + _post_serial_data = saved_mosi_data[7]; + end + end + else begin + _post_state = S_RESTART; + _post_serial_data = 1; + end + _bit_counter = 8; + _serial_data = 0; + _state = S_CHECK_ACK; + end + else begin + _serial_data = saved_register_address[bit_counter-1]; + end + _process_counter = 0; + end + endcase + end + end + S_WRITE_REG_DATA_MSB: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _process_counter = 1; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _last_acknowledge = 0; + _process_counter = 2; + end + end + 2: begin + _serial_clock = 0; + _bit_counter = bit_counter - 1; + _process_counter = 3; + end + 3: begin + if (bit_counter == 0) begin + _state = S_CHECK_ACK; + _post_state = S_WRITE_REG_DATA; + _post_serial_data = saved_mosi_data[7]; + _bit_counter = 8; + _serial_data = 0; + end + else begin + _serial_data = saved_mosi_data[bit_counter+7]; + end + _process_counter = 0; + end + endcase + end + end + S_WRITE_REG_DATA: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _process_counter = 1; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _last_acknowledge = 0; + _process_counter = 2; + end + end + 2: begin + _serial_clock = 0; + _bit_counter = bit_counter - 1; + _process_counter = 3; + end + 3: begin + if (bit_counter == 0) begin + _state = S_CHECK_ACK; + _post_state = S_SEND_STOP; + _post_serial_data = 0; + _bit_counter = 8; + _serial_data = 0; + end + else begin + _serial_data = saved_mosi_data[bit_counter-1]; + end + _process_counter = 0; + end + endcase + end + end + S_RESTART: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _process_counter = 1; + end + 1: begin + _process_counter = 2; + _serial_clock = 1; + end + 2: begin + _process_counter = 3; + end + 3: begin + _state = S_START; + _post_state = S_WRITE_ADDR_R; + _saved_device_address[0] = 1; + _process_counter = 0; + end + endcase + end + end + S_WRITE_ADDR_R: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _process_counter = 1; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _last_acknowledge = 0; + _process_counter = 2; + end + end + 2: begin + _serial_clock = 0; + _bit_counter = bit_counter - 1; + _process_counter = 3; + end + 3: begin + if (bit_counter == 0) begin + if (DATA_WIDTH == 16) begin + _post_state = S_READ_REG_MSB; + _post_serial_data = 0; + end + else begin + _post_state = S_READ_REG; + _post_serial_data = 0; + end + _state = S_CHECK_ACK; + _bit_counter = 8; + end + else begin + _serial_data = saved_device_address[bit_counter-1]; + end + _process_counter = 0; + end + endcase + end + end + S_READ_REG_MSB: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _process_counter = 1; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _last_acknowledge = 0; + _process_counter = 2; + end + end + 2: begin + _serial_clock = 0; + //sample data on this rising edge of scl + _miso_data[bit_counter+7] = external_serial_data; + _bit_counter = bit_counter - 1; + _process_counter = 3; + end + 3: begin + if (bit_counter == 0) begin + _post_state = S_READ_REG; + _state = S_SEND_ACK; + _bit_counter = 8; + _serial_data = 0; + end + _process_counter = 0; + end + endcase + end + end + S_READ_REG: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _process_counter = 1; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _last_acknowledge = 0; + _process_counter = 2; + end + end + 2: begin + _serial_clock = 0; + //sample data on this rising edge of scl + _miso_data[bit_counter-1] = external_serial_data; + _bit_counter = bit_counter - 1; + _process_counter = 3; + end + 3: begin + if (bit_counter == 0) begin + _state = S_SEND_NACK; + _serial_data = 0; + end + _process_counter = 0; + end + endcase + end + end + S_SEND_NACK: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _serial_data = 1; + _process_counter = 1; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _last_acknowledge = 0; + _process_counter = 2; + end + end + 2: begin + _process_counter = 3; + _serial_clock = 0; + end + 3: begin + _state = S_SEND_STOP; + _process_counter = 0; + _serial_data = 0; + end + endcase + end + end + S_SEND_ACK: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _process_counter = 1; + _serial_data = 0; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _last_acknowledge = 0; + _process_counter = 2; + end + end + 2: begin + _process_counter = 3; + _serial_clock = 0; + end + 3: begin + _state = post_state; + _process_counter = 0; + end + endcase + end + end + S_SEND_STOP: begin + if (divider_tick) begin + case (process_counter) + 0: begin + _serial_clock = 1; + _process_counter = 1; + end + 1: begin + //check for clock stretching + if (external_serial_clock == 1) begin + _last_acknowledge = 0; + _process_counter = 2; + end + end + 2: begin + _process_counter = 3; + _serial_data = 1; + end + 3: begin + _state = S_IDLE; + end + endcase + end + end + endcase +end + +always_ff @(posedge clock) begin + if (!reset_n) begin + state <= S_IDLE; + post_state <= S_IDLE; + process_counter <= 0; + bit_counter <= 0; + last_acknowledge <= 0; + miso_data <= 0; + saved_read_write <= 0; + divider_counter <= 0; + saved_device_address <= 0; + saved_register_address <= 0; + saved_mosi_data <= 0; + serial_clock <= 0; + serial_data <= 0; + saved_mosi_data <= 0; + post_serial_data <= 0; + busy <= 0; + end + else begin + state <= _state; + post_state <= _post_state; + process_counter <= _process_counter; + bit_counter <= _bit_counter; + last_acknowledge <= _last_acknowledge; + miso_data <= _miso_data; + saved_read_write <= _saved_read_write; + divider_counter <= _divider_counter; + saved_device_address <= _saved_device_address; + saved_register_address <= _saved_register_address; + saved_mosi_data <= _saved_mosi_data; + serial_clock <= _serial_clock; + serial_data <= _serial_data; + post_serial_data <= _post_serial_data; + busy <= _busy; + end + end + +endmodule \ No newline at end of file diff --git a/rtl/spd/spd_reader.v b/rtl/spd/spd_reader.v new file mode 100644 index 0000000..320a417 --- /dev/null +++ b/rtl/spd/spd_reader.v @@ -0,0 +1,103 @@ +`default_nettype none +`timescale 1ns / 1ps + +module spd_reader ( + // clock and reset + input wire i_clk, + input wire i_rst_n, + // i2c interface + inout wire i2c_scl, + inout wire i2c_sda + // uart interface + // input uart_rx, + // output uart_tx +); + + // byte 2: DRAM Device Type (DDR3 SDRAM = 0x0B) + // byte 3: Module Type (SO-DIMM = 0x03) + // byte 4: SDRAM Density and Banks ([6:4] = BA_BITS, [3:0] = SDRAM capacity) + // byte 5: SDRAM Addressing ([5:3] = Row Addr , [2:0] = Column Addr) + // byte 7: Module Organization ([5:3] = Ranks , Device Width = [2:0]) + // byte 8: Module Memory Bus Width ([2:0] = Bus Width) + // byte 10,11: Medium Timebase (MTB) Dividend (0x01), Medium Timebase (MTB) Divisor (0x08 = 0.125ns , 0x10 = 0.0625ns) (tXX = tXX(MTB) * MTB) + // byte 12: SDRAM Minimum Cycle Time tCK + + localparam I2C_ADDRESS = 7'h30; + localparam IDLE = 0, + READ_ADDRESS = 1, + WAIT_ACK = 2; + (* mark_debug = "true" *) reg[1:0] state_find_i2c_address; + (* mark_debug = "true" *) reg find_i2c_address_done; + (* mark_debug = "true" *) reg[6:0] i2c_address; + + // i2c master interface + reg enable; + reg read_write; + reg[7:0] register_address; + reg[6:0] device_address; + wire[7:0] miso_data; + wire busy; + wire slave_nack; + + always @(posedge i_clk, negedge i_rst_n) begin + if(!i_rst_n) begin + state_find_i2c_address <= IDLE; + find_i2c_address_done <= 0; + enable <= 1'b0; + read_write <= 1'b0; + register_address <= 8'd0; + i2c_address <= 7'd0; + end + else begin + // Find I2C Address of SPD + case(state_find_i2c_address) + IDLE: if(!find_i2c_address_done) begin + state_find_i2c_address <= READ_ADDRESS; + i2c_address <= 7'd0; + end + READ_ADDRESS: if(!busy) begin + enable <= 1'b1; + read_write <= 1'b1; // read i2c + register_address <= 8'h00; // just always read byte 0 + device_address <= i2c_address; + state_find_i2c_address <= WAIT_ACK; + end + WAIT_ACK: if(!busy && !enable) begin + if(slave_nack) begin // if wrong i2c_address + i2c_address <= i2c_address + 1; // increment i2c address + state_find_i2c_address <= READ_ADDRESS; + end + else begin // I2C acks so i2c_address is correct! + state_find_i2c_address <= IDLE; + find_i2c_address_done <= 1'b1; + end + end + else begin + enable <= 1'b0; + end + endcase + end + end + + // module instantiations + i2c_master #(.DATA_WIDTH(8),.REGISTER_WIDTH(8),.ADDRESS_WIDTH(7)) + i2c_master_inst( + .clock (i_clk), + .reset_n (i_rst_n), + .enable (enable), + .read_write (read_write), + .mosi_data (8'd0), + .register_address (register_address), + .device_address (device_address), + .divider (249), // 100MHz/(4*(249+1)) = 100KHz + + .miso_data (miso_data), + .busy (busy), + + .external_serial_data (i2c_sda), + .external_serial_clock (i2c_scl), + .slave_nack (slave_nack) + ); +endmodule + + diff --git a/rtl/spd/spd_reader_top.v b/rtl/spd/spd_reader_top.v new file mode 100644 index 0000000..436dffb --- /dev/null +++ b/rtl/spd/spd_reader_top.v @@ -0,0 +1,60 @@ +`default_nettype none +`timescale 1ns / 1ps + +module spd_reader_top ( + // clock and reset + input wire sys_clk_p, + input wire sys_clk_n, + input wire i_rst_n, + // i2c interface + inout wire i2c_scl, + inout wire i2c_sda, + output wire i2c_lsb, + // fan + output wire fan_pwm +); + assign fan_pwm = 1'b0; // turn on fan + assign i2c_lsb = 1'b0; + wire clk_locked; + wire main_clk_100; + + + //=========================================================================== + //Differentia system clock to single end clock + //=========================================================================== + wire sys_clk; // 200MHz + IBUFGDS u_ibufg_sys_clk + ( + .I (sys_clk_p), + .IB (sys_clk_n), + .O (sys_clk) + ); + + //=========================================================================== + // Generate 100MHz + //=========================================================================== + clk_wiz clk_wiz_inst + ( + // Clock out ports + .clk_out1(main_clk_100), + // Status and control signals + .reset(!i_rst_n), + .locked(clk_locked), + // Clock in ports + .clk_in1(sys_clk) + ); + + //=========================================================================== + // Instantiate SPD reader + //=========================================================================== + spd_reader spd_reader_inst ( + .i_clk(main_clk_100), + .i_rst_n(i_rst_n && clk_locked), + .i2c_scl(i2c_scl), + .i2c_sda(i2c_sda) + ); + + +endmodule + + diff --git a/testbench/spd_tb/i2c_slave.v b/testbench/spd_tb/i2c_slave.v new file mode 100644 index 0000000..c10c1a6 --- /dev/null +++ b/testbench/spd_tb/i2c_slave.v @@ -0,0 +1,363 @@ +`timescale 1ns / 1ps +///////////////////////////////////////////////////////////////////// +//// //// +//// WISHBONE rev.B2 compliant synthesizable I2C Slave model //// +//// //// +//// //// +//// Authors: Richard Herveille (richard@asics.ws) www.asics.ws //// +//// John Sheahan (jrsheahan@optushome.com.au) //// +//// //// +//// Downloaded from: http://www.opencores.org/projects/i2c/ //// +//// //// +///////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2001,2002 Richard Herveille //// +//// richard@asics.ws //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer.//// +//// //// +//// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// +//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// +//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// +//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// +//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// +//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// +//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// +//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// +//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// +//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// +//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// +//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// +//// POSSIBILITY OF SUCH DAMAGE. //// +//// //// +///////////////////////////////////////////////////////////////////// + +// CVS Log +// +// $Id: i2c_slave_model.v,v 1.7 2006-09-04 09:08:51 rherveille Exp $ +// +// $Date: 2006-09-04 09:08:51 $ +// $Revision: 1.7 $ +// $Author: rherveille $ +// $Locker: $ +// $State: Exp $ +// +// Change History: +// $Log: not supported by cvs2svn $ +// Revision 1.6 2005/02/28 11:33:48 rherveille +// Fixed Tsu:sta timing check. +// Added Thd:sta timing check. +// +// Revision 1.5 2003/12/05 11:05:19 rherveille +// Fixed slave address MSB='1' bug +// +// Revision 1.4 2003/09/11 08:25:37 rherveille +// Fixed a bug in the timing section. Changed 'tst_scl' into 'tst_sto'. +// +// Revision 1.3 2002/10/30 18:11:06 rherveille +// Added timing tests to i2c_model. +// Updated testbench. +// +// Revision 1.2 2002/03/17 10:26:38 rherveille +// Fixed some race conditions in the i2c-slave model. +// Added debug information. +// Added headers. +// + +module i2c_slave (scl, sda); + + // + // parameters + // + parameter I2C_ADR = 7'b001_0000; + + // + // input && outpus + // + input scl; + inout sda; + + // + // Variable declaration + // + wire debug = 1'b1; + genvar i; + + reg [7:0] mem [3:0]; // initiate memory + + initial begin + mem[0] = 8'd00; + mem[1] = 8'd01; + mem[2] = 8'd02; + mem[3] = 8'd03; + end + + reg [7:0] mem_adr; // memory address + reg [7:0] mem_do; // memory data output + + reg sta, d_sta; + reg sto, d_sto; + + reg [7:0] sr; // 8bit shift register + reg rw; // read/write direction + + wire my_adr; // my address called ?? + wire i2c_reset; // i2c-statemachine reset + reg [2:0] bit_cnt; // 3bit downcounter + wire acc_done; // 8bits transfered + reg ld; // load downcounter + + reg sda_o; // sda-drive level + wire sda_dly; // delayed version of sda + + // statemachine declaration + parameter idle = 3'b000; + parameter slave_ack = 3'b001; + parameter get_mem_adr = 3'b010; + parameter gma_ack = 3'b011; + parameter data = 3'b100; + parameter data_ack = 3'b101; + + reg [2:0] state; // synopsys enum_state + + // + // module body + // + + initial + begin + sda_o = 1'b1; + state = idle; + end + + // generate shift register + always @(posedge scl) + sr <= #1 {sr[6:0],sda}; + + //detect my_address + assign my_adr = (sr[7:1] == I2C_ADR); + // FIXME: This should not be a generic assign, but rather + // qualified on address transfer phase and probably reset by stop + + //generate bit-counter + always @(posedge scl) + if(ld) + bit_cnt <= #1 3'b111; + else + bit_cnt <= #1 bit_cnt - 3'h1; + + //generate access done signal + assign acc_done = !(|bit_cnt); + + // generate delayed version of sda + // this model assumes a hold time for sda after the falling edge of scl. + // According to the Phillips i2c spec, there s/b a 0 ns hold time for sda + // with regards to scl. If the data changes coincident with the clock, the + // acknowledge is missed + // Fix by Michael Sosnoski + assign #1 sda_dly = sda; + + + //detect start condition + always @(negedge sda) + if(scl) + begin + sta <= #1 1'b1; + d_sta <= #1 1'b0; + sto <= #1 1'b0; + + if(debug) + $display("DEBUG i2c_slave; start condition detected at %t", $time); + end + else + sta <= #1 1'b0; + + always @(posedge scl) + d_sta <= #1 sta; + + // detect stop condition + always @(posedge sda) + if(scl) + begin + sta <= #1 1'b0; + sto <= #1 1'b1; + + if(debug) + $display("DEBUG i2c_slave; stop condition detected at %t", $time); + end + else + sto <= #1 1'b0; + + //generate i2c_reset signal + assign i2c_reset = sta || sto; + + // generate statemachine + always @(negedge scl or posedge sto) + if (sto || (sta && !d_sta) ) + begin + state <= #1 idle; // reset statemachine + + sda_o <= #1 1'b1; + ld <= #1 1'b1; + end + else + begin + // initial settings + sda_o <= #1 1'b1; + ld <= #1 1'b0; + + case(state) // synopsys full_case parallel_case + idle: // idle state + if (acc_done && my_adr) + begin + state <= #1 slave_ack; + rw <= #1 sr[0]; + sda_o <= #1 1'b0; // generate i2c_ack + + #2; + if(debug && rw) + $display("DEBUG i2c_slave; command byte received (read) at %t", $time); + if(debug && !rw) + $display("DEBUG i2c_slave; command byte received (write) at %t", $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]); + end + end + end + + slave_ack: + begin + if(rw) + begin + state <= #1 data; + sda_o <= #1 mem_do[7]; + end + else + state <= #1 get_mem_adr; + + ld <= #1 1'b1; + end + + get_mem_adr: // wait for memory address + if(acc_done) + begin + state <= #1 gma_ack; + mem_adr <= #1 sr; // store memory address + 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); + end + + gma_ack: + begin + state <= #1 data; + ld <= #1 1'b1; + end + + data: // receive or drive data + begin + if(rw) + sda_o <= #1 mem_do[7]; + + if(acc_done) + begin + state <= #1 data_ack; + mem_adr <= #2 mem_adr + 8'h1; + sda_o <= #1 (rw && (mem_adr <= 15) ); // send ack on write, receive ack on read + + if(rw) + begin + #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); + 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); + end + end + end + + data_ack: + begin + ld <= #1 1'b1; + + if(rw) + if(sr[0]) // read operation && master send NACK + begin + state <= #1 idle; + sda_o <= #1 1'b1; + end + else + begin + state <= #1 data; + sda_o <= #1 mem_do[7]; + end + else + begin + state <= #1 data; + sda_o <= #1 1'b1; + end + end + + endcase + end + + // read data from memory + always @(posedge scl) + if(!acc_done && rw) + mem_do <= #1 {mem_do[6:0], 1'b1}; // insert 1'b1 for host ack generation + + // generate tri-states + assign sda = sda_o ? 1'bz : 1'b0; + + + // + // Timing checks + // + + wire tst_sto = sto; + wire tst_sta = sta; + + specify + specparam normal_scl_low = 4700, + normal_scl_high = 4000, + normal_tsu_sta = 4700, + normal_thd_sta = 4000, + normal_tsu_sto = 4000, + normal_tbuf = 4700, + + fast_scl_low = 1300, + fast_scl_high = 600, + fast_tsu_sta = 1300, + fast_thd_sta = 600, + fast_tsu_sto = 600, + fast_tbuf = 1300; + + $width(negedge scl, normal_scl_low); // scl low time + $width(posedge scl, normal_scl_high); // scl high time + + $setup(posedge scl, negedge sda &&& scl, normal_tsu_sta); // setup start + $setup(negedge sda &&& scl, negedge scl, normal_thd_sta); // hold start + $setup(posedge scl, posedge sda &&& scl, normal_tsu_sto); // setup stop + + $setup(posedge tst_sta, posedge tst_sto, normal_tbuf); // stop to start time + endspecify + +endmodule diff --git a/testbench/spd_tb/spd_reader_tb.sv b/testbench/spd_tb/spd_reader_tb.sv new file mode 100644 index 0000000..543cdd8 --- /dev/null +++ b/testbench/spd_tb/spd_reader_tb.sv @@ -0,0 +1,44 @@ +`timescale 1ns / 1ps + +module spd_reader_tb; + reg clk, rst_n; + wire scl, sda; + + // spd_reader DUT ( + // .i_clk(clk), + // .i_rst_n(rst_n), + // .i2c_scl(scl), + // .i2c_sda(sda) + // ); + spd_reader_top DUT ( + // clock and reset + .sys_clk_p(clk), + .sys_clk_n(!clk), + .i_rst_n(rst_n), + // i2c interface + .i2c_scl(scl), + .i2c_sda(sda), + .i2c_lsb(), + // fan + .fan_pwm() + ); + + initial begin + clk = 0; + rst_n = 0; + #100; + rst_n = 1; + wait(DUT.spd_reader_inst.find_i2c_address_done); + #10_000; + $stop; + end + always #2.5 clk = !clk; // 200MHz + + pullup pullup_scl(scl); // pullup scl line + pullup pullup_sda(sda); // pullup sda line + + i2c_slave i2c_slave( + .scl(scl), + .sda(sda) + ); +endmodule \ No newline at end of file From 75857a0af0461c1e9e703a8eef008f4d96b39360 Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Sun, 29 Dec 2024 14:47:57 +0800 Subject: [PATCH 02/12] read bytes 0 to 63 of spd then store (sim passing) --- .../alinx_ax7325b/ax7325b_spd_reader.xdc | 20 +++++ rtl/spd/spd_reader.v | 73 +++++++++++++++++-- rtl/spd/spd_reader_top.v | 18 +++-- testbench/spd_tb/i2c_slave.v | 24 +++--- testbench/spd_tb/spd_reader_tb.sv | 7 +- 5 files changed, 114 insertions(+), 28 deletions(-) create mode 100644 example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc diff --git a/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc b/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc new file mode 100644 index 0000000..f6a0a2f --- /dev/null +++ b/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc @@ -0,0 +1,20 @@ +############## clock define################## +create_clock -period 5.000 [get_ports sys_clk_p] +set_property IOSTANDARD DIFF_SSTL15 [get_ports sys_clk_p] +# no need to constrain N side (only P side) or else tool will analyze interclock oaths and show failure in timing +# https://support.xilinx.com/s/article/57109?language=en_US +#create_clock -period 5.000 [get_ports sys_clk_n] +set_property PACKAGE_PIN AE10 [get_ports sys_clk_p] +set_property PACKAGE_PIN AF10 [get_ports sys_clk_n] +set_property IOSTANDARD DIFF_SSTL15 [get_ports sys_clk_n] +############## SODIMM SPD define################## +set_property IOSTANDARD LVCMOS33 [get_ports i2c_scl] +set_property PACKAGE_PIN B20 [get_ports i2c_scl] +set_property IOSTANDARD LVCMOS33 [get_ports i2c_sda] +set_property PACKAGE_PIN C20 [get_ports i2c_sda] +############## fan define################## +set_property IOSTANDARD LVCMOS25 [get_ports fan_pwm] +set_property PACKAGE_PIN AE26 [get_ports fan_pwm] +############## key define################## +set_property PACKAGE_PIN AG27 [get_ports i_rst_n] +set_property IOSTANDARD LVCMOS25 [get_ports i_rst_n] diff --git a/rtl/spd/spd_reader.v b/rtl/spd/spd_reader.v index 320a417..7993475 100644 --- a/rtl/spd/spd_reader.v +++ b/rtl/spd/spd_reader.v @@ -7,7 +7,10 @@ module spd_reader ( input wire i_rst_n, // i2c interface inout wire i2c_scl, - inout wire i2c_sda + inout wire i2c_sda, + // state of spd reader + (* mark_debug = "true" *) output reg find_i2c_address_done, + (* mark_debug = "true" *) output reg read_spd_done // uart interface // input uart_rx, // output uart_tx @@ -25,9 +28,9 @@ module spd_reader ( localparam I2C_ADDRESS = 7'h30; localparam IDLE = 0, READ_ADDRESS = 1, + READ_BYTE = 1, WAIT_ACK = 2; (* mark_debug = "true" *) reg[1:0] state_find_i2c_address; - (* mark_debug = "true" *) reg find_i2c_address_done; (* mark_debug = "true" *) reg[6:0] i2c_address; // i2c master interface @@ -38,7 +41,27 @@ module spd_reader ( wire[7:0] miso_data; wire busy; wire slave_nack; - + (* mark_debug = "true" *) reg nack_unexpected_err; + (* mark_debug = "true" *) reg[2:0] state_read_spd; + (* mark_debug = "true" *) reg[5:0] byte_address; // read until byte 63 + (* mark_debug = "true" *) reg[7:0] byte_data[63:0]; + + + // initialize in case fpga starts with no reset + initial begin + state_find_i2c_address = IDLE; + find_i2c_address_done = 0; + enable = 1'b0; + read_write = 1'b0; + register_address = 8'd0; + i2c_address = 7'd0; + read_spd_done = 1'b0; + nack_unexpected_err = 1'b0; + state_read_spd = IDLE; + byte_address = 6'h00; + end + + // main FSM always @(posedge i_clk, negedge i_rst_n) begin if(!i_rst_n) begin state_find_i2c_address <= IDLE; @@ -47,13 +70,17 @@ module spd_reader ( read_write <= 1'b0; register_address <= 8'd0; i2c_address <= 7'd0; + read_spd_done <= 1'b0; + nack_unexpected_err <= 1'b0; + state_read_spd <= IDLE; + byte_address <= 6'h00; end else begin // Find I2C Address of SPD case(state_find_i2c_address) IDLE: if(!find_i2c_address_done) begin state_find_i2c_address <= READ_ADDRESS; - i2c_address <= 7'd0; + i2c_address <= 7'h01; // start brute force find i2c address from 1 (0 might be general call) end READ_ADDRESS: if(!busy) begin enable <= 1'b1; @@ -71,10 +98,44 @@ module spd_reader ( state_find_i2c_address <= IDLE; find_i2c_address_done <= 1'b1; end + end + else begin + enable <= 1'b0; + end + default: state_find_i2c_address <= IDLE; + endcase + + // read bytes from SPD + case(state_read_spd) + IDLE: if(find_i2c_address_done && !read_spd_done && !nack_unexpected_err) begin // start read SPD only once i2c address is found + state_read_spd <= READ_BYTE; + byte_address <= 6'h00; // start read from byte 0 end - else begin - enable <= 1'b0; + READ_BYTE: if(!busy) begin // if not busy, send i2c read transaction + enable <= 1'b1; + read_write <= 1'b1; // read i2c + register_address <= {2'b00,byte_address}; + device_address <= i2c_address; + state_read_spd <= WAIT_ACK; end + WAIT_ACK: if(!busy && !enable) begin + if(slave_nack) begin // if i2c_address NACKS, then something is wrong, raise nack_unexpected_err + nack_unexpected_err <= 1'b1; + state_read_spd <= IDLE; + end + else begin // I2C acks so store the received byte + byte_data[byte_address] <= miso_data; + state_read_spd <= READ_BYTE; + byte_address <= byte_address + 1; + if(byte_address == 63) begin + read_spd_done <= 1'b1; + state_read_spd <= IDLE; + end + end + end + else begin + enable <= 1'b0; + end endcase end end diff --git a/rtl/spd/spd_reader_top.v b/rtl/spd/spd_reader_top.v index 436dffb..ec8b063 100644 --- a/rtl/spd/spd_reader_top.v +++ b/rtl/spd/spd_reader_top.v @@ -9,15 +9,21 @@ module spd_reader_top ( // i2c interface inout wire i2c_scl, inout wire i2c_sda, - output wire i2c_lsb, // fan - output wire fan_pwm + output wire fan_pwm, + //Debug LEDs + output wire[3:0] led ); - assign fan_pwm = 1'b0; // turn on fan - assign i2c_lsb = 1'b0; + wire clk_locked; wire main_clk_100; + wire find_i2c_address_done, read_spd_done; + assign fan_pwm = 1'b0; // turn on fan from the start + assign led[0] = find_i2c_address_done; // lights up once done + assign led[1] = find_i2c_address_done; + assign led[2] = read_spd_done; + assign led[3] = read_spd_done; //=========================================================================== //Differentia system clock to single end clock @@ -51,7 +57,9 @@ module spd_reader_top ( .i_clk(main_clk_100), .i_rst_n(i_rst_n && clk_locked), .i2c_scl(i2c_scl), - .i2c_sda(i2c_sda) + .i2c_sda(i2c_sda), + .find_i2c_address_done(find_i2c_address_done), + .read_spd_done(read_spd_done) ); diff --git a/testbench/spd_tb/i2c_slave.v b/testbench/spd_tb/i2c_slave.v index c10c1a6..b185c19 100644 --- a/testbench/spd_tb/i2c_slave.v +++ b/testbench/spd_tb/i2c_slave.v @@ -72,7 +72,7 @@ module i2c_slave (scl, sda); // // parameters // - parameter I2C_ADR = 7'b001_0000; + parameter I2C_ADR = 7'b000_0100; // // input && outpus @@ -86,13 +86,13 @@ module i2c_slave (scl, sda); wire debug = 1'b1; genvar i; - reg [7:0] mem [3:0]; // initiate memory + reg [7:0] mem [99:0]; // initiate memory + integer index; initial begin - mem[0] = 8'd00; - mem[1] = 8'd01; - mem[2] = 8'd02; - mem[3] = 8'd03; + for (index = 0; index <= 100; index = index + 1) begin + mem[index] = index; // Assign each element with its index value + end end reg [7:0] mem_adr; // memory address @@ -350,14 +350,14 @@ module i2c_slave (scl, sda); fast_tsu_sto = 600, fast_tbuf = 1300; - $width(negedge scl, normal_scl_low); // scl low time - $width(posedge scl, normal_scl_high); // scl high time +// $width(negedge scl, normal_scl_low); // scl low time +// $width(posedge scl, normal_scl_high); // scl high time - $setup(posedge scl, negedge sda &&& scl, normal_tsu_sta); // setup start - $setup(negedge sda &&& scl, negedge scl, normal_thd_sta); // hold start - $setup(posedge scl, posedge sda &&& scl, normal_tsu_sto); // setup stop +// $setup(posedge scl, negedge sda &&& scl, normal_tsu_sta); // setup start +// $setup(negedge sda &&& scl, negedge scl, normal_thd_sta); // hold start +// $setup(posedge scl, posedge sda &&& scl, normal_tsu_sto); // setup stop - $setup(posedge tst_sta, posedge tst_sto, normal_tbuf); // stop to start time +// $setup(posedge tst_sta, posedge tst_sto, normal_tbuf); // stop to start time endspecify endmodule diff --git a/testbench/spd_tb/spd_reader_tb.sv b/testbench/spd_tb/spd_reader_tb.sv index 543cdd8..8106894 100644 --- a/testbench/spd_tb/spd_reader_tb.sv +++ b/testbench/spd_tb/spd_reader_tb.sv @@ -17,10 +17,7 @@ module spd_reader_tb; .i_rst_n(rst_n), // i2c interface .i2c_scl(scl), - .i2c_sda(sda), - .i2c_lsb(), - // fan - .fan_pwm() + .i2c_sda(sda) ); initial begin @@ -28,7 +25,7 @@ module spd_reader_tb; rst_n = 0; #100; rst_n = 1; - wait(DUT.spd_reader_inst.find_i2c_address_done); + wait(DUT.spd_reader_inst.read_spd_done); #10_000; $stop; end From 253d9495ca72c0c7c40883afae10ab08d4daa822 Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Sun, 29 Dec 2024 14:53:19 +0800 Subject: [PATCH 03/12] added led to xdc --- example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc b/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc index f6a0a2f..ce92be1 100644 --- a/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc +++ b/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc @@ -1,20 +1,33 @@ ############## clock define################## create_clock -period 5.000 [get_ports sys_clk_p] set_property IOSTANDARD DIFF_SSTL15 [get_ports sys_clk_p] -# no need to constrain N side (only P side) or else tool will analyze interclock oaths and show failure in timing +# no need to create_clock for N side (only P side) or else tool will analyze interclock oaths and show failure in timing # https://support.xilinx.com/s/article/57109?language=en_US #create_clock -period 5.000 [get_ports sys_clk_n] set_property PACKAGE_PIN AE10 [get_ports sys_clk_p] set_property PACKAGE_PIN AF10 [get_ports sys_clk_n] set_property IOSTANDARD DIFF_SSTL15 [get_ports sys_clk_n] + ############## SODIMM SPD define################## set_property IOSTANDARD LVCMOS33 [get_ports i2c_scl] set_property PACKAGE_PIN B20 [get_ports i2c_scl] set_property IOSTANDARD LVCMOS33 [get_ports i2c_sda] set_property PACKAGE_PIN C20 [get_ports i2c_sda] + ############## fan define################## set_property IOSTANDARD LVCMOS25 [get_ports fan_pwm] set_property PACKAGE_PIN AE26 [get_ports fan_pwm] + ############## key define################## set_property PACKAGE_PIN AG27 [get_ports i_rst_n] set_property IOSTANDARD LVCMOS25 [get_ports i_rst_n] + +##############LED define################## +set_property PACKAGE_PIN A22 [get_ports {led[0]}] +set_property IOSTANDARD LVCMOS15 [get_ports {led[0]}] +set_property PACKAGE_PIN C19 [get_ports {led[1]}] +set_property IOSTANDARD LVCMOS15 [get_ports {led[1]}] +set_property PACKAGE_PIN B19 [get_ports {led[2]}] +set_property IOSTANDARD LVCMOS15 [get_ports {led[2]}] +set_property PACKAGE_PIN E18 [get_ports {led[3]}] +set_property IOSTANDARD LVCMOS15 [get_ports {led[3]}] \ No newline at end of file From 7acaf34b442ade0ddc0d5e94e255ad75b4dbd03e Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Sun, 29 Dec 2024 20:41:17 +0800 Subject: [PATCH 04/12] added uart to display spd report --- .../alinx_ax7325b/ax7325b_spd_reader.xdc | 12 +- rtl/spd/spd_reader.v | 321 +++++++++++++++++- rtl/spd/spd_reader_top.v | 3 + rtl/spd/uart_tx.v | 187 ++++++++++ testbench/spd_tb/i2c_slave.v | 24 +- 5 files changed, 523 insertions(+), 24 deletions(-) create mode 100644 rtl/spd/uart_tx.v diff --git a/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc b/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc index ce92be1..2305b1f 100644 --- a/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc +++ b/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc @@ -24,10 +24,14 @@ set_property IOSTANDARD LVCMOS25 [get_ports i_rst_n] ##############LED define################## set_property PACKAGE_PIN A22 [get_ports {led[0]}] -set_property IOSTANDARD LVCMOS15 [get_ports {led[0]}] +set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}] set_property PACKAGE_PIN C19 [get_ports {led[1]}] -set_property IOSTANDARD LVCMOS15 [get_ports {led[1]}] +set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}] set_property PACKAGE_PIN B19 [get_ports {led[2]}] -set_property IOSTANDARD LVCMOS15 [get_ports {led[2]}] +set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}] set_property PACKAGE_PIN E18 [get_ports {led[3]}] -set_property IOSTANDARD LVCMOS15 [get_ports {led[3]}] \ No newline at end of file +set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}] + +##############uart define########################### +set_property IOSTANDARD LVCMOS33 [get_ports uart_tx] +set_property PACKAGE_PIN AK26 [get_ports uart_tx] \ No newline at end of file diff --git a/rtl/spd/spd_reader.v b/rtl/spd/spd_reader.v index 7993475..9a3307b 100644 --- a/rtl/spd/spd_reader.v +++ b/rtl/spd/spd_reader.v @@ -10,10 +10,9 @@ module spd_reader ( inout wire i2c_sda, // state of spd reader (* mark_debug = "true" *) output reg find_i2c_address_done, - (* mark_debug = "true" *) output reg read_spd_done + (* mark_debug = "true" *) output reg read_spd_done, // uart interface - // input uart_rx, - // output uart_tx + output wire uart_tx ); // byte 2: DRAM Device Type (DDR3 SDRAM = 0x0B) @@ -29,7 +28,13 @@ module spd_reader ( localparam IDLE = 0, READ_ADDRESS = 1, READ_BYTE = 1, - WAIT_ACK = 2; + SEND_BYTE = 1, + WAIT_ACK = 2, + WAIT_SEND = 2, + DONE_FIND_ADDRESS = 3, + UART_SEND = 3, + WAIT_UART_DONE = 4; + (* mark_debug = "true" *) reg[1:0] state_find_i2c_address; (* mark_debug = "true" *) reg[6:0] i2c_address; @@ -45,7 +50,18 @@ module spd_reader ( (* mark_debug = "true" *) reg[2:0] state_read_spd; (* mark_debug = "true" *) reg[5:0] byte_address; // read until byte 63 (* mark_debug = "true" *) reg[7:0] byte_data[63:0]; - + // uart interface + wire uart_tx_busy; + reg uart_tx_en; + reg[7:0] uart_tx_data; + reg[30*8-1:0] uart_text; // max of 30 chars + reg[2:0] state_uart_send; + reg uart_start_send; + reg[9:0] uart_text_length,uart_text_length_index; + reg uart_send_done; + reg skip_byte; + reg[7:0] mtb_dividend, mtb; + reg[3:0] tras_high; // initialize in case fpga starts with no reset initial begin @@ -59,9 +75,21 @@ module spd_reader ( nack_unexpected_err = 1'b0; state_read_spd = IDLE; byte_address = 6'h00; + state_uart_send = IDLE; + uart_text_length_index = 0; + uart_tx_en = 0; + uart_send_done = 0; + uart_tx_data = 0; + uart_start_send = 0; + uart_text_length = 0; + uart_text = {(30*8){1'b0}}; + skip_byte = 0; + mtb_dividend = 0; + mtb = 0; + tras_high = 0; end - // main FSM + // FSM for I2C always @(posedge i_clk, negedge i_rst_n) begin if(!i_rst_n) begin state_find_i2c_address <= IDLE; @@ -74,6 +102,13 @@ module spd_reader ( nack_unexpected_err <= 1'b0; state_read_spd <= IDLE; byte_address <= 6'h00; + uart_start_send <= 0; + uart_text_length = 0; + uart_text = {(30*8){1'b0}}; + skip_byte <= 1'b0; + mtb_dividend <= 0; + mtb <= 0; + tras_high <= 0; end else begin // Find I2C Address of SPD @@ -95,13 +130,25 @@ module spd_reader ( state_find_i2c_address <= READ_ADDRESS; end else begin // I2C acks so i2c_address is correct! - state_find_i2c_address <= IDLE; - find_i2c_address_done <= 1'b1; + uart_start_send <= 1'b1; + uart_text_length <= 18; + uart_text[30*8-1:8*3] <= "I2C Address: 0x"; + uart_text[8*3-1:8*2] <= hex_to_ascii(i2c_address[6:4]); + uart_text[8*2-1:8*1] <= hex_to_ascii(i2c_address[3:0]); + uart_text[7:0] <= 8'h0a; + state_find_i2c_address <= DONE_FIND_ADDRESS; end end else begin enable <= 1'b0; end + DONE_FIND_ADDRESS: if(uart_send_done) begin + state_find_i2c_address <= IDLE; + find_i2c_address_done <= 1'b1; + end + else begin + uart_start_send <= 1'b0; + end default: state_find_i2c_address <= IDLE; endcase @@ -110,6 +157,8 @@ module spd_reader ( IDLE: if(find_i2c_address_done && !read_spd_done && !nack_unexpected_err) begin // start read SPD only once i2c address is found state_read_spd <= READ_BYTE; byte_address <= 6'h00; // start read from byte 0 + uart_start_send <= 1'b0; + skip_byte <= 1'b0; end READ_BYTE: if(!busy) begin // if not busy, send i2c read transaction enable <= 1'b1; @@ -123,23 +172,249 @@ module spd_reader ( nack_unexpected_err <= 1'b1; state_read_spd <= IDLE; end - else begin // I2C acks so store the received byte - byte_data[byte_address] <= miso_data; - state_read_spd <= READ_BYTE; - byte_address <= byte_address + 1; - if(byte_address == 63) begin - read_spd_done <= 1'b1; - state_read_spd <= IDLE; - end + else begin // I2C acks so send via UART the received data + state_read_spd <= UART_SEND; + // byte_data[byte_address] <= miso_data; end end else begin enable <= 1'b0; end + UART_SEND: begin + case(byte_address) + 0: begin + uart_start_send <= 1'b1; + uart_text_length <= 15; + uart_text[30*8-1:8] <= "START SPD READ"; + uart_text[7:0] <= 8'h0a; + end + 1: begin + uart_start_send <= 1'b1; + uart_text_length <= 18; + uart_text[30*8-1:8*4] <= "SPD Revision: "; + uart_text[8*4-1:8*3] <= hex_to_ascii(miso_data[7:4]); + uart_text[8*3-1:8*2] <= "."; + uart_text[8*2-1:8*1] <= hex_to_ascii(miso_data[3:0]); + uart_text[7:0] <= 8'h0a; + end + 2: begin + if(miso_data == 8'h0b) begin + uart_start_send <= 1'b1; + uart_text_length <= 22; + uart_text[30*8-1:8] <= "DRAM Type: DDR3 SDRAM"; + uart_text[7:0] <= 8'h0a; + end + else begin + uart_start_send <= 1'b1; + uart_text_length <= 21; + uart_text[30*8-1:8] <= "DRAM Type: NOT DDR3!"; + uart_text[7:0] <= 8'h0a; + end + end + 3: begin + case(miso_data) + 8'h00: begin + uart_start_send <= 1'b1; + uart_text_length <= 24; + uart_text[30*8-1:8] <= "Module Type: Undefined!"; + uart_text[7:0] <= 8'h0a; + end + 8'h01: begin + uart_start_send <= 1'b1; + uart_text_length <= 19; + uart_text[30*8-1:8] <= "Module Type: RDIMM"; + uart_text[7:0] <= 8'h0a; + end + 8'h02: begin + uart_start_send <= 1'b1; + uart_text_length <= 19; + uart_text[30*8-1:8] <= "Module Type: UDIMM"; + uart_text[7:0] <= 8'h0a; + end + 8'h03: begin + uart_start_send <= 1'b1; + uart_text_length <= 21; + uart_text[30*8-1:8] <= "Module Type: SO-DIMM"; + uart_text[7:0] <= 8'h0a; + end + endcase + end + 4: begin + uart_start_send <= 1'b1; + uart_text_length <= 29; + uart_text[30*8-1:8*20] <= "BA_BITS: "; + uart_text[8*20-1:8*19] <= hex_to_ascii(miso_data[6:4]+3); + uart_text[8*19-1:8*18] <= 8'h0a; + uart_text[8*18-1:8*2] <= "SDRAM_CAPACITY: "; + uart_text[8*2-1:8*1] <= hex_to_ascii(miso_data[3:0]); + uart_text[7:0] <= 8'h0a; + end + 5: begin + uart_start_send <= 1'b1; + uart_text_length <= 26; + uart_text[30*8-1:8*16] <= "ROW_BITS: "; + case(miso_data[5:3]) + 0: uart_text[8*16-1:8*14] <= "12"; + 1: uart_text[8*16-1:8*14] <= "13"; + 2: uart_text[8*16-1:8*14] <= "14"; + 3: uart_text[8*16-1:8*14] <= "15"; + 4: uart_text[8*16-1:8*14] <= "16"; + endcase + uart_text[8*14-1:8*13] <= 8'h0a; + uart_text[8*13-1:8*3] <= "COL_BITS: "; + case(miso_data[2:0]) + 0: uart_text[8*3-1:8*1] <= "9 "; + 1: uart_text[8*3-1:8*1] <= "10"; + 2: uart_text[8*3-1:8*1] <= "11"; + 3: uart_text[8*3-1:8*1] <= "12"; + endcase + uart_text[8*1-1:8*0] <= 8'h0a; + end + 7: begin + uart_start_send <= 1'b1; + uart_text_length <= 18; + if(miso_data[5:3] == 0) begin + uart_text[30*8-1:8] <= "DUAL_RANK_DIMM: 1"; + end + else begin + uart_text[30*8-1:8] <= "DUAL_RANK_DIMM: 0"; + end + uart_text[7:0] <= 8'h0a; + end + 8: begin + uart_start_send <= 1'b1; + uart_text_length <= 14; + case(miso_data[2:0]) + 0: uart_text[30*8-1:8] <= "BYTE_LANES: 1"; + 1: uart_text[30*8-1:8] <= "BYTE_LANES: 2"; + 2: uart_text[30*8-1:8] <= "BYTE_LANES: 4"; + 3: uart_text[30*8-1:8] <= "BYTE_LANES: 8"; + endcase + uart_text[7:0] <= 8'h0a; + end + 10: begin + mtb_dividend = miso_data; + uart_start_send <= 1'b1; + uart_text_length <= 30; + uart_text[30*8-1:8*1] <= "----- timing parameters -----"; + uart_text[7:0] <= 8'h0a; + end + 11: begin + mtb = mtb_dividend*1000/miso_data; + uart_start_send <= 1'b1; + uart_text_length <= 15; + uart_text[30*8-1:8*8] <= "mtb: 0x"; + uart_text[8*8-1:8*7] <= hex_to_ascii(mtb[7:4]); + uart_text[7*8-1:8*6] <= hex_to_ascii(mtb[3:0]); + uart_text[6*8-1:8*1] <= " (ps)"; + uart_text[7:0] <= 8'h0a; + end + 18: begin + uart_start_send <= 1'b1; + uart_text_length <= 17; + uart_text[30*8-1:8*3] <= "TRCD: mtb * 0x"; + uart_text[3*8-1:8*2] <= hex_to_ascii(miso_data[7:4]); + uart_text[2*8-1:8*1] <= hex_to_ascii(miso_data[3:0]); + uart_text[7:0] <= 8'h0a; + end + 20: begin + uart_start_send <= 1'b1; + uart_text_length <= 16; + uart_text[30*8-1:8*3] <= "TRP: mtb * 0x"; + uart_text[3*8-1:8*2] <= hex_to_ascii(miso_data[7:4]); + uart_text[2*8-1:8*1] <= hex_to_ascii(miso_data[3:0]); + uart_text[7:0] <= 8'h0a; + end + 21: begin + tras_high = miso_data[3:0]; + end + 22: begin + uart_start_send <= 1'b1; + uart_text_length <= 18; + uart_text[30*8-1:8*4] <= "TRAS: mtb * 0x"; + uart_text[4*8-1:8*3] <= hex_to_ascii(tras_high[3:0]); + uart_text[3*8-1:8*2] <= hex_to_ascii(miso_data[7:4]); + uart_text[2*8-1:8*1] <= hex_to_ascii(miso_data[3:0]); + uart_text[7:0] <= 8'h0a; + end + default: begin + skip_byte <= 1; + end + endcase + state_read_spd <= WAIT_UART_DONE; + end + WAIT_UART_DONE: if(uart_send_done || skip_byte) begin + state_read_spd <= READ_BYTE; + byte_address <= byte_address + 1; + if(byte_address == 63) begin + read_spd_done <= 1'b1; + state_read_spd <= IDLE; + end + skip_byte <= 1'b0; + end + else begin + uart_start_send <= 1'b0; + end endcase end end + // FSM for uart + // uart_text = "Hello" , uart_text_length = 5 + // [5<<3-1 (39):4<<3 (32)] = "H" , [4<<3-1 (31):3<<3(24)] = "e" , [3<<3-1(23):2<<3(16)] = "l" , [2<<3-1(15):1<<3(8)] = "l" , [1<<3-1(7):0<<3(0)] = "o" + always @(posedge i_clk, negedge i_rst_n) begin + if(!i_rst_n) begin + state_uart_send <= IDLE; + uart_text_length_index <= 0; + uart_tx_en <= 0; + uart_send_done <= 0; + uart_tx_data <= 0; + end + else begin + case(state_uart_send) + IDLE: if (uart_start_send) begin // if receive request to send via uart + state_uart_send <= SEND_BYTE; + uart_text_length_index <= uart_text_length-1; + end + else begin + uart_tx_en <= 1'b0; + uart_send_done <= 1'b0; + end + SEND_BYTE: if(!uart_tx_busy) begin // if uart tx is not busy, send character + uart_tx_en <= 1'b1; + uart_tx_data <= uart_text[((uart_text_length_index)<<3) +: 8]; + end + else begin // once busy, go to wait state + state_uart_send <= WAIT_SEND; + uart_tx_en <= 1'b0; + end + WAIT_SEND: if(!uart_tx_busy) begin // if not busy again, then uart is done sending + if(uart_text_length_index != 0) begin // if not yet at 0, go to next character + uart_text_length_index <= uart_text_length_index - 1; + state_uart_send <= SEND_BYTE; + end + else begin // if already at 1, go back to idle + state_uart_send <= IDLE; + uart_send_done <= 1'b1; + end + end + default: state_uart_send <= IDLE; + endcase + end + end + + // Function to convert hex to ASCII + function [7:0] hex_to_ascii; + input [3:0] hex; + begin + if (hex < 4'd10) + hex_to_ascii = hex + 8'd48; // ASCII for '0'-'9' + else + hex_to_ascii = hex + 8'd55; // ASCII for 'A'-'F' + end + endfunction + + // module instantiations i2c_master #(.DATA_WIDTH(8),.REGISTER_WIDTH(8),.ADDRESS_WIDTH(7)) i2c_master_inst( @@ -159,6 +434,20 @@ module spd_reader ( .external_serial_clock (i2c_scl), .slave_nack (slave_nack) ); + + uart_tx #( + .BIT_RATE(115200), + .CLK_HZ(100_000_000), + .PAYLOAD_BITS(8), + .STOP_BITS(1) + ) uart_tx_inst ( + .clk(i_clk), // Top level system clock input + .resetn(i_rst_n), // Asynchronous active low reset. + .uart_txd(uart_tx) , // UART transmit pin. + .uart_tx_busy(uart_tx_busy), // Module busy sending previous item. + .uart_tx_en(uart_tx_en), // Send the data on uart_tx_data + .uart_tx_data(uart_tx_data) // The data to be sent + ); endmodule diff --git a/rtl/spd/spd_reader_top.v b/rtl/spd/spd_reader_top.v index ec8b063..906df0c 100644 --- a/rtl/spd/spd_reader_top.v +++ b/rtl/spd/spd_reader_top.v @@ -9,6 +9,8 @@ module spd_reader_top ( // i2c interface inout wire i2c_scl, inout wire i2c_sda, + // uart tx interface + output wire uart_tx, // fan output wire fan_pwm, //Debug LEDs @@ -58,6 +60,7 @@ module spd_reader_top ( .i_rst_n(i_rst_n && clk_locked), .i2c_scl(i2c_scl), .i2c_sda(i2c_sda), + .uart_tx(uart_tx), .find_i2c_address_done(find_i2c_address_done), .read_spd_done(read_spd_done) ); diff --git a/rtl/spd/uart_tx.v b/rtl/spd/uart_tx.v new file mode 100644 index 0000000..89906d6 --- /dev/null +++ b/rtl/spd/uart_tx.v @@ -0,0 +1,187 @@ + + +// +// Module: uart_tx +// +// Notes: +// - UART transmitter module. +// + +module uart_tx( +input wire clk , // Top level system clock input. +input wire resetn , // Asynchronous active low reset. +output wire uart_txd , // UART transmit pin. +output wire uart_tx_busy, // Module busy sending previous item. +input wire uart_tx_en , // Send the data on uart_tx_data +input wire [PAYLOAD_BITS-1:0] uart_tx_data // The data to be sent +); + +// --------------------------------------------------------------------------- +// External parameters. +// + +// +// Input bit rate of the UART line. +parameter BIT_RATE = 9600; // bits / sec +localparam BIT_P = 1_000_000_000 * 1/BIT_RATE; // nanoseconds + +// +// Clock frequency in hertz. +parameter CLK_HZ = 50_000_000; +localparam CLK_P = 1_000_000_000 * 1/CLK_HZ; // nanoseconds + +// +// Number of data bits recieved per UART packet. +parameter PAYLOAD_BITS = 8; + +// +// Number of stop bits indicating the end of a packet. +parameter STOP_BITS = 1; + +// --------------------------------------------------------------------------- +// Internal parameters. +// + +// +// Number of clock cycles per uart bit. +localparam CYCLES_PER_BIT = BIT_P / CLK_P; + +// +// Size of the registers which store sample counts and bit durations. +localparam COUNT_REG_LEN = 1+$clog2(CYCLES_PER_BIT); + +// --------------------------------------------------------------------------- +// Internal registers. +// + +// +// Internally latched value of the uart_txd line. Helps break long timing +// paths from the logic to the output pins. +reg txd_reg; + +// +// Storage for the serial data to be sent. +reg [PAYLOAD_BITS-1:0] data_to_send; + +// +// Counter for the number of cycles over a packet bit. +reg [COUNT_REG_LEN-1:0] cycle_counter; + +// +// Counter for the number of sent bits of the packet. +reg [3:0] bit_counter; + +// +// Current and next states of the internal FSM. +reg [2:0] fsm_state; +reg [2:0] n_fsm_state; + +localparam FSM_IDLE = 0; +localparam FSM_START= 1; +localparam FSM_SEND = 2; +localparam FSM_STOP = 3; + + +// --------------------------------------------------------------------------- +// FSM next state selection. +// + +assign uart_tx_busy = fsm_state != FSM_IDLE; +assign uart_txd = txd_reg; + +wire next_bit = cycle_counter == CYCLES_PER_BIT; +wire payload_done = bit_counter == PAYLOAD_BITS ; +wire stop_done = bit_counter == STOP_BITS && fsm_state == FSM_STOP; + +// +// Handle picking the next state. +always @(*) begin : p_n_fsm_state + case(fsm_state) + FSM_IDLE : n_fsm_state = uart_tx_en ? FSM_START: FSM_IDLE ; + FSM_START: n_fsm_state = next_bit ? FSM_SEND : FSM_START; + FSM_SEND : n_fsm_state = payload_done ? FSM_STOP : FSM_SEND ; + FSM_STOP : n_fsm_state = stop_done ? FSM_IDLE : FSM_STOP ; + default : n_fsm_state = FSM_IDLE; + endcase +end + +// --------------------------------------------------------------------------- +// Internal register setting and re-setting. +// + +// +// Handle updates to the sent data register. +integer i = 0; +always @(posedge clk) begin : p_data_to_send + if(!resetn) begin + data_to_send <= {PAYLOAD_BITS{1'b0}}; + end else if(fsm_state == FSM_IDLE && uart_tx_en) begin + data_to_send <= uart_tx_data; + end else if(fsm_state == FSM_SEND && next_bit ) begin + for ( i = PAYLOAD_BITS-2; i >= 0; i = i - 1) begin + data_to_send[i] <= data_to_send[i+1]; + end + end +end + + +// +// Increments the bit counter each time a new bit frame is sent. +always @(posedge clk) begin : p_bit_counter + if(!resetn) begin + bit_counter <= 4'b0; + end else if(fsm_state != FSM_SEND && fsm_state != FSM_STOP) begin + bit_counter <= {COUNT_REG_LEN{1'b0}}; + end else if(fsm_state == FSM_SEND && n_fsm_state == FSM_STOP) begin + bit_counter <= {COUNT_REG_LEN{1'b0}}; + end else if(fsm_state == FSM_STOP&& next_bit) begin + bit_counter <= bit_counter + 1'b1; + end else if(fsm_state == FSM_SEND && next_bit) begin + bit_counter <= bit_counter + 1'b1; + end +end + + +// +// Increments the cycle counter when sending. +always @(posedge clk) begin : p_cycle_counter + if(!resetn) begin + cycle_counter <= {COUNT_REG_LEN{1'b0}}; + end else if(next_bit) begin + cycle_counter <= {COUNT_REG_LEN{1'b0}}; + end else if(fsm_state == FSM_START || + fsm_state == FSM_SEND || + fsm_state == FSM_STOP ) begin + cycle_counter <= cycle_counter + 1'b1; + end +end + + +// +// Progresses the next FSM state. +always @(posedge clk) begin : p_fsm_state + if(!resetn) begin + fsm_state <= FSM_IDLE; + end else begin + fsm_state <= n_fsm_state; + end +end + + +// +// Responsible for updating the internal value of the txd_reg. +always @(posedge clk) begin : p_txd_reg + if(!resetn) begin + txd_reg <= 1'b1; + end else if(fsm_state == FSM_IDLE) begin + txd_reg <= 1'b1; + end else if(fsm_state == FSM_START) begin + txd_reg <= 1'b0; + end else if(fsm_state == FSM_SEND) begin + txd_reg <= data_to_send[0]; + end else if(fsm_state == FSM_STOP) begin + txd_reg <= 1'b1; + end +end + +endmodule diff --git a/testbench/spd_tb/i2c_slave.v b/testbench/spd_tb/i2c_slave.v index b185c19..04b5a9d 100644 --- a/testbench/spd_tb/i2c_slave.v +++ b/testbench/spd_tb/i2c_slave.v @@ -86,13 +86,29 @@ module i2c_slave (scl, sda); wire debug = 1'b1; genvar i; - reg [7:0] mem [99:0]; // initiate memory + reg [7:0] mem [22:0]; // initiate memory integer index; initial begin - for (index = 0; index <= 100; index = index + 1) begin - mem[index] = index; // Assign each element with its index value - end +// for (index = 0; index <= 100; index = index + 1) begin +// mem[index] = index; // Assign each element with its index value +// end + mem[0] = 8'h00; + mem[1] = 8'h11; + mem[2] = 8'h0b; + mem[3] = 8'h03; + mem[4] = 8'h05; // BA_BITS = 3 , SDRAM_CAPACITY = 5 + mem[5][5:3] = 3'h3; // ROW_BITS = 15, COL_BITS = 10 + mem[5][2:0] = 3'h1; + mem[7][5:3] = 3'h1; // DUAL_RANK_DIMM = 0 + mem[7][2:0] = 3'h1; // + mem[8] = 8'h03; // BYTE_LANES = 8 + mem[10] = 8'h01; + mem[11] = 8'h08; // mtb = mtb (1/8) + mem[18] = 8'h6e; // TRCD = 13_750 + mem[20] = 8'h6c; //TRP = 13_500 + mem[21] = 8'h01; // TRAS = 35_000 + mem[22] = 8'h18; end reg [7:0] mem_adr; // memory address From d424bcdf4ea0d930ddd9066f4e49cb0aab98ef20 Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Sun, 29 Dec 2024 20:59:57 +0800 Subject: [PATCH 05/12] add option to debug all registers in ILA --- rtl/spd/spd_reader.v | 137 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/rtl/spd/spd_reader.v b/rtl/spd/spd_reader.v index 9a3307b..6b7b922 100644 --- a/rtl/spd/spd_reader.v +++ b/rtl/spd/spd_reader.v @@ -174,7 +174,7 @@ module spd_reader ( end else begin // I2C acks so send via UART the received data state_read_spd <= UART_SEND; - // byte_data[byte_address] <= miso_data; + byte_data[byte_address] <= miso_data; end end else begin @@ -448,6 +448,141 @@ module spd_reader ( .uart_tx_en(uart_tx_en), // Send the data on uart_tx_data .uart_tx_data(uart_tx_data) // The data to be sent ); + + generate + if(1) begin // set to 1 to debug registers in ILA + (* mark_debug = "true" *) wire [7:0] byte_data_0; + (* mark_debug = "true" *) wire [7:0] byte_data_1; + (* mark_debug = "true" *) wire [7:0] byte_data_2; + (* mark_debug = "true" *) wire [7:0] byte_data_3; + (* mark_debug = "true" *) wire [7:0] byte_data_4; + (* mark_debug = "true" *) wire [7:0] byte_data_5; + (* mark_debug = "true" *) wire [7:0] byte_data_6; + (* mark_debug = "true" *) wire [7:0] byte_data_7; + (* mark_debug = "true" *) wire [7:0] byte_data_8; + (* mark_debug = "true" *) wire [7:0] byte_data_9; + (* mark_debug = "true" *) wire [7:0] byte_data_10; + (* mark_debug = "true" *) wire [7:0] byte_data_11; + (* mark_debug = "true" *) wire [7:0] byte_data_12; + (* mark_debug = "true" *) wire [7:0] byte_data_13; + (* mark_debug = "true" *) wire [7:0] byte_data_14; + (* mark_debug = "true" *) wire [7:0] byte_data_15; + (* mark_debug = "true" *) wire [7:0] byte_data_16; + (* mark_debug = "true" *) wire [7:0] byte_data_17; + (* mark_debug = "true" *) wire [7:0] byte_data_18; + (* mark_debug = "true" *) wire [7:0] byte_data_19; + (* mark_debug = "true" *) wire [7:0] byte_data_20; + (* mark_debug = "true" *) wire [7:0] byte_data_21; + (* mark_debug = "true" *) wire [7:0] byte_data_22; + (* mark_debug = "true" *) wire [7:0] byte_data_23; + (* mark_debug = "true" *) wire [7:0] byte_data_24; + (* mark_debug = "true" *) wire [7:0] byte_data_25; + (* mark_debug = "true" *) wire [7:0] byte_data_26; + (* mark_debug = "true" *) wire [7:0] byte_data_27; + (* mark_debug = "true" *) wire [7:0] byte_data_28; + (* mark_debug = "true" *) wire [7:0] byte_data_29; + (* mark_debug = "true" *) wire [7:0] byte_data_30; + (* mark_debug = "true" *) wire [7:0] byte_data_31; + (* mark_debug = "true" *) wire [7:0] byte_data_32; + (* mark_debug = "true" *) wire [7:0] byte_data_33; + (* mark_debug = "true" *) wire [7:0] byte_data_34; + (* mark_debug = "true" *) wire [7:0] byte_data_35; + (* mark_debug = "true" *) wire [7:0] byte_data_36; + (* mark_debug = "true" *) wire [7:0] byte_data_37; + (* mark_debug = "true" *) wire [7:0] byte_data_38; + (* mark_debug = "true" *) wire [7:0] byte_data_39; + (* mark_debug = "true" *) wire [7:0] byte_data_40; + (* mark_debug = "true" *) wire [7:0] byte_data_41; + (* mark_debug = "true" *) wire [7:0] byte_data_42; + (* mark_debug = "true" *) wire [7:0] byte_data_43; + (* mark_debug = "true" *) wire [7:0] byte_data_44; + (* mark_debug = "true" *) wire [7:0] byte_data_45; + (* mark_debug = "true" *) wire [7:0] byte_data_46; + (* mark_debug = "true" *) wire [7:0] byte_data_47; + (* mark_debug = "true" *) wire [7:0] byte_data_48; + (* mark_debug = "true" *) wire [7:0] byte_data_49; + (* mark_debug = "true" *) wire [7:0] byte_data_50; + (* mark_debug = "true" *) wire [7:0] byte_data_51; + (* mark_debug = "true" *) wire [7:0] byte_data_52; + (* mark_debug = "true" *) wire [7:0] byte_data_53; + (* mark_debug = "true" *) wire [7:0] byte_data_54; + (* mark_debug = "true" *) wire [7:0] byte_data_55; + (* mark_debug = "true" *) wire [7:0] byte_data_56; + (* mark_debug = "true" *) wire [7:0] byte_data_57; + (* mark_debug = "true" *) wire [7:0] byte_data_58; + (* mark_debug = "true" *) wire [7:0] byte_data_59; + (* mark_debug = "true" *) wire [7:0] byte_data_60; + (* mark_debug = "true" *) wire [7:0] byte_data_61; + (* mark_debug = "true" *) wire [7:0] byte_data_62; + (* mark_debug = "true" *) wire [7:0] byte_data_63; + + // Assign each wire to its respective array index + assign byte_data_0 = byte_data[0]; + assign byte_data_1 = byte_data[1]; + assign byte_data_2 = byte_data[2]; + assign byte_data_3 = byte_data[3]; + assign byte_data_4 = byte_data[4]; + assign byte_data_5 = byte_data[5]; + assign byte_data_6 = byte_data[6]; + assign byte_data_7 = byte_data[7]; + assign byte_data_8 = byte_data[8]; + assign byte_data_9 = byte_data[9]; + assign byte_data_10 = byte_data[10]; + assign byte_data_11 = byte_data[11]; + assign byte_data_12 = byte_data[12]; + assign byte_data_13 = byte_data[13]; + assign byte_data_14 = byte_data[14]; + assign byte_data_15 = byte_data[15]; + assign byte_data_16 = byte_data[16]; + assign byte_data_17 = byte_data[17]; + assign byte_data_18 = byte_data[18]; + assign byte_data_19 = byte_data[19]; + assign byte_data_20 = byte_data[20]; + assign byte_data_21 = byte_data[21]; + assign byte_data_22 = byte_data[22]; + assign byte_data_23 = byte_data[23]; + assign byte_data_24 = byte_data[24]; + assign byte_data_25 = byte_data[25]; + assign byte_data_26 = byte_data[26]; + assign byte_data_27 = byte_data[27]; + assign byte_data_28 = byte_data[28]; + assign byte_data_29 = byte_data[29]; + assign byte_data_30 = byte_data[30]; + assign byte_data_31 = byte_data[31]; + assign byte_data_32 = byte_data[32]; + assign byte_data_33 = byte_data[33]; + assign byte_data_34 = byte_data[34]; + assign byte_data_35 = byte_data[35]; + assign byte_data_36 = byte_data[36]; + assign byte_data_37 = byte_data[37]; + assign byte_data_38 = byte_data[38]; + assign byte_data_39 = byte_data[39]; + assign byte_data_40 = byte_data[40]; + assign byte_data_41 = byte_data[41]; + assign byte_data_42 = byte_data[42]; + assign byte_data_43 = byte_data[43]; + assign byte_data_44 = byte_data[44]; + assign byte_data_45 = byte_data[45]; + assign byte_data_46 = byte_data[46]; + assign byte_data_47 = byte_data[47]; + assign byte_data_48 = byte_data[48]; + assign byte_data_49 = byte_data[49]; + assign byte_data_50 = byte_data[50]; + assign byte_data_51 = byte_data[51]; + assign byte_data_52 = byte_data[52]; + assign byte_data_53 = byte_data[53]; + assign byte_data_54 = byte_data[54]; + assign byte_data_55 = byte_data[55]; + assign byte_data_56 = byte_data[56]; + assign byte_data_57 = byte_data[57]; + assign byte_data_58 = byte_data[58]; + assign byte_data_59 = byte_data[59]; + assign byte_data_60 = byte_data[60]; + assign byte_data_61 = byte_data[61]; + assign byte_data_62 = byte_data[62]; + assign byte_data_63 = byte_data[63]; + end + endgenerate endmodule From f636dcbd2e24baad9dc4cb76d9d6099e936ae4cc Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Sun, 29 Dec 2024 21:22:52 +0800 Subject: [PATCH 06/12] bring all timing parameters to top --- .../alinx_ax7325b/ax7325b_spd_reader.xdc | 2 +- rtl/ddr3_controller.v | 71 ++++++++++--------- rtl/ddr3_top.v | 13 +++- 3 files changed, 49 insertions(+), 37 deletions(-) diff --git a/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc b/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc index 2305b1f..b6d7ec0 100644 --- a/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc +++ b/example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc @@ -33,5 +33,5 @@ set_property PACKAGE_PIN E18 [get_ports {led[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}] ##############uart define########################### -set_property IOSTANDARD LVCMOS33 [get_ports uart_tx] +set_property IOSTANDARD LVCMOS25 [get_ports uart_tx] set_property PACKAGE_PIN AK26 [get_ports uart_tx] \ No newline at end of file diff --git a/rtl/ddr3_controller.v b/rtl/ddr3_controller.v index b090d86..4768686 100644 --- a/rtl/ddr3_controller.v +++ b/rtl/ddr3_controller.v @@ -46,11 +46,6 @@ //`define DDR3_1333_9_9_9 //`define DDR3_1066_7_7_7 // -//DDR3 Capacity -`define RAM_8Gb -//`define RAM_2Gb -//`define RAM_4Gb -//`define RAM_8Gb module ddr3_controller #( parameter integer CONTROLLER_CLK_PERIOD = 10_000, //ps, clock period of the controller interface @@ -64,6 +59,12 @@ module ddr3_controller #( WB2_ADDR_BITS = 7, //width of 2nd wishbone address bus WB2_DATA_BITS = 32, //width of 2nd wishbone data bus DUAL_RANK_DIMM = 0, // enable dual rank DIMM (1 = enable, 0 = disable) + // DDR3 timing parameter values + parameter SPEED_BIN = 3, // 0 = Use top-level parameters , 1 = DDR3-1066 (7-7-7) , 2 = DR3-1333 (9-9-9) , 3 = DDR3-1600 (11-11-11) + SDRAM_CAPACITY = 5, // 0 = 256Mb, 1 = 512Mb, 2 = 1Gb, 3 = 2Gb, 4 = 4Gb, 5 = 8Gb, 6 = 16Gb + TRCD = 13_750, // ps Active to Read/Write command time (only used if SPEED_BIN = 0) + TRP = 13_750, // ps Precharge command period (only used if SPEED_BIN = 0) + TRAS = 35_000, // ps ACT to PRE command period (only used if SPEED_BIN = 0) parameter[0:0] MICRON_SIM = 0, //enable faster simulation for micron ddr3 model (shorten POWER_ON_RESET_HIGH and INITIAL_CKE_LOW) ODELAY_SUPPORTED = 1, //set to 1 when ODELAYE2 is supported SECOND_WISHBONE = 0, //set to 1 if 2nd wishbone is needed @@ -208,36 +209,34 @@ module ddr3_controller #( /********************************************************** Timing Parameters ***********************************************************************************/ localparam DELAY_SLOT_WIDTH = 19; //Bitwidth of the delay slot and mode register slot on the reset/refresh rom will be at the same size as the Mode Register - localparam POWER_ON_RESET_HIGH = 200_000_000; // 200_000_000 ps (200 us) reset must be active at initialization - localparam INITIAL_CKE_LOW = 500_000_000; // 500_000_000 ps (500 us) cke must be low before activating - `ifdef DDR3_1600_11_11_11 //DDR3-1600 (11-11-11) speed bin - localparam tRCD = 13_750; // ps Active to Read/Write command time - localparam tRP = 13_750; // ps Precharge command period - localparam tRAS = 35_000; // ps ACT to PRE command period - `elsif DDR3_1333_9_9_9 //DDR3-1333 (9-9-9) speed bin - localparam tRCD = 13_500; // ps Active to Read/Write command time - localparam tRP = 13_500; // ps Precharge command period - localparam tRAS = 36_000; // ps ACT to PRE command period - `elsif DDR3_1066_7_7_7 //DDR3-1066 (7-7-7) speed bin - localparam tRCD = 13_125; // ps Active to Read/Write command time - localparam tRP = 13_125; // ps Precharge command period - localparam tRAS = 37_500; // ps ACT to PRE command period - `else - "Throw an error here if speed bin is not recognized (or not defined)" - `endif - - `ifdef RAM_1Gb - localparam tRFC = 110_000; // ps Refresh command to ACT or REF - `elsif RAM_2Gb - localparam tRFC = 160_000; // ps Refresh command to ACT or REF - `elsif RAM_4Gb - localparam tRFC = 300_000; // ps Refresh command to ACT or REF - `elsif RAM_8Gb - localparam tRFC = 350_000; // ps Refresh command to ACT or REF - `else - "Throw an error here if capacity is not recognized (or not defined)" - `endif + localparam POWER_ON_RESET_HIGH = 200_000_000; // 200_000_000 ps (200 us) reset must be active at initialization + localparam INITIAL_CKE_LOW = 500_000_000; // 500_000_000 ps (500 us) cke must be low before activating + // ps Active to Read/Write command time + localparam tRCD = (SPEED_BIN == 0) ? TRCD : // use top-level parameters + (SPEED_BIN == 1) ? 13_750 : // DDR3-1066 (7-7-7) + (SPEED_BIN == 2) ? 13_500 : // DDR3-1333 (9-9-9) + (SPEED_BIN == 3) ? 13_750 : 13_750; // DDR3-1600 (11-11-11) + + // ps Precharge command period + localparam tRP = (SPEED_BIN == 0) ? TRP : // use top-level parameters + (SPEED_BIN == 1) ? 13_750 : // DDR3-1066 (7-7-7) + (SPEED_BIN == 2) ? 13_500 : // DDR3-1333 (9-9-9) + (SPEED_BIN == 3) ? 13_750 : 13_750; // DDR3-1600 (11-11-11) + + // ps ACT to PRE command period + localparam tRAS = (SPEED_BIN == 0) ? TRAS : // use top-level parameters + (SPEED_BIN == 1) ? 35_000 : // DDR3-1066 (7-7-7) + (SPEED_BIN == 2) ? 36_000 : // DDR3-1333 (9-9-9) + (SPEED_BIN == 3) ? 35_000 : 35_000; // DDR3-1600 (11-11-11) + + // ps Refresh command to ACT or REF + localparam tRFC = ((SDRAM_CAPACITY == 4'b0000) || (SDRAM_CAPACITY == 4'b0001)) ? 90_000 : // 256Mb, 512Mb + (SDRAM_CAPACITY == 4'b0010) ? 110_000 : // 1Gb + (SDRAM_CAPACITY == 4'b0011) ? 160_000 : // 2Gb + (SDRAM_CAPACITY == 4'b0100) ? 300_000 : // 4Gb + (SDRAM_CAPACITY == 4'b0101) ? 350_000 : 350_000; // 8Gb + localparam tREFI = 7_800_000; //ps Average periodic refresh interval localparam tXPR = max(5*DDR3_CLK_PERIOD, tRFC+10_000); // ps Exit Reset from CKE HIGH to a valid command localparam tWR = 15_000; // ps Write Recovery Time @@ -3160,7 +3159,9 @@ ALTERNATE_WRITE_READ: if(!o_wb_stall_calib) begin // find anticipate activate command slot number if(CL_nCK > CWL_nCK) slot_number[1:0] = read_slot[1:0]; else slot_number[1:0] = write_slot[1:0]; - delay = ps_to_nCK(tRCD); + + // delay = ps_to_nCK(tRCD); + delay = $rtoi( $ceil( tRCD*1.0/ DDR3_CLK_PERIOD ) ); for(slot_number = slot_number; delay != 0; delay = delay - 1) begin slot_number[1:0] = slot_number[1:0] - 1'b1; end diff --git a/rtl/ddr3_top.v b/rtl/ddr3_top.v index 63d6ad8..7d26733 100644 --- a/rtl/ddr3_top.v +++ b/rtl/ddr3_top.v @@ -41,6 +41,12 @@ module ddr3_top #( WB2_ADDR_BITS = 7, //width of 2nd wishbone address bus WB2_DATA_BITS = 32, //width of 2nd wishbone data bus DUAL_RANK_DIMM = 0, // enable dual rank DIMM (1 = enable, 0 = disable) + // DDR3 timing parameter values + parameter SPEED_BIN = 3, // 0 = Use top-level parameters , 1 = DDR3-1066 (7-7-7) , 2 = DR3-1333 (9-9-9) , 3 = DDR3-1600 (11-11-11) + SDRAM_CAPACITY = 5, // 0 = 256Mb, 1 = 512Mb, 2 = 1Gb, 3 = 2Gb, 4 = 4Gb, 5 = 8Gb, 6 = 16Gb + TRCD = 13_750, // ps Active to Read/Write command time (only used if SPEED_BIN = 0) + TRP = 13_750, // ps Precharge command period (only used if SPEED_BIN = 0) + TRAS = 35_000, // ps ACT to PRE command period (only used if SPEED_BIN = 0) parameter[0:0] MICRON_SIM = 0, //enable faster simulation for micron ddr3 model (shorten POWER_ON_RESET_HIGH and INITIAL_CKE_LOW) ODELAY_SUPPORTED = 0, //set to 1 when ODELAYE2 is supported SECOND_WISHBONE = 0, //set to 1 if 2nd wishbone for debugging is needed @@ -256,7 +262,12 @@ ddr3_top #( .SKIP_INTERNAL_TEST(SKIP_INTERNAL_TEST), // skip built-in self test (would require >2 seconds of internal test right after calibration) .DIC(DIC), //Output Driver Impedance Control (2'b00 = RZQ/6, 2'b01 = RZQ/7, RZQ = 240ohms) .RTT_NOM(RTT_NOM), //RTT Nominal (3'b000 = disabled, 3'b001 = RZQ/4, 3'b010 = RZQ/2 , 3'b011 = RZQ/6, RZQ = 240ohms) - .DUAL_RANK_DIMM(DUAL_RANK_DIMM) // enable dual rank DIMM (1 = enable, 0 = disable) + .DUAL_RANK_DIMM(DUAL_RANK_DIMM), // enable dual rank DIMM (1 = enable, 0 = disable) + .SPEED_BIN(SPEED_BIN), // 0 = Use top-level parameters , 1 = DDR3-1066 (7-7-7) , 2 = DR3-1333 (9-9-9) , 3 = DDR3-1600 (11-11-11) + .SDRAM_CAPACITY(SDRAM_CAPACITY), // 0 = 256Mb, 1 = 512Mb, 2 = 1Gb, 3 = 2Gb, 4 = 4Gb, 5 = 8Gb, 6 = 16Gb + .TRCD(TRCD), // ps Active to Read/Write command time (only used if SPEED_BIN = 0) + .TRP(TRP), // ps Precharge command period (only used if SPEED_BIN = 0) + .TRAS(TRAS) // ps ACT to PRE command period (only used if SPEED_BIN = 0) ) ddr3_controller_inst ( .i_controller_clk(i_controller_clk), //i_controller_clk has period of CONTROLLER_CLK_PERIOD .i_rst_n(i_rst_n), //200MHz input clock From 6ead81ba48fcd11c5196369e3f4d32d4359ae2d4 Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Sun, 29 Dec 2024 21:33:58 +0800 Subject: [PATCH 07/12] fixed stuck on addr 21, and fixed dual rank --- rtl/spd/spd_reader.v | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rtl/spd/spd_reader.v b/rtl/spd/spd_reader.v index 6b7b922..25ecf7c 100644 --- a/rtl/spd/spd_reader.v +++ b/rtl/spd/spd_reader.v @@ -273,7 +273,7 @@ module spd_reader ( 7: begin uart_start_send <= 1'b1; uart_text_length <= 18; - if(miso_data[5:3] == 0) begin + if(miso_data[5:3] == 1) begin uart_text[30*8-1:8] <= "DUAL_RANK_DIMM: 1"; end else begin @@ -327,11 +327,14 @@ module spd_reader ( end 21: begin tras_high = miso_data[3:0]; + uart_start_send <= 1'b1; + uart_text_length <= 1; + uart_text[7:0] <= "T"; end 22: begin uart_start_send <= 1'b1; - uart_text_length <= 18; - uart_text[30*8-1:8*4] <= "TRAS: mtb * 0x"; + uart_text_length <= 17; + uart_text[30*8-1:8*4] <= "RAS: mtb * 0x"; uart_text[4*8-1:8*3] <= hex_to_ascii(tras_high[3:0]); uart_text[3*8-1:8*2] <= hex_to_ascii(miso_data[7:4]); uart_text[2*8-1:8*1] <= hex_to_ascii(miso_data[3:0]); From ab1a5b9f81f9a6ed83ad0500849cce68c8cde830 Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Sun, 29 Dec 2024 21:40:53 +0800 Subject: [PATCH 08/12] make spd read display better --- rtl/spd/spd_reader.v | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rtl/spd/spd_reader.v b/rtl/spd/spd_reader.v index 25ecf7c..1ad962e 100644 --- a/rtl/spd/spd_reader.v +++ b/rtl/spd/spd_reader.v @@ -184,8 +184,9 @@ module spd_reader ( case(byte_address) 0: begin uart_start_send <= 1'b1; - uart_text_length <= 15; - uart_text[30*8-1:8] <= "START SPD READ"; + uart_text_length <= 29; + uart_text[30*8-1:8*29] <= 8'h0a; + uart_text[29*8-1:8] <= "------ START SPD READ ------"; uart_text[7:0] <= 8'h0a; end 1: begin From 1afd06542f499a816dd8fb22e155f20b169a47b6 Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Sun, 29 Dec 2024 21:58:26 +0800 Subject: [PATCH 09/12] make mtb mcp to meet timing --- rtl/spd/spd_reader.v | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/rtl/spd/spd_reader.v b/rtl/spd/spd_reader.v index 1ad962e..08765db 100644 --- a/rtl/spd/spd_reader.v +++ b/rtl/spd/spd_reader.v @@ -60,7 +60,8 @@ module spd_reader ( reg[9:0] uart_text_length,uart_text_length_index; reg uart_send_done; reg skip_byte; - reg[7:0] mtb_dividend, mtb; + reg[7:0] mtb_dividend, mtb_divisor; + wire[7:0] mtb; reg[3:0] tras_high; // initialize in case fpga starts with no reset @@ -85,7 +86,7 @@ module spd_reader ( uart_text = {(30*8){1'b0}}; skip_byte = 0; mtb_dividend = 0; - mtb = 0; + mtb_divisor = 0; tras_high = 0; end @@ -107,7 +108,7 @@ module spd_reader ( uart_text = {(30*8){1'b0}}; skip_byte <= 1'b0; mtb_dividend <= 0; - mtb <= 0; + mtb_divisor <= 0; tras_high <= 0; end else begin @@ -301,10 +302,15 @@ module spd_reader ( uart_text[7:0] <= 8'h0a; end 11: begin - mtb = mtb_dividend*1000/miso_data; + mtb_divisor <= miso_data; uart_start_send <= 1'b1; - uart_text_length <= 15; - uart_text[30*8-1:8*8] <= "mtb: 0x"; + uart_text_length <= 1; + uart_text[7:0] <= "m"; + end + 12: begin // give time for mtb to be computer + uart_start_send <= 1'b1; + uart_text_length <= 14; + uart_text[30*8-1:8*8] <= "tb: 0x"; uart_text[8*8-1:8*7] <= hex_to_ascii(mtb[7:4]); uart_text[7*8-1:8*6] <= hex_to_ascii(mtb[3:0]); uart_text[6*8-1:8*1] <= " (ps)"; @@ -362,6 +368,7 @@ module spd_reader ( endcase end end + assign mtb = mtb_dividend*1000/miso_data; // mtb is MCP (multicycle path) to give time for multiplication to be done // FSM for uart // uart_text = "Hello" , uart_text_length = 5 From c11d90440e795d48b5ebf768d2d4cccbf49ac36c Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Sun, 29 Dec 2024 22:11:26 +0800 Subject: [PATCH 10/12] fixed mtb computation --- rtl/spd/spd_reader.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtl/spd/spd_reader.v b/rtl/spd/spd_reader.v index 08765db..7187753 100644 --- a/rtl/spd/spd_reader.v +++ b/rtl/spd/spd_reader.v @@ -368,7 +368,7 @@ module spd_reader ( endcase end end - assign mtb = mtb_dividend*1000/miso_data; // mtb is MCP (multicycle path) to give time for multiplication to be done + assign mtb = mtb_dividend*1000/mtb_divisor; // mtb is MCP (multicycle path) to give time for multiplication to be done // FSM for uart // uart_text = "Hello" , uart_text_length = 5 From fbb3b65aafc905b3392f5f8b6771fe247fecc111 Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Thu, 2 Jan 2025 13:02:05 +0800 Subject: [PATCH 11/12] added waveform for spd reader testbench --- testbench/spd_tb/spd_reader_tb_behav.wcfg | 168 ++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 testbench/spd_tb/spd_reader_tb_behav.wcfg diff --git a/testbench/spd_tb/spd_reader_tb_behav.wcfg b/testbench/spd_tb/spd_reader_tb_behav.wcfg new file mode 100644 index 0000000..3ee56d3 --- /dev/null +++ b/testbench/spd_tb/spd_reader_tb_behav.wcfg @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + spd_reader_top + label + + + sys_clk_p + sys_clk_p + + + sys_clk_n + sys_clk_n + + + i_rst_n + i_rst_n + + + i2c_scl + i2c_scl + + + i2c_sda + i2c_sda + + + fan_pwm + fan_pwm + + + clk_locked + clk_locked + + + main_clk_100 + main_clk_100 + + + sys_clk + sys_clk + + + spd_reader + label + + + i_clk + i_clk + + + i_rst_n + i_rst_n + + + i2c_scl + i2c_scl + + + i2c_sda + i2c_sda + + + state_find_i2c_address[1:0] + state_find_i2c_address[1:0] + + + find_i2c_address_done + find_i2c_address_done + + + i2c_address[6:0] + i2c_address[6:0] + + + enable + enable + + + read_write + read_write + + + register_address[7:0] + register_address[7:0] + + + device_address[6:0] + device_address[6:0] + + + miso_data[7:0] + miso_data[7:0] + + + busy + busy + + + slave_nack + slave_nack + + + read_spd_done + read_spd_done + + + nack_unexpected_err + nack_unexpected_err + + + state_read_spd[2:0] + state_read_spd[2:0] + + + byte_address[5:0] + byte_address[5:0] + + + byte_data[63:0][7:0] + byte_data[63:0][7:0] + + + + I2C_ADDRESS[6:0] + I2C_ADDRESS[6:0] + + + IDLE[31:0] + IDLE[31:0] + + + READ_ADDRESS[31:0] + READ_ADDRESS[31:0] + + + READ_BYTE[31:0] + READ_BYTE[31:0] + + + WAIT_ACK[31:0] + WAIT_ACK[31:0] + + From d8cb6d16d9cac5efcc5619f1828cf2ec999f0f03 Mon Sep 17 00:00:00 2001 From: AngeloJacobo Date: Thu, 2 Jan 2025 13:18:42 +0800 Subject: [PATCH 12/12] update copyright date --- example_demo/alinx_ax7103b/ax7103_ddr3.v | 2 +- example_demo/alinx_ax7325b/ax7325b_ddr3.v | 2 +- example_demo/arty_s7/arty_ddr3.v | 2 +- .../enclustra_kx2_st1/enclustra_ddr3.v | 2 +- example_demo/qmtech_wukong/wukong_ddr3.v | 2 +- example_demo/sechzig_mx2/sechzig_mx2_ddr3.v | 2 +- rtl/ddr3_controller.v | 2 +- rtl/ddr3_phy.v | 2 +- rtl/ddr3_top.v | 2 +- rtl/spd/spd_reader.v | 40 ++++++++++++++----- rtl/spd/spd_reader_top.v | 26 ++++++++++++ 11 files changed, 66 insertions(+), 18 deletions(-) diff --git a/example_demo/alinx_ax7103b/ax7103_ddr3.v b/example_demo/alinx_ax7103b/ax7103_ddr3.v index dd58b72..a260f41 100644 --- a/example_demo/alinx_ax7103b/ax7103_ddr3.v +++ b/example_demo/alinx_ax7103b/ax7103_ddr3.v @@ -16,7 +16,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2023-2024 Angelo Jacobo +// Copyright (C) 2023-2025 Angelo Jacobo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/example_demo/alinx_ax7325b/ax7325b_ddr3.v b/example_demo/alinx_ax7325b/ax7325b_ddr3.v index 0773307..7f2d10d 100644 --- a/example_demo/alinx_ax7325b/ax7325b_ddr3.v +++ b/example_demo/alinx_ax7325b/ax7325b_ddr3.v @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2023-2024 Angelo Jacobo +// Copyright (C) 2023-2025 Angelo Jacobo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/example_demo/arty_s7/arty_ddr3.v b/example_demo/arty_s7/arty_ddr3.v index d654e5f..ef299c3 100644 --- a/example_demo/arty_s7/arty_ddr3.v +++ b/example_demo/arty_s7/arty_ddr3.v @@ -16,7 +16,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2023-2024 Angelo Jacobo +// Copyright (C) 2023-2025 Angelo Jacobo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/example_demo/enclustra_kx2_st1/enclustra_ddr3.v b/example_demo/enclustra_kx2_st1/enclustra_ddr3.v index 0574947..1df9e34 100644 --- a/example_demo/enclustra_kx2_st1/enclustra_ddr3.v +++ b/example_demo/enclustra_kx2_st1/enclustra_ddr3.v @@ -16,7 +16,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2023-2024 Angelo Jacobo +// Copyright (C) 2023-2025 Angelo Jacobo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/example_demo/qmtech_wukong/wukong_ddr3.v b/example_demo/qmtech_wukong/wukong_ddr3.v index df9ddf5..0fc69a1 100644 --- a/example_demo/qmtech_wukong/wukong_ddr3.v +++ b/example_demo/qmtech_wukong/wukong_ddr3.v @@ -16,7 +16,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2023-2024 Angelo Jacobo +// Copyright (C) 2023-2025 Angelo Jacobo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/example_demo/sechzig_mx2/sechzig_mx2_ddr3.v b/example_demo/sechzig_mx2/sechzig_mx2_ddr3.v index de857b6..6219492 100644 --- a/example_demo/sechzig_mx2/sechzig_mx2_ddr3.v +++ b/example_demo/sechzig_mx2/sechzig_mx2_ddr3.v @@ -16,7 +16,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2023-2024 Angelo Jacobo +// Copyright (C) 2023-2025 Angelo Jacobo // Copyright (C) 2024 Lone Dynamics Corporation // // This program is free software: you can redistribute it and/or modify diff --git a/rtl/ddr3_controller.v b/rtl/ddr3_controller.v index 4768686..ede1081 100644 --- a/rtl/ddr3_controller.v +++ b/rtl/ddr3_controller.v @@ -17,7 +17,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2023-2024 Angelo Jacobo +// Copyright (C) 2023-2025 Angelo Jacobo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/rtl/ddr3_phy.v b/rtl/ddr3_phy.v index 5211865..927098e 100644 --- a/rtl/ddr3_phy.v +++ b/rtl/ddr3_phy.v @@ -11,7 +11,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2023-2024 Angelo Jacobo +// Copyright (C) 2023-2025 Angelo Jacobo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/rtl/ddr3_top.v b/rtl/ddr3_top.v index 7d26733..ca7c550 100644 --- a/rtl/ddr3_top.v +++ b/rtl/ddr3_top.v @@ -10,7 +10,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2023-2024 Angelo Jacobo +// Copyright (C) 2023-2025 Angelo Jacobo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/rtl/spd/spd_reader.v b/rtl/spd/spd_reader.v index 7187753..f493db9 100644 --- a/rtl/spd/spd_reader.v +++ b/rtl/spd/spd_reader.v @@ -1,3 +1,34 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: spd_reader.v +// Project: Serial Presence Detect (SPD) Reader for UberDDR3 +// +// Purpose: Communicates with the SPD (Serial Presence Detect) chip on the +// DDR3 DIMM using the I2C protocol to retrieve essential timing parameters +// and configuration details required by UberDDR3. The SPD report will be +// reported via the UART line. +// +// Engineer: Angelo C. Jacobo +// +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023-2025 Angelo Jacobo +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//////////////////////////////////////////////////////////////////////////////// + `default_nettype none `timescale 1ns / 1ps @@ -15,15 +46,6 @@ module spd_reader ( output wire uart_tx ); - // byte 2: DRAM Device Type (DDR3 SDRAM = 0x0B) - // byte 3: Module Type (SO-DIMM = 0x03) - // byte 4: SDRAM Density and Banks ([6:4] = BA_BITS, [3:0] = SDRAM capacity) - // byte 5: SDRAM Addressing ([5:3] = Row Addr , [2:0] = Column Addr) - // byte 7: Module Organization ([5:3] = Ranks , Device Width = [2:0]) - // byte 8: Module Memory Bus Width ([2:0] = Bus Width) - // byte 10,11: Medium Timebase (MTB) Dividend (0x01), Medium Timebase (MTB) Divisor (0x08 = 0.125ns , 0x10 = 0.0625ns) (tXX = tXX(MTB) * MTB) - // byte 12: SDRAM Minimum Cycle Time tCK - localparam I2C_ADDRESS = 7'h30; localparam IDLE = 0, READ_ADDRESS = 1, diff --git a/rtl/spd/spd_reader_top.v b/rtl/spd/spd_reader_top.v index 906df0c..b7647b4 100644 --- a/rtl/spd/spd_reader_top.v +++ b/rtl/spd/spd_reader_top.v @@ -1,3 +1,29 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: spd_reader_top.v +// Project: Top-level file for SPD reader (intended for AX7325B FPGA board) +// +// Engineer: Angelo C. Jacobo +// +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2023-2025 Angelo Jacobo +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//////////////////////////////////////////////////////////////////////////////// + `default_nettype none `timescale 1ns / 1ps