FPGA module for TI's CDCE906/CDCE706Somewhere in 2006 TI (Texas Instruments) released a clock synthesizer, CDCE906, which is pretty much everything one can look for in a system clock generator: Several (up to 6) clock outputs derived from a single reference clock or crystal with excellent multiplication and division capabilities, not to mention low jitter, slew rate control, spread spectrum and more. On top of all, it's surprisingly low-priced. So it's a chip you want to consider, even if all you (think you) need for now, is one single system clock with a fixed frequency. It will save you, when you'll discover three weeks before release that you actually need another clock with a weird frequency. Believe me.Except for the non-E devices, (that is, CDC906 and CDC706, which TI seems to neglect) the devices' setting can be preprogrammed into an on-chip EEPROM, before or after the chip is put on the PCB board. If this is enough for you, frankly, you have nothing to look for on this page. On the other hand, if you want to set the current configuration regardless of the EEPROM, it's possible over the SMBus. Now, SMBus is more or less like I2C (also known as IIC), so if you have a controller on the board with I2C/SMBus cababilities, you may want to use it (if it has I2C, I suggest knowing the differences). If there is no controller around, or you prefer the FPGA to set up the synthesizer, the Verilog code below is what you need. It should be pointed out, that TI also have another very similar device, CDCE706, which is surprisingly enough more capable than its "little brother" CDCE906. Their register structure is identical however, so the code below can be used for CDCE706 as well. The only downside about both devices is that being very versatile, they're also complicated. I'm not going to describe how to run these chips. There is no way out of reading the extensive datasheet, and I promise finding some hidden treasures there. The only thing this module will do for you, is get the right values to the right register. So let's get to business. Before doing anything else, be sure to set the parameter "clk_freq" to the frequency of the module's input clock "clk" (in MHz, nearest integer). This is crucial, because the SMBus is derived from this clock. If several frequencies are possible, make "clk_freq" the highest one. Important notes:
Next we have the actual synthesizer parameters, whose meaning should be obvious to whoever has read the datasheet well. The only thing note is that the term "PLL" has been exchanged for "VCO", which I find more intuitive. So VCO1_M and VCO1_N are the M and N for PLL1, using the datasheet's terminology, etc. The comments in the module itself may help as well. All these inputs can be constants (which will shrink the synthesized logic) or you can wire them to whatever you like, but these inputs must be steady as long as their content is flushed to the chip ( = as long as the "busy" output is high). So all we have left is the module itself. I've lifted the copyright restrictions for it, so you can use it in any way you like. (And by the way: I have no special relations with TI whatsoever. Not even as a freelancer). ----------------------------------- // Eli Billauer, 30.12.06 (Explicitly not copyrighted). // This function is released to the public domain; Any use is allowed. module cdce906 ( clk, reset, send_data, busy, error, sclk, sdata, S0, S1, VCO1_M, VCO1_N, VCO1_bypass, VCO1_hi_freq_range, VCO2_M, VCO2_N, VCO2_bypass, VCO2_hi_freq_range, VCO3_M, VCO3_N, VCO3_bypass, VCO3_hi_freq_range, P0_VCO_select, P0_div, P1_VCO_select, P1_div, P2_VCO_select, P2_div, P3_VCO_select, P3_div, P4_VCO_select, P4_div, P5_VCO_select, P5_div, Y0_en, Y0_inv, Y0_slew_rate, Y0_div_select, Y1_en, Y1_inv, Y1_slew_rate, Y1_div_select, Y2_en, Y2_inv, Y2_slew_rate, Y2_div_select, Y3_en, Y3_inv, Y3_slew_rate, Y3_div_select, Y4_en, Y4_inv, Y4_slew_rate, Y4_div_select, Y5_en, Y5_inv, Y5_slew_rate, Y5_div_select, SSC_mod_select, SSC_freq_select ); input clk; input reset; // Active high output sclk; inout sdata; output S0, S1; output busy, error; input send_data; // PLL settings: input [8:0] VCO1_M; input [11:0] VCO1_N; input VCO1_bypass; // '1' means VCO bypassed input VCO1_hi_freq_range; // '1' means 180-300 MHz input [8:0] VCO2_M; input [11:0] VCO2_N; input VCO2_bypass; // '1' means VCO bypassed input VCO2_hi_freq_range; // '1' means 180-300 MHz input [8:0] VCO3_M; input [11:0] VCO3_N; input VCO3_bypass; // '1' means VCO bypassed input VCO3_hi_freq_range; // '1' means 180-300 MHz // Post divider (P) settings // For Px_VCO_select: // 0 = input clock (bypass) // 1 = VCO1 // 2 = VCO2 // 3 = VCO2 with spread spectrum modulation // 4 = VCO3 input [2:0] P0_VCO_select; input [6:0] P0_div; input [2:0] P1_VCO_select; input [6:0] P1_div; input [2:0] P2_VCO_select; input [6:0] P2_div; input [2:0] P3_VCO_select; input [6:0] P3_div; input [2:0] P4_VCO_select; input [6:0] P4_div; input [2:0] P5_VCO_select; input [6:0] P5_div; // Output (Y) settings input Y0_en; // '1' is output enabled input Y0_inv; // '1' means inverted input [1:0] Y0_slew_rate; // '11' is nominal input [2:0] Y0_div_select; // 0=P0, 1=P1, ... , 5=P5 input Y1_en; // '1' is output enabled input Y1_inv; // '1' means inverted input [1:0] Y1_slew_rate; // '11' is nominal input [2:0] Y1_div_select; // 0=P0, 1=P1, ... , 5=P5 input Y2_en; // '1' is output enabled input Y2_inv; // '1' means inverted input [1:0] Y2_slew_rate; // '11' is nominal input [2:0] Y2_div_select; // 0=P0, 1=P1, ... , 5=P5 input Y3_en; // '1' is output enabled input Y3_inv; // '1' means inverted input [1:0] Y3_slew_rate; // '11' is nominal input [2:0] Y3_div_select; // 0=P0, 1=P1, ... , 5=P5 input Y4_en; // '1' is output enabled input Y4_inv; // '1' means inverted input [1:0] Y4_slew_rate; // '11' is nominal input [2:0] Y4_div_select; // 0=P0, 1=P1, ... , 5=P5 input Y5_en; // '1' is output enabled input Y5_inv; // '1' means inverted input [1:0] Y5_slew_rate; // '11' is nominal input [2:0] Y5_div_select; // 0=P0, 1=P1, ... , 5=P5 // Spread spectrum settings input [2:0] SSC_mod_select; // See datasheet input [3:0] SSC_freq_select; // See datasheet reg SMBus_en, pre_en; reg kickoff; reg [2:0] state; reg get_bit; reg next_bit, this_is_ack, wait_for_ack; reg sdata_d; reg error; reg [4:0] word_addr; reg [3:0] bit_pos; reg no_more_bits; reg [7:0] SMBus_word; reg [11:0] div_counter; reg sclk_logic, sdata_logic; parameter clk_freq = 10; // In MHz, nearest integer parameter st_idle = 0, st_start0 = 1, st_start1 = 2, st_bit0 = 3, st_bit1 = 4, st_bit2 = 5, st_stop0 = 6, st_stop1 = 7; // Emulated open collector output // Note that sclk and sdata must be pulled up, possibly with // a PULLUP constraint on the IOB (or a 10 kOhm ext. resistor) assign sclk = sclk_logic ? 1'bz : 1'b0 ; assign sdata = sdata_logic ? 1'bz : 1'b0 ; assign S0 = 1; // No powerdown assign S1 = 1; // No output inhibit // SMBus_en should be high every 10 us // This allows a huge multicycle path constraint on SBBus_en, // but "kickoff" MUST BE EXCLUDED from the group. always @(posedge clk) begin SMBus_en <= pre_en; sdata_d <= sdata; if (div_counter == ((clk_freq * 10) - 1)) begin div_counter <= 0; pre_en <= 1; end else begin div_counter <= div_counter + 1; pre_en <= 0; end end always @(posedge clk or posedge reset) if (reset) kickoff <= 0; else if (send_data) kickoff <= 1; else if (SMBus_en) kickoff <= 0; assign busy = kickoff || (state != st_idle); always @(posedge clk or posedge reset) if (reset) begin state <= st_idle; error <= 0; end else if (SMBus_en) case (state) st_idle: begin sclk_logic <= 1; sdata_logic <= 1; get_bit <= 0; if (kickoff) state <= st_start0; end st_start0: begin sclk_logic <= 1; sdata_logic <= 0; state <= st_start1; error <= 0; end st_start1: begin sclk_logic <= 0; state <= st_bit0; end st_bit0: begin sclk_logic <= 0; sdata_logic <= next_bit; wait_for_ack <= this_is_ack; get_bit <= 1; state <= st_bit1; end st_bit1: begin sclk_logic <= 1; get_bit <= 0; state <= st_bit2; end st_bit2: begin sclk_logic <= 0; if (wait_for_ack && !sdata_d && !kickoff) state <= st_bit2; else if (no_more_bits) state <= st_stop0; else state <= st_bit0; if (wait_for_ack && sdata_d && sclk_logic) // No acknowledge error <= 1; end st_stop0: begin sclk_logic <= 0; sdata_logic <= 0; state <= st_stop1; end st_stop1: begin sclk_logic <= 1; state <= st_idle; end endcase always @(posedge clk) if (SMBus_en) begin if (kickoff) begin word_addr <= 0; bit_pos <= 7; no_more_bits <= 0; end else if (get_bit) begin bit_pos <= (bit_pos == 0) ? 8 : bit_pos - 1; if (bit_pos == 0) word_addr <= word_addr + 1; if ((word_addr == 29) && (bit_pos == 8)) no_more_bits <= 1; end if (bit_pos == 8) // Ack position begin next_bit <= 1; // Don't pull bus to zero this_is_ack <= 1; end else begin next_bit <= SMBus_word[ bit_pos[2:0] ]; this_is_ack <= 0; end end always @(posedge clk) if (SMBus_en) case (word_addr) 0: SMBus_word <= 8'b1101001_0 ; // Slave address, write 1: SMBus_word <= 0 ; // Command code = Block (write) 2: SMBus_word <= 26 ; // Byte count 3: SMBus_word <= 0 ; // Byte 0 4: SMBus_word <= VCO1_M[7:0] ; 5: SMBus_word <= VCO1_N[7:0] ; 6: SMBus_word <= { VCO1_bypass, VCO2_bypass, VCO3_bypass, VCO1_N[11:8], VCO1_M[8] } ; 7: SMBus_word <= VCO2_M[7:0] ; 8: SMBus_word <= VCO2_N[7:0] ; 9: SMBus_word <= { VCO1_hi_freq_range, VCO2_hi_freq_range, VCO3_hi_freq_range, VCO2_N[11:8], VCO2_M[8] } ; 10: SMBus_word <= VCO3_M[7:0] ; 11: SMBus_word <= VCO3_N[7:0] ; 12: SMBus_word <= { P0_VCO_select, VCO3_N[11:8], VCO3_M[8] } ; 13: SMBus_word <= { P1_VCO_select, 1'b0, 2'b00, 2'b00 } ; 14: SMBus_word <= { 2'b00, P3_VCO_select, P2_VCO_select } ; 15: SMBus_word <= { 2'b00, P5_VCO_select, P4_VCO_select } ; 16: SMBus_word <= { 1'b0, P0_div } ; 17: SMBus_word <= { 1'b0, P1_div } ; 18: SMBus_word <= { 1'b0, P2_div } ; 19: SMBus_word <= { 1'b0, P3_div } ; 20: SMBus_word <= { 1'b0, P4_div } ; 21: SMBus_word <= { 1'b0, P5_div } ; 22: SMBus_word <= { 1'b0, Y0_inv, Y0_slew_rate, Y0_en, Y0_div_select } ; 23: SMBus_word <= { 1'b0, Y1_inv, Y1_slew_rate, Y1_en, Y1_div_select } ; 24: SMBus_word <= { 1'b0, Y2_inv, Y2_slew_rate, Y2_en, Y2_div_select } ; 25: SMBus_word <= { 1'b0, Y3_inv, Y3_slew_rate, Y3_en, Y3_div_select } ; 26: SMBus_word <= { 1'b0, Y4_inv, Y4_slew_rate, Y4_en, Y4_div_select } ; 27: SMBus_word <= { 1'b0, Y5_inv, Y5_slew_rate, Y5_en, Y5_div_select } ; 28: SMBus_word <= { 1'b0, SSC_mod_select, SSC_freq_select } ; // Byte 25 default: SMBus_word <= 0 ; endcase endmodule
Last modified on Thu May 17 17:30:00 2012. E-mail:
|