Merge branch 'sinara'

This commit is contained in:
Sebastien Bourdeauducq 2017-09-30 01:11:16 +08:00
commit b4c52c34f7
28 changed files with 3542 additions and 123 deletions

View File

@ -8,6 +8,9 @@ build = "build.rs"
name = "board" name = "board"
path = "lib.rs" path = "lib.rs"
[build-dependencies]
build_artiq = { path = "../libbuild_artiq" }
[dependencies] [dependencies]
log = { version = "0.3", default-features = false } log = { version = "0.3", default-features = false }

View File

@ -2,7 +2,7 @@ use csr;
use clock; use clock;
use ad9154_reg; use ad9154_reg;
fn spi_setup() { fn spi_setup(dacno: u8) {
unsafe { unsafe {
csr::converter_spi::offline_write(1); csr::converter_spi::offline_write(1);
csr::converter_spi::cs_polarity_write(0); csr::converter_spi::cs_polarity_write(0);
@ -14,7 +14,7 @@ fn spi_setup() {
csr::converter_spi::clk_div_read_write(16); csr::converter_spi::clk_div_read_write(16);
csr::converter_spi::xfer_len_write_write(24); csr::converter_spi::xfer_len_write_write(24);
csr::converter_spi::xfer_len_read_write(0); csr::converter_spi::xfer_len_read_write(0);
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_DAC_CS); csr::converter_spi::cs_write(1 << (csr::CONFIG_CONVERTER_SPI_FIRST_AD9154_CS + dacno as u32));
csr::converter_spi::offline_write(0); csr::converter_spi::offline_write(0);
} }
} }
@ -35,39 +35,39 @@ fn read(addr: u16) -> u8 {
} }
} }
fn jesd_reset(rst: bool) { fn jesd_reset(dacno: u8, rst: bool) {
unsafe { unsafe {
csr::ad9154::jesd_jreset_write(if rst {1} else {0}) (csr::AD9154[dacno as usize].jesd_jreset_write)(if rst {1} else {0})
} }
} }
fn jesd_enable(en: bool) { fn jesd_enable(dacno: u8, en: bool) {
unsafe { unsafe {
csr::ad9154::jesd_control_enable_write(if en {1} else {0}) (csr::AD9154[dacno as usize].jesd_control_enable_write)(if en {1} else {0})
} }
} }
fn jesd_ready() -> bool { fn jesd_ready(dacno: u8) -> bool {
unsafe { unsafe {
csr::ad9154::jesd_control_ready_read() != 0 (csr::AD9154[dacno as usize].jesd_control_ready_read)() != 0
} }
} }
fn jesd_prbs(en: bool) { fn jesd_prbs(dacno: u8, en: bool) {
unsafe { unsafe {
csr::ad9154::jesd_control_prbs_config_write(if en {1} else {0}) (csr::AD9154[dacno as usize].jesd_control_prbs_config_write)(if en {1} else {0})
} }
} }
fn jesd_stpl(en: bool) { fn jesd_stpl(dacno: u8, en: bool) {
unsafe { unsafe {
csr::ad9154::jesd_control_stpl_enable_write(if en {1} else {0}) (csr::AD9154[dacno as usize].jesd_control_stpl_enable_write)(if en {1} else {0})
} }
} }
fn jesd_jsync() -> bool { fn jesd_jsync(dacno: u8) -> bool {
unsafe { unsafe {
csr::ad9154::jesd_control_jsync_read() != 0 (csr::AD9154[dacno as usize].jesd_control_jsync_read)() != 0
} }
} }
@ -421,19 +421,24 @@ fn monitor() {
write(ad9154_reg::IRQ_STATUS3, 0x00); write(ad9154_reg::IRQ_STATUS3, 0x00);
} }
fn cfg() -> Result<(), &'static str> { fn cfg(dacno: u8) -> Result<(), &'static str> {
jesd_enable(false); spi_setup(dacno);
jesd_prbs(false); // Release the JESD clock domain reset late, as we need to
jesd_stpl(false); // set up clock chips before.
jesd_reset(dacno, false);
jesd_enable(dacno, false);
jesd_prbs(dacno, false);
jesd_stpl(dacno, false);
clock::spin_us(10000); clock::spin_us(10000);
jesd_enable(true); jesd_enable(dacno, true);
dac_setup()?; dac_setup()?;
jesd_enable(false); jesd_enable(dacno, false);
clock::spin_us(10000); clock::spin_us(10000);
jesd_enable(true); jesd_enable(dacno, true);
monitor(); monitor();
let t = clock::get_ms(); let t = clock::get_ms();
while !jesd_ready() { while !jesd_ready(dacno) {
if clock::get_ms() > t + 200 { if clock::get_ms() > t + 200 {
return Err("JESD ready timeout"); return Err("JESD ready timeout");
} }
@ -442,7 +447,7 @@ fn cfg() -> Result<(), &'static str> {
if read(ad9154_reg::CODEGRPSYNCFLG) != 0x0f { if read(ad9154_reg::CODEGRPSYNCFLG) != 0x0f {
return Err("bad CODEGRPSYNCFLG") return Err("bad CODEGRPSYNCFLG")
} }
if !jesd_jsync() { if !jesd_jsync(dacno) {
return Err("bad SYNC") return Err("bad SYNC")
} }
if read(ad9154_reg::FRAMESYNCFLG) != 0x0f { if read(ad9154_reg::FRAMESYNCFLG) != 0x0f {
@ -458,18 +463,10 @@ fn cfg() -> Result<(), &'static str> {
} }
pub fn init() -> Result<(), &'static str> { pub fn init() -> Result<(), &'static str> {
spi_setup(); for dacno in 0..csr::AD9154.len() {
let dacno = dacno as u8;
// Release the JESD clock domain reset late, as we need to debug!("setting up DAC #{}", dacno);
// set up clock chips before. cfg(dacno)?;
jesd_reset(false);
for i in 0..99 {
let outcome = cfg();
match outcome {
Ok(_) => return outcome,
Err(e) => warn!("config attempt #{} failed ({}), retrying", i, e)
} }
} Ok(())
cfg()
} }

View File

@ -13,7 +13,7 @@ fn spi_setup() {
csr::converter_spi::clk_div_read_write(16); csr::converter_spi::clk_div_read_write(16);
csr::converter_spi::xfer_len_write_write(24); csr::converter_spi::xfer_len_write_write(24);
csr::converter_spi::xfer_len_read_write(0); csr::converter_spi::xfer_len_read_write(0);
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_CLK_CS); csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_AD9516_CS);
csr::converter_spi::offline_write(0); csr::converter_spi::offline_write(0);
} }
} }

View File

