Files
gps/hardware/ip/max2771_spi/rtl/max2771_spi_engine.sv
T
2026-08-06 22:14:52 +03:00

105 lines
3.1 KiB
Systemverilog

`timescale 1ns/1ps
module max2771_spi_engine #(
parameter integer CLOCK_HZ = 50_000_000,
parameter integer MAX_SCLK_HZ = 4_000_000
) (
input wire clk,
input wire rst_n,
input wire start,
input wire read_not_write,
input wire [3:0] address,
input wire [31:0] write_data,
output reg [31:0] read_data,
output reg busy,
output wire ready,
output reg sclk,
output reg csn,
input wire sdata_i,
output reg sdata_o,
output reg sdata_oe
);
// Ceiling division keeps SCLK at or below the MAX2771 4 MHz limit.
localparam integer HALF_PERIOD_CYCLES =
(CLOCK_HZ + (2 * MAX_SCLK_HZ) - 1) / (2 * MAX_SCLK_HZ);
localparam integer CS_INACTIVE_CYCLES =
(CLOCK_HZ + 10_000_000 - 1) / 10_000_000;
reg [47:0] tx_shift;
reg [31:0] rx_shift;
reg [5:0] bit_index;
integer half_count;
integer inactive_count;
reg transaction_is_read;
assign ready = !busy && (inactive_count == 0);
initial begin
if (CLOCK_HZ <= 0)
$error("CLOCK_HZ must be positive");
if (MAX_SCLK_HZ <= 0)
$error("MAX_SCLK_HZ must be positive");
if (HALF_PERIOD_CYCLES < 1)
$error("HALF_PERIOD_CYCLES must be at least one");
end
always @(posedge clk) begin
if (!rst_n) begin
read_data <= 32'd0;
busy <= 1'b0;
sclk <= 1'b0;
csn <= 1'b1;
sdata_o <= 1'b0;
sdata_oe <= 1'b0;
tx_shift <= 48'd0;
rx_shift <= 32'd0;
bit_index <= 6'd0;
half_count <= 0;
inactive_count <= 0;
transaction_is_read <= 1'b0;
end else if (busy) begin
if (half_count != 0) begin
half_count <= half_count - 1;
end else begin
half_count <= HALF_PERIOD_CYCLES - 1;
if (!sclk) begin
// MAX2771 samples input and the host samples read data on rising SCLK.
sclk <= 1'b1;
if (transaction_is_read && (bit_index >= 6'd16))
rx_shift <= {rx_shift[30:0], sdata_i};
end else begin
// Both sides change or release SDATA on falling SCLK.
sclk <= 1'b0;
if (bit_index == 6'd47) begin
read_data <= rx_shift;
busy <= 1'b0;
csn <= 1'b1;
sdata_oe <= 1'b0;
inactive_count <= CS_INACTIVE_CYCLES;
end else begin
bit_index <= bit_index + 1'b1;
tx_shift <= {tx_shift[46:0], 1'b0};
sdata_o <= tx_shift[46];
if (transaction_is_read && (bit_index == 6'd12))
sdata_oe <= 1'b0;
end
end
end
end else if (inactive_count != 0) begin
inactive_count <= inactive_count - 1;
end else if (start) begin
// Frame: A[11:0], R/W, three turnaround bits, D[31:0]. A[11:4] are zero.
tx_shift <= {8'd0, address, read_not_write, 3'd0, write_data};
rx_shift <= 32'd0;
bit_index <= 6'd0;
transaction_is_read <= read_not_write;
busy <= 1'b1;
csn <= 1'b0;
sclk <= 1'b0;
sdata_o <= 1'b0;
sdata_oe <= 1'b1;
half_count <= HALF_PERIOD_CYCLES - 1;
end
end
endmodule