Merge pull request #20 from AngeloJacobo/spd_reader
SPD reader for DDR3 DIMM
This commit is contained in:
commit
ae3cb666e6
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
############## 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 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 LVCMOS33 [get_ports {led[0]}]
|
||||
set_property PACKAGE_PIN C19 [get_ports {led[1]}]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
|
||||
set_property PACKAGE_PIN B19 [get_ports {led[2]}]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
|
||||
set_property PACKAGE_PIN E18 [get_ports {led[3]}]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
|
||||
|
||||
##############uart define###########################
|
||||
set_property IOSTANDARD LVCMOS25 [get_ports uart_tx]
|
||||
set_property PACKAGE_PIN AK26 [get_ports uart_tx]
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -210,33 +211,31 @@ module ddr3_controller #(
|
|||
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
|
||||
// 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,621 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
`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,
|
||||
// state of spd reader
|
||||
(* mark_debug = "true" *) output reg find_i2c_address_done,
|
||||
(* mark_debug = "true" *) output reg read_spd_done,
|
||||
// uart interface
|
||||
output wire uart_tx
|
||||
);
|
||||
|
||||
localparam I2C_ADDRESS = 7'h30;
|
||||
localparam IDLE = 0,
|
||||
READ_ADDRESS = 1,
|
||||
READ_BYTE = 1,
|
||||
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;
|
||||
|
||||
// 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;
|
||||
(* 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];
|
||||
// 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_divisor;
|
||||
wire[7:0] mtb;
|
||||
reg[3:0] tras_high;
|
||||
|
||||
// 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;
|
||||
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_divisor = 0;
|
||||
tras_high = 0;
|
||||
end
|
||||
|
||||
// FSM for I2C
|
||||
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;
|
||||
read_spd_done <= 1'b0;
|
||||
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_divisor <= 0;
|
||||
tras_high <= 0;
|
||||
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'h01; // start brute force find i2c address from 1 (0 might be general call)
|
||||
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!
|
||||
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
|
||||
|
||||
// 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
|
||||
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;
|
||||
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 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 <= 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
|
||||
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] == 1) 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_divisor <= miso_data;
|
||||
uart_start_send <= 1'b1;
|
||||
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)";
|
||||
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];
|
||||
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 <= 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]);
|
||||
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
|
||||
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
|
||||
// [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(
|
||||
.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)
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
`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,
|
||||
// uart tx interface
|
||||
output wire uart_tx,
|
||||
// fan
|
||||
output wire fan_pwm,
|
||||
//Debug LEDs
|
||||
output wire[3:0] led
|
||||
);
|
||||
|
||||
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
|
||||
//===========================================================================
|
||||
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),
|
||||
.uart_tx(uart_tx),
|
||||
.find_i2c_address_done(find_i2c_address_done),
|
||||
.read_spd_done(read_spd_done)
|
||||
);
|
||||
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,379 @@
|
|||
`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'b000_0100;
|
||||
|
||||
//
|
||||
// input && outpus
|
||||
//
|
||||
input scl;
|
||||
inout sda;
|
||||
|
||||
//
|
||||
// Variable declaration
|
||||
//
|
||||
wire debug = 1'b1;
|
||||
genvar i;
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
`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)
|
||||
);
|
||||
|
||||
initial begin
|
||||
clk = 0;
|
||||
rst_n = 0;
|
||||
#100;
|
||||
rst_n = 1;
|
||||
wait(DUT.spd_reader_inst.read_spd_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
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<wave_config>
|
||||
<wave_state>
|
||||
</wave_state>
|
||||
<db_ref_list>
|
||||
<db_ref path="spd_reader_tb_behav.wdb" id="1">
|
||||
<top_modules>
|
||||
<top_module name="glbl" />
|
||||
<top_module name="spd_reader_tb" />
|
||||
</top_modules>
|
||||
</db_ref>
|
||||
</db_ref_list>
|
||||
<zoom_setting>
|
||||
<ZoomStartTime time="0.004104000 ms"></ZoomStartTime>
|
||||
<ZoomEndTime time="5.854104001 ms"></ZoomEndTime>
|
||||
<Cursor1Time time="0.284104000 ms"></Cursor1Time>
|
||||
</zoom_setting>
|
||||
<column_width_setting>
|
||||
<NameColumnWidth column_width="238"></NameColumnWidth>
|
||||
<ValueColumnWidth column_width="104"></ValueColumnWidth>
|
||||
</column_width_setting>
|
||||
<WVObjectSize size="35" />
|
||||
<wave_markers>
|
||||
<marker label="" time="6062762500" />
|
||||
<marker label="" time="6172762500" />
|
||||
</wave_markers>
|
||||
<wvobject fp_name="divider37" type="divider">
|
||||
<obj_property name="label">spd_reader_top</obj_property>
|
||||
<obj_property name="DisplayName">label</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/sys_clk_p">
|
||||
<obj_property name="ElementShortName">sys_clk_p</obj_property>
|
||||
<obj_property name="ObjectShortName">sys_clk_p</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/sys_clk_n">
|
||||
<obj_property name="ElementShortName">sys_clk_n</obj_property>
|
||||
<obj_property name="ObjectShortName">sys_clk_n</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/i_rst_n">
|
||||
<obj_property name="ElementShortName">i_rst_n</obj_property>
|
||||
<obj_property name="ObjectShortName">i_rst_n</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/i2c_scl">
|
||||
<obj_property name="ElementShortName">i2c_scl</obj_property>
|
||||
<obj_property name="ObjectShortName">i2c_scl</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/i2c_sda">
|
||||
<obj_property name="ElementShortName">i2c_sda</obj_property>
|
||||
<obj_property name="ObjectShortName">i2c_sda</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/fan_pwm">
|
||||
<obj_property name="ElementShortName">fan_pwm</obj_property>
|
||||
<obj_property name="ObjectShortName">fan_pwm</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/clk_locked">
|
||||
<obj_property name="ElementShortName">clk_locked</obj_property>
|
||||
<obj_property name="ObjectShortName">clk_locked</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/main_clk_100">
|
||||
<obj_property name="ElementShortName">main_clk_100</obj_property>
|
||||
<obj_property name="ObjectShortName">main_clk_100</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/sys_clk">
|
||||
<obj_property name="ElementShortName">sys_clk</obj_property>
|
||||
<obj_property name="ObjectShortName">sys_clk</obj_property>
|
||||
</wvobject>
|
||||
<wvobject fp_name="divider37" type="divider">
|
||||
<obj_property name="label">spd_reader</obj_property>
|
||||
<obj_property name="DisplayName">label</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/spd_reader_inst/i_clk">
|
||||
<obj_property name="ElementShortName">i_clk</obj_property>
|
||||
<obj_property name="ObjectShortName">i_clk</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/spd_reader_inst/i_rst_n">
|
||||
<obj_property name="ElementShortName">i_rst_n</obj_property>
|
||||
<obj_property name="ObjectShortName">i_rst_n</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/spd_reader_inst/i2c_scl">
|
||||
<obj_property name="ElementShortName">i2c_scl</obj_property>
|
||||
<obj_property name="ObjectShortName">i2c_scl</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/spd_reader_inst/i2c_sda">
|
||||
<obj_property name="ElementShortName">i2c_sda</obj_property>
|
||||
<obj_property name="ObjectShortName">i2c_sda</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/state_find_i2c_address">
|
||||
<obj_property name="ElementShortName">state_find_i2c_address[1:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">state_find_i2c_address[1:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/spd_reader_inst/find_i2c_address_done">
|
||||
<obj_property name="ElementShortName">find_i2c_address_done</obj_property>
|
||||
<obj_property name="ObjectShortName">find_i2c_address_done</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/i2c_address">
|
||||
<obj_property name="ElementShortName">i2c_address[6:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">i2c_address[6:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/spd_reader_inst/enable">
|
||||
<obj_property name="ElementShortName">enable</obj_property>
|
||||
<obj_property name="ObjectShortName">enable</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/spd_reader_inst/read_write">
|
||||
<obj_property name="ElementShortName">read_write</obj_property>
|
||||
<obj_property name="ObjectShortName">read_write</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/register_address">
|
||||
<obj_property name="ElementShortName">register_address[7:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">register_address[7:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/device_address">
|
||||
<obj_property name="ElementShortName">device_address[6:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">device_address[6:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/miso_data">
|
||||
<obj_property name="ElementShortName">miso_data[7:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">miso_data[7:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/spd_reader_inst/busy">
|
||||
<obj_property name="ElementShortName">busy</obj_property>
|
||||
<obj_property name="ObjectShortName">busy</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/spd_reader_inst/slave_nack">
|
||||
<obj_property name="ElementShortName">slave_nack</obj_property>
|
||||
<obj_property name="ObjectShortName">slave_nack</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/spd_reader_inst/read_spd_done">
|
||||
<obj_property name="ElementShortName">read_spd_done</obj_property>
|
||||
<obj_property name="ObjectShortName">read_spd_done</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="logic" fp_name="/spd_reader_tb/DUT/spd_reader_inst/nack_unexpected_err">
|
||||
<obj_property name="ElementShortName">nack_unexpected_err</obj_property>
|
||||
<obj_property name="ObjectShortName">nack_unexpected_err</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/state_read_spd">
|
||||
<obj_property name="ElementShortName">state_read_spd[2:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">state_read_spd[2:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/byte_address">
|
||||
<obj_property name="ElementShortName">byte_address[5:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">byte_address[5:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/byte_data">
|
||||
<obj_property name="ElementShortName">byte_data[63:0][7:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">byte_data[63:0][7:0]</obj_property>
|
||||
<obj_property name="isExpanded"></obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/I2C_ADDRESS">
|
||||
<obj_property name="ElementShortName">I2C_ADDRESS[6:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">I2C_ADDRESS[6:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/IDLE">
|
||||
<obj_property name="ElementShortName">IDLE[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">IDLE[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/READ_ADDRESS">
|
||||
<obj_property name="ElementShortName">READ_ADDRESS[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">READ_ADDRESS[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/READ_BYTE">
|
||||
<obj_property name="ElementShortName">READ_BYTE[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">READ_BYTE[31:0]</obj_property>
|
||||
</wvobject>
|
||||
<wvobject type="array" fp_name="/spd_reader_tb/DUT/spd_reader_inst/WAIT_ACK">
|
||||
<obj_property name="ElementShortName">WAIT_ACK[31:0]</obj_property>
|
||||
<obj_property name="ObjectShortName">WAIT_ACK[31:0]</obj_property>
|
||||
</wvobject>
|
||||
</wave_config>
|
||||
Loading…
Reference in New Issue