@ -1,15 +1,29 @@
extern crate build_artiq;
use std::env; use std::env;
use std::path::Path;
use std::io::{BufRead, BufReader};
use std::fs::File; use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::process::Command;
fn gen_hmc7043_writes() {
println!("cargo:rerun-if-changed=hmc7043_gen_writes.py");
println!("cargo:rerun-if-changed=hmc7043_guiexport_10gbps.py");
let hmc7043_writes =
Command::new("python3")
.arg("hmc7043_gen_writes.py")
.arg("hmc7043_guiexport_10gbps.py")
.output()
.ok()
.and_then(|o| String::from_utf8(o.stdout).ok())
.unwrap();
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let mut f = File::create(out_dir.join("hmc7043_writes.rs")).unwrap();
write!(f, "{}", hmc7043_writes).unwrap();
}
fn main() { fn main() {
let out_dir = env::var("BUILDINC_DIRECTORY").unwrap(); build_artiq::misoc_cfg();
let cfg_path = Path::new(&out_dir).join("generated").join("rust-cfg"); gen_hmc7043_writes();
println!("cargo:rerun-if-changed={}", cfg_path.to_str().unwrap());
let f = BufReader::new(File::open(&cfg_path).unwrap());
for line in f.lines() {
println!("cargo:rustc-cfg={}", line.unwrap());
}
} }

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
# The HMC7043 GUI exports register write lists into Python files.
# This script converts them into Rust arrays.
import sys
import runpy
class DUT:
def __init__(self):
self.writes = []
def write(self, address, value):
self.writes.append((address, value))
def main():
dut = DUT()
runpy.run_path(sys.argv[1], {"dut": dut})
print("// This file was autogenerated by hmc7043_gen_writes.py")
print("const HMC7043_WRITES: [(u16, u8); {}] = [".format(len(dut.writes)))
for address, value in dut.writes:
print(" (0x{:04x}, 0x{:02x}),".format(address, value))
print("];")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,697 @@
# glbl_cfg1_swrst[0:0] = 0x0
dut.write(0x0, 0x0)
# glbl_cfg1_sleep[0:0] = 0x0
# glbl_cfg1_restart[1:1] = 0x0
# sysr_cfg1_pulsor_req[2:2] = 0x0
# grpx_cfg1_mute[3:3] = 0x0
# dist_cfg1_perf_floor[6:6] = 0x0
# sysr_cfg1_reseed_req[7:7] = 0x0
dut.write(0x1, 0x0)
# sysr_cfg1_rev[0:0] = 0x0
# sysr_cfg1_slipN_req[1:1] = 0x0
dut.write(0x2, 0x0)
# glbl_cfg1_ena_sysr[2:2] = 0x1
# glbl_cfg2_ena_vcos[4:3] = 0x0
# glbl_cfg1_ena_sysri[5:5] = 0x1
dut.write(0x3, 0x24)
# glbl_cfg7_ena_clkgr[6:0] = 0x3B
dut.write(0x4, 0x3B)
# glbl_cfg1_clear_alarms[0:0] = 0x0
dut.write(0x6, 0x0)
# glbl_reserved[0:0] = 0x0
dut.write(0x7, 0x0)
# glbl_cfg5_ibuf0_en[0:0] = 0x0
# glbl_cfg5_ibuf0_mode[4:1] = 0x7
dut.write(0xA, 0xE)
# glbl_cfg5_ibuf1_en[0:0] = 0x1
# glbl_cfg5_ibuf1_mode[4:1] = 0x7
dut.write(0xB, 0xF)
# glbl_cfg5_gpi1_en[0:0] = 0x0
# glbl_cfg5_gpi1_sel[4:1] = 0x0
dut.write(0x46, 0x0)
# glbl_cfg8_gpo1_en[0:0] = 0x1
# glbl_cfg8_gpo1_mode[1:1] = 0x1
# glbl_cfg8_gpo1_sel[7:2] = 0x7
dut.write(0x50, 0x1F)
# glbl_cfg2_sdio_en[0:0] = 0x1
# glbl_cfg2_sdio_mode[1:1] = 0x1
dut.write(0x54, 0x3)
# sysr_cfg3_pulsor_mode[2:0] = 0x1
dut.write(0x5A, 0x1)
# sysr_cfg1_synci_invpol[0:0] = 0x0
# sysr_cfg1_ext_sync_retimemode[2:2] = 0x1
dut.write(0x5B, 0x4)
# sysr_cfg16_divrat_lsb[7:0] = 0x0
dut.write(0x5C, 0x0)
# sysr_cfg16_divrat_msb[3:0] = 0x6
dut.write(0x5D, 0x6)
# dist_cfg1_extvco_islowfreq_sel[0:0] = 0x0
# dist_cfg1_extvco_div2_sel[1:1] = 0x1
dut.write(0x64, 0x2)
# clkgrpx_cfg1_alg_dly_lowpwr_sel[0:0] = 0x0
dut.write(0x65, 0x0)
# alrm_cfg1_sysr_unsyncd_allow[1:1] = 0x0
# alrm_cfg1_clkgrpx_validph_allow[2:2] = 0x0
# alrm_cfg1_sync_req_allow[4:4] = 0x1
dut.write(0x71, 0x10)
# glbl_ro8_chipid_lob[7:0] = 0x1
dut.write(0x78, 0x1)
# glbl_ro8_chipid_mid[7:0] = 0x52
dut.write(0x79, 0x52)
# glbl_ro8_chipid_hib[7:0] = 0x4
dut.write(0x7A, 0x4)
# alrm_ro1_sysr_unsyncd_now[1:1] = 0x1
# alrm_ro1_clkgrpx_validph_now[2:2] = 0x0
# alrm_ro1_sync_req_now[4:4] = 0x1
dut.write(0x7D, 0x12)
# sysr_ro4_fsmstate[3:0] = 0x2
# grpx_ro1_outdivfsm_busy[4:4] = 0x0
dut.write(0x91, 0x2)
# reg_98[7:0] = 0x0
dut.write(0x98, 0x0)
# reg_99[7:0] = 0x0
dut.write(0x99, 0x0)
# reg_9A[7:0] = 0x0
dut.write(0x9A, 0x0)
# reg_9B[7:0] = 0xAA
dut.write(0x9B, 0xAA)
# reg_9C[7:0] = 0xAA
dut.write(0x9C, 0xAA)
# reg_9D[7:0] = 0xAA
dut.write(0x9D, 0xAA)
# reg_9E[7:0] = 0xAA
dut.write(0x9E, 0xAA)
# reg_9F[7:0] = 0x4D
dut.write(0x9F, 0x4D)
# reg_A0[7:0] = 0xDF
dut.write(0xA0, 0xDF)
# reg_A1[7:0] = 0x97
dut.write(0xA1, 0x97)
# reg_A2[7:0] = 0x3
dut.write(0xA2, 0x3)
# reg_A3[7:0] = 0x0
dut.write(0xA3, 0x0)
# reg_A4[7:0] = 0x0
dut.write(0xA4, 0x0)
# reg_AD[7:0] = 0x0
dut.write(0xAD, 0x0)
# reg_AE[7:0] = 0x8
dut.write(0xAE, 0x8)
# reg_AF[7:0] = 0x50
dut.write(0xAF, 0x50)
# reg_B0[7:0] = 0x4
dut.write(0xB0, 0x4)
# reg_B1[7:0] = 0xD
dut.write(0xB1, 0xD)
# reg_B2[7:0] = 0x0
dut.write(0xB2, 0x0)
# reg_B3[7:0] = 0x0
dut.write(0xB3, 0x0)
# reg_B5[7:0] = 0x0
dut.write(0xB5, 0x0)
# reg_B6[7:0] = 0x0
dut.write(0xB6, 0x0)
# reg_B7[7:0] = 0x0
dut.write(0xB7, 0x0)
# reg_B8[7:0] = 0x0
dut.write(0xB8, 0x0)
# clkgrp1_div1_cfg1_en[0:0] = 0x1
# clkgrp1_div1_cfg1_phdelta_mslip[1:1] = 0x1
# clkgrp1_div1_cfg2_startmode[3:2] = 0x0
# clkgrp1_div1_cfg1_rev[4:4] = 0x1
# clkgrp1_div1_cfg1_slipmask[5:5] = 0x1
# clkgrp1_div1_cfg1_reseedmask[6:6] = 0x1
# clkgrp1_div1_cfg1_hi_perf[7:7] = 0x0
dut.write(0xC8, 0x73)
# clkgrp1_div1_cfg12_divrat_lsb[7:0] = 0x1
dut.write(0xC9, 0x1)
# clkgrp1_div1_cfg12_divrat_msb[3:0] = 0x0
dut.write(0xCA, 0x0)
# clkgrp1_div1_cfg5_fine_delay[4:0] = 0x0
dut.write(0xCB, 0x0)
# clkgrp1_div1_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0xCC, 0x0)
# clkgrp1_div1_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0xCD, 0x0)
# clkgrp1_div1_cfg12_mslip_msb[3:0] = 0x0
dut.write(0xCE, 0x0)
# clkgrp1_div1_cfg2_sel_outmux[1:0] = 0x3
# clkgrp1_div1_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0xCF, 0x3)
# clkgrp1_div1_cfg5_drvr_res[1:0] = 0x0
# clkgrp1_div1_cfg5_drvr_spare[2:2] = 0x0
# clkgrp1_div1_cfg5_drvr_mode[4:3] = 0x1
# clkgrp1_div1_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp1_div1_cfg2_mutesel[7:6] = 0x0
dut.write(0xD0, 0x8)
# clkgrp1_div2_cfg1_en[0:0] = 0x1
# clkgrp1_div2_cfg1_phdelta_mslip[1:1] = 0x0
# clkgrp1_div2_cfg2_startmode[3:2] = 0x0
# clkgrp1_div2_cfg1_rev[4:4] = 0x1
# clkgrp1_div2_cfg1_slipmask[5:5] = 0x1
# clkgrp1_div2_cfg1_reseedmask[6:6] = 0x1
# clkgrp1_div2_cfg1_hi_perf[7:7] = 0x0
dut.write(0xD2, 0x71)
# clkgrp1_div2_cfg12_divrat_lsb[7:0] = 0x40
dut.write(0xD3, 0x40)
# clkgrp1_div2_cfg12_divrat_msb[3:0] = 0x0
dut.write(0xD4, 0x0)
# clkgrp1_div2_cfg5_fine_delay[4:0] = 0x0
dut.write(0xD5, 0x0)
# clkgrp1_div2_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0xD6, 0x0)
# clkgrp1_div2_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0xD7, 0x0)
# clkgrp1_div2_cfg12_mslip_msb[3:0] = 0x0
dut.write(0xD8, 0x0)
# clkgrp1_div2_cfg2_sel_outmux[1:0] = 0x0
# clkgrp1_div2_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0xD9, 0x0)
# clkgrp1_div2_cfg5_drvr_res[1:0] = 0x1
# clkgrp1_div2_cfg5_drvr_spare[2:2] = 0x0
# clkgrp1_div2_cfg5_drvr_mode[4:3] = 0x1
# clkgrp1_div2_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp1_div2_cfg2_mutesel[7:6] = 0x0
dut.write(0xDA, 0x9)
# clkgrp2_div1_cfg1_en[0:0] = 0x1
# clkgrp2_div1_cfg1_phdelta_mslip[1:1] = 0x1
# clkgrp2_div1_cfg2_startmode[3:2] = 0x0
# clkgrp2_div1_cfg1_rev[4:4] = 0x1
# clkgrp2_div1_cfg1_slipmask[5:5] = 0x1
# clkgrp2_div1_cfg1_reseedmask[6:6] = 0x1
# clkgrp2_div1_cfg1_hi_perf[7:7] = 0x0
dut.write(0xDC, 0x73)
# clkgrp2_div1_cfg12_divrat_lsb[7:0] = 0x1
dut.write(0xDD, 0x1)
# clkgrp2_div1_cfg12_divrat_msb[3:0] = 0x0
dut.write(0xDE, 0x0)
# clkgrp2_div1_cfg5_fine_delay[4:0] = 0x0
dut.write(0xDF, 0x0)
# clkgrp2_div1_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0xE0, 0x0)
# clkgrp2_div1_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0xE1, 0x0)
# clkgrp2_div1_cfg12_mslip_msb[3:0] = 0x0
dut.write(0xE2, 0x0)
# clkgrp2_div1_cfg2_sel_outmux[1:0] = 0x0
# clkgrp2_div1_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0xE3, 0x0)
# clkgrp2_div1_cfg5_drvr_res[1:0] = 0x0
# clkgrp2_div1_cfg5_drvr_spare[2:2] = 0x0
# clkgrp2_div1_cfg5_drvr_mode[4:3] = 0x1
# clkgrp2_div1_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp2_div1_cfg2_mutesel[7:6] = 0x0
dut.write(0xE4, 0x8)
# clkgrp2_div2_cfg1_en[0:0] = 0x1
# clkgrp2_div2_cfg1_phdelta_mslip[1:1] = 0x0
# clkgrp2_div2_cfg2_startmode[3:2] = 0x0
# clkgrp2_div2_cfg1_rev[4:4] = 0x1
# clkgrp2_div2_cfg1_slipmask[5:5] = 0x1
# clkgrp2_div2_cfg1_reseedmask[6:6] = 0x1
# clkgrp2_div2_cfg1_hi_perf[7:7] = 0x0
dut.write(0xE6, 0x71)
# clkgrp2_div2_cfg12_divrat_lsb[7:0] = 0x40
dut.write(0xE7, 0x40)
# clkgrp2_div2_cfg12_divrat_msb[3:0] = 0x0
dut.write(0xE8, 0x0)
# clkgrp2_div2_cfg5_fine_delay[4:0] = 0x0
dut.write(0xE9, 0x0)
# clkgrp2_div2_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0xEA, 0x0)
# clkgrp2_div2_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0xEB, 0x0)
# clkgrp2_div2_cfg12_mslip_msb[3:0] = 0x0
dut.write(0xEC, 0x0)
# clkgrp2_div2_cfg2_sel_outmux[1:0] = 0x0
# clkgrp2_div2_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0xED, 0x0)
# clkgrp2_div2_cfg5_drvr_res[1:0] = 0x1
# clkgrp2_div2_cfg5_drvr_spare[2:2] = 0x0
# clkgrp2_div2_cfg5_drvr_mode[4:3] = 0x1
# clkgrp2_div2_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp2_div2_cfg2_mutesel[7:6] = 0x0
dut.write(0xEE, 0x9)
# clkgrp3_div1_cfg1_en[0:0] = 0x0
# clkgrp3_div1_cfg1_phdelta_mslip[1:1] = 0x1
# clkgrp3_div1_cfg2_startmode[3:2] = 0x0
# clkgrp3_div1_cfg1_rev[4:4] = 0x1
# clkgrp3_div1_cfg1_slipmask[5:5] = 0x1
# clkgrp3_div1_cfg1_reseedmask[6:6] = 0x1
# clkgrp3_div1_cfg1_hi_perf[7:7] = 0x0
dut.write(0xF0, 0x72)
# clkgrp3_div1_cfg12_divrat_lsb[7:0] = 0x2
dut.write(0xF1, 0x2)
# clkgrp3_div1_cfg12_divrat_msb[3:0] = 0x0
dut.write(0xF2, 0x0)
# clkgrp3_div1_cfg5_fine_delay[4:0] = 0x0
dut.write(0xF3, 0x0)
# clkgrp3_div1_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0xF4, 0x0)
# clkgrp3_div1_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0xF5, 0x0)
# clkgrp3_div1_cfg12_mslip_msb[3:0] = 0x0
dut.write(0xF6, 0x0)
# clkgrp3_div1_cfg2_sel_outmux[1:0] = 0x0
# clkgrp3_div1_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0xF7, 0x0)
# clkgrp3_div1_cfg5_drvr_res[1:0] = 0x0
# clkgrp3_div1_cfg5_drvr_spare[2:2] = 0x0
# clkgrp3_div1_cfg5_drvr_mode[4:3] = 0x1
# clkgrp3_div1_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp3_div1_cfg2_mutesel[7:6] = 0x0
dut.write(0xF8, 0x8)
# clkgrp3_div2_cfg1_en[0:0] = 0x0
# clkgrp3_div2_cfg1_phdelta_mslip[1:1] = 0x0
# clkgrp3_div2_cfg2_startmode[3:2] = 0x0
# clkgrp3_div2_cfg1_rev[4:4] = 0x1
# clkgrp3_div2_cfg1_slipmask[5:5] = 0x1
# clkgrp3_div2_cfg1_reseedmask[6:6] = 0x1
# clkgrp3_div2_cfg1_hi_perf[7:7] = 0x0
dut.write(0xFA, 0x70)
# clkgrp3_div2_cfg12_divrat_lsb[7:0] = 0x80
dut.write(0xFB, 0x80)
# clkgrp3_div2_cfg12_divrat_msb[3:0] = 0x0
dut.write(0xFC, 0x0)
# clkgrp3_div2_cfg5_fine_delay[4:0] = 0x0
dut.write(0xFD, 0x0)
# clkgrp3_div2_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0xFE, 0x0)
# clkgrp3_div2_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0xFF, 0x0)
# clkgrp3_div2_cfg12_mslip_msb[3:0] = 0x0
dut.write(0x100, 0x0)
# clkgrp3_div2_cfg2_sel_outmux[1:0] = 0x0
# clkgrp3_div2_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0x101, 0x0)
# clkgrp3_div2_cfg5_drvr_res[1:0] = 0x3
# clkgrp3_div2_cfg5_drvr_spare[2:2] = 0x0
# clkgrp3_div2_cfg5_drvr_mode[4:3] = 0x1
# clkgrp3_div2_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp3_div2_cfg2_mutesel[7:6] = 0x0
dut.write(0x102, 0xB)
# clkgrp4_div1_cfg1_en[0:0] = 0x1
# clkgrp4_div1_cfg1_phdelta_mslip[1:1] = 0x1
# clkgrp4_div1_cfg2_startmode[3:2] = 0x0
# clkgrp4_div1_cfg1_rev[4:4] = 0x1
# clkgrp4_div1_cfg1_slipmask[5:5] = 0x1
# clkgrp4_div1_cfg1_reseedmask[6:6] = 0x1
# clkgrp4_div1_cfg1_hi_perf[7:7] = 0x0
dut.write(0x104, 0x73)
# clkgrp4_div1_cfg12_divrat_lsb[7:0] = 0x4
dut.write(0x105, 0x4)
# clkgrp4_div1_cfg12_divrat_msb[3:0] = 0x0
dut.write(0x106, 0x0)
# clkgrp4_div1_cfg5_fine_delay[4:0] = 0x0
dut.write(0x107, 0x0)
# clkgrp4_div1_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0x108, 0x0)
# clkgrp4_div1_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0x109, 0x0)
# clkgrp4_div1_cfg12_mslip_msb[3:0] = 0x0
dut.write(0x10A, 0x0)
# clkgrp4_div1_cfg2_sel_outmux[1:0] = 0x0
# clkgrp4_div1_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0x10B, 0x0)
# clkgrp4_div1_cfg5_drvr_res[1:0] = 0x0
# clkgrp4_div1_cfg5_drvr_spare[2:2] = 0x0
# clkgrp4_div1_cfg5_drvr_mode[4:3] = 0x1
# clkgrp4_div1_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp4_div1_cfg2_mutesel[7:6] = 0x0
dut.write(0x10C, 0x8)
# clkgrp4_div2_cfg1_en[0:0] = 0x1
# clkgrp4_div2_cfg1_phdelta_mslip[1:1] = 0x0
# clkgrp4_div2_cfg2_startmode[3:2] = 0x0
# clkgrp4_div2_cfg1_rev[4:4] = 0x1
# clkgrp4_div2_cfg1_slipmask[5:5] = 0x1
# clkgrp4_div2_cfg1_reseedmask[6:6] = 0x1
# clkgrp4_div2_cfg1_hi_perf[7:7] = 0x0
dut.write(0x10E, 0x71)
# clkgrp4_div2_cfg12_divrat_lsb[7:0] = 0x40
dut.write(0x10F, 0x40)
# clkgrp4_div2_cfg12_divrat_msb[3:0] = 0x0
dut.write(0x110, 0x0)
# clkgrp4_div2_cfg5_fine_delay[4:0] = 0x0
dut.write(0x111, 0x0)
# clkgrp4_div2_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0x112, 0x0)
# clkgrp4_div2_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0x113, 0x0)
# clkgrp4_div2_cfg12_mslip_msb[3:0] = 0x0
dut.write(0x114, 0x0)
# clkgrp4_div2_cfg2_sel_outmux[1:0] = 0x0
# clkgrp4_div2_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0x115, 0x0)
# clkgrp4_div2_cfg5_drvr_res[1:0] = 0x3
# clkgrp4_div2_cfg5_drvr_spare[2:2] = 0x0
# clkgrp4_div2_cfg5_drvr_mode[4:3] = 0x2
# clkgrp4_div2_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp4_div2_cfg2_mutesel[7:6] = 0x0
dut.write(0x116, 0x13)
# clkgrp5_div1_cfg1_en[0:0] = 0x1
# clkgrp5_div1_cfg1_phdelta_mslip[1:1] = 0x1
# clkgrp5_div1_cfg2_startmode[3:2] = 0x0
# clkgrp5_div1_cfg1_rev[4:4] = 0x1
# clkgrp5_div1_cfg1_slipmask[5:5] = 0x1
# clkgrp5_div1_cfg1_reseedmask[6:6] = 0x1
# clkgrp5_div1_cfg1_hi_perf[7:7] = 0x0
dut.write(0x118, 0x73)
# clkgrp5_div1_cfg12_divrat_lsb[7:0] = 0x4
dut.write(0x119, 0x4)
# clkgrp5_div1_cfg12_divrat_msb[3:0] = 0x0
dut.write(0x11A, 0x0)
# clkgrp5_div1_cfg5_fine_delay[4:0] = 0x0
dut.write(0x11B, 0x0)
# clkgrp5_div1_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0x11C, 0x0)
# clkgrp5_div1_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0x11D, 0x0)
# clkgrp5_div1_cfg12_mslip_msb[3:0] = 0x0
dut.write(0x11E, 0x0)
# clkgrp5_div1_cfg2_sel_outmux[1:0] = 0x0
# clkgrp5_div1_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0x11F, 0x0)
# clkgrp5_div1_cfg5_drvr_res[1:0] = 0x0
# clkgrp5_div1_cfg5_drvr_spare[2:2] = 0x0
# clkgrp5_div1_cfg5_drvr_mode[4:3] = 0x1
# clkgrp5_div1_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp5_div1_cfg2_mutesel[7:6] = 0x0
dut.write(0x120, 0x8)
# clkgrp5_div2_cfg1_en[0:0] = 0x1
# clkgrp5_div2_cfg1_phdelta_mslip[1:1] = 0x1
# clkgrp5_div2_cfg2_startmode[3:2] = 0x0
# clkgrp5_div2_cfg1_rev[4:4] = 0x1
# clkgrp5_div2_cfg1_slipmask[5:5] = 0x1
# clkgrp5_div2_cfg1_reseedmask[6:6] = 0x1
# clkgrp5_div2_cfg1_hi_perf[7:7] = 0x0
dut.write(0x122, 0x73)
# clkgrp5_div2_cfg12_divrat_lsb[7:0] = 0x4
dut.write(0x123, 0x4)
# clkgrp5_div2_cfg12_divrat_msb[3:0] = 0x0
dut.write(0x124, 0x0)
# clkgrp5_div2_cfg5_fine_delay[4:0] = 0x0
dut.write(0x125, 0x0)
# clkgrp5_div2_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0x126, 0x0)
# clkgrp5_div2_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0x127, 0x0)
# clkgrp5_div2_cfg12_mslip_msb[3:0] = 0x0
dut.write(0x128, 0x0)
# clkgrp5_div2_cfg2_sel_outmux[1:0] = 0x0
# clkgrp5_div2_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0x129, 0x0)
# clkgrp5_div2_cfg5_drvr_res[1:0] = 0x3
# clkgrp5_div2_cfg5_drvr_spare[2:2] = 0x0
# clkgrp5_div2_cfg5_drvr_mode[4:3] = 0x1
# clkgrp5_div2_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp5_div2_cfg2_mutesel[7:6] = 0x0
dut.write(0x12A, 0xB)
# clkgrp6_div1_cfg1_en[0:0] = 0x1
# clkgrp6_div1_cfg1_phdelta_mslip[1:1] = 0x1
# clkgrp6_div1_cfg2_startmode[3:2] = 0x0
# clkgrp6_div1_cfg1_rev[4:4] = 0x1
# clkgrp6_div1_cfg1_slipmask[5:5] = 0x1
# clkgrp6_div1_cfg1_reseedmask[6:6] = 0x1
# clkgrp6_div1_cfg1_hi_perf[7:7] = 0x0
dut.write(0x12C, 0x73)
# clkgrp6_div1_cfg12_divrat_lsb[7:0] = 0x4
dut.write(0x12D, 0x4)
# clkgrp6_div1_cfg12_divrat_msb[3:0] = 0x0
dut.write(0x12E, 0x0)
# clkgrp6_div1_cfg5_fine_delay[4:0] = 0x0
dut.write(0x12F, 0x0)
# clkgrp6_div1_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0x130, 0x0)
# clkgrp6_div1_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0x131, 0x0)
# clkgrp6_div1_cfg12_mslip_msb[3:0] = 0x0
dut.write(0x132, 0x0)
# clkgrp6_div1_cfg2_sel_outmux[1:0] = 0x0
# clkgrp6_div1_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0x133, 0x0)
# clkgrp6_div1_cfg5_drvr_res[1:0] = 0x0
# clkgrp6_div1_cfg5_drvr_spare[2:2] = 0x0
# clkgrp6_div1_cfg5_drvr_mode[4:3] = 0x1
# clkgrp6_div1_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp6_div1_cfg2_mutesel[7:6] = 0x0
dut.write(0x134, 0x8)
# clkgrp6_div2_cfg1_en[0:0] = 0x1
# clkgrp6_div2_cfg1_phdelta_mslip[1:1] = 0x0
# clkgrp6_div2_cfg2_startmode[3:2] = 0x0
# clkgrp6_div2_cfg1_rev[4:4] = 0x1
# clkgrp6_div2_cfg1_slipmask[5:5] = 0x1
# clkgrp6_div2_cfg1_reseedmask[6:6] = 0x1
# clkgrp6_div2_cfg1_hi_perf[7:7] = 0x0
dut.write(0x136, 0x71)
# clkgrp6_div2_cfg12_divrat_lsb[7:0] = 0x80
dut.write(0x137, 0x80)
# clkgrp6_div2_cfg12_divrat_msb[3:0] = 0x0
dut.write(0x138, 0x0)
# clkgrp6_div2_cfg5_fine_delay[4:0] = 0x0
dut.write(0x139, 0x0)
# clkgrp6_div2_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0x13A, 0x0)
# clkgrp6_div2_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0x13B, 0x0)
# clkgrp6_div2_cfg12_mslip_msb[3:0] = 0x0
dut.write(0x13C, 0x0)
# clkgrp6_div2_cfg2_sel_outmux[1:0] = 0x0
# clkgrp6_div2_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0x13D, 0x0)
# clkgrp6_div2_cfg5_drvr_res[1:0] = 0x1
# clkgrp6_div2_cfg5_drvr_spare[2:2] = 0x0
# clkgrp6_div2_cfg5_drvr_mode[4:3] = 0x2
# clkgrp6_div2_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp6_div2_cfg2_mutesel[7:6] = 0x0
dut.write(0x13E, 0x11)
# clkgrp7_div1_cfg1_en[0:0] = 0x0
# clkgrp7_div1_cfg1_phdelta_mslip[1:1] = 0x1
# clkgrp7_div1_cfg2_startmode[3:2] = 0x0
# clkgrp7_div1_cfg1_rev[4:4] = 0x1
# clkgrp7_div1_cfg1_slipmask[5:5] = 0x1
# clkgrp7_div1_cfg1_reseedmask[6:6] = 0x1
# clkgrp7_div1_cfg1_hi_perf[7:7] = 0x0
dut.write(0x140, 0x72)
# clkgrp7_div1_cfg12_divrat_lsb[7:0] = 0x2
dut.write(0x141, 0x2)
# clkgrp7_div1_cfg12_divrat_msb[3:0] = 0x0
dut.write(0x142, 0x0)
# clkgrp7_div1_cfg5_fine_delay[4:0] = 0x0
dut.write(0x143, 0x0)
# clkgrp7_div1_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0x144, 0x0)
# clkgrp7_div1_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0x145, 0x0)
# clkgrp7_div1_cfg12_mslip_msb[3:0] = 0x0
dut.write(0x146, 0x0)
# clkgrp7_div1_cfg2_sel_outmux[1:0] = 0x0
# clkgrp7_div1_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0x147, 0x0)
# clkgrp7_div1_cfg5_drvr_res[1:0] = 0x0
# clkgrp7_div1_cfg5_drvr_spare[2:2] = 0x0
# clkgrp7_div1_cfg5_drvr_mode[4:3] = 0x1
# clkgrp7_div1_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp7_div1_cfg2_mutesel[7:6] = 0x0
dut.write(0x148, 0x8)
# clkgrp7_div2_cfg1_en[0:0] = 0x0
# clkgrp7_div2_cfg1_phdelta_mslip[1:1] = 0x0
# clkgrp7_div2_cfg2_startmode[3:2] = 0x0
# clkgrp7_div2_cfg1_rev[4:4] = 0x1
# clkgrp7_div2_cfg1_slipmask[5:5] = 0x1
# clkgrp7_div2_cfg1_reseedmask[6:6] = 0x1
# clkgrp7_div2_cfg1_hi_perf[7:7] = 0x0
dut.write(0x14A, 0x70)
# clkgrp7_div2_cfg12_divrat_lsb[7:0] = 0x80
dut.write(0x14B, 0x80)
# clkgrp7_div2_cfg12_divrat_msb[3:0] = 0x0
dut.write(0x14C, 0x0)
# clkgrp7_div2_cfg5_fine_delay[4:0] = 0x0
dut.write(0x14D, 0x0)
# clkgrp7_div2_cfg5_sel_coarse_delay[4:0] = 0x0
dut.write(0x14E, 0x0)
# clkgrp7_div2_cfg12_mslip_lsb[7:0] = 0x0
dut.write(0x14F, 0x0)
# clkgrp7_div2_cfg12_mslip_msb[3:0] = 0x0
dut.write(0x150, 0x0)
# clkgrp7_div2_cfg2_sel_outmux[1:0] = 0x0
# clkgrp7_div2_cfg1_drvr_sel_testclk[2:2] = 0x0
dut.write(0x151, 0x0)
# clkgrp7_div2_cfg5_drvr_res[1:0] = 0x3
# clkgrp7_div2_cfg5_drvr_spare[2:2] = 0x0
# clkgrp7_div2_cfg5_drvr_mode[4:3] = 0x1
# clkgrp7_div2_cfg_outbuf_dyn[5:5] = 0x0
# clkgrp7_div2_cfg2_mutesel[7:6] = 0x0
dut.write(0x152, 0xB)

