CircuitModule(fn; kwargs...)

A data structure to store information to generate hardware for fn.

Hardware generation traverses dfg and uses handlers to generate Verilog strings.


  • name::Symbol: the name of the module (defaults to nameof(fn))
  • bitwidth::@NamedTuple{integral::Int, fractional::Int}: the maximum bit width in the circuit
  • parameters::Dict{String, Number}: a map from the name of each parameter to its default value
  • submodules::Vector{Type}: a list of submodule types
  • dfg::MetaDiGraph{Int, Float64}: a data flow graph representing the circuit to be generated
  • handlers::Dict{Operation, AbstractHandler}: a map from operation type to a hardware generation handler and state
Net(; name = "", suffixes = [""], value = missing,
      type = :wire, class = :internal, signed = false,
      bitwidth = 1, size = (1, 1))
Net(x; kwargs...)

Create a new multi-dimensional SystemVerilog net (for x).


  • name::String: the name of the net (this can be a SystemVerilog constant expression too)
  • suffixes::Vector{String}: set if this net represents multiple wires with suffixes (e.g. a signed stochastic bitstream has suffixes _p and _m)
  • value: the underlying Julia value that this net represents
  • type::Symbol: one of :logic
  • class::Symbol: one of [:input, :output, :internal, :constant, :parameter]
  • signed::Bool: set true if the net is signed in SystemVerilog
  • bitwidth::Int: the bit width of each element in the net
  • size::Tuple{Int, Int, ...}: the size of the net in terms of number of elements

If x is specified, then value = x and size = netsize(x). netsize is like Base.size, but it expands scalars and vectors to 2D sizes (e.g. (1, 1)) If x is a SBitstream or AbstractArray{<:SBitstream}, then suffixes = ["_p", "_m"].


A stochastic bitstream that represents a real (floating-point) number between [-1, 1].


  • bits::Vector{SBit}: the underlying bitstream
  • value::Float64: the underlying floating-point number being represented

Used by BitSAD's simulation engine to track bit-level returns and previously transformed calls.


  • popmap: a map from the original call to the simulated bit computation
  • opmap: a "reverse" map from a function + arguments to returned Ghost.Variable

Return the bitwidth of elements of x

estimate!(buffer::AbstractVector, b::SBit)
estimate!(buffer::AbstractVector, b::VecOrMat{SBit})

Push b into the buffer and return the current estimate.


Get the empirical mean of the bits in buffer/s

generate(s::SBitstream, T::Integer = 1)
generate!(s::SBitstream, T::Integer = 1)

Generate T samples of the bitstream, s. Add them to the queue for generate!, otherwise return the vector of bits.

generatehw([io::IO = IOBuffer()], f, args...;
           top = nameof(f), submodules = [],
           transforms = [insertrng!, constantreduction!])

Generate a SystemVerilog module named top for f(args...) as a circuit. Apply transforms to the circuit before generating the SystemVerilog.

gethandler(isbroadcast::Bool, ftype::Type, argtypes::Type...)

Return an instance of the hardware handler for the call of type ftype on arguments of types argtypes. isbroadcast is true if the call is a broadcasted call.

Custom operators should overload this function to generate SystemVerilog for calls to the operator.

See also BitSAD.init_state.

is_simulatable_primitive(ftype::Type, argtypes::Type...)

Return true if calling a callable of type ftype on arguments of types argtypes is a BitSAD simulatable primitive operator.

Custom operators should overload BitSAD.is_trace_primitive before this function. is_simulatable_primitive should only be overloaded if the primitive behavior is different for only simulation. Defaults to is_trace_primitive.

is_trace_primitive(ftype::Type, argtypes::Type...)

Return true if calling a callable of type ftype on arguments of types argtypes is a BitSAD primitive operator.

Custom operators should overload this function. Defaults to false.


Return the type of the underlying Julia value that x represents.


Return the net name for x. This can be a SystemVerilog constant expression.


Return the net size of x. Like Base.size, but it expands scalars and vectors to 2D sizes (e.g. (1, 1)).

simulatable(f, args...)

Return a simulatable variation of f(args...) that emulates the bit-level operations for SBitstream variables. The returned function, call it sim, can be called via sim(f, args...).

This is done by recursively tracing the execution of f(args...) then replacing each primitive simulatable operation with a simulatable operator.

simulator(f, args...)

Return a simulatable Ghost.Tape for f(args...). End-users should use simulatable instead.


Return true if a callable of type ftype is a "squashable" 2-arg function. Defaults to false.

Functions like + are n-arg functions in Julia, but most hardware designers think of them as 2-arg. When squashable returns true, BitSAD's tracing engine will "squash" a single n-arg call into nested 2-arg calls.

trace(f, args; is_primitive = is_trace_primitive,
               submodules = [],
               ctx = Dict{Any, Any}())

Trace the function call f(args...) and return the Ghost.Tape. Callables in submodules are considered primitive operators.

This function is not meant to be called by users. Users should use simulatable or generatehw instead.

transform!(f, [fctx,] tape::Ghost.Tape)

Transform tape by applying f to each entry in the tape.

f(tape.ctx, entry) cannot manipulate tape directly. Instead, f should return a tuple of the form ([calls...], idx) where [calls...] is a vector of tape entries that should replace entry. idx specifies that references to entry in tape should be rebound to calls[idx]. If calls is empty, then entry is deleted from the tape, and references to it are rebound to idx. Note that if entry isa Ghost.Input, then it cannot be deleted from tape.

fctx(tape.ctx, calls) can be used to update tape.ctx. calls is the same list of entries returned by f except that the ID for each entry in calls is the ID after being rebound in tape. If calls was empty, then fctx is not called. If not specified, fctx is a no-op.


Return the underlying Julia value that x represents.

@nosim f(x, y::T, ...) [kwargs=false]

Mark a function call of the form f(x, y::T, ...) as a simulation primitive. This will prevent BitSAD's simulation engine for recursing this function, and instead use the return value of f directly (without simulating it). Arguments may or may not have a type specified.

Set kwargs=true if f accepts keyword arguments. Note that the type and name of keywords cannot be specified.

If f(x, y, ...) has a corresponding simulatable operator, then define BitSAD.getsimulator for f.