#[allow(clippy::wildcard_imports)]
use super::{
hmcad1511::*,
AdcMode,
};
use crate::{
transport::{
Deserialize,
Serialize,
Transport,
},
yellow_blocks::Address,
};
use casperfpga_derive::{
address,
CasperSerde,
};
use packed_struct::prelude::*;
use std::sync::{
Mutex,
Weak,
};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Transport(#[from] crate::transport::Error),
#[error("ADC16 controller doesn't support demux modes")]
NoDemux,
}
#[derive(Debug)]
pub struct Adc16<T> {
transport: Weak<Mutex<T>>,
cs: ChipSelect,
}
impl<T> Adc16<T>
where
T: Transport,
{
const NAME: &'static str = "adc16_controller";
#[must_use]
pub fn new(transport: Weak<Mutex<T>>) -> Self {
Self {
transport,
cs: ChipSelect::default(),
}
}
#[allow(clippy::missing_panics_doc)]
pub fn supported_chips(&self) -> Result<u8, Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let word: Adc3Wire = transport.read_addr(Self::NAME)?;
Ok(word.supported_chips.into())
}
#[allow(clippy::missing_panics_doc)]
pub fn revision(&self) -> Result<u8, Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let word: Adc3Wire = transport.read_addr(Self::NAME)?;
Ok(word.revision.into())
}
#[allow(clippy::missing_panics_doc)]
pub fn locked(&self) -> Result<bool, Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let word: Adc3Wire = transport.read_addr(Self::NAME)?;
let ll: u8 = word.line_lock.into();
let num_adcs: u8 = word.supported_chips.into();
Ok(match ll {
0 | 2 => false,
1 => num_adcs <= 4,
3 => true,
_ => unreachable!(),
})
}
pub fn chip_select(&mut self, cs: &ChipSelect) {
self.cs = *cs;
}
fn send_3wire_bit(&self, transport: &mut T, bit: bool) -> Result<(), Error> {
transport.write_addr(
Self::NAME,
&Adc3Wire {
sclk: false,
sdata: bit,
chip_select: self.cs,
..Default::default()
},
)?;
transport.write_addr(
Self::NAME,
&Adc3Wire {
sclk: true,
sdata: bit,
chip_select: self.cs,
..Default::default()
},
)?;
Ok(())
}
fn send_reg_raw(&self, transport: &mut T, addr: u8, val: u16) -> Result<(), Error> {
transport.write_addr(Self::NAME, &Adc3Wire::idle())?;
for i in (0..=7).rev() {
self.send_3wire_bit(transport, ((addr >> i) & 1) == 1)?;
}
for i in (0..=15).rev() {
self.send_3wire_bit(transport, ((val >> i) & 1) == 1)?;
}
transport.write_addr(Self::NAME, &Adc3Wire::idle())?;
Ok(())
}
fn send_reg<R>(&self, transport: &mut T, reg: &R) -> Result<(), Error>
where
R: Address + PackedStruct,
{
let addr = R::addr();
let mut packed = [0u8; 2];
reg.pack_to_slice(&mut packed)
.map_err(crate::transport::Error::Packing)?;
let value = u16::from_be_bytes(packed);
self.send_reg_raw(
transport,
addr.try_into().expect("Address didn't fit in a u8"),
value,
)
}
#[allow(clippy::missing_panics_doc)]
pub fn supports_demux(&self) -> Result<bool, Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
transport.write_addr(
Self::NAME,
&AdcControl {
demux_write_enable: true,
..Default::default()
},
)?;
let demux_test: AdcControl = transport.read_addr(Self::NAME)?;
Ok(!demux_test.demux_write_enable)
}
#[allow(clippy::missing_panics_doc)]
pub fn get_demux(&self) -> Result<Option<DemuxMode>, Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
Ok(if self.supports_demux()? {
let demux_test: AdcControl = transport.read_addr(Self::NAME)?;
Some(demux_test.demux_mode)
} else {
None
})
}
#[allow(clippy::missing_panics_doc)]
pub fn set_demux(&self, mode: DemuxMode) -> Result<(), Error> {
if self.supports_demux()? {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let mut ctl: AdcControl = transport.read_addr(Self::NAME)?;
ctl.demux_mode = mode;
transport.write_addr(Self::NAME, &ctl)?;
Ok(())
} else {
Err(Error::NoDemux)
}
}
#[allow(clippy::missing_panics_doc)]
pub fn reset(&self) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
self.send_reg(&mut transport, &Reset { reset: true })
}
#[allow(clippy::missing_panics_doc)]
pub fn power_down(&self) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
self.send_reg(
&mut transport,
&SleepPd {
pd: true,
..Default::default()
},
)
}
#[allow(clippy::missing_panics_doc)]
pub fn power_up(&self) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
self.send_reg(
&mut transport,
&SleepPd {
pd: false,
..Default::default()
},
)
}
#[allow(clippy::missing_panics_doc)]
pub fn power_cycle(&mut self) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
self.send_reg(
&mut transport,
&SleepPd {
pd: true,
..Default::default()
},
)?;
let old_cs = self.cs;
for i in 0..=7 {
self.cs = ChipSelect::by_number(i);
self.send_reg(&mut transport, &SleepPd::default())?;
}
self.cs = old_cs;
Ok(())
}
#[allow(clippy::missing_panics_doc)]
pub fn enable_pattern(&self, pat: TestPattern) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
self.send_reg(&mut transport, &PatternCtl::default())?;
self.send_reg(&mut transport, &DeskewSyncPattern::default())?;
match pat {
TestPattern::Ramp => self.send_reg(
&mut transport,
&PatternCtl {
pattern: Pattern::Ramp,
},
),
TestPattern::Deskew => self.send_reg(
&mut transport,
&DeskewSyncPattern {
pat_deskew_sync: DeskewSyncMode::Deskew,
},
),
TestPattern::Sync => self.send_reg(
&mut transport,
&DeskewSyncPattern {
pat_deskew_sync: DeskewSyncMode::Sync,
},
),
TestPattern::Custom1 | TestPattern::Custom2 => self.send_reg(
&mut transport,
&PatternCtl {
pattern: Pattern::SingleCustom,
},
),
TestPattern::Dual => self.send_reg(
&mut transport,
&PatternCtl {
pattern: Pattern::DualCustom,
},
),
TestPattern::None => Ok(()),
}
}
#[allow(clippy::missing_panics_doc)]
pub fn custom_1(&self, bits: [bool; 8]) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
self.send_reg(&mut transport, &CustomPattern1 { bits_custom1: bits })
}
#[allow(clippy::missing_panics_doc)]
pub fn custom_2(&self, bits: [bool; 8]) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
self.send_reg(&mut transport, &CustomPattern2 { bits_custom2: bits })
}
#[allow(clippy::missing_panics_doc)]
pub fn bitslip(&self, bitslips: Bitslip) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let slip = AdcControl {
bitslip: bitslips,
..Default::default()
};
transport.write_addr(Self::NAME, &AdcControl::default())?;
transport.write_addr(Self::NAME, &slip)?;
transport.write_addr(Self::NAME, &AdcControl::default())?;
Ok(())
}
#[allow(clippy::missing_panics_doc)]
pub fn snap_req(&self) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let snap_req = AdcControl {
snap_request: true,
..Default::default()
};
transport.write_addr(Self::NAME, &AdcControl::default())?;
transport.write_addr(Self::NAME, &snap_req)?;
transport.write_addr(Self::NAME, &AdcControl::default())?;
Ok(())
}
#[allow(clippy::missing_panics_doc)]
pub fn set_operating_mode(&self, mode: AdcMode, freq: f64) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let low_clk = LvdsOutputControl {
low_clk_freq: ((mode == AdcMode::Single) && (freq < 240.))
|| ((mode == AdcMode::Dual) && (freq < 120.))
|| ((mode == AdcMode::Quad) && (freq < 60.)),
lvds_shift: LvdsShift::Disabled,
};
let chan_cfg = ChanNumClkDiv {
channel_num: match mode {
AdcMode::Single => ChannelNum::Single,
AdcMode::Dual => ChannelNum::Dual,
AdcMode::Quad => ChannelNum::Quad,
},
clk_divide: ClockDivide::_1,
};
self.send_reg(&mut transport, &chan_cfg)?;
self.send_reg(&mut transport, &low_clk)?;
Ok(())
}
#[allow(clippy::missing_panics_doc)]
pub fn init(&mut self, mode: AdcMode, freq: f64) -> Result<(), Error> {
self.reset()?;
self.power_down()?;
self.set_operating_mode(mode, freq)?;
self.power_up()?;
Ok(())
}
#[allow(clippy::missing_panics_doc)]
pub fn input_select(&self, inputs: ChannelInput) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
let mut selections = [InputSelect::default(); 4];
match inputs {
ChannelInput::Single(a) => {
selections[0] = a;
selections[1] = a;
selections[2] = a;
selections[3] = a;
}
ChannelInput::Dual(a, b) => {
selections[0] = a;
selections[1] = a;
selections[2] = b;
selections[3] = b;
}
ChannelInput::Quad(a, b, c, d) => {
selections[0] = a;
selections[1] = b;
selections[2] = c;
selections[3] = d;
}
}
self.send_reg(
&mut transport,
&InputSelect12 {
inp_sel_adc1: selections[0],
inp_sel_adc2: selections[1],
},
)?;
self.send_reg(
&mut transport,
&InputSelect34 {
inp_sel_adc3: selections[2],
inp_sel_adc4: selections[3],
},
)?;
Ok(())
}
#[allow(clippy::missing_panics_doc)]
pub fn disable_termination(&self) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
self.send_reg(
&mut transport,
&LvdsTerminations {
en_lvds_term: false,
..Default::default()
},
)
}
#[allow(clippy::missing_panics_doc)]
pub fn set_terminations(
&self,
lclk: LvdsTermination,
frame: LvdsTermination,
data: LvdsTermination,
) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
self.send_reg(
&mut transport,
&LvdsTerminations {
en_lvds_term: true,
term_lclk: lclk,
term_frame: frame,
term_dat: data,
},
)
}
#[allow(clippy::missing_panics_doc)]
pub fn set_drive_strength(
&self,
lclk: LvdsDriveStrength,
frame: LvdsDriveStrength,
data: LvdsDriveStrength,
) -> Result<(), Error> {
let tarc = self.transport.upgrade().unwrap();
let mut transport = (*tarc).lock().unwrap();
self.send_reg(
&mut transport,
&LvdsDrives {
ilvds_lclk: lclk,
ilvds_frame: frame,
ilvds_dat: data,
},
)
}
}
#[derive(Debug, Copy, Clone)]
pub enum ChannelInput {
Single(InputSelect),
Dual(InputSelect, InputSelect),
Quad(InputSelect, InputSelect, InputSelect, InputSelect),
}
#[derive(Debug, Copy, Clone, Default)]
pub enum TestPattern {
#[default]
Ramp,
Deskew,
Sync,
Custom1,
Custom2,
Dual,
None,
}
#[derive(PackedStruct, Default, Debug, PartialEq, Eq, Copy, Clone)]
#[packed_struct(bit_numbering = "msb0")]
#[allow(clippy::struct_excessive_bools)]
pub struct ChipSelect {
#[packed_field(bits = "7")]
pub a: bool,
#[packed_field(bits = "6")]
pub b: bool,
#[packed_field(bits = "5")]
pub c: bool,
#[packed_field(bits = "4")]
pub d: bool,
#[packed_field(bits = "3")]
pub e: bool,
#[packed_field(bits = "2")]
pub f: bool,
#[packed_field(bits = "1")]
pub g: bool,
#[packed_field(bits = "0")]
pub h: bool,
}
impl ChipSelect {
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn select_all() -> Self {
Self::unpack_from_slice(&[0b1111_1111]).unwrap()
}
fn by_number(v: u8) -> Self {
match v {
0 => Self {
a: true,
..Default::default()
},
1 => Self {
b: true,
..Default::default()
},
2 => Self {
c: true,
..Default::default()
},
3 => Self {
d: true,
..Default::default()
},
4 => Self {
e: true,
..Default::default()
},
5 => Self {
f: true,
..Default::default()
},
6 => Self {
g: true,
..Default::default()
},
7 => Self {
h: true,
..Default::default()
},
_ => unreachable!(),
}
}
}
#[derive(Debug, PackedStruct, CasperSerde, Default)]
#[address(0x0)]
#[packed_struct(bit_numbering = "msb0", size_bytes = "4")]
pub struct Adc3Wire {
#[packed_field(bits = "6..=7")]
line_lock: Integer<u8, packed_bits::Bits<2>>,
#[packed_field(bits = "8..=11")]
supported_chips: Integer<u8, packed_bits::Bits<4>>,
#[packed_field(bits = "14..=15")]
revision: Integer<u8, packed_bits::Bits<2>>,
#[packed_field(bits = "22")]
sclk: bool,
#[packed_field(bits = "23")]
sdata: bool,
#[packed_field(bits = "24..=31")]
chip_select: ChipSelect,
}
impl Adc3Wire {
fn idle() -> Self {
Self {
sclk: true,
..Default::default()
}
}
}
#[derive(PrimitiveEnum, Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum DemuxMode {
#[default]
SingleChannel = 0,
DualChannel = 1,
QuadChannel = 2,
}
#[derive(PackedStruct, Default, Debug, PartialEq, Eq)]
#[packed_struct(bit_numbering = "msb0")]
#[allow(clippy::struct_excessive_bools)]
pub struct Bitslip {
#[packed_field(bits = "7")]
a: bool,
#[packed_field(bits = "6")]
b: bool,
#[packed_field(bits = "5")]
c: bool,
#[packed_field(bits = "4")]
d: bool,
#[packed_field(bits = "3")]
e: bool,
#[packed_field(bits = "2")]
f: bool,
#[packed_field(bits = "1")]
g: bool,
#[packed_field(bits = "0")]
h: bool,
}
#[derive(Debug, PackedStruct, CasperSerde, Default)]
#[address(0x4)]
#[packed_struct(bit_numbering = "msb0", size_bytes = "4")]
pub struct AdcControl {
#[packed_field(bits = "5")]
demux_write_enable: bool,
#[packed_field(bits = "6..=7", ty = "enum")]
demux_mode: DemuxMode,
#[packed_field(bits = "11")]
reset: bool,
#[packed_field(bits = "15")]
snap_request: bool,
#[packed_field(bits = "16..=23")]
bitslip: Bitslip,
#[packed_field(bits = "27..=31")]
delay_taps: [bool; 5],
}
#[derive(Debug, PackedStruct, CasperSerde)]
#[address(0x8)]
#[packed_struct(bit_numbering = "msb0", size_bytes = "4")]
pub struct AdcDelayAStrobe {
#[packed_field(bits = "0..=3")]
h: [bool; 4],
#[packed_field(bits = "4..=7")]
g: [bool; 4],
#[packed_field(bits = "8..=11")]
f: [bool; 4],
#[packed_field(bits = "12..=15")]
e: [bool; 4],
#[packed_field(bits = "16..=19")]
d: [bool; 4],
#[packed_field(bits = "20..=23")]
c: [bool; 4],
#[packed_field(bits = "24..=27")]
b: [bool; 4],
#[packed_field(bits = "28..=31")]
a: [bool; 4],
}
#[derive(Debug, PackedStruct, CasperSerde)]
#[address(0xC)]
#[packed_struct(bit_numbering = "msb0", size_bytes = "4")]
pub struct AdcDelayBStrobe {
#[packed_field(bits = "0..=3")]
h: [bool; 4],
#[packed_field(bits = "4..=7")]
g: [bool; 4],
#[packed_field(bits = "8..=11")]
f: [bool; 4],
#[packed_field(bits = "12..=15")]
e: [bool; 4],
#[packed_field(bits = "16..=19")]
d: [bool; 4],
#[packed_field(bits = "20..=23")]
c: [bool; 4],
#[packed_field(bits = "24..=27")]
b: [bool; 4],
#[packed_field(bits = "28..=31")]
a: [bool; 4],
}