View File

@ -0,0 +1,151 @@
/*
* HMC830 config:
* 100MHz input, 1GHz output
* fvco = (refclk / r_divider) * n_divider
* fout = fvco/2
*
* HMC7043 config:
* dac clock: 1GHz (div=1)
* fpga clock: 250MHz (div=4)
* sysref clock: 15.625MHz (div=64)
*/
mod hmc830 {
use csr;
const HMC830_WRITES: [(u8, u32); 16] = [
(0x0, 0x20),
(0x1, 0x2),
(0x2, 0x2), // r_divider
(0x5, 0x1628),
(0x5, 0x60a0),
(0x5, 0xe110),
(0x5, 0x2818),
(0x5, 0x0),
(0x6, 0x303ca),
(0x7, 0x14d),
(0x8, 0xc1beff),
(0x9, 0x153fff),
(0xa, 0x2046),
(0xb, 0x7c061),
(0xf, 0x81),
(0x3, 0x28), // n_divider
];
fn spi_setup() {
unsafe {
csr::converter_spi::offline_write(1);
csr::converter_spi::cs_polarity_write(0);
csr::converter_spi::clk_polarity_write(0);
csr::converter_spi::clk_phase_write(0);
csr::converter_spi::lsb_first_write(0);
csr::converter_spi::half_duplex_write(0);
csr::converter_spi::clk_div_write_write(8);
csr::converter_spi::clk_div_read_write(8);
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC830_CS);
csr::converter_spi::offline_write(0);
}
}
fn write(addr: u8, data: u32) {
let cmd = (0 << 6) | addr;
let val = ((cmd as u32) << 24) | data;
unsafe {
csr::converter_spi::xfer_len_write_write(32);
csr::converter_spi::xfer_len_read_write(0);
csr::converter_spi::data_write_write(val << (32-31));
while csr::converter_spi::pending_read() != 0 {}
while csr::converter_spi::active_read() != 0 {}
}
}
fn read(addr: u8) -> u32 {
let cmd = (1 << 6) | addr;
let val = (cmd as u32) << 24;
unsafe {
csr::converter_spi::xfer_len_write_write(7);
csr::converter_spi::xfer_len_read_write(25);
csr::converter_spi::data_write_write(val << (32-31));
while csr::converter_spi::pending_read() != 0 {}
while csr::converter_spi::active_read() != 0 {}
csr::converter_spi::data_read_read()
}
}
pub fn init() -> Result<(), &'static str> {
spi_setup();
let id = read(0);
if id != 0xa7975 {
error!("invalid HMC830 ID: 0x{:08x}", id);
return Err("invalid HMC830 identification");
}
for &(addr, data) in HMC830_WRITES.iter() {
write(addr, data);
}
Ok(())
}
}
mod hmc7043 {
use csr;
include!(concat!(env!("OUT_DIR"), "/hmc7043_writes.rs"));
fn spi_setup() {
unsafe {
csr::converter_spi::offline_write(1);
csr::converter_spi::cs_polarity_write(0);
csr::converter_spi::clk_polarity_write(0);
csr::converter_spi::clk_phase_write(0);
csr::converter_spi::lsb_first_write(0);
csr::converter_spi::half_duplex_write(1);
csr::converter_spi::clk_div_write_write(8);
csr::converter_spi::clk_div_read_write(8);
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC7043_CS);
csr::converter_spi::offline_write(0);
}
}
fn write(addr: u16, data: u8) {
let cmd = (0 << 15) | addr;
let val = ((cmd as u32) << 8) | data as u32;
unsafe {
csr::converter_spi::xfer_len_write_write(24);
csr::converter_spi::xfer_len_read_write(0);
csr::converter_spi::data_write_write(val << (32-24));
while csr::converter_spi::pending_read() != 0 {}
while csr::converter_spi::active_read() != 0 {}
}
}
fn read(addr: u16) -> u8 {
let cmd = (0 << 15) | addr;
let val = (cmd as u32) << 8;
unsafe {
csr::converter_spi::xfer_len_write_write(16);
csr::converter_spi::xfer_len_read_write(8);
csr::converter_spi::data_write_write(val << (32-24));
while csr::converter_spi::pending_read() != 0 {}
while csr::converter_spi::active_read() != 0 {}
csr::converter_spi::data_read_read() as u8
}
}
pub fn init() -> Result<(), &'static str> {
spi_setup();
let id = (read(0x78) as u32) << 16 | (read(0x79) as u32) << 8 | read(0x7a) as u32;
if id != 0xf17904 {
error!("invalid HMC7043 ID: 0x{:08x}", id);
return Err("invalid HMC7043 identification");
}
for &(addr, data) in HMC7043_WRITES.iter() {
write(addr, data);
}
Ok(())
}
}
pub fn init() -> Result<(), &'static str> {
hmc830::init()?;
hmc7043::init()
}

View File

@ -25,11 +25,15 @@ pub mod spi;
#[cfg(has_si5324)] #[cfg(has_si5324)]
pub mod si5324; pub mod si5324;
#[cfg(has_serwb_phy)]
pub mod serwb;
#[cfg(has_ad9516)] #[cfg(has_ad9516)]
#[allow(dead_code)] #[allow(dead_code)]
mod ad9516_reg; mod ad9516_reg;
#[cfg(has_ad9516)] #[cfg(has_ad9516)]
pub mod ad9516; pub mod ad9516;
#[cfg(has_hmc830_7043)]
pub mod hmc830_7043;
#[cfg(has_ad9154)] #[cfg(has_ad9154)]
#[allow(dead_code)] #[allow(dead_code)]
mod ad9154_reg; mod ad9154_reg;

View File

@ -0,0 +1,18 @@
use csr;
pub fn wait_init() {
info!("waiting for AMC/RTM serwb bridge to be ready...");
unsafe {
while csr::serwb_phy::control_ready_read() != 0 {}
}
info!("done.");
// Try reading the identifier register on the other side of the bridge.
let rtm_identifier = unsafe {
csr::rtm_identifier::identifier_read()
};
if rtm_identifier != 0x5352544d {
error!("incorrect RTM identifier: 0x{:08x}", rtm_identifier);
// proceed anyway
}
}

View File

