pluto_hdl_adi/library/axi_ad3552r/axi_ad3552r_if.v

317 lines
11 KiB
Verilog
Executable File

// ***************************************************************************
// ***************************************************************************
// Copyright (C) 2022-2023 Analog Devices, Inc. All rights reserved.
//
// In this HDL repository, there are many different and unique modules, consisting
// of various HDL (Verilog or VHDL) components. The individual modules are
// developed independently, and may be accompanied by separate and unique license
// terms.
//
// The user should read each of these license terms, and understand the
// freedoms and responsibilities that he or she has by using this source/core.
//
// This core 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.
//
// Redistribution and use of source or resulting binaries, with or without modification
// of this file, are permitted under one of the following two license terms:
//
// 1. The GNU General Public License version 2 as published by the
// Free Software Foundation, which can be found in the top level directory
// of this repository (LICENSE_GPL2), and also online at:
// <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
//
// OR
//
// 2. An ADI specific BSD license, which can be found in the top level directory
// of this repository (LICENSE_ADIBSD), and also on-line at:
// https://github.com/analogdevicesinc/hdl/blob/main/LICENSE_ADIBSD
// This will allow to generate bit files and not release the source code,
// as long as it attaches to an ADI device.
//
// ***************************************************************************
// ***************************************************************************
`timescale 1ns/100ps
module axi_ad3552r_if (
input clk_in, // 120MHz
input reset_in,
input [31:0] dac_data,
input dac_data_valid,
input dac_data_valid_ext,
output dac_data_ready,
input [ 7:0] address,
input [23:0] data_write,
input sdr_ddr_n,
input symb_8_16b,
input transfer_data,
input stream,
input external_sync,
input external_sync_arm,
output if_busy,
output sync_ext_device,
output reg [23:0] data_read,
// DAC control signals
output sclk,
output reg csn,
input [ 3:0] sdio_i,
output [ 3:0] sdio_o,
output sdio_t
);
wire transfer_data_s;
wire start_synced;
wire [31:0] dac_data_int;
wire dac_data_valid_synced;
wire external_sync_s;
reg [55:0] transfer_reg = 56'h0;
reg [15:0] counter = 16'h0;
reg [ 2:0] transfer_state = 0;
reg [ 2:0] transfer_state_next = 0;
reg cycle_done = 1'b0;
reg transfer_step = 1'b0;
reg sclk_ddr = 1'b0;
reg full_speed = 1'b0;
reg transfer_data_d = 1'b0;
reg transfer_data_dd = 1'b0;
reg [ 3:0] valid_captured_d = 4'b0;
reg data_r_wn = 1'b0;
reg valid_captured = 1'b0;
reg start_transfer = 1'b0;
reg if_busy_reg = 1'b0;
reg dac_data_ready_s = 1'b0;
reg external_sync_arm_reg = 1'b0;
reg external_sync_reg = 1'b0;
localparam [ 2:0] IDLE = 3'h0,
CS_LOW = 3'h1,
WRITE_ADDRESS = 3'h2,
TRANSFER_REGISTER = 3'h3,
READ_REGISTER = 3'h4,
STREAM = 3'h5,
CS_HIGH = 3'h6;
assign if_busy = if_busy_reg;
// transform the transfer data rising edge into a pulse
assign transfer_data_s = transfer_data_d & ~transfer_data_dd;
// start the data stream transfer after valid has been captured
assign start_synced = valid_captured_d[1] & start_transfer & stream;
assign sync_ext_device = start_synced ;
// use dac_data valid from an external source only if external_sync_arm_reg is 1
assign dac_data_valid_synced = (external_sync_arm_reg == 1'b1) ? (dac_data_valid & dac_data_valid_ext) : dac_data_valid ;
assign dac_data_ready = dac_data_ready_s & dac_data_valid_synced;
assign dac_data_int = dac_data;
// sync the data only if the synchronizations has been armed in software
assign external_sync_s = ~external_sync_arm_reg | external_sync_reg;
always @(posedge clk_in) begin
if(reset_in == 1'b1) begin
transfer_data_d <= 'd0;
transfer_data_dd <= 'd0;
valid_captured_d <= 4'b0;
valid_captured <= 1'b0;
end else begin
transfer_data_d <= transfer_data;
transfer_data_dd <= transfer_data_d;
valid_captured_d <= {valid_captured_d[2:0], valid_captured};
end
if(transfer_state == CS_HIGH || stream == 1'b0) begin
start_transfer <= 1'b0;
valid_captured <= 1'b0;
valid_captured_d <= 4'b0;
end
// pulse to level conversion
if(external_sync_arm == 1'b1) begin
external_sync_arm_reg <= 1'b1;
end
if(external_sync == 1'b1) begin
external_sync_reg <= 1'b1;
end
if(transfer_state == CS_HIGH) begin
external_sync_arm_reg <= 1'b0;
external_sync_reg <= 1'b0;
end
if(dac_data_valid == 1'b1 && start_transfer == 1'b1) begin
valid_captured <= 1'b1;
end
if(transfer_data == 1'b1) begin
start_transfer <= 1'b1;
end
end
always @(posedge clk_in) begin
if (reset_in == 1'b1) begin
transfer_state <= IDLE;
end else begin
transfer_state <= transfer_state_next;
end
end
// FSM next state logic
always @(*) begin
case (transfer_state)
IDLE : begin
// goes in to the next state only if the control is to transfer register or synced transfer(if it's armed in software)
transfer_state_next = ((transfer_data_s == 1'b1 && stream == 1'b0) || (start_synced == 1'b1 && external_sync_s)) ? CS_LOW : IDLE;
csn = 1'b1;
transfer_step = 0;
cycle_done = 0;
end
CS_LOW : begin
// brings CS down
// loads all configuration
// puts data on the SDIO pins
// needs 5 ns before risedge of the clock
transfer_state_next = WRITE_ADDRESS;
csn = 1'b0;
transfer_step = 0;
cycle_done = 0;
end
WRITE_ADDRESS : begin
// writes the address
// it works either at full speed (60 MHz) when streaming or normal
// speed (15 MHz)
// full speed - 2 clock cycles
// half speed 8 clock cycles
cycle_done = full_speed ? (counter == 16'h3) : (counter == 16'hf);
transfer_state_next = cycle_done ? (stream ? STREAM : TRANSFER_REGISTER) : WRITE_ADDRESS ;
csn = 1'b0;
// in streaming, change data on falledge. On regular transfer, change data on negedge.
//A single step should be done for 8 bit addressing
transfer_step = full_speed ? (counter[0]== 1'h1) : ((counter[4:0] == 5'h5));
end
TRANSFER_REGISTER : begin
// always works at 15 MHz
// can be DDR or SDR
cycle_done = sdr_ddr_n ? (symb_8_16b ? (counter == 16'h10) : (counter == 16'h20)) :
(symb_8_16b ? (counter == 16'h09) : (counter == 16'h11));
transfer_state_next = cycle_done ? CS_HIGH : TRANSFER_REGISTER;
csn = 1'b0;
// in DDR mode, change data on falledge
transfer_step = sdr_ddr_n ? (counter[2:0] == 3'h0) : (counter[1:0] == 2'h0);
end
STREAM : begin
// can be DDR or SDR
// in DDR mode needs to be make sure the clock and data is shifted by 2 ns
cycle_done = stream ? (sdr_ddr_n ? (counter == 16'h0f) : (counter == 16'h7)):
(sdr_ddr_n ? (counter == 16'h10) : (counter == 16'h7));
transfer_state_next = (stream && external_sync_s) ? STREAM: ((cycle_done || external_sync_s == 1'b0) ? CS_HIGH :STREAM);
csn = 1'b0;
transfer_step = sdr_ddr_n ? counter[0] : 1'b1;
end
CS_HIGH : begin
cycle_done = 1'b1;
transfer_state_next = cycle_done ? IDLE : CS_HIGH;
csn = 1'b1;
transfer_step = 0;
end
default : begin
cycle_done = 0;
transfer_state_next = IDLE;
csn = 1'b1;
transfer_step = 0;
end
endcase
end
// counter is used to time all states
// depends on number of clock cycles per phase
always @(posedge clk_in) begin
if (transfer_state == IDLE || reset_in == 1'b1) begin
counter <= 'b0;
end else if (transfer_state == WRITE_ADDRESS | transfer_state == TRANSFER_REGISTER | transfer_state == STREAM) begin
if (cycle_done) begin
counter <= 0;
end else begin
counter <= counter + 1;
end
end
end
always @(negedge clk_in) begin
if (transfer_state == STREAM | transfer_state == WRITE_ADDRESS) begin
sclk_ddr <= !sclk_ddr;
end else begin
sclk_ddr <= 0;
end
end
// selection between 60 MHz and 15 MHz
assign sclk = full_speed ? (sdr_ddr_n ? counter[0] : sclk_ddr) : counter[2];
always @(posedge clk_in) begin
if (transfer_state == CS_LOW) begin
data_r_wn <= address[7];
end else if (transfer_state == CS_HIGH) begin
data_r_wn <=1'b0;
end
if (transfer_state == STREAM) begin
if (cycle_done == 1'b1) begin
dac_data_ready_s <= stream;
end else begin
dac_data_ready_s <= 1'b0;
end
end else begin
dac_data_ready_s <= 1'b0;
end
if (transfer_state == CS_LOW) begin
full_speed = stream;
if(stream) begin
transfer_reg <= {address,dac_data_int, 16'h0};
end else begin
transfer_reg <= {address,data_write, 24'h0};
end
end else if ((transfer_state == STREAM & cycle_done) || (transfer_state != STREAM && transfer_state_next == STREAM)) begin
transfer_reg <= {dac_data_int, 24'h0};
end else if (transfer_step && transfer_state != CS_HIGH) begin
transfer_reg <= {transfer_reg[51:0], sdio_i};
end
if (transfer_state == CS_HIGH) begin
if (symb_8_16b == 1'b0) begin
data_read <= {8'h0,transfer_reg[15:0]};
end else begin
data_read <= {16'h0,transfer_reg[7:0]};
end
end else begin
data_read <= data_read;
end
if (transfer_state == CS_HIGH || transfer_state == IDLE) begin
if_busy_reg <= 1'b0;
end else begin
if_busy_reg <= 1'b1;
end
end
// address[7] is r_wn : depends also on the state machine, input only when
// in TRANSFER register mode
assign sdio_t = (data_r_wn & transfer_state == TRANSFER_REGISTER);
assign sdio_o = transfer_reg[55:52];
endmodule