OLED example #74
@@ -4,6 +4,10 @@ Zynq 7000 Bare-Metal Rust Support
|
||||
This crate collection provides support to write bare-metal Rust applications for the AMD Zynq 7000
|
||||
family of SoCs.
|
||||
|
||||
<p align="center">
|
||||
<img src="./ferris-zedboard.jpeg" alt="Ferris on the Zedboard" width="400" />
|
||||
</p>
|
||||
|
||||
# List of crates
|
||||
|
||||
This project contains the following crates:
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 414 KiB |
@@ -27,9 +27,14 @@ critical-section = "1"
|
||||
static_cell = "2"
|
||||
embedded-alloc = "0.7"
|
||||
embedded-hal = "1"
|
||||
embedded-hal-bus = "0.3"
|
||||
embedded-hal-async = "1"
|
||||
dummy-pin = "1"
|
||||
fugit = "0.3"
|
||||
embedded-graphics = "0.8"
|
||||
log = "0.4"
|
||||
ssd1306 = { version = "0.10" }
|
||||
tinybmp = "0.7"
|
||||
rand = { version = "0.10", default-features = false }
|
||||
|
||||
embassy-executor = { version = "0.10", features = ["platform-cortex-ar", "executor-thread"] }
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 402 B |
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="0.16in" height="0.106667in"
|
||||
viewBox="0 0 48 32">
|
||||
<defs>
|
||||
</defs>
|
||||
<image id="raster0"
|
||||
x="0"
|
||||
y="0"
|
||||
width="48"
|
||||
height="32"
|
||||
opacity="1.000000"
|
||||
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAgCAQAAAD+3TOXAAAHDmVYSWZJSSoACAAAAAAADgAAAAkA/gAEAAEAAAABAAAAAAEEAAEAAAAAAQAAAQEEAAEAAACqAAAAAgEDAAMAAACAAAAAAwEDAAEAAAAGAAAABgEDAAEAAAAGAAAAFQEDAAEAAAADAAAAAQIEAAEAAACGAAAAAgIEAAEAAACIBgAAAAAAAAgACAAIAP/Y/+AAEEpGSUYAAQEAAAEAAQAA/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAqgEAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A+f6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAr6/+CX/ACSHQv8At4/9KJK+QK+v/gl/ySHQv+3j/wBKJKAPnzxr8Jte8B6NDqmqXemzQS3C26rayOzBirNk7kUYwh7+lcHX0/8AtHf8k80//sKx/wDoqWvmCgAroPAn/JQ/DX/YVtf/AEatc/XQeBP+Sh+Gv+wra/8Ao1aAPqP4s+CtS8eeFbXS9LntIZ4r1LhmunZVKhHXA2qxzlx29a+VPFPhu88I+I7vQ7+SCS6tdm94GJQ7kVxgkA9GHavuevkD42/8le13/t3/APSeOgDz+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK+g/hL8WdB0rw74f8Hz2mpNqD3BtxIkaGLdLOxU5Lg4+cZ49etfPlWLC+uNM1G2v7OTy7q1lSaF9oO11IKnB4OCB1oA+968j+J3xZ0HSofEXg+e01JtQeye3EiRoYt0sOVOS4OPnGePXrXjn/AAu34h/9DD/5JW//AMbrj9b1vUfEesT6tq1x9ovp9vmS7FTdtUKOFAA4AHAoAz6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9mZmF3EAAAAAXNSR0IB2cksfwAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAhhJREFUSMft1T1I1WEUBvDfVQn1hoHoDQlMqCSlaLAiCKMhbIpqKLCtTQKJoLWh76GgpQ+isKWMgoiiocgpKoKEoCHCoHJJ1GjRAvu4p8GPrt57Ta/W5HmW9/w553ne9z3nf14WbJ6tXMu/or7vPM76aoVavbbMH3W/NtXSwnU/hJd6hA5LPXZ0btQbJNQLYVBMQdqgcBc1agoVCD1uZ1FnYtAt313+O9WiPAIzw8UpeUnnbEZy1C1WpskzqzJCLvnozowF3nnkm4ax3OU+CJU4IQU3bFMuDGuaEDgyY/I/GN1vwhshUKbf3hJb7TOiy4CUbgmstER61vXqsxpvbZo4yXEpQZuQtt4LIdTjaQG7H0erPWOrDmFENc1C6NUlhEYcKJj+s3IpPyf8U6NHuTDxoQ8NDhUs8FqzSmfGvIdKxu+vzZAQ2rXnS45pvEkYViuE04oyS1TlldAs6WM+gcjrTcJJXcL27C6o0yvtis45CaQ9Fw7mbrRlHsyhf/7gZiZpccZ6SNKOeZi+a5R6kutPapEWwq95OMW+cdLMSo9IgGO+ueTLdNMvhw1M8u7JOz2HVQuh1G7XsnsqR3n7ddqpGKFp6g4SWRKw2LAKQ6DGOiusVaF1SuxVfd7q1pORW+mT0izWHLdQ53ABz1OxEMr/HhiSNs6SvkSjIu9zFyiXRCG2KzuvKGdgwn4VBQhU5eHLI7Jg/8d+A9OUD/TqXTt5AAAAAElFTkSuQmCCAA==" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 80 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 274 B |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 8.4 KiB |
Binary file not shown.
@@ -57,12 +57,17 @@ async fn main(spawner: Spawner) -> ! {
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let mut clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
|
||||
// SPI reference clock must be larger than the CPU 1x clock.
|
||||
let spi_ref_clk_div = spi::calculate_largest_allowed_spi_ref_clk_divisor(&clocks)
|
||||
.unwrap()
|
||||
.value()
|
||||
- 1;
|
||||
spi::configure_spi_ref_clk(&mut clocks, arbitrary_int::u6::new(spi_ref_clk_div as u8));
|
||||
let target_spi_ref_clock = clocks.arm_clocks().cpu_1x_clk() * 2;
|
||||
// SPI reference clock must be larger than the CPU 1x clock. Also, taking the largest value
|
||||
// actually seems to be problematic. We take 200 MHz here, which is significantly larger than
|
||||
// the CPU 1x clock which is around 110 MHz.
|
||||
spi::configure_spi_ref_clock(&mut clocks, target_spi_ref_clock);
|
||||
|
||||
assert!(
|
||||
clocks.io_clocks().spi_clk().raw()
|
||||
> (clocks.arm_clocks().cpu_1x_clk().raw() as f32 * 1.2) as u32,
|
||||
"SPI reference clock must be larger than CPU 1x clock"
|
||||
);
|
||||
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||
@@ -99,23 +104,21 @@ async fn main(spawner: Spawner) -> ! {
|
||||
|
||||
if DEBUG_SPI_CLK_CONFIG {
|
||||
info!(
|
||||
"SPI Clock Information: CPU 1x: {:?}, IO Ref Clk: {:?}, SPI Ref Clk: {:?}, DIV: {:?}",
|
||||
"SPI Clock Information: CPU 1x: {:?}, IO Ref Clk: {:?}, SPI Ref Clk: {:?}",
|
||||
clocks.arm_clocks().cpu_1x_clk(),
|
||||
clocks.io_clocks().ref_clk(),
|
||||
clocks.io_clocks().spi_clk(),
|
||||
spi_ref_clk_div
|
||||
);
|
||||
}
|
||||
|
||||
let mut spi = spi::Spi::new_one_hw_cs(
|
||||
dp.spi_1,
|
||||
clocks.io_clocks(),
|
||||
spi::Config::new(
|
||||
// 10 MHz maximum rating of the sensor.
|
||||
zynq7000::spi::BaudDivSel::By64,
|
||||
//l3gd20::MODE,
|
||||
embedded_hal::spi::MODE_3,
|
||||
spi::SlaveSelectConfig::AutoWithAutoStart,
|
||||
spi::SlaveSelectConfig::AutoCsAutoStart,
|
||||
),
|
||||
(
|
||||
gpio_pins.mio.mio12,
|
||||
@@ -125,10 +128,13 @@ async fn main(spawner: Spawner) -> ! {
|
||||
gpio_pins.mio.mio13,
|
||||
)
|
||||
.unwrap();
|
||||
let sclk = Hertz::from_raw(
|
||||
clocks.io_clocks().spi_clk().raw() / zynq7000::spi::BaudDivSel::By64.div_value() as u32,
|
||||
);
|
||||
let mod_id = spi.regs().read_mod_id();
|
||||
assert_eq!(mod_id, spi::MODULE_ID);
|
||||
assert!(spi.sclk() <= Hertz::from_raw(10_000_000));
|
||||
let min_delay = (spi.sclk().raw() * 5) / 1_000_000_000;
|
||||
assert!(sclk <= Hertz::from_raw(10_000_000));
|
||||
let min_delay = (sclk.raw() * 5) / 1_000_000_000;
|
||||
spi.inner().configure_delays(
|
||||
DelayControl::builder()
|
||||
.with_inter_word_cs_deassert(0)
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aarch32_cpu::asm::nop;
|
||||
use core::panic::PanicInfo;
|
||||
use dummy_pin::DummyPin;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_time::{Delay, Duration, Ticker};
|
||||
use embedded_graphics::{Drawable as _, geometry::Point};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_hal_async::delay::DelayNs as _;
|
||||
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use ssd1306::{Ssd1306, prelude::*};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zynq7000_hal::{
|
||||
BootMode, clocks, gic, gpio, gtc, spi,
|
||||
time::Hertz,
|
||||
uart::{self, TxAsync},
|
||||
};
|
||||
|
||||
use embedded_graphics::image::Image;
|
||||
use tinybmp::Bmp;
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard OLED example --\n\r";
|
||||
|
||||
/// Entry point which calls the embassy main method.
|
||||
#[zynq7000_rt::entry]
|
||||
fn entry_point() -> ! {
|
||||
main();
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) -> ! {
|
||||
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
|
||||
init_l2_cache: true,
|
||||
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
|
||||
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
|
||||
})
|
||||
.unwrap();
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let mut clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
|
||||
let target_spi_ref_clock = clocks.arm_clocks().cpu_1x_clk() * 2;
|
||||
// SPI reference clock must be larger than the CPU 1x clock. Also, taking the largest value
|
||||
// actually seems to be problematic. We take 200 MHz here, which is significantly larger than
|
||||
// the CPU 1x clock which is around 110 MHz.
|
||||
spi::configure_spi_ref_clock(&mut clocks, target_spi_ref_clock);
|
||||
|
||||
assert!(
|
||||
clocks.io_clocks().spi_clk().raw()
|
||||
> (clocks.arm_clocks().cpu_1x_clk().raw() as f32 * 1.2) as u32,
|
||||
"SPI reference clock must be larger than CPU 1x clock"
|
||||
);
|
||||
|
||||
let mut gpio_pins = gpio::GpioPins::new(periphs.gpio);
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut uart = uart::Uart::new_with_mio_for_uart_1(
|
||||
periphs.uart_1,
|
||||
uart::Config::new_with_clk_config(uart_clk_config),
|
||||
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
uart.flush().unwrap();
|
||||
|
||||
// Safety: We are not multi-threaded yet.
|
||||
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace)
|
||||
.expect("Failed to initialize async logger");
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
info!(
|
||||
"SPI reference clock speed: {:?}",
|
||||
clocks.io_clocks().spi_clk()
|
||||
);
|
||||
|
||||
spawner.spawn(logger_task(uart, log_reader).unwrap());
|
||||
|
||||
let mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
|
||||
let emio_leds: [gpio::Output; 8] = [
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), gpio::PinState::Low),
|
||||
];
|
||||
spawner.spawn(blinky_task(mio_led, emio_leds).unwrap());
|
||||
|
||||
let dc_pin = gpio::Output::new_for_emio(gpio_pins.emio.take(11).unwrap(), gpio::PinState::High);
|
||||
|
||||
let mut reset_pin =
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(12).unwrap(), gpio::PinState::High);
|
||||
let mut oled_vdd_switch =
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(13).unwrap(), gpio::PinState::High);
|
||||
let mut oled_vbat_switch =
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(14).unwrap(), gpio::PinState::High);
|
||||
Delay.delay_ms(100).await;
|
||||
|
||||
let spi = spi::Spi::new_for_emio(
|
||||
periphs.spi_0,
|
||||
spi::Config::calculate_for_io_clock(
|
||||
Hertz::MHz(8),
|
||||
clocks.io_clocks(),
|
||||
embedded_hal::spi::MODE_0,
|
||||
spi::SlaveSelectConfig::AutoCsManualStart,
|
||||
),
|
||||
)
|
||||
.expect("Failed to initialize SPI");
|
||||
let exclusive_device = ExclusiveDevice::new(spi, DummyPin::new_high(), NoDelay)
|
||||
.expect("Failed to create exclusive SPI device");
|
||||
let spi_if = SPIInterface::new(exclusive_device, dc_pin);
|
||||
let mut ssd1306 = Ssd1306::new(spi_if, DisplaySize128x32, DisplayRotation::Rotate180);
|
||||
|
||||
oled_vdd_switch.set_low();
|
||||
oled_vbat_switch.set_low();
|
||||
Delay.delay_ms(100).await;
|
||||
|
||||
ssd1306.reset(&mut reset_pin, &mut embassy_time::Delay {});
|
||||
let mut display = ssd1306.into_buffered_graphics_mode();
|
||||
display.init().unwrap();
|
||||
|
||||
// Include the BMP file data.
|
||||
let ferris_data = include_bytes!("../../assets/ferris-flat-happy-small.bmp");
|
||||
let rust_logo_data = include_bytes!("../../assets/rust-logo-single-path.bmp");
|
||||
// Parse the BMP file.
|
||||
let bmp_rust = Bmp::from_slice(rust_logo_data).unwrap();
|
||||
let bmp_ferris = Bmp::from_slice(ferris_data).unwrap();
|
||||
// Draw the image with the top left corner at (10, 20) by wrapping it in
|
||||
// an embedded-graphics `Image`.
|
||||
Image::new(&bmp_rust, Point::new(0, 0))
|
||||
.draw(&mut display)
|
||||
.unwrap();
|
||||
Image::new(&bmp_ferris, Point::new(32, 0))
|
||||
.draw(&mut display)
|
||||
.unwrap();
|
||||
display.flush().unwrap();
|
||||
let mut ticker = Ticker::every(Duration::from_millis(50));
|
||||
let mut ferris = FerrisMovement::new(32, Direction::Right, 32, 76);
|
||||
|
||||
loop {
|
||||
Image::new(&bmp_ferris, Point::new(ferris.pos as i32, 0))
|
||||
.draw(&mut display)
|
||||
.unwrap();
|
||||
display.flush().unwrap();
|
||||
ferris.step();
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
Right,
|
||||
Left,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct FerrisMovement {
|
||||
pub pos: u16,
|
||||
pub dir: Direction,
|
||||
pub left_threshold: u16,
|
||||
pub right_threshold: u16,
|
||||
}
|
||||
|
||||
impl FerrisMovement {
|
||||
/// Creates a new movement controller.
|
||||
/// Assumes: left_threshold <= right_threshold and pos is within that range.
|
||||
pub fn new(pos: u16, dir: Direction, left_threshold: u16, right_threshold: u16) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
dir,
|
||||
left_threshold,
|
||||
right_threshold,
|
||||
}
|
||||
}
|
||||
|
||||
/// Move one tick and "bounce" between thresholds.
|
||||
pub fn step(&mut self) {
|
||||
match self.dir {
|
||||
Direction::Right => {
|
||||
if self.pos >= self.right_threshold {
|
||||
self.dir = Direction::Left; // flip at right boundary
|
||||
} else {
|
||||
self.pos += 1;
|
||||
}
|
||||
}
|
||||
Direction::Left => {
|
||||
if self.pos <= self.left_threshold {
|
||||
self.dir = Direction::Right; // flip at left boundary
|
||||
} else {
|
||||
self.pos -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn logger_task(
|
||||
uart: uart::Uart,
|
||||
reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>,
|
||||
) -> ! {
|
||||
let (tx, _) = uart.split();
|
||||
let mut tx_async = TxAsync::new(tx);
|
||||
let mut log_buf: [u8; 2048] = [0; 2048];
|
||||
loop {
|
||||
let read_bytes = reader.read(&mut log_buf).await;
|
||||
if read_bytes > 0 {
|
||||
tx_async.write(&log_buf[0..read_bytes]).unwrap().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn blinky_task(mut mio_led: gpio::Output, mut emio_leds: [gpio::Output; 8]) {
|
||||
let mut ticker = Ticker::every(Duration::from_millis(200));
|
||||
loop {
|
||||
mio_led.toggle().unwrap();
|
||||
|
||||
// Create a wave pattern for emio_leds
|
||||
for led in emio_leds.iter_mut() {
|
||||
led.toggle().unwrap();
|
||||
ticker.next().await; // Wait for the next ticker for each toggle
|
||||
}
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = gic::GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
gic::Interrupt::Sgi(_) => (),
|
||||
gic::Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
gic::Interrupt::Spi(spi_interrupt) => match spi_interrupt {
|
||||
gic::SpiInterrupt::Uart1 => {
|
||||
zynq7000_hal::uart::tx_async::on_interrupt_tx(uart::UartId::Uart1);
|
||||
}
|
||||
_ => {
|
||||
log::warn!("Unhandled SPI interrupt: {:?}", spi_interrupt);
|
||||
}
|
||||
},
|
||||
gic::Interrupt::Invalid(_) => (),
|
||||
gic::Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[zynq7000_rt::exception(DataAbort)]
|
||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[zynq7000_rt::exception(Undefined)]
|
||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {info:?}");
|
||||
loop {}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ pub fn on_interrupt(peripheral: SpiId) {
|
||||
let enabled_irqs = spi.read_enabled_interrupts();
|
||||
// Prevent spurious interrupts from messing with out logic here.
|
||||
spi.disable_interrupts();
|
||||
let interrupt_status = spi.read_isr();
|
||||
let interrupt_status = spi.read_interrupt_status();
|
||||
spi.clear_interrupts();
|
||||
// IRQ is not related.
|
||||
if !enabled_irqs.tx_below_threshold()
|
||||
|
||||
@@ -19,9 +19,10 @@ use arbitrary_int::{prelude::*, u3, u4, u6};
|
||||
use embedded_hal::delay::DelayNs;
|
||||
pub use embedded_hal::spi::Mode;
|
||||
use zynq7000::slcr::reset::DualRefAndClockResetSpiUart;
|
||||
pub use zynq7000::spi::DelayControl;
|
||||
use zynq7000::spi::{
|
||||
BaudDivSel, DelayControl, FifoWrite, InterruptControl, InterruptEnabled, InterruptStatus,
|
||||
MmioRegisters, SPI_0_BASE_ADDR, SPI_1_BASE_ADDR,
|
||||
BaudDivSel, FifoWrite, InterruptControl, InterruptEnabled, InterruptStatus, MmioRegisters,
|
||||
SPI_0_BASE_ADDR, SPI_1_BASE_ADDR,
|
||||
};
|
||||
|
||||
pub const FIFO_DEPTH: usize = 128;
|
||||
@@ -352,13 +353,16 @@ impl ChipSelect {
|
||||
/// Slave select configuration.
|
||||
pub enum SlaveSelectConfig {
|
||||
/// User must take care of controlling slave select lines as well as issuing a start command.
|
||||
ManualWithManualStart = 0b11,
|
||||
ManualAutoStart = 0b10,
|
||||
ManualCsManualStart = 0b11,
|
||||
/// Software controls the slave select, but the controller hardware automatically starts to
|
||||
/// serialize data when there is data in the TxFIFO.
|
||||
ManualCsAutoStart = 0b10,
|
||||
/// Hardware slave select, but start needs to be issued manually.
|
||||
AutoWithManualStart = 0b01,
|
||||
/// Hardware slave select, auto serialiation if there is data in the TX FIFO.
|
||||
AutoCsManualStart = 0b01,
|
||||
/// Hardware slave select, auto serialiation if there is data in the TX FIFO. Might be
|
||||
/// problematic for higher SPI speeds, where the processor can not fill the TX FIFO fast enough.
|
||||
#[default]
|
||||
AutoWithAutoStart = 0b00,
|
||||
AutoCsAutoStart = 0b00,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@@ -379,6 +383,31 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculate_for_io_clock(
|
||||
target_clock: Hertz,
|
||||
io_clock: &IoClocks,
|
||||
init_mode: Mode,
|
||||
ss_config: SlaveSelectConfig,
|
||||
) -> Self {
|
||||
let divisor_raw = io_clock.spi_clk().raw().div_ceil(target_clock.raw());
|
||||
let baud_div_sel = match divisor_raw {
|
||||
0..=4 => BaudDivSel::By4,
|
||||
5..=8 => BaudDivSel::By8,
|
||||
9..=16 => BaudDivSel::By16,
|
||||
17..=32 => BaudDivSel::By32,
|
||||
33..=64 => BaudDivSel::By64,
|
||||
65..=128 => BaudDivSel::By128,
|
||||
129..=256 => BaudDivSel::By256,
|
||||
_ => BaudDivSel::By256,
|
||||
};
|
||||
Self {
|
||||
baud_div: baud_div_sel,
|
||||
init_mode,
|
||||
ss_config,
|
||||
with_ext_decoding: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_external_decoding(&mut self) {
|
||||
self.with_ext_decoding = true;
|
||||
}
|
||||
@@ -474,10 +503,10 @@ impl SpiLowLevel {
|
||||
pub fn reconfigure(&mut self, config: Config) {
|
||||
self.regs.write_enable(0);
|
||||
let (man_ss, man_start) = match config.ss_config {
|
||||
SlaveSelectConfig::ManualWithManualStart => (true, true),
|
||||
SlaveSelectConfig::ManualAutoStart => (true, false),
|
||||
SlaveSelectConfig::AutoWithManualStart => (false, true),
|
||||
SlaveSelectConfig::AutoWithAutoStart => (false, false),
|
||||
SlaveSelectConfig::ManualCsManualStart => (true, true),
|
||||
SlaveSelectConfig::ManualCsAutoStart => (true, false),
|
||||
SlaveSelectConfig::AutoCsManualStart => (false, true),
|
||||
SlaveSelectConfig::AutoCsAutoStart => (false, false),
|
||||
};
|
||||
let (cpol, cpha) = spi_mode_const_to_cpol_cpha(config.init_mode);
|
||||
|
||||
@@ -530,14 +559,11 @@ impl SpiLowLevel {
|
||||
|
||||
#[inline]
|
||||
pub fn issue_manual_start(&mut self) {
|
||||
self.regs.modify_cr(|mut val| {
|
||||
val.set_manual_start(true);
|
||||
val
|
||||
});
|
||||
self.regs.modify_cr(|val| val.with_manual_start(true));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_isr(&self) -> InterruptStatus {
|
||||
pub fn read_interrupt_status(&self) -> InterruptStatus {
|
||||
self.regs.read_interrupt_status()
|
||||
}
|
||||
|
||||
@@ -569,6 +595,11 @@ impl SpiLowLevel {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_delay_control(&mut self, delay_control: DelayControl) {
|
||||
self.regs.write_delay_control(delay_control);
|
||||
}
|
||||
|
||||
/// This disables all interrupts relevant for non-blocking interrupt driven SPI operation
|
||||
/// in SPI master mode.
|
||||
#[inline]
|
||||
@@ -638,9 +669,7 @@ impl core::ops::DerefMut for SpiLowLevel {
|
||||
/// Blocking Driver for the PS SPI peripheral in master mode.
|
||||
pub struct Spi {
|
||||
inner: SpiLowLevel,
|
||||
sclk: Hertz,
|
||||
config: Config,
|
||||
outstanding_rx: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -669,7 +698,6 @@ pub enum SpiConstructionError {
|
||||
impl Spi {
|
||||
pub fn new_no_hw_ss<Sck: SckPin, Mosi: MosiPin, Miso: MisoPin>(
|
||||
spi: impl PsSpi,
|
||||
clocks: &IoClocks,
|
||||
config: Config,
|
||||
spi_pins: (Sck, Mosi, Miso),
|
||||
) -> Result<Self, SpiConstructionError> {
|
||||
@@ -687,17 +715,11 @@ impl Spi {
|
||||
IoPeriphPin::new(spi_pins.0, SPI_MUX_CONF, Some(false));
|
||||
IoPeriphPin::new(spi_pins.1, SPI_MUX_CONF, Some(false));
|
||||
IoPeriphPin::new(spi_pins.2, SPI_MUX_CONF, Some(false));
|
||||
Ok(Self::new_generic_unchecked(
|
||||
spi_id,
|
||||
spi.reg_block(),
|
||||
clocks,
|
||||
config,
|
||||
))
|
||||
Ok(Self::new_generic_unchecked(spi_id, spi.reg_block(), config))
|
||||
}
|
||||
|
||||
pub fn new_one_hw_cs<Sck: SckPin, Mosi: MosiPin, Miso: MisoPin, Ss: SsPin>(
|
||||
spi: impl PsSpi,
|
||||
clocks: &IoClocks,
|
||||
config: Config,
|
||||
spi_pins: (Sck, Mosi, Miso),
|
||||
ss_pin: Ss,
|
||||
@@ -717,17 +739,11 @@ impl Spi {
|
||||
IoPeriphPin::new(spi_pins.1, SPI_MUX_CONF, Some(false));
|
||||
IoPeriphPin::new(spi_pins.2, SPI_MUX_CONF, Some(false));
|
||||
IoPeriphPin::new(ss_pin, SPI_MUX_CONF, Some(false));
|
||||
Ok(Self::new_generic_unchecked(
|
||||
spi_id,
|
||||
spi.reg_block(),
|
||||
clocks,
|
||||
config,
|
||||
))
|
||||
Ok(Self::new_generic_unchecked(spi_id, spi.reg_block(), config))
|
||||
}
|
||||
|
||||
pub fn new_with_two_hw_cs<Sck: SckPin, Mosi: MosiPin, Miso: MisoPin, Ss0: SsPin, Ss1: SsPin>(
|
||||
spi: impl PsSpi,
|
||||
clocks: &IoClocks,
|
||||
config: Config,
|
||||
spi_pins: (Sck, Mosi, Miso),
|
||||
ss_pins: (Ss0, Ss1),
|
||||
@@ -757,12 +773,7 @@ impl Spi {
|
||||
IoPeriphPin::new(spi_pins.2, SPI_MUX_CONF, Some(false));
|
||||
IoPeriphPin::new(ss_pins.0, SPI_MUX_CONF, Some(false));
|
||||
IoPeriphPin::new(ss_pins.1, SPI_MUX_CONF, Some(false));
|
||||
Ok(Self::new_generic_unchecked(
|
||||
spi_id,
|
||||
spi.reg_block(),
|
||||
clocks,
|
||||
config,
|
||||
))
|
||||
Ok(Self::new_generic_unchecked(spi_id, spi.reg_block(), config))
|
||||
}
|
||||
|
||||
pub fn new_with_three_hw_cs<
|
||||
@@ -774,7 +785,6 @@ impl Spi {
|
||||
Ss2: SsPin,
|
||||
>(
|
||||
spi: impl PsSpi,
|
||||
clocks: &IoClocks,
|
||||
config: Config,
|
||||
spi_pins: (Sck, Mosi, Miso),
|
||||
ss_pins: (Ss0, Ss1, Ss2),
|
||||
@@ -807,36 +817,40 @@ impl Spi {
|
||||
IoPeriphPin::new(ss_pins.0, SPI_MUX_CONF, Some(false));
|
||||
IoPeriphPin::new(ss_pins.1, SPI_MUX_CONF, Some(false));
|
||||
IoPeriphPin::new(ss_pins.2, SPI_MUX_CONF, Some(false));
|
||||
Ok(Self::new_generic_unchecked(spi_id, spi.reg_block(), config))
|
||||
}
|
||||
|
||||
/// Constructor for usage with EMIO pins.
|
||||
pub fn new_for_emio(spi: impl PsSpi, config: Config) -> Result<Self, InvalidPsSpiError> {
|
||||
let spi_id = spi.id();
|
||||
if spi_id.is_none() {
|
||||
return Err(InvalidPsSpiError);
|
||||
}
|
||||
Ok(Self::new_generic_unchecked(
|
||||
spi_id,
|
||||
spi_id.unwrap(),
|
||||
spi.reg_block(),
|
||||
clocks,
|
||||
config,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn new_generic_unchecked(
|
||||
id: SpiId,
|
||||
regs: MmioRegisters<'static>,
|
||||
clocks: &IoClocks,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
pub fn new_generic_unchecked(id: SpiId, regs: MmioRegisters<'static>, config: Config) -> Self {
|
||||
let periph_sel = match id {
|
||||
SpiId::Spi0 => crate::PeriphSelect::Spi0,
|
||||
SpiId::Spi1 => crate::PeriphSelect::Spi1,
|
||||
};
|
||||
enable_amba_peripheral_clock(periph_sel);
|
||||
let sclk = clocks.spi_clk() / config.baud_div.div_value() as u32;
|
||||
let mut spi = Self {
|
||||
inner: SpiLowLevel { regs, id },
|
||||
sclk,
|
||||
config,
|
||||
outstanding_rx: false,
|
||||
};
|
||||
spi.reset_and_reconfigure();
|
||||
spi
|
||||
}
|
||||
|
||||
pub fn write_delay_control(&mut self, delay_control: DelayControl) {
|
||||
self.inner.write_delay_control(delay_control);
|
||||
}
|
||||
|
||||
/// Re-configures the SPI peripheral with the initial configuration.
|
||||
pub fn reconfigure(&mut self) {
|
||||
self.inner.reconfigure(self.config);
|
||||
@@ -852,19 +866,13 @@ impl Spi {
|
||||
|
||||
#[inline]
|
||||
pub fn issue_manual_start_for_manual_cfg(&mut self) {
|
||||
if self.config.ss_config == SlaveSelectConfig::AutoWithManualStart
|
||||
|| self.config.ss_config == SlaveSelectConfig::ManualWithManualStart
|
||||
if self.config.ss_config == SlaveSelectConfig::AutoCsManualStart
|
||||
|| self.config.ss_config == SlaveSelectConfig::ManualCsManualStart
|
||||
{
|
||||
self.inner.issue_manual_start();
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve SCLK clock frequency currently configured for this SPI.
|
||||
#[inline]
|
||||
pub const fn sclk(&self) -> Hertz {
|
||||
self.sclk
|
||||
}
|
||||
|
||||
/// Retrieve inner low-level helper.
|
||||
#[inline]
|
||||
pub const fn inner(&mut self) -> &mut SpiLowLevel {
|
||||
@@ -876,7 +884,7 @@ impl Spi {
|
||||
&mut self.inner.regs
|
||||
}
|
||||
|
||||
fn initial_fifo_fill(&mut self, words: &[u8]) -> usize {
|
||||
fn prefill_fifo(&mut self, words: &[u8]) -> usize {
|
||||
let write_len = core::cmp::min(FIFO_DEPTH, words.len());
|
||||
(0..write_len).for_each(|idx| {
|
||||
self.inner.write_fifo_unchecked(words[idx]);
|
||||
@@ -892,7 +900,7 @@ impl Spi {
|
||||
self.inner.regs.write_rx_trig(1);
|
||||
|
||||
// Fill the FIFO with initial data.
|
||||
let written = self.initial_fifo_fill(words);
|
||||
let written = self.prefill_fifo(words);
|
||||
|
||||
// We assume that the slave select configuration was already performed, but we take
|
||||
// care of issuing a start if necessary.
|
||||
@@ -900,7 +908,7 @@ impl Spi {
|
||||
written
|
||||
}
|
||||
|
||||
fn read(&mut self, words: &mut [u8]) {
|
||||
pub fn read(&mut self, words: &mut [u8]) {
|
||||
if words.is_empty() {
|
||||
return;
|
||||
}
|
||||
@@ -935,33 +943,38 @@ impl Spi {
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, words: &[u8]) {
|
||||
pub fn write(&mut self, words: &[u8]) {
|
||||
if words.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut written = self.prepare_generic_blocking_transfer(words);
|
||||
let mut read_idx = 0;
|
||||
if words.len() > FIFO_DEPTH {
|
||||
self.inner.regs.write_tx_trig(FIFO_DEPTH as u32 / 2);
|
||||
}
|
||||
|
||||
while written < words.len() {
|
||||
loop {
|
||||
let status = self.regs().read_interrupt_status();
|
||||
let rx_pending = read_idx < words.len();
|
||||
let tx_pending = written < words.len();
|
||||
// We empty the FIFO to prevent it filling up completely, as long as we have to write
|
||||
// bytes
|
||||
if status.rx_not_empty() {
|
||||
if status.rx_not_empty() && rx_pending {
|
||||
self.inner.read_fifo_unchecked();
|
||||
read_idx += 1;
|
||||
}
|
||||
if !status.tx_full() {
|
||||
if !status.tx_full() && tx_pending {
|
||||
self.inner.write_fifo_unchecked(words[written]);
|
||||
written += 1;
|
||||
}
|
||||
if !rx_pending && !tx_pending {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// We exit once all bytes have been written, so some bytes to read might be outstanding.
|
||||
// We use the FIFO trigger mechanism to determine when we can read all the remaining bytes.
|
||||
self.regs().write_rx_trig((words.len() - read_idx) as u32);
|
||||
self.outstanding_rx = true;
|
||||
self.inner.regs.write_tx_trig(1);
|
||||
}
|
||||
|
||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) {
|
||||
pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) {
|
||||
if read.is_empty() {
|
||||
return;
|
||||
}
|
||||
@@ -997,7 +1010,7 @@ impl Spi {
|
||||
}
|
||||
}
|
||||
|
||||
fn transfer_in_place(&mut self, words: &mut [u8]) {
|
||||
pub fn transfer_in_place(&mut self, words: &mut [u8]) {
|
||||
if words.is_empty() {
|
||||
return;
|
||||
}
|
||||
@@ -1007,7 +1020,7 @@ impl Spi {
|
||||
let mut writes_finished = write_idx == words.len();
|
||||
let mut reads_finished = false;
|
||||
while !writes_finished || !reads_finished {
|
||||
let status = self.inner.read_isr();
|
||||
let status = self.inner.read_interrupt_status();
|
||||
if status.rx_not_empty() && !reads_finished {
|
||||
words[read_idx] = self.inner.read_fifo_unchecked();
|
||||
read_idx += 1;
|
||||
@@ -1024,16 +1037,16 @@ impl Spi {
|
||||
|
||||
/// Blocking flush implementation.
|
||||
fn flush(&mut self) {
|
||||
if !self.outstanding_rx {
|
||||
return;
|
||||
}
|
||||
let rx_trig = self.inner.read_rx_not_empty_threshold();
|
||||
while !self.inner.read_isr().rx_not_empty() {}
|
||||
(0..rx_trig).for_each(|_| {
|
||||
self.inner.write_tx_trig(1);
|
||||
let status = self.inner.read_interrupt_status();
|
||||
while self.inner.read_interrupt_status().rx_not_empty() {
|
||||
self.inner.read_fifo_unchecked();
|
||||
});
|
||||
self.inner.set_rx_fifo_trigger(1).unwrap();
|
||||
self.outstanding_rx = false;
|
||||
}
|
||||
while status.tx_full() {
|
||||
while self.inner.read_interrupt_status().rx_not_empty() {
|
||||
self.inner.read_fifo_unchecked();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1145,7 +1158,7 @@ pub fn reset(id: SpiId) {
|
||||
regs.reset_ctrl().write_spi(assert_reset);
|
||||
// Keep it in reset for some cycles.. The TMR just mentions some small delay,
|
||||
// no idea what is meant with that.
|
||||
for _ in 0..5 {
|
||||
for _ in 0..10 {
|
||||
aarch32_cpu::asm::nop();
|
||||
}
|
||||
regs.reset_ctrl()
|
||||
@@ -1159,22 +1172,32 @@ pub fn reset(id: SpiId) {
|
||||
/// The Zynq7000 SPI peripheral has the following requirement for the SPI reference clock:
|
||||
/// It must be larger than the CPU 1X clock. Therefore, the divisor used to calculate the reference
|
||||
/// clock has a maximum value, which can be calculated with this function.
|
||||
/// [configure_spi_ref_clk_with_divisor] can be used to configure the SPI reference clock with a
|
||||
/// divisor.
|
||||
///
|
||||
/// [configure_spi_ref_clk] can be used to configure the SPI reference clock with the calculated
|
||||
/// value.
|
||||
/// *NOTE* - It is recommended to avoid the largest theoretical value which was proven to be
|
||||
/// problematic for driving certain sensors and instead take a smaller value! Reduce the divisor
|
||||
/// calculated by this function subtracting a small value to get a functioning SPI clock.
|
||||
pub fn calculate_largest_allowed_spi_ref_clk_divisor(clks: &Clocks) -> Option<u6> {
|
||||
let slcr = unsafe { Slcr::steal() };
|
||||
let spi_clk_ctrl = slcr.regs().clk_ctrl_shared().read_spi_clk_ctrl();
|
||||
let div = match spi_clk_ctrl.srcsel() {
|
||||
zynq7000::slcr::clocks::SrcSelIo::IoPll | zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
|
||||
clks.io_clocks().ref_clk() / clks.arm_clocks().cpu_1x_clk()
|
||||
}
|
||||
zynq7000::slcr::clocks::SrcSelIo::ArmPll => {
|
||||
clks.arm_clocks().ref_clk() / clks.arm_clocks().cpu_1x_clk()
|
||||
}
|
||||
zynq7000::slcr::clocks::SrcSelIo::DdrPll => {
|
||||
clks.ddr_clocks().ref_clk() / clks.arm_clocks().cpu_1x_clk()
|
||||
clks.io_clocks()
|
||||
.ref_clk()
|
||||
.raw()
|
||||
.div_ceil(clks.arm_clocks().cpu_1x_clk().raw())
|
||||
}
|
||||
zynq7000::slcr::clocks::SrcSelIo::ArmPll => clks
|
||||
.arm_clocks()
|
||||
.ref_clk()
|
||||
.raw()
|
||||
.div_ceil(clks.arm_clocks().cpu_1x_clk().raw()),
|
||||
zynq7000::slcr::clocks::SrcSelIo::DdrPll => clks
|
||||
.ddr_clocks()
|
||||
.ref_clk()
|
||||
.raw()
|
||||
.div_ceil(clks.arm_clocks().cpu_1x_clk().raw()),
|
||||
};
|
||||
if div > u6::MAX.value() as u32 {
|
||||
return None;
|
||||
@@ -1183,7 +1206,28 @@ pub fn calculate_largest_allowed_spi_ref_clk_divisor(clks: &Clocks) -> Option<u6
|
||||
Some(u6::new(div as u8))
|
||||
}
|
||||
|
||||
pub fn configure_spi_ref_clk(clks: &mut Clocks, divisor: u6) {
|
||||
/// Configures the SPI reference clock.
|
||||
///
|
||||
/// It is strongly advised to take a clock value which is substantially higher than the CPU 1x
|
||||
/// clock. It was proven that taking values which are only slightly larger than the CPU 1x
|
||||
/// clock are problematic for driving ceratin devices.
|
||||
pub fn configure_spi_ref_clock(clks: &mut Clocks, target_clock: Hertz) {
|
||||
let slcr = unsafe { Slcr::steal() };
|
||||
let spi_clk_ctrl = slcr.regs().clk_ctrl_shared().read_spi_clk_ctrl();
|
||||
let ref_clk = match spi_clk_ctrl.srcsel() {
|
||||
zynq7000::slcr::clocks::SrcSelIo::IoPll | zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
|
||||
clks.io_clocks().ref_clk().raw()
|
||||
}
|
||||
zynq7000::slcr::clocks::SrcSelIo::ArmPll => clks.arm_clocks().ref_clk().raw(),
|
||||
zynq7000::slcr::clocks::SrcSelIo::DdrPll => clks.ddr_clocks().ref_clk().raw(),
|
||||
};
|
||||
let div = ref_clk.div_ceil(target_clock.raw());
|
||||
if div > u6::MAX.value() as u32 {
|
||||
configure_spi_ref_clock_with_divisor(clks, u6::new(div as u8));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_spi_ref_clock_with_divisor(clks: &mut Clocks, divisor: u6) {
|
||||
let mut slcr = unsafe { Slcr::steal() };
|
||||
let spi_clk_ctrl = slcr.regs().clk_ctrl_shared().read_spi_clk_ctrl();
|
||||
slcr.modify(|regs| {
|
||||
|
||||
@@ -6,7 +6,7 @@ pub use crate::{SpiClockPhase, SpiClockPolarity};
|
||||
pub const SPI_0_BASE_ADDR: usize = 0xE000_6000;
|
||||
pub const SPI_1_BASE_ADDR: usize = 0xE000_7000;
|
||||
|
||||
/// The SPI reference block will be divided by a divisor value.
|
||||
/// The SPI reference clock will be divided by a divisor value.
|
||||
#[bitbybit::bitenum(u3)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
all: check build check-fmt clippy docs-zynq
|
||||
# Common paths (single source of truth)
|
||||
INIT_SCRIPT := justfile_directory() / "scripts/zynq7000-init.py"
|
||||
GDB_CMD := justfile_directory() / "firmware/gdb.gdb"
|
||||
|
||||
all: check build check-fmt clippy docs-zynq
|
||||
check: (check-dir "firmware") (check-dir "host")
|
||||
clean: (clean-dir "firmware") (clean-dir "host")
|
||||
build: build-zynq (build-dir "host")
|
||||
@@ -52,13 +55,19 @@ bootgen:
|
||||
bootgen -arch zynq -image boot.bif -o boot.bin -w on
|
||||
echo "Generated boot.bin at zynq-boot-image/staging"
|
||||
|
||||
# Internal helper to start GDB after running init.
|
||||
# Pass init flags (if any) via `init_args`.
|
||||
[no-cd]
|
||||
run binary:
|
||||
# Run the initialization script. It needs to be run inside the justfile directory.
|
||||
python3 {{justfile_directory()}}/scripts/zynq7000-init.py
|
||||
run_generic binary init_args="":
|
||||
python3 {{INIT_SCRIPT}} {{init_args}}
|
||||
gdb-multiarch -q -x {{GDB_CMD}} {{binary}} -tui
|
||||
|
||||
# Run the GDB debugger in GUI mode.
|
||||
gdb-multiarch -q -x {{justfile_directory()}}/firmware/gdb.gdb {{binary}} -tui
|
||||
# Public targets
|
||||
[no-cd]
|
||||
run binary: (run_generic binary)
|
||||
|
||||
[no-cd]
|
||||
run-no-reset binary: (run_generic binary "-s")
|
||||
|
||||
flash-nor-zedboard boot_binary:
|
||||
cd {{justfile_directory()}}/firmware/zedboard-qspi-flasher && cargo build --release
|
||||
|
||||
+21
-18
@@ -33,10 +33,11 @@ proc ps7_clock_init_data_3_0 {} {
|
||||
mask_write 0XF800014C 0x00003F31 0x00000501
|
||||
mask_write 0XF8000150 0x00003F33 0x00001401
|
||||
mask_write 0XF8000154 0x00003F33 0x00000A03
|
||||
mask_write 0XF8000158 0x00003F33 0x00000601
|
||||
mask_write 0XF8000168 0x00003F31 0x00000501
|
||||
mask_write 0XF8000170 0x03F03F30 0x00200500
|
||||
mask_write 0XF80001C4 0x00000001 0x00000001
|
||||
mask_write 0XF800012C 0x01FFCCCD 0x01FC044D
|
||||
mask_write 0XF800012C 0x01FFCCCD 0x01FC444D
|
||||
mwr -force 0XF8000004 0x0000767B
|
||||
}
|
||||
proc ps7_ddr_init_data_3_0 {} {
|
||||
@@ -149,7 +150,7 @@ proc ps7_mio_init_data_3_0 {} {
|
||||
mask_write 0XF8000714 0x00003FFF 0x00000702
|
||||
mask_write 0XF8000718 0x00003FFF 0x00000702
|
||||
mask_write 0XF800071C 0x00003FFF 0x00000600
|
||||
mask_write 0XF8000720 0x00003FFF 0x00000702
|
||||
mask_write 0XF8000720 0x00003FFF 0x00000700
|
||||
mask_write 0XF8000724 0x00003FFF 0x00001600
|
||||
mask_write 0XF8000728 0x00003FFF 0x00001600
|
||||
mask_write 0XF800072C 0x00003FFF 0x00001600
|
||||
@@ -188,14 +189,14 @@ proc ps7_mio_init_data_3_0 {} {
|
||||
mask_write 0XF80007B0 0x00003FFF 0x00000380
|
||||
mask_write 0XF80007B4 0x00003FFF 0x00000380
|
||||
mask_write 0XF80007B8 0x00003FFF 0x00001200
|
||||
mask_write 0XF80007BC 0x00003F01 0x00000201
|
||||
mask_write 0XF80007BC 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007C0 0x00003FFF 0x000002E0
|
||||
mask_write 0XF80007C4 0x00003FFF 0x000002E1
|
||||
mask_write 0XF80007C8 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007CC 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007D0 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007D4 0x00003FFF 0x00000200
|
||||
mask_write 0XF8000830 0x003F003F 0x002F0037
|
||||
mask_write 0XF80007D0 0x00003FFF 0x00000280
|
||||
mask_write 0XF80007D4 0x00003FFF 0x00000280
|
||||
mask_write 0XF8000830 0x003F003F 0x00380037
|
||||
mwr -force 0XF8000004 0x0000767B
|
||||
}
|
||||
proc ps7_peripherals_init_data_3_0 {} {
|
||||
@@ -268,10 +269,11 @@ proc ps7_clock_init_data_2_0 {} {
|
||||
mask_write 0XF800014C 0x00003F31 0x00000501
|
||||
mask_write 0XF8000150 0x00003F33 0x00001401
|
||||
mask_write 0XF8000154 0x00003F33 0x00000A03
|
||||
mask_write 0XF8000158 0x00003F33 0x00000601
|
||||
mask_write 0XF8000168 0x00003F31 0x00000501
|
||||
mask_write 0XF8000170 0x03F03F30 0x00200500
|
||||
mask_write 0XF80001C4 0x00000001 0x00000001
|
||||
mask_write 0XF800012C 0x01FFCCCD 0x01FC044D
|
||||
mask_write 0XF800012C 0x01FFCCCD 0x01FC444D
|
||||
mwr -force 0XF8000004 0x0000767B
|
||||
}
|
||||
proc ps7_ddr_init_data_2_0 {} {
|
||||
@@ -385,7 +387,7 @@ proc ps7_mio_init_data_2_0 {} {
|
||||
mask_write 0XF8000714 0x00003FFF 0x00000702
|
||||
mask_write 0XF8000718 0x00003FFF 0x00000702
|
||||
mask_write 0XF800071C 0x00003FFF 0x00000600
|
||||
mask_write 0XF8000720 0x00003FFF 0x00000702
|
||||
mask_write 0XF8000720 0x00003FFF 0x00000700
|
||||
mask_write 0XF8000724 0x00003FFF 0x00001600
|
||||
mask_write 0XF8000728 0x00003FFF 0x00001600
|
||||
mask_write 0XF800072C 0x00003FFF 0x00001600
|
||||
@@ -424,14 +426,14 @@ proc ps7_mio_init_data_2_0 {} {
|
||||
mask_write 0XF80007B0 0x00003FFF 0x00000380
|
||||
mask_write 0XF80007B4 0x00003FFF 0x00000380
|
||||
mask_write 0XF80007B8 0x00003FFF 0x00001200
|
||||
mask_write 0XF80007BC 0x00003F01 0x00000201
|
||||
mask_write 0XF80007BC 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007C0 0x00003FFF 0x000002E0
|
||||
mask_write 0XF80007C4 0x00003FFF 0x000002E1
|
||||
mask_write 0XF80007C8 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007CC 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007D0 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007D4 0x00003FFF 0x00000200
|
||||
mask_write 0XF8000830 0x003F003F 0x002F0037
|
||||
mask_write 0XF80007D0 0x00003FFF 0x00000280
|
||||
mask_write 0XF80007D4 0x00003FFF 0x00000280
|
||||
mask_write 0XF8000830 0x003F003F 0x00380037
|
||||
mwr -force 0XF8000004 0x0000767B
|
||||
}
|
||||
proc ps7_peripherals_init_data_2_0 {} {
|
||||
@@ -504,10 +506,11 @@ proc ps7_clock_init_data_1_0 {} {
|
||||
mask_write 0XF800014C 0x00003F31 0x00000501
|
||||
mask_write 0XF8000150 0x00003F33 0x00001401
|
||||
mask_write 0XF8000154 0x00003F33 0x00000A03
|
||||
mask_write 0XF8000158 0x00003F33 0x00000601
|
||||
mask_write 0XF8000168 0x00003F31 0x00000501
|
||||
mask_write 0XF8000170 0x03F03F30 0x00200500
|
||||
mask_write 0XF80001C4 0x00000001 0x00000001
|
||||
mask_write 0XF800012C 0x01FFCCCD 0x01FC044D
|
||||
mask_write 0XF800012C 0x01FFCCCD 0x01FC444D
|
||||
mwr -force 0XF8000004 0x0000767B
|
||||
}
|
||||
proc ps7_ddr_init_data_1_0 {} {
|
||||
@@ -619,7 +622,7 @@ proc ps7_mio_init_data_1_0 {} {
|
||||
mask_write 0XF8000714 0x00003FFF 0x00000702
|
||||
mask_write 0XF8000718 0x00003FFF 0x00000702
|
||||
mask_write 0XF800071C 0x00003FFF 0x00000600
|
||||
mask_write 0XF8000720 0x00003FFF 0x00000702
|
||||
mask_write 0XF8000720 0x00003FFF 0x00000700
|
||||
mask_write 0XF8000724 0x00003FFF 0x00001600
|
||||
mask_write 0XF8000728 0x00003FFF 0x00001600
|
||||
mask_write 0XF800072C 0x00003FFF 0x00001600
|
||||
@@ -658,14 +661,14 @@ proc ps7_mio_init_data_1_0 {} {
|
||||
mask_write 0XF80007B0 0x00003FFF 0x00000380
|
||||
mask_write 0XF80007B4 0x00003FFF 0x00000380
|
||||
mask_write 0XF80007B8 0x00003FFF 0x00001200
|
||||
mask_write 0XF80007BC 0x00003F01 0x00000201
|
||||
mask_write 0XF80007BC 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007C0 0x00003FFF 0x000002E0
|
||||
mask_write 0XF80007C4 0x00003FFF 0x000002E1
|
||||
mask_write 0XF80007C8 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007CC 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007D0 0x00003FFF 0x00000200
|
||||
mask_write 0XF80007D4 0x00003FFF 0x00000200
|
||||
mask_write 0XF8000830 0x003F003F 0x002F0037
|
||||
mask_write 0XF80007D0 0x00003FFF 0x00000280
|
||||
mask_write 0XF80007D4 0x00003FFF 0x00000280
|
||||
mask_write 0XF8000830 0x003F003F 0x00380037
|
||||
mwr -force 0XF8000004 0x0000767B
|
||||
}
|
||||
proc ps7_peripherals_init_data_1_0 {} {
|
||||
|
||||
@@ -13,13 +13,15 @@ set bitstream ""
|
||||
proc usage {} {
|
||||
puts "Usage: xsct xsct-helper.tcl <init.tcl> \[-a|--app app.elf\] \[-b|--bit design.bit]"
|
||||
puts "Options:"
|
||||
puts " -a, --app Path to application ELF to download"
|
||||
puts " -b, --bit Path to FPGA bitstream (.bit) to program"
|
||||
puts " -h, --help Show this help"
|
||||
puts " -a, --app Path to application ELF to download"
|
||||
puts " -b, --bit Path to FPGA bitstream (.bit) to program"
|
||||
puts " -s, --skip-reset Skip reset"
|
||||
puts " -h, --help Show this help"
|
||||
}
|
||||
|
||||
# Compact, robust parser
|
||||
set expecting ""
|
||||
set skip_reset 0
|
||||
set endopts 0
|
||||
foreach arg $argv {
|
||||
# If previous option expects a value, take this arg
|
||||
@@ -34,6 +36,7 @@ foreach arg $argv {
|
||||
if {$arg eq "--"} { set endopts 1; continue }
|
||||
if {$arg eq "-h" || $arg eq "--help"} { usage; exit 0 }
|
||||
if {$arg eq "-a" || $arg eq "--app"} { set expecting app; continue }
|
||||
if {$arg eq "-s" || $arg eq "--skip-reset"} { set skip_reset 1; continue }
|
||||
if {$arg eq "-b" || $arg eq "--bit"} { set expecting bitstream; continue }
|
||||
puts "error: unknown option: $arg"; usage; exit 1
|
||||
}
|
||||
@@ -100,10 +103,11 @@ target $apu_device_num
|
||||
# Resetting the target involved problems when an image is stored on the flash.
|
||||
# It has turned out that it is not essential to reset the system before loading
|
||||
# the software components into the device.
|
||||
puts "Reset target"
|
||||
# TODO: Make the reset optional/configurable via input argument.
|
||||
# Reset the target
|
||||
rst
|
||||
if {!$skip_reset} {
|
||||
puts "Reset target"
|
||||
# Reset the target
|
||||
rst
|
||||
}
|
||||
|
||||
# Check if bitstream is set and the file exists before programming FPGA
|
||||
if {$bitstream eq ""} {
|
||||
|
||||
@@ -40,6 +40,12 @@ def main():
|
||||
action="store_true",
|
||||
help="No bitstream flashing for initialization with SDT.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--skip-reset",
|
||||
action="store_true",
|
||||
help="Skip device reset",
|
||||
)
|
||||
parser.add_argument("-a", "--app", dest="app", help="Path to the app to program")
|
||||
default_ip = os.getenv("HW_SERVER_IP")
|
||||
if not default_ip:
|
||||
@@ -141,6 +147,8 @@ def main():
|
||||
if args.app:
|
||||
cmd_list.append("--app")
|
||||
cmd_list.append(args.app)
|
||||
if args.skip_reset:
|
||||
cmd_list.append("-s")
|
||||
|
||||
# Join safely for shell execution
|
||||
xsct_cmd = shlex.join(cmd_list)
|
||||
|
||||
@@ -39,6 +39,12 @@ vivado zedboard-rust.xpr
|
||||
You can perform all the steps specified in the Vivado GUI as well using `Execute TCL script` and
|
||||
`Load Project`.
|
||||
|
||||
# Updating the project
|
||||
|
||||
If you add custom RTL code, you might have to edit the `zedboard-rust.tcl` project file and
|
||||
add the source files there. This file was created using the `write_project_tcl` but was
|
||||
optimized, so it might be easier to manually update the file.
|
||||
|
||||
# Generating the SDT folder from a hardware description
|
||||
|
||||
You can generate a hardware description by building the block design by using `Generate Bitstream`
|
||||
|
||||
@@ -246,6 +246,10 @@ proc create_root_design { parentCell } {
|
||||
set TTC0_WAVEOUT [ create_bd_port -dir O TTC0_WAVEOUT ]
|
||||
set OLED_SCLK [ create_bd_port -dir O OLED_SCLK ]
|
||||
set OLED_SDIN [ create_bd_port -dir O OLED_SDIN ]
|
||||
set OLED_DC [ create_bd_port -dir O -from 0 -to 0 OLED_DC ]
|
||||
set OLED_RESET [ create_bd_port -dir O -from 0 -to 0 OLED_RESET ]
|
||||
set OLED_VDD [ create_bd_port -dir O -from 0 -to 0 OLED_VDD ]
|
||||
set OLED_VBAT [ create_bd_port -dir O -from 0 -to 0 OLED_VBAT ]
|
||||
|
||||
# Create instance: processing_system7_0, and set properties
|
||||
set processing_system7_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 processing_system7_0 ]
|
||||
@@ -283,25 +287,27 @@ proc create_root_design { parentCell } {
|
||||
CONFIG.PCW_DDR_RAM_HIGHADDR {0x1FFFFFFF} \
|
||||
CONFIG.PCW_ENET0_ENET0_IO {MIO 16 .. 27} \
|
||||
CONFIG.PCW_ENET0_GRP_MDIO_ENABLE {1} \
|
||||
CONFIG.PCW_ENET0_GRP_MDIO_IO {EMIO} \
|
||||
CONFIG.PCW_ENET0_GRP_MDIO_IO {MIO 52 .. 53} \
|
||||
CONFIG.PCW_ENET0_PERIPHERAL_CLKSRC {IO PLL} \
|
||||
CONFIG.PCW_ENET0_PERIPHERAL_ENABLE {1} \
|
||||
CONFIG.PCW_ENET0_PERIPHERAL_FREQMHZ {1000 Mbps} \
|
||||
CONFIG.PCW_ENET0_RESET_ENABLE {0} \
|
||||
CONFIG.PCW_ENET_RESET_ENABLE {1} \
|
||||
CONFIG.PCW_ENET_RESET_SELECT {Share reset pin} \
|
||||
CONFIG.PCW_EN_EMIO_ENET0 {0} \
|
||||
CONFIG.PCW_EN_EMIO_GPIO {1} \
|
||||
CONFIG.PCW_EN_EMIO_SPI0 {0} \
|
||||
CONFIG.PCW_EN_EMIO_SPI1 {1} \
|
||||
CONFIG.PCW_EN_EMIO_SPI0 {1} \
|
||||
CONFIG.PCW_EN_EMIO_SPI1 {0} \
|
||||
CONFIG.PCW_EN_EMIO_TTC0 {1} \
|
||||
CONFIG.PCW_EN_EMIO_TTC1 {0} \
|
||||
CONFIG.PCW_EN_EMIO_UART0 {1} \
|
||||
CONFIG.PCW_EN_EMIO_WP_SDIO0 {1} \
|
||||
CONFIG.PCW_EN_EMIO_WP_SDIO0 {0} \
|
||||
CONFIG.PCW_EN_ENET0 {1} \
|
||||
CONFIG.PCW_EN_GPIO {1} \
|
||||
CONFIG.PCW_EN_QSPI {1} \
|
||||
CONFIG.PCW_EN_SDIO0 {1} \
|
||||
CONFIG.PCW_EN_SPI0 {0} \
|
||||
CONFIG.PCW_EN_SPI1 {1} \
|
||||
CONFIG.PCW_EN_SPI0 {1} \
|
||||
CONFIG.PCW_EN_SPI1 {0} \
|
||||
CONFIG.PCW_EN_TTC0 {1} \
|
||||
CONFIG.PCW_EN_TTC1 {0} \
|
||||
CONFIG.PCW_EN_UART0 {1} \
|
||||
@@ -472,9 +478,9 @@ proc create_root_design { parentCell } {
|
||||
CONFIG.PCW_MIO_9_PULLUP {enabled} \
|
||||
CONFIG.PCW_MIO_9_SLEW {slow} \
|
||||
CONFIG.PCW_MIO_TREE_PERIPHERALS {GPIO#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#GPIO#Quad SPI Flash#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#Enet 0#Enet 0#Enet\
|
||||
0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#SD 0#SD 0#SD 0#SD 0#SD 0#SD 0#USB Reset#SD 0#UART 1#UART 1#GPIO#GPIO#GPIO#GPIO}\
|
||||
\
|
||||
CONFIG.PCW_MIO_TREE_SIGNALS {gpio[0]#qspi0_ss_b#qspi0_io[0]#qspi0_io[1]#qspi0_io[2]#qspi0_io[3]/HOLD_B#qspi0_sclk#gpio[7]#qspi_fbclk#gpio[9]#gpio[10]#gpio[11]#gpio[12]#gpio[13]#gpio[14]#gpio[15]#tx_clk#txd[0]#txd[1]#txd[2]#txd[3]#tx_ctl#rx_clk#rxd[0]#rxd[1]#rxd[2]#rxd[3]#rx_ctl#data[4]#dir#stp#nxt#data[0]#data[1]#data[2]#data[3]#clk#data[5]#data[6]#data[7]#clk#cmd#data[0]#data[1]#data[2]#data[3]#reset#cd#tx#rx#gpio[50]#gpio[51]#gpio[52]#gpio[53]}\
|
||||
0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#SD 0#SD 0#SD 0#SD 0#SD 0#SD 0#USB Reset#GPIO#UART 1#UART 1#GPIO#GPIO#Enet\
|
||||
0#Enet 0} \
|
||||
CONFIG.PCW_MIO_TREE_SIGNALS {gpio[0]#qspi0_ss_b#qspi0_io[0]#qspi0_io[1]#qspi0_io[2]#qspi0_io[3]/HOLD_B#qspi0_sclk#gpio[7]#qspi_fbclk#gpio[9]#gpio[10]#gpio[11]#gpio[12]#gpio[13]#gpio[14]#gpio[15]#tx_clk#txd[0]#txd[1]#txd[2]#txd[3]#tx_ctl#rx_clk#rxd[0]#rxd[1]#rxd[2]#rxd[3]#rx_ctl#data[4]#dir#stp#nxt#data[0]#data[1]#data[2]#data[3]#clk#data[5]#data[6]#data[7]#clk#cmd#data[0]#data[1]#data[2]#data[3]#reset#gpio[47]#tx#rx#gpio[50]#gpio[51]#mdc#mdio}\
|
||||
\
|
||||
CONFIG.PCW_PRESET_BANK1_VOLTAGE {LVCMOS 1.8V} \
|
||||
CONFIG.PCW_QSPI_GRP_FBCLK_ENABLE {1} \
|
||||
@@ -486,19 +492,17 @@ proc create_root_design { parentCell } {
|
||||
CONFIG.PCW_QSPI_PERIPHERAL_ENABLE {1} \
|
||||
CONFIG.PCW_QSPI_PERIPHERAL_FREQMHZ {200} \
|
||||
CONFIG.PCW_QSPI_QSPI_IO {MIO 1 .. 6} \
|
||||
CONFIG.PCW_SD0_GRP_CD_ENABLE {1} \
|
||||
CONFIG.PCW_SD0_GRP_CD_IO {MIO 47} \
|
||||
CONFIG.PCW_SD0_GRP_CD_ENABLE {0} \
|
||||
CONFIG.PCW_SD0_GRP_POW_ENABLE {0} \
|
||||
CONFIG.PCW_SD0_GRP_WP_ENABLE {1} \
|
||||
CONFIG.PCW_SD0_GRP_WP_IO {EMIO} \
|
||||
CONFIG.PCW_SD0_GRP_WP_ENABLE {0} \
|
||||
CONFIG.PCW_SD0_PERIPHERAL_ENABLE {1} \
|
||||
CONFIG.PCW_SD0_SD0_IO {MIO 40 .. 45} \
|
||||
CONFIG.PCW_SDIO_PERIPHERAL_FREQMHZ {50} \
|
||||
CONFIG.PCW_SDIO_PERIPHERAL_VALID {1} \
|
||||
CONFIG.PCW_SINGLE_QSPI_DATA_MODE {x4} \
|
||||
CONFIG.PCW_SPI0_PERIPHERAL_ENABLE {0} \
|
||||
CONFIG.PCW_SPI1_PERIPHERAL_ENABLE {1} \
|
||||
CONFIG.PCW_SPI1_SPI1_IO {EMIO} \
|
||||
CONFIG.PCW_SPI0_PERIPHERAL_ENABLE {1} \
|
||||
CONFIG.PCW_SPI0_SPI0_IO {EMIO} \
|
||||
CONFIG.PCW_SPI1_PERIPHERAL_ENABLE {0} \
|
||||
CONFIG.PCW_SPI_PERIPHERAL_FREQMHZ {166.666666} \
|
||||
CONFIG.PCW_SPI_PERIPHERAL_VALID {1} \
|
||||
CONFIG.PCW_TTC0_PERIPHERAL_ENABLE {1} \
|
||||
@@ -654,6 +658,50 @@ proc create_root_design { parentCell } {
|
||||
# Create instance: IRQ_F2P, and set properties
|
||||
set IRQ_F2P [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilconcat:1.0 IRQ_F2P ]
|
||||
|
||||
# Create instance: OLED_DC, and set properties
|
||||
set OLED_DC [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 OLED_DC ]
|
||||
set_property -dict [list \
|
||||
CONFIG.DIN_FROM {11} \
|
||||
CONFIG.DIN_TO {11} \
|
||||
CONFIG.DIN_WIDTH {16} \
|
||||
] $OLED_DC
|
||||
|
||||
|
||||
# Create instance: OLED_RESET, and set properties
|
||||
set OLED_RESET [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 OLED_RESET ]
|
||||
set_property -dict [list \
|
||||
CONFIG.DIN_FROM {12} \
|
||||
CONFIG.DIN_TO {12} \
|
||||
CONFIG.DIN_WIDTH {16} \
|
||||
] $OLED_RESET
|
||||
|
||||
|
||||
# Create instance: OLED_VDD, and set properties
|
||||
set OLED_VDD [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 OLED_VDD ]
|
||||
set_property -dict [list \
|
||||
CONFIG.DIN_FROM {13} \
|
||||
CONFIG.DIN_TO {13} \
|
||||
CONFIG.DIN_WIDTH {16} \
|
||||
] $OLED_VDD
|
||||
|
||||
|
||||
# Create instance: OLED_VBAT, and set properties
|
||||
set OLED_VBAT [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 OLED_VBAT ]
|
||||
set_property -dict [list \
|
||||
CONFIG.DIN_FROM {14} \
|
||||
CONFIG.DIN_TO {14} \
|
||||
CONFIG.DIN_WIDTH {16} \
|
||||
] $OLED_VBAT
|
||||
|
||||
|
||||
# Create instance: ilconstant_2, and set properties
|
||||
set ilconstant_2 [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilconstant:1.0 ilconstant_2 ]
|
||||
|
||||
# Create instance: ilconstant_3, and set properties
|
||||
set ilconstant_3 [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilconstant:1.0 ilconstant_3 ]
|
||||
set_property CONFIG.CONST_VAL {0} $ilconstant_3
|
||||
|
||||
|
||||
# Create interface connections
|
||||
connect_bd_intf_net -intf_net processing_system7_0_DDR [get_bd_intf_ports DDR] [get_bd_intf_pins processing_system7_0/DDR]
|
||||
connect_bd_intf_net -intf_net processing_system7_0_FIXED_IO [get_bd_intf_ports FIXED_IO] [get_bd_intf_pins processing_system7_0/FIXED_IO]
|
||||
@@ -668,6 +716,12 @@ proc create_root_design { parentCell } {
|
||||
[get_bd_pins EMIO_I/In3]
|
||||
connect_bd_net -net EMIO_O_1_Dout [get_bd_pins EMIO_O_1/Dout] \
|
||||
[get_bd_pins EMIO_I/In2]
|
||||
connect_bd_net -net OLED_RESET_Dout [get_bd_pins OLED_RESET/Dout] \
|
||||
[get_bd_ports OLED_RESET]
|
||||
connect_bd_net -net OLED_VBAT_Dout [get_bd_pins OLED_VBAT/Dout] \
|
||||
[get_bd_ports OLED_VBAT]
|
||||
connect_bd_net -net OLED_VDD_Dout [get_bd_pins OLED_VDD/Dout] \
|
||||
[get_bd_ports OLED_VDD]
|
||||
connect_bd_net -net SWITCHES_1 [get_bd_ports SWITCHES] \
|
||||
[get_bd_pins EMIO_I_0/In0]
|
||||
connect_bd_net -net axi_uart16550_0_ip2intc_irpt [get_bd_pins axi_uart16550_0/ip2intc_irpt] \
|
||||
@@ -686,10 +740,18 @@ proc create_root_design { parentCell } {
|
||||
[get_bd_pins processing_system7_0/IRQ_F2P]
|
||||
connect_bd_net -net ilconstant_0_dout [get_bd_pins ilconstant_0/dout] \
|
||||
[get_bd_pins EMIO_I_0/In2]
|
||||
connect_bd_net -net ilconstant_2_dout [get_bd_pins ilconstant_2/dout] \
|
||||
[get_bd_pins processing_system7_0/SPI0_MISO_I] \
|
||||
[get_bd_pins processing_system7_0/SPI0_SS_I]
|
||||
connect_bd_net -net ilconstant_3_dout [get_bd_pins ilconstant_3/dout] \
|
||||
[get_bd_pins processing_system7_0/SPI0_MOSI_I] \
|
||||
[get_bd_pins processing_system7_0/SPI0_SCLK_I]
|
||||
connect_bd_net -net ilslice_0_Dout [get_bd_pins UART_MUX/Dout] \
|
||||
[get_bd_pins uart_mux_0/sel]
|
||||
connect_bd_net -net ilslice_0_Dout1 [get_bd_pins LEDS/Dout] \
|
||||
[get_bd_ports LEDS]
|
||||
connect_bd_net -net ilslice_0_Dout2 [get_bd_pins OLED_DC/Dout] \
|
||||
[get_bd_ports OLED_DC]
|
||||
connect_bd_net -net processing_system7_0_FCLK_CLK0 [get_bd_pins processing_system7_0/FCLK_CLK0] \
|
||||
[get_bd_pins processing_system7_0/M_AXI_GP0_ACLK] \
|
||||
[get_bd_pins rst_ps7_0_100M/slowest_sync_clk] \
|
||||
@@ -702,9 +764,9 @@ proc create_root_design { parentCell } {
|
||||
connect_bd_net -net processing_system7_0_GPIO_O [get_bd_pins processing_system7_0/GPIO_O] \
|
||||
[get_bd_pins EMIO_O_0/Din] \
|
||||
[get_bd_pins EMIO_O_1/Din]
|
||||
connect_bd_net -net processing_system7_0_SPI1_MOSI_O [get_bd_pins processing_system7_0/SPI1_MOSI_O] \
|
||||
connect_bd_net -net processing_system7_0_SPI0_MOSI_O [get_bd_pins processing_system7_0/SPI0_MOSI_O] \
|
||||
[get_bd_ports OLED_SDIN]
|
||||
connect_bd_net -net processing_system7_0_SPI1_SCLK_O [get_bd_pins processing_system7_0/SPI1_SCLK_O] \
|
||||
connect_bd_net -net processing_system7_0_SPI0_SCLK_O [get_bd_pins processing_system7_0/SPI0_SCLK_O] \
|
||||
[get_bd_ports OLED_SCLK]
|
||||
connect_bd_net -net processing_system7_0_TTC0_WAVE0_OUT [get_bd_pins processing_system7_0/TTC0_WAVE0_OUT] \
|
||||
[get_bd_ports TTC0_WAVEOUT]
|
||||
@@ -732,7 +794,11 @@ proc create_root_design { parentCell } {
|
||||
connect_bd_net -net xlslice_1_Dout [get_bd_pins EMIO_O_0/Dout] \
|
||||
[get_bd_pins EMIO_I/In0] \
|
||||
[get_bd_pins UART_MUX/Din] \
|
||||
[get_bd_pins LEDS/Din]
|
||||
[get_bd_pins LEDS/Din] \
|
||||
[get_bd_pins OLED_DC/Din] \
|
||||
[get_bd_pins OLED_RESET/Din] \
|
||||
[get_bd_pins OLED_VDD/Din] \
|
||||
[get_bd_pins OLED_VBAT/Din]
|
||||
|
||||
# Create address segments
|
||||
assign_bd_address -offset 0x43C00000 -range 0x00010000 -target_address_space [get_bd_addr_spaces processing_system7_0/Data] [get_bd_addr_segs axi_uart16550_0/S_AXI/Reg] -force
|
||||
|
||||
@@ -72,11 +72,44 @@ set_property IOSTANDARD LVCMOS33 [get_ports UART_txd]
|
||||
|
||||
# OLED SPI
|
||||
set_property PACKAGE_PIN AB12 [get_ports OLED_SCLK]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports OLED_SCLK]
|
||||
set_property PACKAGE_PIN AA12 [get_ports OLED_SDIN]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports OLED_SDIN]
|
||||
set_property PULLUP TRUE [get_ports OLED_SDIN]
|
||||
set_property PACKAGE_PIN U9 [get_ports OLED_RESET]
|
||||
set_property PULLUP TRUE [get_ports OLED_RESET]
|
||||
set_property PACKAGE_PIN U10 [get_ports OLED_DC]
|
||||
set_property PACKAGE_PIN U12 [get_ports OLED_VDD]
|
||||
set_property PACKAGE_PIN U11 [get_ports OLED_VBAT]
|
||||
|
||||
# TTC0 Wave Out
|
||||
set_property PACKAGE_PIN W12 [get_ports {TTC0_WAVEOUT}]
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports {TTC0_WAVEOUT}]
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# IOSTANDARD Constraints
|
||||
#
|
||||
# Note that these IOSTANDARD constraints are applied to all IOs currently
|
||||
# assigned within an I/O bank. If these IOSTANDARD constraints are
|
||||
# evaluated prior to other PACKAGE_PIN constraints being applied, then
|
||||
# the IOSTANDARD specified will likely not be applied properly to those
|
||||
# pins. Therefore, bank wide IOSTANDARD constraints should be placed
|
||||
# within the XDC file in a location that is evaluated AFTER all
|
||||
# PACKAGE_PIN constraints within the target bank have been evaluated.
|
||||
#
|
||||
# Un-comment one or more of the following IOSTANDARD constraints according to
|
||||
# the bank pin assignments that are required within a design.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# Note that the bank voltage for IO Bank 33 is fixed to 3.3V on ZedBoard.
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports -of_objects [get_iobanks 33]];
|
||||
|
||||
# Set the bank voltage for IO Bank 34 to 1.8V by default.
|
||||
# set_property IOSTANDARD LVCMOS33 [get_ports -of_objects [get_iobanks 34]];
|
||||
# set_property IOSTANDARD LVCMOS25 [get_ports -of_objects [get_iobanks 34]];
|
||||
set_property IOSTANDARD LVCMOS18 [get_ports -of_objects [get_iobanks 34]];
|
||||
|
||||
# Set the bank voltage for IO Bank 35 to 1.8V by default.
|
||||
# set_property IOSTANDARD LVCMOS33 [get_ports -of_objects [get_iobanks 35]];
|
||||
# set_property IOSTANDARD LVCMOS25 [get_ports -of_objects [get_iobanks 35]];
|
||||
set_property IOSTANDARD LVCMOS18 [get_ports -of_objects [get_iobanks 35]];
|
||||
|
||||
# Note that the bank voltage for IO Bank 13 is fixed to 3.3V on ZedBoard.
|
||||
set_property IOSTANDARD LVCMOS33 [get_ports -of_objects [get_iobanks 13]];
|
||||
|
||||
Reference in New Issue
Block a user