arty_a7: add divided clocks as available input peripherals so you're not stuck with 100MHz
This commit is contained in:
parent
4d9e8d3b47
commit
26840daf13
2 changed files with 86 additions and 23 deletions
105
crates/fayalite/src/vendor/xilinx/arty_a7.rs
vendored
105
crates/fayalite/src/vendor/xilinx/arty_a7.rs
vendored
|
@ -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() {
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue