Defines the MVP SystemVerilog data-type subset and runtime value encoding.
This module is the single source of truth for the value shapes that the
runtime protocol accepts in the first release. It is intentionally narrower
than SystemVerilog: only scalar bit and logic, one-dimensional packed
vectors, explicit signed and unsigned integer views, and clock/reset roles are
supported.
Runtime representation
Every runtime value is encoded as a map with two fields:
:bits- a string containing one character per bit:width- the encoded bit width
Bit strings are ordered from the most significant bit to the least significant
bit. For the canonical packed vector range [width - 1:0], index width - 1
is the first character and index 0 is the last character.
Two-state values may only contain "0" and "1". Four-state values may also
contain "x" for unknown and "z" for high impedance. "x" and "z" are
preserved for scalar and vector values, but they are never accepted for integer
values because integers are numeric views over two-state bits.
Supported MVP subset
| MVP type | SystemVerilog shape | Runtime value |
|---|---|---|
:bit | bit | one two-state bit |
:logic | logic | one four-state bit |
{:bit_vector, width} | bit [width - 1:0] | two-state bit string |
{:logic_vector, width} | logic [width - 1:0] | four-state bit string |
{:uint, width} | unsigned numeric view | Elixir non-negative integer |
{:int, width} | signed numeric view | Elixir integer using two's complement |
:clock | scalar bit role | one two-state bit |
:reset | scalar bit role | one two-state bit with active level |
Vector widths are constrained to 1..4096 bits. Packed vector
ranges are canonicalised to [width - 1:0]; non-canonical source ranges can be
represented only after conversion to this runtime width and ordering.
Examples
iex> SvPortSim.Protocol.DataType.max_vector_width()
4096
iex> SvPortSim.Protocol.DataType.supported?(:bit)
true
iex> SvPortSim.Protocol.DataType.supported?({:logic_vector, 8})
true
iex> SvPortSim.Protocol.DataType.supported?({:logic_vector, 0})
false
iex> SvPortSim.Protocol.DataType.encode({:logic_vector, 4}, "10XZ")
{:ok, %{bits: "10xz", width: 4}}
iex> SvPortSim.Protocol.DataType.encode({:bit_vector, 4}, "10xz")
{:error, {:invalid_bits, "10xz", ["0", "1"]}}
iex> {:ok, type} = SvPortSim.Protocol.DataType.signed_integer(8)
iex> SvPortSim.Protocol.DataType.encode(type, -1)
{:ok, %{bits: "11111111", width: 8}}
iex> SvPortSim.Protocol.DataType.decode(type, %{bits: "11111110"})
{:ok, -2}
iex> {:ok, clock} = SvPortSim.Protocol.DataType.clock()
iex> clock.role
:clock
iex> SvPortSim.Protocol.DataType.encode(clock, true)
{:ok, %{bits: "1", width: 1}}
Summary
Types
A supported SystemVerilog base type.
A protocol value encoded as a bit string plus its width.
A supported runtime type category.
A decoded native Elixir value.
A supported signal role.
The accepted state space for encoded bits.
A normalized MVP data-type descriptor.
Values accepted by normalize/1 as type descriptors.
Functions
Returns a normalized descriptor for scalar bit.
Returns a normalized descriptor for a packed bit vector.
Returns a normalized descriptor for a clock signal role.
Decodes a runtime bit-string value into a native Elixir value.
Decodes an encoded runtime value and raises ArgumentError on failure.
Encodes a native Elixir value as a runtime bit-string value.
Encodes a value and raises ArgumentError on failure.
Returns a normalized descriptor for an integer view.
Returns a normalized descriptor for scalar logic.
Returns a normalized descriptor for a packed logic vector.
Returns the maximum packed-vector width supported by the MVP runtime contract.
Normalises a type descriptor or shorthand into the canonical map form.
Returns a normalized descriptor for a reset signal role.
Returns a normalized scalar descriptor for base.
Returns a normalized descriptor for a signed integer view.
Returns whether a type descriptor is supported by the MVP runtime contract.
Returns an explicit table of the data types supported by the MVP.
Returns a normalized descriptor for an unsigned integer view.
Returns the explicitly unsupported SystemVerilog type forms for the MVP.
Returns a normalized packed-vector descriptor.
Types
@type base() :: :bit | :logic
A supported SystemVerilog base type.
@type encoded_value() :: %{bits: String.t(), width: pos_integer()}
A protocol value encoded as a bit string plus its width.
@type kind() :: :scalar | :vector | :integer
A supported runtime type category.
A decoded native Elixir value.
@type role() :: :data | :clock | :reset
A supported signal role.
@type states() :: :two | :four
The accepted state space for encoded bits.
@type t() :: %{ :base => base(), :kind => kind(), :role => role(), :signed => boolean(), :states => states(), :width => pos_integer(), optional(:active) => :high | :low }
A normalized MVP data-type descriptor.
@type typeish() :: t() | :bit | :logic | :clock | :reset | {:clock, base()} | {:reset, :high | :low} | {:reset, :high | :low, base()} | {:bit_vector, pos_integer()} | {:bit_vector, pos_integer(), boolean() | :signed | :unsigned} | {:logic_vector, pos_integer()} | {:logic_vector, pos_integer(), boolean() | :signed | :unsigned} | {:uint, pos_integer()} | {:int, pos_integer()} | {:integer, pos_integer(), boolean() | :signed | :unsigned}
Values accepted by normalize/1 as type descriptors.
Functions
@spec bit() :: t()
Returns a normalized descriptor for scalar bit.
Examples
iex> SvPortSim.Protocol.DataType.bit().states
:two
Returns a normalized descriptor for a packed bit vector.
Examples
iex> {:ok, type} = SvPortSim.Protocol.DataType.bit_vector(4)
iex> type.width
4
Returns a normalized descriptor for a clock signal role.
A clock is a scalar bit or logic value with the :clock role. Runtime clock
values are always driven as 0 or 1; x and z are rejected even when the
base type is logic.
Examples
iex> {:ok, clock} = SvPortSim.Protocol.DataType.clock(:logic)
iex> {clock.role, clock.states}
{:clock, :four}
@spec decode(typeish() | term(), encoded_value() | map() | String.t()) :: {:ok, native_value()} | {:error, term()}
Decodes a runtime bit-string value into a native Elixir value.
Scalar values decode to 0, 1, :x, or :z. Vector values decode to a
normalized bit string. Integer values decode to Elixir integers.
Examples
iex> SvPortSim.Protocol.DataType.decode(:logic, %{bits: "Z"})
{:ok, :z}
iex> SvPortSim.Protocol.DataType.decode({:logic_vector, 4}, %{bits: "10XZ"})
{:ok, "10xz"}
iex> SvPortSim.Protocol.DataType.decode({:int, 4}, %{bits: "1111"})
{:ok, -1}
@spec decode!(typeish() | term(), encoded_value() | map() | String.t()) :: native_value()
Decodes an encoded runtime value and raises ArgumentError on failure.
Examples
iex> SvPortSim.Protocol.DataType.decode!({:uint, 4}, %{bits: "1010"})
10
@spec encode(typeish() | term(), term()) :: {:ok, encoded_value()} | {:error, term()}
Encodes a native Elixir value as a runtime bit-string value.
Scalar and vector values accept bit strings, bit lists, integers 0 and 1,
booleans, :x, and :z as appropriate for the type state space. Integer
views accept Elixir integers and encode them as unsigned or two's complement
bits.
Examples
iex> SvPortSim.Protocol.DataType.encode(:bit, 1)
{:ok, %{bits: "1", width: 1}}
iex> SvPortSim.Protocol.DataType.encode({:logic_vector, 4}, [1, 0, :x, :z])
{:ok, %{bits: "10xz", width: 4}}
iex> SvPortSim.Protocol.DataType.encode({:uint, 4}, 16)
{:error, {:integer_out_of_range, 16, {0, 15}}}
@spec encode!(typeish() | term(), term()) :: encoded_value()
Encodes a value and raises ArgumentError on failure.
Examples
iex> SvPortSim.Protocol.DataType.encode!(:bit, false)
%{bits: "0", width: 1}
Returns a normalized descriptor for an integer view.
Integer views are always two-state. Unknown and high-impedance values are not valid integer values in the MVP protocol.
Examples
iex> {:ok, type} = SvPortSim.Protocol.DataType.integer(4, signed: :unsigned)
iex> {type.kind, type.width, type.signed, type.states}
{:integer, 4, false, :two}
@spec logic() :: t()
Returns a normalized descriptor for scalar logic.
Examples
iex> SvPortSim.Protocol.DataType.logic().states
:four
Returns a normalized descriptor for a packed logic vector.
Examples
iex> {:ok, type} = SvPortSim.Protocol.DataType.logic_vector(4, signed: :signed)
iex> {type.states, type.signed}
{:four, true}
@spec max_vector_width() :: pos_integer()
Returns the maximum packed-vector width supported by the MVP runtime contract.
Examples
iex> SvPortSim.Protocol.DataType.max_vector_width()
4096
Normalises a type descriptor or shorthand into the canonical map form.
Examples
iex> {:ok, type} = SvPortSim.Protocol.DataType.normalize(:bit)
iex> {type.base, type.width, type.states}
{:bit, 1, :two}
iex> {:ok, type} = SvPortSim.Protocol.DataType.normalize({:logic_vector, 8, :signed})
iex> {type.base, type.width, type.signed}
{:logic, 8, true}
Returns a normalized descriptor for a reset signal role.
Options:
:base-:bitor:logic. Defaults to:bit.:active-:highor:low. Defaults to:high.
Runtime reset values are always driven as 0 or 1; x and z are rejected
even when the base type is logic.
Examples
iex> {:ok, reset} = SvPortSim.Protocol.DataType.reset(active: :low)
iex> {reset.role, reset.active}
{:reset, :low}
Returns a normalized scalar descriptor for base.
Examples
iex> {:ok, type} = SvPortSim.Protocol.DataType.scalar(:bit)
iex> {type.base, type.width, type.states}
{:bit, 1, :two}
iex> SvPortSim.Protocol.DataType.scalar(:byte)
{:error, {:invalid_base, :byte}}
Returns a normalized descriptor for a signed integer view.
Signed integers use two's complement bit encoding.
Examples
iex> {:ok, type} = SvPortSim.Protocol.DataType.signed_integer(4)
iex> SvPortSim.Protocol.DataType.encode(type, -8)
{:ok, %{bits: "1000", width: 4}}
Returns whether a type descriptor is supported by the MVP runtime contract.
Examples
iex> SvPortSim.Protocol.DataType.supported?({:uint, 32})
true
iex> SvPortSim.Protocol.DataType.supported?({:real, 64})
false
@spec supported_types() :: [map()]
Returns an explicit table of the data types supported by the MVP.
The table is data, not prose, so tests and downstream modules can assert that the supported subset has not changed accidentally.
Examples
iex> SvPortSim.Protocol.DataType.supported_types() |> Enum.map(& &1.name)
[:bit, :logic, :bit_vector, :logic_vector, :unsigned_integer, :signed_integer, :clock, :reset]
Returns a normalized descriptor for an unsigned integer view.
Examples
iex> {:ok, type} = SvPortSim.Protocol.DataType.unsigned_integer(4)
iex> SvPortSim.Protocol.DataType.encode(type, 15)
{:ok, %{bits: "1111", width: 4}}
@spec unsupported_features() :: [atom()]
Returns the explicitly unsupported SystemVerilog type forms for the MVP.
This list is intentionally conservative. A type form should be removed from
this list only when normalize/1, encode/2, and decode/2 define exact
behaviour for it.
Examples
iex> :unpacked_arrays in SvPortSim.Protocol.DataType.unsupported_features()
true
Returns a normalized packed-vector descriptor.
:signed may be true, false, :signed, or :unsigned.
Examples
iex> {:ok, type} = SvPortSim.Protocol.DataType.vector(:logic, 2, signed: true)
iex> {type.base, type.width, type.signed, type.states}
{:logic, 2, true, :four}