copy vendor/xilinx to vendor/lattice

This commit is contained in:
Tobias Alexandra Platen 2026-01-21 20:00:29 +01:00
parent e366793204
commit ad5e7e29e9
5 changed files with 1712 additions and 2 deletions

View file

@ -2,11 +2,14 @@
// See Notices.txt for copyright information
pub mod xilinx;
pub mod lattice;
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> {
xilinx::built_in_job_kinds()
xilinx::built_in_job_kinds();
lattice::built_in_job_kinds()
}
pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = crate::platform::DynPlatform> {
xilinx::built_in_platforms()
xilinx::built_in_platforms();
lattice::built_in_platforms()
}

208
crates/fayalite/src/vendor/lattice.rs vendored Normal file
View file

@ -0,0 +1,208 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
annotations::make_annotation_enum,
build::{GlobalParams, ToArgs, WriteArgs},
intern::Interned,
prelude::{DynPlatform, Platform},
};
use clap::ValueEnum;
use ordered_float::NotNan;
use serde::{Deserialize, Serialize};
use std::fmt;
pub mod orangecrab;
pub mod primitives;
pub mod yosys_nextpnr;
/* must be removed later */
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct XdcIOStandardAnnotation {
pub value: Interned<str>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct XdcLocationAnnotation {
pub location: Interned<str>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct XdcCreateClockAnnotation {
/// clock period in nanoseconds
pub period: NotNan<f64>,
}
/* fixme make_annotation_enum! {
#[non_exhaustive]
pub enum LatticeAnnotation {
XdcIOStandard(XdcIOStandardAnnotation),
XdcLocation(XdcLocationAnnotation),
XdcCreateClock(XdcCreateClockAnnotation),
}
} */
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
pub struct LatticeArgs {
#[arg(long)]
pub device: Option<Device>,
}
impl LatticeArgs {
pub fn require_device(
&self,
platform: Option<&DynPlatform>,
global_params: &GlobalParams,
) -> clap::error::Result<Device> {
if let Some(device) = self.device {
return Ok(device);
}
if let Some(device) =
platform.and_then(|platform| platform.aspects().get_single_by_type::<Device>().copied())
{
return Ok(device);
}
Err(global_params.clap_error(
clap::error::ErrorKind::MissingRequiredArgument,
"missing --device option",
))
}
}
impl ToArgs for LatticeArgs {
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
if let Some(device) = self.device {
args.write_long_option_eq("device", device.as_str());
}
}
}
macro_rules! make_device_enum {
($vis:vis enum $Device:ident {
$(
#[
name = $name:literal,
xray_part = $xray_part:literal,
xray_device = $xray_device:literal,
xray_family = $xray_family:literal,
]
$variant:ident,
)*
}) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, ValueEnum)]
$vis enum $Device {
$(
#[value(name = $name, alias = $xray_part)]
$variant,
)*
}
impl $Device {
$vis fn as_str(self) -> &'static str {
match self {
$(Self::$variant => $name,)*
}
}
$vis fn xray_part(self) -> &'static str {
match self {
$(Self::$variant => $xray_part,)*
}
}
$vis fn xray_device(self) -> &'static str {
match self {
$(Self::$variant => $xray_device,)*
}
}
$vis fn xray_family(self) -> &'static str {
match self {
$(Self::$variant => $xray_family,)*
}
}
}
struct DeviceVisitor;
impl<'de> serde::de::Visitor<'de> for DeviceVisitor {
type Value = $Device;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a Lattice device string")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match $Device::from_str(v, false) {
Ok(v) => Ok(v),
Err(_) => Err(E::invalid_value(serde::de::Unexpected::Str(v), &self)),
}
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match str::from_utf8(v).ok().and_then(|v| $Device::from_str(v, false).ok()) {
Some(v) => Ok(v),
None => Err(E::invalid_value(serde::de::Unexpected::Bytes(v), &self)),
}
}
}
impl<'de> Deserialize<'de> for $Device {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_string(DeviceVisitor)
}
}
impl Serialize for $Device {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.as_str().serialize(serializer)
}
}
};
}
make_device_enum! {
pub enum Device {
#[
name = "xc7a35ticsg324-1L",
xray_part = "xc7a35tcsg324-1",
xray_device = "xc7a35t",
xray_family = "artix7",
]
Xc7a35ticsg324_1l,
#[
name = "xc7a100ticsg324-1L",
xray_part = "xc7a100tcsg324-1",
xray_device = "xc7a100t",
xray_family = "artix7",
]
Xc7a100ticsg324_1l,
}
}
impl fmt::Display for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> {
orangecrab::built_in_job_kinds()
.into_iter()
.chain(yosys_nextpnr::built_in_job_kinds())
}
pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = crate::platform::DynPlatform> {
orangecrab::built_in_platforms()
.into_iter()
.chain(yosys_nextpnr::built_in_platforms())
}

View file

