forked from M-Labs/artiq
Merge branch 'sinara'
This commit is contained in:
commit
b4c52c34f7
|
@ -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 }
|
||||||
|
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()) {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
from artiq.gateware.serwb import s7phy, kusphy, phy, core, packet, etherbone
|
|
@ -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),
|
||||||
|
]
|
|
@ -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)
|
||||||
|
]
|
|
@ -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)))
|
||||||
|
|
||||||
|
]
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)))
|
||||||
|
|
||||||
|
]
|
|
@ -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"])
|
||||||
|
|
||||||
|
|
|
@ -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"])
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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()
|
|
@ -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()
|
|
@ -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))
|
|
@ -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))
|
Loading…
Reference in New Issue