zynq-rs/libboard_zynq/src/devc/mod.rs

191 lines
6.1 KiB
Rust

use crate::slcr;
use libregister::*;
use log::debug;
mod regs;
pub struct DevC {
regs: &'static mut regs::RegisterBlock,
}
/// DMA transfer type for PCAP
/// All insecure, we do not implement encrypted transfer
#[derive(PartialEq)]
pub enum TransferType {
PcapWrite,
PcapReadback,
ConcurrentReadWrite,
}
pub const INVALID_ADDR: u32 = 0xFFFFFFFF;
impl DevC {
pub fn new() -> Self {
DevC {
regs: regs::RegisterBlock::devc(),
}
}
pub fn enable(&mut self) {
unsafe {
// unlock register with magic pattern
self.regs.unlock.write(0x757BDF0D);
}
self.regs
.control
.modify(|_, w| w.pcap_mode(true).pcap_pr(true));
self.regs
.int_mask
.write(self::regs::int_mask::Write { inner: 0xFFFFFFFF });
self.clear_interrupts();
}
pub fn disable(&mut self) {
self.regs
.control
.modify(|_, w| w.pcap_mode(false).pcap_pr(false))
}
pub fn is_done(&self) -> bool {
// Note: contrary to what the TRM says, this appears to be simply
// the state of the DONE signal.
self.regs.int_sts.read().ixr_pcfg_done()
}
pub fn program(&mut self, src_addr: u32, len: u32) {
debug!("Init preload FPGA");
slcr::RegisterBlock::unlocked(|slcr| {
slcr.init_preload_fpga();
});
debug!("Toggling PROG_B");
// set PCFG_PROG_B to high low high
self.regs.control.modify(|_, w| w.pcfg_prog_b(true));
self.regs.control.modify(|_, w| w.pcfg_prog_b(false));
while self.regs.status.read().pcfg_init() {}
self.regs.control.modify(|_, w| w.pcfg_prog_b(true));
while !self.regs.status.read().pcfg_init() {}
self.regs.int_sts.write(
self::regs::IntSts::zeroed()
.pss_cfg_reset_b_int(true)
.ixr_pcfg_cfg_rst(true),
);
debug!("ADDR: {:0X}", src_addr);
self.dma_transfer(
src_addr | 0x1,
INVALID_ADDR | 0x1,
len,
len,
TransferType::PcapWrite,
);
self.wait_dma_transfer_complete();
debug!("INT_STS: {:0X}", self.regs.int_sts.read().inner);
debug!("STATUS: {:0X}", self.regs.status.read().inner);
debug!("Waiting for done");
while !self.is_done() {}
debug!("INT_STS: {:0X}", self.regs.int_sts.read().inner);
debug!("Init postload FPGA");
slcr::RegisterBlock::unlocked(|slcr| {
slcr.init_postload_fpga();
});
}
/// Initiate DMA transaction, all lengths are in words.
/// > This function is not meant to be used directly.
fn initiate_dma(&mut self, src_addr: u32, dest_addr: u32, src_len: u32, dest_len: u32) {
self.regs.dma_src_addr.modify(|_, w| w.src_addr(src_addr));
self.regs
.dma_dest_addr
.modify(|_, w| w.dest_addr(dest_addr));
self.regs.dma_src_len.modify(|_, w| w.dma_len(src_len));
self.regs.dma_dest_len.modify(|_, w| w.dma_len(dest_len));
}
/// DMA transfer, all lengths are in words.
pub fn dma_transfer(
&mut self,
src_addr: u32,
dest_addr: u32,
src_len: u32,
dest_len: u32,
transfer_type: TransferType,
) -> Result<(), &str> {
if self.regs.status.read().dma_cmd_q_f() {
return Err("DMA busy");
}
if transfer_type != TransferType::ConcurrentReadWrite
&& !self.regs.status.read().pcfg_init()
{
return Err("Fabric not initialized");
}
match &transfer_type {
TransferType::PcapReadback => {
// clear internal PCAP loopback
self.regs.mctrl.modify(|_, w| w.pcap_lpbk(false));
// send READ frame command
self.initiate_dma(src_addr, INVALID_ADDR, src_len, 0);
// wait until DMA done
while !self.regs.int_sts.read().ixr_d_p_done() {}
// initiate the DMA write
self.initiate_dma(INVALID_ADDR, dest_addr, 0, dest_len);
}
TransferType::PcapWrite | TransferType::ConcurrentReadWrite => {
self.regs
.mctrl
.modify(|_, w| w.pcap_lpbk(transfer_type == TransferType::ConcurrentReadWrite));
// PCAP data transmitted every clock
self.regs.control.modify(|_, w| w.pcap_rate_en(false));
self.initiate_dma(src_addr, dest_addr, src_len, dest_len);
}
}
Ok(())
}
pub fn wait_dma_transfer_complete(&mut self) {
debug!("Wait for DMA done");
while !self.regs.int_sts.read().ixr_dma_done() {}
self.regs
.int_sts
.write(self::regs::IntSts::zeroed().ixr_dma_done(true));
}
pub fn dump_registers(&self) {
debug!("Control: 0x{:0X}", self.regs.control.read().inner);
debug!("Status: 0x{:0X}", self.regs.status.read().inner);
debug!("INT STS: 0x{:0X}", self.regs.int_sts.read().inner);
}
pub fn clear_interrupts(&mut self) {
self.regs.int_sts.modify(|_, w| {
w.pss_gts_usr_b_int(true)
.pss_fst_cfg_b_int(true)
.pss_gpwrdwn_b_int(true)
.pss_gts_cfg_b_int(true)
.pss_cfg_reset_b_int(true)
.ixr_axi_wto(true)
.ixr_axi_werr(true)
.ixr_axi_rto(true)
.ixr_axi_rerr(true)
.ixr_rx_fifo_ov(true)
.ixr_wr_fifo_lvl(true)
.ixr_rd_fifo_lvl(true)
.ixr_dma_cmd_err(true)
.ixr_dma_q_ov(true)
.ixr_dma_done(true)
.ixr_d_p_done(true)
.ixr_p2d_len_err(true)
.ixr_pcfg_hmac_err(true)
.ixr_pcfg_seu_err(true)
.ixr_pcfg_por_b(true)
.ixr_pcfg_cfg_rst(true)
.ixr_pcfg_done(true)
.ixr_pcfg_init_pe(true)
.ixr_pcfg_init_ne(true)
})
}
}