Skip to content

Add Gowin Tangnano9k support #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"rust-hdl-ok-core",
"rust-hdl-fpga-support",
"rust-hdl-bsp-alchitry-cu",
"rust-hdl-bsp-tangnano9k",
"rust-hdl-bsp-ok-xem6010",
"rust-hdl-bsp-ok-xem7010",
"rust-hdl-x",
Expand Down
16 changes: 16 additions & 0 deletions rust-hdl-bsp-gowin/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "rust-hdl-bsp-gowin"
version = "0.46.0"
edition = "2024"
license = "MIT"
description = "Support crate for RustHDL - provides Board Support Package for the Gowin Tangnano9k board"
homepage = "https://github.com/samitbasu/rust-hdl"
repository = "https://github.com/samitbasu/rust-hdl"
keywords = ["fpga", "verilog", "hardware"]
authors = ["Samit Basu <[email protected]>"]


# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rust-hdl = { version = "0.46.0", path = "../rust-hdl", features = ["fpga"] }
1 change: 1 addition & 0 deletions rust-hdl-bsp-gowin/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod tangnano9k;
2 changes: 2 additions & 0 deletions rust-hdl-bsp-gowin/src/tangnano9k/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod pins;
pub mod synth;
31 changes: 31 additions & 0 deletions rust-hdl-bsp-gowin/src/tangnano9k/pins.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use rust_hdl::core::prelude::*;

pub const CLOCK_SPEED_27MHZ: u64 = 27_000_000;

pub fn clock() -> Signal<In, Clock> {
let mut signal = Signal::default();
signal.add_location(0, "52");
// signal.add_signal_type(0, SignalType::LowVoltageCMOS_3v3);
signal.connect();
signal
}

pub fn lamp() -> Signal<Out, Bits<6>> {
let mut signal = Signal::default();
let locs = ["10", "11", "13", "14", "15", "16"];
for (i, loc) in locs.iter().enumerate() {
signal.add_location(i, loc);
// signal.add_signal_type(i, SignalType::LowVoltageCMOS_3v3);
}
signal
}

pub fn button() -> Signal<In, Bits<2>> {
let mut signal = Signal::default();
let locs = ["3", "4"];
for (i, loc) in locs.iter().enumerate() {
signal.add_location(i, loc);
// signal.add_signal_type(i, SignalType::LowVoltageCMOS_3v3);
}
signal
}
78 changes: 78 additions & 0 deletions rust-hdl-bsp-gowin/src/tangnano9k/synth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use std::{
fs::{create_dir_all, File},
io::Write,
path::PathBuf,
process::{Command, Output},
str::FromStr,
};

use rust_hdl::core::{check_error::check_all, prelude::*};
use rust_hdl::fpga::toolchains::gowin::generate_cst;

fn save_stdout(output: Output, dir: &PathBuf, basename: &str) -> Result<(), std::io::Error> {
let stdout = String::from_utf8(output.stdout).unwrap();
let stderr = String::from_utf8(output.stderr).unwrap();
let mut out_file = File::create(dir.clone().join(format!("{}.out", basename)))?;
out_file.write_all(stdout.as_bytes())?;
let mut err_file = File::create(dir.clone().join(format!("{}.err", basename)))?;
err_file.write_all(stderr.as_bytes())?;
Ok(())
}

pub fn generate_bitstream<U: Block>(mut uut: U, download: bool, prefix: &str, yosys: &str) {
uut.connect_all();
check_all(&uut).unwrap();

let verilog_text = generate_verilog(&uut);
let cst_text = generate_cst(&uut);
let dir = PathBuf::from_str(prefix).unwrap();

create_dir_all(&dir).unwrap();

let mut v_file = File::create(dir.join("top.v")).unwrap();
write!(v_file, "{}", verilog_text).unwrap();
let mut cst_file = File::create(dir.join("top.cst")).unwrap();
write!(cst_file, "{}", cst_text).unwrap();

let output = Command::new(format!("{yosys}/yosys"))
.current_dir(dir.clone())
.arg("-p")
.arg("read_verilog top.v; synth_gowin -json top.json")
.output()
.unwrap();
save_stdout(output, &dir, "yosys").unwrap();

let output = Command::new(format!("{yosys}/nextpnr-himbaechel"))
.current_dir(dir.clone())
.args([
"--json",
"top.json",
"--write",
"top.pnr.json",
"--device",
"GW1NR-LV9QN88PC6/I5",
"--vopt",
"family=GW1N-9C",
"--vopt",
"cst=top.cst",
])
.output()
.unwrap();
save_stdout(output, &dir, "nextpnr").unwrap();

let output = Command::new(format!("{yosys}/gowin_pack"))
.current_dir(dir.clone())
.args(["-d", "GW1N-9C", "-o", "top.fs", "top.pnr.json"])
.output()
.unwrap();
save_stdout(output, &dir, "pack").unwrap();

if download {
let output = Command::new(format!("{yosys}/openFPGALoader"))
.current_dir(dir.clone())
.args(["-b", "tangnano9k", "-f", "top.fs"])
.output()
.unwrap();
save_stdout(output, &dir, "loader").unwrap();
}
}
46 changes: 46 additions & 0 deletions rust-hdl-bsp-gowin/test/bsp_tangnano9k.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use rust_hdl::prelude::*;
use rust_hdl_bsp_gowin::tangnano9k::{pins, synth};

