From 153dc261e331d3266d8bb662da922219228f296d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 18 Jul 2024 00:13:28 -0700 Subject: [PATCH] WIP writing docs -- refactor #[hdl] docs to be a module tree for easier navigation --- .forgejo/workflows/test.yml | 2 +- crates/fayalite/Cargo.toml | 6 + crates/fayalite/src/_docs.rs | 29 ++ crates/fayalite/src/_docs/modules.rs | 19 ++ .../src/_docs/modules/extern_module.rs | 19 ++ .../src/_docs/modules/module_bodies.rs | 8 + .../module_bodies/hdl_if_statements.rs | 3 + .../module_bodies/hdl_let_statements.rs | 7 + .../hdl_let_statements/inputs_outputs.rs | 17 ++ .../hdl_let_statements/instances.rs | 23 ++ .../hdl_let_statements/memories.rs | 101 +++++++ .../hdl_let_statements/registers.rs | 25 ++ .../module_bodies/hdl_let_statements/wires.rs | 29 ++ .../modules/module_bodies/hdl_literals.rs | 17 ++ .../src/_docs/modules/normal_module.rs | 6 + crates/fayalite/src/_docs/semantics.rs | 7 + .../_docs/semantics/connection_semantics.rs | 81 ++++++ crates/fayalite/src/lib.rs | 256 +----------------- 18 files changed, 403 insertions(+), 252 deletions(-) create mode 100644 crates/fayalite/src/_docs.rs create mode 100644 crates/fayalite/src/_docs/modules.rs create mode 100644 crates/fayalite/src/_docs/modules/extern_module.rs create mode 100644 crates/fayalite/src/_docs/modules/module_bodies.rs create mode 100644 crates/fayalite/src/_docs/modules/module_bodies/hdl_if_statements.rs create mode 100644 crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs create mode 100644 crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/inputs_outputs.rs create mode 100644 crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/instances.rs create mode 100644 crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/memories.rs create mode 100644 crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs create mode 100644 crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/wires.rs create mode 100644 crates/fayalite/src/_docs/modules/module_bodies/hdl_literals.rs create mode 100644 crates/fayalite/src/_docs/modules/normal_module.rs create mode 100644 crates/fayalite/src/_docs/semantics.rs create mode 100644 crates/fayalite/src/_docs/semantics/connection_semantics.rs diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 503c5fe..6cc3727 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -15,4 +15,4 @@ jobs: with: save-if: ${{ github.ref == 'refs/heads/master' }} - run: cargo test - - run: cargo doc + - run: cargo doc --features=unstable-doc diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 4d3e289..e412fd2 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -27,3 +27,9 @@ trybuild = { workspace = true } [build-dependencies] fayalite-visit-gen = { workspace = true } + +[features] +unstable-doc = [] + +[package.metadata.docs.rs] +features = ["unstable-doc"] \ No newline at end of file diff --git a/crates/fayalite/src/_docs.rs b/crates/fayalite/src/_docs.rs new file mode 100644 index 0000000..f7020f9 --- /dev/null +++ b/crates/fayalite/src/_docs.rs @@ -0,0 +1,29 @@ +#![doc = include_str!("../README.md")] + +//! +//! # Organization +//! +//! All Fayalite-based designs are organized as one or more [modules][`module::Module`] +//! -- modules are created by writing a Rust function with the +//! [`#[hdl_module]` attribute][hdl_module]. You can then invoke the function to create a module. +//! You use the implicitly-added [`m: ModuleBuilder`][`module::ModuleBuilder`] variable in that +//! function to add inputs/outputs and other components to that module. +//! +//! ``` +//! # use fayalite::{hdl_module, int::UInt}; +//! # +//! #[hdl_module] +//! pub fn example_module() { +//! #[hdl] +//! let an_input: UInt<10> = m.input(); // create an input that is a 10-bit unsigned integer +//! #[hdl] +//! let some_output: UInt<10> = m.output(); +//! m.connect(some_output, an_input); // assigns the value of `an_input` to `some_output` +//! } +//! ``` + +pub mod modules; +pub mod semantics; + +#[allow(unused)] +use crate::{hdl_module, module}; diff --git a/crates/fayalite/src/_docs/modules.rs b/crates/fayalite/src/_docs/modules.rs new file mode 100644 index 0000000..c392f2e --- /dev/null +++ b/crates/fayalite/src/_docs/modules.rs @@ -0,0 +1,19 @@ +//! # Fayalite Modules +//! +//! The [`#[hdl_module]`][`crate::hdl_module`] attribute is applied to a Rust +//! function so that that function creates a [`Module`][`crate::module::Module`] when called. +//! In the function body it will implicitly create a +//! variable [`m: ModuleBuilder`][`crate::module::ModuleBuilder`]. +//! # Module Kinds +//! +//! There are two different kinds of modules: +//! +//! * [Normal modules][`normal_module`]. These are used for general Fayalite-based code. +//! * [Extern modules][`extern_module`]. These are for when you want to use modules written in +//! some other language, such as Verilog. +//! +//! See also: [Module Bodies][`module_bodies`] + +pub mod extern_module; +pub mod module_bodies; +pub mod normal_module; diff --git a/crates/fayalite/src/_docs/modules/extern_module.rs b/crates/fayalite/src/_docs/modules/extern_module.rs new file mode 100644 index 0000000..353cbe4 --- /dev/null +++ b/crates/fayalite/src/_docs/modules/extern_module.rs @@ -0,0 +1,19 @@ +//! These are for when you want to use modules written in +//! some other language, such as Verilog. +//! +//! You create an extern module by using an [`#[hdl_module(extern)]`][crate::hdl_module] attribute +//! on your module function. You then create [inputs/outputs] like for normal modules, then you +//! can set the verilog name and parameters using [`ModuleBuilder`] methods: +//! +//! * [`verilog_name()`][`ModuleBuilder::verilog_name`] +//! * [`parameter_int()`][`ModuleBuilder::parameter_int`] +//! * [`parameter_str()`][`ModuleBuilder::parameter_str`] +//! * [`parameter_raw_verilog()`][`ModuleBuilder::parameter_raw_verilog`] +//! * [`parameter()`][`ModuleBuilder::parameter`] +//! +//! These use the [`ExternModule`][`crate::module::ExternModule`] tag type. +//! +//! [inputs/outputs]: crate::_docs::modules::module_bodies::hdl_let_statements::inputs_outputs + +#[allow(unused)] +use crate::module::ModuleBuilder; diff --git a/crates/fayalite/src/_docs/modules/module_bodies.rs b/crates/fayalite/src/_docs/modules/module_bodies.rs new file mode 100644 index 0000000..3d69cba --- /dev/null +++ b/crates/fayalite/src/_docs/modules/module_bodies.rs @@ -0,0 +1,8 @@ +//! # Module Function Bodies +//! +//! The `#[hdl_module]` attribute lets you have statements/expressions with `#[hdl]` annotations +//! and `_hdl`-suffixed literals in the module function's body + +pub mod hdl_if_statements; +pub mod hdl_let_statements; +pub mod hdl_literals; diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_if_statements.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_if_statements.rs new file mode 100644 index 0000000..4410010 --- /dev/null +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_if_statements.rs @@ -0,0 +1,3 @@ +//! # `#[hdl] if` Statements +//! +//! FIXME diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs new file mode 100644 index 0000000..c4e3e70 --- /dev/null +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs @@ -0,0 +1,7 @@ +//! ## `#[hdl] let` statements + +pub mod inputs_outputs; +pub mod instances; +pub mod memories; +pub mod registers; +pub mod wires; diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/inputs_outputs.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/inputs_outputs.rs new file mode 100644 index 0000000..b070e71 --- /dev/null +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/inputs_outputs.rs @@ -0,0 +1,17 @@ +//! ### Inputs/Outputs +//! +//! Inputs/Outputs follow [connection semantics], which are unlike assignments in software, +//! so you should read it. +//! +//! ``` +//! # use fayalite::{hdl_module, int::UInt, array::Array}; +//! # #[hdl_module] +//! # fn module() { +//! #[hdl] +//! let my_input: UInt<10> = m.input(); +//! #[hdl] +//! let my_output: Array<[UInt<10>; 3]> = m.output(); +//! # } +//! ``` +//! +//! [connection semantics]: crate::_docs::semantics::connection_semantics diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/instances.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/instances.rs new file mode 100644 index 0000000..ab82a14 --- /dev/null +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/instances.rs @@ -0,0 +1,23 @@ +//! ### Module Instances +//! +//! module instances are kinda like the hardware equivalent of calling a function, +//! you can create them like so: +//! +//! ``` +//! # use fayalite::{hdl_module, int::UInt, array::Array}; +//! # #[hdl_module] +//! # fn module() { +//! #[hdl] +//! let my_instance = m.instance(some_module()); +//! // now you can use `my_instance`'s inputs/outputs like so: +//! #[hdl] +//! let v: UInt<3> = m.input(); +//! m.connect(my_instance.a, v); +//! #[hdl_module] +//! fn some_module() { +//! #[hdl] +//! let a: UInt<3> = m.input(); +//! // ... +//! } +//! # } +//! ``` diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/memories.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/memories.rs new file mode 100644 index 0000000..0f82e4f --- /dev/null +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/memories.rs @@ -0,0 +1,101 @@ +//! # Memories +//! +//! Memories are optimized for storing large amounts of data. +//! +//! When you create a memory, you get a [`MemBuilder`], which you +//! can then use to add memory ports, which is how you can read/write the memory. +//! +//! There are several different ways to create a memory: +//! +//! ## using [`ModuleBuilder::memory()`] +//! +//! This way you have to set the [`depth`][`MemBuilder::depth`] separately. +//! +//! ``` +//! # use fayalite::{hdl_module, int::UInt, clock::ClockDomain}; +//! # #[hdl_module] +//! # fn module() { +//! // first, we need some IO +//! #[hdl] +//! let cd: ClockDomain = m.input(); +//! #[hdl] +//! let read_addr: UInt<8> = m.input(); +//! #[hdl] +//! let read_data: UInt<8> = m.output(); +//! +//! // now create the memory +//! #[hdl] +//! let mut my_memory = m.memory(); +//! my_memory.depth(256); // the memory has 256 elements +//! +//! let read_port = my_memory.new_read_port(); +//! +//! // connect up the read port +//! m.connect_any(read_port.addr, read_addr); +//! m.connect(read_port.en, 1_hdl_u1); +//! m.connect(read_port.clk, cd.clk); +//! m.connect(read_data, read_port.data); +//! +//! // we need more IO for the write port +//! #[hdl] +//! let write_addr: UInt<8> = m.input(); +//! #[hdl] +//! let do_write: UInt<1> = m.input(); +//! #[hdl] +//! let write_data: UInt<8> = m.input(); +//! +//! let write_port = my_memory.new_write_port(); +//! +//! m.connect_any(write_port.addr, write_addr); +//! m.connect(write_port.en, do_write); +//! m.connect(write_port.clk, cd.clk); +//! m.connect(write_port.data, write_port.data); +//! m.connect(write_port.mask, 1_hdl_u1); +//! # } +//! ``` +//! +//! ## using [`ModuleBuilder::memory_array()`] +//! +//! this allows you to specify the memory's underlying array type directly. +//! +//! ``` +//! # use fayalite::{hdl_module, int::UInt, memory::MemBuilder}; +//! # #[hdl_module] +//! # fn module() { +//! #[hdl] +//! let mut my_memory: MemBuilder<[UInt<8>; 256]> = m.memory_array(); +//! +//! let read_port = my_memory.new_read_port(); +//! // ... +//! let write_port = my_memory.new_write_port(); +//! // ... +//! # } +//! ``` +//! +//! ## using [`ModuleBuilder::memory_with_init()`] +//! +//! This allows you to deduce the memory's array type from the data used to initialize the memory. +//! +//! ``` +//! # use fayalite::{hdl_module, int::UInt}; +//! # #[hdl_module] +//! # fn module() { +//! # #[hdl] +//! # let read_addr: UInt<2> = m.input(); +//! #[hdl] +//! let mut my_memory = m.memory_with_init( +//! #[hdl] +//! [0x12_hdl_u8, 0x34_hdl_u8, 0x56_hdl_u8, 0x78_hdl_u8], +//! ); +//! +//! let read_port = my_memory.new_read_port(); +//! // note that `read_addr` is `UInt<2>` since the memory only has 4 elements +//! m.connect_any(read_port.addr, read_addr); +//! // ... +//! let write_port = my_memory.new_write_port(); +//! // ... +//! # } +//! ``` + +#[allow(unused)] +use crate::{memory::MemBuilder, module::ModuleBuilder}; diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs new file mode 100644 index 0000000..2408372 --- /dev/null +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs @@ -0,0 +1,25 @@ +//! ### Registers +//! +//! Registers are memory devices that will change their state only on a clock +//! edge (or when being reset). They retain their state when not connected to. +//! +//! Registers follow [connection semantics], which are unlike assignments in software, so you should read it. +//! +//! ``` +//! # use fayalite::{hdl_module, int::UInt, array::Array, clock::ClockDomain}; +//! # #[hdl_module] +//! # fn module() { +//! # let v = true; +//! #[hdl] +//! let cd: ClockDomain = m.input(); +//! #[hdl] +//! let my_register: UInt<8> = m.reg_builder().clock_domain(cd).reset(8_hdl_u8); +//! #[hdl] +//! if v { +//! // my_register is only changed when both `v` is set and `cd`'s clock edge occurs. +//! m.connect(my_register, 0x45_hdl_u8); +//! } +//! # } +//! ``` +//! +//! [connection semantics]: crate::_docs::semantics::connection_semantics diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/wires.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/wires.rs new file mode 100644 index 0000000..d1616d8 --- /dev/null +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/wires.rs @@ -0,0 +1,29 @@ +//! ### Wires +//! +//! Wires are kinda like variables, but unlike registers, +//! they have no memory (they're combinatorial). +//! You must [connect][`ModuleBuilder::connect`] to all wires, so they have a defined value. +//! +//! Wires follow [connection semantics], which are unlike assignments in software, so you should read it. +//! +//! ``` +//! # use fayalite::{hdl_module, int::UInt, array::Array, clock::ClockDomain}; +//! # #[hdl_module] +//! # fn module() { +//! # let v = true; +//! #[hdl] +//! let cd: ClockDomain = m.input(); +//! #[hdl] +//! let my_register: UInt<8> = m.reg_builder().clock_domain(cd).reset(8_hdl_u8); +//! #[hdl] +//! if v { +//! // my_register is only changed when both `v` is set and `cd`'s clock edge occurs. +//! m.connect(my_register, 0x45_hdl_u8); +//! } +//! # } +//! ``` +//! +//! [connection semantics]: crate::_docs::semantics::connection_semantics + +#[allow(unused)] +use crate::module::ModuleBuilder; diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_literals.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_literals.rs new file mode 100644 index 0000000..0c12358 --- /dev/null +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_literals.rs @@ -0,0 +1,17 @@ +//! # `_hdl`-suffixed literals +//! +//! You can have integer literals with an arbitrary number of bits like so: +//! +//! ``` +//! # #[fayalite::hdl_module] +//! # fn module() { +//! let a = 0x1234_hdl_u14; // a UInt<14> with value 0x1234 +//! let b = 0x7_hdl_i3; // a SInt<3> with value 0x7 +//! let lf = b'\n'_hdl; // a UInt<8> with value b'\n' -- aka. 0x0A +//! let large_a = b'A'_hdl; // a UInt<8> with value b'A' -- aka. 0x41 +//! let n5 = -5_hdl_i4; // a SInt<4> with value -5 +//! let n1 = -1_hdl_i200; // a SInt<200> with value -1 +//! let v = 0xfedcba9876543210_fedcba9876543210_fedcba9876543210_hdl_u192; // a UInt<192> +//! let empty = 0_hdl_u0; // a UInt<0> +//! # } +//! ``` diff --git a/crates/fayalite/src/_docs/modules/normal_module.rs b/crates/fayalite/src/_docs/modules/normal_module.rs new file mode 100644 index 0000000..7fb6c09 --- /dev/null +++ b/crates/fayalite/src/_docs/modules/normal_module.rs @@ -0,0 +1,6 @@ +//! # Normal Modules +//! +//! These use the [`NormalModule`][`crate::module::NormalModule`] tag type. +//! +//! See also: [Extern Modules][`super::extern_module`] +//! See also: [Module Bodies][`super::module_bodies`] diff --git a/crates/fayalite/src/_docs/semantics.rs b/crates/fayalite/src/_docs/semantics.rs new file mode 100644 index 0000000..a499e8e --- /dev/null +++ b/crates/fayalite/src/_docs/semantics.rs @@ -0,0 +1,7 @@ +//! # Fayalite Semantics +//! +//! Fayalite's semantics are based on [FIRRTL]. Due to their significance, some of the semantics are also documented here. +//! +//! [FIRRTL]: https://github.com/chipsalliance/firrtl-spec + +pub mod connection_semantics; diff --git a/crates/fayalite/src/_docs/semantics/connection_semantics.rs b/crates/fayalite/src/_docs/semantics/connection_semantics.rs new file mode 100644 index 0000000..ef30329 --- /dev/null +++ b/crates/fayalite/src/_docs/semantics/connection_semantics.rs @@ -0,0 +1,81 @@ +//! # Connection Semantics +//! +//! Fayalite's connection semantics are unlike assignments in software, so be careful! +//! +//! Fayalite's connection semantics follow [FIRRTL]'s Last-Connect-Semantics and +//! Conditional-Last-Connect-Semantics: +//! +//! Basically, every [wire] behaves as if you ran all connections in a module, and everywhere +//! the wire is read from, it takes on the value it has at the end of the module, with every +//! connect (except those in any kind of [conditional block] where the condition doesn't hold, +//! such as an [`#[hdl] if`][if] with a false condition). +//! overwriting the appropriate portion of the wire. +//! +//! Any other things that are connected to (on the LHS of a +//! [`connect()`] or [`connect_any()`] call) have analogous connection semantics. +//! +//! This description is intended to match [FIRRTL]'s description, so if they conflict with +//! each other, please report it as a bug in Fayalite. +//! +//! Connection Semantics Example: +//! +//! ``` +//! # use fayalite::module_hdl; +//! # #[module_hdl] +//! # fn module() { +//! #[hdl] +//! let a: UInt<8> = m.wire(); +//! #[hdl] +//! let b: UInt<8> = m.output(); +//! +//! // doesn't actually affect anything, since `a` is completely overwritten later +//! m.connect(a, 5_hdl_u8); +//! +//! // here `a` has value `7` since the last connection assigns +//! // `7` to `a`, so `b` has value `7` too. +//! m.connect(b, a); +//! +//! // this is the last `connect` to `a`, so this `connect` determines `a`'s value +//! m.connect(a, 7_hdl_u8); +//! # } +//! ``` +//! +//! # Conditional Connection Semantics +//! +//! ``` +//! # use fayalite::module_hdl; +//! # #[module_hdl] +//! # fn module() { +//! #[hdl] +//! let cond: UInt<1> = m.input(); +//! #[hdl] +//! let a: UInt<8> = m.wire(); +//! #[hdl] +//! let b: UInt<8> = m.output(); +//! +//! // this is the last `connect` to `a` when `cond` is `0` +//! m.connect(a, 5_hdl_u8); +//! +//! // here `a` has value `7` if `cond` is `1` since the last connection assigns +//! // `7` to `a`, so `b` has value `7` too, otherwise `a` (and therefore `b`) +//! // have value `5` since then the connection assigning `7` is in a +//! // conditional block where the condition doesn't hold. +//! m.connect(b, a); +//! +//! #[hdl] +//! if cond { +//! // this is the last `connect` to `a` when `cond` is `1` +//! m.connect(a, 7_hdl_u8); +//! } +//! # } +//! ``` +//! +//! [conditional block]: self#conditional-connection-semantics +//! [`connect()`]: ModuleBuilder::connect +//! [`connect_any()`]: ModuleBuilder::connect_any +//! [wire]: crate::_docs::modules::module_bodies::hdl_let_statements::wires +//! [if]: crate::_docs::modules::module_bodies::hdl_if_statements +//! [FIRRTL]: https://github.com/chipsalliance/firrtl-spec + +#[allow(unused)] +use crate::module::ModuleBuilder; diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index b2e9dc0..15828ab 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -4,28 +4,7 @@ // TODO: enable: // #![warn(missing_docs)] -#![doc = include_str!("../README.md")] -//! -//! # Organization -//! -//! All Fayalite-based designs are organized as one or more [modules][`module::Module`] -//! -- modules are created by writing a Rust function with the -//! [`#[hdl_module]` attribute][hdl_module]. You can then invoke the function to create a module. -//! You use the implicitly-added [`m: ModuleBuilder`][`module::ModuleBuilder`] variable in that -//! function to add inputs/outputs and other components to that module. -//! -//! ``` -//! # use fayalite::{hdl_module, int::UInt}; -//! # -//! #[hdl_module] -//! pub fn example_module() { -//! #[hdl] -//! let an_input: UInt<10> = m.input(); // create an input that is a 10-bit unsigned integer -//! #[hdl] -//! let some_output: UInt<10> = m.output(); -//! m.connect(some_output, an_input); // assigns the value of `an_input` to `some_output` -//! } -//! ``` +//! [Main Documentation][_docs] extern crate self as fayalite; @@ -39,237 +18,12 @@ pub use std as __std; /// In the function body it will implicitly create a /// variable [`m: ModuleBuilder`][`module::ModuleBuilder`]. /// -/// # Module Kinds -/// -/// There are two different kinds of modules: -/// -/// * Normal modules. These are used for general Fayalite-based code. -/// These use the [`NormalModule`][`module::NormalModule`] tag type. -/// * Extern modules. These are for when you want to use modules written in -/// some other language, such as Verilog. -/// You create an extern module by instead using an `#[hdl_module(extern)]` attribute on your -/// module function. You then create inputs/outputs like for normal modules, then you can set -/// the verilog name and parameters using [`ModuleBuilder`][`module::ModuleBuilder`] methods: -/// -/// * [`verilog_name()`][`module::ModuleBuilder::verilog_name`] -/// * [`parameter_int()`][`module::ModuleBuilder::parameter_int`] -/// * [`parameter_str()`][`module::ModuleBuilder::parameter_str`] -/// * [`parameter_raw_verilog()`][`module::ModuleBuilder::parameter_raw_verilog`] -/// * [`parameter()`][`module::ModuleBuilder::parameter`] -/// -/// These use the [`ExternModule`][`module::ExternModule`] tag type. -/// -/// # Module Function Bodies -/// -/// The `#[hdl_module]` attribute lets you have statements/expressions with `#[hdl]` annotations -/// and `_hdl_` integer literals in the function body: -/// -/// ## `_hdl_` integer literals -/// -/// You can have integer literals with an arbitrary number of bits like so: -/// -/// ``` -/// # #[fayalite::hdl_module] -/// # fn module() { -/// let a = 0x1234_hdl_u14; // a UInt<14> with value 0x1234 -/// let b = 0x7_hdl_i3; // a SInt<3> with value 0x7 -/// let lf = b'\n'_hdl; // a UInt<8> with value b'\n' -- aka. 0x0A -/// let large_a = b'A'_hdl; // a UInt<8> with value b'A' -- aka. 0x41 -/// let n5 = -5_hdl_i4; // a SInt<4> with value -5 -/// let n1 = -1_hdl_i200; // a SInt<200> with value -1 -/// let v = 0xfedcba9876543210_fedcba9876543210_fedcba9876543210_hdl_u192; // a UInt<192> -/// let empty = 0_hdl_u0; // a UInt<0> -/// # } -/// ``` -/// -/// ## `#[hdl] let` statements -/// -/// ### Inputs/Outputs -/// -/// ``` -/// # use fayalite::{hdl_module, int::UInt, array::Array}; -/// # #[hdl_module] -/// # fn module() { -/// #[hdl] -/// let my_input: UInt<10> = m.input(); -/// #[hdl] -/// let my_output: Array<[UInt<10>; 3]> = m.output(); -/// # } -/// ``` -/// -/// ### Module Instances -/// -/// module instances are kinda like the hardware equivalent of calling a function, -/// you can create them like so: -/// -/// ``` -/// # use fayalite::{hdl_module, int::UInt, array::Array}; -/// # #[hdl_module] -/// # fn module() { -/// #[hdl] -/// let my_instance = m.instance(some_module()); -/// // now you can use `my_instance`'s inputs/outputs like so: -/// #[hdl] -/// let v: UInt<3> = m.input(); -/// m.connect(my_instance.a, v); -/// #[hdl_module] -/// fn some_module() { -/// #[hdl] -/// let a: UInt<3> = m.input(); -/// // ... -/// } -/// # } -/// ``` -/// -/// ### Registers -/// -/// Registers are memory devices that will change their state only on a clock -/// edge (or when being reset). They retain their state when not connected to. -/// -/// ``` -/// # use fayalite::{hdl_module, int::UInt, array::Array, clock::ClockDomain}; -/// # #[hdl_module] -/// # fn module() { -/// # let v = true; -/// #[hdl] -/// let cd: ClockDomain = m.input(); -/// #[hdl] -/// let my_register: UInt<8> = m.reg_builder().clock_domain(cd).reset(8_hdl_u8); -/// #[hdl] -/// if v { -/// // my_register is only changed when both `v` is set and `cd`'s clock edge occurs. -/// m.connect(my_register, 0x45_hdl_u8); -/// } -/// # } -/// ``` -/// -/// ### Wires -/// -/// Wires are kinda like variables, but unlike registers, -/// they have no memory (they're combinatorial). -/// You must [connect][`module::ModuleBuilder::connect`] to all wires, so they have a defined value. -/// -/// ``` -/// # use fayalite::{hdl_module, int::UInt, array::Array, clock::ClockDomain}; -/// # #[hdl_module] -/// # fn module() { -/// # let v = true; -/// #[hdl] -/// let cd: ClockDomain = m.input(); -/// #[hdl] -/// let my_register: UInt<8> = m.reg_builder().clock_domain(cd).reset(8_hdl_u8); -/// #[hdl] -/// if v { -/// // my_register is only changed when both `v` is set and `cd`'s clock edge occurs. -/// m.connect(my_register, 0x45_hdl_u8); -/// } -/// # } -/// ``` -/// -/// ### Memories -/// -/// Memories are optimized for storing large amounts of data. -/// -/// When you create a memory, you get a [`MemBuilder`][`memory::MemBuilder`], which you -/// can then use to add memory ports, which is how you can read/write the memory. -/// -/// There are several different ways to create a memory: -/// -/// ### using [`ModuleBuilder::memory()`][`module::ModuleBuilder::memory`] -/// -/// This way you have to set the [`depth`][`memory::MemBuilder::depth`] separately. -/// -/// ``` -/// # use fayalite::{hdl_module, int::UInt, clock::ClockDomain}; -/// # #[hdl_module] -/// # fn module() { -/// // first, we need some IO -/// #[hdl] -/// let cd: ClockDomain = m.input(); -/// #[hdl] -/// let read_addr: UInt<8> = m.input(); -/// #[hdl] -/// let read_data: UInt<8> = m.output(); -/// -/// // now create the memory -/// #[hdl] -/// let mut my_memory = m.memory(); -/// my_memory.depth(256); // the memory has 256 elements -/// -/// let read_port = my_memory.new_read_port(); -/// -/// // connect up the read port -/// m.connect_any(read_port.addr, read_addr); -/// m.connect(read_port.en, 1_hdl_u1); -/// m.connect(read_port.clk, cd.clk); -/// m.connect(read_data, read_port.data); -/// -/// // we need more IO for the write port -/// #[hdl] -/// let write_addr: UInt<8> = m.input(); -/// #[hdl] -/// let do_write: UInt<1> = m.input(); -/// #[hdl] -/// let write_data: UInt<8> = m.input(); -/// -/// let write_port = my_memory.new_write_port(); -/// -/// m.connect_any(write_port.addr, write_addr); -/// m.connect(write_port.en, do_write); -/// m.connect(write_port.clk, cd.clk); -/// m.connect(write_port.data, write_port.data); -/// m.connect(write_port.mask, 1_hdl_u1); -/// # } -/// ``` -/// -/// ### using [`ModuleBuilder::memory_array()`][`module::ModuleBuilder::memory_array`] -/// -/// this allows you to specify the memory's underlying array type directly. -/// -/// ``` -/// # use fayalite::{hdl_module, int::UInt, memory::MemBuilder}; -/// # #[hdl_module] -/// # fn module() { -/// #[hdl] -/// let mut my_memory: MemBuilder<[UInt<8>; 256]> = m.memory_array(); -/// -/// let read_port = my_memory.new_read_port(); -/// // ... -/// let write_port = my_memory.new_write_port(); -/// // ... -/// # } -/// ``` -/// -/// ### using [`ModuleBuilder::memory_with_init()`][`module::ModuleBuilder::memory_with_init`] -/// -/// This allows you to deduce the memory's array type from the data used to initialize the memory. -/// -/// ``` -/// # use fayalite::{hdl_module, int::UInt}; -/// # #[hdl_module] -/// # fn module() { -/// # #[hdl] -/// # let read_addr: UInt<2> = m.input(); -/// #[hdl] -/// let mut my_memory = m.memory_with_init( -/// #[hdl] -/// [0x12_hdl_u8, 0x34_hdl_u8, 0x56_hdl_u8, 0x78_hdl_u8], -/// ); -/// -/// let read_port = my_memory.new_read_port(); -/// // note that `read_addr` is `UInt<2>` since the memory only has 4 elements -/// m.connect_any(read_port.addr, read_addr); -/// // ... -/// let write_port = my_memory.new_write_port(); -/// // ... -/// # } -/// ``` -/// -/// # `#[hdl]` expressions/statements: -/// -/// FIXME: finish writing +/// See [Fayalite Modules][crate::_docs::modules] pub use fayalite_proc_macros::hdl_module; +#[cfg(feature = "unstable-doc")] +pub mod _docs; + pub mod annotations; pub mod array; pub mod bundle;