added uart to display spd report

This commit is contained in:
AngeloJacobo 2024-12-29 20:41:17 +08:00
parent 253d9495ca
commit 7acaf34b44
5 changed files with 523 additions and 24 deletions

View File

@ -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]}]
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]

View File

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

View File

@ -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)
);

187
rtl/spd/uart_tx.v Normal file
View File

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

View File

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