#[derive(LogicBlock)]
pub struct Blinky {
pulser: Pulser,
clock: Signal<In, Clock>,
lamp: Signal<Out, Bits<6>>,
}

impl Default for Blinky {
fn default() -> Self {
Blinky {
pulser: Pulser::new(
pins::CLOCK_SPEED_27MHZ,
1.0,
std::time::Duration::from_millis(250),
),
clock: pins::clock(),
lamp: pins::lamp(),
}
}
}

impl Logic for Blinky {
#[hdl_gen]
fn update(&mut self) {
self.pulser.enable.next = true;
self.pulser.clock.next = self.clock.val();

self.lamp.next = 0.into();
if self.pulser.pulse.val() {
self.lamp.next = 0xff.into();
}
}
}

#[test]
fn main() {
synth::generate_bitstream(
Blinky::default(),
true, // auto download
target_path!("tangnano9k/blink"),
"", // yosys(or oss-cad-suite) path, "" or same "/home/xxx/software/oss-cad-suite/bin"
);
}
69 changes: 69 additions & 0 deletions rust-hdl-fpga-support/src/toolchains/gowin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use rust_hdl_core::prelude::*;

use super::map_signal_type_to_gowin_string;

#[derive(Default)]
struct CSTGenerator {
path: NamedPath,
namespace: NamedPath,
cst: Vec<String>,
}

impl Probe for CSTGenerator {
fn visit_start_scope(&mut self, name: &str, _node: &dyn Block) {
self.path.push(name);
self.namespace.reset();
}

fn visit_start_namespace(&mut self, name: &str, _node: &dyn Block) {
self.namespace.push(name);
}

fn visit_atom(&mut self, name: &str, signal: &dyn Atom) {
if self.path.len() == 1 {
let namespace = self.namespace.flat("$");
let name = if namespace.is_empty() {
name.to_owned()
} else {
format!("{}${}", namespace, name)
};

for pin in &signal.constraints() {
let prefix = if signal.bits() == 1 {
format!("{}", name)
} else {
format!("{}[{}]", name, pin.index)
};

match &pin.constraint {
Constraint::Location(l) => {
self.cst.push(format!("IO_LOC \"{}\" {};", prefix, l));
}
Constraint::Kind(k) => {
let name = map_signal_type_to_gowin_string(k);
self.cst
.push(format!("IO_PORT \"{}\" IO_TYPE={};", prefix, name))
}
Constraint::Custom(s) => self.cst.push(s.clone()),
_ => {
panic!("Pin constraint type {:?} is unsupported!", pin.constraint)
}
}
}
}
}

fn visit_end_namespace(&mut self, _name: &str, _node: &dyn Block) {
self.namespace.pop();
}

fn visit_end_scope(&mut self, _name: &str, _node: &dyn Block) {
self.path.pop();
}
}

pub fn generate_cst<U: Block>(uut: &U) -> String {
let mut cst = CSTGenerator::default();
uut.accept("top", &mut cst);
cst.cst.join("\n") + "\n"
}
18 changes: 18 additions & 0 deletions rust-hdl-fpga-support/src/toolchains/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ pub fn map_signal_type_to_lattice_string(k: &SignalType) -> &str {
}
}

pub fn map_signal_type_to_gowin_string(k: &SignalType) -> &str {
match k {
SignalType::LowVoltageCMOS_1v8 => "LVCMOS18",
SignalType::LowVoltageCMOS_3v3 => "LVCMOS33",
SignalType::StubSeriesTerminatedLogic_II => "SSTL18_II",
SignalType::DifferentialStubSeriesTerminatedLogic_II => "DIFF_SSTL18_II",
SignalType::StubSeriesTerminatedLogic_II_No_Termination => "SSTL18_II | IN_TERM=NONE",
SignalType::DifferentialStubSeriesTerminatedLogic_II_No_Termination => {
"DIFF_SSTL18_II | IN_TERM=NONE"
}
SignalType::Custom(c) => c,
SignalType::LowVoltageDifferentialSignal_2v5 => "LVDS_25",
SignalType::StubSeriesTerminatedLogic_1v5 => "SSTL15",
SignalType::LowVoltageCMOS_1v5 => "LVCMOS15",
SignalType::DifferentialStubSeriesTerminatedLogic_1v5 => "DIFF_SSTL15",
}
}

pub fn map_signal_type_to_xilinx_string(k: &SignalType) -> &str {
match k {
SignalType::LowVoltageCMOS_1v8 => "LVCMOS18",
Expand Down