@ -62,6 +62,9 @@ fn startup() {
info!("software version {}", include_str!(concat!(env!("OUT_DIR"), "/git-describe"))); info!("software version {}", include_str!(concat!(env!("OUT_DIR"), "/git-describe")));
info!("gateware version {}", board::ident(&mut [0; 64])); info!("gateware version {}", board::ident(&mut [0; 64]));
#[cfg(has_serwb_phy)]
board::serwb::wait_init();
let t = board::clock::get_ms(); let t = board::clock::get_ms();
info!("press 'e' to erase startup and idle kernels..."); info!("press 'e' to erase startup and idle kernels...");
while board::clock::get_ms() < t + 1000 { while board::clock::get_ms() < t + 1000 {
@ -77,9 +80,11 @@ fn startup() {
#[cfg(has_i2c)] #[cfg(has_i2c)]
board::i2c::init(); board::i2c::init();
#[cfg(has_ad9516)] #[cfg(has_ad9516)]
board::ad9516::init().expect("cannot initialize ad9516"); board::ad9516::init().expect("cannot initialize AD9516");
#[cfg(has_hmc830_7043)]
board::hmc830_7043::init().expect("cannot initialize HMC830/7043");
#[cfg(has_ad9154)] #[cfg(has_ad9154)]
board::ad9154::init().expect("cannot initialize ad9154"); board::ad9154::init().expect("cannot initialize AD9154");
let hardware_addr; let hardware_addr;
match config::read_str("mac", |r| r?.parse()) { match config::read_str("mac", |r| r?.parse()) {

View File

@ -195,10 +195,15 @@ fn startup() {
info!("software version {}", include_str!(concat!(env!("OUT_DIR"), "/git-describe"))); info!("software version {}", include_str!(concat!(env!("OUT_DIR"), "/git-describe")));
info!("gateware version {}", board::ident(&mut [0; 64])); info!("gateware version {}", board::ident(&mut [0; 64]));
#[cfg(has_serwb_phy)]
board::serwb::wait_init();
#[cfg(has_ad9516)] #[cfg(has_ad9516)]
board::ad9516::init().expect("cannot initialize ad9516"); board::ad9516::init().expect("cannot initialize AD9516");
#[cfg(has_hmc830_7043)]
board::hmc830_7043::init().expect("cannot initialize HMC830/7043");
board::i2c::init(); board::i2c::init();
board::si5324::setup(&SI5324_SETTINGS).expect("cannot initialize si5324"); board::si5324::setup(&SI5324_SETTINGS).expect("cannot initialize Si5324");
loop { loop {
while !drtio_link_is_up() { while !drtio_link_is_up() {

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Copyright (C) 2015 Robert Jordens <jordens@gmail.com>
import argparse import argparse
import os import os
@ -11,16 +10,6 @@ from artiq import __artiq_dir__ as artiq_dir
from artiq.frontend.bit2bin import bit2bin from artiq.frontend.bit2bin import bit2bin
def scripts_path():
p = ["share", "openocd", "scripts"]
if os.name == "nt":
p.insert(0, "Library")
p = os.path.abspath(os.path.join(
os.path.dirname(shutil.which("openocd")),
"..", *p))
return p
def get_argparser(): def get_argparser():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
@ -47,8 +36,8 @@ Prerequisites:
""") """)
parser.add_argument("-t", "--target", default="kc705", parser.add_argument("-t", "--target", default="kc705",
help="target board, default: %(default)s") help="target board, default: %(default)s")
parser.add_argument("-m", "--adapter", default="nist_clock", parser.add_argument("-m", "--adapter", default=None,
help="target adapter, default: %(default)s") help="target adapter, default: board-dependent")
parser.add_argument("--target-file", default=None, parser.add_argument("--target-file", default=None,
help="use alternative OpenOCD target file") help="use alternative OpenOCD target file")
parser.add_argument("-f", "--storage", help="write file to storage area") parser.add_argument("-f", "--storage", help="write file to storage area")
@ -59,84 +48,210 @@ Prerequisites:
return parser return parser
def scripts_path():
p = ["share", "openocd", "scripts"]
if os.name == "nt":
p.insert(0, "Library")
p = os.path.abspath(os.path.join(
os.path.dirname(shutil.which("openocd")),
"..", *p))
return p
class Programmer:
def __init__(self, target_file):
self.target_file = target_file
self.prog = []
def load(self, bitfile):
raise NotImplementedError
def proxy(self, proxy_bitfile):
raise NotImplementedError
def flash_binary(self, flashno, address, filename):
raise NotImplementedError
def start(self):
raise NotImplementedError
def do(self):
self.prog.append("exit")
cmdline = [
"openocd",
"-s", scripts_path()
]
if self.target_file is not None:
cmdline += ["-f", self.target_file]
cmdline += ["-c", "; ".join(self.prog)]
subprocess.check_call(cmdline)
class ProgrammerJtagSpi7(Programmer):
def __init__(self, target_file):
Programmer.__init__(self, target_file)
self.prog.append("init")
def load(self, bitfile):
self.prog.append("pld load 0 " + bitfile)
def proxy(self, proxy_bitfile):
self.prog.append("jtagspi_init 0 {{{}}}".format(proxy_bitfile))
def flash_binary(self, flashno, address, filename):
# jtagspi_program supports only one flash
assert flashno == 0
self.prog.append("jtagspi_program {{{}}} 0x{:x}".format(
filename, address))
def start(self):
self.prog.append("xc7_program xc7.tap")
class ProgrammerSayma(Programmer):
def __init__(self, target_file):
# TODO: use target_file
# TODO: support Sayma RTM
Programmer.__init__(self, None)
self.proxy_loaded = False
self.prog += [
"interface ftdi",
"ftdi_device_desc \"Quad RS232-HS\"",
"ftdi_vid_pid 0x0403 0x6011",
"ftdi_channel 0",
# EN_USB_JTAG on ADBUS7: out, high
# nTRST on ADBUS4: out, high, but R46 is DNP
"ftdi_layout_init 0x0098 0x008b",
"ftdi_tdo_sample_edge falling",
"ftdi_layout_signal nSRST -data 0x0080",
"reset_config srst_only srst_pulls_trst srst_gates_jtag srst_push_pull",
"adapter_khz 25000",
"transport select jtag",
"jtag newtap amc_xcu tap -irlen 6 -ignore-version -expected-id 0x03822093",
"pld device virtex2 amc_xcu.tap 1",
"set XILINX_USER1 0x02",
"set XILINX_USER2 0x03",
"set AMC_DR_LEN 1",
"target create amc_xcu.proxy testee -chain-position amc_xcu.tap",
"flash bank amc_xcu.spi0 jtagspi 0 0 0 0 amc_xcu.proxy $XILINX_USER1 $AMC_DR_LEN",
"flash bank amc_xcu.spi1 jtagspi 0 0 0 0 amc_xcu.proxy $XILINX_USER2 $AMC_DR_LEN",
"init"
]
def load(self, bitfile):
self.prog.append("pld load 0 " + bitfile)
def proxy(self, proxy_bitfile):
self.prog += [
"pld load 0 " + proxy_bitfile,
"reset halt"
]
def flash_binary(self, flashno, address, filename):
self.prog += [
"flash probe amc_xcu.spi{}".format(flashno),
"irscan amc_xcu.tap $XILINX_USER{}".format(flashno+1),
"flash write_bank {} {} 0x{:x}".format(flashno, filename, address)
]
def start(self):
self.proxy_loaded = False
self.prog.append("xcu_program xcu.tap")
def main(): def main():
parser = get_argparser() parser = get_argparser()
opts = parser.parse_args() opts = parser.parse_args()
config = { config = {
"kc705": { "kc705": {
"chip": "xc7k325t", "programmer_factory": ProgrammerJtagSpi7,
"start": "xc7_program xc7.tap", "proxy_bitfile": "bscan_spi_xc7k325t.bit",
"gateware": 0x000000, "adapters": ["nist_clock", "nist_qc2"],
"bios": 0xaf0000, "gateware": (0, 0x000000),
"runtime": 0xb00000, "bios": (0, 0xaf0000),
"storage": 0xb80000, "runtime": (0, 0xb00000),
"storage": (0, 0xb80000),
},
"sayma": {
"programmer_factory": ProgrammerSayma,
"proxy_bitfile": "bscan_spi_xcku040_sayma.bit",
"adapters": [],
"gateware": (0, 0x000000),
"bios": (1, 0x000000),
"runtime": (1, 0x010000),
"storage": (1, 0x090000),
}, },
}[opts.target] }[opts.target]
if opts.dir is None: adapter = opts.adapter
opts.dir = os.path.join(artiq_dir, "binaries", if adapter is not None and adapter not in config["adapters"]:
"{}-{}".format(opts.target, opts.adapter)) raise SystemExit("Invalid adapter for this board")
if not os.path.exists(opts.dir) and opts.action != ["start"]: if adapter is None and config["adapters"]:
raise SystemExit("Binaries directory '{}' does not exist" adapter = config["adapters"][0]
.format(opts.dir)) bin_dir = opts.dir
if bin_dir is None:
conv = False if adapter is None:
bin_dir = os.path.join(artiq_dir, "binaries",
prog = [] "{}".format(opts.target))
prog.append("init")
for action in opts.action:
if action == "proxy":
proxy_base = "bscan_spi_{}.bit".format(config["chip"])
proxy = None
for p in [opts.dir, os.path.expanduser("~/.migen"),
"/usr/local/share/migen", "/usr/share/migen"]:
proxy_ = os.path.join(p, proxy_base)
if os.access(proxy_, os.R_OK):
proxy = "jtagspi_init 0 {{{}}}".format(proxy_)
break
if not proxy:
raise SystemExit(
"proxy gateware bitstream {} not found".format(proxy_base))
prog.append(proxy)
elif action == "gateware":
bin = os.path.join(opts.dir, "top.bin")
if not os.access(bin, os.R_OK):
bin_handle, bin = tempfile.mkstemp()
bit = os.path.join(opts.dir, "top.bit")
conv = True
prog.append("jtagspi_program {{{}}} 0x{:x}".format(
bin, config["gateware"]))
elif action == "bios":
prog.append("jtagspi_program {{{}}} 0x{:x}".format(
os.path.join(opts.dir, "bios.bin"), config["bios"]))
elif action == "runtime":
prog.append("jtagspi_program {{{}}} 0x{:x}".format(
os.path.join(opts.dir, "runtime.fbi"), config["runtime"]))
elif action == "storage":
prog.append("jtagspi_program {{{}}} 0x{:x}".format(
opts.storage, config["storage"]))
elif action == "load":
prog.append("pld load 0 {{{}}}".format(
os.path.join(opts.dir, "top.bit")))
elif action == "start":
prog.append(config["start"])
else: else:
raise ValueError("invalid action", action) bin_dir = os.path.join(artiq_dir, "binaries",
prog.append("exit") "{}-{}".format(opts.target, adapter))
try: if not os.path.exists(bin_dir) and opts.action != ["start"]:
if conv: raise SystemExit("Binaries directory '{}' does not exist"
bit2bin(bit, bin_handle) .format(bin_dir))
if opts.target_file is None: if opts.target_file is None:
target_file = os.path.join("board", opts.target + ".cfg") target_file = os.path.join("board", opts.target + ".cfg")
else: else:
target_file = opts.target_file target_file = opts.target_file
subprocess.check_call([ programmer = config["programmer_factory"](target_file)
"openocd",
"-s", scripts_path(), conv = False
"-f", target_file, for action in opts.action:
"-c", "; ".join(prog), if action == "proxy":
]) proxy_found = False
for p in [bin_dir, os.path.expanduser("~/.migen"),
"/usr/local/share/migen", "/usr/share/migen"]:
proxy_bitfile = os.path.join(p, config["proxy_bitfile"])
if os.access(proxy_bitfile, os.R_OK):
programmer.proxy(proxy_bitfile)
proxy_found = True
break
if not proxy_found:
raise SystemExit(
"proxy gateware bitstream {} not found".format(config["proxy_bitfile"]))
elif action == "gateware":
bin = os.path.join(bin_dir, "top.bin")
if not os.access(bin, os.R_OK):
bin_handle, bin = tempfile.mkstemp()
bit = os.path.join(bin_dir, "top.bit")
conv = True
programmer.flash_binary(*config["gateware"], bin)
elif action == "bios":
programmer.flash_binary(*config["bios"], os.path.join(bin_dir, "bios.bin"))
elif action == "runtime":
programmer.flash_binary(*config["runtime"], os.path.join(bin_dir, "runtime.fbi"))
elif action == "storage":
programmer.flash_binary(*config["storage"], opts.storage)
elif action == "load":
programmer.load(os.path.join(bin_dir, "top.bit"))
elif action == "start":
programmer.start()
else:
raise ValueError("invalid action", action)
if conv:
bit2bin(bit, bin_handle)
try:
programmer.do()
finally: finally:
if conv: if conv:
os.unlink(bin) os.unlink(bin)

View File

@ -0,0 +1,42 @@
from collections import OrderedDict
from operator import itemgetter
import csv
from misoc.interconnect.csr import CSRStatus, CSRStorage
def _get_csr_data(csv_file):
csr_data = OrderedDict()
with open(csv_file) as csv_file_f:
csv_reader = csv.reader(csv_file_f)
for name, address, length, ro in csv_reader:
region_name, csr_name = name.split(".")
address = int(address, 0)
length = int(length, 0)
ro = ro == "ro"
if region_name not in csr_data:
csr_data[region_name] = []
csr_data[region_name].append((csr_name, address, length, ro))
return csr_data
def get_remote_csr_regions(offset, csv_file):
busword = 32
regions = []
for region_name, csrs_info in _get_csr_data(csv_file).items():
csrs_info = sorted(csrs_info, key=itemgetter(1))
origin = csrs_info[0][1]
next_address = origin
csrs = []
for csr_name, address, length, ro in csrs_info:
if address != next_address:
raise ValueError("CSRs are not contiguous")
nr = (length + busword - 1)//busword
next_address += nr*busword//8
if ro:
csr = CSRStatus(length, name=csr_name)
else:
csr = CSRStorage(length, name=csr_name)
csrs.append(csr)
regions.append((region_name, offset + origin, busword, csrs))
return regions

View File

@ -0,0 +1 @@
from artiq.gateware.serwb import s7phy, kusphy, phy, core, packet, etherbone

View File

@ -0,0 +1,37 @@
from migen import *
from misoc.interconnect import stream
from artiq.gateware.serwb.packet import Depacketizer, Packetizer
from artiq.gateware.serwb.etherbone import Etherbone
class SERWBCore(Module):
def __init__(self, phy, clk_freq, mode):
self.submodules.etherbone = etherbone = Etherbone(mode)
depacketizer = Depacketizer(clk_freq)
packetizer = Packetizer()
self.submodules += depacketizer, packetizer
tx_cdc = stream.AsyncFIFO([("data", 32)], 8)
tx_cdc = ClockDomainsRenamer({"write": "sys", "read": "serwb_serdes"})(tx_cdc)
self.submodules += tx_cdc
rx_cdc = stream.AsyncFIFO([("data", 32)], 8)
rx_cdc = ClockDomainsRenamer({"write": "serwb_serdes", "read": "sys"})(rx_cdc)
self.submodules += rx_cdc
self.comb += [
# core <--> etherbone
depacketizer.source.connect(etherbone.sink),
etherbone.source.connect(packetizer.sink),
# core --> serdes
packetizer.source.connect(tx_cdc.sink),
If(tx_cdc.source.stb & phy.init.ready,
phy.serdes.tx_data.eq(tx_cdc.source.data)
),
tx_cdc.source.ack.eq(phy.init.ready),
# serdes --> core
rx_cdc.sink.stb.eq(phy.init.ready),
rx_cdc.sink.data.eq(phy.serdes.rx_data),
rx_cdc.source.connect(depacketizer.sink),
]

View File

@ -0,0 +1,740 @@
"""
Etherbone
CERN's Etherbone protocol is initially used to run a Wishbone bus over an
ethernet network. This re-implementation is meant to be run over serdes
and introduces some limitations:
- no probing (pf/pr)
- no address spaces (rca/bca/wca/wff)
- 32bits data and address
- 1 record per frame
"""
from migen import *
from misoc.interconnect import stream
from misoc.interconnect import wishbone
from artiq.gateware.serwb.packet import *
class _Packetizer(Module):
def __init__(self, sink_description, source_description, header):
self.sink = sink = stream.Endpoint(sink_description)
self.source = source = stream.Endpoint(source_description)
self.header = Signal(header.length*8)
# # #
dw = len(self.sink.data)
header_reg = Signal(header.length*8, reset_less=True)
header_words = (header.length*8)//dw
load = Signal()
shift = Signal()
counter = Signal(max=max(header_words, 2))
counter_reset = Signal()
counter_ce = Signal()
self.sync += \
If(counter_reset,
counter.eq(0)
).Elif(counter_ce,
counter.eq(counter + 1)
)
self.comb += header.encode(sink, self.header)
if header_words == 1:
self.sync += [
If(load,
header_reg.eq(self.header)
)
]
else:
self.sync += [
If(load,
header_reg.eq(self.header)
).Elif(shift,
header_reg.eq(Cat(header_reg[dw:], Signal(dw)))
)
]
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
if header_words == 1:
idle_next_state = "COPY"
else:
idle_next_state = "SEND_HEADER"
fsm.act("IDLE",
sink.ack.eq(1),
counter_reset.eq(1),
If(sink.stb,
sink.ack.eq(0),
source.stb.eq(1),
source.eop.eq(0),
source.data.eq(self.header[:dw]),
If(source.stb & source.ack,
load.eq(1),
NextState(idle_next_state)
)
)
)
if header_words != 1:
fsm.act("SEND_HEADER",
source.stb.eq(1),
source.eop.eq(0),
source.data.eq(header_reg[dw:2*dw]),
If(source.stb & source.ack,
shift.eq(1),
counter_ce.eq(1),
If(counter == header_words-2,
NextState("COPY")
)
)
)
if hasattr(sink, "error"):
self.comb += source.error.eq(sink.error)
fsm.act("COPY",
source.stb.eq(sink.stb),
source.eop.eq(sink.eop),
source.data.eq(sink.data),
If(source.stb & source.ack,
sink.ack.eq(1),
If(source.eop,
NextState("IDLE")
)
)
)
class _Depacketizer(Module):
def __init__(self, sink_description, source_description, header):
self.sink = sink = stream.Endpoint(sink_description)
self.source = source = stream.Endpoint(source_description)
self.header = Signal(header.length*8)
# # #
dw = len(sink.data)
header_reg = Signal(header.length*8, reset_less=True)
header_words = (header.length*8)//dw
shift = Signal()
counter = Signal(max=max(header_words, 2))
counter_reset = Signal()
counter_ce = Signal()
self.sync += \
If(counter_reset,
counter.eq(0)
).Elif(counter_ce,
counter.eq(counter + 1)
)
if header_words == 1:
self.sync += \
If(shift,
header_reg.eq(sink.data)
)
else:
self.sync += \
If(shift,
header_reg.eq(Cat(header_reg[dw:], sink.data))
)
self.comb += self.header.eq(header_reg)
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
if header_words == 1:
idle_next_state = "COPY"
else:
idle_next_state = "RECEIVE_HEADER"
fsm.act("IDLE",
sink.ack.eq(1),
counter_reset.eq(1),
If(sink.stb,
shift.eq(1),
NextState(idle_next_state)
)
)
if header_words != 1:
fsm.act("RECEIVE_HEADER",
sink.ack.eq(1),
If(sink.stb,
counter_ce.eq(1),
shift.eq(1),
If(counter == header_words-2,
NextState("COPY")
)
)
)
no_payload = Signal()
self.sync += \
If(fsm.before_entering("COPY"),
no_payload.eq(sink.eop)
)
if hasattr(sink, "error"):
self.comb += source.error.eq(sink.error)
self.comb += [
source.eop.eq(sink.eop | no_payload),
source.data.eq(sink.data),
header.decode(self.header, source)
]
fsm.act("COPY",
sink.ack.eq(source.ack),
source.stb.eq(sink.stb | no_payload),
If(source.stb & source.ack & source.eop,
NextState("IDLE")
)
)
etherbone_magic = 0x4e6f
etherbone_version = 1
etherbone_packet_header_length = 8
etherbone_packet_header_fields = {
"magic": HeaderField(0, 0, 16),
"version": HeaderField(2, 4, 4),
"nr": HeaderField(2, 2, 1),
"pr": HeaderField(2, 1, 1), # unused
"pf": HeaderField(2, 0, 1), # unused
"addr_size": HeaderField(3, 4, 4), # static
"port_size": HeaderField(3, 0, 4) # static
}
etherbone_packet_header = Header(etherbone_packet_header_fields,
etherbone_packet_header_length,
swap_field_bytes=True)
etherbone_record_header_length = 4
etherbone_record_header_fields = {
"bca": HeaderField(0, 0, 1), # unused
"rca": HeaderField(0, 1, 1), # unused
"rff": HeaderField(0, 2, 1), # unused
"cyc": HeaderField(0, 4, 1), # unused
"wca": HeaderField(0, 5, 1), # unused
"wff": HeaderField(0, 6, 1), # unused
"byte_enable": HeaderField(1, 0, 8),
"wcount": HeaderField(2, 0, 8),
"rcount": HeaderField(3, 0, 8)
}
etherbone_record_header = Header(etherbone_record_header_fields,
etherbone_record_header_length,
swap_field_bytes=True)
def _remove_from_layout(layout, *args):
r = []
for f in layout:
remove = False
for arg in args:
if f[0] == arg:
remove = True
if not remove:
r.append(f)
return r
def etherbone_packet_description(dw):
layout = etherbone_packet_header.get_layout()
layout += [("data", dw)]
return stream.EndpointDescription(layout)
def etherbone_packet_user_description(dw):
layout = etherbone_packet_header.get_layout()
layout = _remove_from_layout(layout,
"magic",
"portsize",
"addrsize",
"version")
layout += user_description(dw).payload_layout
return stream.EndpointDescription(layout)
def etherbone_record_description(dw):
layout = etherbone_record_header.get_layout()
layout += [("data", dw)]
return stream.EndpointDescription(layout)
def etherbone_mmap_description(dw):
layout = [
("we", 1),
("count", 8),
("base_addr", 32),
("be", dw//8),
("addr", 32),
("data", dw)
]
return stream.EndpointDescription(layout)
# etherbone packet
class _EtherbonePacketPacketizer(_Packetizer):
def __init__(self):
_Packetizer.__init__(self,
etherbone_packet_description(32),
user_description(32),
etherbone_packet_header)
class _EtherbonePacketTX(Module):
def __init__(self):
self.sink = sink = stream.Endpoint(etherbone_packet_user_description(32))
self.source = source = stream.Endpoint(user_description(32))
# # #
self.submodules.packetizer = packetizer = _EtherbonePacketPacketizer()
self.comb += [
packetizer.sink.stb.eq(sink.stb),
packetizer.sink.eop.eq(sink.eop),
sink.ack.eq(packetizer.sink.ack),
packetizer.sink.magic.eq(etherbone_magic),
packetizer.sink.port_size.eq(32//8),
packetizer.sink.addr_size.eq(32//8),
packetizer.sink.nr.eq(sink.nr),
packetizer.sink.version.eq(etherbone_version),
packetizer.sink.data.eq(sink.data)
]
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
packetizer.source.ack.eq(1),
If(packetizer.source.stb,
packetizer.source.ack.eq(0),
NextState("SEND")
)
)
fsm.act("SEND",
packetizer.source.connect(source),
source.length.eq(sink.length + etherbone_packet_header.length),
If(source.stb & source.eop & source.ack,
NextState("IDLE")
)
)
class _EtherbonePacketDepacketizer(_Depacketizer):
def __init__(self):
_Depacketizer.__init__(self,
user_description(32),
etherbone_packet_description(32),
etherbone_packet_header)
class _EtherbonePacketRX(Module):
def __init__(self):
self.sink = sink = stream.Endpoint(user_description(32))
self.source = source = stream.Endpoint(etherbone_packet_user_description(32))
# # #
self.submodules.depacketizer = depacketizer = _EtherbonePacketDepacketizer()
self.comb += sink.connect(depacketizer.sink)
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
depacketizer.source.ack.eq(1),
If(depacketizer.source.stb,
depacketizer.source.ack.eq(0),
NextState("CHECK")
)
)
stb = Signal()
self.sync += stb.eq(
depacketizer.source.stb &
(depacketizer.source.magic == etherbone_magic)
)
fsm.act("CHECK",
If(stb,
NextState("PRESENT")
).Else(
NextState("DROP")
)
)
self.comb += [
source.eop.eq(depacketizer.source.eop),
source.nr.eq(depacketizer.source.nr),
source.data.eq(depacketizer.source.data),
source.length.eq(sink.length - etherbone_packet_header.length)
]
fsm.act("PRESENT",
source.stb.eq(depacketizer.source.stb),
depacketizer.source.ack.eq(source.ack),
If(source.stb & source.eop & source.ack,
NextState("IDLE")
)
)
fsm.act("DROP",
depacketizer.source.ack.eq(1),
If(depacketizer.source.stb &
depacketizer.source.eop &
depacketizer.source.ack,
NextState("IDLE")
)
)
class _EtherbonePacket(Module):
def __init__(self, port_sink, port_source):
self.submodules.tx = tx = _EtherbonePacketTX()
self.submodules.rx = rx = _EtherbonePacketRX()
self.comb += [
tx.source.connect(port_sink),
port_source.connect(rx.sink)
]
self.sink, self.source = self.tx.sink, self.rx.source
# etherbone record
class _EtherboneRecordPacketizer(_Packetizer):
def __init__(self):
_Packetizer.__init__(self,
etherbone_record_description(32),
etherbone_packet_user_description(32),
etherbone_record_header)
class _EtherboneRecordDepacketizer(_Depacketizer):
def __init__(self):
_Depacketizer.__init__(self,
etherbone_packet_user_description(32),
etherbone_record_description(32),
etherbone_record_header)
class _EtherboneRecordReceiver(Module):
def __init__(self, buffer_depth=256):
self.sink = sink = stream.Endpoint(etherbone_record_description(32))
self.source = source = stream.Endpoint(etherbone_mmap_description(32))
# # #
fifo = stream.SyncFIFO(etherbone_record_description(32), buffer_depth,
buffered=True)
self.submodules += fifo
self.comb += sink.connect(fifo.sink)
base_addr = Signal(32)
base_addr_update = Signal()
self.sync += If(base_addr_update, base_addr.eq(fifo.source.data))
counter = Signal(max=512)
counter_reset = Signal()
counter_ce = Signal()
self.sync += \
If(counter_reset,
counter.eq(0)
).Elif(counter_ce,
counter.eq(counter + 1)
)
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
fifo.source.ack.eq(1),
counter_reset.eq(1),
If(fifo.source.stb,
base_addr_update.eq(1),
If(fifo.source.wcount,
NextState("RECEIVE_WRITES")
).Elif(fifo.source.rcount,
NextState("RECEIVE_READS")
)
)
)
fsm.act("RECEIVE_WRITES",
source.stb.eq(fifo.source.stb),
source.eop.eq(counter == fifo.source.wcount-1),
source.count.eq(fifo.source.wcount),
source.be.eq(fifo.source.byte_enable),
source.addr.eq(base_addr[2:] + counter),
source.we.eq(1),
source.data.eq(fifo.source.data),
fifo.source.ack.eq(source.ack),
If(source.stb & source.ack,
counter_ce.eq(1),
If(source.eop,
If(fifo.source.rcount,
NextState("RECEIVE_BASE_RET_ADDR")
).Else(
NextState("IDLE")
)
)
)
)
fsm.act("RECEIVE_BASE_RET_ADDR",
counter_reset.eq(1),
If(fifo.source.stb,
base_addr_update.eq(1),
NextState("RECEIVE_READS")
)
)
fsm.act("RECEIVE_READS",
source.stb.eq(fifo.source.stb),
source.eop.eq(counter == fifo.source.rcount-1),
source.count.eq(fifo.source.rcount),
source.base_addr.eq(base_addr),
source.addr.eq(fifo.source.data[2:]),
fifo.source.ack.eq(source.ack),
If(source.stb & source.ack,
counter_ce.eq(1),
If(source.eop,
NextState("IDLE")
)
)
)
class _EtherboneRecordSender(Module):
def __init__(self, buffer_depth=256):
self.sink = sink = stream.Endpoint(etherbone_mmap_description(32))
self.source = source = stream.Endpoint(etherbone_record_description(32))
# # #
pbuffer = stream.SyncFIFO(etherbone_mmap_description(32), buffer_depth)
self.submodules += pbuffer
self.comb += sink.connect(pbuffer.sink)
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
pbuffer.source.ack.eq(1),
If(pbuffer.source.stb,
pbuffer.source.ack.eq(0),
NextState("SEND_BASE_ADDRESS")
)
)
self.comb += [
source.byte_enable.eq(pbuffer.source.be),
If(pbuffer.source.we,
source.wcount.eq(pbuffer.source.count)
).Else(
source.rcount.eq(pbuffer.source.count)
)
]
fsm.act("SEND_BASE_ADDRESS",
source.stb.eq(pbuffer.source.stb),
source.eop.eq(0),
source.data.eq(pbuffer.source.base_addr),
If(source.ack,
NextState("SEND_DATA")
)
)
fsm.act("SEND_DATA",
source.stb.eq(pbuffer.source.stb),
source.eop.eq(pbuffer.source.eop),
source.data.eq(pbuffer.source.data),
If(source.stb & source.ack,
pbuffer.source.ack.eq(1),
If(source.eop,
NextState("IDLE")
)
)
)
class _EtherboneRecord(Module):
def __init__(self):
self.sink = sink = stream.Endpoint(etherbone_packet_user_description(32))
self.source = source = stream.Endpoint(etherbone_packet_user_description(32))
# # #
# receive record, decode it and generate mmap stream
self.submodules.depacketizer = depacketizer = _EtherboneRecordDepacketizer()
self.submodules.receiver = receiver = _EtherboneRecordReceiver()
self.comb += [
sink.connect(depacketizer.sink),
depacketizer.source.connect(receiver.sink)
]
# receive mmap stream, encode it and send records
self.submodules.sender = sender = _EtherboneRecordSender()
self.submodules.packetizer = packetizer = _EtherboneRecordPacketizer()
self.comb += [
sender.source.connect(packetizer.sink),
packetizer.source.connect(source),
source.length.eq(etherbone_record_header.length +
(sender.source.wcount != 0)*4 + sender.source.wcount*4 +
(sender.source.rcount != 0)*4 + sender.source.rcount*4)
]
# etherbone wishbone
class _EtherboneWishboneMaster(Module):
def __init__(self):
self.sink = sink = stream.Endpoint(etherbone_mmap_description(32))
self.source = source = stream.Endpoint(etherbone_mmap_description(32))
self.bus = bus = wishbone.Interface()
# # #
data = Signal(32)
data_update = Signal()
self.sync += If(data_update, data.eq(bus.dat_r))
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
sink.ack.eq(1),
If(sink.stb,
sink.ack.eq(0),
If(sink.we,
NextState("WRITE_DATA")
).Else(
NextState("READ_DATA")
)
)
)
fsm.act("WRITE_DATA",
bus.adr.eq(sink.addr),
bus.dat_w.eq(sink.data),
bus.sel.eq(sink.be),
bus.stb.eq(sink.stb),
bus.we.eq(1),
bus.cyc.eq(1),
If(bus.stb & bus.ack,
sink.ack.eq(1),
If(sink.eop,
NextState("IDLE")
)
)
)
fsm.act("READ_DATA",
bus.adr.eq(sink.addr),
bus.sel.eq(sink.be),
bus.stb.eq(sink.stb),
bus.cyc.eq(1),
If(bus.stb & bus.ack,
data_update.eq(1),
NextState("SEND_DATA")
)
)
fsm.act("SEND_DATA",
source.stb.eq(sink.stb),
source.eop.eq(sink.eop),
source.base_addr.eq(sink.base_addr),
source.addr.eq(sink.addr),
source.count.eq(sink.count),
source.be.eq(sink.be),
source.we.eq(1),
source.data.eq(data),
If(source.stb & source.ack,
sink.ack.eq(1),
If(source.eop,
NextState("IDLE")
).Else(
NextState("READ_DATA")
)
)
)
class _EtherboneWishboneSlave(Module):
def __init__(self):
self.bus = bus = wishbone.Interface()
self.ready = Signal(reset=1)
self.sink = sink = stream.Endpoint(etherbone_mmap_description(32))
self.source = source = stream.Endpoint(etherbone_mmap_description(32))
# # #
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
sink.ack.eq(1),
If(bus.stb & bus.cyc,
If(self.ready,
If(bus.we,
NextState("SEND_WRITE")
).Else(
NextState("SEND_READ")
)
).Else(
NextState("SEND_ERROR")
)
)
)
fsm.act("SEND_WRITE",
If(~self.ready,
NextState("SEND_ERROR")
).Else(
source.stb.eq(1),
source.eop.eq(1),
source.base_addr[2:].eq(bus.adr),
source.count.eq(1),
source.be.eq(bus.sel),
source.we.eq(1),
source.data.eq(bus.dat_w),
If(source.stb & source.ack,
bus.ack.eq(1),
NextState("IDLE")
)
)
)
fsm.act("SEND_READ",
If(~self.ready,
NextState("SEND_ERROR")
).Else(
source.stb.eq(1),
source.eop.eq(1),
source.base_addr.eq(0),
source.count.eq(1),
source.be.eq(bus.sel),
source.we.eq(0),
source.data[2:].eq(bus.adr),
If(source.stb & source.ack,
NextState("WAIT_READ")
)
)
)
fsm.act("WAIT_READ",
sink.ack.eq(1),
If(~self.ready,
NextState("SEND_ERROR")
).Elif(sink.stb & sink.we,
bus.ack.eq(1),
bus.dat_r.eq(sink.data),
NextState("IDLE")
)
)
fsm.act("SEND_ERROR",
bus.ack.eq(1),
bus.err.eq(1)
)
# etherbone
class Etherbone(Module):
def __init__(self, mode="master"):
self.sink = sink = stream.Endpoint(user_description(32))
self.source = source = stream.Endpoint(user_description(32))
# # #
self.submodules.packet = _EtherbonePacket(source, sink)
self.submodules.record = _EtherboneRecord()
if mode == "master":
self.submodules.wishbone = _EtherboneWishboneMaster()
elif mode == "slave":
self.submodules.wishbone = _EtherboneWishboneSlave()
else:
raise ValueError
self.comb += [
self.packet.source.connect(self.record.sink),
self.record.source.connect(self.packet.sink),
self.record.receiver.source.connect(self.wishbone.sink),
self.wishbone.source.connect(self.record.sender.sink)
]

View File

@ -0,0 +1,224 @@
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg, PulseSynchronizer, Gearbox
from migen.genlib.misc import BitSlip
from misoc.cores.code_8b10b import Encoder, Decoder
class KUSSerdes(Module):
def __init__(self, pll, pads, mode="master"):
self.tx_data = Signal(32)
self.rx_data = Signal(32)
self.tx_idle = Signal()
self.tx_comma = Signal()
self.rx_idle = Signal()
self.rx_comma = Signal()
self.rx_bitslip_value = Signal(6)
self.rx_delay_rst = Signal()
self.rx_delay_inc = Signal()
self.rx_delay_ce = Signal()
self.rx_delay_en_vtc = Signal()
# # #
self.submodules.encoder = ClockDomainsRenamer("serwb_serdes")(
Encoder(4, True))
self.decoders = [ClockDomainsRenamer("serwb_serdes")(
Decoder(True)) for _ in range(4)]
self.submodules += self.decoders
# clocking:
# In master mode:
# - linerate/10 pll refclk provided by user
# - linerate/10 slave refclk generated on clk_pads
# In Slave mode:
# - linerate/10 pll refclk provided by clk_pads
self.clock_domains.cd_serwb_serdes = ClockDomain()
self.clock_domains.cd_serwb_serdes_5x = ClockDomain()
self.clock_domains.cd_serwb_serdes_20x = ClockDomain(reset_less=True)
self.comb += [
self.cd_serwb_serdes.clk.eq(pll.serwb_serdes_clk),
self.cd_serwb_serdes_5x.clk.eq(pll.serwb_serdes_5x_clk),
self.cd_serwb_serdes_20x.clk.eq(pll.serwb_serdes_20x_clk)
]
self.specials += AsyncResetSynchronizer(self.cd_serwb_serdes, ~pll.lock)
self.comb += self.cd_serwb_serdes_5x.rst.eq(self.cd_serwb_serdes.rst)
# control/status cdc
tx_idle = Signal()
tx_comma = Signal()
rx_idle = Signal()
rx_comma = Signal()
rx_bitslip_value = Signal(6)
rx_delay_rst = Signal()
rx_delay_inc = Signal()
rx_delay_en_vtc = Signal()
rx_delay_ce = Signal()
self.specials += [
MultiReg(self.tx_idle, tx_idle, "serwb_serdes"),
MultiReg(self.tx_comma, tx_comma, "serwb_serdes"),
MultiReg(rx_idle, self.rx_idle, "sys"),
MultiReg(rx_comma, self.rx_comma, "sys"),
MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serwb_serdes"),
MultiReg(self.rx_delay_inc, rx_delay_inc, "serwb_serdes_5x"),
MultiReg(self.rx_delay_en_vtc, rx_delay_en_vtc, "serwb_serdes_5x")
]
self.submodules.do_rx_delay_rst = PulseSynchronizer("sys", "serwb_serdes_5x")
self.comb += [
rx_delay_rst.eq(self.do_rx_delay_rst.o),
self.do_rx_delay_rst.i.eq(self.rx_delay_rst)
]
self.submodules.do_rx_delay_ce = PulseSynchronizer("sys", "serwb_serdes_5x")
self.comb += [
rx_delay_ce.eq(self.do_rx_delay_ce.o),
self.do_rx_delay_ce.i.eq(self.rx_delay_ce)
]
# tx clock (linerate/10)
if mode == "master":
self.submodules.tx_clk_gearbox = Gearbox(40, "serwb_serdes", 8, "serwb_serdes_5x")
self.comb += self.tx_clk_gearbox.i.eq((0b1111100000 << 30) |
(0b1111100000 << 20) |
(0b1111100000 << 10) |
(0b1111100000 << 0))
clk_o = Signal()
self.specials += [
Instance("OSERDESE3",
p_DATA_WIDTH=8, p_INIT=0,
p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0,
o_OQ=clk_o,
i_RST=ResetSignal("serwb_serdes"),
i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKDIV=ClockSignal("serwb_serdes_5x"),
i_D=self.tx_clk_gearbox.o
),
Instance("OBUFDS",
i_I=clk_o,
o_O=pads.clk_p,
o_OB=pads.clk_n
)
]
# tx datapath
# tx_data -> encoders -> gearbox -> serdes
self.submodules.tx_gearbox = Gearbox(40, "serwb_serdes", 8, "serwb_serdes_5x")
self.comb += [
If(tx_comma,
self.encoder.k[0].eq(1),
self.encoder.d[0].eq(0xbc)
).Else(
self.encoder.d[0].eq(self.tx_data[0:8]),
self.encoder.d[1].eq(self.tx_data[8:16]),
self.encoder.d[2].eq(self.tx_data[16:24]),
self.encoder.d[3].eq(self.tx_data[24:32])
)
]
self.sync.serwb_serdes += \
If(tx_idle,
self.tx_gearbox.i.eq(0)
).Else(
self.tx_gearbox.i.eq(Cat(*[self.encoder.output[i] for i in range(4)]))
)
serdes_o = Signal()
self.specials += [
Instance("OSERDESE3",
p_DATA_WIDTH=8, p_INIT=0,
p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0,
o_OQ=serdes_o,
i_RST=ResetSignal("serwb_serdes"),
i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKDIV=ClockSignal("serwb_serdes_5x"),
i_D=self.tx_gearbox.o
),
Instance("OBUFDS",
i_I=serdes_o,
o_O=pads.tx_p,
o_OB=pads.tx_n
)
]
# rx clock
use_bufr = True
if mode == "slave":
clk_i = Signal()
clk_i_bufg = Signal()
self.specials += [
Instance("IBUFDS",
i_I=pads.clk_p,
i_IB=pads.clk_n,
o_O=clk_i
)
]
if use_bufr:
clk_i_bufr = Signal()
self.specials += [
Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr),
Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg)
]
else:
self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg)
self.comb += pll.refclk.eq(clk_i_bufg)
# rx datapath
# serdes -> gearbox -> bitslip -> decoders -> rx_data
self.submodules.rx_gearbox = Gearbox(8, "serwb_serdes_5x", 40, "serwb_serdes")
self.submodules.rx_bitslip = ClockDomainsRenamer("serwb_serdes")(BitSlip(40))
serdes_i_nodelay = Signal()
self.specials += [
Instance("IBUFDS_DIFF_OUT",
i_I=pads.rx_p,
i_IB=pads.rx_n,
o_O=serdes_i_nodelay
)
]
serdes_i_delayed = Signal()
serdes_q = Signal(8)
self.specials += [
Instance("IDELAYE3",
p_CASCADE="NONE", p_UPDATE_MODE="ASYNC", p_REFCLK_FREQUENCY=200.0,
p_IS_CLK_INVERTED=0, p_IS_RST_INVERTED=0,
p_DELAY_FORMAT="COUNT", p_DELAY_SRC="IDATAIN",
p_DELAY_TYPE="VARIABLE", p_DELAY_VALUE=0,
i_CLK=ClockSignal("serwb_serdes_5x"),
i_RST=rx_delay_rst, i_LOAD=0,
i_INC=rx_delay_inc, i_EN_VTC=rx_delay_en_vtc,
i_CE=rx_delay_ce,
i_IDATAIN=serdes_i_nodelay, o_DATAOUT=serdes_i_delayed
),
Instance("ISERDESE3",
p_DATA_WIDTH=8,
i_D=serdes_i_delayed,
i_RST=ResetSignal("serwb_serdes"),
i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0,
i_CLK=ClockSignal("serwb_serdes_20x"), i_CLK_B=~ClockSignal("serwb_serdes_20x"),
i_CLKDIV=ClockSignal("serwb_serdes_5x"),
o_Q=serdes_q
)
]
self.comb += [
self.rx_gearbox.i.eq(serdes_q),
self.rx_bitslip.value.eq(rx_bitslip_value),
self.rx_bitslip.i.eq(self.rx_gearbox.o),
self.decoders[0].input.eq(self.rx_bitslip.o[0:10]),
self.decoders[1].input.eq(self.rx_bitslip.o[10:20]),
self.decoders[2].input.eq(self.rx_bitslip.o[20:30]),
self.decoders[3].input.eq(self.rx_bitslip.o[30:40]),
self.rx_data.eq(Cat(*[self.decoders[i].d for i in range(4)])),
rx_idle.eq(self.rx_bitslip.o == 0),
rx_comma.eq(((self.decoders[0].d == 0xbc) & (self.decoders[0].k == 1)) &
((self.decoders[1].d == 0x00) & (self.decoders[1].k == 0)) &
((self.decoders[2].d == 0x00) & (self.decoders[2].k == 0)) &
((self.decoders[3].d == 0x00) & (self.decoders[3].k == 0)))
]

View File

@ -0,0 +1,175 @@
from math import ceil
from copy import copy
from collections import OrderedDict
from migen import *
from migen.genlib.misc import WaitTimer
from misoc.interconnect import stream
from misoc.interconnect.stream import EndpointDescription
def reverse_bytes(signal):
n = ceil(len(signal)/8)
return Cat(iter([signal[i*8:(i+1)*8] for i in reversed(range(n))]))
class HeaderField:
def __init__(self, byte, offset, width):
self.byte = byte
self.offset = offset
self.width = width
class Header:
def __init__(self, fields, length, swap_field_bytes=True):
self.fields = fields
self.length = length
self.swap_field_bytes = swap_field_bytes
def get_layout(self):
layout = []
for k, v in sorted(self.fields.items()):
layout.append((k, v.width))
return layout
def get_field(self, obj, name, width):
if "_lsb" in name:
field = getattr(obj, name.replace("_lsb", ""))[:width]
elif "_msb" in name:
field = getattr(obj, name.replace("_msb", ""))[width:2*width]
else:
field = getattr(obj, name)
if len(field) != width:
raise ValueError("Width mismatch on " + name + " field")
return field
def encode(self, obj, signal):
r = []
for k, v in sorted(self.fields.items()):
start = v.byte*8 + v.offset
end = start + v.width
field = self.get_field(obj, k, v.width)
if self.swap_field_bytes:
field = reverse_bytes(field)
r.append(signal[start:end].eq(field))
return r
def decode(self, signal, obj):
r = []
for k, v in sorted(self.fields.items()):
start = v.byte*8 + v.offset
end = start + v.width
field = self.get_field(obj, k, v.width)
if self.swap_field_bytes:
r.append(field.eq(reverse_bytes(signal[start:end])))
else:
r.append(field.eq(signal[start:end]))
return r
def phy_description(dw):
layout = [("data", dw)]
return stream.EndpointDescription(layout)
def user_description(dw):
layout = [
("data", 32),
("length", 32)
]
return stream.EndpointDescription(layout)
class Packetizer(Module):
def __init__(self):
self.sink = sink = stream.Endpoint(user_description(32))
self.source = source = stream.Endpoint(phy_description(32))
# # #
# Packet description
# - preamble : 4 bytes
# - length : 4 bytes
# - payload
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
If(sink.stb,
NextState("INSERT_PREAMBLE")
)
)
fsm.act("INSERT_PREAMBLE",
source.stb.eq(1),
source.data.eq(0x5aa55aa5),
If(source.ack,
NextState("INSERT_LENGTH")
)
)
fsm.act("INSERT_LENGTH",
source.stb.eq(1),
source.data.eq(sink.length),
If(source.ack,
NextState("COPY")
)
)
fsm.act("COPY",
source.stb.eq(sink.stb),
source.data.eq(sink.data),
sink.ack.eq(source.ack),
If(source.ack & sink.eop,
NextState("IDLE")
)
)
class Depacketizer(Module):
def __init__(self, clk_freq, timeout=10):
self.sink = sink = stream.Endpoint(phy_description(32))
self.source = source = stream.Endpoint(user_description(32))
# # #
# Packet description
# - preamble : 4 bytes
# - length : 4 bytes
# - payload
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
self.submodules.timer = WaitTimer(clk_freq*timeout)
self.comb += self.timer.wait.eq(~fsm.ongoing("IDLE"))
fsm.act("IDLE",
sink.ack.eq(1),
If(sink.stb & (sink.data == 0x5aa55aa5),
NextState("RECEIVE_LENGTH")
)
)
fsm.act("RECEIVE_LENGTH",
sink.ack.eq(1),
If(sink.stb,
NextValue(source.length, sink.data),
NextState("COPY")
)
)
eop = Signal()
cnt = Signal(32)
fsm.act("COPY",
source.stb.eq(sink.stb),
source.eop.eq(eop),
source.data.eq(sink.data),
sink.ack.eq(source.ack),
If((source.stb & source.ack & eop) | self.timer.done,
NextState("IDLE")
)
)
self.sync += \
If(fsm.ongoing("IDLE"),
cnt.eq(0)
).Elif(source.stb & source.ack,
cnt.eq(cnt + 1)
)
self.comb += eop.eq(cnt == source.length[2:] - 1)

394
artiq/gateware/serwb/phy.py Normal file
View File

@ -0,0 +1,394 @@
from migen import *
from migen.genlib.cdc import MultiReg, PulseSynchronizer
from migen.genlib.misc import WaitTimer
from misoc.interconnect.csr import *
from artiq.gateware.serwb.kusphy import KUSSerdes
from artiq.gateware.serwb.s7phy import S7Serdes
# Master <--> Slave synchronization:
# 1) Master sends idle pattern (zeroes) to reset Slave.
# 2) Master sends K28.5 commas to allow Slave to calibrate, Slave sends idle pattern.
# 3) Slave sends K28.5 commas to allow Master to calibrate, Master sends K28.5 commas.
# 4) Master stops sending K28.5 commas.
# 5) Slave stops sending K25.5 commas.
# 6) Link is ready.
class _SerdesMasterInit(Module):
def __init__(self, serdes, taps, timeout=1024):
self.reset = Signal()
self.ready = Signal()
self.error = Signal()
# # #
self.delay = delay = Signal(max=taps)
self.delay_min = delay_min = Signal(max=taps)
self.delay_min_found = delay_min_found = Signal()
self.delay_max = delay_max = Signal(max=taps)
self.delay_max_found = delay_max_found = Signal()
self.bitslip = bitslip = Signal(max=40)
timer = WaitTimer(timeout)
self.submodules += timer
self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE"))
self.comb += self.fsm.reset.eq(self.reset)
fsm.act("IDLE",
NextValue(delay, 0),
NextValue(delay_min, 0),
NextValue(delay_min_found, 0),
NextValue(delay_max, 0),
NextValue(delay_max_found, 0),
serdes.rx_delay_rst.eq(1),
NextValue(bitslip, 0),
NextState("RESET_SLAVE"),
serdes.tx_idle.eq(1)
)
fsm.act("RESET_SLAVE",
timer.wait.eq(1),
If(timer.done,
timer.wait.eq(0),
NextState("SEND_PATTERN")
),
serdes.tx_idle.eq(1)
)
fsm.act("SEND_PATTERN",
If(~serdes.rx_idle,
NextState("WAIT_STABLE")
),
serdes.tx_comma.eq(1)
)
fsm.act("WAIT_STABLE",
timer.wait.eq(1),
If(timer.done,
timer.wait.eq(0),
NextState("CHECK_PATTERN")
),
serdes.tx_comma.eq(1)
)
fsm.act("CHECK_PATTERN",
If(~delay_min_found,
If(serdes.rx_comma,
timer.wait.eq(1),
If(timer.done,
timer.wait.eq(0),
NextValue(delay_min, delay),
NextValue(delay_min_found, 1)
)
).Else(
NextState("INC_DELAY_BITSLIP")
),
).Else(
If(~serdes.rx_comma,
NextValue(delay_max, delay),
NextValue(delay_max_found, 1),
NextState("CHECK_SAMPLING_WINDOW")
).Else(
NextState("INC_DELAY_BITSLIP")
)
),
serdes.tx_comma.eq(1)
)
self.comb += serdes.rx_bitslip_value.eq(bitslip)
fsm.act("INC_DELAY_BITSLIP",
NextState("WAIT_STABLE"),
If(delay == (taps - 1),
If(bitslip == (40 - 1),
NextState("ERROR")
).Else(
NextValue(delay_min_found, 0),
NextValue(bitslip, bitslip + 1)
),
NextValue(delay, 0),
serdes.rx_delay_rst.eq(1)
).Else(
NextValue(delay, delay + 1),
serdes.rx_delay_inc.eq(1),
serdes.rx_delay_ce.eq(1)
),
serdes.tx_comma.eq(1)
)
fsm.act("CHECK_SAMPLING_WINDOW",
If((delay_min == 0) |
(delay_max == (taps - 1)) |
((delay_max - delay_min) < taps//16),
NextValue(delay_min_found, 0),
NextValue(delay_max_found, 0),
NextState("WAIT_STABLE")
).Else(
NextState("RESET_SAMPLING_WINDOW")
)
)
fsm.act("RESET_SAMPLING_WINDOW",
NextValue(delay, 0),
serdes.rx_delay_rst.eq(1),
NextState("WAIT_SAMPLING_WINDOW"),
serdes.tx_comma.eq(1)
)
fsm.act("CONFIGURE_SAMPLING_WINDOW",
If(delay == (delay_min + (delay_max - delay_min)[1:]),
NextState("READY")
).Else(
NextValue(delay, delay + 1),
serdes.rx_delay_inc.eq(1),
serdes.rx_delay_ce.eq(1),
NextState("WAIT_SAMPLING_WINDOW")
),
serdes.tx_comma.eq(1)
)
fsm.act("WAIT_SAMPLING_WINDOW",
timer.wait.eq(1),
If(timer.done,
timer.wait.eq(0),
NextState("CONFIGURE_SAMPLING_WINDOW")
),
serdes.tx_comma.eq(1)
)
fsm.act("READY",
self.ready.eq(1)
)
fsm.act("ERROR",
self.error.eq(1)
)
class _SerdesSlaveInit(Module, AutoCSR):
def __init__(self, serdes, taps, timeout=1024):
self.reset = Signal()
self.ready = Signal()
self.error = Signal()
# # #
self.delay = delay = Signal(max=taps)
self.delay_min = delay_min = Signal(max=taps)
self.delay_min_found = delay_min_found = Signal()
self.delay_max = delay_max = Signal(max=taps)
self.delay_max_found = delay_max_found = Signal()
self.bitslip = bitslip = Signal(max=40)
timer = WaitTimer(timeout)
self.submodules += timer
self.comb += self.reset.eq(serdes.rx_idle)
self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE"))
fsm.act("IDLE",
NextValue(delay, 0),
NextValue(delay_min, 0),
NextValue(delay_min_found, 0),
NextValue(delay_max, 0),
NextValue(delay_max_found, 0),
serdes.rx_delay_rst.eq(1),
NextValue(bitslip, 0),
NextState("WAIT_STABLE"),
serdes.tx_idle.eq(1)
)
fsm.act("WAIT_STABLE",
timer.wait.eq(1),
If(timer.done,
timer.wait.eq(0),
NextState("CHECK_PATTERN")
),
serdes.tx_idle.eq(1)
)
fsm.act("CHECK_PATTERN",
If(~delay_min_found,
If(serdes.rx_comma,
timer.wait.eq(1),
If(timer.done,
timer.wait.eq(0),
NextValue(delay_min, delay),
NextValue(delay_min_found, 1)
)
).Else(
NextState("INC_DELAY_BITSLIP")
),
).Else(
If(~serdes.rx_comma,
NextValue(delay_max, delay),
NextValue(delay_max_found, 1),
NextState("CHECK_SAMPLING_WINDOW")
).Else(
NextState("INC_DELAY_BITSLIP")
)
),
serdes.tx_idle.eq(1)
)
self.comb += serdes.rx_bitslip_value.eq(bitslip)
fsm.act("INC_DELAY_BITSLIP",
NextState("WAIT_STABLE"),
If(delay == (taps - 1),
If(bitslip == (40 - 1),
NextState("ERROR")
).Else(
NextValue(delay_min_found, 0),
NextValue(bitslip, bitslip + 1)
),
NextValue(delay, 0),
serdes.rx_delay_rst.eq(1)
).Else(
NextValue(delay, delay + 1),
serdes.rx_delay_inc.eq(1),
serdes.rx_delay_ce.eq(1)
),
serdes.tx_idle.eq(1)
)
fsm.act("CHECK_SAMPLING_WINDOW",
If((delay_min == 0) |
(delay_max == (taps - 1)) |
((delay_max - delay_min) < taps//16),
NextValue(delay_min_found, 0),
NextValue(delay_max_found, 0),
NextState("WAIT_STABLE")
).Else(
NextState("RESET_SAMPLING_WINDOW")
)
)
fsm.act("RESET_SAMPLING_WINDOW",
NextValue(delay, 0),
serdes.rx_delay_rst.eq(1),
NextState("WAIT_SAMPLING_WINDOW")
)
fsm.act("CONFIGURE_SAMPLING_WINDOW",
If(delay == (delay_min + (delay_max - delay_min)[1:]),
NextState("SEND_PATTERN")
).Else(
NextValue(delay, delay + 1),
serdes.rx_delay_inc.eq(1),
serdes.rx_delay_ce.eq(1),
NextState("WAIT_SAMPLING_WINDOW")
)
)
fsm.act("WAIT_SAMPLING_WINDOW",
timer.wait.eq(1),
If(timer.done,
timer.wait.eq(0),
NextState("CONFIGURE_SAMPLING_WINDOW")
)
)
fsm.act("SEND_PATTERN",
timer.wait.eq(1),
If(timer.done,
If(~serdes.rx_comma,
NextState("READY")
)
),
serdes.tx_comma.eq(1)
)
fsm.act("READY",
self.ready.eq(1)
)
fsm.act("ERROR",
self.error.eq(1)
)
class _SerdesControl(Module, AutoCSR):
def __init__(self, init, mode="master"):
if mode == "master":
self.reset = CSR()
self.ready = CSRStatus()
self.error = CSRStatus()
self.delay = CSRStatus(9)
self.delay_min_found = CSRStatus()
self.delay_min = CSRStatus(9)
self.delay_max_found = CSRStatus()
self.delay_max = CSRStatus(9)
self.bitslip = CSRStatus(6)
# # #
if mode == "master":
self.comb += init.reset.eq(self.reset.re)
self.comb += [
self.ready.status.eq(init.ready),
self.error.status.eq(init.error),
self.delay.status.eq(init.delay),
self.delay_min_found.status.eq(init.delay_min_found),
self.delay_min.status.eq(init.delay_min),
self.delay_max_found.status.eq(init.delay_max_found),
self.delay_max.status.eq(init.delay_max),
self.bitslip.status.eq(init.bitslip)
]
class SERWBPLL(Module):
def __init__(self, refclk_freq, linerate, vco_div=1):
assert refclk_freq == 125e6
assert linerate == 1.25e9
self.lock = Signal()
self.refclk = Signal()
self.serwb_serdes_clk = Signal()
self.serwb_serdes_20x_clk = Signal()
self.serwb_serdes_5x_clk = Signal()
# # #
#----------------------------
# refclk: 125MHz
# vco: 1250MHz
#----------------------------
# serwb_serdes: 31.25MHz
# serwb_serdes_20x: 625MHz
# serwb_serdes_5x: 156.25MHz
#----------------------------
self.linerate = linerate
pll_locked = Signal()
pll_fb = Signal()
pll_serwb_serdes_clk = Signal()
pll_serwb_serdes_20x_clk = Signal()
pll_serwb_serdes_5x_clk = Signal()
self.specials += [
Instance("PLLE2_BASE",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
# VCO @ 1.25GHz / vco_div
p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=8.0,
p_CLKFBOUT_MULT=10, p_DIVCLK_DIVIDE=vco_div,
i_CLKIN1=self.refclk, i_CLKFBIN=pll_fb,
o_CLKFBOUT=pll_fb,
# 31.25MHz: serwb_serdes
p_CLKOUT0_DIVIDE=40//vco_div, p_CLKOUT0_PHASE=0.0,
o_CLKOUT0=pll_serwb_serdes_clk,
# 625MHz: serwb_serdes_20x
p_CLKOUT1_DIVIDE=2//vco_div, p_CLKOUT1_PHASE=0.0,
o_CLKOUT1=pll_serwb_serdes_20x_clk,
# 156.25MHz: serwb_serdes_5x
p_CLKOUT2_DIVIDE=8//vco_div, p_CLKOUT2_PHASE=0.0,
o_CLKOUT2=pll_serwb_serdes_5x_clk
),
Instance("BUFG", i_I=pll_serwb_serdes_clk, o_O=self.serwb_serdes_clk),
Instance("BUFG", i_I=pll_serwb_serdes_20x_clk, o_O=self.serwb_serdes_20x_clk),
Instance("BUFG", i_I=pll_serwb_serdes_5x_clk, o_O=self.serwb_serdes_5x_clk)
]
self.specials += MultiReg(pll_locked, self.lock)
class SERWBPHY(Module, AutoCSR):
def __init__(self, device, pll, pads, mode="master"):
assert mode in ["master", "slave"]
if device[:4] == "xcku":
taps = 512
self.submodules.serdes = KUSSerdes(pll, pads, mode)
elif device[:4] == "xc7a":
taps = 32
self.submodules.serdes = S7Serdes(pll, pads, mode)
else:
raise NotImplementedError
if mode == "master":
self.submodules.init = _SerdesMasterInit(self.serdes, taps)
else:
self.submodules.init = _SerdesSlaveInit(self.serdes, taps)
self.submodules.control = _SerdesControl(self.init, mode)

View File

@ -0,0 +1,223 @@
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg, Gearbox
from migen.genlib.misc import BitSlip
from misoc.cores.code_8b10b import Encoder, Decoder
class S7Serdes(Module):
def __init__(self, pll, pads, mode="master"):
self.tx_data = Signal(32)
self.rx_data = Signal(32)
self.tx_idle = Signal()
self.tx_comma = Signal()
self.rx_idle = Signal()
self.rx_comma = Signal()
self.rx_bitslip_value = Signal(6)
self.rx_delay_rst = Signal()
self.rx_delay_inc = Signal()
self.rx_delay_ce = Signal()
# # #
self.submodules.encoder = ClockDomainsRenamer("serwb_serdes")(
Encoder(4, True))
self.decoders = [ClockDomainsRenamer("serwb_serdes")(
Decoder(True)) for _ in range(4)]
self.submodules += self.decoders
# clocking:
# In master mode:
# - linerate/10 pll refclk provided by user
# - linerate/10 slave refclk generated on clk_pads
# In Slave mode:
# - linerate/10 pll refclk provided by clk_pads
self.clock_domains.cd_serwb_serdes = ClockDomain()
self.clock_domains.cd_serwb_serdes_5x = ClockDomain()
self.clock_domains.cd_serwb_serdes_20x = ClockDomain(reset_less=True)
self.comb += [
self.cd_serwb_serdes.clk.eq(pll.serwb_serdes_clk),
self.cd_serwb_serdes_5x.clk.eq(pll.serwb_serdes_5x_clk),
self.cd_serwb_serdes_20x.clk.eq(pll.serwb_serdes_20x_clk)
]
self.specials += AsyncResetSynchronizer(self.cd_serwb_serdes, ~pll.lock)
self.comb += self.cd_serwb_serdes_5x.rst.eq(self.cd_serwb_serdes.rst)
# control/status cdc
tx_idle = Signal()
tx_comma = Signal()
rx_idle = Signal()
rx_comma = Signal()
rx_bitslip_value = Signal(6)
self.specials += [
MultiReg(self.tx_idle, tx_idle, "serwb_serdes"),
MultiReg(self.tx_comma, tx_comma, "serwb_serdes"),
MultiReg(rx_idle, self.rx_idle, "sys"),
MultiReg(rx_comma, self.rx_comma, "sys")
]
self.specials += MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serwb_serdes"),
# tx clock (linerate/10)
if mode == "master":
self.submodules.tx_clk_gearbox = Gearbox(40, "serwb_serdes", 8, "serwb_serdes_5x")
self.comb += self.tx_clk_gearbox.i.eq((0b1111100000 << 30) |
(0b1111100000 << 20) |
(0b1111100000 << 10) |
(0b1111100000 << 0))
clk_o = Signal()
self.specials += [
Instance("OSERDESE2",
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
p_SERDES_MODE="MASTER",
o_OQ=clk_o,
i_OCE=1,
i_RST=ResetSignal("serwb_serdes"),
i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKDIV=ClockSignal("serwb_serdes_5x"),
i_D1=self.tx_clk_gearbox.o[0], i_D2=self.tx_clk_gearbox.o[1],
i_D3=self.tx_clk_gearbox.o[2], i_D4=self.tx_clk_gearbox.o[3],
i_D5=self.tx_clk_gearbox.o[4], i_D6=self.tx_clk_gearbox.o[5],
i_D7=self.tx_clk_gearbox.o[6], i_D8=self.tx_clk_gearbox.o[7]
),
Instance("OBUFDS",
i_I=clk_o,
o_O=pads.clk_p,
o_OB=pads.clk_n
)
]
# tx datapath
# tx_data -> encoders -> gearbox -> serdes
self.submodules.tx_gearbox = Gearbox(40, "serwb_serdes", 8, "serwb_serdes_5x")
self.comb += [
If(tx_comma,
self.encoder.k[0].eq(1),
self.encoder.d[0].eq(0xbc)
).Else(
self.encoder.d[0].eq(self.tx_data[0:8]),
self.encoder.d[1].eq(self.tx_data[8:16]),
self.encoder.d[2].eq(self.tx_data[16:24]),
self.encoder.d[3].eq(self.tx_data[24:32])
)
]
self.sync.serwb_serdes += \
If(tx_idle,
self.tx_gearbox.i.eq(0)
).Else(
self.tx_gearbox.i.eq(Cat(*[self.encoder.output[i] for i in range(4)]))
)
serdes_o = Signal()
self.specials += [
Instance("OSERDESE2",
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
p_SERDES_MODE="MASTER",
o_OQ=serdes_o,
i_OCE=1,
i_RST=ResetSignal("serwb_serdes"),
i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKDIV=ClockSignal("serwb_serdes_5x"),
i_D1=self.tx_gearbox.o[0], i_D2=self.tx_gearbox.o[1],
i_D3=self.tx_gearbox.o[2], i_D4=self.tx_gearbox.o[3],
i_D5=self.tx_gearbox.o[4], i_D6=self.tx_gearbox.o[5],
i_D7=self.tx_gearbox.o[6], i_D8=self.tx_gearbox.o[7]
),
Instance("OBUFDS",
i_I=serdes_o,
o_O=pads.tx_p,
o_OB=pads.tx_n
)
]
# rx clock
use_bufr = True
if mode == "slave":
clk_i = Signal()
clk_i_bufg = Signal()
self.specials += [
Instance("IBUFDS",
i_I=pads.clk_p,
i_IB=pads.clk_n,
o_O=clk_i
)
]
if use_bufr:
clk_i_bufr = Signal()
self.specials += [
Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr),
Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg)
]
else:
self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg)
self.comb += pll.refclk.eq(clk_i_bufg)
# rx datapath
# serdes -> gearbox -> bitslip -> decoders -> rx_data
self.submodules.rx_gearbox = Gearbox(8, "serwb_serdes_5x", 40, "serwb_serdes")
self.submodules.rx_bitslip = ClockDomainsRenamer("serwb_serdes")(BitSlip(40))
serdes_i_nodelay = Signal()
self.specials += [
Instance("IBUFDS_DIFF_OUT",
i_I=pads.rx_p,
i_IB=pads.rx_n,
o_O=serdes_i_nodelay
)
]
serdes_i_delayed = Signal()
serdes_q = Signal(8)
self.specials += [
Instance("IDELAYE2",
p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA",
p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE",
p_REFCLK_FREQUENCY=200.0, p_PIPE_SEL="FALSE",
p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=0,
i_C=ClockSignal(),
i_LD=self.rx_delay_rst,
i_CE=self.rx_delay_ce,
i_LDPIPEEN=0, i_INC=self.rx_delay_inc,
i_IDATAIN=serdes_i_nodelay, o_DATAOUT=serdes_i_delayed
),
Instance("ISERDESE2",
p_DATA_WIDTH=8, p_DATA_RATE="DDR",
p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING",
p_NUM_CE=1, p_IOBDELAY="IFD",
i_DDLY=serdes_i_delayed,
i_CE1=1,
i_RST=ResetSignal("serwb_serdes"),
i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKB=~ClockSignal("serwb_serdes_20x"),
i_CLKDIV=ClockSignal("serwb_serdes_5x"),
i_BITSLIP=0,
o_Q8=serdes_q[0], o_Q7=serdes_q[1],
o_Q6=serdes_q[2], o_Q5=serdes_q[3],
o_Q4=serdes_q[4], o_Q3=serdes_q[5],
o_Q2=serdes_q[6], o_Q1=serdes_q[7]
)
]
self.comb += [
self.rx_gearbox.i.eq(serdes_q),
self.rx_bitslip.value.eq(rx_bitslip_value),
self.rx_bitslip.i.eq(self.rx_gearbox.o),
self.decoders[0].input.eq(self.rx_bitslip.o[0:10]),
self.decoders[1].input.eq(self.rx_bitslip.o[10:20]),
self.decoders[2].input.eq(self.rx_bitslip.o[20:30]),
self.decoders[3].input.eq(self.rx_bitslip.o[30:40]),
self.rx_data.eq(Cat(*[self.decoders[i].d for i in range(4)])),
rx_idle.eq(self.rx_bitslip.o == 0),
rx_comma.eq(((self.decoders[0].d == 0xbc) & (self.decoders[0].k == 1)) &
((self.decoders[1].d == 0x00) & (self.decoders[1].k == 0)) &
((self.decoders[2].d == 0x00) & (self.decoders[2].k == 0)) &
((self.decoders[3].d == 0x00) & (self.decoders[3].k == 0)))
]

View File

@ -59,7 +59,7 @@ class Master(MiniSoC, AMPSoC):
self.add_wb_slave(self.mem_map["drtio_aux"], 0x800, self.add_wb_slave(self.mem_map["drtio_aux"], 0x800,
self.drtio0.aux_controller.bus) self.drtio0.aux_controller.bus)
self.add_memory_region("drtio0_aux", self.mem_map["drtio_aux"] | self.shadow_base, 0x800) self.add_memory_region("drtio0_aux", self.mem_map["drtio_aux"] | self.shadow_base, 0x800)
self.config["has_drtio"] = None self.config["HAS_DRTIO"] = None
self.add_csr_group("drtio", ["drtio0"]) self.add_csr_group("drtio", ["drtio0"])
self.add_memory_group("drtio_aux", ["drtio0_aux"]) self.add_memory_group("drtio_aux", ["drtio0_aux"])

View File

@ -66,7 +66,7 @@ class Satellite(BaseSoC):
self.add_wb_slave(self.mem_map["drtio_aux"], 0x800, self.add_wb_slave(self.mem_map["drtio_aux"], 0x800,
self.drtio0.aux_controller.bus) self.drtio0.aux_controller.bus)
self.add_memory_region("drtio0_aux", self.mem_map["drtio_aux"] | self.shadow_base, 0x800) self.add_memory_region("drtio0_aux", self.mem_map["drtio_aux"] | self.shadow_base, 0x800)
self.config["has_drtio"] = None self.config["HAS_DRTIO"] = None
self.add_csr_group("drtio", ["drtio0"]) self.add_csr_group("drtio", ["drtio0"])
self.add_memory_group("drtio_aux", ["drtio0_aux"]) self.add_memory_group("drtio_aux", ["drtio0_aux"])

View File

@ -192,12 +192,14 @@ class Phaser(MiniSoC, AMPSoC):
self.comb += ad9154_spi.en.eq(1) self.comb += ad9154_spi.en.eq(1)
self.submodules.converter_spi = spi_csr.SPIMaster(ad9154_spi) self.submodules.converter_spi = spi_csr.SPIMaster(ad9154_spi)
self.csr_devices.append("converter_spi") self.csr_devices.append("converter_spi")
self.config["CONVERTER_SPI_DAC_CS"] = 0
self.config["CONVERTER_SPI_CLK_CS"] = 1
self.config["HAS_AD9516"] = None self.config["HAS_AD9516"] = None
self.config["CONVERTER_SPI_AD9516_CS"] = 1
self.config["CONVERTER_SPI_FIRST_AD9154_CS"] = 0
self.submodules.ad9154 = AD9154(platform) self.submodules.ad9154_0 = AD9154(platform)
self.csr_devices.append("ad9154") self.csr_devices.append("ad9154_0")
self.config["HAS_AD9154"] = None
self.add_csr_group("ad9154", ["ad9154_0"])
rtio_channels = [] rtio_channels = []
@ -218,7 +220,7 @@ class Phaser(MiniSoC, AMPSoC):
self.config["RTIO_FIRST_SAWG_CHANNEL"] = len(rtio_channels) self.config["RTIO_FIRST_SAWG_CHANNEL"] = len(rtio_channels)
rtio_channels.extend(rtio.Channel.from_phy(phy) rtio_channels.extend(rtio.Channel.from_phy(phy)
for sawg in self.ad9154.sawgs for sawg in self.ad9154_0.sawgs
for phy in sawg.phys) for phy in sawg.phys)
self.config["HAS_RTIO_LOG"] = None self.config["HAS_RTIO_LOG"] = None
@ -226,7 +228,7 @@ class Phaser(MiniSoC, AMPSoC):
rtio_channels.append(rtio.LogChannel()) rtio_channels.append(rtio.LogChannel())
self.submodules.rtio_crg = _PhaserCRG( self.submodules.rtio_crg = _PhaserCRG(
platform, self.ad9154.jesd.cd_jesd.clk) platform, self.ad9154_0.jesd.cd_jesd.clk)
self.csr_devices.append("rtio_crg") self.csr_devices.append("rtio_crg")
self.submodules.rtio_core = rtio.Core(rtio_channels) self.submodules.rtio_core = rtio.Core(rtio_channels)
self.csr_devices.append("rtio_core") self.csr_devices.append("rtio_core")
@ -248,8 +250,8 @@ class Phaser(MiniSoC, AMPSoC):
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.rtio_crg.cd_rtio.clk) self.crg.cd_sys.clk, self.rtio_crg.cd_rtio.clk)
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.ad9154.jesd.cd_jesd.clk) self.crg.cd_sys.clk, self.ad9154_0.jesd.cd_jesd.clk)
for phy in self.ad9154.jesd.phys: for phy in self.ad9154_0.jesd.phys:
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, phy.transmitter.cd_tx.clk) self.crg.cd_sys.clk, phy.transmitter.cd_tx.clk)

