WIP adding docs
All checks were successful
/ test (push) Successful in 9m6s

This commit is contained in:
Jacob Lifshay 2024-07-16 19:46:52 -07:00
parent 86827def91
commit 190e440b35
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
5 changed files with 277 additions and 0 deletions

View file

@ -0,0 +1,5 @@
# Fayalite
Fayalite is a library for designing digital hardware -- a hardware description language (HDL) embedded in the Rust programming language. Fayalite's semantics are based on [FIRRTL] as interpreted by [LLVM CIRCT](https://circt.llvm.org/docs/Dialects/FIRRTL/FIRRTLAnnotations/).
[FIRRTL]: https://github.com/chipsalliance/firrtl-spec

View file

@ -1,5 +1,10 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
//! proc macros for `fayalite`
//!
//! see `fayalite::hdl_module` and `fayalite::ty::Value` for docs
// intentionally not documented here, see `fayalite::hdl_module` for docs
#[proc_macro_attribute]
pub fn hdl_module(
attr: proc_macro::TokenStream,
@ -11,6 +16,7 @@ pub fn hdl_module(
}
}
// intentionally not documented here, see `fayalite::ty::Value` for docs
#[proc_macro_derive(Value, attributes(hdl))]
pub fn value_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
match fayalite_proc_macros_impl::value_derive(item.into()) {

View file

@ -1,10 +1,273 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
// 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`
//! }
//! ```
extern crate self as fayalite;
#[doc(hidden)]
pub use std as __std;
#[doc(inline)]
#[doc(alias = "hdl")]
/// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
/// a [`Module`][`::fayalite::module::Module`] when called.
/// 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
pub use fayalite_proc_macros::hdl_module;
pub mod annotations;

View file

@ -1108,6 +1108,8 @@ impl ModuleBody {
}
}
/// The runtime representation of a Fayalite module. The preferred way to create a [`Module`] is by
/// calling a function annotated with the [`#[hdl_module]`][`crate::hdl_module`] attribute.
#[derive(PartialEq, Eq, Hash)]
pub struct Module<T: BundleValue>
where

View file

@ -28,6 +28,7 @@ use std::{
sync::Arc,
};
#[doc(inline)]
pub use fayalite_proc_macros::Value;
mod sealed {