forked from libre-chip/fayalite
WIP writing docs -- refactor #[hdl] docs to be a module tree for easier navigation
This commit is contained in:
parent
190e440b35
commit
153dc261e3
|
@ -15,4 +15,4 @@ jobs:
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/master' }}
|
save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||||
- run: cargo test
|
- run: cargo test
|
||||||
- run: cargo doc
|
- run: cargo doc --features=unstable-doc
|
||||||
|
|
|
@ -27,3 +27,9 @@ trybuild = { workspace = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
fayalite-visit-gen = { workspace = true }
|
fayalite-visit-gen = { workspace = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
unstable-doc = []
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["unstable-doc"]
|
29
crates/fayalite/src/_docs.rs
Normal file
29
crates/fayalite/src/_docs.rs
Normal file
|
@ -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};
|
19
crates/fayalite/src/_docs/modules.rs
Normal file
19
crates/fayalite/src/_docs/modules.rs
Normal file
|
@ -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;
|
19
crates/fayalite/src/_docs/modules/extern_module.rs
Normal file
19
crates/fayalite/src/_docs/modules/extern_module.rs
Normal file
|
@ -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;
|
8
crates/fayalite/src/_docs/modules/module_bodies.rs
Normal file
8
crates/fayalite/src/_docs/modules/module_bodies.rs
Normal file
|
@ -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;
|
|
@ -0,0 +1,3 @@
|
||||||
|
//! # `#[hdl] if` Statements
|
||||||
|
//!
|
||||||
|
//! FIXME
|
|
@ -0,0 +1,7 @@
|
||||||
|
//! ## `#[hdl] let` statements
|
||||||
|
|
||||||
|
pub mod inputs_outputs;
|
||||||
|
pub mod instances;
|
||||||
|
pub mod memories;
|
||||||
|
pub mod registers;
|
||||||
|
pub mod wires;
|
|
@ -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
|
|
@ -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();
|
||||||
|
//! // ...
|
||||||
|
//! }
|
||||||
|
//! # }
|
||||||
|
//! ```
|
|
@ -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};
|
|
@ -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
|
|
@ -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;
|
|
@ -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>
|
||||||
|
//! # }
|
||||||
|
//! ```
|
6
crates/fayalite/src/_docs/modules/normal_module.rs
Normal file
6
crates/fayalite/src/_docs/modules/normal_module.rs
Normal file
|
@ -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`]
|
7
crates/fayalite/src/_docs/semantics.rs
Normal file
7
crates/fayalite/src/_docs/semantics.rs
Normal file
|
@ -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;
|
81
crates/fayalite/src/_docs/semantics/connection_semantics.rs
Normal file
81
crates/fayalite/src/_docs/semantics/connection_semantics.rs
Normal file
|
@ -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;
|
|
@ -4,28 +4,7 @@
|
||||||
// TODO: enable:
|
// TODO: enable:
|
||||||
// #![warn(missing_docs)]
|
// #![warn(missing_docs)]
|
||||||
|
|
||||||
#![doc = include_str!("../README.md")]
|
//! [Main Documentation][_docs]
|
||||||
//!
|
|
||||||
//! # 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`
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
extern crate self as fayalite;
|
extern crate self as fayalite;
|
||||||
|
|
||||||
|
@ -39,237 +18,12 @@ pub use std as __std;
|
||||||
/// In the function body it will implicitly create a
|
/// In the function body it will implicitly create a
|
||||||
/// variable [`m: ModuleBuilder`][`module::ModuleBuilder`].
|
/// variable [`m: ModuleBuilder`][`module::ModuleBuilder`].
|
||||||
///
|
///
|
||||||
/// # Module Kinds
|
/// See [Fayalite Modules][crate::_docs::modules]
|
||||||
///
|
|
||||||
/// 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
|
|
||||||
pub use fayalite_proc_macros::hdl_module;
|
pub use fayalite_proc_macros::hdl_module;
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-doc")]
|
||||||
|
pub mod _docs;
|
||||||
|
|
||||||
pub mod annotations;
|
pub mod annotations;
|
||||||
pub mod array;
|
pub mod array;
|
||||||
pub mod bundle;
|
pub mod bundle;
|
||||||
|
|
Loading…
Reference in a new issue