View File

@ -0,0 +1,153 @@
#!/usr/bin/env python3
import argparse
import os
from migen import *
from misoc.cores import gpio
from misoc.integration.soc_sdram import soc_sdram_args, soc_sdram_argdict
from misoc.integration.builder import builder_args, builder_argdict
from misoc.interconnect import stream
from misoc.targets.sayma_amc import MiniSoC
from artiq.gateware.amp import AMPSoC, build_artiq_soc
from artiq.gateware import serwb
from artiq.gateware import remote_csr
from artiq.gateware import rtio
from artiq.gateware.rtio.phy import ttl_simple
from artiq import __version__ as artiq_version
class SaymaAMCStandalone(MiniSoC, AMPSoC):
mem_map = {
"cri_con": 0x10000000,
"rtio": 0x11000000,
"rtio_dma": 0x12000000,
"serwb": 0x13000000,
"mailbox": 0x70000000
}
mem_map.update(MiniSoC.mem_map)
def __init__(self, cpu_type="or1k", **kwargs):
MiniSoC.__init__(self,
cpu_type=cpu_type,
sdram_controller_type="minicon",
l2_size=128*1024,
ident=artiq_version,
ethmac_nrxslots=4,
ethmac_ntxslots=4,
**kwargs)
AMPSoC.__init__(self)
platform = self.platform
platform.toolchain.bitstream_commands.append(
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]")
self.submodules.leds = gpio.GPIOOut(Cat(
platform.request("user_led", 0),
platform.request("user_led", 1)))
self.csr_devices.append("leds")
# forward RTM UART to second FTDI UART channel
serial_1 = platform.request("serial", 1)
serial_rtm = platform.request("serial_rtm")
self.comb += [
serial_1.tx.eq(serial_rtm.rx),
serial_rtm.tx.eq(serial_1.rx)
]
# AMC/RTM serwb
serwb_pll = serwb.phy.SERWBPLL(125e6, 1.25e9, vco_div=2)
self.comb += serwb_pll.refclk.eq(self.crg.cd_sys.clk)
self.submodules += serwb_pll
serwb_pads = platform.request("amc_rtm_serwb")
serwb_phy = serwb.phy.SERWBPHY(platform.device, serwb_pll, serwb_pads, mode="master")
self.submodules.serwb_phy = serwb_phy
self.csr_devices.append("serwb_phy")
serwb_phy.serdes.cd_serwb_serdes.clk.attr.add("keep")
serwb_phy.serdes.cd_serwb_serdes_20x.clk.attr.add("keep")
serwb_phy.serdes.cd_serwb_serdes_5x.clk.attr.add("keep")
platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes.clk, 32.0),
platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes_20x.clk, 1.6),
platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes_5x.clk, 6.4)
platform.add_false_path_constraints(
self.crg.cd_sys.clk,
serwb_phy.serdes.cd_serwb_serdes.clk,
serwb_phy.serdes.cd_serwb_serdes_5x.clk)
serwb_core = serwb.core.SERWBCore(serwb_phy, int(self.clk_freq), mode="slave")
self.submodules += serwb_core
self.add_wb_slave(self.mem_map["serwb"], 8192, serwb_core.etherbone.wishbone.bus)
# RTIO
rtio_channels = []
for i in (2, 3):
phy = ttl_simple.Output(platform.request("user_led", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
for i in (0, 1):
sma_io = platform.request("sma_io", i)
self.comb += sma_io.direction.eq(1)
phy = ttl_simple.Output(sma_io.level)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["HAS_RTIO_LOG"] = None
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel())
self.clock_domains.cd_rtio = ClockDomain()
self.comb += [
self.cd_rtio.clk.eq(ClockSignal()),
self.cd_rtio.rst.eq(ResetSignal())
]
self.submodules.rtio_core = rtio.Core(rtio_channels)
self.csr_devices.append("rtio_core")
self.submodules.rtio = rtio.KernelInitiator()
self.submodules.rtio_dma = ClockDomainsRenamer("sys_kernel")(
rtio.DMA(self.get_native_sdram_if()))
self.register_kernel_cpu_csrdevice("rtio")
self.register_kernel_cpu_csrdevice("rtio_dma")
self.submodules.cri_con = rtio.CRIInterconnectShared(
[self.rtio.cri, self.rtio_dma.cri],
[self.rtio_core.cri])
self.register_kernel_cpu_csrdevice("cri_con")
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj")
self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio_core.cri,
self.get_native_sdram_if())
self.csr_devices.append("rtio_analyzer")
def main():
parser = argparse.ArgumentParser(
description="ARTIQ device binary builder / Sayma AMC stand-alone")
builder_args(parser)
soc_sdram_args(parser)
parser.add_argument("--rtm-csr-csv",
default=os.path.join("artiq_sayma_rtm", "sayma_rtm_csr.csv"),
help="CSV file listing remote CSRs on RTM (default: %(default)s)")
args = parser.parse_args()
soc = SaymaAMCStandalone(**soc_sdram_argdict(args))
remote_csr_regions = remote_csr.get_remote_csr_regions(
soc.mem_map["serwb"] | soc.shadow_base,
args.rtm_csr_csv)
for name, origin, busword, csrs in remote_csr_regions:
soc.add_csr_region(name, origin, busword, csrs)
# Configuration for RTM peripherals. Keep in sync with sayma_rtm.py!
soc.config["HAS_HMC830_7043"] = None
soc.config["CONVERTER_SPI_HMC830_CS"] = 0
soc.config["CONVERTER_SPI_HMC7043_CS"] = 1
soc.config["CONVERTER_SPI_FIRST_AD9154_CS"] = 2
build_artiq_soc(soc, builder_argdict(args))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,155 @@
#!/usr/bin/env python3
import os
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.build.platforms.sinara import sayma_rtm
from misoc.interconnect import wishbone, stream
from misoc.interconnect.csr import *
from misoc.cores import spi
from misoc.integration.wb_slaves import WishboneSlaveManager
from misoc.integration.cpu_interface import get_csr_csv
from artiq.gateware import serwb
class CRG(Module):
def __init__(self, platform):
self.clock_domains.cd_sys = ClockDomain()
self.clock_domains.cd_clk200 = ClockDomain()
clk50 = platform.request("clk50")
self.reset = Signal()
pll_locked = Signal()
pll_fb = Signal()
pll_sys = Signal()
pll_clk200 = Signal()
self.specials += [
Instance("PLLE2_BASE",
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
# VCO @ 1GHz
p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=20.0,
p_CLKFBOUT_MULT=20, p_DIVCLK_DIVIDE=1,
i_CLKIN1=clk50, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb,
# 125MHz
p_CLKOUT0_DIVIDE=8, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=pll_sys,
# 200MHz
p_CLKOUT3_DIVIDE=5, p_CLKOUT3_PHASE=0.0, o_CLKOUT3=pll_clk200
),
Instance("BUFG", i_I=pll_sys, o_O=self.cd_sys.clk),
Instance("BUFG", i_I=pll_clk200, o_O=self.cd_clk200.clk),
AsyncResetSynchronizer(self.cd_sys, ~pll_locked | self.reset),
AsyncResetSynchronizer(self.cd_clk200, ~pll_locked | self.reset)
]
reset_counter = Signal(4, reset=15)
ic_reset = Signal(reset=1)
self.sync.clk200 += \
If(reset_counter != 0,
reset_counter.eq(reset_counter - 1)
).Else(
ic_reset.eq(0)
)
self.specials += Instance("IDELAYCTRL", i_REFCLK=ClockSignal("clk200"), i_RST=ic_reset)
class RTMIdentifier(Module, AutoCSR):
def __init__(self):
self.identifier = CSRStatus(32)
self.comb += self.identifier.status.eq(0x5352544d) # "SRTM"
CSR_RANGE_SIZE = 0x800
class SaymaRTM(Module):
def __init__(self, platform):
csr_devices = []
self.submodules.crg = CRG(platform)
self.crg.cd_sys.clk.attr.add("keep")
clk_freq = 125e6
platform.add_period_constraint(self.crg.cd_sys.clk, 8.0)
self.submodules.rtm_identifier = RTMIdentifier()
csr_devices.append("rtm_identifier")
# clock mux: 125MHz ext SMA clock to HMC830 input
self.comb += [
platform.request("clk_src_ext_sel").eq(1), # use ext clk from sma
platform.request("ref_clk_src_sel").eq(1),
platform.request("dac_clk_src_sel").eq(0), # use clk from dac_clk
]
self.comb += [
platform.request("ad9154_rst_n").eq(1),
platform.request("ad9154_txen", 0).eq(0b11),
platform.request("ad9154_txen", 1).eq(0b11)
]
self.submodules.converter_spi = spi.SPIMaster([
platform.request("hmc_spi"),
platform.request("ad9154_spi", 0),
platform.request("ad9154_spi", 1)])
csr_devices.append("converter_spi")
self.comb += platform.request("hmc7043_reset").eq(0)
# AMC/RTM serwb
serwb_pll = serwb.phy.SERWBPLL(125e6, 1.25e9, vco_div=1)
self.submodules += serwb_pll
serwb_pads = platform.request("amc_rtm_serwb")
serwb_phy = serwb.phy.SERWBPHY(platform.device, serwb_pll, serwb_pads, mode="slave")
self.submodules.serwb_phy = serwb_phy
self.comb += self.crg.reset.eq(serwb_phy.init.reset)
serwb_phy.serdes.cd_serwb_serdes.clk.attr.add("keep")
serwb_phy.serdes.cd_serwb_serdes_20x.clk.attr.add("keep")
serwb_phy.serdes.cd_serwb_serdes_5x.clk.attr.add("keep")
platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes.clk, 32.0),
platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes_20x.clk, 1.6),
platform.add_period_constraint(serwb_phy.serdes.cd_serwb_serdes_5x.clk, 6.4)
platform.add_false_path_constraints(
self.crg.cd_sys.clk,
serwb_phy.serdes.cd_serwb_serdes.clk,
serwb_phy.serdes.cd_serwb_serdes_5x.clk)
serwb_core = serwb.core.SERWBCore(serwb_phy, int(clk_freq), mode="master")
self.submodules += serwb_core
# process CSR devices and connect them to serwb
self.csr_regions = []
wb_slaves = WishboneSlaveManager(0x10000000)
for i, name in enumerate(csr_devices):
origin = i*CSR_RANGE_SIZE
module = getattr(self, name)
csrs = module.get_csrs()
bank = wishbone.CSRBank(csrs)
self.submodules += bank
wb_slaves.add(origin, CSR_RANGE_SIZE, bank.bus)
self.csr_regions.append((name, origin, 32, csrs))
self.submodules += wishbone.Decoder(serwb_core.etherbone.wishbone.bus,
wb_slaves.get_interconnect_slaves(),
register=True)
def main():
build_dir = "artiq_sayma_rtm"
platform = sayma_rtm.Platform()
top = SaymaRTM(platform)
with open(os.path.join(build_dir, "sayma_rtm_csr.csv"), "w") as f:
f.write(get_csr_csv(top.csr_regions))
platform.build(top, build_dir=build_dir)
if __name__ == "__main__":
main()

