// (C) 2001-2019 Intel Corporation. All rights reserved. // Your use of Intel Corporation's design tools, logic functions and other // software and tools, and its AMPP partner logic functions, and any output // files from any of the foregoing (including device programming or simulation // files), and any associated documentation or information are expressly subject // to the terms and conditions of the Intel Program License Subscription // Agreement, Intel FPGA IP License Agreement, or other applicable // license agreement, including, without limitation, that your use is for the // sole purpose of programming logic devices manufactured by Intel and sold by // Intel or its authorized distributors. Please refer to the applicable // agreement for further details. // ****** // phy_mgr // ****** // // PHY Manager // // General Description // ------------------- // // This component allows a way for a master on the Avalon bus to // control various aspects of the PHY connection. // // Architecture // ------------ // // The PHY Manager is organized as follows // - Avalon Interface: it's a Memory-Mapped interface to the Avalon // Bus. // - Register File: it's a set of registers used to control the functioning // of the PHY. This register file can be read and written from the Avalon // interface and can only be read by the PHY. // - Command Issue: a write to this port runs a particular sequence at PHY // speeds. // // Address Space // ------------- // // The address space is divided into 2 identical portions. // The most significant bit select one of the internal components. // The rest of the address word (whose size depend on parameterization) // is used to access a specific location of the selected component. // // 0 - Instruction/Configuration Address. // Data written to this component is intepreted as a command. // Data read from this component returns various parameters specific // to the PHY manager. // During read, case (lower address bits) // 000: returns MAX_LATENCY_COUNT_WIDTH // 001: returns AFI_MAX_WRITE_LATENCY_COUNT_WIDTH // 010: returns AFI_MAX_READ_LATENCY_COUNT_WIDTH // During write, case (lower address bits) // 000: increments vfifo_fr specified by word being written // 001: increments vfifo_hr specified by word being written // 010: resets the fifo. // (the following only used by quarter-rate PHY) // 011: asserts vfifo_fr and vfifo_hr // 100: asserts vfifo_qr // // 1 - Register File. // 8 registers controlling various aspects of the PHY are provided. // The lowest three bits are used to address the specific register // begin read/written from/to. // During reads/writes, case (lower address bits) // 000: returns/sets read latency counter // 001: returns/sets mem stable bit. // `timescale 1 ps / 1 ps module sequencer_phy_mgr ( // Avalon Interface avl_clk, avl_reset_n, avl_address, avl_write, avl_writedata, avl_read, avl_readdata, avl_waitrequest, // PHY control phy_clk, phy_reset_n, phy_read_latency_counter, phy_read_increment_vfifo_fr, phy_read_increment_vfifo_hr, phy_read_increment_vfifo_qr, phy_reset_mem_stable, phy_afi_wlat, phy_afi_rlat, phy_mux_sel, phy_cal_success, phy_cal_fail, phy_cal_debug_info, phy_read_fifo_reset, phy_vfifo_rd_en_override, phy_read_fifo_q, phy_write_fr_cycle_shifts, calib_skip_steps ); parameter AVL_DATA_WIDTH = 32; parameter AVL_ADDR_WIDTH = 13; parameter MAX_LATENCY_COUNT_WIDTH = 5; parameter MEM_IF_READ_DQS_WIDTH = 1; parameter MEM_IF_WRITE_DQS_WIDTH = 1; parameter AFI_DQ_WIDTH = 64; parameter AFI_DEBUG_INFO_WIDTH = 32; parameter AFI_MAX_WRITE_LATENCY_COUNT_WIDTH = 5; parameter AFI_MAX_READ_LATENCY_COUNT_WIDTH = 5; parameter CALIB_VFIFO_OFFSET = 10; parameter CALIB_LFIFO_OFFSET = 3; parameter CALIB_REG_WIDTH = 8; parameter READ_VALID_FIFO_SIZE = 16; parameter MEM_T_WL = 1; parameter MEM_T_RL = 2; parameter CTL_REGDIMM_ENABLED = 0; parameter NUM_WRITE_FR_CYCLE_SHIFTS = 0; parameter DEVICE_FAMILY = ""; parameter VFIFO_CONTROL_WIDTH_PER_DQS = 1; localparam WRITE_FR_CYCLE_SHIFT_WIDTH = 2*MEM_IF_WRITE_DQS_WIDTH; localparam AFI_GROUP_WRITE_LATENCY_COUNT_WIDTH = (AFI_MAX_WRITE_LATENCY_COUNT_WIDTH <= 6) ? AFI_MAX_WRITE_LATENCY_COUNT_WIDTH : AFI_MAX_WRITE_LATENCY_COUNT_WIDTH/MEM_IF_WRITE_DQS_WIDTH; input avl_clk; input avl_reset_n; input [AVL_ADDR_WIDTH - 1:0] avl_address; input avl_write; input [AVL_DATA_WIDTH - 1:0] avl_writedata; input avl_read; output [AVL_DATA_WIDTH - 1:0] avl_readdata; output avl_waitrequest; input phy_clk; input phy_reset_n; output [MAX_LATENCY_COUNT_WIDTH - 1:0] phy_read_latency_counter; output [MEM_IF_READ_DQS_WIDTH - 1:0] phy_read_increment_vfifo_fr; output [MEM_IF_READ_DQS_WIDTH - 1:0] phy_read_increment_vfifo_hr; output [MEM_IF_READ_DQS_WIDTH - 1:0] phy_read_increment_vfifo_qr; output phy_reset_mem_stable; output [AFI_MAX_WRITE_LATENCY_COUNT_WIDTH - 1:0] phy_afi_wlat; output [AFI_MAX_READ_LATENCY_COUNT_WIDTH - 1:0] phy_afi_rlat; output [WRITE_FR_CYCLE_SHIFT_WIDTH - 1:0] phy_write_fr_cycle_shifts; output phy_mux_sel; output phy_cal_success; output phy_cal_fail; output [AFI_DEBUG_INFO_WIDTH - 1:0] phy_cal_debug_info; output [MEM_IF_READ_DQS_WIDTH - 1:0] phy_read_fifo_reset; output [MEM_IF_READ_DQS_WIDTH - 1:0] phy_vfifo_rd_en_override; input [AFI_DQ_WIDTH-1:0] phy_read_fifo_q; input [CALIB_REG_WIDTH - 1:0] calib_skip_steps; reg avl_waitrequest; reg [AVL_DATA_WIDTH - 1:0] avl_readdata_r; reg [AVL_DATA_WIDTH - 1:0] avl_readdata; reg avl_rfile_wr; reg avl_issue_wr; reg [VFIFO_CONTROL_WIDTH_PER_DQS*MEM_IF_READ_DQS_WIDTH - 1:0] avl_read_increment_vfifo_fr; reg [MEM_IF_READ_DQS_WIDTH - 1:0] avl_read_increment_vfifo_hr; reg [MEM_IF_READ_DQS_WIDTH - 1:0] avl_read_increment_vfifo_qr; reg avl_read_fifo_reset; reg [MAX_LATENCY_COUNT_WIDTH - 1:0] phy_read_latency_counter; reg [MEM_IF_READ_DQS_WIDTH - 1:0] phy_read_increment_vfifo_fr; reg [VFIFO_CONTROL_WIDTH_PER_DQS*MEM_IF_READ_DQS_WIDTH - 1:0] phy_read_increment_vfifo_fr_pre_combined /* synthesis preserve = 1 */; wire [MEM_IF_READ_DQS_WIDTH - 1:0] phy_read_increment_vfifo_fr_combined /* synthesis keep = 1 */; reg [MEM_IF_READ_DQS_WIDTH - 1:0] phy_read_increment_vfifo_hr; reg [MEM_IF_READ_DQS_WIDTH - 1:0] phy_read_increment_vfifo_qr; reg phy_reset_mem_stable; reg [AFI_MAX_WRITE_LATENCY_COUNT_WIDTH - 1:0] phy_afi_wlat; reg [AFI_MAX_READ_LATENCY_COUNT_WIDTH - 1:0] phy_afi_rlat; reg [WRITE_FR_CYCLE_SHIFT_WIDTH - 1:0] phy_write_fr_cycle_shifts; reg phy_mux_sel; reg phy_cal_success; reg phy_cal_fail; reg [AFI_DEBUG_INFO_WIDTH - 1:0] phy_cal_debug_info; reg [MEM_IF_READ_DQS_WIDTH - 1:0] phy_read_fifo_reset; reg [MEM_IF_READ_DQS_WIDTH - 1:0] phy_vfifo_rd_en_override; reg phy_done; wire [32 - 1:0] num_fr_cycle_shifts; assign num_fr_cycle_shifts = NUM_WRITE_FR_CYCLE_SHIFTS; function integer ceil_log2; input integer value; begin value = value - 1; for (ceil_log2 = 0; value > 0; ceil_log2 = ceil_log2 + 1) value = value >> 1; end endfunction localparam RFILE_ADDR_WIDTH = (NUM_WRITE_FR_CYCLE_SHIFTS == 0) ? 3 : ((AFI_MAX_WRITE_LATENCY_COUNT_WIDTH <= 6) ? ceil_log2(7 + MEM_IF_WRITE_DQS_WIDTH + 1) : ceil_log2(7 + 2*MEM_IF_WRITE_DQS_WIDTH + 1)); integer i; typedef enum int unsigned { STATE_AVL_IDLE, STATE_AVL_EXEC, STATE_AVL_DONE } STATE_AVL_T; STATE_AVL_T state_avl_curr; typedef enum int unsigned { STATE_PHY_IDLE, STATE_PHY_DONE } STATE_PHY_T; STATE_PHY_T state_phy_curr; // the register file reg [AVL_DATA_WIDTH - 1:0] rfile [0:(NUM_WRITE_FR_CYCLE_SHIFTS == 0) ? 7 : ((AFI_MAX_WRITE_LATENCY_COUNT_WIDTH <= 6) ? 7 + MEM_IF_WRITE_DQS_WIDTH : 7+2*MEM_IF_WRITE_DQS_WIDTH)]; // register file selected wire sel_rfile, sel_rfile_wr, sel_rfile_rd; // command issue selected wire sel_any; wire sel_issue, sel_issue_wr, sel_issue_rd; assign sel_rfile = avl_address[AVL_ADDR_WIDTH - 1]; assign sel_rfile_wr = sel_rfile & avl_write; assign sel_rfile_rd = sel_rfile & avl_read; assign sel_issue = ~avl_address[AVL_ADDR_WIDTH - 1]; assign sel_issue_wr = sel_issue & avl_write; assign sel_issue_rd = sel_issue & avl_read; assign sel_any = sel_rfile_wr | sel_rfile_rd | sel_issue_wr | sel_issue_rd; // State machine, AVALON side always_ff @ (posedge avl_clk or negedge avl_reset_n) begin integer reg_file_index; if (avl_reset_n == 0) begin state_avl_curr <= STATE_AVL_IDLE; avl_readdata_r <= '0; avl_rfile_wr <= 0; avl_issue_wr <= 0; avl_read_increment_vfifo_fr <= '0; avl_read_increment_vfifo_hr <= '0; avl_read_increment_vfifo_qr <= '0; avl_read_fifo_reset <= 0; rfile[0][MAX_LATENCY_COUNT_WIDTH - 1:0] <= '0; rfile[1][0] <= 0; rfile[2][0] <= 1; rfile[3][1:0] <= '0; rfile[4] <= '0; rfile[5][0] <= 0; rfile[6] <= 0; rfile[7] <= 0; if (NUM_WRITE_FR_CYCLE_SHIFTS == -1) begin for (reg_file_index = 8; reg_file_index < 8 + MEM_IF_WRITE_DQS_WIDTH; reg_file_index++) begin rfile[reg_file_index] <= '0; end if (AFI_MAX_WRITE_LATENCY_COUNT_WIDTH > 6) begin for (reg_file_index = 8 + MEM_IF_WRITE_DQS_WIDTH; reg_file_index < 8 + 2*MEM_IF_WRITE_DQS_WIDTH; reg_file_index++) begin rfile[reg_file_index] <= '0; end end end end else begin case (state_avl_curr) STATE_AVL_IDLE: begin if (sel_rfile_rd) begin // NIOS is reading from the register file state_avl_curr <= STATE_AVL_DONE; avl_readdata_r <= '0 /*rfile[avl_address[RFILE_ADDR_WIDTH - 1:0]] */; // removing address file access for further optimization. end else if (sel_issue_rd) begin // NIOS is reading parameters state_avl_curr <= STATE_AVL_DONE; case (avl_address[3:0]) 4'b0000: avl_readdata_r <= MAX_LATENCY_COUNT_WIDTH; 4'b0001: avl_readdata_r <= AFI_MAX_WRITE_LATENCY_COUNT_WIDTH; 4'b0010: avl_readdata_r <= AFI_MAX_READ_LATENCY_COUNT_WIDTH; `ifndef SYNTH_FOR_SIM //synthesis translate_off `endif // extra bit indicates we are in the simulator 4'b0011: avl_readdata_r <= {{(AVL_DATA_WIDTH-CALIB_REG_WIDTH-1){1'b0}},1'b1,calib_skip_steps}; `ifndef SYNTH_FOR_SIM //synthesis translate_on `endif `ifndef SYNTH_FOR_SIM //synthesis read_comments_as_HDL on `endif `ifndef SYNTH_FOR_SIM // synthesis read_comments_as_HDL off `endif // 4'b0011: avl_readdata_r <= {{(AVL_DATA_WIDTH-CALIB_REG_WIDTH){1'b0}},calib_skip_steps}; 4'b0100: avl_readdata_r <= CALIB_VFIFO_OFFSET; 4'b0101: avl_readdata_r <= CALIB_LFIFO_OFFSET; 4'b0110: if (CTL_REGDIMM_ENABLED == 1) avl_readdata_r <= 1; else avl_readdata_r <= 0; 4'b0111: avl_readdata_r <= MEM_T_WL; 4'b1000: avl_readdata_r <= MEM_T_RL; 4'b1001: avl_readdata_r <= READ_VALID_FIFO_SIZE; endcase end else if (sel_rfile_wr) begin // NIOS is writing into the register file. // Need to issue a command the PHY side to pick up changes. state_avl_curr <= STATE_AVL_EXEC; avl_rfile_wr <= 1; rfile[avl_address[RFILE_ADDR_WIDTH - 1:0]] <= avl_writedata; end else if (sel_issue_wr) begin // NIOS is asking for a particular command to be issued state_avl_curr <= STATE_AVL_EXEC; avl_issue_wr <= 1; case (avl_address[2:0]) 3'b000: if (avl_writedata == 8'b11111111) begin avl_read_increment_vfifo_fr <= {(MEM_IF_READ_DQS_WIDTH*VFIFO_CONTROL_WIDTH_PER_DQS){1'b1}}; end else begin avl_read_increment_vfifo_fr[avl_writedata] <= 1; end 3'b001: if (avl_writedata == 8'b11111111) begin avl_read_increment_vfifo_hr <= {MEM_IF_READ_DQS_WIDTH{1'b1}}; end else begin avl_read_increment_vfifo_hr[avl_writedata] <= 1; end 3'b010: avl_read_fifo_reset <= 1; 3'b011: if (avl_writedata == 8'b11111111) begin avl_read_increment_vfifo_fr <= {(MEM_IF_READ_DQS_WIDTH*VFIFO_CONTROL_WIDTH_PER_DQS){1'b1}}; avl_read_increment_vfifo_hr <= {MEM_IF_READ_DQS_WIDTH{1'b1}}; end else begin avl_read_increment_vfifo_fr[avl_writedata] <= 1; avl_read_increment_vfifo_hr[avl_writedata] <= 1; end 3'b100: if (avl_writedata == 8'b11111111) begin avl_read_increment_vfifo_qr <= {MEM_IF_READ_DQS_WIDTH{1'b1}}; end else begin avl_read_increment_vfifo_qr[avl_writedata] <= 1; end endcase end end STATE_AVL_EXEC: begin // We are currently executing a command on the PHY side. // Wait until it is done. if (phy_done) begin state_avl_curr <= STATE_AVL_DONE; // Need to bring all the command lines down to prevent // the PHY from reexecuting the same command. avl_rfile_wr <= 0; avl_issue_wr <= 0; avl_read_increment_vfifo_fr <= '0; avl_read_increment_vfifo_hr <= '0; avl_read_increment_vfifo_qr <= '0; avl_read_fifo_reset <= 0; end end STATE_AVL_DONE: begin // Done operation. During this clock waitrequest is de-asserted. // Return to idle state next clock. state_avl_curr <= STATE_AVL_IDLE; end endcase end end // State machine, PHY side always_ff @ (posedge phy_clk or negedge phy_reset_n) begin integer reg_file_index; integer offset; if (phy_reset_n == 0) begin state_phy_curr <= STATE_PHY_IDLE; phy_done <= 0; phy_read_increment_vfifo_fr_pre_combined <= '0; phy_read_increment_vfifo_hr <= '0; phy_read_increment_vfifo_qr <= '0; phy_read_fifo_reset <= '0; phy_reset_mem_stable <= 0; phy_mux_sel <= 1; phy_cal_success <= 0; phy_cal_fail <= 0; phy_cal_debug_info <= '0; phy_vfifo_rd_en_override <= '0; phy_write_fr_cycle_shifts <= '0; phy_afi_rlat <= '0; phy_afi_wlat <= '0; phy_read_latency_counter <= '0; end else begin case (state_phy_curr) STATE_PHY_IDLE: begin // Waiting for a command to arrive. if (avl_rfile_wr) begin // Register file was just changed on the AVL side. Pick // up changes. state_phy_curr <= STATE_PHY_DONE; phy_done <= 1; phy_read_latency_counter <= rfile[0][MAX_LATENCY_COUNT_WIDTH - 1:0]; phy_reset_mem_stable <= rfile[1][0]; phy_mux_sel <= rfile[2][0]; phy_cal_success <= rfile[3][0]; phy_cal_fail <= rfile[3][1]; phy_cal_debug_info <= rfile[4][AFI_DEBUG_INFO_WIDTH - 1:0]; phy_vfifo_rd_en_override <= {(MEM_IF_READ_DQS_WIDTH){rfile[5][0]}}; if (NUM_WRITE_FR_CYCLE_SHIFTS == -1) begin for (reg_file_index = 8; reg_file_index < 8 + MEM_IF_WRITE_DQS_WIDTH; reg_file_index++) begin offset = (reg_file_index - 8)*WRITE_FR_CYCLE_SHIFT_WIDTH/MEM_IF_WRITE_DQS_WIDTH; phy_write_fr_cycle_shifts[offset+:WRITE_FR_CYCLE_SHIFT_WIDTH/MEM_IF_WRITE_DQS_WIDTH] <= rfile[reg_file_index][WRITE_FR_CYCLE_SHIFT_WIDTH/MEM_IF_WRITE_DQS_WIDTH - 1:0]; end if (AFI_MAX_WRITE_LATENCY_COUNT_WIDTH > 6) begin for (reg_file_index = 8 + MEM_IF_WRITE_DQS_WIDTH; reg_file_index < 8 + 2*MEM_IF_WRITE_DQS_WIDTH; reg_file_index++) begin offset = (reg_file_index - 8 - MEM_IF_WRITE_DQS_WIDTH)*AFI_GROUP_WRITE_LATENCY_COUNT_WIDTH; phy_afi_wlat[offset+:AFI_GROUP_WRITE_LATENCY_COUNT_WIDTH] <= rfile[reg_file_index][AFI_GROUP_WRITE_LATENCY_COUNT_WIDTH - 1:0]; end end else begin phy_afi_wlat <= rfile[6][AFI_MAX_WRITE_LATENCY_COUNT_WIDTH - 1:0]; end end else begin for (reg_file_index = 0; reg_file_index < MEM_IF_WRITE_DQS_WIDTH; reg_file_index++) begin offset = reg_file_index*WRITE_FR_CYCLE_SHIFT_WIDTH/MEM_IF_WRITE_DQS_WIDTH; phy_write_fr_cycle_shifts[offset+:WRITE_FR_CYCLE_SHIFT_WIDTH/MEM_IF_WRITE_DQS_WIDTH] <= num_fr_cycle_shifts[1:0]; end phy_afi_wlat <= rfile[6][AFI_MAX_WRITE_LATENCY_COUNT_WIDTH - 1:0]; end phy_afi_rlat <= rfile[7][AFI_MAX_READ_LATENCY_COUNT_WIDTH - 1:0]; end else if (avl_issue_wr) begin // NIOS just issued a command to be run. state_phy_curr <= STATE_PHY_DONE; phy_done <= 1; phy_read_increment_vfifo_fr_pre_combined <= avl_read_increment_vfifo_fr; phy_read_increment_vfifo_hr <= avl_read_increment_vfifo_hr; phy_read_increment_vfifo_qr <= avl_read_increment_vfifo_qr; phy_read_fifo_reset <= {(MEM_IF_READ_DQS_WIDTH){avl_read_fifo_reset}}; end end STATE_PHY_DONE: begin phy_read_increment_vfifo_fr_pre_combined <= '0; phy_read_increment_vfifo_hr <= '0; phy_read_increment_vfifo_qr <= '0; phy_read_fifo_reset <= '0; if (~avl_rfile_wr && ~avl_issue_wr) begin state_phy_curr <= STATE_PHY_IDLE; phy_done <= 0; end end endcase end end // Do not modify the following logic. This is used by the fitter to identify VFIFO control structures in Arria V generate if (DEVICE_FAMILY == "ARRIAV" && VFIFO_CONTROL_WIDTH_PER_DQS != 1) begin genvar dqs_index; for (dqs_index=0; dqs_index < MEM_IF_READ_DQS_WIDTH; dqs_index=dqs_index+1) begin : vfifo_fb56390_gen wire [5:0] phy_read_increment_vfifo_fr_pre_combined_ext; assign phy_read_increment_vfifo_fr_pre_combined_ext = {2'b00, phy_read_increment_vfifo_fr_pre_combined[(dqs_index*VFIFO_CONTROL_WIDTH_PER_DQS+VFIFO_CONTROL_WIDTH_PER_DQS-1):dqs_index*VFIFO_CONTROL_WIDTH_PER_DQS]}; // synthesis read_comments_as_HDL on // arriav_lcell_comb vfifo_fb56390 // ( // .dataa(phy_read_increment_vfifo_fr_pre_combined_ext[0]), // .datab(phy_read_increment_vfifo_fr_pre_combined_ext[1]), // .datac(phy_read_increment_vfifo_fr_pre_combined_ext[2]), // .datad(phy_read_increment_vfifo_fr_pre_combined_ext[3]), // .datae(phy_read_increment_vfifo_fr_pre_combined_ext[4]), // .dataf(phy_read_increment_vfifo_fr_pre_combined_ext[5]), // .combout(phy_read_increment_vfifo_fr_combined[dqs_index]) // ); // defparam vfifo_fb56390.lut_mask = 64'h000000000000FFFE; // synthesis read_comments_as_HDL off // synthesis translate_off assign phy_read_increment_vfifo_fr_combined[dqs_index] = phy_read_increment_vfifo_fr_pre_combined[dqs_index*VFIFO_CONTROL_WIDTH_PER_DQS]; // synthesis translate_on end end else begin assign phy_read_increment_vfifo_fr_combined = phy_read_increment_vfifo_fr_pre_combined; end endgenerate assign phy_read_increment_vfifo_fr = phy_read_increment_vfifo_fr_combined; // wait request management and read data gating always_comb begin if (sel_any && state_avl_curr != STATE_AVL_DONE || ~avl_reset_n) avl_waitrequest <= 1; else avl_waitrequest <= 0; if (sel_rfile_rd || sel_issue_rd) avl_readdata <= avl_readdata_r; else avl_readdata <= '0; end endmodule