arty_a7: add divided clocks as available input peripherals so you're not stuck with 100MHz

This commit is contained in:
Jacob Lifshay 2025-10-22 20:09:41 -07:00
parent 4d9e8d3b47
commit 26840daf13
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
2 changed files with 86 additions and 23 deletions

View file

@ -3,7 +3,7 @@
use crate::{ use crate::{
intern::{Intern, Interned}, intern::{Intern, Interned},
module::{instance_with_loc, wire_with_loc}, module::{instance_with_loc, reg_builder_with_loc, wire_with_loc},
platform::{ platform::{
DynPlatform, Peripheral, PeripheralRef, Peripherals, PeripheralsBuilderFactory, DynPlatform, Peripheral, PeripheralRef, Peripherals, PeripheralsBuilderFactory,
PeripheralsBuilderFinished, Platform, PlatformAspectSet, PeripheralsBuilderFinished, Platform, PlatformAspectSet,
@ -12,7 +12,7 @@ use crate::{
prelude::*, prelude::*,
vendor::xilinx::{ vendor::xilinx::{
Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation,
primitives::{self, BUFGCE, STARTUPE2_default_inputs}, primitives,
}, },
}; };
use ordered_float::NotNan; use ordered_float::NotNan;
@ -66,7 +66,7 @@ arty_a7_platform! {
#[derive(Debug)] #[derive(Debug)]
pub struct ArtyA7Peripherals { pub struct ArtyA7Peripherals {
clk100: Peripheral<ClockInput>, clk100_div_pow2: [Peripheral<ClockInput>; 4],
rst: Peripheral<Reset>, rst: Peripheral<Reset>,
rst_sync: Peripheral<SyncReset>, rst_sync: Peripheral<SyncReset>,
ld0: Peripheral<RgbLed>, ld0: Peripheral<RgbLed>,
@ -83,7 +83,7 @@ pub struct ArtyA7Peripherals {
impl Peripherals for ArtyA7Peripherals { impl Peripherals for ArtyA7Peripherals {
fn append_peripherals<'a>(&'a self, peripherals: &mut Vec<PeripheralRef<'a, CanonicalType>>) { fn append_peripherals<'a>(&'a self, peripherals: &mut Vec<PeripheralRef<'a, CanonicalType>>) {
let Self { let Self {
clk100, clk100_div_pow2,
rst, rst,
rst_sync, rst_sync,
ld0, ld0,
@ -95,7 +95,7 @@ impl Peripherals for ArtyA7Peripherals {
ld6, ld6,
ld7, ld7,
} = self; } = self;
clk100.append_peripherals(peripherals); clk100_div_pow2.append_peripherals(peripherals);
rst.append_peripherals(peripherals); rst.append_peripherals(peripherals);
rst_sync.append_peripherals(peripherals); rst_sync.append_peripherals(peripherals);
ld0.append_peripherals(peripherals); ld0.append_peripherals(peripherals);
@ -168,9 +168,20 @@ impl Platform for ArtyA7Platform {
builder_factory: PeripheralsBuilderFactory<'builder>, builder_factory: PeripheralsBuilderFactory<'builder>,
) -> (Self::Peripherals, PeripheralsBuilderFinished<'builder>) { ) -> (Self::Peripherals, PeripheralsBuilderFinished<'builder>) {
let mut builder = builder_factory.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 { ArtyA7Peripherals {
clk100: builder.input_peripheral("clk100", ClockInput::new(100e6)), clk100_div_pow2,
rst: builder.input_peripheral("rst", Reset), rst: builder.input_peripheral("rst", Reset),
rst_sync: builder.input_peripheral("rst_sync", SyncReset), rst_sync: builder.input_peripheral("rst_sync", SyncReset),
ld0: builder.output_peripheral("ld0", RgbLed), 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) { fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) {
let ArtyA7Peripherals { let ArtyA7Peripherals {
clk100, clk100_div_pow2,
rst, rst,
rst_sync, rst_sync,
ld0, ld0,
@ -249,30 +260,82 @@ impl Platform for ArtyA7Platform {
connect(buf.T, false); connect(buf.T, false);
buf.I buf.I
}; };
let clock_annotation = XdcCreateClockAnnotation { let mut frequency = clk100_div_pow2[0].ty().frequency();
period: NotNan::new(1e9 / clk100.ty().frequency()).expect("known to be valid"), 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 clk100_buf = make_buffered_input("clk100", "E3", "LVCMOS33", false);
let startup = instance_with_loc( let startup = instance_with_loc(
"startup", "startup",
STARTUPE2_default_inputs(), primitives::STARTUPE2_default_inputs(),
SourceLocation::builtin(), SourceLocation::builtin(),
); );
let clk100_sync = instance_with_loc("clk100_sync", BUFGCE(), SourceLocation::builtin()); let clk_global_buf = instance_with_loc(
connect(clk100_sync.CE, startup.EOS); "clk_global_buf",
connect(clk100_sync.I, clk100_buf); primitives::BUFGCE(),
let clk100_out = wire_with_loc("clk100_out", SourceLocation::builtin(), Clock); SourceLocation::builtin(),
connect(clk100_out, clk100_sync.O); );
annotate(clk100_out, clock_annotation); connect(clk_global_buf.CE, startup.EOS);
annotate(clk100_out, DontTouchAnnotation); let mut clk_global_buf_in = clk100_buf.to_clock();
if let Some(clk100) = clk100.into_used() { for prev_log2_divisor in 0..log2_divisor {
connect(clk100.instance_io_field().clk, clk100_out); 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_value = {
let rst_buf = make_buffered_input("rst", "C2", "LVCMOS33", true); let rst_buf = make_buffered_input("rst", "C2", "LVCMOS33", true);
let rst_sync = instance_with_loc("rst_sync", reset_sync(), SourceLocation::builtin()); let rst_sync = instance_with_loc("rst_sync", reset_sync(), SourceLocation::builtin());
connect(rst_sync.clk, clk100_sync.O); connect(rst_sync.clk, clk_out);
connect(rst_sync.inp, rst_buf); connect(rst_sync.inp, rst_buf | !startup.EOS);
rst_sync.out rst_sync.out
}; };
if let Some(rst) = rst.into_used() { if let Some(rst) = rst.into_used() {

View file

@ -33,7 +33,7 @@ pub fn BUFGCE() {
#[hdl] #[hdl]
let CE: Bool = m.input(); let CE: Bool = m.input();
#[hdl] #[hdl]
let I: Bool = m.input(); let I: Clock = m.input();
} }
#[hdl_module(extern)] #[hdl_module(extern)]