View File

View File

@ -0,0 +1,70 @@
import unittest
import random
from migen import *
from misoc.interconnect.wishbone import SRAM
from misoc.interconnect.stream import Converter
from artiq.gateware.serwb import packet
from artiq.gateware.serwb import etherbone
class DUT(Module):
def __init__(self):
# wishbone slave
slave_depacketizer = packet.Depacketizer(int(100e6))
slave_packetizer = packet.Packetizer()
self.submodules += slave_depacketizer, slave_packetizer
slave_etherbone = etherbone.Etherbone(mode="slave")
self.submodules += slave_etherbone
self.comb += [
slave_depacketizer.source.connect(slave_etherbone.sink),
slave_etherbone.source.connect(slave_packetizer.sink)
]
# wishbone master
master_depacketizer = packet.Depacketizer(int(100e6))
master_packetizer = packet.Packetizer()
self.submodules += master_depacketizer, master_packetizer
master_etherbone = etherbone.Etherbone(mode="master")
master_sram = SRAM(64, bus=master_etherbone.wishbone.bus)
self.submodules += master_etherbone, master_sram
self.comb += [
master_depacketizer.source.connect(master_etherbone.sink),
master_etherbone.source.connect(master_packetizer.sink)
]
# connect core directly with converters in the loop
s2m_downconverter = Converter(32, 16)
s2m_upconverter = Converter(16, 32)
self.submodules += s2m_downconverter, s2m_upconverter
m2s_downconverter = Converter(32, 16)
m2s_upconverter = Converter(16, 32)
self.submodules += m2s_upconverter, m2s_downconverter
self.comb += [
slave_packetizer.source.connect(s2m_downconverter.sink),
s2m_downconverter.source.connect(s2m_upconverter.sink),
s2m_upconverter.source.connect(master_depacketizer.sink),
master_packetizer.source.connect(m2s_downconverter.sink),
m2s_downconverter.source.connect(m2s_upconverter.sink),
m2s_upconverter.source.connect(slave_depacketizer.sink)
]
# expose wishbone slave
self.wishbone = slave_etherbone.wishbone.bus
class TestEtherbone(unittest.TestCase):
def test_write_read_sram(self):
dut = DUT()
prng = random.Random(1)
def generator(dut):
datas = [prng.randrange(0, 2**32-1) for i in range(16)]
for i in range(16):
yield from dut.wishbone.write(i, datas[i])
for i in range(16):
data = (yield from dut.wishbone.read(i))
self.assertEqual(data, datas[i])
run_simulation(dut, generator(dut))