@ -0,0 +1,405 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
intern::{Intern, Interned},
module::{instance_with_loc, reg_builder_with_loc, wire_with_loc},
platform::{
DynPlatform, Peripheral, PeripheralRef, Peripherals, PeripheralsBuilderFactory,
PeripheralsBuilderFinished, Platform, PlatformAspectSet,
peripherals::{ClockInput, Led, RgbLed, Uart},
},
prelude::*,
vendor::lattice::{
Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation,
primitives,
},
};
use ordered_float::NotNan;
use std::sync::OnceLock;
macro_rules! orangecrab_platform {
(
$vis:vis enum $ArtyA7Platform:ident {
$(#[name = $name:literal, device = $device:ident]
$Variant:ident,)*
}
) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
$vis enum $ArtyA7Platform {
$($Variant,)*
}
impl $ArtyA7Platform {
$vis const VARIANTS: &'static [Self] = &[$(Self::$Variant,)*];
$vis fn device(self) -> Device {
match self {
$(Self::$Variant => Device::$device,)*
}
}
$vis const fn as_str(self) -> &'static str {
match self {
$(Self::$Variant => $name,)*
}
}
fn get_aspects(self) -> &'static PlatformAspectSet {
match self {
$(Self::$Variant => {
static ASPECTS_SET: OnceLock<PlatformAspectSet> = OnceLock::new();
ASPECTS_SET.get_or_init(|| self.make_aspects())
})*
}
}
}
};
}
orangecrab_platform! {
pub enum ArtyA7Platform {
#[name = "arty-a7-35t", device = Xc7a35ticsg324_1l]
ArtyA7_35T,
#[name = "arty-a7-100t", device = Xc7a100ticsg324_1l]
ArtyA7_100T,
}
}
#[derive(Debug)]
pub struct ArtyA7Peripherals {
clk100_div_pow2: [Peripheral<ClockInput>; 4],
rst: Peripheral<Reset>,
rst_sync: Peripheral<SyncReset>,
ld0: Peripheral<RgbLed>,
ld1: Peripheral<RgbLed>,
ld2: Peripheral<RgbLed>,
ld3: Peripheral<RgbLed>,
ld4: Peripheral<Led>,
ld5: Peripheral<Led>,
ld6: Peripheral<Led>,
ld7: Peripheral<Led>,
uart: Peripheral<Uart>,
// TODO: add rest of peripherals when we need them
}
impl Peripherals for ArtyA7Peripherals {
fn append_peripherals<'a>(&'a self, peripherals: &mut Vec<PeripheralRef<'a, CanonicalType>>) {
let Self {
clk100_div_pow2,
rst,
rst_sync,
ld0,
ld1,
ld2,
ld3,
ld4,
ld5,
ld6,
ld7,
uart,
} = self;
clk100_div_pow2.append_peripherals(peripherals);
rst.append_peripherals(peripherals);
rst_sync.append_peripherals(peripherals);
ld0.append_peripherals(peripherals);
ld1.append_peripherals(peripherals);
ld2.append_peripherals(peripherals);
ld3.append_peripherals(peripherals);
ld4.append_peripherals(peripherals);
ld5.append_peripherals(peripherals);
ld6.append_peripherals(peripherals);
ld7.append_peripherals(peripherals);
uart.append_peripherals(peripherals);
}
}
impl ArtyA7Platform {
fn make_aspects(self) -> PlatformAspectSet {
let mut retval = PlatformAspectSet::new();
retval.insert_new(self.device());
retval
}
}
#[hdl_module(extern)]
fn reset_sync() {
#[hdl]
let clk: Clock = m.input();
#[hdl]
let inp: Bool = m.input();
#[hdl]
let out: SyncReset = m.output();
m.annotate_module(BlackBoxInlineAnnotation {
path: "fayalite_orangecrab_reset_sync.v".intern(),
text: r#"module __fayalite_orangecrab_reset_sync(input clk, input inp, output out);
wire reset_0_out;
(* ASYNC_REG = "TRUE" *)
FDPE #(
.INIT(1'b1)
) reset_0 (
.Q(reset_0_out),
.C(clk),
.CE(1'b1),
.PRE(inp),
.D(1'b0)
);
(* ASYNC_REG = "TRUE" *)
FDPE #(
.INIT(1'b1)
) reset_1 (
.Q(out),
.C(clk),
.CE(1'b1),
.PRE(inp),
.D(reset_0_out)
);
endmodule
"#
.intern(),
});
m.verilog_name("__fayalite_orangecrab_reset_sync");
}
impl Platform for ArtyA7Platform {
type Peripherals = ArtyA7Peripherals;
fn name(&self) -> Interned<str> {
self.as_str().intern()
}
fn new_peripherals<'builder>(
&self,
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_div_pow2,
rst: builder.input_peripheral("rst", Reset),
rst_sync: builder.input_peripheral("rst_sync", SyncReset),
ld0: builder.output_peripheral("ld0", RgbLed),
ld1: builder.output_peripheral("ld1", RgbLed),
ld2: builder.output_peripheral("ld2", RgbLed),
ld3: builder.output_peripheral("ld3", RgbLed),
ld4: builder.output_peripheral("ld4", Led),
ld5: builder.output_peripheral("ld5", Led),
ld6: builder.output_peripheral("ld6", Led),
ld7: builder.output_peripheral("ld7", Led),
uart: builder.output_peripheral("uart", Uart),
},
builder.finish(),
)
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn add_peripherals_in_wrapper_module(&self, m: &ModuleBuilder, peripherals: Self::Peripherals) {
let ArtyA7Peripherals {
clk100_div_pow2,
rst,
rst_sync,
ld0,
ld1,
ld2,
ld3,
ld4,
ld5,
ld6,
ld7,
uart,
} = peripherals;
let make_buffered_input = |name: &str, location: &str, io_standard: &str, invert: bool| {
let pin = m.input_with_loc(name, SourceLocation::builtin(), Bool);
/* fixme annotate(
pin,
XdcLocationAnnotation {
location: location.intern(),
},
); */
/* fixme annotate(
pin,
XdcIOStandardAnnotation {
value: io_standard.intern(),
},
); */
let buf = instance_with_loc(
&format!("{name}_buf"),
primitives::IBUF(),
SourceLocation::builtin(),
);
connect(buf.I, pin);
if invert { !buf.O } else { buf.O }
};
let make_buffered_output = |name: &str, location: &str, io_standard: &str| {
let pin = m.output_with_loc(name, SourceLocation::builtin(), Bool);
/* fixme annotate(
pin,
XdcLocationAnnotation {
location: location.intern(),
},
);
annotate(
pin,
XdcIOStandardAnnotation {
value: io_standard.intern(),
},
); */
let buf = instance_with_loc(
&format!("{name}_buf"),
primitives::OBUFT(),
SourceLocation::builtin(),
);
connect(pin, buf.O);
connect(buf.T, false);
buf.I
};
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",
primitives::STARTUPE2_default_inputs(),
SourceLocation::builtin(),
);
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);
/* fixme
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);
/* fixme 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, clk_out);
connect(rst_sync.inp, rst_buf | !startup.EOS);
rst_sync.out
};
if let Some(rst) = rst.into_used() {
connect(rst.instance_io_field(), rst_value.to_reset());
}
if let Some(rst_sync) = rst_sync.into_used() {
connect(rst_sync.instance_io_field(), rst_value);
}
let rgb_leds = [
(ld0, ("G6", "F6", "E1")),
(ld1, ("G3", "J4", "G4")),
(ld2, ("J3", "J2", "H4")),
(ld3, ("K1", "H6", "K2")),
];
for (rgb_led, (r_loc, g_loc, b_loc)) in rgb_leds {
let r = make_buffered_output(&format!("{}_r", rgb_led.name()), r_loc, "LVCMOS33");
let g = make_buffered_output(&format!("{}_g", rgb_led.name()), g_loc, "LVCMOS33");
let b = make_buffered_output(&format!("{}_b", rgb_led.name()), b_loc, "LVCMOS33");
if let Some(rgb_led) = rgb_led.into_used() {
connect(r, rgb_led.instance_io_field().r);
connect(g, rgb_led.instance_io_field().g);
connect(b, rgb_led.instance_io_field().b);
} else {
connect(r, false);
connect(g, false);
connect(b, false);
}
}
let leds = [(ld4, "H5"), (ld5, "J5"), (ld6, "T9"), (ld7, "T10")];
for (led, loc) in leds {
let o = make_buffered_output(&led.name(), loc, "LVCMOS33");
if let Some(led) = led.into_used() {
connect(o, led.instance_io_field().on);
} else {
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 {
self.get_aspects().clone()
}
}
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::DynJobKind> {
[]
}
pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = DynPlatform> {
ArtyA7Platform::VARIANTS
.iter()
.map(|&v| DynPlatform::new(v))
}

View file

@ -0,0 +1,50 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#![allow(non_snake_case)]
use crate::prelude::*;
#[hdl_module(extern)]
pub fn IBUF() {
m.verilog_name("IBUF");
#[hdl]
let O: Bool = m.output();
#[hdl]
let I: Bool = m.input();
}
#[hdl_module(extern)]
pub fn OBUFT() {
m.verilog_name("OBUFT");
#[hdl]
let O: Bool = m.output();
#[hdl]
let I: Bool = m.input();
#[hdl]
let T: Bool = m.input();
}
#[hdl_module(extern)]
pub fn BUFGCE() {
m.verilog_name("BUFGCE");
#[hdl]
let O: Clock = m.output();
#[hdl]
let CE: Bool = m.input();
#[hdl]
let I: Clock = m.input();
}
#[hdl_module(extern)]
pub fn STARTUPE2_default_inputs() {
m.verilog_name("STARTUPE2");
#[hdl]
let CFGCLK: Clock = m.output();
#[hdl]
let CFGMCLK: Clock = m.output();
#[hdl]
let EOS: Bool = m.output();
#[hdl]
let PREQ: Bool = m.output();
}

File diff suppressed because it is too large Load diff