`timescale 1ns / 1ns // Serial device support // Handles one each of MAX1202, MAX5742, TCN75, and ADF4001 // November 19, 2002: TCN75 support is obviously flawed and // incomplete; all other device waveforms need checking // November 25, 2002: all waveforms look proper in simulation; // Xilinx ISE 4.2 reports 32 FF, 70 4-LUT, 41 Slices module sportx( clk, host_addr, host_write, write_bus, read_bus, SCLK, SDIN, DOUT1202, SDA75, CS1202, CS5742, PLL_CLK, PLL_DATA, PLL_LE, PLL_MUXOUT, PLL_CE); // local bus interface to host // reads are passive // host_write is guaranteed to be valid for exactly one (posedge clk) // adjust width of host_addr as needed input clk; input host_write; input host_addr; input [15:0] write_bus; output [15:0] read_bus; // Housekeeping devices output SCLK, SDIN, CS1202, CS5742; reg SCLK, SDIN, CS1202, CS5742; inout SDA75; input DOUT1202; // ADF4001 pins are kept separate for noise reasons. // Leave them quiet when using other devices. output PLL_CLK, PLL_DATA, PLL_LE, PLL_CE; reg PLL_CLK, PLL_DATA, PLL_LE, PLL_CE; input PLL_MUXOUT; // ====================end of Verilog interface definition // Software register map: // host_addr // 0 16 bit data shift register // 1 control: // 0x001 running shared for all devices // devsel two bits to choose active device // 00 - MAX1202 // 01 - TCN75 // 10 - ADF4001 // 11 - MAX5742 // 0x08 MUXOUT from adf4001 (read only) // 0x10 CE to adf4001 (1 to turn on chip) // 0x20 LE to adf4001 (0 during shifts) // 0x40 tcn_ckhi force tcn75 clock high (use for start/stop) // 0x80 CS to max5742 (0 during shifts) // // All serial devices are intertwined. The software may only // interact with one at a time. // // Software drive notes (also see the associated sportx_tb for example use): // // all: writing a '1' to the run bit starts a serial shift operation // to the specified device. Software should poll the status // register until that bit drops back to '0' at completion. // // max1202: put the 8 control bits into the msb of data, and trigger // a transaction. All 16 bits of the result land in the // data register. CS pin handling is automatic. // // tcn75: Since the TCN75 SDA pin is open collector, there is no difference // coded here between reads and writes. The pin status is always // recorded in the shift register (data). Make sure to write a 1 // during any bit time that you want to receive from the TCN75. // Transactions use 10 data bits: 8 data, 1 ack, and one trailer. // Create the start/stop protocol in software, using the tcn_ckhi // bit of the status register. Writes to data[9] show up immediately // on the SDA pin; in general, data[9] of any given write should // match data[0] of the previous write. // // adf4001: data is written 8 bits at a time. Lower 'LE', send // 24 bits split into three transactions, finally raise 'LE'. // You can turn off the chip by setting 'CE' low. The MUXOUT // pin is available at all times in the status register, useful // if you configure that output as LD. // // max5742: All 16 bits of the data are written at once. Set 'CS' to 0 // before transactions, and 1 afterwards. reg [15:0] data; // shift register shared for all devices reg running, tcn_ckhi; // control bits, also PLL_CE, PLL_LE, CS5742 reg [1:0] devsel; reg [4:0] divider; // slows down incoming 25 MHz clock reg [5:0] bitcnt; // runs at double the bit shift rate reg tcn_inp, tcn_clk, muxout_sample; `ifdef SIMULATE initial divider=0; `endif // This status readback is overkill, the software really only needs // to know {muxout_sample, running} wire [7:0] status = {CS5742, tcn_ckhi, PLL_LE, PLL_CE, muxout_sample, devsel, running}; assign read_bus = host_addr ? status : data ; reg tick, ldin, endc, dout, sck; always @(devsel or bitcnt or DOUT1202 or SDA75 or muxout_sample or divider or data or tcn_inp or tcn_clk or tcn_ckhi or running) case (devsel) 2'b00: begin // MAX1202 // fails Tcss > 100 ns tick = (divider[2:0] == 0); // 320 ns ticks -> 640 ns clock period ldin = DOUT1202; endc = (bitcnt == 6'd49); // 24 clock rising edges dout = data[15]; sck = ~bitcnt[0] & (bitcnt[5:1] != 0) & running; end 2'b01: begin // TCN75 tick = (divider == 0); // 1280 ns ticks -> 2560 ns clock period ldin = tcn_inp; endc = (bitcnt == 6'd20); // 9 clock pulses dout = 1'b0; sck = tcn_clk | tcn_ckhi; end 2'b10: begin // ADF4001 tick = 1'b1; // continuous ticks -> 80 ns clock period ldin = muxout_sample; // only useful if MUXOUT=SD_out endc = (bitcnt == 6'd15); // 8 clock rising edges dout = 1'b0; sck = 1'b0; end 2'b11: begin // MAX5742 tick = 1'b1; // continuous ticks -> 80 ns clock period ldin = 1'b0; // no readback possible; loopback analog output to verify endc = (bitcnt == 6'd31); // 16 clock falling edges dout = data[15]; sck = ~bitcnt[0]; end endcase wire tcn_drive = data[10] | (devsel != 2'b01); assign SDA75 = tcn_drive ? 1'bz : 1'b0 ; wire shift_enable = tick & running & bitcnt[0]; wire host_write_data = host_write & ~host_addr; wire host_write_ctl = host_write & host_addr; always @(posedge clk) begin divider <= divider + 1'b1; if (host_write_ctl | endc) running <= host_write_ctl & write_bus[0]; if (host_write_ctl) begin devsel <= write_bus[2:1]; // write_bus[3] ignored, since muxout_sample is read-only PLL_CE <= write_bus[4]; PLL_LE <= write_bus[5]; tcn_ckhi <= write_bus[6]; CS5742 <= write_bus[7]; end if (shift_enable | host_write_data) data <= host_write_data ? write_bus : {data[14:0],ldin}; if (tick) bitcnt <= running ? (bitcnt + 1'b1) : 0 ; CS1202 <= ~running | (devsel != 2'b00); SCLK <= sck; SDIN <= dout; PLL_CLK <= bitcnt[0] & (devsel == 2'b10); PLL_DATA <= data[7] & (devsel == 2'b10); muxout_sample <= PLL_MUXOUT; if (divider[4]) tcn_clk <= ~bitcnt[0] & (bitcnt[4:1] != 0) & running; if (tick & running & ~bitcnt[0]) tcn_inp <= SDA75; end endmodule