diff --git a/crates/fayalite/src/vendor/xilinx/arty_a7.rs b/crates/fayalite/src/vendor/xilinx/arty_a7.rs index beeee0a..549e631 100644 --- a/crates/fayalite/src/vendor/xilinx/arty_a7.rs +++ b/crates/fayalite/src/vendor/xilinx/arty_a7.rs @@ -3,7 +3,7 @@ use crate::{ intern::{Intern, Interned}, - module::{instance_with_loc, wire_with_loc}, + module::{instance_with_loc, reg_builder_with_loc, wire_with_loc}, platform::{ DynPlatform, Peripheral, PeripheralRef, Peripherals, PeripheralsBuilderFactory, PeripheralsBuilderFinished, Platform, PlatformAspectSet, @@ -12,7 +12,7 @@ use crate::{ prelude::*, vendor::xilinx::{ Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, - primitives::{self, BUFGCE, STARTUPE2_default_inputs}, + primitives, }, }; use ordered_float::NotNan; @@ -66,7 +66,7 @@ arty_a7_platform! { #[derive(Debug)] pub struct ArtyA7Peripherals { - clk100: Peripheral, + clk100_div_pow2: [Peripheral; 4], rst: Peripheral, rst_sync: Peripheral, ld0: Peripheral, @@ -83,7 +83,7 @@ pub struct ArtyA7Peripherals { impl Peripherals for ArtyA7Peripherals { fn append_peripherals<'a>(&'a self, peripherals: &mut Vec>) { let Self { - clk100, + clk100_div_pow2, rst, rst_sync, ld0, @@ -95,7 +95,7 @@ impl Peripherals for ArtyA7Peripherals { ld6, ld7, } = self; - clk100.append_peripherals(peripherals); + clk100_div_pow2.append_peripherals(peripherals); rst.append_peripherals(peripherals); rst_sync.append_peripherals(peripherals); ld0.append_peripherals(peripherals); @@ -168,9 +168,20 @@ impl Platform for ArtyA7Platform { builder_factory: PeripheralsBuilderFactory<'builder>, ) -> (Self::Peripherals, PeripheralsBuilderFinished<'builder>) { let mut builder = builder_factory.builder(); + + let clk100_div_pow2 = std::array::from_fn(|log2_divisor| { + let divisor = 1u64 << log2_divisor; + let name = if divisor != 1 { + format!("clk100_div_{divisor}") + } else { + "clk100".into() + }; + builder.input_peripheral(name, ClockInput::new(100e6 / divisor as f64)) + }); + builder.add_conflicts(Vec::from_iter(clk100_div_pow2.iter().map(|v| v.id()))); ( ArtyA7Peripherals { - clk100: builder.input_peripheral("clk100", ClockInput::new(100e6)), + clk100_div_pow2, rst: builder.input_peripheral("rst", Reset), rst_sync: builder.input_peripheral("rst_sync", SyncReset), ld0: builder.output_peripheral("ld0", RgbLed), @@ -192,7 +203,7 @@ impl Platform for ArtyA7Platform { fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) { let ArtyA7Peripherals { - clk100, + clk100_div_pow2, rst, rst_sync, ld0, @@ -249,30 +260,82 @@ impl Platform for ArtyA7Platform { connect(buf.T, false); buf.I }; - let clock_annotation = XdcCreateClockAnnotation { - period: NotNan::new(1e9 / clk100.ty().frequency()).expect("known to be valid"), - }; + let mut frequency = clk100_div_pow2[0].ty().frequency(); + let mut log2_divisor = 0; + let mut clk = None; + for (cur_log2_divisor, p) in clk100_div_pow2.into_iter().enumerate() { + let Some(p) = p.into_used() else { + continue; + }; + debug_assert!( + clk.is_none(), + "conflict-handling logic should ensure at most one clock is used", + ); + frequency = p.ty().frequency(); + clk = Some(p); + log2_divisor = cur_log2_divisor; + } let clk100_buf = make_buffered_input("clk100", "E3", "LVCMOS33", false); let startup = instance_with_loc( "startup", - STARTUPE2_default_inputs(), + primitives::STARTUPE2_default_inputs(), SourceLocation::builtin(), ); - let clk100_sync = instance_with_loc("clk100_sync", BUFGCE(), SourceLocation::builtin()); - connect(clk100_sync.CE, startup.EOS); - connect(clk100_sync.I, clk100_buf); - let clk100_out = wire_with_loc("clk100_out", SourceLocation::builtin(), Clock); - connect(clk100_out, clk100_sync.O); - annotate(clk100_out, clock_annotation); - annotate(clk100_out, DontTouchAnnotation); - if let Some(clk100) = clk100.into_used() { - connect(clk100.instance_io_field().clk, clk100_out); + let clk_global_buf = instance_with_loc( + "clk_global_buf", + primitives::BUFGCE(), + SourceLocation::builtin(), + ); + connect(clk_global_buf.CE, startup.EOS); + let mut clk_global_buf_in = clk100_buf.to_clock(); + for prev_log2_divisor in 0..log2_divisor { + let prev_divisor = 1u64 << prev_log2_divisor; + let clk_in = wire_with_loc( + &format!("clk_div_{prev_divisor}"), + SourceLocation::builtin(), + Clock, + ); + connect(clk_in, clk_global_buf_in); + annotate( + clk_in, + XdcCreateClockAnnotation { + period: NotNan::new(1e9 / (100e6 / prev_divisor as f64)) + .expect("known to be valid"), + }, + ); + annotate(clk_in, DontTouchAnnotation); + let cd = wire_with_loc( + &format!("clk_div_{prev_divisor}_in"), + SourceLocation::builtin(), + ClockDomain[AsyncReset], + ); + connect(cd.clk, clk_in); + connect(cd.rst, (!startup.EOS).to_async_reset()); + let divider = reg_builder_with_loc("divider", SourceLocation::builtin()) + .clock_domain(cd) + .reset(false) + .build(); + connect(divider, !divider); + clk_global_buf_in = divider.to_clock(); + } + connect(clk_global_buf.I, clk_global_buf_in); + let clk_out = wire_with_loc("clk_out", SourceLocation::builtin(), Clock); + connect(clk_out, clk_global_buf.O); + annotate( + clk_out, + XdcCreateClockAnnotation { + period: NotNan::new(1e9 / frequency).expect("known to be valid"), + }, + ); + annotate(clk_out, DontTouchAnnotation); + if let Some(clk) = clk { + connect(clk.instance_io_field().clk, clk_out); } let rst_value = { let rst_buf = make_buffered_input("rst", "C2", "LVCMOS33", true); let rst_sync = instance_with_loc("rst_sync", reset_sync(), SourceLocation::builtin()); - connect(rst_sync.clk, clk100_sync.O); - connect(rst_sync.inp, rst_buf); + connect(rst_sync.clk, clk_out); + connect(rst_sync.inp, rst_buf | !startup.EOS); rst_sync.out }; if let Some(rst) = rst.into_used() { diff --git a/crates/fayalite/src/vendor/xilinx/primitives.rs b/crates/fayalite/src/vendor/xilinx/primitives.rs index 5dc2567..9e22d26 100644 --- a/crates/fayalite/src/vendor/xilinx/primitives.rs +++ b/crates/fayalite/src/vendor/xilinx/primitives.rs @@ -33,7 +33,7 @@ pub fn BUFGCE() { #[hdl] let CE: Bool = m.input(); #[hdl] - let I: Bool = m.input(); + let I: Clock = m.input(); } #[hdl_module(extern)]