Compare commits

..

1 commit

Author SHA1 Message Date
Tobias Alexandra Platen
34b4a57507 copy vendor/xilinx to vendor/lattice 2026-03-28 08:41:36 +01:00
8 changed files with 480 additions and 394 deletions

21
Makefile Normal file
View file

@ -0,0 +1,21 @@
DIRU=/home/alex/Desktop/Hacking/libre-chip/fayalite-wip/target/blinky-out
NEXTPNR_DENSITY:=--25k
all:
cp $(DIRU)/*.pcf /tmp
RUST_BACKTRACE=full cargo run --example blinky yosys-nextpnr-ecp5 \
--nextpnr /home/alex/.guix-profile/bin/nextpnr-ecp5 \
--platform orangecrab-85k -o target/blinky-out \
--ecppack /home/alex/.guix-profile/bin/ecppack \
--placeholder-dir /tmp/anyPathBuf/orangecrab_r0.2.1.pcf
ls -1 $(DIRU)/*.bit
ls:
ls -1 $(DIRU)
clean:
rm $(DIRU)/*
nextpnr:
cd $(DIRU) && nextpnr-ecp5 --json blinky.json --textcfg blinky.nextpnr.out $(NEXTPNR_DENSITY) \
--package CSFBGA285 --lpf orangecrab_r0.2.1.pcf --lpf-allow-unconstrained
pack:
cd $(DIRU) && ecppack --compress --freq 38.8 --input blinky.nextpnr.out --bit blinky.nextpnr.bit
cd $(DIRU) && file *.bit

View file

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

View file

@ -12,4 +12,176 @@ use ordered_float::NotNan;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
// copy of xilinx.rs with same header pub mod orangecrab;
pub mod primitives;
pub mod yosys_nextpnr;
#[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,
lattice_part = $lattice_part:literal,
lattice_device = $lattice_device:literal,
lattice_family = $lattice_family:literal,
]
$variant:ident,
)*
}) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, ValueEnum)]
$vis enum $Device {
$(
#[value(name = $name, alias = $lattice_part)]
$variant,
)*
}
impl $Device {
$vis fn as_str(self) -> &'static str {
match self {
$(Self::$variant => $name,)*
}
}
$vis fn lattice_part(self) -> &'static str {
match self {
$(Self::$variant => $lattice_part,)*
}
}
$vis fn lattice_device(self) -> &'static str {
match self {
$(Self::$variant => $lattice_device,)*
}
}
$vis fn lattice_family(self) -> &'static str {
match self {
$(Self::$variant => $lattice_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)
}
}
};
}
//ECP5 variants
make_device_enum! {
pub enum Device {
#[
name = "placeholder25k",
lattice_part = "fixme",
lattice_device = "fixme",
lattice_family = "fixme",
]
Placeholder25k,
#[
name = "placeholder85k",
lattice_part = "fixme",
lattice_device = "fimxe",
lattice_family = "fixme",
]
Placeholder85k,
}
}
//rest looks good
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())
}
//first step yosys -p "read_verilog $<; synth_ecp5 -json $@"

View file

@ -10,28 +10,29 @@ use crate::{
peripherals::{ClockInput, Led, RgbLed, Uart}, peripherals::{ClockInput, Led, RgbLed, Uart},
}, },
prelude::*, prelude::*,
vendor::xilinx::{ vendor::lattice::{
Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, Device,
primitives, primitives,
}, },
}; };
use ordered_float::NotNan; use ordered_float::NotNan;
use std::sync::OnceLock; use std::sync::OnceLock;
macro_rules! arty_a7_platform { //keep unchanged
macro_rules! orangecrab_platform {
( (
$vis:vis enum $ArtyA7Platform:ident { $vis:vis enum $OrangeCrabPlatform:ident {
$(#[name = $name:literal, device = $device:ident] $(#[name = $name:literal, device = $device:ident]
$Variant:ident,)* $Variant:ident,)*
} }
) => { ) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive] #[non_exhaustive]
$vis enum $ArtyA7Platform { $vis enum $OrangeCrabPlatform {
$($Variant,)* $($Variant,)*
} }
impl $ArtyA7Platform { impl $OrangeCrabPlatform {
$vis const VARIANTS: &'static [Self] = &[$(Self::$Variant,)*]; $vis const VARIANTS: &'static [Self] = &[$(Self::$Variant,)*];
$vis fn device(self) -> Device { $vis fn device(self) -> Device {
match self { match self {
@ -55,15 +56,17 @@ macro_rules! arty_a7_platform {
}; };
} }
arty_a7_platform! { //untested
pub enum ArtyA7Platform { orangecrab_platform! {
#[name = "arty-a7-35t", device = Xc7a35ticsg324_1l] pub enum OrangeCrabPlatform {
ArtyA7_35T, #[name = "orangecrab-25k", device = Placeholder25k]
#[name = "arty-a7-100t", device = Xc7a100ticsg324_1l] OrangeCrab_25k,
ArtyA7_100T, #[name = "orangecrab-85k", device = Placeholder85k]
OrangeCrab_85k,
} }
} }
//FIXME
#[derive(Debug)] #[derive(Debug)]
pub struct ArtyA7Peripherals { pub struct ArtyA7Peripherals {
clk100_div_pow2: [Peripheral<ClockInput>; 4], clk100_div_pow2: [Peripheral<ClockInput>; 4],
@ -112,7 +115,7 @@ impl Peripherals for ArtyA7Peripherals {
} }
} }
impl ArtyA7Platform { impl OrangeCrabPlatform {
fn make_aspects(self) -> PlatformAspectSet { fn make_aspects(self) -> PlatformAspectSet {
let mut retval = PlatformAspectSet::new(); let mut retval = PlatformAspectSet::new();
retval.insert_new(self.device()); retval.insert_new(self.device());
@ -129,37 +132,21 @@ fn reset_sync() {
#[hdl] #[hdl]
let out: SyncReset = m.output(); let out: SyncReset = m.output();
m.annotate_module(BlackBoxInlineAnnotation { m.annotate_module(BlackBoxInlineAnnotation {
path: "fayalite_arty_a7_reset_sync.v".intern(), path: "fayalite_orangecrab_reset_sync.v".intern(),
text: r#"module __fayalite_arty_a7_reset_sync(input clk, input inp, output out); text: r#"module __fayalite_orangecrab_reset_sync(input clk, input inp, output out);
wire reset_0_out; wire reset_0_out;
(* ASYNC_REG = "TRUE" *) always @(posedge clk) begin
FDPE #( reset_0_out <= inp;
.INIT(1'b1) outp <= reset_0_out;
) reset_0 ( end
.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 endmodule
"# "#
.intern(), .intern(),
}); });
m.verilog_name("__fayalite_arty_a7_reset_sync"); m.verilog_name("__fayalite_orangecrab_reset_sync");
} }
impl Platform for ArtyA7Platform { impl Platform for OrangeCrabPlatform {
type Peripherals = ArtyA7Peripherals; type Peripherals = ArtyA7Peripherals;
fn name(&self) -> Interned<str> { fn name(&self) -> Interned<str> {
@ -222,29 +209,30 @@ impl Platform for ArtyA7Platform {
} = 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);
annotate( /* fixme annotate(
pin, pin,
XdcLocationAnnotation { XdcLocationAnnotation {
location: location.intern(), location: location.intern(),
}, },
); ); */
annotate( /* fixme annotate(
pin, pin,
XdcIOStandardAnnotation { XdcIOStandardAnnotation {
value: io_standard.intern(), value: io_standard.intern(),
}, },
); ); */
let buf = instance_with_loc( //let buf = instance_with_loc(
&format!("{name}_buf"), // &format!("{name}_buf"),
primitives::IBUF(), // //primitives::IBUF(),
SourceLocation::builtin(), // SourceLocation::builtin(),
); //);
connect(buf.I, pin); //connect(buf.I, pin);
if invert { !buf.O } else { buf.O } //if invert { !buf.O } else { buf.O }
if invert { !pin } else { pin }
}; };
let make_buffered_output = |name: &str, location: &str, io_standard: &str| { let make_buffered_output = |name: &str, location: &str, io_standard: &str| {
let pin = m.output_with_loc(name, SourceLocation::builtin(), Bool); let pin = m.output_with_loc(name, SourceLocation::builtin(), Bool);
annotate( /* fixme annotate(
pin, pin,
XdcLocationAnnotation { XdcLocationAnnotation {
location: location.intern(), location: location.intern(),
@ -255,15 +243,16 @@ impl Platform for ArtyA7Platform {
XdcIOStandardAnnotation { XdcIOStandardAnnotation {
value: io_standard.intern(), value: io_standard.intern(),
}, },
); ); */
let buf = instance_with_loc( //let buf = instance_with_loc(
&format!("{name}_buf"), // &format!("{name}_buf"),
primitives::OBUFT(), // primitives::OBUFT(),
SourceLocation::builtin(), // SourceLocation::builtin(),
); //);
connect(pin, buf.O); //connect(pin, buf.O);
connect(buf.T, false); //connect(buf.T, false);
buf.I //buf.I
pin
}; };
let mut frequency = clk100_div_pow2[0].ty().frequency(); let mut frequency = clk100_div_pow2[0].ty().frequency();
let mut log2_divisor = 0; let mut log2_divisor = 0;
@ -281,17 +270,17 @@ impl Platform for ArtyA7Platform {
log2_divisor = cur_log2_divisor; 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",
primitives::STARTUPE2_default_inputs(), // primitives::STARTUPE2_default_inputs(),
SourceLocation::builtin(), // SourceLocation::builtin(),
); //);
let clk_global_buf = instance_with_loc( //let clk_global_buf = instance_with_loc(
"clk_global_buf", // "clk_global_buf",
primitives::BUFGCE(), // primitives::BUFGCE(),
SourceLocation::builtin(), // SourceLocation::builtin(),
); //);
connect(clk_global_buf.CE, startup.EOS); //connect(clk_global_buf.CE, startup.EOS);
let mut clk_global_buf_in = clk100_buf.to_clock(); let mut clk_global_buf_in = clk100_buf.to_clock();
for prev_log2_divisor in 0..log2_divisor { for prev_log2_divisor in 0..log2_divisor {
let prev_divisor = 1u64 << prev_log2_divisor; let prev_divisor = 1u64 << prev_log2_divisor;
@ -301,13 +290,14 @@ impl Platform for ArtyA7Platform {
Clock, Clock,
); );
connect(clk_in, clk_global_buf_in); connect(clk_in, clk_global_buf_in);
/* fixme
annotate( annotate(
clk_in, clk_in,
XdcCreateClockAnnotation { XdcCreateClockAnnotation {
period: NotNan::new(1e9 / (100e6 / prev_divisor as f64)) period: NotNan::new(1e9 / (100e6 / prev_divisor as f64))
.expect("known to be valid"), .expect("known to be valid"),
}, },
); ); */
annotate(clk_in, DontTouchAnnotation); annotate(clk_in, DontTouchAnnotation);
let cd = wire_with_loc( let cd = wire_with_loc(
&format!("clk_div_{prev_divisor}_in"), &format!("clk_div_{prev_divisor}_in"),
@ -315,7 +305,7 @@ impl Platform for ArtyA7Platform {
ClockDomain[AsyncReset], ClockDomain[AsyncReset],
); );
connect(cd.clk, clk_in); connect(cd.clk, clk_in);
connect(cd.rst, (!startup.EOS).to_async_reset()); //FIXME connect(cd.rst, (!startup.EOS).to_async_reset());
let divider = reg_builder_with_loc("divider", SourceLocation::builtin()) let divider = reg_builder_with_loc("divider", SourceLocation::builtin())
.clock_domain(cd) .clock_domain(cd)
.reset(false) .reset(false)
@ -323,24 +313,26 @@ impl Platform for ArtyA7Platform {
connect(divider, !divider); connect(divider, !divider);
clk_global_buf_in = divider.to_clock(); clk_global_buf_in = divider.to_clock();
} }
connect(clk_global_buf.I, clk_global_buf_in); //connect(clk_global_buf.I, clk_global_buf_in);
let clk_out = wire_with_loc("clk_out", SourceLocation::builtin(), Clock); let clk_out = wire_with_loc("clk_out", SourceLocation::builtin(), Clock);
connect(clk_out, clk_global_buf.O); //connect(clk_out, clk_global_buf.O);
annotate( connect(clk_out, clk_global_buf_in);
/* fixme annotate(
clk_out, clk_out,
XdcCreateClockAnnotation { XdcCreateClockAnnotation {
period: NotNan::new(1e9 / frequency).expect("known to be valid"), period: NotNan::new(1e9 / frequency).expect("known to be valid"),
}, },
); ); */
annotate(clk_out, DontTouchAnnotation); annotate(clk_out, DontTouchAnnotation);
if let Some(clk) = clk { if let Some(clk) = clk {
connect(clk.instance_io_field().clk, clk_out); connect(clk.instance_io_field().clk, clk_out);
} }
//undo 1
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, clk_out); connect(rst_sync.clk, clk_out);
connect(rst_sync.inp, rst_buf | !startup.EOS); connect(rst_sync.inp, rst_buf/* | !startup.EOS*/); //FIXME
rst_sync.out rst_sync.out
}; };
if let Some(rst) = rst.into_used() { if let Some(rst) = rst.into_used() {
@ -398,7 +390,7 @@ pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = crate::build::Dyn
} }
pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = DynPlatform> { pub(crate) fn built_in_platforms() -> impl IntoIterator<Item = DynPlatform> {
ArtyA7Platform::VARIANTS OrangeCrabPlatform::VARIANTS
.iter() .iter()
.map(|&v| DynPlatform::new(v)) .map(|&v| DynPlatform::new(v))
} }

