// sporty.v
// Serial device support for AMC7820 and ADF4001
// $Id$
// Larry Doolittle, LBNL
// llc-suite Copyright (c) 2004, The Regents of the University of
// California, through Lawrence Berkeley National Laboratory (subject
// to receipt of any required approvals from the U.S. Dept. of Energy).
// All rights reserved.
// Your use of this software is pursuant to a "BSD-style" open
// source license agreement, the text of which is in license.txt
// (md5sum a1e0e81c78f6eba050b0e96996f49fd5) that should accompany
// this file. If the license agreement is not there, or if you
// have questions about the license, please contact Berkeley Lab's
// Technology Transfer Department at TTD@lbl.gov referring to
// "llc-suite (LBNL Ref CR-1988)"
`timescale 1ns / 1ns
module sporty
(
input host_clk, // interconnect
input host_we, // interconnect
input [15:0] host_data, // interconnect
output reg [15:0] data, // register SPORTY_DATA
output [15:0] status, // register SPORTY_CNTL
input select_data, // select SPORTY_DATA
input select_cntl, // select SPORTY_CNTL
output pll_muxout_, // interconnect
output reg AMC_CLK, // pin a
output reg AMC_SDI, // pin b
output reg AMC_CS, // pin c
input AMC_SDO, // pin d
output AMC_RST, // pin e
output reg PLL_CLK, // pin g
output reg PLL_DATA, // pin h
output reg PLL_LE, // pin i
input PLL_MUXOUT // pin k
);
// reads are passive
// host_we is guaranteed to be valid for exactly one (posedge host_clk)
// ADF4001 pins are kept separate for noise reasons.
// Leave them quiet when using other devices.
// ====================end of Verilog interface definition
// Software register map:
// 16 bit data shift register
// control:
// 0x01 running shared for all devices
// devsel two bits to choose active device
// 00 - AMC7820
// 01 - unused
// 10 - ADF4001
// 11 - unused
// 0x08 MUXOUT from ADF4001 (read only)
// 0x10 CE to ADF4001 (1 to turn on chip)
// 0x20 LE to ADF4001 (0 during shifts)
// 0x80 CS to AMC7820 (0 during shifts)
//
// All serial devices share a common data shift register and a common
// timing state machine. The software may only interact with one at a time.
//
// Software driver 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.
//
// 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.
// "output reg [15:0] data" above is a shift register shared for all devices
// Special note on timing: the FCM module where this is used uses
// 40 MHz for the host clock. The test benches put this on a virtual
// nanoEngine where the host clock is 25 MHz. The SCLK times documented
// below reflect reality; simulations will seem slow.
assign AMC_RST = 1'b1; // Really !RST
reg running; // control bits, also PLL_LE, CS5742
reg [1:0] devsel;
reg [1:0] divider; // slows down incoming 40 MHz clock
reg [5:0] bitcnt; // runs at double the bit shift rate
reg muxout_sample;
`ifdef SIMULATE
initial divider=0;
`endif
// This status readback is overkill, the software really only needs
// to know {muxout_sample, running}
assign status = {AMC_CS, 1'b0, PLL_LE, 1'b1,
muxout_sample, devsel, running};
assign pll_muxout_ = muxout_sample;
reg tick, ldin, endc, dout, sck;
always @(devsel or bitcnt or AMC_SDO or muxout_sample or
divider or data or running)
case (devsel)
2'b00: begin // AMC7820
tick = (divider[1:0] == 2'b00); // 100 ns ticks -> 200 ns clock period
ldin = AMC_SDO;
endc = (bitcnt == 6'd32); // 16 clock rising edges
dout = data[15];
sck = bitcnt[0];
end
2'b01: begin // unused
tick = (divider[1:0] == 2'b00); // 100 ns ticks -> 200 ns clock period
ldin = 1'b0;
endc = (bitcnt == 6'd20); // 9 clock pulses
dout = 1'b0;
sck = 1'b0;
end
2'b10: begin // ADF4001
tick = (divider[1:0] == 2'b00); // 100 ns ticks -> 200 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 // unused
tick = (divider[1:0] == 2'b00); // 100 ns ticks -> 200 ns clock period
ldin = 1'b0;
endc = (bitcnt == 6'd31); // 16 clock falling edges
dout = 1'b0;
sck = 1'b0;
end
endcase
wire shift_enable = tick & running & bitcnt[0];
wire host_write_data = host_we & select_data;
wire host_write_cntl = host_we & select_cntl;
always @(posedge host_clk) begin
divider <= divider + 1'b1;
if (host_write_cntl | endc)
running <= host_write_cntl & host_data[0];
if (host_write_cntl) begin
devsel <= host_data[2:1];
// host_data[3] ignored, since muxout_sample is read-only
// host_data[4] ignored, see sportx.v
PLL_LE <= host_data[5];
// host_data[6] ignored, see sportx.v
AMC_CS <= host_data[7];
end
if (shift_enable | host_write_data)
data <= host_write_data ? host_data : {data[14:0],ldin};
if (tick) bitcnt <= running ? (bitcnt + 1'b1) : 0 ;
AMC_CLK <= sck;
if (bitcnt[0]) AMC_SDI <= dout;
PLL_CLK <= bitcnt[0] & (devsel == 2'b10);
PLL_DATA <= data[7] & (devsel == 2'b10);
muxout_sample <= PLL_MUXOUT;
end
endmodule