In this work, I came into contact with the AD9528, a clock generation chip from Arnold & Son, which can generate multiple synchronous clocks. This clock uses the spi interface to configure accordingly. Later, I will record how I wrote the corresponding FPGA code for this process.
AD9528 basic introduction:
Specific configuration parameters and methods:
Specifically, we used a five-way clock in the project. Our configuration requirements are as follows:
There is corresponding host computer software that can be downloaded from the official website and can be configured accordingly. After configuration, you can know what the corresponding register parameters that need to be configured are.
This is the basic configuration method after consulting the manufacturer. For the specific configuration principle, you need to read the datasheet in detail. Specifically, the two clocks OUT1 and OUT2 need to use the clock generated from the PLL2 divider.
The specific registers that need to be configured are as follows:
Code writing and AD9528 spi timing diagram:
AD9528 schematic:
AD9528 timing diagram:
For this timing diagram, we started to write code:
Verilog code implements SPI configuration register:
//------------------------------------------------ -------------------------------- // Copyright (c) 2014-2023 All rights reserved //------------------------------------------------ -------------------------- // Author: xibo wu (Gatsby) // File: spi.v // Create : 2023-10-25 16:16:08 // Revise : 2023-10-27 13:46:23 // Editor : sublime text3, tab size (4) //------------------------------------------------ -------------------------- module spi_module ( input i_clk , // global clock 100MHz input i_rst_n, //reset signal, active low level //Four-wire standard SPI signal definition input i_spi_miso, // SPI serial input, used to receive data from the slave output o_spi_sck , // SPI clock output o_spi_cs , // SPI chip select signal output o_spi_mosi // SPI output, used to send data to the slave ); //-------parameter--------// parameter TX_IDLE = 4'b0001; parameter TX_HEADER = 4'b0010; parameter TX_PAYLOAD = 4'b0100; parameter TX_END = 4'b1000; //-----parameter_reg-------// //---form "addr_data"------// parameter REG_1 = 24'h0104_01; parameter REG_2 = 24'h0100_01; parameter REG_3 = 24'h0102_01; parameter REG_4 = 24'h0204_01; parameter REG_5 = 24'h0201_14; parameter REG_6 = 24'h010A_02; parameter REG_7 = 24'h0208_13; parameter REG_8 = 24'h0501_E0; parameter REG_9 = 24'h0502_3F; parameter REG_10 = 24'h0302_11; parameter REG_11 = 24'h030E_23; parameter REG_12 = 24'h0303_00; parameter REG_13 = 24'h0305_05; parameter REG_14 = 24'h0308_05; //---------regs-----------// reg [4:0] tx_bit_sel; reg wr_en; reg [3:0] clk_cnt; reg cs_n_t; reg sck_t = 1'b0; reg [4:0] reg_cnt = 5'd14; //It is not clear whether subsequent changes are needed here reg mosi_t; reg [23:0] reg_cfg; //Variable register writes configuration value //--------debug-----------// reg [23:0] reg_debug; reg [3:0] cur_state; reg [3:0] nxt_state; reg skip_en; //-------assign----------// assign o_spi_sck = sck_t; assign o_spi_cs = cs_n_t; assign o_spi_mosi = mosi_t; //---------debug-----------// always @(posedge i_clk or negedge i_rst_n) begin if (i_rst_n == 1'b0) begin reg_debug <= 24'b0; end else if(cs_n_t == 1'b0 & amp; & amp; clk_cnt == 'd1) begin reg_debug <= {reg_debug[22:0], mosi_t}; end else begin reg_debug <= reg_debug; end end //---------main------------// always @(posedge i_clk or negedge i_rst_n) begin if (i_rst_n == 1'b0) begin clk_cnt <= 4'b0; end else if(clk_cnt == 'd3) begin clk_cnt <= 'd0; end else begin clk_cnt <= clk_cnt + 4'd1; end end //The data must be prepared before the rising edge of sck always @(posedge i_clk or negedge i_rst_n) begin if (i_rst_n == 1'b0) begin wr_en <= 1'b0; end else if(clk_cnt == 'd2) begin wr_en <= 1'b1; end else begin wr_en <= 1'b0; end end always @(posedge i_clk or negedge i_rst_n) begin if (i_rst_n == 1'b0) begin sck_t <= 1'b0; end else if(clk_cnt == 'd1 & amp; & amp; cs_n_t == 1'b0) begin sck_t <= 1'b1; end else if(clk_cnt == 'd3 & amp; & amp; cs_n_t == 1'b0) begin sck_t <= 1'b0; end else begin sck_t <= sck_t; end end //Register data configuration unit j always @(posedge i_clk or posedge i_rst_n) begin if (i_rst_n == 1'b0) begin reg_cfg <= REG_1; end else begin case(reg_cnt) 5'd14: reg_cfg <= REG_1; //Fill the corresponding register value in the middle. 5'd13: reg_cfg <= REG_2; 5'd12: reg_cfg <= REG_3; 5'd11: reg_cfg <= REG_4; 5'd10: reg_cfg <= REG_5; 5'd9 : reg_cfg <= REG_6; 5'd8 : reg_cfg <= REG_7; 5'd7 : reg_cfg <= REG_8; 5'd6 : reg_cfg <= REG_9; 5'd5 : reg_cfg <= REG_10; 5'd4 : reg_cfg <= REG_11; 5'd3 : reg_cfg <= REG_12; 5'd2 : reg_cfg <= REG_13; 5'd1 : reg_cfg <= REG_14; default:; endcase end end always @(posedge i_clk or negedge i_rst_n) begin if (i_rst_n == 1'b0) begin cur_state <= TX_IDLE; end else begin cur_state <= nxt_state; end end always @(*) begin nxt_state = TX_IDLE; case (cur_state) TX_IDLE: begin if (skip_en) begin nxt_state = TX_HEADER; end else begin nxt_state = TX_IDLE; end end TX_HEADER : begin if (skip_en) begin nxt_state = TX_PAYLOAD; end else begin nxt_state = TX_HEADER; end end TX_PAYLOAD : begin if (skip_en) begin nxt_state = TX_END; end else begin nxt_state = TX_PAYLOAD; end end TX_END : begin if (skip_en) begin nxt_state = TX_IDLE; end else begin nxt_state = TX_END; end end default: nxt_state = TX_IDLE; endcase end always @(posedge i_clk or negedge i_rst_n) begin if (i_rst_n == 1'b0) begin skip_en <= 1'b0; cs_n_t <= 1'b1; tx_bit_sel <= 5'd0; end else begin skip_en <= 1'b0; case(nxt_state) TX_IDLE: begin if(reg_cnt != 'd0) begin skip_en <= 1'b1; end else begin cs_n_t <= 1'b1; end end TX_HEADER : begin cs_n_t <= 1'b0; if (wr_en == 1'b1) begin tx_bit_sel <= tx_bit_sel + 'd1; cs_n_t <= 1'b0; case(tx_bit_sel) 5'd0: mosi_t <= reg_cfg[23]; //Read and write bits 5'd1: mosi_t <= reg_cfg[22]; //empty 5'd2: mosi_t <= reg_cfg[21]; //empty 5'd3 : mosi_t <= reg_cfg[20]; //addr[12] 5'd4 : mosi_t <= reg_cfg[19]; //addr[11] 5'd5 : mosi_t <= reg_cfg[18]; //addr[10] 5'd6 : mosi_t <= reg_cfg[17]; //addr[9] 5'd7 : mosi_t <= reg_cfg[16]; //addr[8] 5'd8 : mosi_t <= reg_cfg[15]; //addr[7] 5'd9 : mosi_t <= reg_cfg[14]; //addr[6] 5'd10: mosi_t <= reg_cfg[13]; //addr[5] 5'd11: mosi_t <= reg_cfg[12]; //addr[4] 5'd12: mosi_t <= reg_cfg[11]; //addr[3] 5'd13: mosi_t <= reg_cfg[10]; //addr[2] 5'd14: mosi_t <= reg_cfg[9]; //addr[1] 5'd15: begin mosi_t <= reg_cfg[8]; //addr[0] tx_bit_sel <= 'd0; skip_en <= 1'b1; end endcase end end TX_PAYLOAD : begin cs_n_t <= 1'b0; if (wr_en == 1'b1) begin tx_bit_sel <= tx_bit_sel + 'd1; case(tx_bit_sel) 5'd0: mosi_t <= reg_cfg[7]; //data[7] 5'd1 : mosi_t <= reg_cfg[6]; //data[6] 5'd2 : mosi_t <= reg_cfg[5]; //data[5] 5'd3 : mosi_t <= reg_cfg[4]; //data[4] 5'd4 : mosi_t <= reg_cfg[3]; //data[3] 5'd5 : mosi_t <= reg_cfg[2]; //data[2] 5'd6: mosi_t <= reg_cfg[1]; //data[1] 5'd7 : begin mosi_t <= reg_cfg[0]; //data[0] tx_bit_sel <= 'd0; skip_en <= 1'b1; end endcase end end TX_END : begin if(reg_cnt != 'd0) begin reg_cnt <= reg_cnt - 1'd1; skip_en <= 1'b1; end end default:; endcase end end endmodule
The simulation results are as follows:
This article is for communication only, please correct me if there are any errors.