View file

@ -6,5 +6,45 @@
use crate::prelude::*; use crate::prelude::*;
//#[hdl_module(extern)] //#[hdl_module(extern)]
//pub fn PLACEHOLDER() { //pub fn IBUF() {
//do this first // 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 FIXME_PLACEHOLDER() {
m.verilog_name("FIXME_PLACEHOLDER");
#[hdl]
let CFGCLK: Clock = m.output();
#[hdl]
let CFGMCLK: Clock = m.output();
#[hdl]
let EOS: Bool = m.output();
#[hdl]
let PREQ: Bool = m.output();
}

View file

@ -24,8 +24,8 @@ use crate::{
source_location::SourceLocation, source_location::SourceLocation,
util::{HashSet, job_server::AcquiredJob}, util::{HashSet, job_server::AcquiredJob},
vendor::lattice::{ vendor::lattice::{
Device, XdcCreateClockAnnotation, XdcIOStandardAnnotation, XdcLocationAnnotation, Device,
LatticeAnnotation, LatticeArgs, /* fixme LatticeAnnotation,*/ LatticeArgs,
}, },
}; };
use eyre::Context; use eyre::Context;
@ -39,26 +39,26 @@ use std::{
}; };
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
pub struct YosysNextpnrTrellisWriteYsFileJobKind; pub struct YosysNextpnrWriteYsFileJobKind;
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
pub struct YosysNextpnrTrellisWriteYsFileArgs {} pub struct YosysNextpnrWriteYsFileArgs {}
impl ToArgs for YosysNextpnrTrellisWriteYsFileArgs { impl ToArgs for YosysNextpnrWriteYsFileArgs {
fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) { fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) {
let Self {} = self; let Self {} = self;
} }
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct YosysNextpnrTrellisWriteYsFile { pub struct YosysNextpnrWriteYsFile {
main_verilog_file: Interned<Path>, main_verilog_file: Interned<Path>,
ys_file: Interned<Path>, ys_file: Interned<Path>,
json_file: Interned<Path>, json_file: Interned<Path>,
json_file_name: Interned<OsStr>, json_file_name: Interned<OsStr>,
} }
impl YosysNextpnrTrellisWriteYsFile { impl YosysNextpnrWriteYsFile {
pub fn main_verilog_file(&self) -> Interned<Path> { pub fn main_verilog_file(&self) -> Interned<Path> {
self.main_verilog_file self.main_verilog_file
} }
@ -91,7 +91,7 @@ impl YosysNextpnrTrellisWriteYsFile {
let circuit_name = crate::firrtl::get_circuit_name(main_module_name_id); let circuit_name = crate::firrtl::get_circuit_name(main_module_name_id);
writeln!( writeln!(
output, output,
"synth_lattice -flatten -abc9 -nobram -arch xc7 -top {circuit_name}" "synth_ecp5 -top {circuit_name}"
) )
.expect("writing to OsString can't fail"); .expect("writing to OsString can't fail");
output.push("write_json \""); output.push("write_json \"");
@ -101,9 +101,9 @@ impl YosysNextpnrTrellisWriteYsFile {
} }
} }
impl JobKind for YosysNextpnrTrellisWriteYsFileJobKind { impl JobKind for YosysNextpnrWriteYsFileJobKind {
type Args = YosysNextpnrTrellisWriteYsFileArgs; type Args = YosysNextpnrWriteYsFileArgs;
type Job = YosysNextpnrTrellisWriteYsFile; type Job = YosysNextpnrWriteYsFile;
type Dependencies = JobKindAndDependencies<VerilogJobKind>; type Dependencies = JobKindAndDependencies<VerilogJobKind>;
fn dependencies(self) -> Self::Dependencies { fn dependencies(self) -> Self::Dependencies {
@ -123,11 +123,11 @@ impl JobKind for YosysNextpnrTrellisWriteYsFileJobKind {
.verilog_dialect .verilog_dialect
.get_or_insert(VerilogDialect::Yosys); .get_or_insert(VerilogDialect::Yosys);
args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| {
let YosysNextpnrTrellisWriteYsFileArgs {} = args; let YosysNextpnrWriteYsFileArgs {} = args;
let base_job = dependencies.get_job::<BaseJob, _>(); let base_job = dependencies.get_job::<BaseJob, _>();
let verilog_job = dependencies.get_job::<VerilogJob, _>(); let verilog_job = dependencies.get_job::<VerilogJob, _>();
let json_file = base_job.file_with_ext("json"); let json_file = base_job.file_with_ext("json");
Ok(YosysNextpnrTrellisWriteYsFile { Ok(YosysNextpnrWriteYsFile {
main_verilog_file: verilog_job.main_verilog_file(), main_verilog_file: verilog_job.main_verilog_file(),
ys_file: base_job.file_with_ext("ys"), ys_file: base_job.file_with_ext("ys"),
json_file, json_file,
@ -150,7 +150,7 @@ impl JobKind for YosysNextpnrTrellisWriteYsFileJobKind {
} }
fn name(self) -> Interned<str> { fn name(self) -> Interned<str> {
"yosys-nextpnr-trellis-write-ys-file".intern() "yosys-nextpnr-ecp5-write-ys-file".intern()
} }
fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> { fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> {
@ -188,26 +188,26 @@ impl JobKind for YosysNextpnrTrellisWriteYsFileJobKind {
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
pub struct YosysNextpnrTrellisSynthArgs {} pub struct YosysNextpnrSynthArgs {}
impl ToArgs for YosysNextpnrTrellisSynthArgs { impl ToArgs for YosysNextpnrSynthArgs {
fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) { fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) {
let Self {} = self; let Self {} = self;
} }
} }
#[derive(Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] #[derive(Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct YosysNextpnrTrellisSynth { pub struct YosysNextpnrSynth {
#[serde(flatten)] #[serde(flatten)]
write_ys_file: YosysNextpnrTrellisWriteYsFile, write_ys_file: YosysNextpnrWriteYsFile,
ys_file_name: Interned<OsStr>, ys_file_name: Interned<OsStr>,
} }
impl fmt::Debug for YosysNextpnrTrellisSynth { impl fmt::Debug for YosysNextpnrSynth {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { let Self {
write_ys_file: write_ys_file:
YosysNextpnrTrellisWriteYsFile { YosysNextpnrWriteYsFile {
main_verilog_file, main_verilog_file,
ys_file, ys_file,
json_file, json_file,
@ -215,7 +215,7 @@ impl fmt::Debug for YosysNextpnrTrellisSynth {
}, },
ys_file_name, ys_file_name,
} = self; } = self;
f.debug_struct("YosysNextpnrTrellisSynth") f.debug_struct("YosysNextpnrSynth")
.field("main_verilog_file", main_verilog_file) .field("main_verilog_file", main_verilog_file)
.field("ys_file", ys_file) .field("ys_file", ys_file)
.field("ys_file_name", ys_file_name) .field("ys_file_name", ys_file_name)
@ -225,7 +225,7 @@ impl fmt::Debug for YosysNextpnrTrellisSynth {
} }
} }
impl YosysNextpnrTrellisSynth { impl YosysNextpnrSynth {
pub fn main_verilog_file(&self) -> Interned<Path> { pub fn main_verilog_file(&self) -> Interned<Path> {
self.write_ys_file.main_verilog_file() self.write_ys_file.main_verilog_file()
} }
@ -248,19 +248,19 @@ pub struct Yosys;
impl ExternalProgramTrait for Yosys { impl ExternalProgramTrait for Yosys {
fn default_program_name() -> Interned<str> { fn default_program_name() -> Interned<str> {
"yosys".intern() "yosys".intern() //must be yosys
} }
} }
impl ExternalCommand for YosysNextpnrTrellisSynth { impl ExternalCommand for YosysNextpnrSynth {
type AdditionalArgs = YosysNextpnrTrellisSynthArgs; type AdditionalArgs = YosysNextpnrSynthArgs;
type AdditionalJobData = Self; type AdditionalJobData = Self;
type BaseJobPosition = GetJobPositionDependencies< type BaseJobPosition = GetJobPositionDependencies<
GetJobPositionDependencies< GetJobPositionDependencies<
GetJobPositionDependencies<<UnadjustedVerilog as ExternalCommand>::BaseJobPosition>, GetJobPositionDependencies<<UnadjustedVerilog as ExternalCommand>::BaseJobPosition>,
>, >,
>; >;
type Dependencies = JobKindAndDependencies<YosysNextpnrTrellisWriteYsFileJobKind>; type Dependencies = JobKindAndDependencies<YosysNextpnrWriteYsFileJobKind>;
type ExternalProgram = Yosys; type ExternalProgram = Yosys;
fn dependencies() -> Self::Dependencies { fn dependencies() -> Self::Dependencies {
@ -276,7 +276,7 @@ impl ExternalCommand for YosysNextpnrTrellisSynth {
<Self::Dependencies as JobDependencies>::JobsAndKinds, <Self::Dependencies as JobDependencies>::JobsAndKinds,
)> { )> {
args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { args.args_to_jobs_external_simple(params, global_params, |args, dependencies| {
let YosysNextpnrTrellisSynthArgs {} = args.additional_args; let YosysNextpnrSynthArgs {} = args.additional_args;
Ok(Self { Ok(Self {
write_ys_file: dependencies.job.job.clone(), write_ys_file: dependencies.job.job.clone(),
ys_file_name: dependencies ys_file_name: dependencies
@ -318,7 +318,7 @@ impl ExternalCommand for YosysNextpnrTrellisSynth {
} }
fn job_kind_name() -> Interned<str> { fn job_kind_name() -> Interned<str> {
"yosys-nextpnr-trellis-synth".intern() "yosys-nextpnr-ecp5-synth".intern()
} }
fn subcommand_hidden() -> bool { fn subcommand_hidden() -> bool {
@ -327,33 +327,33 @@ impl ExternalCommand for YosysNextpnrTrellisSynth {
} }
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
pub struct YosysNextpnrTrellisWriteXdcFileJobKind; pub struct YosysNextpnrWritePcfFileJobKind;
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
pub struct YosysNextpnrTrellisWriteXdcFileArgs {} pub struct YosysNextpnrWritePcfFileArgs {}
impl ToArgs for YosysNextpnrTrellisWriteXdcFileArgs { impl ToArgs for YosysNextpnrWritePcfFileArgs {
fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) { fn to_args(&self, _args: &mut (impl WriteArgs + ?Sized)) {
let Self {} = self; let Self {} = self;
} }
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct YosysNextpnrTrellisWriteXdcFile { pub struct YosysNextpnrWritePcfFile {
firrtl_export_options: crate::firrtl::ExportOptions, firrtl_export_options: crate::firrtl::ExportOptions,
output_dir: Interned<Path>, output_dir: Interned<Path>,
xdc_file: Interned<Path>, pcf_file: Interned<Path>,
} }
struct WriteXdcContentsError(eyre::Report); struct WritePcfContentsError(eyre::Report);
impl From<eyre::Report> for WriteXdcContentsError { impl From<eyre::Report> for WritePcfContentsError {
fn from(v: eyre::Report) -> Self { fn from(v: eyre::Report) -> Self {
Self(v) Self(v)
} }
} }
impl From<fmt::Error> for WriteXdcContentsError { impl From<fmt::Error> for WritePcfContentsError {
fn from(_v: fmt::Error) -> Self { fn from(_v: fmt::Error) -> Self {
unreachable!("String write can't fail") unreachable!("String write can't fail")
} }
@ -395,7 +395,7 @@ impl AnnotationTarget {
} }
} }
struct XdcFileWriter<W: fmt::Write> { struct PcfFileWriter<W: fmt::Write> { //TODO
output: W, output: W,
module_depth: usize, module_depth: usize,
annotation_target: AnnotationTarget, annotation_target: AnnotationTarget,
@ -403,8 +403,8 @@ struct XdcFileWriter<W: fmt::Write> {
required_dont_touch_targets: HashSet<Interned<Target>>, required_dont_touch_targets: HashSet<Interned<Target>>,
} }
impl<W: fmt::Write> XdcFileWriter<W> { impl<W: fmt::Write> PcfFileWriter<W> {
fn run(output: W, top_module: Module<Bundle>) -> Result<(), WriteXdcContentsError> { fn run(output: W, top_module: Module<Bundle>) -> Result<(), WritePcfContentsError> {
let mut this = Self { let mut this = Self {
output, output,
module_depth: 0, module_depth: 0,
@ -433,7 +433,7 @@ impl<W: fmt::Write> XdcFileWriter<W> {
module_depth: usize, module_depth: usize,
annotation_target: AnnotationTarget, annotation_target: AnnotationTarget,
v: &T, v: &T,
) -> Result<(), WriteXdcContentsError> { ) -> Result<(), WritePcfContentsError> {
let Self { let Self {
output: _, output: _,
module_depth: old_module_depth, module_depth: old_module_depth,
@ -450,8 +450,8 @@ impl<W: fmt::Write> XdcFileWriter<W> {
} }
} }
impl<W: fmt::Write> Visitor for XdcFileWriter<W> { impl<W: fmt::Write> Visitor for PcfFileWriter<W> {
type Error = WriteXdcContentsError; type Error = WritePcfContentsError;
fn visit_targeted_annotation(&mut self, v: &TargetedAnnotation) -> Result<(), Self::Error> { fn visit_targeted_annotation(&mut self, v: &TargetedAnnotation) -> Result<(), Self::Error> {
self.default_visit_with(self.module_depth, AnnotationTarget::Target(v.target()), v) self.default_visit_with(self.module_depth, AnnotationTarget::Target(v.target()), v)
@ -486,161 +486,21 @@ impl<W: fmt::Write> Visitor for XdcFileWriter<W> {
Ok(()) Ok(())
} }
fn visit_lattice_annotation(&mut self, v: &LatticeAnnotation) -> Result<(), Self::Error> {
fn todo( /* FIXME fn visit_lattice_annotation(&mut self, v: &LatticeAnnotation) -> Result<(), Self::Error> */
msg: &str,
annotation: &LatticeAnnotation,
source_location: SourceLocation,
) -> Result<Infallible, WriteXdcContentsError> {
Err(WriteXdcContentsError(eyre::eyre!(
"{msg}\nannotation: {annotation:?}\nat: {source_location}"
)))
}
if self.module_depth != 1 {
match todo(
"annotations are not yet supported outside of the top module since the logic to figure out the correct name isn't implemented",
v,
self.annotation_target.source_location(),
)? {}
}
match self.annotation_target {
AnnotationTarget::None => unreachable!(),
AnnotationTarget::Module(module) => match v {
LatticeAnnotation::XdcIOStandard(_)
| LatticeAnnotation::XdcLocation(_)
| LatticeAnnotation::XdcCreateClock(_) => {
return Err(WriteXdcContentsError(eyre::eyre!(
"annotation not allowed on a module: {v:?}\nat: {}",
module.source_location(),
)));
}
},
AnnotationTarget::Mem(mem) => match todo(
"lattice annotations are not yet supported on memories since the logic to figure out the correct name isn't implemented",
v,
mem.source_location(),
)? {},
AnnotationTarget::Target(target) => {
let base = target.base();
match *base {
TargetBase::ModuleIO(_) => {
// already handled by write_xdc_contents handling the main module's ScalarizedModuleABI
Ok(())
}
TargetBase::MemPort(mem_port) => {
match todo(
"lattice annotations are not yet supported on memory ports since the logic to figure out the correct name isn't implemented",
v,
mem_port.source_location(),
)? {}
}
TargetBase::Reg(_)
| TargetBase::RegSync(_)
| TargetBase::RegAsync(_)
| TargetBase::Wire(_) => {
match *target {
Target::Base(_) => {}
Target::Child(_) => match todo(
"lattice annotations are not yet supported on parts of registers/wires since the logic to figure out the correct name isn't implemented",
v,
base.source_location(),
)? {},
}
match base.canonical_ty() {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_) => {}
CanonicalType::Enum(_)
| CanonicalType::Array(_)
| CanonicalType::Bundle(_)
| CanonicalType::PhantomConst(_)
| CanonicalType::DynSimOnly(_) => match todo(
"lattice annotations are not yet supported on types other than integers, Bool, resets, or Clock since the logic to figure out the correct name isn't implemented",
v,
base.source_location(),
)? {},
}
self.required_dont_touch_targets.insert(target);
match v {
LatticeAnnotation::XdcIOStandard(_)
| LatticeAnnotation::XdcLocation(_) => {
return Err(WriteXdcContentsError(eyre::eyre!(
"annotation must be on a ModuleIO: {v:?}\nat: {}",
base.source_location(),
)));
}
LatticeAnnotation::XdcCreateClock(XdcCreateClockAnnotation {
period,
}) => {
let TargetName(ScopedNameId(_, NameId(name, _)), _) =
base.target_name();
writeln!(
self.output,
"create_clock -period {period} [get_nets {}]",
tcl_escape(name),
)?;
Ok(())
}
}
}
TargetBase::Instance(instance) => match todo(
"lattice annotations are not yet supported on instances' IO since the logic to figure out the correct name isn't implemented",
v,
instance.source_location(),
)? {},
}
}
}
}
} }
impl YosysNextpnrTrellisWriteXdcFile { impl YosysNextpnrWritePcfFile {
fn write_xdc_contents_for_port_and_annotations( fn write_pcf_contents_for_port_and_annotations(
&self, &self,
output: &mut impl fmt::Write, output: &mut impl fmt::Write,
port: &ScalarizedModuleABIPort, port: &ScalarizedModuleABIPort,
annotations: ScalarizedModuleABIAnnotations<'_>, annotations: ScalarizedModuleABIAnnotations<'_>,
) -> Result<(), WriteXdcContentsError> { ) -> Result<(), WritePcfContentsError> {
for annotation in annotations { /* fixme for annotation in annotations .. */
match annotation.annotation() {
Annotation::DontTouch(_)
| Annotation::SVAttribute(_)
| Annotation::BlackBoxInline(_)
| Annotation::BlackBoxPath(_)
| Annotation::DocString(_)
| Annotation::CustomFirrtl(_) => {}
Annotation::Lattice(LatticeAnnotation::XdcLocation(XdcLocationAnnotation {
location,
})) => writeln!(
output,
"set_property LOC {} [get_ports {}]",
tcl_escape(location),
tcl_escape(port.scalarized_name()),
)?,
Annotation::Lattice(LatticeAnnotation::XdcIOStandard(XdcIOStandardAnnotation {
value,
})) => writeln!(
output,
"set_property IOSTANDARD {} [get_ports {}]",
tcl_escape(value),
tcl_escape(port.scalarized_name()),
)?,
Annotation::Lattice(LatticeAnnotation::XdcCreateClock(
XdcCreateClockAnnotation { period },
)) => writeln!(
output,
"create_clock -period {period} [get_ports {}]",
tcl_escape(port.scalarized_name()),
)?,
}
}
Ok(()) Ok(())
} }
fn write_xdc_contents( fn write_pcf_contents(
&self, &self,
output: &mut String, output: &mut String,
top_module: &Module<Bundle>, top_module: &Module<Bundle>,
@ -649,7 +509,7 @@ impl YosysNextpnrTrellisWriteXdcFile {
ScalarizedModuleABI::new(top_module, self.firrtl_export_options) ScalarizedModuleABI::new(top_module, self.firrtl_export_options)
.map_err(eyre::Report::from)?; .map_err(eyre::Report::from)?;
match scalarized_module_abi.for_each_port_and_annotations(|port, annotations| { match scalarized_module_abi.for_each_port_and_annotations(|port, annotations| {
match self.write_xdc_contents_for_port_and_annotations(output, port, annotations) { match self.write_pcf_contents_for_port_and_annotations(output, port, annotations) {
Ok(()) => ControlFlow::Continue(()), Ok(()) => ControlFlow::Continue(()),
Err(e) => ControlFlow::Break(e), Err(e) => ControlFlow::Break(e),
} }
@ -657,14 +517,14 @@ impl YosysNextpnrTrellisWriteXdcFile {
ControlFlow::Continue(()) => {} ControlFlow::Continue(()) => {}
ControlFlow::Break(e) => return Err(e.0), ControlFlow::Break(e) => return Err(e.0),
} }
XdcFileWriter::run(output, *top_module).map_err(|e| e.0) PcfFileWriter::run(output, *top_module).map_err(|e| e.0)
} }
} }
impl JobKind for YosysNextpnrTrellisWriteXdcFileJobKind { impl JobKind for YosysNextpnrWritePcfFileJobKind {
type Args = YosysNextpnrTrellisWriteXdcFileArgs; type Args = YosysNextpnrWritePcfFileArgs;
type Job = YosysNextpnrTrellisWriteXdcFile; type Job = YosysNextpnrWritePcfFile;
type Dependencies = JobKindAndDependencies<ExternalCommandJobKind<YosysNextpnrTrellisSynth>>; type Dependencies = JobKindAndDependencies<ExternalCommandJobKind<YosysNextpnrSynth>>;
fn dependencies(self) -> Self::Dependencies { fn dependencies(self) -> Self::Dependencies {
Default::default() Default::default()
@ -685,12 +545,12 @@ impl JobKind for YosysNextpnrTrellisWriteXdcFileJobKind {
.args .args
.export_options; .export_options;
args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| { args.args_to_jobs_simple(params, global_params, |_kind, args, dependencies| {
let YosysNextpnrTrellisWriteXdcFileArgs {} = args; let YosysNextpnrWritePcfFileArgs {} = args;
let base_job = dependencies.get_job::<BaseJob, _>(); let base_job = dependencies.get_job::<BaseJob, _>();
Ok(YosysNextpnrTrellisWriteXdcFile { Ok(YosysNextpnrWritePcfFile {
firrtl_export_options, firrtl_export_options,
output_dir: base_job.output_dir(), output_dir: base_job.output_dir(),
xdc_file: base_job.file_with_ext("xdc"), pcf_file: base_job.file_with_ext("pcf"),
}) })
}) })
} }
@ -703,11 +563,11 @@ impl JobKind for YosysNextpnrTrellisWriteXdcFileJobKind {
} }
fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> { fn outputs(self, job: &Self::Job) -> Interned<[JobItemName]> {
[JobItemName::Path { path: job.xdc_file }].intern_slice() [JobItemName::Path { path: job.pcf_file }].intern_slice()
} }
fn name(self) -> Interned<str> { fn name(self) -> Interned<str> {
"yosys-nextpnr-trellis-write-xdc-file".intern() "yosys-nextpnr-ecp5-write-pcf-file".intern()
} }
fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> { fn external_command_params(self, _job: &Self::Job) -> Option<CommandParams> {
@ -723,10 +583,10 @@ impl JobKind for YosysNextpnrTrellisWriteXdcFileJobKind {
_acquired_job: &mut AcquiredJob, _acquired_job: &mut AcquiredJob,
) -> eyre::Result<Vec<JobItem>> { ) -> eyre::Result<Vec<JobItem>> {
assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job))); assert!(inputs.iter().map(JobItem::name).eq(self.inputs(job)));
let mut xdc = String::new(); let mut pcf = String::new();
job.write_xdc_contents(&mut xdc, params.main_module())?; job.write_pcf_contents(&mut pcf, params.main_module())?;
std::fs::write(job.xdc_file, xdc)?; std::fs::write(job.pcf_file, pcf)?;
Ok(vec![JobItem::Path { path: job.xdc_file }]) Ok(vec![JobItem::Path { path: job.pcf_file }])
} }
fn subcommand_hidden(self) -> bool { fn subcommand_hidden(self) -> bool {
@ -739,65 +599,49 @@ pub struct NextpnrLattice;
impl ExternalProgramTrait for NextpnrLattice { impl ExternalProgramTrait for NextpnrLattice {
fn default_program_name() -> Interned<str> { fn default_program_name() -> Interned<str> {
"nextpnr-lattice".intern() "nextpnr".intern()
} }
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
pub struct YosysNextpnrTrellisRunNextpnrArgs { pub struct YosysNextpnrRunNextpnrArgs {
#[command(flatten)] #[command(flatten)]
pub common: LatticeArgs, pub common: LatticeArgs,
#[arg(long, env = "CHIPDB_DIR", value_hint = clap::ValueHint::DirPath)]
pub nextpnr_lattice_chipdb_dir: PathBuf,
#[arg(long, default_value_t = 0)] #[arg(long, default_value_t = 0)]
pub nextpnr_lattice_seed: i32, pub nextpnr_lattice_seed: i32,
} }
impl ToArgs for YosysNextpnrTrellisRunNextpnrArgs { impl ToArgs for YosysNextpnrRunNextpnrArgs {
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
let Self { let Self {
common, common,
nextpnr_lattice_chipdb_dir,
nextpnr_lattice_seed, nextpnr_lattice_seed,
} = self; } = self;
common.to_args(args); common.to_args(args);
args.write_long_option_eq("nextpnr-lattice-chipdb-dir", nextpnr_lattice_chipdb_dir);
args.write_display_arg(format_args!("--nextpnr-lattice-seed={nextpnr_lattice_seed}")); args.write_display_arg(format_args!("--nextpnr-lattice-seed={nextpnr_lattice_seed}"));
} }
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct YosysNextpnrTrellisRunNextpnr { pub struct YosysNextpnrRunNextpnr {
nextpnr_lattice_chipdb_dir: Interned<Path>,
device: Device, device: Device,
nextpnr_lattice_seed: i32, nextpnr_lattice_seed: i32,
xdc_file: Interned<Path>, pcf_file: Interned<Path>,
xdc_file_name: Interned<OsStr>,
json_file: Interned<Path>, json_file: Interned<Path>,
json_file_name: Interned<OsStr>, json_file_name: Interned<OsStr>,
routed_json_file: Interned<Path>, routed_json_file: Interned<Path>,
routed_json_file_name: Interned<OsStr>, routed_json_file_name: Interned<OsStr>,
fasm_file: Interned<Path>, textcfg_file: Interned<Path>,
fasm_file_name: Interned<OsStr>, textcfg_file_name: Interned<OsStr>,
} }
impl YosysNextpnrTrellisRunNextpnr { impl ExternalCommand for YosysNextpnrRunNextpnr {
fn chipdb_file(&self) -> Interned<Path> { type AdditionalArgs = YosysNextpnrRunNextpnrArgs;
let mut retval = self
.nextpnr_lattice_chipdb_dir
.join(self.device.trellis_device());
retval.set_extension("bin");
retval.intern_deref()
}
}
impl ExternalCommand for YosysNextpnrTrellisRunNextpnr {
type AdditionalArgs = YosysNextpnrTrellisRunNextpnrArgs;
type AdditionalJobData = Self; type AdditionalJobData = Self;
type BaseJobPosition = GetJobPositionDependencies< type BaseJobPosition = GetJobPositionDependencies<
GetJobPositionDependencies<<YosysNextpnrTrellisSynth as ExternalCommand>::BaseJobPosition>, GetJobPositionDependencies<<YosysNextpnrSynth as ExternalCommand>::BaseJobPosition>,
>; >;
type Dependencies = JobKindAndDependencies<YosysNextpnrTrellisWriteXdcFileJobKind>; type Dependencies = JobKindAndDependencies<YosysNextpnrWritePcfFileJobKind>;
type ExternalProgram = NextpnrLattice; type ExternalProgram = NextpnrLattice;
fn dependencies() -> Self::Dependencies { fn dependencies() -> Self::Dependencies {
@ -813,35 +657,29 @@ impl ExternalCommand for YosysNextpnrTrellisRunNextpnr {
<Self::Dependencies as JobDependencies>::JobsAndKinds, <Self::Dependencies as JobDependencies>::JobsAndKinds,
)> { )> {
args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { args.args_to_jobs_external_simple(params, global_params, |args, dependencies| {
let YosysNextpnrTrellisRunNextpnrArgs { let YosysNextpnrRunNextpnrArgs {
common, common,
nextpnr_lattice_chipdb_dir,
nextpnr_lattice_seed, nextpnr_lattice_seed,
} = args.additional_args; } = args.additional_args;
let base_job = dependencies.get_job::<BaseJob, _>(); let base_job = dependencies.get_job::<BaseJob, _>();
let write_xdc_file = dependencies.get_job::<YosysNextpnrTrellisWriteXdcFile, _>(); let write_pcf_file = dependencies.get_job::<YosysNextpnrWritePcfFile, _>();
let synth = dependencies.get_job::<ExternalCommandJob<YosysNextpnrTrellisSynth>, _>(); let synth = dependencies.get_job::<ExternalCommandJob<YosysNextpnrSynth>, _>();
let routed_json_file = base_job.file_with_ext("routed.json"); let routed_json_file = base_job.file_with_ext("routed.json");
let fasm_file = base_job.file_with_ext("fasm"); let textcfg_file = base_job.file_with_ext("config"); //file must exist
Ok(Self { Ok(Self {
nextpnr_lattice_chipdb_dir: nextpnr_lattice_chipdb_dir.intern_deref(),
device: common.require_device(base_job.platform(), global_params)?, device: common.require_device(base_job.platform(), global_params)?,
nextpnr_lattice_seed, nextpnr_lattice_seed,
xdc_file: write_xdc_file.xdc_file, pcf_file: write_pcf_file.pcf_file,
xdc_file_name: write_xdc_file
.xdc_file
.interned_file_name()
.expect("known to have file name"),
json_file: synth.additional_job_data().json_file(), json_file: synth.additional_job_data().json_file(),
json_file_name: synth.additional_job_data().json_file_name(), json_file_name: synth.additional_job_data().json_file_name(),
routed_json_file, routed_json_file: routed_json_file,
routed_json_file_name: routed_json_file routed_json_file_name: routed_json_file
.interned_file_name() .interned_file_name()
.expect("known to have file name"), .expect("known to have file name"),
fasm_file, textcfg_file:textcfg_file,
fasm_file_name: fasm_file textcfg_file_name: textcfg_file
.interned_file_name() .interned_file_name()
.expect("known to have file name"), .expect("known to have file name"),
}) })
}) })
} }
@ -852,7 +690,7 @@ impl ExternalCommand for YosysNextpnrTrellisRunNextpnr {
path: job.additional_job_data().json_file, path: job.additional_job_data().json_file,
}, },
JobItemName::Path { JobItemName::Path {
path: job.additional_job_data().xdc_file, path: job.additional_job_data().pcf_file,
}, },
] ]
.intern_slice() .intern_slice()
@ -861,26 +699,26 @@ impl ExternalCommand for YosysNextpnrTrellisRunNextpnr {
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> { fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> {
[ [
job.additional_job_data().routed_json_file, job.additional_job_data().routed_json_file,
job.additional_job_data().fasm_file,
] ]
.intern_slice() .intern_slice()
} }
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) { fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
let job_data @ YosysNextpnrTrellisRunNextpnr { let job_data @ YosysNextpnrRunNextpnr {
nextpnr_lattice_seed, nextpnr_lattice_seed,
xdc_file_name,
json_file_name, json_file_name,
routed_json_file_name, routed_json_file_name,
fasm_file_name, textcfg_file_name,
.. ..
} = job.additional_job_data(); } = job.additional_job_data();
args.write_long_option_eq("chipdb", job_data.chipdb_file());
args.write_long_option_eq("xdc", xdc_file_name);
args.write_long_option_eq("json", json_file_name); args.write_long_option_eq("json", json_file_name);
args.write_long_option_eq("write", routed_json_file_name); args.write_long_option_eq("textcfg",textcfg_file_name);
args.write_long_option_eq("fasm", fasm_file_name); args.write_arg("--25k");
args.write_display_arg(format_args!("--seed={nextpnr_lattice_seed}")); args.write_long_option_eq("package","CSFBGA285");
args.write_long_option_eq("lpf","/tmp/orangecrab_r0.2.1.pcf");
args.write_arg("--lpf-allow-unconstrained");
//???args.write_display_arg(format_args!("--seed={nextpnr_lattice_seed}"));
} }
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>> { fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>> {
@ -888,7 +726,7 @@ impl ExternalCommand for YosysNextpnrTrellisRunNextpnr {
} }
fn job_kind_name() -> Interned<str> { fn job_kind_name() -> Interned<str> {
"yosys-nextpnr-trellis-run-nextpnr".intern() "yosys-nextpnr-ecp5-run-nextpnr".intern()
} }
fn subcommand_hidden() -> bool { fn subcommand_hidden() -> bool {
@ -896,62 +734,75 @@ impl ExternalCommand for YosysNextpnrTrellisRunNextpnr {
} }
} }
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct Xcfasm;
impl ExternalProgramTrait for Xcfasm {
fn default_program_name() -> Interned<str> {
"xcfasm".intern()
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)] #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
pub struct YosysNextpnrTrellisArgs { pub struct YosysNextpnrArgs {
#[arg(long, env = "DB_DIR", value_hint = clap::ValueHint::DirPath)] #[arg(long, env = "DB_DIR", value_hint = clap::ValueHint::DirPath)]
pub prjtrellis_db_dir: PathBuf, pub pcf1: PathBuf,
} }
impl ToArgs for YosysNextpnrTrellisArgs { impl ToArgs for YosysNextpnrArgs {
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) { fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
let Self { prjtrellis_db_dir } = self; let Self { pcf1 } = self;
args.write_long_option_eq("prjtrellis-db-dir", prjtrellis_db_dir); args.write_long_option_eq("pcf1", pcf1);
} }
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct YosysNextpnrTrellis { pub struct YosysNextpnr {
prjtrellis_db_dir: Interned<Path>, pcf1: Interned<Path>,
device: Device, device: Device,
fasm_file: Interned<Path>,
fasm_file_name: Interned<OsStr>,
frames_file: Interned<Path>, frames_file: Interned<Path>,
frames_file_name: Interned<OsStr>, frames_file_name: Interned<OsStr>,
bit_file: Interned<Path>, bit_file: Interned<Path>,
bit_file_name: Interned<OsStr>, bit_file_name: Interned<OsStr>,
} }
impl YosysNextpnrTrellis {
fn db_root(&self) -> Interned<Path> { #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
self.prjtrellis_db_dir pub struct Ecppack;
.join(self.device.trellis_family())
.intern_deref() impl ExternalProgramTrait for Ecppack {
} fn default_program_name() -> Interned<str> {
fn part_file(&self) -> Interned<Path> { "ecppack".intern()
let mut retval = self.prjtrellis_db_dir.join(self.device.trellis_family());
retval.push(self.device.trellis_part());
retval.push("part.yaml");
retval.intern_deref()
} }
} }
impl ExternalCommand for YosysNextpnrTrellis { //begin
type AdditionalArgs = YosysNextpnrTrellisArgs; #[derive(Clone, PartialEq, Eq, Hash, Debug, clap::Args)]
pub struct YosysNextpnrRunEcpPackArgs {
#[arg(long, env = "DB_DIR", value_hint = clap::ValueHint::DirPath)]
pub placeholder_dir: PathBuf,
}
impl ToArgs for YosysNextpnrRunEcpPackArgs {
fn to_args(&self, args: &mut (impl WriteArgs + ?Sized)) {
let Self { placeholder_dir } = self;
args.write_long_option_eq("placeholder-dir", placeholder_dir);
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct YosysNextpnrRunEcpPack {
placeholder_dir: Interned<Path>,
device: Device,
routed_json_file: Interned<Path>,
routed_json_file_name: Interned<OsStr>,
textcfg_file: Interned<Path>,
textcfg_file_name: Interned<OsStr>,
frames_file: Interned<Path>,
frames_file_name: Interned<OsStr>,
bit_file: Interned<Path>,
bit_file_name: Interned<OsStr>,
}
impl ExternalCommand for YosysNextpnrRunEcpPack {
type AdditionalArgs = YosysNextpnrRunEcpPackArgs;
type AdditionalJobData = Self; type AdditionalJobData = Self;
type BaseJobPosition = GetJobPositionDependencies< type BaseJobPosition = GetJobPositionDependencies<
<YosysNextpnrTrellisRunNextpnr as ExternalCommand>::BaseJobPosition, <YosysNextpnrRunNextpnr as ExternalCommand>::BaseJobPosition,
>; >;
type Dependencies = JobKindAndDependencies<ExternalCommandJobKind<YosysNextpnrTrellisRunNextpnr>>; type Dependencies = JobKindAndDependencies<ExternalCommandJobKind<YosysNextpnrRunNextpnr>>;
type ExternalProgram = Xcfasm; type ExternalProgram = Ecppack;
fn dependencies() -> Self::Dependencies { fn dependencies() -> Self::Dependencies {
Default::default() Default::default()
@ -966,15 +817,18 @@ impl ExternalCommand for YosysNextpnrTrellis {
<Self::Dependencies as JobDependencies>::JobsAndKinds, <Self::Dependencies as JobDependencies>::JobsAndKinds,
)> { )> {
args.args_to_jobs_external_simple(params, global_params, |args, dependencies| { args.args_to_jobs_external_simple(params, global_params, |args, dependencies| {
let YosysNextpnrTrellisArgs { prjtrellis_db_dir } = args.additional_args; let YosysNextpnrRunEcpPackArgs { placeholder_dir } = args.additional_args;
let base_job = dependencies.get_job::<BaseJob, _>(); let base_job = dependencies.get_job::<BaseJob, _>();
let frames_file = base_job.file_with_ext("frames"); let frames_file = base_job.file_with_ext("frames");
let bit_file = base_job.file_with_ext("bit"); let bit_file = base_job.file_with_ext("bit");
Ok(Self { Ok(Self {
prjtrellis_db_dir: prjtrellis_db_dir.intern_deref(), placeholder_dir: placeholder_dir.intern_deref(),
device: dependencies.job.job.additional_job_data().device, device: dependencies.job.job.additional_job_data().device,
fasm_file: dependencies.job.job.additional_job_data().fasm_file, //fixme glue code
fasm_file_name: dependencies.job.job.additional_job_data().fasm_file_name, routed_json_file: dependencies.job.job.additional_job_data().routed_json_file,
routed_json_file_name: dependencies.job.job.additional_job_data().routed_json_file_name,
textcfg_file: dependencies.job.job.additional_job_data().textcfg_file,
textcfg_file_name: dependencies.job.job.additional_job_data().textcfg_file_name,
frames_file, frames_file,
frames_file_name: frames_file frames_file_name: frames_file
.interned_file_name() .interned_file_name()
@ -989,34 +843,32 @@ impl ExternalCommand for YosysNextpnrTrellis {
fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]> { fn inputs(job: &ExternalCommandJob<Self>) -> Interned<[JobItemName]> {
[JobItemName::Path { [JobItemName::Path {
path: job.additional_job_data().fasm_file, path: job.additional_job_data().routed_json_file,
}] }]
.intern_slice() .intern_slice()
} }
fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> { fn output_paths(job: &ExternalCommandJob<Self>) -> Interned<[Interned<Path>]> {
[ [
job.additional_job_data().frames_file,
job.additional_job_data().bit_file, job.additional_job_data().bit_file,
] ]
.intern_slice() .intern_slice()
} }
fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) { fn command_line_args<W: ?Sized + WriteArgs>(job: &ExternalCommandJob<Self>, args: &mut W) {
let job_data @ YosysNextpnrTrellis { let job_data @ YosysNextpnrRunEcpPack {
placeholder_dir,
device, device,
fasm_file_name, routed_json_file_name,
textcfg_file_name,
frames_file_name, frames_file_name,
bit_file_name, bit_file_name,
.. ..
} = job.additional_job_data(); } = job.additional_job_data();
args.write_arg("--sparse"); args.write_arg("--compress");
args.write_long_option_eq("db-root", job_data.db_root()); args.write_long_option_eq("freq", "38.8"); //FIXME do not hardcode
args.write_long_option_eq("part", device.trellis_part()); args.write_long_option_eq("input",textcfg_file_name);
args.write_long_option_eq("part_file", job_data.part_file()); args.write_long_option_eq("bit",bit_file_name);
args.write_long_option_eq("fn_in", fasm_file_name);
args.write_long_option_eq("frm_out", frames_file_name);
args.write_long_option_eq("bit_out", bit_file_name);
} }
fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>> { fn current_dir(job: &ExternalCommandJob<Self>) -> Option<Interned<Path>> {
@ -1024,17 +876,18 @@ impl ExternalCommand for YosysNextpnrTrellis {
} }
fn job_kind_name() -> Interned<str> { fn job_kind_name() -> Interned<str> {
"yosys-nextpnr-trellis".intern() "yosys-nextpnr-ecp5".intern()
} }
} }
//end
pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> { pub(crate) fn built_in_job_kinds() -> impl IntoIterator<Item = DynJobKind> {
[ [
DynJobKind::new(YosysNextpnrTrellisWriteYsFileJobKind), DynJobKind::new(YosysNextpnrWriteYsFileJobKind), //working
DynJobKind::new(ExternalCommandJobKind::<YosysNextpnrTrellisSynth>::new()), DynJobKind::new(ExternalCommandJobKind::<YosysNextpnrSynth>::new()), //working
DynJobKind::new(YosysNextpnrTrellisWriteXdcFileJobKind), DynJobKind::new(YosysNextpnrWritePcfFileJobKind), //TODO
DynJobKind::new(ExternalCommandJobKind::<YosysNextpnrTrellisRunNextpnr>::new()), DynJobKind::new(ExternalCommandJobKind::<YosysNextpnrRunNextpnr>::new()), //working
DynJobKind::new(ExternalCommandJobKind::<YosysNextpnrTrellis>::new()), DynJobKind::new(ExternalCommandJobKind::<YosysNextpnrRunEcpPack>::new()), //working
] ]
} }

5
nextpnr.txt Normal file
View file

@ -0,0 +1,5 @@
nextpnr-ecp5 for orangecrab
nextpnr-ice40 for other one*
modified: Makefile
modified: crates/fayalite/src/vendor/lattice/yosys_nextpnr.rs

BIN
tools/firrtl.tar.gz Normal file

Binary file not shown.