105 lines
3.1 KiB
Systemverilog
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
|