View File

@ -0,0 +1,164 @@
#!/usr/bin/env python3
import unittest
from migen import *
from artiq.gateware.serwb import packet
from artiq.gateware.serwb import etherbone
from artiq.gateware.serwb.phy import _SerdesMasterInit, _SerdesSlaveInit
class SerdesModel(Module):
def __init__(self, taps, mode="slave"):
self.tx_idle = Signal()
self.tx_comma = Signal()
self.rx_idle = Signal()
self.rx_comma = Signal()
self.rx_bitslip_value = Signal(6)
self.rx_delay_rst = Signal()
self.rx_delay_inc = Signal()
self.rx_delay_ce = Signal()
self.valid_bitslip = Signal(6)
self.valid_delays = Signal(taps)
# # #
delay = Signal(max=taps)
bitslip = Signal(6)
valid_delays = Array(Signal() for i in range(taps))
for i in range(taps):
self.comb += valid_delays[taps-1-i].eq(self.valid_delays[i])
self.sync += [
bitslip.eq(self.rx_bitslip_value),
If(self.rx_delay_rst,
delay.eq(0)
).Elif(self.rx_delay_inc & self.rx_delay_ce,
delay.eq(delay + 1)
)
]
if mode == "master":
self.submodules.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE"))
self.comb += self.fsm.reset.eq(self.tx_idle)
fsm.act("IDLE",
If(self.tx_comma,
NextState("SEND_COMMA")
),
self.rx_idle.eq(1)
)
fsm.act("SEND_COMMA",
If(valid_delays[delay] &
(bitslip == self.valid_bitslip),
self.rx_comma.eq(1)
),
If(~self.tx_comma,
NextState("READY")
)
)
fsm.act("READY")
elif mode == "slave":
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
self.rx_idle.eq(1),
NextState("SEND_COMMA")
)
fsm.act("SEND_COMMA",
If(valid_delays[delay] &
(bitslip == self.valid_bitslip),
self.rx_comma.eq(1)
),
If(~self.tx_idle,
NextState("READY")
)
)
fsm.act("READY")
class DUTMaster(Module):
def __init__(self, taps=32):
self.submodules.serdes = SerdesModel(taps, mode="master")
self.submodules.init = _SerdesMasterInit(self.serdes, taps, timeout=1)
class DUTSlave(Module):
def __init__(self, taps=32):
self.submodules.serdes = SerdesModel(taps, mode="slave")
self.submodules.init = _SerdesSlaveInit(self.serdes, taps, timeout=1)
def generator(test, dut, valid_bitslip, valid_delays, check_success):
yield dut.serdes.valid_bitslip.eq(valid_bitslip)
yield dut.serdes.valid_delays.eq(valid_delays)
while not ((yield dut.init.ready) or
(yield dut.init.error)):
yield
if check_success:
ready = (yield dut.init.ready)
error = (yield dut.init.error)
delay_min = (yield dut.init.delay_min)
delay_max = (yield dut.init.delay_max)
delay = (yield dut.init.delay)
bitslip = (yield dut.init.bitslip)
test.assertEqual(ready, 1)
test.assertEqual(error, 0)
test.assertEqual(delay_min, 4)
test.assertEqual(delay_max, 9)
test.assertEqual(delay, 6)
test.assertEqual(bitslip, valid_bitslip)
else:
ready = (yield dut.init.ready)
error = (yield dut.init.error)
test.assertEqual(ready, 0)
test.assertEqual(error, 1)
class TestPHYInit(unittest.TestCase):
def test_master_init_success(self):
dut = DUTMaster()
valid_bitslip = 2
valid_delays = 0b10001111100000111110000011111000
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, True))
def test_master_init_failure(self):
# partial window at the beginning
dut = DUTMaster()
valid_bitslip = 2
valid_delays = 0b11000000000000000000000000000000
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False))
# partial window at the end
dut = DUTMaster()
valid_bitslip = 2
valid_delays = 0b00000000000000000000000000000011
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False))
# too small window
dut = DUTMaster()
valid_bitslip = 2
valid_delays = 0b00000000000000010000000000000000
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False))
def test_slave_init_success(self):
dut = DUTSlave()
valid_bitslip = 2
valid_delays = 0b10001111100000111110000011111000
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, True))
def test_slave_init_failure(self):
# partial window at the beginning
dut = DUTSlave()
valid_bitslip = 2
valid_delays = 0b11000000000000000000000000000000
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False))
# partial window at the end
dut = DUTSlave()
valid_bitslip = 2
valid_delays = 0b00000000000000000000000000000011
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False))
# too small window
dut = DUTSlave()
valid_bitslip = 2
valid_delays = 0b00000000000000010000000000000000
run_simulation(dut, generator(self, dut, valid_bitslip, valid_delays, False))