add transmit-only UART example #41
6 changed files with 329 additions and 24 deletions
|
|
@ -22,3 +22,4 @@ jobs:
|
||||||
- run: cargo doc --features=unstable-doc
|
- run: cargo doc --features=unstable-doc
|
||||||
- run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher
|
- run: FAYALITE_TEST_HASHER=always_zero cargo test --test=module --features=unstable-doc,unstable-test-hasher
|
||||||
- run: cargo run --example blinky yosys-nextpnr-xray --platform=arty-a7-100t --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db -o target/blinky-out
|
- run: cargo run --example blinky yosys-nextpnr-xray --platform=arty-a7-100t --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db -o target/blinky-out
|
||||||
|
- run: cargo run --example tx_only_uart yosys-nextpnr-xray --platform=arty-a7-100t --nextpnr-xilinx-chipdb-dir /opt/fayalite-deps/nextpnr-xilinx/xilinx --prjxray-db-dir /opt/fayalite-deps/prjxray-db -o target/tx_only_uart-out
|
||||||
|
|
|
||||||
30
README.md
30
README.md
|
|
@ -45,6 +45,36 @@ To program the Flash also, so it stays programmed when power-cycling the board:
|
||||||
sudo openFPGALoader --board arty_a7_100t -f target/blinky-out/blinky.bit
|
sudo openFPGALoader --board arty_a7_100t -f target/blinky-out/blinky.bit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Building the [Transmit-only UART example] for the Arty A7 100T on Linux
|
||||||
|
|
||||||
|
[Transmit-only UART example]: crates/fayalite/examples/tx_only_uart.rs
|
||||||
|
|
||||||
|
Follow the steps above of building the Blinky example, but replace `blinky` with `tx_only_uart`.
|
||||||
|
|
||||||
|
View the output using [tio](https://github.com/tio/tio) which you can install in Debian using `apt`.
|
||||||
|
|
||||||
|
Find the correct USB device:
|
||||||
|
```bash
|
||||||
|
sudo tio --list
|
||||||
|
```
|
||||||
|
|
||||||
|
You want the device with a name like (note the `if01`, `if00` is presumably the JTAG port):
|
||||||
|
`/dev/serial/by-id/usb-Digilent_Digilent_USB_Device_210319B4A51E-if01-port0`
|
||||||
|
|
||||||
|
Connect to the serial port:
|
||||||
|
```bash
|
||||||
|
sudo tio -b115200 /dev/serial/by-id/put-your-device-id-here
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll see (repeating endlessly):
|
||||||
|
```text
|
||||||
|
Hello World from Fayalite!!!
|
||||||
|
Hello World from Fayalite!!!
|
||||||
|
Hello World from Fayalite!!!
|
||||||
|
```
|
||||||
|
|
||||||
|
Press Ctrl+T then `q` to exit tio.
|
||||||
|
|
||||||
# Funding
|
# Funding
|
||||||
|
|
||||||
## NLnet Grants
|
## NLnet Grants
|
||||||
|
|
|
||||||
188
crates/fayalite/examples/tx_only_uart.rs
Normal file
188
crates/fayalite/examples/tx_only_uart.rs
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
use clap::builder::TypedValueParser;
|
||||||
|
use fayalite::{
|
||||||
|
build::{ToArgs, WriteArgs},
|
||||||
|
platform::PeripheralRef,
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
|
fn pick_clock<'a>(
|
||||||
|
platform_io_builder: &PlatformIOBuilder<'a>,
|
||||||
|
) -> PeripheralRef<'a, peripherals::ClockInput> {
|
||||||
|
let mut clks = platform_io_builder.peripherals_with_type::<peripherals::ClockInput>();
|
||||||
|
clks.sort_by_key(|clk| {
|
||||||
|
// sort clocks by preference, smaller return values means higher preference
|
||||||
|
let mut frequency = clk.ty().frequency();
|
||||||
|
let priority;
|
||||||
|
if frequency < 10e6 {
|
||||||
|
frequency = -frequency; // prefer bigger frequencies
|
||||||
|
priority = 1;
|
||||||
|
} else if frequency > 50e6 {
|
||||||
|
// prefer smaller frequencies
|
||||||
|
priority = 2; // least preferred
|
||||||
|
} else {
|
||||||
|
priority = 0; // most preferred
|
||||||
|
frequency = (frequency - 25e6).abs(); // prefer closer to 25MHz
|
||||||
|
}
|
||||||
|
(priority, NotNan::new(frequency).expect("should be valid"))
|
||||||
|
});
|
||||||
|
clks[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl_module]
|
||||||
|
fn tx_only_uart(
|
||||||
|
platform_io_builder: PlatformIOBuilder<'_>,
|
||||||
|
divisor: f64,
|
||||||
|
message: impl AsRef<[u8]>,
|
||||||
|
) {
|
||||||
|
let message = message.as_ref();
|
||||||
|
let clk_input = pick_clock(&platform_io_builder).use_peripheral();
|
||||||
|
let rst = platform_io_builder.peripherals_with_type::<Reset>()[0].use_peripheral();
|
||||||
|
let cd = #[hdl]
|
||||||
|
ClockDomain {
|
||||||
|
clk: clk_input.clk,
|
||||||
|
rst,
|
||||||
|
};
|
||||||
|
let numerator = 1u128 << 16;
|
||||||
|
let denominator = (divisor * numerator as f64).round() as u128;
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let remainder_reg: UInt<128> = reg_builder().clock_domain(cd).reset(0u128);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let sum: UInt<128> = wire();
|
||||||
|
connect_any(sum, remainder_reg + numerator);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let tick_reg = reg_builder().clock_domain(cd).reset(false);
|
||||||
|
connect(tick_reg, false);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let next_remainder: UInt<128> = wire();
|
||||||
|
connect(remainder_reg, next_remainder);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
if sum.cmp_ge(denominator) {
|
||||||
|
connect_any(next_remainder, sum - denominator);
|
||||||
|
connect(tick_reg, true);
|
||||||
|
} else {
|
||||||
|
connect(next_remainder, sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let uart_state_reg = reg_builder().clock_domain(cd).reset(0_hdl_u4);
|
||||||
|
#[hdl]
|
||||||
|
let next_uart_state: UInt<4> = wire();
|
||||||
|
|
||||||
|
connect_any(next_uart_state, uart_state_reg + 1u8);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let message_mem: Array<UInt<8>> = wire(Array[UInt::new_static()][message.len()]);
|
||||||
|
for (message, message_mem) in message.iter().zip(message_mem) {
|
||||||
|
connect(message_mem, *message);
|
||||||
|
}
|
||||||
|
#[hdl]
|
||||||
|
let addr_reg: UInt<32> = reg_builder().clock_domain(cd).reset(0u32);
|
||||||
|
#[hdl]
|
||||||
|
let next_addr: UInt<32> = wire();
|
||||||
|
connect(next_addr, addr_reg);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let tx = reg_builder().clock_domain(cd).reset(true);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let tx_bits: Array<Bool, 10> = wire();
|
||||||
|
|
||||||
|
connect(tx_bits[0], false); // start bit
|
||||||
|
connect(tx_bits[9], true); // stop bit
|
||||||
|
|
||||||
|
for i in 0..8 {
|
||||||
|
connect(tx_bits[i + 1], message_mem[addr_reg][i]); // data bits
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(tx, tx_bits[uart_state_reg]);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
if uart_state_reg.cmp_eq(Expr::ty(tx_bits).len() - 1) {
|
||||||
|
connect(next_uart_state, 0_hdl_u4);
|
||||||
|
let next_addr_val = addr_reg + 1u8;
|
||||||
|
#[hdl]
|
||||||
|
if next_addr_val.cmp_lt(message.len()) {
|
||||||
|
connect_any(next_addr, next_addr_val);
|
||||||
|
} else {
|
||||||
|
connect(next_addr, 0u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
if tick_reg {
|
||||||
|
connect(uart_state_reg, next_uart_state);
|
||||||
|
connect(addr_reg, next_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
for uart in platform_io_builder.peripherals_with_type::<peripherals::Uart>() {
|
||||||
|
connect(uart.use_peripheral().tx, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let io = m.add_platform_io(platform_io_builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_baud_rate(
|
||||||
|
v: impl AsRef<str>,
|
||||||
|
) -> Result<NotNan<f64>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let retval: NotNan<f64> = v
|
||||||
|
.as_ref()
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| "invalid baud rate, must be a finite positive floating-point value")?;
|
||||||
|
if *retval > 0.0 && retval.is_finite() {
|
||||||
|
Ok(retval)
|
||||||
|
} else {
|
||||||
|
Err("baud rate must be finite and positive".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
|
||||||
|
pub struct ExtraArgs {
|
||||||
|
#[arg(long, value_parser = clap::builder::StringValueParser::new().try_map(parse_baud_rate), default_value = "115200")]
|
||||||
|
pub baud_rate: NotNan<f64>,
|
||||||
|
#[arg(long, default_value = "Hello World from Fayalite!!!\r\n", value_parser = clap::builder::NonEmptyStringValueParser::new())]
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToArgs for ExtraArgs {
|
||||||
|
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
|
||||||
|
let Self { baud_rate, message } = self;
|
||||||
|
args.write_display_arg(format_args!("--baud-rate={baud_rate}"));
|
||||||
|
args.write_long_option_eq("message", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
type Cli = BuildCli<ExtraArgs>;
|
||||||
|
Cli::main(
|
||||||
|
"tx_only_uart",
|
||||||
|
|_, platform, ExtraArgs { baud_rate, message }| {
|
||||||
|
Ok(JobParams::new(platform.try_wrap_main_module(|io| {
|
||||||
|
let clk = pick_clock(&io).ty();
|
||||||
|
let divisor = clk.frequency() / *baud_rate;
|
||||||
|
let baud_rate_error = |msg| {
|
||||||
|
<Cli as clap::CommandFactory>::command()
|
||||||
|
.error(clap::error::ErrorKind::ValueValidation, msg)
|
||||||
|
};
|
||||||
|
const HUGE_DIVISOR: f64 = u64::MAX as f64;
|
||||||
|
match divisor {
|
||||||
|
divisor if !divisor.is_finite() => {
|
||||||
|
return Err(baud_rate_error("bad baud rate"));
|
||||||
|
}
|
||||||
|
HUGE_DIVISOR.. => return Err(baud_rate_error("baud rate is too small")),
|
||||||
|
4.0.. => {}
|
||||||
|
_ => return Err(baud_rate_error("baud rate is too large")),
|
||||||
|
}
|
||||||
|
Ok(tx_only_uart(io, divisor, message))
|
||||||
|
})?))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -50,3 +50,13 @@ pub struct RgbLed {
|
||||||
pub g: Bool,
|
pub g: Bool,
|
||||||
pub b: Bool,
|
pub b: Bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
/// UART, used as an output from the FPGA
|
||||||
|
pub struct Uart {
|
||||||
|
/// transmit from the FPGA's perspective
|
||||||
|
pub tx: Bool,
|
||||||
|
/// receive from the FPGA's perspective
|
||||||
|
#[hdl(flip)]
|
||||||
|
pub rx: Bool,
|
||||||
|
}
|
||||||
|
|
|
||||||
120
crates/fayalite/src/vendor/xilinx/arty_a7.rs
vendored
120
crates/fayalite/src/vendor/xilinx/arty_a7.rs
vendored
|
|
@ -3,16 +3,16 @@
|
||||||
|
|
||||||
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,
|
||||||
peripherals::{ClockInput, Led, RgbLed},
|
peripherals::{ClockInput, Led, RgbLed, Uart},
|
||||||
},
|
},
|
||||||
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>,
|
||||||
|
|
@ -77,13 +77,14 @@ pub struct ArtyA7Peripherals {
|
||||||
ld5: Peripheral<Led>,
|
ld5: Peripheral<Led>,
|
||||||
ld6: Peripheral<Led>,
|
ld6: Peripheral<Led>,
|
||||||
ld7: Peripheral<Led>,
|
ld7: Peripheral<Led>,
|
||||||
|
uart: Peripheral<Uart>,
|
||||||
// TODO: add rest of peripherals when we need them
|
// TODO: add rest of peripherals when we need them
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
|
@ -94,8 +95,9 @@ impl Peripherals for ArtyA7Peripherals {
|
||||||
ld5,
|
ld5,
|
||||||
ld6,
|
ld6,
|
||||||
ld7,
|
ld7,
|
||||||
|
uart,
|
||||||
} = 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);
|
||||||
|
|
@ -106,6 +108,7 @@ impl Peripherals for ArtyA7Peripherals {
|
||||||
ld5.append_peripherals(peripherals);
|
ld5.append_peripherals(peripherals);
|
||||||
ld6.append_peripherals(peripherals);
|
ld6.append_peripherals(peripherals);
|
||||||
ld7.append_peripherals(peripherals);
|
ld7.append_peripherals(peripherals);
|
||||||
|
uart.append_peripherals(peripherals);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,9 +171,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),
|
||||||
|
|
@ -181,6 +195,7 @@ impl Platform for ArtyA7Platform {
|
||||||
ld5: builder.output_peripheral("ld5", Led),
|
ld5: builder.output_peripheral("ld5", Led),
|
||||||
ld6: builder.output_peripheral("ld6", Led),
|
ld6: builder.output_peripheral("ld6", Led),
|
||||||
ld7: builder.output_peripheral("ld7", Led),
|
ld7: builder.output_peripheral("ld7", Led),
|
||||||
|
uart: builder.output_peripheral("uart", Uart),
|
||||||
},
|
},
|
||||||
builder.finish(),
|
builder.finish(),
|
||||||
)
|
)
|
||||||
|
|
@ -192,7 +207,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,
|
||||||
|
|
@ -203,6 +218,7 @@ impl Platform for ArtyA7Platform {
|
||||||
ld5,
|
ld5,
|
||||||
ld6,
|
ld6,
|
||||||
ld7,
|
ld7,
|
||||||
|
uart,
|
||||||
} = peripherals;
|
} = peripherals;
|
||||||
let make_buffered_input = |name: &str, location: &str, io_standard: &str, invert: bool| {
|
let make_buffered_input = |name: &str, location: &str, io_standard: &str, invert: bool| {
|
||||||
let pin = m.input_with_loc(name, SourceLocation::builtin(), Bool);
|
let pin = m.input_with_loc(name, SourceLocation::builtin(), Bool);
|
||||||
|
|
@ -249,30 +265,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() {
|
||||||
|
|
@ -310,6 +378,14 @@ impl Platform for ArtyA7Platform {
|
||||||
connect(o, false);
|
connect(o, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let uart_tx = make_buffered_output("uart_tx", "D10", "LVCMOS33");
|
||||||
|
let uart_rx = make_buffered_input("uart_rx", "A9", "LVCMOS33", false);
|
||||||
|
if let Some(uart) = uart.into_used() {
|
||||||
|
connect(uart_tx, uart.instance_io_field().tx);
|
||||||
|
connect(uart.instance_io_field().rx, uart_rx);
|
||||||
|
} else {
|
||||||
|
connect(uart_tx, true); // idle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn aspects(&self) -> PlatformAspectSet {
|
fn aspects(&self) -> PlatformAspectSet {
|
||||||
|
|
|
||||||
|
|
@ -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